|
|
1.1 root 1: /*
2: Written 1994 by Donald Becker.
3:
4: This driver is for the Hewlett Packard PC LAN (27***) plus ethercards.
5: These cards are sold under several model numbers, usually 2724*.
6:
7: This software may be used and distributed according to the terms
8: of the GNU Public License, incorporated herein by reference.
9:
10: The author may be reached as [email protected], or C/O
11:
12: Center of Excellence in Space Data and Information Sciences
13: Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
14:
15: As is often the case, a great deal of credit is owed to Russ Nelson.
16: The Crynwr packet driver was my primary source of HP-specific
17: programming information.
18: */
19:
20: /*
21: * Ported to mach by Stephen Clawson, [email protected]
22: * University of Utah CSL.
23: *
24: * Derived from the Linux driver by Donald Becker.
25: *
26: * Also uses code Shantanu Goel adapted from Donald Becker
27: * for ns8930 support.
28: *
29: */
30:
31: #include <hpp.h>
32: #if NHPP > 0
33:
34: #include <sys/types.h>
35: #include "vm_param.h"
36: #include <kern/time_out.h>
37: #include <device/device_types.h>
38: #include <device/errno.h>
39: #include <device/io_req.h>
40: #include <device/if_hdr.h>
41: #include <device/if_ether.h>
42: #include <device/net_status.h>
43: #include <device/net_io.h>
44: #include <chips/busses.h>
45: #include <i386/ipl.h>
46: #include <i386/pio.h>
47: #include <i386at/gpl/if_nsreg.h>
48:
49:
50: /*
51: * XXX - This is some gross glue garbage. The io instructions really
52: * should be integrated into pio.h...
53: */
54: #define IO_DELAY __asm__ __volatile__("outb %al,$0x80")
55: #define outb_p(p, v) { outb(p, v); IO_DELAY; }
56: #define inb_p(p) ({ unsigned char _v; _v = inb(p); IO_DELAY; _v; })
57:
58:
59: static __inline void
60: insw(u_short port, void *addr, int cnt)
61: {
62: __asm __volatile("cld\n\trepne\n\tinsw" :
63: : "d" (port), "D" (addr), "c" (cnt) : "%edi", "%ecx");
64: }
65:
66: static __inline void
67: outsw(u_short port, void *addr, int cnt)
68: {
69: __asm __volatile("cld\n\trepne\n\toutsw" :
70: : "d" (port), "S" (addr), "c" (cnt) : "%esi", "%ecx");
71: }
72:
73:
74: /*
75: The HP EtherTwist chip implementation is a fairly routine DP8390
76: implementation. It allows both shared memory and programmed-I/O buffer
77: access, using a custom interface for both. The programmed-I/O mode is
78: entirely implemented in the HP EtherTwist chip, bypassing the problem
79: ridden built-in 8390 facilities used on NE2000 designs. The shared
80: memory mode is likewise special, with an offset register used to make
81: packets appear at the shared memory base. Both modes use a base and bounds
82: page register to hide the Rx ring buffer wrap -- a packet that spans the
83: end of physical buffer memory appears continuous to the driver. (c.f. the
84: 3c503 and Cabletron E2100)
85:
86: A special note: the internal buffer of the board is only 8 bits wide.
87: This lays several nasty traps for the unaware:
88: - the 8390 must be programmed for byte-wide operations
89: - all I/O and memory operations must work on whole words (the access
90: latches are serially preloaded and have no byte-swapping ability).
91:
92: This board is laid out in I/O space much like the earlier HP boards:
93: the first 16 locations are for the board registers, and the second 16 are
94: for the 8390. The board is easy to identify, with both a dedicated 16 bit
95: ID register and a constant 0x530* value in the upper bits of the paging
96: register.
97: */
98:
99: #define HP_ID 0x00 /* ID register, always 0x4850. */
100: #define HP_PAGING 0x02 /* Registers visible @ 8-f, see PageName. */
101: #define HPP_OPTION 0x04 /* Bitmapped options, see HP_Option.*/
102: #define HPP_OUT_ADDR 0x08 /* I/O output location in Perf_Page.*/
103: #define HPP_IN_ADDR 0x0A /* I/O input location in Perf_Page.*/
104: #define HP_DATAPORT 0x0c /* I/O data transfer in Perf_Page.*/
105: #define HPP_NIC_OFFSET 0x10 /* Offset to the 8390 registers.*/
106: #define HP_IO_EXTENT 32
107:
108: #define HP_START_PG 0x00 /* First page of TX buffer */
109: #define HP_STOP_PG 0x80 /* Last page +1 of RX ring */
110: /*#define HP_STOP_PG 0x1f
111:
112: /* The register set selected in HP_PAGING. */
113: enum PageName {
114: Perf_Page = 0, /* Normal operation. */
115: MAC_Page = 1, /* The ethernet address (+checksum). */
116: HW_Page = 2, /* EEPROM-loaded hw parameters. */
117: LAN_Page = 4, /* Transciever type, testing, etc. */
118: ID_Page = 6 };
119:
120: /* The bit definitions for the HPP_OPTION register. */
121: enum HP_Option {
122: NICReset = 1, /* Active low, really UNreset. */
123: ChipReset = 2,
124: EnableIRQ = 4,
125: FakeIntr = 8,
126: BootROMEnb = 0x10,
127: IOEnb = 0x20,
128: MemEnable = 0x40,
129: ZeroWait = 0x80,
130: MemDisable = 0x1000, };
131:
132:
133: void hpp_reset_8390(struct nssoftc *ns);
134:
135: void hpp_mem_block_input(struct nssoftc *ns, int, char *, int);
136: int hpp_mem_block_output(struct nssoftc *ns, int, char *, int);
137: void hpp_io_block_input(struct nssoftc *ns, int, char *, int);
138: int hpp_io_block_output(struct nssoftc *ns, int,char *, int);
139:
140:
141: /*
142: * Watchdog timer.
143: */
144: int hppwstart = 0;
145: void hppwatch(void);
146:
147:
148: /*
149: * Autoconfig structures.
150: */
151: int hpp_std[] = { 0x200, 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340, 0 };
152: struct bus_device *hpp_info[NHPP];
153: int hpp_probe();
154: void hpp_attach();
155: struct bus_driver hppdriver = {
156: hpp_probe, 0, hpp_attach, 0, hpp_std, "hpp", hpp_info, 0, 0, 0
157: };
158:
159:
160: /*
161: * ns8390 state.
162: */
163: struct nssoftc hppnssoftc[NHPP];
164:
165:
166: /*
167: * hpp state.
168: */
169: struct hppsoftc {
170: unsigned long rmem_start; /* shmem "recv" start */
171: unsigned long rmem_end; /* shmem "recv" end */
172: unsigned long mem_start; /* shared mem start */
173: unsigned long mem_end; /* shared mem end */
174: } hppsoftc[NHPP];
175:
176:
177: /*
178: * Probe a list of addresses for the card.
179: *
180: */
181: int hpp_probe(port, dev)
182: int port;
183: struct bus_device *dev;
184: {
185: int unit = dev->unit;
186: char *str = "hp-plus ethernet board %d out of range.\n";
187: caddr_t base = (caddr_t) (dev ? dev->address : 0);
188: int i;
189:
190: if ((unit < 0) || (unit >= NHPP)) {
191: printf(str, unit);
192: return(0);
193: }
194:
195: /* Check a single specified location. */
196: if (base > (caddr_t) 0x1ff)
197: return hpp_probe1(dev, base);
198: else if (base != 0) /* Don't probe at all. */
199: return 0;
200:
201: for (i = 0; hpp_std[i]; i++) {
202: int ioaddr = hpp_std[i];
203:
204: if ( ioaddr > 0 && hpp_probe1(dev, ioaddr) ) {
205: dev->address = ioaddr;
206: hpp_std[i] = -1; /* Mark address used */
207: return(1);
208: }
209: }
210:
211: return 0;
212: }
213:
214:
215:
216: /*
217: * Do the interesting part of the probe at a single address.
218: *
219: */
220: int hpp_probe1(dev, ioaddr)
221: struct bus_device *dev;
222: int ioaddr;
223: {
224: int i;
225: u_char checksum = 0;
226: int mem_start;
227:
228: struct hppsoftc *hpp = &hppsoftc[dev->unit];
229: struct nssoftc *ns = &hppnssoftc[dev->unit];
230: struct ifnet *ifp = &ns->sc_if;
231:
232: /* Check for the HP+ signature, 50 48 0x 53. */
233: if (inw(ioaddr + HP_ID) != 0x4850
234: || (inw(ioaddr + HP_PAGING) & 0xfff0) != 0x5300)
235: return 0;
236:
237:
238: printf("%s%d: HP PClan plus at %#3x,", dev->name, dev->unit, ioaddr);
239: /* Retrieve and checksum the station address. */
240: outw(ioaddr + HP_PAGING, MAC_Page);
241:
242: printf("MAC_Page = %d, ioaddr = %x\n", MAC_Page, ioaddr);
243:
244: for(i = 0; i < ETHER_ADDR_LEN; i++) {
245: u_char inval = inb(ioaddr + 8 + i);
246: ns->sc_addr[i] = inval;
247: checksum += inval;
248: printf(" %2.2x", inval);
249: }
250: checksum += inb(ioaddr + 14);
251:
252: if (checksum != 0xff) {
253: printf(" bad checksum %2.2x.\n", checksum);
254: return 0;
255: } else {
256: /* Point at the Software Configuration Flags. */
257: outw(ioaddr + HP_PAGING, ID_Page);
258: printf(" ID %4.4x", inw(ioaddr + 12));
259: }
260:
261:
262: /* Read the IRQ line. */
263: outw(ioaddr + HP_PAGING, HW_Page);
264: {
265: int irq = inb(ioaddr + 13) & 0x0f;
266: int option = inw(ioaddr + HPP_OPTION);
267:
268: dev->sysdep1 = irq;
269: take_dev_irq(dev);
270:
271: if (option & MemEnable) {
272: mem_start = inw(ioaddr + 9) << 8;
273: printf(", IRQ %d, memory address %#x.\n", irq, mem_start);
274: } else {
275: mem_start = 0;
276: printf(", IRQ %d, programmed-I/O mode.\n", irq);
277: }
278: }
279:
280: /* Set the wrap registers for string I/O reads. */
281: outw( ioaddr + 14, (HP_START_PG + TX_2X_PAGES) | ((HP_STOP_PG - 1) << 8));
282:
283: /* Set the base address to point to the NIC, not the "real" base! */
284: ns->sc_port = ioaddr + HPP_NIC_OFFSET;
285:
286: ns->sc_name = dev->name;
287: ns->sc_unit = dev->unit;
288: ns->sc_pingpong = 0; /* turn off pingpong mode */
289: ns->sc_word16 = 0; /* Agggghhhhh! Debug time: 2 days! */
290: ns->sc_txstrtpg = HP_START_PG;
291: ns->sc_rxstrtpg = HP_START_PG + TX_2X_PAGES;
292: ns->sc_stoppg = HP_STOP_PG;
293:
294:
295: ns->sc_reset = hpp_reset_8390;
296: ns->sc_input = hpp_io_block_input;
297: ns->sc_output = hpp_io_block_output;
298:
299: /* Check if the memory_enable flag is set in the option register. */
300: if (mem_start) {
301: ns->sc_input = hpp_mem_block_input;
302: ns->sc_output = hpp_mem_block_output;
303: hpp->mem_start = mem_start;
304: hpp->rmem_start = hpp->mem_start + TX_2X_PAGES * 256;
305: hpp->mem_end = hpp->rmem_end
306: = hpp->mem_start + (HP_STOP_PG - HP_START_PG) * 256;
307: }
308:
309: outw(ioaddr + HP_PAGING, Perf_Page);
310:
311: /* Leave the 8390 and HP chip reset. */
312: outw( ioaddr + HPP_OPTION, inw(ioaddr + HPP_OPTION) & ~EnableIRQ );
313:
314: /*
315: * Initialize interface header.
316: */
317: ifp->if_unit = dev->unit;
318: ifp->if_mtu = ETHERMTU;
319: ifp->if_flags = IFF_BROADCAST;
320: ifp->if_header_size = sizeof(struct ether_header);
321: ifp->if_header_format = HDR_ETHERNET;
322: ifp->if_address_size = ETHER_ADDR_LEN;
323: ifp->if_address = ns->sc_addr;
324: if_init_queues(ifp);
325:
326: return (1);
327: }
328:
329: /*
330: * XXX
331: *
332: * this routine really should do the invasive part of the setup.
333: */
334: void
335: hpp_attach(dev)
336: struct bus_device *dev;
337: {
338: /* NULL */
339: }
340:
341:
342:
343: int
344: hppopen(dev, flag)
345: dev_t dev;
346: int flag;
347: {
348: int s, unit = minor(dev);
349: struct bus_device *bd;
350: struct hppsoftc *hpp;
351: struct nssoftc *ns = &hppnssoftc[unit];
352:
353: int ioaddr = ns->sc_port - HPP_NIC_OFFSET;
354: int option_reg;
355:
356: if (unit < 0 || unit >= NHPP ||
357: (bd = hpp_info[unit]) == 0 || !(bd->alive))
358: return ENXIO;
359:
360: /*
361: * Start watchdog.
362: */
363: if (!hppwstart) {
364: hppwstart++;
365: timeout(hppwatch, 0, hz);
366: }
367: hpp = &hppsoftc[unit];
368: ns->sc_if.if_flags |= IFF_UP;
369:
370: s = splimp();
371:
372: /* Reset the 8390 and HP chip. */
373: option_reg = inw(ioaddr + HPP_OPTION);
374: outw( ioaddr + HPP_OPTION, option_reg & ~(NICReset + ChipReset) );
375: IO_DELAY; IO_DELAY;
376:
377: /* Unreset the board and enable interrupts. */
378: outw( ioaddr + HPP_OPTION, option_reg | (EnableIRQ + NICReset + ChipReset));
379:
380: /* Set the wrap registers for programmed-I/O operation. */
381: outw( ioaddr + HP_PAGING, HW_Page );
382: outw( ioaddr + 14, (HP_START_PG + TX_2X_PAGES) | ((HP_STOP_PG - 1) << 8) );
383:
384: /* Select the operational page. */
385: outw( ioaddr + HP_PAGING, Perf_Page );
386: nsinit(ns);
387:
388: splx(s);
389:
390: return (0);
391: }
392:
393: /*
394: * needs to be called at splimp()?
395: *
396: */
397: void
398: hpp_reset_8390(ns)
399: struct nssoftc *ns;
400: {
401: int ioaddr = ns->sc_port - HPP_NIC_OFFSET;
402: int option_reg = inw(ioaddr + HPP_OPTION);
403:
404: outw( ioaddr + HPP_OPTION, option_reg & ~(NICReset + ChipReset) );
405: /* Pause a few cycles for the hardware reset to take place. */
406: IO_DELAY;
407: IO_DELAY;
408: ns->sc_txing = 0;
409: outw( ioaddr + HPP_OPTION, option_reg | (EnableIRQ + NICReset + ChipReset) );
410:
411: /*
412: * XXX - I'm not sure there needs to be this many IO_DELAY's...
413: */
414: IO_DELAY; IO_DELAY;
415: IO_DELAY; IO_DELAY;
416:
417: if ((inb_p(ioaddr + HPP_NIC_OFFSET + EN0_ISR) & ENISR_RESET) == 0)
418: printf("%s: hp_reset_8390() did not complete.\n", ns->sc_name);
419:
420: return;
421: }
422:
423:
424: /*
425: * Block input and output, similar to the Crynwr packet driver.
426: * Note that transfer with the EtherTwist+ must be on word boundaries.
427: */
428: void
429: hpp_io_block_input(ns, count, buf, ring_offset)
430: struct nssoftc *ns;
431: int count;
432: char *buf;
433: int ring_offset;
434: {
435: int ioaddr = ns->sc_port - HPP_NIC_OFFSET;
436:
437: outw(ioaddr + HPP_IN_ADDR, ring_offset);
438:
439: insw(ioaddr + HP_DATAPORT, buf, count >> 1 );
440:
441: if (count & 0x01)
442: buf[count-1] = (char) inw(ioaddr + HP_DATAPORT);
443:
444: }
445:
446: void
447: hpp_mem_block_input(ns, count, buf, ring_offset)
448: struct nssoftc *ns;
449: int count;
450: char *buf;
451: int ring_offset;
452: {
453: int ioaddr = ns->sc_port - HPP_NIC_OFFSET;
454: int option_reg = inw(ioaddr + HPP_OPTION);
455: char *mem_start = (char *)phystokv(hppsoftc[ns->sc_unit].mem_start);
456:
457: outw(ioaddr + HPP_IN_ADDR, ring_offset);
458: outw(ioaddr + HPP_OPTION, option_reg & ~(MemDisable + BootROMEnb));
459:
460: /* copy as much as we can straight through */
461: bcopy16(mem_start, buf, count & ~1);
462:
463: /* Now we copy that last byte. */
464: if (count & 0x01) {
465: u_short savebyte[2];
466:
467: bcopy16(mem_start + (count & ~1), savebyte, 2);
468: buf[count-1] = savebyte[0];
469: }
470:
471: outw(ioaddr + HPP_OPTION, option_reg);
472: }
473:
474:
475: /*
476: * output data into NIC buffers.
477: *
478: * NOTE: All transfers must be on word boundaries.
479: */
480: int
481: hpp_io_block_output(ns, count, buf, start_page)
482: struct nssoftc *ns;
483: int count;
484: char *buf;
485: int start_page;
486: {
487: int ioaddr = ns->sc_port - HPP_NIC_OFFSET;
488:
489: outw(ioaddr + HPP_OUT_ADDR, start_page << 8) ;
490:
491: if (count > 1) {
492: outsw(ioaddr + HP_DATAPORT, buf, count >> 1);
493: }
494:
495: if ( (count & 1) == 1 ) {
496: u_char savebyte[2];
497:
498: savebyte[1] = 0;
499: savebyte[0] = buf[count - 1];
500: outw(ioaddr + HP_DATAPORT, *(u_short *)savebyte);
501: }
502:
503: if (count < (ETHERMIN + sizeof( struct ether_header )))
504: count = ETHERMIN + sizeof( struct ether_header );
505:
506:
507: return (count) ;
508: }
509:
510:
511: /* XXX
512: *
513: * I take great pains to not try and bcopy past the end of the buffer,
514: * does this matter? Are the io request buffers the exact byte size?
515: */
516: int
517: hpp_mem_block_output(ns, count, buf, start_page )
518: struct nssoftc *ns;
519: int count;
520: char *buf;
521: int start_page;
522: {
523: int ioaddr = ns->sc_port - HPP_NIC_OFFSET;
524: int option_reg = inw(ioaddr + HPP_OPTION);
525: struct hppsoftc *hpp = &hppsoftc[ns->sc_unit];
526: char *shmem;
527:
528: outw(ioaddr + HPP_OUT_ADDR, start_page << 8);
529: outw(ioaddr + HPP_OPTION, option_reg & ~(MemDisable + BootROMEnb));
530:
531: shmem = (char *)phystokv(hpp->mem_start);
532: bcopy16(buf, shmem, count & ~1);
533:
534: if ( (count & 1) == 1 ) {
535: u_char savebyte[2];
536:
537: savebyte[1] = 0;
538: savebyte[0] = buf[count - 1];
539: bcopy16(savebyte, shmem + (count & ~1), 2);
540: }
541:
542: while (count < ETHERMIN + sizeof(struct ether_header)) {
543: *(shmem + count) = 0;
544: count++;
545: }
546:
547: outw(ioaddr + HPP_OPTION, option_reg);
548:
549: return count;
550: }
551:
552:
553: int
554: hppintr(unit)
555: int unit;
556: {
557: nsintr(&hppnssoftc[unit]);
558:
559: return(0);
560: }
561:
562: void
563: hppstart(unit)
564: int unit;
565: {
566: nsstart(&hppnssoftc[unit]);
567: }
568:
569: int hppoutput();
570:
571: int
572: hppoutput(dev, ior)
573: dev_t dev;
574: io_req_t ior;
575: {
576: int unit = minor(dev);
577: struct bus_device *ui;
578:
579: if (unit >= NHPP || (ui = hpp_info[unit]) == 0 || ui->alive == 0)
580: return (ENXIO);
581:
582: return (net_write(&hppnssoftc[unit].sc_if, hppstart, ior));
583: }
584:
585:
586: int
587: hppsetinput(dev, receive_port, priority, filter, filter_count)
588: dev_t dev;
589: mach_port_t receive_port;
590: int priority;
591: filter_t *filter;
592: unsigned filter_count;
593: {
594: int unit = minor(dev);
595: struct bus_device *ui;
596:
597: if (unit >= NHPP || (ui = hpp_info[unit]) == 0 || ui->alive == 0)
598: return (ENXIO);
599:
600: return (net_set_filter(&hppnssoftc[unit].sc_if, receive_port,
601: priority, filter, filter_count));
602: }
603:
604:
605: int
606: hppgetstat(dev, flavor, status, count)
607: dev_t dev;
608: int flavor;
609: dev_status_t status;
610: unsigned *count;
611: {
612: int unit = minor(dev);
613: struct bus_device *ui;
614:
615: if (unit >= NHPP || (ui = hpp_info[unit]) == 0 || ui->alive == 0)
616: return (ENXIO);
617:
618: return (net_getstat(&hppnssoftc[unit].sc_if, flavor, status, count));
619: }
620:
621:
622: int
623: hppsetstat(dev, flavor, status, count)
624: dev_t dev;
625: int flavor;
626: dev_status_t status;
627: unsigned count;
628: {
629: int unit = minor(dev), oflags, s;
630: struct bus_device *ui;
631: struct ifnet *ifp;
632: struct net_status *ns;
633:
634: if (unit >= NHPP || (ui = hpp_info[unit]) == 0 || ui->alive == 0)
635: return (ENXIO);
636:
637: ifp = &hppnssoftc[unit].sc_if;
638:
639: switch (flavor) {
640:
641: case NET_STATUS:
642: if (count < NET_STATUS_COUNT)
643: return (D_INVALID_SIZE);
644: ns = (struct net_status *)status;
645: oflags = ifp->if_flags & (IFF_ALLMULTI|IFF_PROMISC);
646: ifp->if_flags &= ~(IFF_ALLMULTI|IFF_PROMISC);
647: ifp->if_flags |= ns->flags & (IFF_ALLMULTI|IFF_PROMISC);
648: if ((ifp->if_flags & (IFF_ALLMULTI|IFF_PROMISC)) != oflags) {
649: s = splimp();
650: nsinit(&hppnssoftc[unit]);
651: splx(s);
652: }
653: break;
654:
655: default:
656: return (D_INVALID_OPERATION);
657: }
658: return (D_SUCCESS);
659: }
660:
661: /*
662: * Watchdog.
663: * Check for hung transmissions.
664: */
665: void
666: hppwatch()
667: {
668: int unit, s;
669: struct nssoftc *ns;
670:
671: timeout(hppwatch, 0, hz);
672:
673: s = splimp();
674: for (unit = 0; unit < NHPP; unit++) {
675: if (hpp_info[unit] == 0 || hpp_info[unit]->alive == 0)
676: continue;
677: ns = &hppnssoftc[unit];
678: if (ns->sc_timer && --ns->sc_timer == 0) {
679: printf("hpp%d: transmission timeout\n", unit);
680: (*ns->sc_reset)(ns);
681: nsinit(ns);
682: }
683: }
684: splx(s);
685: }
686:
687:
688: #endif /* NHPP > 0 */
689:
690:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.