FreeBSD/Linux Kernel Cross Reference
sys/pc/ethersmc.c
1 /*
2 * SMC EtherEZ (SMC91cXX chip) PCMCIA card support.
3 */
4
5 #include "u.h"
6 #include "../port/lib.h"
7 #include "mem.h"
8 #include "dat.h"
9 #include "fns.h"
10 #include "io.h"
11 #include "../port/error.h"
12 #include "../port/netif.h"
13 #include "etherif.h"
14
15 enum {
16 IoSize = 0x10, /* port pool size */
17 TxTimeout = 150,
18 };
19
20 enum { /* PCMCIA related */
21 TupleFunce = 0x22,
22 TfNodeId = 0x04,
23 };
24
25 enum { /* bank 0 registers */
26 Tcr = 0x0000, /* transmit control */
27 Eph = 0x0002, /* ethernet protocol handler */
28 Rcr = 0x0004, /* receiver control */
29 Counter = 0x0006, /* statistics counter */
30 MemInfo = 0x0008,
31 MemCfg = 0x000A,
32 };
33
34 enum { /* bank 1 registers */
35 Config = 0x0000,
36 BaseAddr = 0x0002,
37 Addr0 = 0x0004, /* ethernet address */
38 Addr1 = 0x0006,
39 Addr2 = 0x0008,
40 General = 0x000A,
41 Control = 0x000C,
42 };
43
44 enum { /* bank 2 registers */
45 MmuCmd = 0x0000,
46 PktNo = 0x0002,
47 AllocRes = 0x0003,
48 FifoPorts = 0x0004,
49 Pointer = 0x0006,
50 Data1 = 0x0008,
51 Interrupt = 0x000C,
52 IntrMask = 0x000D,
53 };
54
55 enum { /* bank 3 registers */
56 Mcast0 = 0x0000,
57 Mcast2 = 0x0002,
58 Mcast4 = 0x0004,
59 Mcast6 = 0x0006,
60 Revision = 0x000A,
61 };
62
63 enum {
64 BankSelect = 0x000E /* bank select register */
65 };
66
67 enum {
68 BsrMask = 0xFF00, /* mask for chip identification */
69 BsrId = 0x3300,
70 };
71
72
73 enum { /* Tcr values */
74 TcrClear = 0x0000,
75 TcrEnable = 0x0001, /* enable transmit */
76 TcrLoop = 0x0002, /* enable internal analogue loopback */
77 TcrForceCol = 0x0004, /* force collision on next tx */
78 TcrPadEn = 0x0080, /* pad short packets to 64 bytes */
79 TcrNoCrc = 0x0100, /* do not append CRC */
80 TcrMonCns = 0x0400, /* monitor carrier status */
81 TcrFduplx = 0x0800,
82 TcrStpSqet = 0x1000,
83 TcrEphLoop = 0x2000,
84 TcrNormal = TcrEnable,
85 };
86
87 enum { /* Eph values */
88 EphTxOk = 0x0001,
89 Eph1Col = 0x0002, /* single collision */
90 EphMCol = 0x0004, /* multiple collisions */
91 EphTxMcast = 0x0008, /* multicast transmit */
92 Eph16Col = 0x0010, /* 16 collisions, tx disabled */
93 EphSqet = 0x0020, /* SQE test failed, tx disabled */
94 EphTxBcast = 0x0040, /* broadcast tx */
95 EphDefr = 0x0080, /* deffered tx */
96 EphLatCol = 0x0200, /* late collision, tx disabled */
97 EphLostCarr = 0x0400, /* lost carrier, tx disabled */
98 EphExcDefr = 0x0800, /* excessive defferals */
99 EphCntRol = 0x1000, /* ECR counter(s) rolled over */
100 EphRxOvrn = 0x2000, /* receiver overrun, packets dropped */
101 EphLinkOk = 0x4000,
102 EphTxUnrn = 0x8000, /* tx underrun */
103 };
104
105 enum { /* Rcr values */
106 RcrClear = 0x0000,
107 RcrPromisc = 0x0002,
108 RcrAllMcast = 0x0004,
109 RcrEnable = 0x0100,
110 RcrStripCrc = 0x0200,
111 RcrSoftReset = 0x8000,
112 RcrNormal = RcrStripCrc | RcrEnable,
113 };
114
115 enum { /* Counter value masks */
116 CntColMask = 0x000F, /* collisions */
117 CntMColMask = 0x00F0, /* multiple collisions */
118 CntDtxMask = 0x0F00, /* deferred transmits */
119 CntExDtxMask = 0xF000, /* excessively deferred transmits */
120
121 CntColShr = 1,
122 CntMColShr = 4,
123 CntDtxShr = 8,
124 };
125
126 enum { /* MemInfo value masks */
127 MirTotalMask = 0x00FF,
128 MirFreeMask = 0xFF00,
129 };
130
131 enum { /* Config values */
132 CfgIrqSel0 = 0x0002,
133 CfgIrqSel1 = 0x0004,
134 CfgDisLink = 0x0040, /* disable 10BaseT link test */
135 Cfg16Bit = 0x0080,
136 CfgAuiSelect = 0x0100,
137 CfgSetSqlch = 0x0200,
138 CfgFullStep = 0x0400,
139 CfgNoWait = 0x1000,
140 CfgMiiSelect = 0x8000,
141 };
142
143 enum { /* Control values */
144 CtlStore = 0x0001, /* store to EEPROM */
145 CtlReload = 0x0002, /* reload EEPROM into registers */
146 CtlEeSelect = 0x0004, /* select registers for reload/store */
147 CtlTeEnable = 0x0020, /* tx error detection via eph irq */
148 CtlCrEnable = 0x0040, /* counter rollover via eph irq */
149 CtlLeEnable = 0x0080, /* link error detection via eph irq*/
150 CtlAutoRls = 0x0800, /* auto release mode */
151 CtlPowerDn = 0x2000,
152 };
153
154 enum { /* MmuCmd values */
155 McBusy = 0x0001,
156 McAlloc = 0x0020, /* | with number of 256 byte packets - 1 */
157 McReset = 0x0040,
158 McRelease = 0x0080, /* dequeue (but not free) current rx packet */
159 McFreePkt = 0x00A0, /* dequeue and free current rx packet */
160 McEnqueue = 0x00C0, /* enqueue the packet for tx */
161 McTxReset = 0x00E0, /* reset transmit queues */
162 };
163
164 enum { /* AllocRes values */
165 ArFailed = 0x80,
166 };
167
168 enum { /* FifoPorts values */
169 FpTxEmpty = 0x0080,
170 FpRxEmpty = 0x8000,
171 FpTxMask = 0x007F,
172 FpRxMask = 0x7F00,
173 };
174
175 enum { /* Pointer values */
176 PtrRead = 0x2000,
177 PtrAutoInc = 0x4000,
178 PtrRcv = 0x8000,
179 };
180
181 enum { /* Interrupt values */
182 IntRcv = 0x0001,
183 IntTxError = 0x0002,
184 IntTxEmpty = 0x0004,
185 IntAlloc = 0x0008,
186 IntRxOvrn = 0x0010,
187 IntEph = 0x0020,
188 };
189
190 enum { /* transmit status bits */
191 TsSuccess = 0x0001,
192 Ts16Col = 0x00A0,
193 TsLatCol = 0x0200,
194 TsLostCar = 0x0400,
195 };
196
197 enum { /* receive status bits */
198 RsMcast = 0x0001,
199 RsTooShort = 0x0400,
200 RsTooLong = 0x0800,
201 RsOddFrame = 0x1000,
202 RsBadCrc = 0x2000,
203 RsAlgnErr = 0x8000,
204 RsError = RsAlgnErr | RsBadCrc | RsTooLong | RsTooShort,
205 };
206
207 enum {
208 RxLenMask = 0x07FF, /* significant rx len bits */
209 HdrSize = 6, /* packet header length */
210 PageSize = 256, /* page length */
211 };
212
213 typedef struct Smc91xx Smc91xx;
214 struct Smc91xx {
215 Lock;
216 ushort rev;
217 int attached;
218 Block *txbp;
219 ulong txtime;
220
221 ulong rovrn;
222 ulong lcar;
223 ulong col;
224 ulong scol;
225 ulong mcol;
226 ulong lcol;
227 ulong dfr;
228 };
229
230 #define SELECT_BANK(x) outs(port + BankSelect, x)
231
232 static int
233 readnodeid(int slot, Ether* ether)
234 {
235 uchar data[Eaddrlen + 1];
236 int len;
237
238 len = sizeof(data);
239 if (pcmcistuple(slot, TupleFunce, TfNodeId, data, len) != len)
240 return -1;
241
242 if (data[0] != Eaddrlen)
243 return -1;
244
245 memmove(ether->ea, &data[1], Eaddrlen);
246 return 0;
247 }
248
249 static void
250 chipreset(Ether* ether)
251 {
252 int port;
253 int i;
254
255 port = ether->port;
256
257 /* reset the chip */
258 SELECT_BANK(0);
259 outs(port + Rcr, RcrSoftReset);
260 delay(1);
261 outs(port + Rcr, RcrClear);
262 outs(port + Tcr, TcrClear);
263 SELECT_BANK(1);
264 outs(port + Control, CtlAutoRls | CtlTeEnable |
265 CtlCrEnable);
266
267 for(i = 0; i < 6; i++) {
268 outb(port + Addr0 + i, ether->ea[i]);
269 }
270
271 SELECT_BANK(2);
272 outs(port + MmuCmd, McReset);
273 }
274
275 static void
276 chipenable(Ether* ether)
277 {
278 int port;
279
280 port = ether->port;
281 SELECT_BANK(0);
282 outs(port + Tcr, TcrNormal);
283 outs(port + Rcr, RcrNormal);
284 SELECT_BANK(2);
285 outb(port + IntrMask, IntEph | IntRxOvrn | IntRcv);
286 }
287
288 static void
289 attach(Ether *ether)
290 {
291 Smc91xx* ctlr;
292
293 ctlr = ether->ctlr;
294 ilock(ctlr);
295
296 if (ctlr->attached) {
297 iunlock(ctlr);
298 return;
299 }
300
301 chipenable(ether);
302 ctlr->attached = 1;
303 iunlock(ctlr);
304 }
305
306 static void
307 txstart(Ether* ether)
308 {
309 int port;
310 Smc91xx* ctlr;
311 Block* bp;
312 int len, npages;
313 int pno;
314
315 /* assumes ctlr is locked and bank 2 is selected */
316 /* leaves bank 2 selected on return */
317 port = ether->port;
318 ctlr = ether->ctlr;
319
320 if (ctlr->txbp) {
321 bp = ctlr->txbp;
322 ctlr->txbp = 0;
323 } else {
324 bp = qget(ether->oq);
325 if (bp == 0)
326 return;
327
328 len = BLEN(bp);
329 npages = (len + HdrSize) / PageSize;
330 outs(port + MmuCmd, McAlloc | npages);
331 }
332
333 pno = inb(port + AllocRes);
334 if (pno & ArFailed) {
335 outb(port + IntrMask, inb(port + IntrMask) | IntAlloc);
336 ctlr->txbp = bp;
337 ctlr->txtime = MACHP(0)->ticks;
338 return;
339 }
340
341 outb(port + PktNo, pno);
342 outs(port + Pointer, PtrAutoInc);
343
344 len = BLEN(bp);
345 outs(port + Data1, 0);
346 outb(port + Data1, (len + HdrSize) & 0xFF);
347 outb(port + Data1, (len + HdrSize) >> 8);
348 outss(port + Data1, bp->rp, len / 2);
349 if ((len & 1) == 0) {
350 outs(port + Data1, 0);
351 } else {
352 outb(port + Data1, bp->rp[len - 1]);
353 outb(port + Data1, 0x20); /* no info what 0x20 means */
354 }
355
356 outb(port + IntrMask, inb(port + IntrMask) |
357 IntTxError | IntTxEmpty);
358
359 outs(port + MmuCmd, McEnqueue);
360 freeb(bp);
361 }
362
363 static void
364 receive(Ether* ether)
365 {
366 int port;
367 Block* bp;
368 int pktno, status, len;
369
370 /* assumes ctlr is locked and bank 2 is selected */
371 /* leaves bank 2 selected on return */
372 port = ether->port;
373
374 pktno = ins(port + FifoPorts);
375 if (pktno & FpRxEmpty) {
376 return;
377 }
378
379 outs(port + Pointer, PtrRead | PtrRcv | PtrAutoInc);
380 status = ins(port + Data1);
381 len = ins(port + Data1) & RxLenMask - HdrSize;
382
383 if (status & RsOddFrame)
384 len++;
385
386 if ((status & RsError) || (bp = iallocb(len)) == 0) {
387
388 if (status & RsAlgnErr)
389 ether->frames++;
390 if (status & (RsTooShort | RsTooLong))
391 ether->buffs++;
392 if (status & RsBadCrc)
393 ether->crcs++;
394
395 outs(port + MmuCmd, McRelease);
396 return;
397 }
398
399 /* packet length is padded to word */
400 inss(port + Data1, bp->rp, len / 2);
401 bp->wp = bp->rp + (len & ~1);
402
403 if (len & 1) {
404 *bp->wp = inb(port + Data1);
405 bp->wp++;
406 }
407
408 etheriq(ether, bp, 1);
409 ether->inpackets++;
410 outs(port + MmuCmd, McRelease);
411 }
412
413 static void
414 txerror(Ether* ether)
415 {
416 int port;
417 Smc91xx* ctlr;
418 int save_pkt;
419 int pktno, status;
420
421 /* assumes ctlr is locked and bank 2 is selected */
422 /* leaves bank 2 selected on return */
423 port = ether->port;
424 ctlr = ether->ctlr;
425
426 save_pkt = inb(port + PktNo);
427
428 pktno = ins(port + FifoPorts) & FpTxMask;
429 outb(port + PktNo, pktno);
430 outs(port + Pointer, PtrAutoInc | PtrRead);
431 status = ins(port + Data1);
432
433 if (status & TsLostCar)
434 ctlr->lcar++;
435
436 if (status & TsLatCol)
437 ctlr->lcol++;
438
439 if (status & Ts16Col)
440 ctlr->scol++;
441
442 ether->oerrs++;
443
444 SELECT_BANK(0);
445 outs(port + Tcr, ins(port + Tcr) | TcrEnable);
446
447 SELECT_BANK(2);
448 outs(port + MmuCmd, McFreePkt);
449
450 outb(port + PktNo, save_pkt);
451 }
452
453 static void
454 eph_irq(Ether* ether)
455 {
456 int port;
457 Smc91xx* ctlr;
458 ushort status;
459 int n;
460
461 /* assumes ctlr is locked and bank 2 is selected */
462 /* leaves bank 2 selected on return */
463 port = ether->port;
464 ctlr = ether->ctlr;
465
466 SELECT_BANK(0);
467 status = ins(port + Eph);
468
469 if (status & EphCntRol) {
470 /* read the counter register even if we don't need it */
471 /* otherwise we will keep getting this interrupt */
472 n = ins(port + Counter);
473 ctlr->col += (n & CntColMask) >> CntColShr;
474 ctlr->mcol += (n & CntMColMask) >> CntMColShr;
475 ctlr->dfr += (n & CntDtxMask) >> CntDtxShr;
476 }
477
478 /* if there was a transmit error, Tcr is disabled */
479 outs(port + Tcr, ins(port + Tcr) | TcrEnable);
480
481 /* clear a link error interrupt */
482 SELECT_BANK(1);
483 outs(port + Control, CtlAutoRls);
484 outs(port + Control, CtlAutoRls | CtlTeEnable | CtlCrEnable);
485
486 SELECT_BANK(2);
487 }
488
489 static void
490 transmit(Ether* ether)
491 {
492 Smc91xx* ctlr;
493 int port, n;
494
495 ctlr = ether->ctlr;
496 port = ether->port;
497 ilock(ctlr);
498
499 if (ctlr->txbp) {
500 n = TK2MS(MACHP(0)->ticks - ctlr->txtime);
501 if (n > TxTimeout) {
502 chipreset(ether);
503 chipenable(ether);
504 freeb(ctlr->txbp);
505 ctlr->txbp = 0;
506 }
507 iunlock(ctlr);
508 return;
509 }
510
511 SELECT_BANK(2);
512 txstart(ether);
513 iunlock(ctlr);
514 }
515
516 static void
517 interrupt(Ureg*, void *arg)
518 {
519 int port;
520 Smc91xx* ctlr;
521 Ether* ether;
522 int save_bank;
523 int save_pointer;
524 int mask, status;
525
526 ether = arg;
527 port = ether->port;
528 ctlr = ether->ctlr;
529
530 ilock(ctlr);
531 save_bank = ins(port + BankSelect);
532 SELECT_BANK(2);
533 save_pointer = ins(port + Pointer);
534
535 mask = inb(port + IntrMask);
536 outb(port + IntrMask, 0);
537
538 while ((status = inb(port + Interrupt) & mask) != 0) {
539 if (status & IntRcv) {
540 receive(ether);
541 }
542
543 if (status & IntTxError) {
544 txerror(ether);
545 }
546
547 if (status & IntTxEmpty) {
548 outb(port + Interrupt, IntTxEmpty);
549 outb(port + IntrMask, mask & ~IntTxEmpty);
550 txstart(ether);
551 mask = inb(port + IntrMask);
552 }
553
554 if (status & IntAlloc) {
555 outb(port + IntrMask, mask & ~IntAlloc);
556 txstart(ether);;
557 mask = inb(port + IntrMask);
558 }
559
560 if (status & IntRxOvrn) {
561 ctlr->rovrn++;
562 ether->misses++;
563 outb(port + Interrupt,IntRxOvrn);
564 }
565
566 if (status & IntEph)
567 eph_irq(ether);
568 }
569
570 outb(port + IntrMask, mask);
571 outs(port + Pointer, save_pointer);
572 outs(port + BankSelect, save_bank);
573 iunlock(ctlr);
574 }
575
576 static void
577 promiscuous(void* arg, int on)
578 {
579 int port;
580 Smc91xx *ctlr;
581 Ether* ether;
582 ushort x;
583
584 ether = arg;
585 port = ether->port;
586 ctlr = ether->ctlr;
587
588 ilock(ctlr);
589 SELECT_BANK(0);
590 x = ins(port + Rcr);
591 if (on)
592 x |= RcrPromisc;
593 else
594 x &= ~RcrPromisc;
595
596 outs(port + Rcr, x);
597 iunlock(ctlr);
598 }
599
600 static void
601 multicast(void* arg, uchar *addr, int on)
602 {
603 int port;
604 Smc91xx*ctlr;
605 Ether *ether;
606 ushort x;
607
608 USED(addr, on);
609
610 ether = arg;
611 port = ether->port;
612 ctlr = ether->ctlr;
613 ilock(ctlr);
614
615 SELECT_BANK(0);
616 x = ins(port + Rcr);
617
618 if (ether->nmaddr)
619 x |= RcrAllMcast;
620 else
621 x &= ~RcrAllMcast;
622
623 outs(port + Rcr, x);
624 iunlock(ctlr);
625 }
626
627 static long
628 ifstat(Ether* ether, void* a, long n, ulong offset)
629 {
630 static char *chiprev[] = {
631 [3] "92",
632 [5] "95",
633 [7] "100",
634 [8] "100-FD",
635 [9] "110",
636 };
637
638 Smc91xx* ctlr;
639 char* p;
640 int r, len;
641 char* s;
642
643 if (n == 0)
644 return 0;
645
646 ctlr = ether->ctlr;
647 p = malloc(READSTR);
648
649 s = 0;
650 if (ctlr->rev > 0) {
651 r = ctlr->rev >> 4;
652 if (r < nelem(chiprev))
653 s = chiprev[r];
654
655 if (r == 4) {
656 if ((ctlr->rev & 0x0F) >= 6)
657 s = "96";
658 else
659 s = "94";
660 }
661 }
662
663 len = snprint(p, READSTR, "rev: 91c%s\n", (s) ? s : "???");
664 len += snprint(p + len, READSTR - len, "rxovrn: %uld\n", ctlr->rovrn);
665 len += snprint(p + len, READSTR - len, "lcar: %uld\n", ctlr->lcar);
666 len += snprint(p + len, READSTR - len, "col: %uld\n", ctlr->col);
667 len += snprint(p + len, READSTR - len, "16col: %uld\n", ctlr->scol);
668 len += snprint(p + len, READSTR - len, "mcol: %uld\n", ctlr->mcol);
669 len += snprint(p + len, READSTR - len, "lcol: %uld\n", ctlr->lcol);
670 len += snprint(p + len, READSTR - len, "dfr: %uld\n", ctlr->dfr);
671 USED(len);
672
673 n = readstr(offset, a, n, p);
674 free(p);
675
676 return n;
677 }
678
679 static int
680 reset(Ether* ether)
681 {
682 int port;
683 int i, x;
684 char* type;
685 Smc91xx* ctlr;
686 int slot;
687 uchar ea[Eaddrlen];
688
689 if (ether->irq == 0)
690 ether->irq = 9;
691
692 if (ether->port == 0)
693 ether->port = 0x100;
694
695 type = "8020";
696 for(i = 0; i < ether->nopt; i++) {
697 if (cistrncmp(ether->opt[i], "id=", 3))
698 continue;
699 type = ðer->opt[i][3];
700 break;
701 }
702
703 if ((slot = pcmspecial(type, ether)) < 0)
704 return -1;
705
706 if (ioalloc(ether->port, IoSize, 0, "smc91cXX") < 0) {
707 pcmspecialclose(slot);
708 return -1;
709 }
710
711 ether->ctlr = malloc(sizeof(Smc91xx));
712 ctlr = ether->ctlr;
713 if (ctlr == 0) {
714 iofree(ether->port);
715 pcmspecialclose(slot);
716 return -1;
717 }
718
719 ilock(ctlr);
720 ctlr->rev = 0;
721 ctlr->txbp = nil;
722 ctlr->attached = 0;
723 ctlr->rovrn = 0;
724 ctlr->lcar = 0;
725 ctlr->col = 0;
726 ctlr->scol = 0;
727 ctlr->mcol = 0;
728 ctlr->lcol = 0;
729 ctlr->dfr = 0;
730
731 port = ether->port;
732
733 SELECT_BANK(1);
734 if ((ins(port + BankSelect) & BsrMask) != BsrId) {
735 outs(port + Control, 0); /* try powering up the chip */
736 delay(55);
737 }
738
739 outs(port + Config, ins(port + Config) | Cfg16Bit);
740 x = ins(port + BaseAddr);
741
742 if (((ins(port + BankSelect) & BsrMask) != BsrId) ||
743 ((x >> 8) == (x & 0xFF))) {
744 iunlock(ctlr);
745 iofree(port);
746 pcmspecialclose(slot);
747 return -1;
748 }
749
750 SELECT_BANK(3);
751 ctlr->rev = ins(port + Revision) & 0xFF;
752
753 memset(ea, 0, Eaddrlen);
754 if (memcmp(ea, ether->ea, Eaddrlen) == 0) {
755 if (readnodeid(slot, ether) < 0) {
756 print("Smc91cXX: cannot find ethernet address\n");
757 iunlock(ctlr);
758 iofree(port);
759 pcmspecialclose(slot);
760 return -1;
761 }
762 }
763
764 chipreset(ether);
765
766 ether->attach = attach;
767 ether->transmit = transmit;
768 ether->interrupt = interrupt;
769 ether->ifstat = ifstat;
770 ether->promiscuous = promiscuous;
771 ether->multicast = multicast;
772 ether->arg = ether;
773 iunlock(ctlr);
774 return 0;
775 }
776
777 void
778 ethersmclink(void)
779 {
780 addethercard("smc91cXX", reset);
781 }
Cache object: 2591ce0f7007a80b93df5ba75b5395ed
|