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 } 782