|
|
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 1992,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 <ul.h>
29: #include <wd.h>
30: #include <hpp.h>
31: #if NUL > 0 || NWD > 0 || NHPP > 0
32: /*
33: * Generic NS8390 routines.
34: * Derived from the Linux driver by Donald Becker.
35: *
36: * Shantanu Goel ([email protected])
37: */
38: #include <sys/types.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 IO_DELAY __asm__ __volatile__ ("outb %al,$0x80")
52: #define outb_p(p, v) { outb(p, v); IO_DELAY; }
53: #define inb_p(p) ({ unsigned char _v; _v = inb(p); IO_DELAY; _v; })
54:
55: #define NSDEBUG
56: #ifdef NSDEBUG
57: int nsdebug = 0;
58: #define DEBUGF(stmt) { if (nsdebug) stmt; }
59: #else
60: #define DEBUGF(stmt)
61: #endif
62:
63: void nsxint(struct nssoftc *);
64: void nsrint(struct nssoftc *);
65: void nsxmit(struct nssoftc *, unsigned, int);
66: void nsrxoverrun(struct nssoftc *);
67:
68: /*
69: * Initialize the NIC.
70: * Must be called at splimp().
71: */
72: void
73: nsinit(sc)
74: struct nssoftc *sc;
75: {
76: int port = sc->sc_port, i, rxconfig;
77: int endcfg = sc->sc_word16 ? (0x48 | ENDCFG_WTS) : 0x48;
78: struct ifnet *ifp = &sc->sc_if;
79:
80: /*
81: * Reset the board.
82: */
83: (*sc->sc_reset)(sc);
84:
85: sc->sc_oactive = 0;
86: sc->sc_txing = 0;
87: sc->sc_timer = 0;
88: sc->sc_tx1 = sc->sc_tx2 = 0;
89: sc->sc_curpg = sc->sc_rxstrtpg;
90:
91: /*
92: * Follow National Semiconductor's recommendations for
93: * initializing the DP83902.
94: */
95: outb_p(port, E8390_NODMA+E8390_PAGE0+E8390_STOP); /* 0x21 */
96: outb_p(port + EN0_DCFG, endcfg); /* 0x48 or 0x49 */
97:
98: /*
99: * Clear remote byte count registers.
100: */
101: outb_p(port + EN0_RCNTLO, 0);
102: outb_p(port + EN0_RCNTHI, 0);
103:
104: /*
105: * Set to monitor and loopback mode -- this is vital!
106: */
107: outb_p(port + EN0_RXCR, E8390_RXOFF); /* 0x20 */
108: outb_p(port + EN0_TXCR, E8390_TXOFF); /* 0x02 */
109:
110: /*
111: * Set transmit page and receive ring.
112: */
113: outb_p(port + EN0_TPSR, sc->sc_txstrtpg);
114: outb_p(port + EN0_STARTPG, sc->sc_rxstrtpg);
115: outb_p(port + EN0_BOUNDARY, sc->sc_stoppg - 1);
116: outb_p(port + EN0_STOPPG, sc->sc_stoppg);
117:
118: /*
119: * Clear pending interrupts and mask.
120: */
121: outb_p(port + EN0_ISR, 0xff);
122:
123: /*
124: * Enable the following interrupts: receive/transmit complete,
125: * receive/transmit error, and Receiver OverWrite.
126: *
127: * Counter overflow and Remote DMA complete are *not* enabled.
128: */
129: outb_p(port + EN0_IMR, ENISR_RX | ENISR_TX | ENISR_RX_ERR |
130: ENISR_TX_ERR | ENISR_OVER );
131:
132: /*
133: * Copy station address into 8390 registers.
134: */
135: outb_p(port, E8390_NODMA + E8390_PAGE1 + E8390_STOP); /* 0x61 */
136: for (i = 0; i < ETHER_ADDR_LEN; i++)
137: outb_p(port + EN1_PHYS + i, sc->sc_addr[i]);
138:
139: /*
140: * Set up to accept all multicast packets.
141: */
142: for (i = 0; i < 8; i++)
143: outb_p(port + EN1_MULT + i, 0xff);
144:
145: /*
146: * Initialize CURRent pointer
147: */
148: outb_p(port + EN1_CURPAG, sc->sc_rxstrtpg);
149:
150: /*
151: * Program command register for page 0.
152: */
153: outb_p(port, E8390_NODMA + E8390_PAGE0 + E8390_STOP);
154:
155: #if 0
156: outb_p(port + EN0_ISR, 0xff);
157: outb_p(port + EN0_IMR, ENISR_ALL);
158: #endif
159:
160: outb_p(port + E8390_CMD, E8390_NODMA + E8390_PAGE0 + E8390_START);
161: outb_p(port + EN0_TXCR, E8390_TXCONFIG); /* xmit on */
162:
163: /* 3c503 TechMan says rxconfig only after the NIC is started. */
164: rxconfig = E8390_RXCONFIG;
165: if (ifp->if_flags & IFF_ALLMULTI)
166: rxconfig |= 0x08;
167: if (ifp->if_flags & IFF_PROMISC)
168: rxconfig |= 0x10;
169: outb_p(port + EN0_RXCR, rxconfig); /* rx on */
170:
171: /*
172: * Mark interface as up and start output.
173: */
174: ifp->if_flags |= IFF_RUNNING;
175: nsstart(sc);
176: }
177:
178: /*
179: * Start output on interface.
180: * Must be called at splimp().
181: */
182: void
183: nsstart(sc)
184: struct nssoftc *sc;
185: {
186: io_req_t ior;
187: struct ifnet *ifp = &sc->sc_if;
188:
189: /*
190: * Drop packets if interface is down.
191: */
192: if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
193: while (1) {
194: IF_DEQUEUE(&ifp->if_snd, ior);
195: if (ior == 0)
196: return;
197: iodone(ior);
198: }
199: }
200: /*
201: * If transmitter is busy, bail out.
202: */
203: if (sc->sc_oactive)
204: return;
205:
206: /*
207: * Dequeue a packet.
208: */
209: IF_DEQUEUE(&ifp->if_snd, ior);
210: if (ior == 0)
211: return;
212:
213: /* Mask interrupts from the ethercard. */
214: outb( sc->sc_port + EN0_IMR, 0x00);
215:
216: if (sc->sc_pingpong) {
217: int count, output_page;
218:
219: if (sc->sc_tx1 == 0) {
220: output_page = sc->sc_txstrtpg;
221: sc->sc_tx1 = count = (*sc->sc_output)(sc,
222: ior->io_count,
223: ior->io_data,
224: sc->sc_txstrtpg);
225: } else if (sc->sc_tx2 == 0) {
226: output_page = sc->sc_txstrtpg + 6;
227: sc->sc_tx2 = count = (*sc->sc_output)(sc,
228: ior->io_count,
229: ior->io_data,
230: output_page);
231: } else {
232: sc->sc_oactive = 1;
233: IF_PREPEND(&ifp->if_snd, ior);
234: return;
235: }
236:
237: DEBUGF({
238: struct ether_header *eh;
239:
240: eh = (struct ether_header *)ior->io_data;
241: printf("send: %s%d: %x:%x:%x:%x:%x:%x, "
242: "olen %d, len %d\n",
243: sc->sc_name, sc->sc_unit,
244: eh->ether_dhost[0], eh->ether_dhost[1],
245: eh->ether_dhost[2], eh->ether_dhost[3],
246: eh->ether_dhost[4], eh->ether_dhost[5],
247: ior->io_count, count);
248: });
249:
250: if (!sc->sc_txing) {
251: nsxmit(sc, count, output_page);
252: if (output_page == sc->sc_txstrtpg)
253: sc->sc_tx1 = -1, sc->sc_lasttx = -1;
254: else
255: sc->sc_tx2 = -1, sc->sc_lasttx = -2;
256: }
257: sc->sc_oactive = (sc->sc_tx1 && sc->sc_tx2);
258: } else {
259: int count;
260:
261: count = (*sc->sc_output)(sc, ior->io_count,
262: ior->io_data, sc->sc_txstrtpg);
263:
264: DEBUGF({
265: struct ether_header *eh;
266:
267: eh = (struct ether_header *)ior->io_data;
268: printf("send: %s%d: %x:%x:%x:%x:%x:%x, "
269: "olen %d, len %d\n",
270: sc->sc_name, sc->sc_unit,
271: eh->ether_dhost[0], eh->ether_dhost[1],
272: eh->ether_dhost[2], eh->ether_dhost[3],
273: eh->ether_dhost[4], eh->ether_dhost[5],
274: ior->io_count, count);
275: });
276:
277: nsxmit(sc, count, sc->sc_txstrtpg);
278: sc->sc_oactive = 1;
279: }
280:
281: /* reenable 8390 interrupts. */
282: outb_p(sc->sc_port + EN0_IMR, ENISR_ALL);
283:
284: iodone(ior);
285: }
286:
287: /*
288: * Interrupt routine.
289: * Called by board level driver.
290: */
291: void
292: nsintr(sc)
293: struct nssoftc *sc;
294: {
295: int port = sc->sc_port;
296: int interrupts, boguscount = 0;
297: struct ifnet *ifp = &sc->sc_if;
298:
299: if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
300: DEBUGF(printf("nsintr: %s%d: interface down\n",
301: sc->sc_name, sc->sc_unit));
302: return;
303: }
304:
305: /*
306: * Change to page 0 and read intr status reg.
307: */
308: outb_p(port + E8390_CMD, E8390_NODMA+E8390_PAGE0);
309:
310: while ((interrupts = inb_p(port + EN0_ISR)) != 0 && ++boguscount < 9) {
311: if (interrupts & ENISR_RDC) {
312: /*
313: * Ack meaningless DMA complete.
314: */
315: outb_p(port + EN0_ISR, ENISR_RDC);
316: }
317:
318: if (interrupts & ENISR_OVER)
319: nsrxoverrun(sc);
320: else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) {
321: nsrint(sc);
322: }
323:
324: if (interrupts & ENISR_TX) {
325: nsxint(sc);
326: }
327: else if (interrupts & ENISR_COUNTERS) {
328: /*
329: * XXX - We really should be storing statistics
330: * about the interface. For now we just drop them.
331: */
332:
333: /* reading resets the counters! */
334: (void) inb_p(port + EN0_COUNTER0); /* frame */
335: (void) inb_p(port + EN0_COUNTER1); /* crc */
336: (void) inb_p(port + EN0_COUNTER2); /* miss */
337:
338: DEBUGF(printf("%s%d: acked counter interrupt.\n",
339: sc->sc_name, sc->sc_unit));
340:
341: outb_p(port + EN0_ISR, ENISR_COUNTERS); /* ack intr */
342: }
343:
344: if (interrupts & ENISR_TX_ERR) {
345: DEBUGF(printf("acking transmit error\n"));
346: outb_p(port + EN0_ISR, ENISR_TX_ERR); /* ack intr */
347: }
348:
349: outb_p(port + E8390_CMD, E8390_NODMA+E8390_PAGE0+E8390_START);
350: }
351:
352: DEBUGF({
353: if (interrupts) {
354: printf("%s%d: unknown interrupt 0x%x",
355: sc->sc_name, sc->sc_unit, interrupts);
356: outb_p(port + E8390_CMD,
357: E8390_NODMA+E8390_PAGE0+E8390_START);
358: outb_p(port + EN0_ISR, 0xff); /* ack all intrs */
359: }
360: })
361: }
362:
363: /*
364: * Process a transmit interrupt.
365: */
366: void
367: nsxint(sc)
368: struct nssoftc *sc;
369: {
370: int port = sc->sc_port, status;
371: struct ifnet *ifp = &sc->sc_if;
372:
373: status = inb(port + EN0_TSR);
374: outb_p(port + EN0_ISR, ENISR_TX); /* ack intr */
375:
376: sc->sc_txing = 0;
377: sc->sc_timer = 0;
378: sc->sc_oactive = 0;
379:
380: if (sc->sc_pingpong) {
381: if (sc->sc_tx1 < 0) {
382: if (sc->sc_lasttx != 1 && sc->sc_lasttx != -1)
383: printf("%s%d: bogus last_tx_buffer %d,"
384: "tx1 = %d\n",
385: sc->sc_name, sc->sc_unit,
386: sc->sc_lasttx, sc->sc_tx1);
387: sc->sc_tx1 = 0;
388: if (sc->sc_tx2 > 0) {
389: nsxmit(sc, sc->sc_tx2, sc->sc_txstrtpg + 6);
390: sc->sc_tx2 = -1;
391: sc->sc_lasttx = 2;
392: } else
393: sc->sc_lasttx = 20;
394: } else if (sc->sc_tx2 < 0) {
395: if (sc->sc_lasttx != 2 && sc->sc_lasttx != -2)
396: printf("%s%d: bogus last_tx_buffer %d,"
397: "tx2 = %d\n",
398: sc->sc_name, sc->sc_unit,
399: sc->sc_lasttx, sc->sc_tx2);
400: sc->sc_tx2 = 0;
401: if (sc->sc_tx1 > 0) {
402: nsxmit(sc, sc->sc_tx1, sc->sc_txstrtpg);
403: sc->sc_tx1 = -1;
404: sc->sc_lasttx = 1;
405: } else
406: sc->sc_lasttx = 10;
407: } else
408: printf("%s%d: unexpected TX-done interrupt, "
409: "lasttx = %d\n",
410: sc->sc_name, sc->sc_unit, sc->sc_lasttx);
411: }
412: /*
413: * Update stats.
414: */
415: if (status & ENTSR_COL) {
416: if (status & ENTSR_ABT)
417: ifp->if_collisions += 16;
418: else
419: ifp->if_collisions += inb(port + EN0_NCR);
420: }
421: if (status & ENTSR_PTX) {
422: DEBUGF(printf("sent: %s%d\n", sc->sc_name, sc->sc_unit));
423: ifp->if_opackets++;
424: } else
425: ifp->if_oerrors++;
426:
427: /*
428: * Start output on interface.
429: */
430: nsstart(sc);
431: }
432:
433: /*
434: * Process a receive interrupt.
435: */
436: void
437: nsrint(sc)
438: struct nssoftc *sc;
439: {
440: int port = sc->sc_port;
441: int rxing_page, this_frame, next_frame, current_offset;
442: int rx_pkt_count = 0;
443: int num_rx_pages = sc->sc_stoppg - sc->sc_rxstrtpg;
444: struct nspkthdr rx_frame;
445: struct ifnet *ifp = &sc->sc_if;
446:
447: while (++rx_pkt_count < 10) {
448: int pkt_len;
449:
450: /*
451: * Get the rx page (incoming packet pointer).
452: */
453: outb_p(port + E8390_CMD, E8390_NODMA+E8390_PAGE1);
454: rxing_page = inb_p(port + EN1_CURPAG);
455: outb_p(port + E8390_CMD, E8390_NODMA+E8390_PAGE0);
456:
457: /*
458: * Remove one frame from the ring.
459: * Boundary is always a page behind.
460: */
461: this_frame = inb_p(port + EN0_BOUNDARY) + 1;
462: if (this_frame >= sc->sc_stoppg)
463: this_frame = sc->sc_rxstrtpg;
464:
465: DEBUGF({
466: if (this_frame != sc->sc_curpg)
467: printf("%s%d: mismatched read page pointers "
468: "%x vs %x\n",
469: sc->sc_name, sc->sc_unit,
470: this_frame, sc->sc_curpg);
471: });
472:
473: if (this_frame == rxing_page) {
474: DEBUGF(printf("this_frame = rxing_page!\n"));
475: break;
476: }
477:
478: current_offset = this_frame << 8;
479: (*sc->sc_input)(sc, sizeof(rx_frame), (char *)&rx_frame,
480: current_offset);
481:
482: pkt_len = rx_frame.count - sizeof(rx_frame);
483:
484: next_frame = this_frame + 1 + ((pkt_len + 4) >> 8);
485:
486: if (rx_frame.next != next_frame
487: && rx_frame.next != next_frame + 1
488: && rx_frame.next != next_frame - num_rx_pages
489: && rx_frame.next != next_frame + 1 - num_rx_pages) {
490: sc->sc_curpg = rxing_page;
491: outb(port + EN0_BOUNDARY, sc->sc_curpg - 1);
492: ifp->if_ierrors++;
493: DEBUGF(printf("INPUT ERROR?\n"));
494: continue;
495: }
496: if (pkt_len < 60 || pkt_len > 1518) {
497: ifp->if_ierrors++;
498: DEBUGF(printf("%s%d: bad packet length %d\n",
499: sc->sc_name, sc->sc_unit, pkt_len));
500: } else if ((rx_frame.status & 0x0f) == ENRSR_RXOK) {
501: ipc_kmsg_t kmsg;
502:
503: kmsg = net_kmsg_get();
504: if (kmsg == 0) {
505: DEBUGF(printf("%s%d: dropped packet\n",
506: sc->sc_name, sc->sc_unit));
507: ifp->if_rcvdrops++;
508: } else {
509: int len, off;
510: struct ether_header *eh;
511: struct packet_header *pkt;
512:
513: ifp->if_ipackets++;
514: off = current_offset + sizeof(rx_frame);
515: eh = ((struct ether_header *)
516: (&net_kmsg(kmsg)->header[0]));
517: (*sc->sc_input)(sc,
518: sizeof(struct ether_header),
519: (char *)eh, off);
520: off += sizeof(struct ether_header);
521: len = pkt_len - sizeof(struct ether_header);
522:
523: DEBUGF(printf("rcv: %s%d: %x:%x:%x:%x:%x:%x, "
524: "len %d, type 0x%x\n",
525: sc->sc_name, sc->sc_unit,
526: eh->ether_shost[0],
527: eh->ether_shost[1],
528: eh->ether_shost[2],
529: eh->ether_shost[3],
530: eh->ether_shost[4],
531: eh->ether_shost[5],
532: len, eh->ether_type));
533:
534: pkt = ((struct packet_header *)
535: (&net_kmsg(kmsg)->packet[0]));
536: (*sc->sc_input)(sc, len, (char *)(pkt+1), off);
537: pkt->type = eh->ether_type;
538: pkt->length = len+sizeof(struct packet_header);
539: net_packet(ifp, kmsg, pkt->length,
540: ethernet_priority(kmsg));
541: }
542: } else {
543: DEBUGF(printf("%s%d: bogus packet: "
544: "status=0x%x nxpg=0x%x size=%d\n",
545: sc->sc_name, sc->sc_unit,
546: rx_frame.status, rx_frame.next,
547: rx_frame.count));
548: ifp->if_ierrors++;
549: }
550: next_frame = rx_frame.next;
551: if (next_frame >= sc->sc_stoppg) {
552: DEBUGF(printf("%s%d: next frame inconsistency, 0x%x\n",
553: sc->sc_name, sc->sc_unit, next_frame));
554: next_frame = sc->sc_rxstrtpg;
555: }
556: sc->sc_curpg = next_frame;
557: outb(port + EN0_BOUNDARY, next_frame - 1);
558: }
559:
560: /*
561: * Bug alert! Reset ENISR_OVER to avoid spurious overruns!
562: */
563: outb_p(port + EN0_ISR, ENISR_RX+ENISR_RX_ERR+ENISR_OVER);
564: }
565:
566: /*
567: * Handle a receive overrun condition.
568: *
569: * XXX - this needs to be gone over in light of the NS documentation.
570: */
571: void
572: nsrxoverrun(sc)
573: struct nssoftc *sc;
574: {
575: int port = sc->sc_port, i;
576: extern unsigned delaycount;
577:
578: printf("%s%d: receive overrun\n", sc->sc_name, sc->sc_unit);
579:
580: /*
581: * We should already be stopped and in page0, but just to be sure...
582: */
583: outb_p(port + E8390_CMD, E8390_NODMA+E8390_PAGE0+E8390_STOP);
584:
585: /*
586: * Clear remote byte counter registers.
587: */
588: outb_p(port + EN0_RCNTLO, 0);
589: outb_p(port + EN0_RCNTHI, 0);
590:
591: /*
592: * Wait for reset to complete.
593: */
594: for (i = delaycount*2; i && !(inb_p(port+EN0_ISR) & ENISR_RESET); i--)
595: ;
596: if (i == 0) {
597: printf("%s%d: reset did not complete at overrun\n",
598: sc->sc_name, sc->sc_unit);
599: nsinit(sc);
600: return;
601: }
602: /*
603: * Disable transmitter.
604: */
605: outb_p(port + EN0_TXCR, E8390_TXOFF);
606:
607: /*
608: * Remove packets.
609: */
610: nsrint(sc);
611:
612: outb_p(port + EN0_ISR, 0xff);
613: outb_p(port + E8390_CMD, E8390_NODMA+E8390_PAGE0+E8390_START);
614: outb_p(port + EN0_TXCR, E8390_TXCONFIG);
615: }
616:
617: /*
618: * Trigger a transmit start.
619: */
620: void
621: nsxmit(sc, length, start_page)
622: struct nssoftc *sc;
623: unsigned length;
624: int start_page;
625: {
626: int port = sc->sc_port;
627:
628: sc->sc_txing = 1;
629: outb_p(port, E8390_NODMA+E8390_PAGE0);
630: if (inb_p(port) & E8390_TRANS) {
631: printf("%s%d: nsxmit() called with the transmitter busy\n",
632: sc->sc_name, sc->sc_unit);
633: return;
634: }
635: outb_p(port + EN0_TCNTLO, length & 0xff);
636: outb_p(port + EN0_TCNTHI, (length >> 8) & 0xff);
637: outb_p(port + EN0_TPSR, start_page);
638: outb_p(port, E8390_NODMA+E8390_TRANS+E8390_START);
639: sc->sc_timer = 4;
640: }
641:
642: #endif /* NUL > 0 || NWD > 0 */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.