diff options
| author | Charles.Forsyth <devnull@localhost> | 2007-04-07 12:52:27 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2007-04-07 12:52:27 +0000 |
| commit | e1bd49a1e1823eab71c293efd1bd0c9b83c350c9 (patch) | |
| tree | a9424cde16004cb9e591045225bc61beed339dd5 /os/pc | |
| parent | 15345f9c75c069d3e2e362af5d6f931eef7772ef (diff) | |
20070407-1350 more updates from plan 9 pc kernel, and a few unreachable fixes(!)
Diffstat (limited to 'os/pc')
| -rw-r--r-- | os/pc/ether2000.c | 6 | ||||
| -rw-r--r-- | os/pc/ether2114x.c | 10 | ||||
| -rw-r--r-- | os/pc/ether79c970.c | 11 | ||||
| -rw-r--r-- | os/pc/ether8139.c | 12 | ||||
| -rw-r--r-- | os/pc/ether82543gc.c | 12 | ||||
| -rw-r--r-- | os/pc/ether82557.c | 26 | ||||
| -rw-r--r-- | os/pc/ether83815.c | 169 | ||||
| -rw-r--r-- | os/pc/ether8390.c | 6 | ||||
| -rw-r--r-- | os/pc/etherdp83820.c | 1246 | ||||
| -rw-r--r-- | os/pc/etherec2t.c | 1 | ||||
| -rw-r--r-- | os/pc/etherelnk3.c | 10 | ||||
| -rw-r--r-- | os/pc/etherga620.c | 143 | ||||
| -rw-r--r-- | os/pc/etherigbe.c | 97 | ||||
| -rw-r--r-- | os/pc/ethersmc.c | 1 | ||||
| -rw-r--r-- | os/pc/ethervt6102.c | 1025 | ||||
| -rw-r--r-- | os/pc/etherwavelan.c | 11 | ||||
| -rw-r--r-- | os/pc/fns.h | 4 | ||||
| -rw-r--r-- | os/pc/i8253.c | 2 | ||||
| -rw-r--r-- | os/pc/io.h | 50 | ||||
| -rw-r--r-- | os/pc/memory.c | 18 | ||||
| -rw-r--r-- | os/pc/mmu.c | 16 | ||||
| -rw-r--r-- | os/pc/pci.c | 1 |
22 files changed, 2728 insertions, 149 deletions
diff --git a/os/pc/ether2000.c b/os/pc/ether2000.c index 4ea8fd4f..8fc8911e 100644 --- a/os/pc/ether2000.c +++ b/os/pc/ether2000.c @@ -132,7 +132,6 @@ ne2000pnp(Ether* edev) static int ne2000reset(Ether* edev) { - static int first; ushort buf[16]; ulong port; Dp8390 *dp8390; @@ -198,11 +197,14 @@ ne2000reset(Ether* edev) * initialisation has been tried, but that wouldn't be * enough, there are other ethernet boards which could * match. + * Parallels has buf[0x0E] == 0x00 whereas real hardware + * usually has 0x57. */ dp8390reset(edev); memset(buf, 0, sizeof(buf)); dp8390read(dp8390, buf, 0, sizeof(buf)); - if((buf[0x0E] & 0xFF) != 0x57 || (buf[0x0F] & 0xFF) != 0x57){ + i = buf[0x0E] & 0xFF; + if((i != 0x00 && i != 0x57) || (buf[0x0F] & 0xFF) != 0x57){ iofree(edev->port); free(edev->ctlr); return -1; diff --git a/os/pc/ether2114x.c b/os/pc/ether2114x.c index 43bae880..f71773c0 100644 --- a/os/pc/ether2114x.c +++ b/os/pc/ether2114x.c @@ -190,6 +190,7 @@ enum { /* Variants */ Pnic = (0x0002<<16)|0x11AD, Pnic2 = (0xC115<<16)|0x11AD, CentaurP = (0x0985<<16)|0x1317, + CentaurPcb = (0x1985<<16)|0x1317, }; typedef struct Ctlr Ctlr; @@ -1515,7 +1516,7 @@ srom(Ctlr* ctlr) ctlr->srom[20+i+1] = ctlr->srom[i]; } } - if(ctlr->id == CentaurP){ + if(ctlr->id == CentaurP || ctlr->id == CentaurPcb){ memmove(&ctlr->srom[20], leafpnic, sizeof(leafpnic)); for(i = 0; i < Eaddrlen; i += 2){ ctlr->srom[20+i] = ctlr->srom[8+i]; @@ -1603,7 +1604,7 @@ srom(Ctlr* ctlr) if(phy){ x = 0; for(k = 0; k < nelem(ctlr->phy); k++){ - if(ctlr->id == CentaurP && k != 1) + if((ctlr->id == CentaurP || ctlr->id == CentaurPcb) && k != 1) continue; if((oui = miir(ctlr, k, 2)) == -1 || oui == 0) continue; @@ -1655,6 +1656,7 @@ dec2114xpci(void) case Pnic: /* PNIC */ case Pnic2: /* PNIC-II */ case CentaurP: /* ADMtek */ + case CentaurPcb: /* ADMtek CardBus */ break; } @@ -1690,7 +1692,6 @@ dec2114xpci(void) switch(ctlr->id){ default: break; - case Pnic: /* PNIC */ /* * Turn off the jabber timer. @@ -1698,6 +1699,7 @@ dec2114xpci(void) csr32w(ctlr, 15, 0x00000001); break; case CentaurP: + case CentaurPcb: /* * Nice - the register offsets change from *8 to *4 * for CSR16 and up... @@ -1823,6 +1825,6 @@ reset(Ether* ether) void ether2114xlink(void) { - addethercard("21140", reset); addethercard("2114x", reset); + addethercard("21140", reset); } diff --git a/os/pc/ether79c970.c b/os/pc/ether79c970.c index 25bf4185..43924a3f 100644 --- a/os/pc/ether79c970.c +++ b/os/pc/ether79c970.c @@ -301,6 +301,12 @@ promiscuous(void* arg, int on) } static void +multicast(void* arg, uchar*, int) +{ + promiscuous(arg, 1); +} + +static void txstart(Ether* ether) { Ctlr *ctlr; @@ -522,7 +528,6 @@ reset(Ether* ether) ctlr->iow = io32w; }else{ print("#l%d: card doesn't talk right\n", ether->ctlrno); -iprint("#l%d: card doesn't talk right\n", ether->ctlrno); iunlock(ctlr); return -1; } @@ -539,8 +544,6 @@ iprint("#l%d: card doesn't talk right\n", ether->ctlrno); default: print("#l%d: unknown PCnet card version %.7ux\n", ether->ctlrno, x&0xFFFFFFF); -iprint("#l%d: unknown PCnet card version %.7ux\n", - ether->ctlrno, x&0xFFFFFFF); iunlock(ctlr); return -1; } @@ -629,6 +632,8 @@ iprint("#l%d: unknown PCnet card version %.7ux\n", ether->arg = ether; ether->promiscuous = promiscuous; + ether->multicast = multicast; +// ether->shutdown = shutdown; return 0; } diff --git a/os/pc/ether8139.c b/os/pc/ether8139.c index 12deba1b..dd3653e3 100644 --- a/os/pc/ether8139.c +++ b/os/pc/ether8139.c @@ -234,6 +234,12 @@ rtl8139promiscuous(void* arg, int on) iunlock(&ctlr->ilock); } +static void +rtl8139multicast(void* arg, uchar*, int) +{ + rtl8139promiscuous(arg, 1); +} + static long rtl8139ifstat(Ether* edev, void* a, long n, ulong offset) { @@ -637,8 +643,10 @@ rtl8139match(Ether* edev, int id) } ctlr->port = port; - if(rtl8139reset(ctlr)) + if(rtl8139reset(ctlr)) { + iofree(port); continue; + } pcisetbme(p); ctlr->active = 1; @@ -738,6 +746,8 @@ rtl8139pnp(Ether* edev) edev->arg = edev; edev->promiscuous = rtl8139promiscuous; + edev->multicast = rtl8139multicast; +// edev->shutdown = rtl8139shutdown; /* * This should be much more dynamic but will do for now. diff --git a/os/pc/ether82543gc.c b/os/pc/ether82543gc.c index 6277b10b..86ec7b75 100644 --- a/os/pc/ether82543gc.c +++ b/os/pc/ether82543gc.c @@ -1243,7 +1243,8 @@ gc82543watchdog(void* arg) static void gc82543pci(void) { - int port, cls; + int cls; + void *mem; Pcidev *p; Ctlr *ctlr; @@ -1262,8 +1263,8 @@ gc82543pci(void) break; } - port = upamalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0); - if(port == 0){ + mem = vmap(p->mem[0].bar & ~0x0F, p->mem[0].size); + if(mem == 0){ print("gc82543: can't map %8.8luX\n", p->mem[0].bar); continue; } @@ -1280,11 +1281,10 @@ gc82543pci(void) cls*4); } ctlr = malloc(sizeof(Ctlr)); - ctlr->port = port; + ctlr->port = p->mem[0].bar & ~0x0F; ctlr->pcidev = p; ctlr->id = (p->did<<16)|p->vid; - - ctlr->nic = KADDR(ctlr->port); + ctlr->nic = mem; if(gc82543reset(ctlr)){ free(ctlr); diff --git a/os/pc/ether82557.c b/os/pc/ether82557.c index b61e3e1c..4dba07bd 100644 --- a/os/pc/ether82557.c +++ b/os/pc/ether82557.c @@ -290,7 +290,7 @@ command(Ctlr* ctlr, int c, int v) if(timeo >= 100){ ctlr->command = -1; iunlock(&ctlr->rlock); - iprint("i82557: command 0x%uX %uX timeout\n", c, v); + iprint("i82557: command %#ux %#ux timeout\n", c, v); return; } @@ -462,7 +462,7 @@ ifstat(Ether* ether, void* a, long n, ulong offset) for(i = 0; i < (1<<ctlr->eepromsz); i++){ if(i && ((i & 0x07) == 0)) len += snprint(p+len, READSTR-len, "\n "); - len += snprint(p+len, READSTR-len, " %4.4uX", ctlr->eeprom[i]); + len += snprint(p+len, READSTR-len, " %4.4ux", ctlr->eeprom[i]); } if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000)){ @@ -471,7 +471,7 @@ ifstat(Ether* ether, void* a, long n, ulong offset) for(i = 0; i < 6; i++){ static int miir(Ctlr*, int, int); - len += snprint(p+len, READSTR-len, " %4.4uX", + len += snprint(p+len, READSTR-len, " %4.4ux", miir(ctlr, phyaddr, i)); } } @@ -523,7 +523,7 @@ txstart(Ether* ether) ctlr->action = 0; } else{ - print("#l%d: action 0x%uX\n", ether->ctlrno, ctlr->action); + print("#l%d: action %#ux\n", ether->ctlrno, ctlr->action); ctlr->action = 0; break; } @@ -630,14 +630,14 @@ receive(Ether* ether) pbp = nil; count = rfd->count & 0x3FFF; if((count < ETHERMAXTU/4) && (pbp = iallocb(count))){ - memmove(pbp->rp, bp->rp+sizeof(Rfd)-sizeof(rfd->data), count); + memmove(pbp->rp, bp->rp+offsetof(Rfd, data[0]), count); pbp->wp = pbp->rp + count; rfd->count = 0; rfd->field = 0; } else if(xbp = rfdalloc(rfd->link)){ - bp->rp += sizeof(Rfd)-sizeof(rfd->data); + bp->rp += offsetof(Rfd, data[0]); bp->wp = bp->rp + count; xbp->next = bp->next; @@ -748,7 +748,7 @@ interrupt(Ureg*, void* arg) } if(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI)) - panic("#l%d: status %uX\n", ether->ctlrno, status); + panic("#l%d: status %#ux\n", ether->ctlrno, status); } } @@ -926,14 +926,16 @@ i82557pci(void) continue; case 0x1031: /* Intel 82562EM */ case 0x1050: /* Intel 82562EZ */ + case 0x1039: /* Intel 82801BD PRO/100 VE */ + case 0x103A: /* Intel 82562 PRO/100 VE */ + case 0x103D: /* Intel 82562 PRO/100 VE */ + case 0x1064: /* Intel 82562 PRO/100 VE */ case 0x2449: /* Intel 82562ET */ nop = 1; /*FALLTHROUGH*/ case 0x1209: /* Intel 82559ER */ case 0x1229: /* Intel 8255[789] */ case 0x1030: /* Intel 82559 InBusiness 10/100 */ - case 0x1039: /* Intel 82801BD PRO/100 VE */ - case 0x103A: /* Intel 82562 PRO/100 VE */ break; } @@ -955,7 +957,7 @@ i82557pci(void) */ port = p->mem[1].bar & ~0x01; if(ioalloc(port, p->mem[1].size, 0, "i82557") < 0){ - print("i82557: port 0x%uX in use\n", port); + print("i82557: port %#ux in use\n", port); continue; } @@ -997,7 +999,7 @@ scanphy(Ctlr* ctlr) oui <<= 6; x = miir(ctlr, i, 3); oui |= x>>10; - //print("phy%d: oui %uX reg1 %uX\n", i, oui, miir(ctlr, i, 1)); + //print("phy%d: oui %#ux reg1 %#ux\n", i, oui, miir(ctlr, i, 1)); ctlr->eeprom[6] = i; if(oui == 0xAA00) @@ -1093,7 +1095,7 @@ reset(Ether* ether) sum += x; } if(sum != 0xBABA) - print("#l%d: EEPROM checksum - 0x%4.4uX\n", ether->ctlrno, sum); + print("#l%d: EEPROM checksum - %#4.4ux\n", ether->ctlrno, sum); /* * Eeprom[6] indicates whether there is a PHY and whether diff --git a/os/pc/ether83815.c b/os/pc/ether83815.c index 2059bad4..3d2d9d76 100644 --- a/os/pc/ether83815.c +++ b/os/pc/ether83815.c @@ -79,9 +79,22 @@ enum { /* cmdsts */ Col = 1<<16, /* collision during receive */ }; -enum { /* Variants */ +enum { /* PCI vendor & device IDs */ Nat83815 = (0x0020<<16)|0x100B, - Sis900 = (0x0630<<16)|0x1039, /* untested */ + SiS = 0x1039, + SiS900 = (0x0900<<16)|SiS, + SiS7016 = (0x7016<<16)|SiS, + + SiS630bridge = 0x0008, + + /* SiS 900 PCI revision codes */ + SiSrev630s = 0x81, + SiSrev630e = 0x82, + SiSrev630ea1 = 0x83, + + SiSeenodeaddr = 8, /* short addr of SiS eeprom mac addr */ + SiS630eenodeaddr = 9, /* likewise for the 630 */ + Nseenodeaddr = 6, /* " for NS eeprom */ }; typedef struct Ctlr Ctlr; @@ -456,9 +469,9 @@ txrxcfg(Ctlr *ctlr, int txdrth) static void interrupt(Ureg*, void* arg) { + int len, status, cmdsts, n; Ctlr *ctlr; Ether *ether; - int len, status, cmdsts; Des *des; Block *bp; @@ -483,6 +496,15 @@ interrupt(Ureg*, void* arg) status &= ~(Hiberr|Txrcmp|Rxrcmp|Rxsovr|Dperr|Sserr|Rmabt|Rtabt); } + /* update link state */ + if(status&Phy){ + status &= ~Phy; + csr32r(ctlr, Rcfg); + n = csr32r(ctlr, Rcfg); +// iprint("83815 phy %x %x\n", n, n&Lnksts); + ether->link = (n&Lnksts) != 0; + } + /* * Received packets. */ @@ -511,11 +533,19 @@ interrupt(Ureg*, void* arg) } else if(bp = iallocb(Rbsz)){ len = (cmdsts&Size)-4; - des->bp->wp = des->bp->rp+len; - etheriq(ether, des->bp, 1); + if(len <= 0){ + debug("ns83815: packet len %d <=0\n", len); + freeb(des->bp); + }else{ + des->bp->wp = des->bp->rp+len; + etheriq(ether, des->bp, 1); + } des->bp = bp; des->addr = PADDR(bp->rp); coherence(); + }else{ + debug("ns83815: interrupt: iallocb for input buffer failed\n"); + des->bp->next = 0; } des->cmdsts = Rbsz; @@ -607,7 +637,7 @@ ctlrinit(Ether* ether) last = nil; for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){ des->bp = iallocb(Rbsz); - if (des->bp == nil) + if(des->bp == nil) error(Enomem); des->cmdsts = Rbsz; des->addr = PADDR(des->bp->rp); @@ -635,8 +665,10 @@ ctlrinit(Ether* ether) txrxcfg(ctlr, Drth512); - csr32w(ctlr, Rimr, Dperr|Sserr|Rmabt|Rtabt|Rxsovr|Hiberr|Txurn|Txerr|Txdesc|Txok|Rxorn|Rxerr|Rxdesc|Rxok); /* Phy|Pme|Mib */ - csr32r(ctlr, Risr); /* clear status */ + csr32w(ctlr, Rimr, Dperr|Sserr|Rmabt|Rtabt|Rxsovr|Hiberr|Txurn|Txerr| + Txdesc|Txok|Rxorn|Rxerr|Rxdesc|Rxok); /* Phy|Pme|Mib */ + csr32w(ctlr, Rmicr, Inten); /* enable phy interrupts */ + csr32r(ctlr, Risr); /* clear status */ csr32w(ctlr, Rier, Ie); } @@ -762,7 +794,7 @@ softreset(Ctlr* ctlr, int resetphys) if(csr16r(ctlr, Ranar) == 0 || (csr32r(ctlr, Rcfg) & Aneg_dn) == 0){ csr16w(ctlr, Rbmcr, Anena|Anrestart); for(i=0;; i++){ - if(i > 6000){ + if(i > 3000){ print("ns83815: auto neg timed out\n"); break; } @@ -791,11 +823,8 @@ media(Ether* ether) ctlr = ether->ctlr; cfg = csr32r(ctlr, Rcfg); ctlr->fd = (cfg & Fdup) != 0; - if(cfg & Speed100) - return 100; - if((cfg & Lnksts) == 0) - return 100; /* no link: use 100 to ensure larger queues */ - return 10; + ether->link = (cfg&Lnksts) != 0; + return (cfg&(Lnksts|Speed100)) == Lnksts? 10: 100; } static char* mediatable[9] = { @@ -810,8 +839,82 @@ static char* mediatable[9] = { "100BASE-FXFD", }; +static int +is630(ulong id, Pcidev *p) +{ + if(id == SiS900) + switch (p->rid) { + case SiSrev630s: + case SiSrev630e: + case SiSrev630ea1: + return 1; + } + return 0; +} + +enum { + MagicReg = 0x48, + MagicRegSz = 1, + Magicrden = 0x40, /* read enable, apparently */ + Paddr= 0x70, /* address port */ + Pdata= 0x71, /* data port */ +}; + +/* rcmos() originally from LANL's SiS 900 driver's rcmos() */ +static int +sisrdcmos(Ctlr *ctlr) +{ + int i; + unsigned reg; + ulong port; + Pcidev *p; + + debug("ns83815: SiS 630 rev. %ux reading mac address from cmos\n", ctlr->pcidev->rid); + p = pcimatch(nil, SiS, SiS630bridge); + if(p == nil) { + print("ns83815: no SiS 630 rev. %ux bridge for mac addr\n", + ctlr->pcidev->rid); + return 0; + } + port = p->mem[0].bar & ~0x01; + debug("ns83815: SiS 630 rev. %ux reading mac addr from cmos via bridge at port 0x%lux\n", ctlr->pcidev->rid, port); + + reg = pcicfgr8(p, MagicReg); + pcicfgw8(p, MagicReg, reg|Magicrden); + + for (i = 0; i < Eaddrlen; i++) { + outb(port+Paddr, SiS630eenodeaddr + i); + ctlr->sromea[i] = inb(port+Pdata); + } + + pcicfgw8(p, MagicReg, reg & ~Magicrden); + return 1; +} + +/* + * If this is a SiS 630E chipset with an embedded SiS 900 controller, + * we have to read the MAC address from the APC CMOS RAM. - sez freebsd. + * However, CMOS *is* NVRAM normally. See devrtc.c:440, memory.c:88. + */ static void -srom(Ctlr* ctlr) +sissrom(Ctlr *ctlr) +{ + union { + uchar eaddr[Eaddrlen]; + ushort alignment; + } ee; + int i, off = SiSeenodeaddr, cnt = sizeof ee.eaddr / sizeof(short); + ushort *shp = (ushort *)ee.eaddr; + + if(!is630(ctlr->id, ctlr->pcidev) || !sisrdcmos(ctlr)) { + for (i = 0; i < cnt; i++) + *shp++ = eegetw(ctlr, off++); + memmove(ctlr->sromea, ee.eaddr, sizeof ctlr->sromea); + } +} + +static void +nssrom(Ctlr* ctlr) { int i, j; @@ -821,8 +924,7 @@ srom(Ctlr* ctlr) /* * the MAC address is reversed, straddling word boundaries */ - memset(ctlr->sromea, 0, sizeof(ctlr->sromea)); - j = 6*16 + 15; + j = Nseenodeaddr*16 + 15; for(i=0; i<48; i++){ ctlr->sromea[i>>3] |= ((ctlr->srom[j>>4] >> (15-(j&0xF))) & 1) << (i&7); j++; @@ -830,21 +932,42 @@ srom(Ctlr* ctlr) } static void +srom(Ctlr* ctlr) +{ + memset(ctlr->sromea, 0, sizeof(ctlr->sromea)); + switch (ctlr->id) { + case SiS900: + case SiS7016: + sissrom(ctlr); + break; + case Nat83815: + nssrom(ctlr); + break; + default: + print("ns83815: srom: unknown id 0x%ux\n", ctlr->id); + break; + } +} + +static void scanpci83815(void) { Ctlr *ctlr; Pcidev *p; + ulong id; p = nil; while(p = pcimatch(p, 0, 0)){ - if(p->ccrb != 0x02 || p->ccru != 0) + if(p->ccrb != Pcibcnet || p->ccru != 0) continue; - switch((p->did<<16)|p->vid){ + id = (p->did<<16)|p->vid; + switch(id){ default: continue; case Nat83815: - case Sis900: + break; + case SiS900: break; } @@ -855,7 +978,7 @@ scanpci83815(void) ctlr = malloc(sizeof(Ctlr)); ctlr->port = p->mem[0].bar & ~0x01; ctlr->pcidev = p; - ctlr->id = (p->did<<16)|p->vid; + ctlr->id = id; if(ioalloc(ctlr->port, p->mem[0].size, 0, "ns83815") < 0){ print("ns83815: port 0x%uX in use\n", ctlr->port); @@ -885,6 +1008,7 @@ reset(Ether* ether) { Ctlr *ctlr; int i, x; + ulong ctladdr; uchar ea[Eaddrlen]; static int scandone; @@ -923,7 +1047,8 @@ reset(Ether* ether) memmove(ether->ea, ctlr->sromea, Eaddrlen); for(i=0; i<Eaddrlen; i+=2){ x = ether->ea[i] | (ether->ea[i+1]<<8); - csr32w(ctlr, Rrfcr, i); + ctladdr = (ctlr->id == Nat83815? i: i<<15); + csr32w(ctlr, Rrfcr, ctladdr); csr32w(ctlr, Rrfdr, x); } csr32w(ctlr, Rrfcr, Rfen|Apm|Aab|Aam); diff --git a/os/pc/ether8390.c b/os/pc/ether8390.c index 42bde3cf..50d7ce39 100644 --- a/os/pc/ether8390.c +++ b/os/pc/ether8390.c @@ -400,7 +400,7 @@ receive(Ether* ether) */ if(hdr.next < ctlr->pstart || hdr.next >= ctlr->pstop || len < 60 || len > sizeof(Etherpkt)){ - print("dp8390: H#%2.2ux#%2.2ux#%2.2ux#%2.2ux,%lud\n", + print("dp8390: H%2.2ux+%2.2ux+%2.2ux+%2.2ux,%lud\n", hdr.status, hdr.next, hdr.len0, hdr.len1, len); regw(ctlr, Cr, Page0|RdABORT|Stp); ringinit(ctlr); @@ -588,7 +588,7 @@ interrupt(Ureg*, void* arg) if(isr & (Txe|Ptx)){ r = regr(ctlr, Tsr); if((isr & Txe) && (r & (Cdh|Fu|Crs|Abt))){ - print("dp8390: Tsr#%2.2ux|", r); + print("dp8390: Tsr %#2.2ux", r); ether->oerrs++; } @@ -686,7 +686,7 @@ multicast(void* arg, uchar *addr, int on) if(reverse[1] == 0){ for(i = 0; i < 64; i++) reverse[i] = ((i&1)<<5) | ((i&2)<<3) | ((i&4)<<1) - | ((i&8)>>1) | ((i&16)>>3) | ((i&32)>>5); + | ((i&8)>>1) | ((i&16)>>3) | ((i&32)>>5); } /* diff --git a/os/pc/etherdp83820.c b/os/pc/etherdp83820.c new file mode 100644 index 00000000..aeda3713 --- /dev/null +++ b/os/pc/etherdp83820.c @@ -0,0 +1,1246 @@ +/* + * National Semiconductor DP83820 + * 10/100/1000 Mb/s Ethernet Network Interface Controller + * (Gig-NIC). + * Driver assumes little-endian and 32-bit host throughout. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" +#include "ethermii.h" + +enum { /* Registers */ + Cr = 0x00, /* Command */ + Cfg = 0x04, /* Configuration and Media Status */ + Mear = 0x08, /* MII/EEPROM Access */ + Ptscr = 0x0C, /* PCI Test Control */ + Isr = 0x10, /* Interrupt Status */ + Imr = 0x14, /* Interrupt Mask */ + Ier = 0x18, /* Interrupt Enable */ + Ihr = 0x1C, /* Interrupt Holdoff */ + Txdp = 0x20, /* Transmit Descriptor Pointer */ + Txdphi = 0x24, /* Transmit Descriptor Pointer Hi */ + Txcfg = 0x28, /* Transmit Configuration */ + Gpior = 0x2C, /* General Purpose I/O Control */ + Rxdp = 0x30, /* Receive Descriptor Pointer */ + Rxdphi = 0x34, /* Receive Descriptor Pointer Hi */ + Rxcfg = 0x38, /* Receive Configuration */ + Pqcr = 0x3C, /* Priority Queueing Control */ + Wcsr = 0x40, /* Wake on LAN Control/Status */ + Pcr = 0x44, /* Pause Control/Status */ + Rfcr = 0x48, /* Receive Filter/Match Control */ + Rfdr = 0x4C, /* Receive Filter/Match Data */ + Brar = 0x50, /* Boot ROM Address */ + Brdr = 0x54, /* Boot ROM Data */ + Srr = 0x58, /* Silicon Revision */ + Mibc = 0x5C, /* MIB Control */ + Mibd = 0x60, /* MIB Data */ + Txdp1 = 0xA0, /* Txdp Priority 1 */ + Txdp2 = 0xA4, /* Txdp Priority 2 */ + Txdp3 = 0xA8, /* Txdp Priority 3 */ + Rxdp1 = 0xB0, /* Rxdp Priority 1 */ + Rxdp2 = 0xB4, /* Rxdp Priority 2 */ + Rxdp3 = 0xB8, /* Rxdp Priority 3 */ + Vrcr = 0xBC, /* VLAN/IP Receive Control */ + Vtcr = 0xC0, /* VLAN/IP Transmit Control */ + Vdr = 0xC4, /* VLAN Data */ + Ccsr = 0xCC, /* Clockrun Control/Status */ + Tbicr = 0xE0, /* TBI Control */ + Tbisr = 0xE4, /* TBI Status */ + Tanar = 0xE8, /* TBI ANAR */ + Tanlpar = 0xEC, /* TBI ANLPAR */ + Taner = 0xF0, /* TBI ANER */ + Tesr = 0xF4, /* TBI ESR */ +}; + +enum { /* Cr */ + Txe = 0x00000001, /* Transmit Enable */ + Txd = 0x00000002, /* Transmit Disable */ + Rxe = 0x00000004, /* Receiver Enable */ + Rxd = 0x00000008, /* Receiver Disable */ + Txr = 0x00000010, /* Transmitter Reset */ + Rxr = 0x00000020, /* Receiver Reset */ + Swien = 0x00000080, /* Software Interrupt Enable */ + Rst = 0x00000100, /* Reset */ + TxpriSHFT = 9, /* Tx Priority Queue Select */ + TxpriMASK = 0x00001E00, + RxpriSHFT = 13, /* Rx Priority Queue Select */ + RxpriMASK = 0x0001E000, +}; + +enum { /* Configuration and Media Status */ + Bem = 0x00000001, /* Big Endian Mode */ + Ext125 = 0x00000002, /* External 125MHz reference Select */ + Bromdis = 0x00000004, /* Disable Boot ROM interface */ + Pesel = 0x00000008, /* Parity Error Detection Action */ + Exd = 0x00000010, /* Excessive Deferral Abort */ + Pow = 0x00000020, /* Program Out of Window Timer */ + Sb = 0x00000040, /* Single Back-off */ + Reqalg = 0x00000080, /* PCI Bus Request Algorithm */ + Extstsen = 0x00000100, /* Extended Status Enable */ + Phydis = 0x00000200, /* Disable PHY */ + Phyrst = 0x00000400, /* Reset PHY */ + M64addren = 0x00000800, /* Master 64-bit Addressing Enable */ + Data64en = 0x00001000, /* 64-bit Data Enable */ + Pci64det = 0x00002000, /* PCI 64-bit Bus Detected */ + T64addren = 0x00004000, /* Target 64-bit Addressing Enable */ + Mwidis = 0x00008000, /* MWI Disable */ + Mrmdis = 0x00010000, /* MRM Disable */ + Tmrtest = 0x00020000, /* Timer Test Mode */ + Spdstsien = 0x00040000, /* PHY Spdsts Interrupt Enable */ + Lnkstsien = 0x00080000, /* PHY Lnksts Interrupt Enable */ + Dupstsien = 0x00100000, /* PHY Dupsts Interrupt Enable */ + Mode1000 = 0x00400000, /* 1000Mb/s Mode Control */ + Tbien = 0x01000000, /* Ten-Bit Interface Enable */ + Dupsts = 0x10000000, /* Full Duplex Status */ + Spdsts100 = 0x20000000, /* SPEED100 Input Pin Status */ + Spdsts1000 = 0x40000000, /* SPEED1000 Input Pin Status */ + Lnksts = 0x80000000, /* Link Status */ +}; + +enum { /* MII/EEPROM Access */ + Eedi = 0x00000001, /* EEPROM Data In */ + Eedo = 0x00000002, /* EEPROM Data Out */ + Eeclk = 0x00000004, /* EEPROM Serial Clock */ + Eesel = 0x00000008, /* EEPROM Chip Select */ + Mdio = 0x00000010, /* MII Management Data */ + Mddir = 0x00000020, /* MII Management Direction */ + Mdc = 0x00000040, /* MII Management Clock */ +}; + +enum { /* Interrupts */ + Rxok = 0x00000001, /* Rx OK */ + Rxdesc = 0x00000002, /* Rx Descriptor */ + Rxerr = 0x00000004, /* Rx Packet Error */ + Rxearly = 0x00000008, /* Rx Early Threshold */ + Rxidle = 0x00000010, /* Rx Idle */ + Rxorn = 0x00000020, /* Rx Overrun */ + Txok = 0x00000040, /* Tx Packet OK */ + Txdesc = 0x00000080, /* Tx Descriptor */ + Txerr = 0x00000100, /* Tx Packet Error */ + Txidle = 0x00000200, /* Tx Idle */ + Txurn = 0x00000400, /* Tx Underrun */ + Mib = 0x00000800, /* MIB Service */ + Swi = 0x00001000, /* Software Interrupt */ + Pme = 0x00002000, /* Power Management Event */ + Phy = 0x00004000, /* PHY Interrupt */ + Hibint = 0x00008000, /* High Bits Interrupt Set */ + Rxsovr = 0x00010000, /* Rx Status FIFO Overrun */ + Rtabt = 0x00020000, /* Received Target Abort */ + Rmabt = 0x00040000, /* Received Master Abort */ + Sserr = 0x00080000, /* Signalled System Error */ + Dperr = 0x00100000, /* Detected Parity Error */ + Rxrcmp = 0x00200000, /* Receive Reset Complete */ + Txrcmp = 0x00400000, /* Transmit Reset Complete */ + Rxdesc0 = 0x00800000, /* Rx Descriptor for Priority Queue 0 */ + Rxdesc1 = 0x01000000, /* Rx Descriptor for Priority Queue 1 */ + Rxdesc2 = 0x02000000, /* Rx Descriptor for Priority Queue 2 */ + Rxdesc3 = 0x04000000, /* Rx Descriptor for Priority Queue 3 */ + Txdesc0 = 0x08000000, /* Tx Descriptor for Priority Queue 0 */ + Txdesc1 = 0x10000000, /* Tx Descriptor for Priority Queue 1 */ + Txdesc2 = 0x20000000, /* Tx Descriptor for Priority Queue 2 */ + Txdesc3 = 0x40000000, /* Tx Descriptor for Priority Queue 3 */ +}; + +enum { /* Interrupt Enable */ + Ien = 0x00000001, /* Interrupt Enable */ +}; + +enum { /* Interrupt Holdoff */ + IhSHFT = 0, /* Interrupt Holdoff */ + IhMASK = 0x000000FF, + Ihctl = 0x00000100, /* Interrupt Holdoff Control */ +}; + +enum { /* Transmit Configuration */ + TxdrthSHFT = 0, /* Tx Drain Threshold */ + TxdrthMASK = 0x000000FF, + FlthSHFT = 16, /* Tx Fill Threshold */ + FlthMASK = 0x0000FF00, + Brstdis = 0x00080000, /* 1000Mb/s Burst Disable */ + MxdmaSHFT = 20, /* Max Size per Tx DMA Burst */ + MxdmaMASK = 0x00700000, + Ecretryen = 0x00800000, /* Excessive Collision Retry Enable */ + Atp = 0x10000000, /* Automatic Transmit Padding */ + Mlb = 0x20000000, /* MAC Loopback */ + Hbi = 0x40000000, /* Heartbeat Ignore */ + Csi = 0x80000000, /* Carrier Sense Ignore */ +}; + +enum { /* Receive Configuration */ + RxdrthSHFT = 1, /* Rx Drain Threshold */ + RxdrthMASK = 0x0000003E, + Airl = 0x04000000, /* Accept In-Range Length Errored */ + Alp = 0x08000000, /* Accept Long Packets */ + Rxfd = 0x10000000, /* Receive Full Duplex */ + Stripcrc = 0x20000000, /* Strip CRC */ + Arp = 0x40000000, /* Accept Runt Packets */ + Aep = 0x80000000, /* Accept Errored Packets */ +}; + +enum { /* Priority Queueing Control */ + Txpqen = 0x00000001, /* Transmit Priority Queuing Enable */ + Txfairen = 0x00000002, /* Transmit Fairness Enable */ + RxpqenSHFT = 2, /* Receive Priority Queue Enable */ + RxpqenMASK = 0x0000000C, +}; + +enum { /* Pause Control/Status */ + PscntSHFT = 0, /* Pause Counter Value */ + PscntMASK = 0x0000FFFF, + Pstx = 0x00020000, /* Transmit Pause Frame */ + PsffloSHFT = 18, /* Rx Data FIFO Lo Threshold */ + PsffloMASK = 0x000C0000, + PsffhiSHFT = 20, /* Rx Data FIFO Hi Threshold */ + PsffhiMASK = 0x00300000, + PsstloSHFT = 22, /* Rx Stat FIFO Hi Threshold */ + PsstloMASK = 0x00C00000, + PssthiSHFT = 24, /* Rx Stat FIFO Hi Threshold */ + PssthiMASK = 0x03000000, + Psrcvd = 0x08000000, /* Pause Frame Received */ + Psact = 0x10000000, /* Pause Active */ + Psda = 0x20000000, /* Pause on Destination Address */ + Psmcast = 0x40000000, /* Pause on Multicast */ + Psen = 0x80000000, /* Pause Enable */ +}; + +enum { /* Receive Filter/Match Control */ + RfaddrSHFT = 0, /* Extended Register Address */ + RfaddrMASK = 0x000003FF, + Ulm = 0x00080000, /* U/L bit mask */ + Uhen = 0x00100000, /* Unicast Hash Enable */ + Mhen = 0x00200000, /* Multicast Hash Enable */ + Aarp = 0x00400000, /* Accept ARP Packets */ + ApatSHFT = 23, /* Accept on Pattern Match */ + ApatMASK = 0x07800000, + Apm = 0x08000000, /* Accept on Perfect Match */ + Aau = 0x10000000, /* Accept All Unicast */ + Aam = 0x20000000, /* Accept All Multicast */ + Aab = 0x40000000, /* Accept All Broadcast */ + Rfen = 0x80000000, /* Rx Filter Enable */ +}; + +enum { /* Receive Filter/Match Data */ + RfdataSHFT = 0, /* Receive Filter Data */ + RfdataMASK = 0x0000FFFF, + BmaskSHFT = 16, /* Byte Mask */ + BmaskMASK = 0x00030000, +}; + +enum { /* MIB Control */ + Wrn = 0x00000001, /* Warning Test Indicator */ + Frz = 0x00000002, /* Freeze All Counters */ + Aclr = 0x00000004, /* Clear All Counters */ + Mibs = 0x00000008, /* MIB Counter Strobe */ +}; + +enum { /* MIB Data */ + Nmibd = 11, /* Number of MIB Data Registers */ +}; + +enum { /* VLAN/IP Receive Control */ + Vtden = 0x00000001, /* VLAN Tag Detection Enable */ + Vtren = 0x00000002, /* VLAN Tag Removal Enable */ + Dvtf = 0x00000004, /* Discard VLAN Tagged Frames */ + Dutf = 0x00000008, /* Discard Untagged Frames */ + Ipen = 0x00000010, /* IP Checksum Enable */ + Ripe = 0x00000020, /* Reject IP Checksum Errors */ + Rtcpe = 0x00000040, /* Reject TCP Checksum Errors */ + Rudpe = 0x00000080, /* Reject UDP Checksum Errors */ +}; + +enum { /* VLAN/IP Transmit Control */ + Vgti = 0x00000001, /* VLAN Global Tag Insertion */ + Vppti = 0x00000002, /* VLAN Per-Packet Tag Insertion */ + Gchk = 0x00000004, /* Global Checksum Generation */ + Ppchk = 0x00000008, /* Per-Packet Checksum Generation */ +}; + +enum { /* VLAN Data */ + VtypeSHFT = 0, /* VLAN Type Field */ + VtypeMASK = 0x0000FFFF, + VtciSHFT = 16, /* VLAN Tag Control Information */ + VtciMASK = 0xFFFF0000, +}; + +enum { /* Clockrun Control/Status */ + Clkrunen = 0x00000001, /* CLKRUN Enable */ + Pmeen = 0x00000100, /* PME Enable */ + Pmests = 0x00008000, /* PME Status */ +}; + +typedef struct { + u32int link; /* Link to the next descriptor */ + u32int bufptr; /* pointer to data Buffer */ + int cmdsts; /* Command/Status */ + int extsts; /* optional Extended Status */ + + Block* bp; /* Block containing bufptr */ + u32int unused; /* pad to 64-bit */ +} Desc; + +enum { /* Common cmdsts bits */ + SizeMASK = 0x0000FFFF, /* Descriptor Byte Count */ + SizeSHFT = 0, + Ok = 0x08000000, /* Packet OK */ + Crc = 0x10000000, /* Suppress/Include CRC */ + Intr = 0x20000000, /* Interrupt on ownership transfer */ + More = 0x40000000, /* not last descriptor in a packet */ + Own = 0x80000000, /* Descriptor Ownership */ +}; + +enum { /* Transmit cmdsts bits */ + CcntMASK = 0x000F0000, /* Collision Count */ + CcntSHFT = 16, + Ec = 0x00100000, /* Excessive Collisions */ + Owc = 0x00200000, /* Out of Window Collision */ + Ed = 0x00400000, /* Excessive Deferral */ + Td = 0x00800000, /* Transmit Deferred */ + Crs = 0x01000000, /* Carrier Sense Lost */ + Tfu = 0x02000000, /* Transmit FIFO Underrun */ + Txa = 0x04000000, /* Transmit Abort */ +}; + +enum { /* Receive cmdsts bits */ + Irl = 0x00010000, /* In-Range Length Error */ + Lbp = 0x00020000, /* Loopback Packet */ + Fae = 0x00040000, /* Frame Alignment Error */ + Crce = 0x00080000, /* CRC Error */ + Ise = 0x00100000, /* Invalid Symbol Error */ + Runt = 0x00200000, /* Runt Packet Received */ + Long = 0x00400000, /* Too Long Packet Received */ + DestMASK = 0x01800000, /* Destination Class */ + DestSHFT = 23, + Rxo = 0x02000000, /* Receive Overrun */ + Rxa = 0x04000000, /* Receive Aborted */ +}; + +enum { /* extsts bits */ + EvtciMASK = 0x0000FFFF, /* VLAN Tag Control Information */ + EvtciSHFT = 0, + Vpkt = 0x00010000, /* VLAN Packet */ + Ippkt = 0x00020000, /* IP Packet */ + Iperr = 0x00040000, /* IP Checksum Error */ + Tcppkt = 0x00080000, /* TCP Packet */ + Tcperr = 0x00100000, /* TCP Checksum Error */ + Udppkt = 0x00200000, /* UDP Packet */ + Udperr = 0x00400000, /* UDP Checksum Error */ +}; + +enum { + Nrd = 256, + Nrb = 4*Nrd, + Rbsz = ROUNDUP(sizeof(Etherpkt)+8, 8), + Ntd = 128, +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int id; + + int eepromsz; /* address size in bits */ + ushort* eeprom; + + int* nic; + int cfg; + int imr; + + QLock alock; /* attach */ + Lock ilock; /* init */ + void* alloc; /* base of per-Ctlr allocated data */ + + Mii* mii; + + Lock rdlock; /* receive */ + Desc* rd; + int nrd; + int nrb; + int rdx; + int rxcfg; + + Lock tlock; /* transmit */ + Desc* td; + int ntd; + int tdh; + int tdt; + int ntq; + int txcfg; + + int rxidle; + + uint mibd[Nmibd]; + + int ec; + int owc; + int ed; + int crs; + int tfu; + int txa; +} Ctlr; + +#define csr32r(c, r) (*((c)->nic+((r)/4))) +#define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v)) + +static Ctlr* dp83820ctlrhead; +static Ctlr* dp83820ctlrtail; + +static Lock dp83820rblock; /* free receive Blocks */ +static Block* dp83820rbpool; + +static char* dp83820mibs[Nmibd] = { + "RXErroredPkts", + "RXFCSErrors", + "RXMsdPktErrors", + "RXFAErrors", + "RXSymbolErrors", + "RXFrameToLong", + "RXIRLErrors", + "RXBadOpcodes", + "RXPauseFrames", + "TXPauseFrames", + "TXSQEErrors", +}; + +static int +mdior(Ctlr* ctlr, int n) +{ + int data, i, mear, r; + + mear = csr32r(ctlr, Mear); + r = ~(Mdc|Mddir) & mear; + data = 0; + for(i = n-1; i >= 0; i--){ + if(csr32r(ctlr, Mear) & Mdio) + data |= (1<<i); + csr32w(ctlr, Mear, Mdc|r); + csr32w(ctlr, Mear, r); + } + csr32w(ctlr, Mear, mear); + + return data; +} + +static void +mdiow(Ctlr* ctlr, int bits, int n) +{ + int i, mear, r; + + mear = csr32r(ctlr, Mear); + r = Mddir|(~Mdc & mear); + for(i = n-1; i >= 0; i--){ + if(bits & (1<<i)) + r |= Mdio; + else + r &= ~Mdio; + csr32w(ctlr, Mear, r); + csr32w(ctlr, Mear, Mdc|r); + } + csr32w(ctlr, Mear, mear); +} + +static int +dp83820miimir(Mii* mii, int pa, int ra) +{ + int data; + Ctlr *ctlr; + + ctlr = mii->ctlr; + + /* + * MII Management Interface Read. + * + * Preamble; + * ST+OP+PA+RA; + * LT + 16 data bits. + */ + mdiow(ctlr, 0xFFFFFFFF, 32); + mdiow(ctlr, 0x1800|(pa<<5)|ra, 14); + data = mdior(ctlr, 18); + + if(data & 0x10000) + return -1; + + return data & 0xFFFF; +} + +static int +dp83820miimiw(Mii* mii, int pa, int ra, int data) +{ + Ctlr *ctlr; + + ctlr = mii->ctlr; + + /* + * MII Management Interface Write. + * + * Preamble; + * ST+OP+PA+RA+LT + 16 data bits; + * Z. + */ + mdiow(ctlr, 0xFFFFFFFF, 32); + data &= 0xFFFF; + data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16); + mdiow(ctlr, data, 32); + + return 0; +} + +static Block * +dp83820rballoc(Desc* desc) +{ + Block *bp; + + if(desc->bp == nil){ + ilock(&dp83820rblock); + if((bp = dp83820rbpool) == nil){ + iunlock(&dp83820rblock); + desc->bp = nil; + desc->cmdsts = Own; + return nil; + } + dp83820rbpool = bp->next; + bp->next = nil; + iunlock(&dp83820rblock); + + desc->bufptr = PCIWADDR(bp->rp); + desc->bp = bp; + } + else{ + bp = desc->bp; + bp->rp = bp->lim - Rbsz; + bp->wp = bp->rp; + } + + coherence(); + desc->cmdsts = Intr|Rbsz; + + return bp; +} + +static void +dp83820rbfree(Block *bp) +{ + bp->rp = bp->lim - Rbsz; + bp->wp = bp->rp; + + ilock(&dp83820rblock); + bp->next = dp83820rbpool; + dp83820rbpool = bp; + iunlock(&dp83820rblock); +} + +static void +dp83820halt(Ctlr* ctlr) +{ + int i, timeo; + + ilock(&ctlr->ilock); + csr32w(ctlr, Imr, 0); + csr32w(ctlr, Ier, 0); + csr32w(ctlr, Cr, Rxd|Txd); + for(timeo = 0; timeo < 1000; timeo++){ + if(!(csr32r(ctlr, Cr) & (Rxe|Txe))) + break; + microdelay(1); + } + csr32w(ctlr, Mibc, Frz); + iunlock(&ctlr->ilock); + + if(ctlr->rd != nil){ + for(i = 0; i < ctlr->nrd; i++){ + if(ctlr->rd[i].bp == nil) + continue; + freeb(ctlr->rd[i].bp); + ctlr->rd[i].bp = nil; + } + } + if(ctlr->td != nil){ + for(i = 0; i < ctlr->ntd; i++){ + if(ctlr->td[i].bp == nil) + continue; + freeb(ctlr->td[i].bp); + ctlr->td[i].bp = nil; + } + } +} + +static void +dp83820cfg(Ctlr* ctlr) +{ + int cfg; + + /* + * Don't know how to deal with a TBI yet. + */ + if(ctlr->mii == nil) + return; + + /* + * The polarity of these bits is at the mercy + * of the board designer. + * The correct answer for all speed and duplex questions + * should be to query the phy. + */ + cfg = csr32r(ctlr, Cfg); + if(!(cfg & Dupsts)){ + ctlr->rxcfg |= Rxfd; + ctlr->txcfg |= Csi|Hbi; + iprint("83820: full duplex, "); + } + else{ + ctlr->rxcfg &= ~Rxfd; + ctlr->txcfg &= ~(Csi|Hbi); + iprint("83820: half duplex, "); + } + csr32w(ctlr, Rxcfg, ctlr->rxcfg); + csr32w(ctlr, Txcfg, ctlr->txcfg); + + switch(cfg & (Spdsts1000|Spdsts100)){ + case Spdsts1000: /* 100Mbps */ + default: /* 10Mbps */ + ctlr->cfg &= ~Mode1000; + if((cfg & (Spdsts1000|Spdsts100)) == Spdsts1000) + iprint("100Mb/s\n"); + else + iprint("10Mb/s\n"); + break; + case Spdsts100: /* 1Gbps */ + ctlr->cfg |= Mode1000; + iprint("1Gb/s\n"); + break; + } + csr32w(ctlr, Cfg, ctlr->cfg); +} + +static void +dp83820init(Ether* edev) +{ + int i; + Ctlr *ctlr; + Desc *desc; + uchar *alloc; + + ctlr = edev->ctlr; + + dp83820halt(ctlr); + + /* + * Receiver + */ + alloc = (uchar*)ROUNDUP((ulong)ctlr->alloc, 8); + ctlr->rd = (Desc*)alloc; + alloc += ctlr->nrd*sizeof(Desc); + memset(ctlr->rd, 0, ctlr->nrd*sizeof(Desc)); + ctlr->rdx = 0; + for(i = 0; i < ctlr->nrd; i++){ + desc = &ctlr->rd[i]; + desc->link = PCIWADDR(&ctlr->rd[NEXT(i, ctlr->nrd)]); + if(dp83820rballoc(desc) == nil) + continue; + } + csr32w(ctlr, Rxdphi, 0); + csr32w(ctlr, Rxdp, PCIWADDR(ctlr->rd)); + + for(i = 0; i < Eaddrlen; i += 2){ + csr32w(ctlr, Rfcr, i); + csr32w(ctlr, Rfdr, (edev->ea[i+1]<<8)|edev->ea[i]); + } + csr32w(ctlr, Rfcr, Rfen|Aab|Aam|Apm); + + ctlr->rxcfg = Stripcrc|(((2*(ETHERMINTU+4))/8)<<RxdrthSHFT); + ctlr->imr |= Rxorn|Rxidle|Rxearly|Rxdesc|Rxok; + + /* + * Transmitter. + */ + ctlr->td = (Desc*)alloc; + memset(ctlr->td, 0, ctlr->ntd*sizeof(Desc)); + ctlr->tdh = ctlr->tdt = ctlr->ntq = 0; + for(i = 0; i < ctlr->ntd; i++){ + desc = &ctlr->td[i]; + desc->link = PCIWADDR(&ctlr->td[NEXT(i, ctlr->ntd)]); + } + csr32w(ctlr, Txdphi, 0); + csr32w(ctlr, Txdp, PCIWADDR(ctlr->td)); + + ctlr->txcfg = Atp|(((2*(ETHERMINTU+4))/32)<<FlthSHFT)|((4096/32)<<TxdrthSHFT); + ctlr->imr |= Txurn|Txidle|Txdesc|Txok; + + ilock(&ctlr->ilock); + + dp83820cfg(ctlr); + + csr32w(ctlr, Mibc, Aclr); + ctlr->imr |= Mib; + + csr32w(ctlr, Imr, ctlr->imr); + + /* try coalescing adjacent interrupts; use hold-off interval of 100µs */ + csr32w(ctlr, Ihr, Ihctl|(1<<IhSHFT)); + + csr32w(ctlr, Ier, Ien); + csr32w(ctlr, Cr, Rxe|Txe); + + iunlock(&ctlr->ilock); +} + +static void +dp83820attach(Ether* edev) +{ + Block *bp; + Ctlr *ctlr; + + ctlr = edev->ctlr; + qlock(&ctlr->alock); + if(ctlr->alloc != nil){ + qunlock(&ctlr->alock); + return; + } + + if(waserror()){ + if(ctlr->mii != nil){ + free(ctlr->mii); + ctlr->mii = nil; + } + if(ctlr->alloc != nil){ + free(ctlr->alloc); + ctlr->alloc = nil; + } + qunlock(&ctlr->alock); + nexterror(); + } + + if(!(ctlr->cfg & Tbien)){ + if((ctlr->mii = malloc(sizeof(Mii))) == nil) + error(Enomem); + ctlr->mii->ctlr = ctlr; + ctlr->mii->mir = dp83820miimir; + ctlr->mii->miw = dp83820miimiw; + if(mii(ctlr->mii, ~0) == 0) + error("no PHY"); + ctlr->cfg |= Dupstsien|Lnkstsien|Spdstsien; + ctlr->imr |= Phy; + } + + ctlr->nrd = Nrd; + ctlr->nrb = Nrb; + ctlr->ntd = Ntd; + ctlr->alloc = mallocz((ctlr->nrd+ctlr->ntd)*sizeof(Desc) + 7, 0); + if(ctlr->alloc == nil) + error(Enomem); + + for(ctlr->nrb = 0; ctlr->nrb < Nrb; ctlr->nrb++){ + if((bp = allocb(Rbsz)) == nil) + break; + bp->free = dp83820rbfree; + dp83820rbfree(bp); + } + + dp83820init(edev); + + qunlock(&ctlr->alock); + poperror(); +} + +static void +dp83820transmit(Ether* edev) +{ + Block *bp; + Ctlr *ctlr; + Desc *desc; + int cmdsts, r, x; + + ctlr = edev->ctlr; + + ilock(&ctlr->tlock); + + bp = nil; + for(x = ctlr->tdh; ctlr->ntq; x = NEXT(x, ctlr->ntd)){ + desc = &ctlr->td[x]; + if((cmdsts = desc->cmdsts) & Own) + break; + if(!(cmdsts & Ok)){ + if(cmdsts & Ec) + ctlr->ec++; + if(cmdsts & Owc) + ctlr->owc++; + if(cmdsts & Ed) + ctlr->ed++; + if(cmdsts & Crs) + ctlr->crs++; + if(cmdsts & Tfu) + ctlr->tfu++; + if(cmdsts & Txa) + ctlr->txa++; + edev->oerrs++; + } + desc->bp->next = bp; + bp = desc->bp; + desc->bp = nil; + + ctlr->ntq--; + } + ctlr->tdh = x; + if(bp != nil) + freeblist(bp); + + x = ctlr->tdt; + while(ctlr->ntq < (ctlr->ntd-1)){ + if((bp = qget(edev->oq)) == nil) + break; + + desc = &ctlr->td[x]; + desc->bufptr = PCIWADDR(bp->rp); + desc->bp = bp; + ctlr->ntq++; + coherence(); + desc->cmdsts = Own|Intr|BLEN(bp); + + x = NEXT(x, ctlr->ntd); + } + if(x != ctlr->tdt){ + ctlr->tdt = x; + r = csr32r(ctlr, Cr); + csr32w(ctlr, Cr, Txe|r); + } + + iunlock(&ctlr->tlock); +} + +static void +dp83820interrupt(Ureg*, void* arg) +{ + Block *bp; + Ctlr *ctlr; + Desc *desc; + Ether *edev; + int cmdsts, i, isr, r, x; + + edev = arg; + ctlr = edev->ctlr; + + for(isr = csr32r(ctlr, Isr); isr & ctlr->imr; isr = csr32r(ctlr, Isr)){ + if(isr & (Rxorn|Rxidle|Rxearly|Rxerr|Rxdesc|Rxok)){ + x = ctlr->rdx; + desc = &ctlr->rd[x]; + while((cmdsts = desc->cmdsts) & Own){ + if((cmdsts & Ok) && desc->bp != nil){ + bp = desc->bp; + desc->bp = nil; + bp->wp += cmdsts & SizeMASK; + etheriq(edev, bp, 1); + } + //else if(!(cmdsts & Ok)){ + // iprint("dp83820: rx %8.8uX:", cmdsts); + // bp = desc->bp; + // for(i = 0; i < 20; i++) + // iprint(" %2.2uX", bp->rp[i]); + // iprint("\n"); + //} + dp83820rballoc(desc); + + x = NEXT(x, ctlr->nrd); + desc = &ctlr->rd[x]; + } + ctlr->rdx = x; + + if(isr & Rxidle){ + r = csr32r(ctlr, Cr); + csr32w(ctlr, Cr, Rxe|r); + ctlr->rxidle++; + } + + isr &= ~(Rxorn|Rxidle|Rxearly|Rxerr|Rxdesc|Rxok); + } + + if(isr & Txurn){ + x = (ctlr->txcfg & TxdrthMASK)>>TxdrthSHFT; + r = (ctlr->txcfg & FlthMASK)>>FlthSHFT; + if(x < ((TxdrthMASK)>>TxdrthSHFT) + && x < (2048/32 - r)){ + ctlr->txcfg &= ~TxdrthMASK; + x++; + ctlr->txcfg |= x<<TxdrthSHFT; + csr32w(ctlr, Txcfg, ctlr->txcfg); + } + } + + if(isr & (Txurn|Txidle|Txdesc|Txok)){ + dp83820transmit(edev); + isr &= ~(Txurn|Txidle|Txdesc|Txok); + } + + if(isr & Mib){ + for(i = 0; i < Nmibd; i++){ + r = csr32r(ctlr, Mibd+(i*sizeof(int))); + ctlr->mibd[i] += r & 0xFFFF; + } + isr &= ~Mib; + } + + if((isr & Phy) && ctlr->mii != nil){ + ctlr->mii->mir(ctlr->mii, 1, Bmsr); + print("phy: cfg %8.8uX bmsr %4.4uX\n", + csr32r(ctlr, Cfg), + ctlr->mii->mir(ctlr->mii, 1, Bmsr)); + dp83820cfg(ctlr); + isr &= ~Phy; + } + if(isr) + iprint("dp83820: isr %8.8uX\n", isr); + } +} + +static long +dp83820ifstat(Ether* edev, void* a, long n, ulong offset) +{ + char *p; + Ctlr *ctlr; + int i, l, r; + + ctlr = edev->ctlr; + + edev->crcs = ctlr->mibd[Mibd+(1*sizeof(int))]; + edev->frames = ctlr->mibd[Mibd+(3*sizeof(int))]; + edev->buffs = ctlr->mibd[Mibd+(5*sizeof(int))]; + edev->overflows = ctlr->mibd[Mibd+(2*sizeof(int))]; + + if(n == 0) + return 0; + + p = malloc(READSTR); + l = 0; + for(i = 0; i < Nmibd; i++){ + r = csr32r(ctlr, Mibd+(i*sizeof(int))); + ctlr->mibd[i] += r & 0xFFFF; + if(ctlr->mibd[i] != 0 && dp83820mibs[i] != nil) + l += snprint(p+l, READSTR-l, "%s: %ud %ud\n", + dp83820mibs[i], ctlr->mibd[i], r); + } + l += snprint(p+l, READSTR-l, "rxidle %d\n", ctlr->rxidle); + l += snprint(p+l, READSTR-l, "ec %d\n", ctlr->ec); + l += snprint(p+l, READSTR-l, "owc %d\n", ctlr->owc); + l += snprint(p+l, READSTR-l, "ed %d\n", ctlr->ed); + l += snprint(p+l, READSTR-l, "crs %d\n", ctlr->crs); + l += snprint(p+l, READSTR-l, "tfu %d\n", ctlr->tfu); + l += snprint(p+l, READSTR-l, "txa %d\n", ctlr->txa); + + l += snprint(p+l, READSTR, "rom:"); + for(i = 0; i < 0x10; i++){ + if(i && ((i & 0x07) == 0)) + l += snprint(p+l, READSTR-l, "\n "); + l += snprint(p+l, READSTR-l, " %4.4uX", ctlr->eeprom[i]); + } + l += snprint(p+l, READSTR-l, "\n"); + + if(ctlr->mii != nil && ctlr->mii->curphy != nil){ + l += snprint(p+l, READSTR, "phy:"); + for(i = 0; i < NMiiPhyr; i++){ + if(i && ((i & 0x07) == 0)) + l += snprint(p+l, READSTR-l, "\n "); + r = miimir(ctlr->mii, i); + l += snprint(p+l, READSTR-l, " %4.4uX", r); + } + snprint(p+l, READSTR-l, "\n"); + } + + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +static void +dp83820promiscuous(void* arg, int on) +{ + USED(arg, on); +} + +/* multicast already on, don't need to do anything */ +static void +dp83820multicast(void*, uchar*, int) +{ +} + +static int +dp83820detach(Ctlr* ctlr) +{ + /* + * Soft reset the controller. + */ + csr32w(ctlr, Cr, Rst); + delay(1); + while(csr32r(ctlr, Cr) & Rst) + delay(1); + return 0; +} + +static void +dp83820shutdown(Ether* ether) +{ +print("dp83820shutdown\n"); + dp83820detach(ether->ctlr); +} + +static int +atc93c46r(Ctlr* ctlr, int address) +{ + int data, i, mear, r, size; + + /* + * Analog Technology, Inc. ATC93C46 + * or equivalent serial EEPROM. + */ + mear = csr32r(ctlr, Mear); + mear &= ~(Eesel|Eeclk|Eedo|Eedi); + r = Eesel|mear; + +reread: + csr32w(ctlr, Mear, r); + data = 0x06; + for(i = 3-1; i >= 0; i--){ + if(data & (1<<i)) + r |= Eedi; + else + r &= ~Eedi; + csr32w(ctlr, Mear, r); + csr32w(ctlr, Mear, Eeclk|r); + microdelay(1); + csr32w(ctlr, Mear, r); + microdelay(1); + } + + /* + * First time through must work out the EEPROM size. + */ + if((size = ctlr->eepromsz) == 0) + size = 8; + + for(size = size-1; size >= 0; size--){ + if(address & (1<<size)) + r |= Eedi; + else + r &= ~Eedi; + csr32w(ctlr, Mear, r); + microdelay(1); + csr32w(ctlr, Mear, Eeclk|r); + microdelay(1); + csr32w(ctlr, Mear, r); + microdelay(1); + if(!(csr32r(ctlr, Mear) & Eedo)) + break; + } + r &= ~Eedi; + + data = 0; + for(i = 16-1; i >= 0; i--){ + csr32w(ctlr, Mear, Eeclk|r); + microdelay(1); + if(csr32r(ctlr, Mear) & Eedo) + data |= (1<<i); + csr32w(ctlr, Mear, r); + microdelay(1); + } + + csr32w(ctlr, Mear, mear); + + if(ctlr->eepromsz == 0){ + ctlr->eepromsz = 8-size; + ctlr->eeprom = malloc((1<<ctlr->eepromsz)*sizeof(ushort)); + goto reread; + } + + return data; +} + +static int +dp83820reset(Ctlr* ctlr) +{ + int i, r; + unsigned char sum; + + /* + * Soft reset the controller; + * read the EEPROM to get the initial settings + * of the Cfg and Gpior bits which should be cleared by + * the reset. + */ + dp83820detach(ctlr); + + atc93c46r(ctlr, 0); + if(ctlr->eeprom == nil) { + print("dp83820reset: no eeprom\n"); + return -1; + } + sum = 0; + for(i = 0; i < 0x0E; i++){ + r = atc93c46r(ctlr, i); + ctlr->eeprom[i] = r; + sum += r; + sum += r>>8; + } + + if(sum != 0){ + print("dp83820reset: bad EEPROM checksum\n"); + return -1; + } + +#ifdef notdef + csr32w(ctlr, Gpior, ctlr->eeprom[4]); + + cfg = Extstsen|Exd; + r = csr32r(ctlr, Cfg); + if(ctlr->eeprom[5] & 0x0001) + cfg |= Ext125; + if(ctlr->eeprom[5] & 0x0002) + cfg |= M64addren; + if((ctlr->eeprom[5] & 0x0004) && (r & Pci64det)) + cfg |= Data64en; + if(ctlr->eeprom[5] & 0x0008) + cfg |= T64addren; + if(!(pcicfgr16(ctlr->pcidev, PciPCR) & 0x10)) + cfg |= Mwidis; + if(ctlr->eeprom[5] & 0x0020) + cfg |= Mrmdis; + if(ctlr->eeprom[5] & 0x0080) + cfg |= Mode1000; + if(ctlr->eeprom[5] & 0x0200) + cfg |= Tbien|Mode1000; + /* + * What about RO bits we might have destroyed with Rst? + * What about Exd, Tmrtest, Extstsen, Pintctl? + * Why does it think it has detected a 64-bit bus when + * it hasn't? + */ +#else + //r = csr32r(ctlr, Cfg); + //r &= ~(Mode1000|T64addren|Data64en|M64addren); + //csr32w(ctlr, Cfg, r); + //csr32w(ctlr, Cfg, 0x2000); +#endif /* notdef */ + ctlr->cfg = csr32r(ctlr, Cfg); +print("cfg %8.8uX pcicfg %8.8uX\n", ctlr->cfg, pcicfgr32(ctlr->pcidev, PciPCR)); + ctlr->cfg &= ~(T64addren|Data64en|M64addren); + csr32w(ctlr, Cfg, ctlr->cfg); + csr32w(ctlr, Mibc, Aclr|Frz); + + return 0; +} + +static void +dp83820pci(void) +{ + void *mem; + Pcidev *p; + Ctlr *ctlr; + + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != Pcibcnet || p->ccru != Pciscether) + continue; + + switch((p->did<<16)|p->vid){ + default: + continue; + case (0x0022<<16)|0x100B: /* DP83820 (Gig-NIC) */ + break; + } + + mem = vmap(p->mem[1].bar & ~0x0F, p->mem[1].size); + if(mem == 0){ + print("DP83820: can't map %8.8luX\n", p->mem[1].bar); + continue; + } + + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = p->mem[1].bar & ~0x0F; + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + + ctlr->nic = mem; + if(dp83820reset(ctlr)){ + free(ctlr); + continue; + } + pcisetbme(p); + + if(dp83820ctlrhead != nil) + dp83820ctlrtail->next = ctlr; + else + dp83820ctlrhead = ctlr; + dp83820ctlrtail = ctlr; + } +} + +static int +dp83820pnp(Ether* edev) +{ + int i; + Ctlr *ctlr; + uchar ea[Eaddrlen]; + + if(dp83820ctlrhead == nil) + dp83820pci(); + + /* + * Any adapter matches if no edev->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = dp83820ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(edev->port == 0 || edev->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = ctlr->pcidev->intl; + edev->tbdf = ctlr->pcidev->tbdf; + edev->mbps = 1000; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to + * loading the station address in the hardware. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, edev->ea, Eaddrlen) == 0) + for(i = 0; i < Eaddrlen/2; i++){ + edev->ea[2*i] = ctlr->eeprom[0x0C-i]; + edev->ea[2*i+1] = ctlr->eeprom[0x0C-i]>>8; + } + + edev->attach = dp83820attach; + edev->transmit = dp83820transmit; + edev->interrupt = dp83820interrupt; + edev->ifstat = dp83820ifstat; + + edev->arg = edev; + edev->promiscuous = dp83820promiscuous; + edev->multicast = dp83820multicast; + edev->shutdown = dp83820shutdown; + + return 0; +} + +void +etherdp83820link(void) +{ + addethercard("DP83820", dp83820pnp); +} diff --git a/os/pc/etherec2t.c b/os/pc/etherec2t.c index ffbec852..c7625060 100644 --- a/os/pc/etherec2t.c +++ b/os/pc/etherec2t.c @@ -31,6 +31,7 @@ static Ec2t ec2tpcmcia[] = { { "FA410TX", 1, }, /* Netgear FA410TX */ { "Network Everywhere", 0, }, /* Linksys NP10T 10BaseT Card */ { "10/100 Port Attached", 1, }, /* SMC 8040TX */ + { "8041TX-10/100-PC-Card-V2", 0 }, /* SMC 8041TX */ { "FA411", 0 }, /* Netgear FA411 PCMCIA */ { nil, 0, }, }; diff --git a/os/pc/etherelnk3.c b/os/pc/etherelnk3.c index e0be41f1..9c612b13 100644 --- a/os/pc/etherelnk3.c +++ b/os/pc/etherelnk3.c @@ -1485,15 +1485,15 @@ tcm59Xpci(void) break; case 0x5157: ctlr->eepromcmd = EepromRead8bRegister; - ctlr->cbfnpa = upamalloc(p->mem[2].bar, p->mem[2].size, 0); + ctlr->cbfnpa = p->mem[2].bar&~0x0F; + ctlr->cbfn = vmap(p->mem[2].bar&~0x0F, p->mem[2].size); break; case 0x6056: ctlr->eepromcmd = EepromReadOffRegister; - ctlr->cbfnpa = upamalloc(p->mem[2].bar, p->mem[2].size, 0); + ctlr->cbfnpa = p->mem[2].bar&~0x0F; + ctlr->cbfn = vmap(p->mem[2].bar&~0x0F, p->mem[2].size); break; } - if(ctlr->cbfnpa != 0) - ctlr->cbfn = KADDR(ctlr->cbfnpa); pcisetbme(p); } } @@ -1834,6 +1834,7 @@ etherelnk3reset(Ether* ether) case 0x9055: /* 3C905B-TX */ case 0x9200: /* 3C905C-TX */ case 0x9201: /* 3C920 */ + case 0x9805: /* 3C9805: 3C980-TX Python-T 10/100baseTX */ /*FALLTHROUGH*/ case 0x9000: /* 3C900-TPO */ case 0x9001: /* 3C900-COMBO */ @@ -1927,6 +1928,7 @@ etherelnk3reset(Ether* ether) case 0x9055: case 0x9200: case 0x9201: + case 0x9805: ctlr->xcvr = xcvrMii; resetctlr(ctlr); break; diff --git a/os/pc/etherga620.c b/os/pc/etherga620.c index 45e34faf..3d555be0 100644 --- a/os/pc/etherga620.c +++ b/os/pc/etherga620.c @@ -124,7 +124,11 @@ enum { /* Om */ enum { /* Lmw */ Lmwsz = 2*1024, /* Local Memory Window Size */ - Sr = 0x3800, /* Send Ring (accessed via Lmw) */ + /* + * legal values are 0x3800 iff Nsr is 128, 0x3000 iff Nsr is 256, + * or 0x2000 iff Nsr is 512. + */ + Sr = 0x2000, /* Send Ring (accessed via Lmw) */ }; enum { /* Link */ @@ -146,17 +150,17 @@ typedef struct Host64 { } Host64; typedef struct Ere { /* Event Ring Element */ - int event; /* (event<<24)|(code<<12)|index */ + int event; /* event<<24 | code<<12 | index */ int unused; } Ere; -typedef int Cmd; /* (cmd<<24)|(flags<<12)|index */ +typedef int Cmd; /* cmd<<24 | flags<<12 | index */ typedef struct Rbd { /* Receive Buffer Descriptor */ Host64 addr; - int indexlen; /* (ring-index<<16)|buffer-length */ + int indexlen; /* ring-index<<16 | buffer-length */ int flags; /* only lower 16-bits */ - int checksum; /* (ip<<16)|tcp/udp */ + int checksum; /* ip<<16 | tcp/udp */ int error; /* only upper 16-bits */ int reserved; void* opaque; /* passed to receive return ring */ @@ -164,7 +168,7 @@ typedef struct Rbd { /* Receive Buffer Descriptor */ typedef struct Sbd { /* Send Buffer Descriptor */ Host64 addr; - int lenflags; /* (len<<16)|flags */ + int lenflags; /* len<<16 | flags */ int reserved; } Sbd; @@ -192,7 +196,7 @@ enum { /* Buffer Error Flags */ typedef struct Rcb { /* Ring Control Block */ Host64 addr; /* points to the Rbd ring */ - int control; /* (max_len<<16)|flags */ + int control; /* max_len<<16 | flags */ int unused; } Rcb; @@ -223,13 +227,17 @@ typedef struct Gib { /* General Information Block */ Host64 rsp; /* Refresh Stats */ } Gib; +/* + * these sizes are all fixed in the card, + * except for Nsr, which has only 3 valid sizes. + */ enum { /* Host/NIC Interface ring sizes */ Ner = 256, /* event ring */ Ncr = 64, /* command ring */ - Nsr = 512, /* send ring */ + Nsr = 512, /* send ring: 128, 256 or 512 */ Nrsr = 512, /* receive standard ring */ Nrjr = 256, /* receive jumbo ring */ - Nrmr = 1024, /* receive mini ring */ + Nrmr = 1024, /* receive mini ring, optional */ Nrrr = 2048, /* receive return ring */ }; @@ -243,7 +251,7 @@ enum { }; typedef struct Ctlr Ctlr; -typedef struct Ctlr { +struct Ctlr { int port; Pcidev* pcidev; Ctlr* next; @@ -286,7 +294,7 @@ typedef struct Ctlr { int st; /* Stat Ticks */ int smcbd; /* Send Max. Coalesced BDs */ int rmcbd; /* Receive Max. Coalesced BDs */ -} Ctlr; +}; static Ctlr* ctlrhead; static Ctlr* ctlrtail; @@ -310,7 +318,7 @@ ga620command(Ctlr* ctlr, int cmd, int flags, int index) int cpi; cpi = csr32r(ctlr, Cpi); - csr32w(ctlr, Cr+(cpi*4), (cmd<<24)|(flags<<12)|index); + csr32w(ctlr, Cr+(cpi*4), cmd<<24 | flags<<12 | index); cpi = NEXT(cpi, Ncr); csr32w(ctlr, Cpi, cpi); } @@ -508,7 +516,7 @@ _ga620transmit(Ether* edev) sbd = &ctlr->sr[spi]; sethost64(&sbd->addr, bp->rp); - sbd->lenflags = (BLEN(bp)<<16)|Fend; + sbd->lenflags = BLEN(bp)<<16 | Fend; ctlr->srb[spi] = bp; work++; @@ -539,7 +547,7 @@ ga620replenish(Ctlr* ctlr) break; rbd = &ctlr->rsr[rspi]; sethost64(&rbd->addr, bp->rp); - rbd->indexlen = (rspi<<16)|(ETHERMAXTU+4); + rbd->indexlen = rspi<<16 | (ETHERMAXTU+4); rbd->flags = 0; rbd->opaque = bp; @@ -550,27 +558,49 @@ ga620replenish(Ctlr* ctlr) } static void -ga620event(Ctlr* ctlr, int eci, int epi) +ga620event(Ether *edev, int eci, int epi) { - int event; + unsigned event, code; + Ctlr *ctlr; + ctlr = edev->ctlr; while(eci != epi){ event = ctlr->er[eci].event; + code = (event >> 12) & ((1<<12)-1); switch(event>>24){ case 0x01: /* firmware operational */ + /* host stack (us) is up. 3rd arg of 2 means down. */ ga620command(ctlr, 0x01, 0x01, 0x00); + /* + * link negotiation: any speed is okay. + * 3rd arg of 1 selects gigabit only; 2 10/100 only. + */ ga620command(ctlr, 0x0B, 0x00, 0x00); -print("%8.8uX: %8.8uX\n", ctlr->port, event); + print("#l%d: ga620: port %8.8uX: firmware is up\n", + edev->ctlrno, ctlr->port); break; case 0x04: /* statistics updated */ break; case 0x06: /* link state changed */ -print("%8.8uX: %8.8uX %8.8uX %8.8uX\n", - ctlr->port, event, csr32r(ctlr, Gls), csr32r(ctlr, Fls)); + switch (code) { + case 1: + edev->mbps = 1000; + break; + case 2: + print("#l%d: link down\n", edev->ctlrno); + break; + case 3: + edev->mbps = 100; /* it's 10 or 100 */ + break; + } + if (code != 2) + print("#l%d: %dMbps link up\n", + edev->ctlrno, edev->mbps); break; case 0x07: /* event error */ default: - print("er[%d] = %8.8uX\n", eci, event); + print("#l%d: ga620: er[%d] = %8.8uX\n", edev->ctlrno, + eci, event); break; } eci = NEXT(eci, Ner); @@ -645,7 +675,7 @@ ga620interrupt(Ureg*, void* arg) csr = csr32r(ctlr, Eci); if(csr != ctlr->epi[0]){ - ga620event(ctlr, csr, ctlr->epi[0]); + ga620event(edev, csr, ctlr->epi[0]); work = 1; } @@ -714,9 +744,9 @@ ga620init(Ether* edev) /* * Load the MAC address. */ - ea = (edev->ea[0]<<8)|edev->ea[1]; + ea = edev->ea[0]<<8 | edev->ea[1]; csr32w(ctlr, Mac, ea); - ea = (edev->ea[2]<<24)|(edev->ea[3]<<16)|(edev->ea[4]<<8)|edev->ea[5]; + ea = edev->ea[2]<<24 | edev->ea[3]<<16 | edev->ea[4]<<8 | edev->ea[5]; csr32w(ctlr, Mac+4, ea); /* @@ -758,7 +788,7 @@ ga620init(Ether* edev) * memory it is accessed via the Local Memory Window; with a send * ring size of 128 the window covers the whole ring and then need * only be set once: - * ctlr->sr = KADDR(ctlr->port+Lmw); + * ctlr->sr = (uchar*)ctlr->nic+Lmw; * ga620lmw(ctlr, Sr, nil, sizeof(Sbd)*Nsr); * ctlr->gib->srcb.addr.lo = Sr; * There is nowhere in the Sbd to hold the Block* associated @@ -772,7 +802,7 @@ ga620init(Ether* edev) flags = HostRing; if(ctlr->coalupdateonly) flags |= CoalUpdateOnly; - ctlr->gib->srcb.control = (Nsr<<16)|flags; + ctlr->gib->srcb.control = Nsr<<16 | flags; sethost64(&ctlr->gib->scp, ctlr->sci); csr32w(ctlr, Spi, 0); ctlr->srb = malloc(sizeof(Block*)*Nsr); @@ -786,7 +816,7 @@ ga620init(Ether* edev) flags = TcpUdpCksum|NoPseudoHdrCksum; else flags = 0; - ctlr->gib->rsrcb.control = ((ETHERMAXTU+4)<<16)|flags; + ctlr->gib->rsrcb.control = (ETHERMAXTU+4)<<16 | flags; csr32w(ctlr, Rspi, 0); /* @@ -802,7 +832,7 @@ ga620init(Ether* edev) */ ctlr->rrr = malign(sizeof(Rbd)*Nrrr); sethost64(&ctlr->gib->rrrcb.addr, ctlr->rrr); - ctlr->gib->rrrcb.control = (Nrrr<<16)|0; + ctlr->gib->rrrcb.control = Nrrr<<16 | 0; sethost64(&ctlr->gib->rrrpp, ctlr->rrrpi); ctlr->rrrci = 0; @@ -1024,8 +1054,8 @@ ga620detach(Ctlr* ctlr) * wait for code to be loaded from serial EEPROM or flash; * make sure CPU A is halted. */ - csr32w(ctlr, Mhc, (Hr<<24)|Hr); - csr32w(ctlr, Mhc, ((Eews|Ci)<<24)|(Eews|Ci)); + csr32w(ctlr, Mhc, Hr<<24 | Hr); + csr32w(ctlr, Mhc, (Eews|Ci)<<24 | Eews|Ci); microdelay(1); for(timeo = 0; timeo < 500000; timeo++){ @@ -1056,7 +1086,7 @@ print("ga620shutdown\n"); static int ga620reset(Ctlr* ctlr) { - int cls, csr, i; + int cls, csr, i, r; if(ga620detach(ctlr) < 0) return -1; @@ -1095,8 +1125,9 @@ ga620reset(Ctlr* ctlr) * Snarf the MAC address from the serial EEPROM. */ for(i = 0; i < Eaddrlen; i++){ - if((ctlr->ea[i] = at24c32r(ctlr, 0x8E+i)) == -1) + if((r = at24c32r(ctlr, 0x8E+i)) == -1) return -1; + ctlr->ea[i] = r; } /* @@ -1114,7 +1145,7 @@ ga620reset(Ctlr* ctlr) static void ga620pci(void) { - int port; + void *mem; Pcidev *p; Ctlr *ctlr; @@ -1123,30 +1154,30 @@ ga620pci(void) if(p->ccrb != 0x02 || p->ccru != 0) continue; - switch((p->did<<16)|p->vid){ + switch(p->did<<16 | p->vid){ default: continue; - case (0x620A<<16)|0x1385: /* Netgear GA620 */ - case (0x630A<<16)|0x1385: /* Netgear GA620T */ - case (0x0001<<16)|0x12AE: /* Alteon Acenic fiber + case 0x620A<<16 | 0x1385: /* Netgear GA620 fiber */ + case 0x630A<<16 | 0x1385: /* Netgear GA620T copper */ + case 0x0001<<16 | 0x12AE: /* Alteon Acenic fiber * and DEC DEGPA-SA */ - case (0x0002<<16)|0x12AE: /* Alteon Acenic copper */ - case (0x0009<<16)|0x10A9: /* SGI Acenic */ + case 0x0002<<16 | 0x12AE: /* Alteon Acenic copper */ + case 0x0009<<16 | 0x10A9: /* SGI Acenic */ break; } - port = upamalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0); - if(port == 0){ + mem = vmap(p->mem[0].bar & ~0x0F, p->mem[0].size); + if(mem == 0){ print("ga620: can't map %8.8luX\n", p->mem[0].bar); continue; } ctlr = malloc(sizeof(Ctlr)); - ctlr->port = port; + ctlr->port = p->mem[0].bar & ~0x0F; ctlr->pcidev = p; - ctlr->id = (p->did<<16)|p->vid; + ctlr->id = p->did<<16 | p->vid; - ctlr->nic = KADDR(ctlr->port); + ctlr->nic = mem; if(ga620reset(ctlr)){ free(ctlr); continue; @@ -1160,6 +1191,25 @@ ga620pci(void) } } +static void +ga620promiscuous(void *arg, int on) +{ + Ether *ether = arg; + + /* 3rd arg: 1 enables, 2 disables */ + ga620command(ether->ctlr, 0xa, (on? 1: 2), 0); +} + +static void +ga620multicast(void *arg, uchar *addr, int on) +{ + Ether *ether = arg; + + USED(addr); + /* 3rd arg: 1 enables, 2 disables */ + ga620command(ether->ctlr, 0xe, (on? 1: 2), 0); +} + static int ga620pnp(Ether* edev) { @@ -1188,7 +1238,7 @@ ga620pnp(Ether* edev) edev->port = ctlr->port; edev->irq = ctlr->pcidev->intl; edev->tbdf = ctlr->pcidev->tbdf; - edev->mbps = 1000; + edev->mbps = 1000; /* placeholder */ /* * Check if the adapter's station address is to be overridden. @@ -1209,10 +1259,11 @@ ga620pnp(Ether* edev) edev->interrupt = ga620interrupt; edev->ifstat = ga620ifstat; edev->ctl = ga620ctl; - edev->shutdown = ga620shutdown; edev->arg = edev; - edev->promiscuous = nil; + edev->promiscuous = ga620promiscuous; + edev->multicast = ga620multicast; + edev->shutdown = ga620shutdown; return 0; } diff --git a/os/pc/etherigbe.c b/os/pc/etherigbe.c index a8c54267..10515097 100644 --- a/os/pc/etherigbe.c +++ b/os/pc/etherigbe.c @@ -29,16 +29,17 @@ #include "ethermii.h" enum { - i82542 = (0x1000<<16)|0x8086, - i82543gc = (0x1004<<16)|0x8086, - i82544ei = (0x1008<<16)|0x8086, - i82547ei = (0x1019<<16)|0x8086, - i82540em = (0x100E<<16)|0x8086, - i82540eplp = (0x101E<<16)|0x8086, + i82542 = (0x1000<<16)|0x8086, + i82543gc = (0x1004<<16)|0x8086, + i82544ei = (0x1008<<16)|0x8086, + i82547ei = (0x1019<<16)|0x8086, + i82540em = (0x100E<<16)|0x8086, + i82540eplp = (0x101E<<16)|0x8086, i82545gmc = (0x1026<<16)|0x8086, - i82547gi = (0x1075<<16)|0x8086, - i82541gi = (0x1076<<16)|0x8086, - i82546gb = (0x1079<<16)|0x8086, + i82547gi = (0x1075<<16)|0x8086, + i82541gi = (0x1076<<16)|0x8086, + i82541gi2 = (0x1077<<16)|0x8086, + i82546gb = (0x1079<<16)|0x8086, i82541pi = (0x107c<<16)|0x8086, i82546eb = (0x1010<<16)|0x8086, }; @@ -172,7 +173,9 @@ enum { /* Eecd */ Do = 0x00000008, /* Data Output from the EEPROM */ Areq = 0x00000040, /* EEPROM Access Request */ Agnt = 0x00000080, /* EEPROM Access Grant */ + Eepresent = 0x00000100, /* EEPROM Present */ Eesz256 = 0x00000200, /* EEPROM is 256 words not 64 */ + Eeszaddr = 0x00000400, /* EEPROM size for 8254[17] */ Spi = 0x00002000, /* EEPROM is SPI not Microwire */ }; @@ -442,6 +445,7 @@ typedef struct Ctlr { int port; Pcidev* pcidev; Ctlr* next; + Ether* edev; int active; int started; int id; @@ -508,7 +512,7 @@ static Ctlr* igbectlrhead; static Ctlr* igbectlrtail; static Lock igberblock; /* free receive Blocks */ -static Block* igberbpool; +static Block* igberbpool; /* receive Blocks for all igbe controllers */ static char* statistics[Nstatistics] = { "CRC Error", @@ -671,13 +675,13 @@ igbectl(Ether* edev, void* buf, long n) { int v; char *p; - Ctlr *ctlr; + Ctlr *ctlr; Cmdbuf *cb; Cmdtab *ct; if((ctlr = edev->ctlr) == nil) error(Enonexist); - + cb = parsecmd(buf, n); if(waserror()){ free(cb); @@ -736,7 +740,7 @@ igbemulticast(void* arg, uchar* addr, int on) ctlr->mta[x] |= 1<<bit; else ctlr->mta[x] &= ~(1<<bit); - + csr32w(ctlr, Mta+x*4, ctlr->mta[x]); } @@ -831,6 +835,7 @@ igbelproc(void* arg) case i82540eplp: case i82547gi: case i82541gi: + case i82541gi2: case i82541pi: break; } @@ -881,6 +886,7 @@ igbetxinit(Ctlr* ctlr) case i82540em: case i82540eplp: case i82541gi: + case i82541gi2: case i82541pi: case i82545gmc: case i82546gb: @@ -923,6 +929,7 @@ igbetxinit(Ctlr* ctlr) case i82546gb: case i82546eb: case i82541gi: + case i82541gi2: case i82541pi: r = csr32r(ctlr, Txdctl); r &= ~WthreshMASK; @@ -1006,7 +1013,8 @@ igbereplenish(Ctlr* ctlr) if(ctlr->rb[rdt] == nil){ bp = igberballoc(); if(bp == nil){ - iprint("no available buffers\n"); + iprint("#l%d: igbereplenish: no available buffers\n", + ctlr->edev->ctlrno); break; } ctlr->rb[rdt] = bp; @@ -1052,6 +1060,7 @@ igberxinit(Ctlr* ctlr) case i82540em: case i82540eplp: case i82541gi: + case i82541gi2: case i82541pi: case i82545gmc: case i82546gb: @@ -1100,10 +1109,10 @@ igberproc(void* arg) rdh = ctlr->rdh; for(;;){ rd = &ctlr->rdba[rdh]; - + if(!(rd->status & Rdd)) break; - + /* * Accept eop packets with no errors. * With no errors and the Ixsm bit set, @@ -1150,7 +1159,7 @@ igberproc(void* arg) rdh = NEXT(rdh, ctlr->nrd); } ctlr->rdh = rdh; - + if(ctlr->rdfree < ctlr->nrd/2 || (ctlr->rim & Rxdmt0)) igbereplenish(ctlr); } @@ -1164,6 +1173,7 @@ igbeattach(Ether* edev) char name[KNAMELEN]; ctlr = edev->ctlr; + ctlr->edev = edev; /* point back to Ether* */ qlock(&ctlr->alock); if(ctlr->alloc != nil){ qunlock(&ctlr->alock); @@ -1177,7 +1187,7 @@ igbeattach(Ether* edev) qunlock(&ctlr->alock); return; } - ctlr->rdba = (Rd*)ROUNDUP((ulong)ctlr->alloc, 128); + ctlr->rdba = (Rd*)ROUNDUP((uintptr)ctlr->alloc, 128); ctlr->tdba = (Td*)(ctlr->rdba+ctlr->nrd); ctlr->rb = malloc(ctlr->nrd*sizeof(Block*)); @@ -1448,6 +1458,7 @@ igbemii(Ctlr* ctlr) case i82540eplp: case i82547gi: case i82541gi: + case i82541gi2: case i82541pi: case i82545gmc: case i82546gb: @@ -1468,7 +1479,8 @@ igbemii(Ctlr* ctlr) ctlr->mii = nil; return -1; } - print("oui %X phyno %d\n", phy->oui, phy->phyno); + USED(phy); + // print("oui %X phyno %d\n", phy->oui, phy->phyno); /* * 8254X-specific PHY registers not in 802.3: @@ -1480,6 +1492,7 @@ igbemii(Ctlr* ctlr) switch(ctlr->id){ case i82547gi: case i82541gi: + case i82541gi2: case i82541pi: case i82545gmc: case i82546gb: @@ -1491,13 +1504,13 @@ igbemii(Ctlr* ctlr) r |= 0x0060; /* auto-crossover all speeds */ r |= 0x0002; /* polarity reversal enabled */ miimiw(ctlr->mii, 16, r); - + r = miimir(ctlr->mii, 20); r |= 0x0070; /* +25MHz clock */ r &= ~0x0F00; r |= 0x0100; /* 1x downshift */ miimiw(ctlr->mii, 20, r); - + miireset(ctlr->mii); p = 0; if(ctlr->txcw & TxcwPs) @@ -1578,7 +1591,7 @@ at93c46io(Ctlr* ctlr, char* op, int data) break; } csr32w(ctlr, Eecd, eecd); - microdelay(1); + microdelay(50); } if(loop >= 0) return -1; @@ -1597,11 +1610,10 @@ at93c46r(Ctlr* ctlr) print("igbe: SPI EEPROM access not implemented\n"); return 0; } - if(eecd & Eesz256) + if(eecd & (Eeszaddr|Eesz256)) bits = 8; else bits = 6; - snprint(rop, sizeof(rop), "S :%dDCc;", bits+3); sum = 0; @@ -1609,11 +1621,12 @@ at93c46r(Ctlr* ctlr) default: areq = 0; break; + case i82541gi: + case i82547gi: case i82540em: case i82540eplp: - case i82541gi: case i82541pi: - case i82547gi: + case i82541gi2: case i82545gmc: case i82546gb: case i82546eb: @@ -1630,6 +1643,7 @@ at93c46r(Ctlr* ctlr) } break; } + snprint(rop, sizeof(rop), "S :%dDCc;", bits+3); for(addr = 0; addr < 0x40; addr++){ /* @@ -1698,6 +1712,7 @@ igbedetach(Ctlr* ctlr) case i82541gi: case i82541pi: case i82547gi: + case i82541gi2: case i82545gmc: case i82546gb: case i82546eb: @@ -1751,6 +1766,8 @@ igbereset(Ctlr* ctlr) if ((ctlr->id == i82546gb || ctlr->id == i82546eb) && BUSFNO(ctlr->pcidev->tbdf) == 1) ctlr->eeprom[Ea+2] += 0x100; // second interface for(i = Ea; i < Eaddrlen/2; i++){ +if(i == Ea && ctlr->id == i82541gi && ctlr->eeprom[i] == 0xFFFF) + ctlr->eeprom[i] = 0xD000; ctlr->ra[2*i] = ctlr->eeprom[i]; ctlr->ra[2*i+1] = ctlr->eeprom[i]>>8; } @@ -1773,14 +1790,14 @@ igbereset(Ctlr* ctlr) /* * Just in case the Eerst didn't load the defaults - * (doesn't appear to fully on the 8243GC), do it manually. + * (doesn't appear to fully on the 82543GC), do it manually. */ - if (ctlr->id == i82543gc) { // 82543 + if (ctlr->id == i82543gc) { txcw = csr32r(ctlr, Txcw); txcw &= ~(TxcwAne|TxcwPauseMASK|TxcwFd); ctrl = csr32r(ctlr, Ctrl); ctrl &= ~(SwdpioloMASK|Frcspd|Ilos|Lrst|Fd); - + if(ctlr->eeprom[Icw1] & 0x0400){ ctrl |= Fd; txcw |= TxcwFd; @@ -1794,7 +1811,7 @@ igbereset(Ctlr* ctlr) swdpio = (ctlr->eeprom[Icw1] & 0x01E0)>>5; ctrl |= swdpio<<SwdpioloSHIFT; csr32w(ctlr, Ctrl, ctrl); - + ctrl = csr32r(ctlr, Ctrlext); ctrl &= ~(Ips|SwdpiohiMASK); swdpio = (ctlr->eeprom[Icw2] & 0x00F0)>>4; @@ -1802,7 +1819,7 @@ igbereset(Ctlr* ctlr) ctrl |= Ips; ctrl |= swdpio<<SwdpiohiSHIFT; csr32w(ctlr, Ctrlext, ctrl); - + if(ctlr->eeprom[Icw2] & 0x0800) txcw |= TxcwAne; pause = (ctlr->eeprom[Icw2] & 0x3000)>>12; @@ -1827,7 +1844,7 @@ igbereset(Ctlr* ctlr) csr32w(ctlr, Txcw, txcw); } - + /* * Flow control - values from the datasheet. */ @@ -1848,9 +1865,10 @@ igbereset(Ctlr* ctlr) static void igbepci(void) { - int port, cls; + int cls; Pcidev *p; Ctlr *ctlr; + void *mem; p = nil; while(p = pcimatch(p, 0, 0)){ @@ -1865,8 +1883,9 @@ igbepci(void) case i82547ei: case i82540em: case i82540eplp: - case i82547gi: case i82541gi: + case i82547gi: + case i82541gi2: case i82541pi: case i82545gmc: case i82546gb: @@ -1874,8 +1893,8 @@ igbepci(void) break; } - port = upamalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0); - if(port == 0){ + mem = vmap(p->mem[0].bar & ~0x0F, p->mem[0].size); + if(mem == nil){ print("igbe: can't map %8.8luX\n", p->mem[0].bar); continue; } @@ -1893,14 +1912,15 @@ igbepci(void) break; } ctlr = malloc(sizeof(Ctlr)); - ctlr->port = port; + ctlr->port = p->mem[0].bar & ~0x0F; ctlr->pcidev = p; ctlr->id = (p->did<<16)|p->vid; ctlr->cls = cls*4; - ctlr->nic = KADDR(ctlr->port); + ctlr->nic = mem; if(igbereset(ctlr)){ free(ctlr); + vunmap(mem, p->mem[0].size); continue; } pcisetbme(p); @@ -1966,3 +1986,4 @@ etherigbelink(void) addethercard("i82543", igbepnp); addethercard("igbe", igbepnp); } + diff --git a/os/pc/ethersmc.c b/os/pc/ethersmc.c index ddd3f729..65ebffca 100644 --- a/os/pc/ethersmc.c +++ b/os/pc/ethersmc.c @@ -713,6 +713,7 @@ reset(Ether* ether) if (ctlr == 0) { iofree(ether->port); pcmspecialclose(slot); + return -1; } ilock(ctlr); diff --git a/os/pc/ethervt6102.c b/os/pc/ethervt6102.c new file mode 100644 index 00000000..20d43a4e --- /dev/null +++ b/os/pc/ethervt6102.c @@ -0,0 +1,1025 @@ +/* + * VIA VT6102 Fast Ethernet Controller (Rhine II). + * To do: + * cache-line size alignments - done + * reduce tx interrupts + * use 2 descriptors on tx for alignment - done + * reorganise initialisation/shutdown/reset + * adjust Tx FIFO threshold on underflow - untested + * why does the link status never cause an interrupt? + * use the lproc as a periodic timer for stalls, etc. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" +#include "ethermii.h" + +enum { + Par0 = 0x00, /* Ethernet Address */ + Rcr = 0x06, /* Receive Configuration */ + Tcr = 0x07, /* Transmit Configuration */ + Cr = 0x08, /* Control */ + Isr = 0x0C, /* Interrupt Status */ + Imr = 0x0E, /* Interrupt Mask */ + Rxdaddr = 0x18, /* Current Rx Descriptor Address */ + Txdaddr = 0x1C, /* Current Tx Descriptor Address */ + Phyadr = 0x6C, /* Phy Address */ + Miisr = 0x6D, /* MII Status */ + Bcr0 = 0x6E, /* Bus Control */ + Bcr1 = 0x6F, + Miicr = 0x70, /* MII Control */ + Miiadr = 0x71, /* MII Address */ + Miidata = 0x72, /* MII Data */ + Eecsr = 0x74, /* EEPROM Control and Status */ +}; + +enum { /* Rcr */ + Sep = 0x01, /* Accept Error Packets */ + Ar = 0x02, /* Accept Small Packets */ + Am = 0x04, /* Accept Multicast */ + Ab = 0x08, /* Accept Broadcast */ + Prom = 0x10, /* Accept Physical Address Packets */ + RrftMASK = 0xE0, /* Receive FIFO Threshold */ + RrftSHIFT = 5, + Rrft64 = 0<<RrftSHIFT, + Rrft32 = 1<<RrftSHIFT, + Rrft128 = 2<<RrftSHIFT, + Rrft256 = 3<<RrftSHIFT, + Rrft512 = 4<<RrftSHIFT, + Rrft768 = 5<<RrftSHIFT, + Rrft1024 = 6<<RrftSHIFT, + RrftSAF = 7<<RrftSHIFT, +}; + +enum { /* Tcr */ + Lb0 = 0x02, /* Loopback Mode */ + Lb1 = 0x04, + Ofset = 0x08, /* Back-off Priority Selection */ + RtsfMASK = 0xE0, /* Transmit FIFO Threshold */ + RtsfSHIFT = 5, + Rtsf128 = 0<<RtsfSHIFT, + Rtsf256 = 1<<RtsfSHIFT, + Rtsf512 = 2<<RtsfSHIFT, + Rtsf1024 = 3<<RtsfSHIFT, + RtsfSAF = 7<<RtsfSHIFT, +}; + +enum { /* Cr */ + Init = 0x0001, /* INIT Process Begin */ + Strt = 0x0002, /* Start NIC */ + Stop = 0x0004, /* Stop NIC */ + Rxon = 0x0008, /* Turn on Receive Process */ + Txon = 0x0010, /* Turn on Transmit Process */ + Tdmd = 0x0020, /* Transmit Poll Demand */ + Rdmd = 0x0040, /* Receive Poll Demand */ + Eren = 0x0100, /* Early Receive Enable */ + Fdx = 0x0400, /* Set MAC to Full Duplex Mode */ + Dpoll = 0x0800, /* Disable Td/Rd Auto Polling */ + Tdmd1 = 0x2000, /* Transmit Poll Demand 1 */ + Rdmd1 = 0x4000, /* Receive Poll Demand 1 */ + Sfrst = 0x8000, /* Software Reset */ +}; + +enum { /* Isr/Imr */ + Prx = 0x0001, /* Received Packet Successfully */ + Ptx = 0x0002, /* Transmitted Packet Successfully */ + Rxe = 0x0004, /* Receive Error */ + Txe = 0x0008, /* Transmit Error */ + Tu = 0x0010, /* Transmit Buffer Underflow */ + Ru = 0x0020, /* Receive Buffer Link Error */ + Be = 0x0040, /* PCI Bus Error */ + Cnt = 0x0080, /* Counter Overflow */ + Eri = 0x0100, /* Early Receive Interrupt */ + Udfi = 0x0200, /* Tx FIFO Underflow */ + Ovfi = 0x0400, /* Receive FIFO Overflow */ + Pktrace = 0x0800, /* Hmmm... */ + Norbf = 0x1000, /* No Receive Buffers */ + Abti = 0x2000, /* Transmission Abort */ + Srci = 0x4000, /* Port State Change */ + Geni = 0x8000, /* General Purpose Interrupt */ +}; + +enum { /* Phyadr */ + PhyadMASK = 0x1F, /* PHY Address */ + PhyadSHIFT = 0, + Mfdc = 0x20, /* Accelerate MDC Speed */ + Mpo0 = 0x40, /* MII Polling Timer Interval */ + Mpo1 = 0x80, +}; + +enum { /* Bcr0 */ + DmaMASK = 0x07, /* DMA Length */ + DmaSHIFT = 0, + Dma32 = 0<<DmaSHIFT, + Dma64 = 1<<DmaSHIFT, + Dma128 = 2<<DmaSHIFT, + Dma256 = 3<<DmaSHIFT, + Dma512 = 4<<DmaSHIFT, + Dma1024 = 5<<DmaSHIFT, + DmaSAF = 7<<DmaSHIFT, + CrftMASK = 0x38, /* Rx FIFO Threshold */ + CrftSHIFT = 3, + Crft64 = 1<<CrftSHIFT, + Crft128 = 2<<CrftSHIFT, + Crft256 = 3<<CrftSHIFT, + Crft512 = 4<<CrftSHIFT, + Crft1024 = 5<<CrftSHIFT, + CrftSAF = 7<<CrftSHIFT, + Extled = 0x40, /* Extra LED Support Control */ + Med2 = 0x80, /* Medium Select Control */ +}; + +enum { /* Bcr1 */ + PotMASK = 0x07, /* Polling Timer Interval */ + PotSHIFT = 0, + CtftMASK = 0x38, /* Tx FIFO Threshold */ + CtftSHIFT = 3, + Ctft64 = 1<<CtftSHIFT, + Ctft128 = 2<<CtftSHIFT, + Ctft256 = 3<<CtftSHIFT, + Ctft512 = 4<<CtftSHIFT, + Ctft1024 = 5<<CtftSHIFT, + CtftSAF = 7<<CtftSHIFT, +}; + +enum { /* Miicr */ + Mdc = 0x01, /* Clock */ + Mdi = 0x02, /* Data In */ + Mdo = 0x04, /* Data Out */ + Mout = 0x08, /* Output Enable */ + Mdpm = 0x10, /* Direct Program Mode Enable */ + Wcmd = 0x20, /* Write Enable */ + Rcmd = 0x40, /* Read Enable */ + Mauto = 0x80, /* Auto Polling Enable */ +}; + +enum { /* Miiadr */ + MadMASK = 0x1F, /* MII Port Address */ + MadSHIFT = 0, + Mdone = 0x20, /* Accelerate MDC Speed */ + Msrcen = 0x40, /* MII Polling Timer Interval */ + Midle = 0x80, +}; + +enum { /* Eecsr */ + Edo = 0x01, /* Data Out */ + Edi = 0x02, /* Data In */ + Eck = 0x04, /* Clock */ + Ecs = 0x08, /* Chip Select */ + Dpm = 0x10, /* Direct Program Mode Enable */ + Autold = 0x20, /* Dynamic Reload */ + Embp = 0x40, /* Embedded Program Enable */ + Eepr = 0x80, /* Programmed */ +}; + +/* + * Ring descriptor. The space allocated for each + * of these will be rounded up to a cache-line boundary. + * The first 4 elements are known to the hardware. + */ +typedef struct Ds Ds; +typedef struct Ds { + uint status; + uint control; + uint addr; + uint branch; + + Block* bp; + void* bounce; + Ds* next; + Ds* prev; +} Ds; + +enum { /* Rx Ds status */ + Rerr = 0x00000001, /* Receiver Error */ + Crc = 0x00000002, /* CRC Error */ + Fae = 0x00000004, /* Frame Alignment Error */ + Fov = 0x00000008, /* FIFO Overflow */ + Long = 0x00000010, /* A Long Packet */ + Runt = 0x00000020, /* A Runt Packet */ + Rxserr = 0x00000040, /* System Error */ + Buff = 0x00000080, /* Buffer Underflow Error */ + Rxedp = 0x00000100, /* End of Packet Buffer */ + Rxstp = 0x00000200, /* Packet Start */ + Chn = 0x00000400, /* Chain Buffer */ + Phy = 0x00000800, /* Physical Address Packet */ + Bar = 0x00001000, /* Broadcast Packet */ + Mar = 0x00002000, /* Multicast Packet */ + Rxok = 0x00008000, /* Packet Received Successfully */ + LengthMASK = 0x07FF0000, /* Received Packet Length */ + LengthSHIFT = 16, + + Own = 0x80000000, /* Descriptor Owned by NIC */ +}; + +enum { /* Tx Ds status */ + NcrMASK = 0x0000000F, /* Collision Retry Count */ + NcrSHIFT = 0, + Cols = 0x00000010, /* Experienced Collisions */ + Cdh = 0x00000080, /* CD Heartbeat */ + Abt = 0x00000100, /* Aborted after Excessive Collisions */ + Owc = 0x00000200, /* Out of Window Collision Seen */ + Crs = 0x00000400, /* Carrier Sense Lost */ + Udf = 0x00000800, /* FIFO Underflow */ + Tbuff = 0x00001000, /* Invalid Td */ + Txserr = 0x00002000, /* System Error */ + Terr = 0x00008000, /* Excessive Collisions */ +}; + +enum { /* Tx Ds control */ + TbsMASK = 0x000007FF, /* Tx Buffer Size */ + TbsSHIFT = 0, + Chain = 0x00008000, /* Chain Buffer */ + Crcdisable = 0x00010000, /* Disable CRC generation */ + Stp = 0x00200000, /* Start of Packet */ + Edp = 0x00400000, /* End of Packet */ + Ic = 0x00800000, /* Assert Interrupt Immediately */ +}; + +enum { + Nrd = 64, + Ntd = 64, + Rdbsz = ROUNDUP(ETHERMAXTU+4, 4), + + Nrxstats = 8, + Ntxstats = 9, + + Txcopy = 128, +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int id; + uchar par[Eaddrlen]; + + QLock alock; /* attach */ + void* alloc; /* receive/transmit descriptors */ + int cls; /* alignment */ + int nrd; + int ntd; + + Ds* rd; + Ds* rdh; + + Lock tlock; + Ds* td; + Ds* tdh; + Ds* tdt; + int tdused; + + Lock clock; /* */ + int cr; + int imr; + int tft; /* Tx threshold */ + + Mii* mii; + Rendez lrendez; + int lwakeup; + + uint rxstats[Nrxstats]; /* statistics */ + uint txstats[Ntxstats]; + uint intr; + uint lintr; + uint lsleep; + uint rintr; + uint tintr; + uint taligned; + uint tsplit; + uint tcopied; + uint txdw; +} Ctlr; + +static Ctlr* vt6102ctlrhead; +static Ctlr* vt6102ctlrtail; + +#define csr8r(c, r) (inb((c)->port+(r))) +#define csr16r(c, r) (ins((c)->port+(r))) +#define csr32r(c, r) (inl((c)->port+(r))) +#define csr8w(c, r, b) (outb((c)->port+(r), (int)(b))) +#define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w))) +#define csr32w(c, r, w) (outl((c)->port+(r), (ulong)(w))) + +static char* rxstats[Nrxstats] = { + "Receiver Error", + "CRC Error", + "Frame Alignment Error", + "FIFO Overflow", + "Long Packet", + "Runt Packet", + "System Error", + "Buffer Underflow Error", +}; +static char* txstats[Ntxstats] = { + "Aborted after Excessive Collisions", + "Out of Window Collision Seen", + "Carrier Sense Lost", + "FIFO Underflow", + "Invalid Td", + "System Error", + nil, + "Excessive Collisions", +}; + +static long +vt6102ifstat(Ether* edev, void* a, long n, ulong offset) +{ + char *p; + Ctlr *ctlr; + int i, l, r; + + ctlr = edev->ctlr; + + p = malloc(2*READSTR); + l = 0; + for(i = 0; i < Nrxstats; i++){ + l += snprint(p+l, 2*READSTR-l, "%s: %ud\n", + rxstats[i], ctlr->rxstats[i]); + } + for(i = 0; i < Ntxstats; i++){ + if(txstats[i] == nil) + continue; + l += snprint(p+l, 2*READSTR-l, "%s: %ud\n", + txstats[i], ctlr->txstats[i]); + } + l += snprint(p+l, 2*READSTR-l, "cls: %ud\n", ctlr->cls); + l += snprint(p+l, 2*READSTR-l, "intr: %ud\n", ctlr->intr); + l += snprint(p+l, 2*READSTR-l, "lintr: %ud\n", ctlr->lintr); + l += snprint(p+l, 2*READSTR-l, "lsleep: %ud\n", ctlr->lsleep); + l += snprint(p+l, 2*READSTR-l, "rintr: %ud\n", ctlr->rintr); + l += snprint(p+l, 2*READSTR-l, "tintr: %ud\n", ctlr->tintr); + l += snprint(p+l, 2*READSTR-l, "taligned: %ud\n", ctlr->taligned); + l += snprint(p+l, 2*READSTR-l, "tsplit: %ud\n", ctlr->tsplit); + l += snprint(p+l, 2*READSTR-l, "tcopied: %ud\n", ctlr->tcopied); + l += snprint(p+l, 2*READSTR-l, "txdw: %ud\n", ctlr->txdw); + l += snprint(p+l, 2*READSTR-l, "tft: %ud\n", ctlr->tft); + + if(ctlr->mii != nil && ctlr->mii->curphy != nil){ + l += snprint(p+l, 2*READSTR, "phy: "); + for(i = 0; i < NMiiPhyr; i++){ + if(i && ((i & 0x07) == 0)) + l += snprint(p+l, 2*READSTR-l, "\n "); + r = miimir(ctlr->mii, i); + l += snprint(p+l, 2*READSTR-l, " %4.4uX", r); + } + snprint(p+l, 2*READSTR-l, "\n"); + } + snprint(p+l, 2*READSTR-l, "\n"); + + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +static void +vt6102promiscuous(void* arg, int on) +{ + int rcr; + Ctlr *ctlr; + Ether *edev; + + edev = arg; + ctlr = edev->ctlr; + rcr = csr8r(ctlr, Rcr); + if(on) + rcr |= Prom; + else + rcr &= ~Prom; + csr8w(ctlr, Rcr, rcr); +} + +static void +vt6102multicast(void* arg, uchar* addr, int on) +{ + /* + * For now Am is set in Rcr. + * Will need to interlock with promiscuous + * when this gets filled in. + */ + USED(arg, addr, on); +} + +static int +vt6102wakeup(void* v) +{ + return *((int*)v) != 0; +} + +static void +vt6102imr(Ctlr* ctlr, int imr) +{ + ilock(&ctlr->clock); + ctlr->imr |= imr; + csr16w(ctlr, Imr, ctlr->imr); + iunlock(&ctlr->clock); +} + +static void +vt6102lproc(void* arg) +{ + Ctlr *ctlr; + Ether *edev; + MiiPhy *phy; + + edev = arg; + ctlr = edev->ctlr; + for(;;){ + if(ctlr->mii == nil || ctlr->mii->curphy == nil) + break; + if(miistatus(ctlr->mii) < 0) + goto enable; + + phy = ctlr->mii->curphy; + ilock(&ctlr->clock); + if(phy->fd) + ctlr->cr |= Fdx; + else + ctlr->cr &= ~Fdx; + csr16w(ctlr, Cr, ctlr->cr); + iunlock(&ctlr->clock); +enable: + ctlr->lwakeup = 0; + vt6102imr(ctlr, Srci); + + ctlr->lsleep++; + sleep(&ctlr->lrendez, vt6102wakeup, &ctlr->lwakeup); + + } + pexit("vt6102lproc: done", 1); +} + +static void +vt6102attach(Ether* edev) +{ + int i; + Ctlr *ctlr; + Ds *ds, *prev; + uchar *alloc, *bounce; + char name[KNAMELEN]; + + ctlr = edev->ctlr; + qlock(&ctlr->alock); + if(ctlr->alloc != nil){ + qunlock(&ctlr->alock); + return; + } + + /* + * Descriptor and bounce-buffer space. + * Must all be aligned on a 4-byte boundary, + * but try to align on cache-lines. + */ + ctlr->nrd = Nrd; + ctlr->ntd = Ntd; + alloc = malloc((ctlr->nrd+ctlr->ntd)*ctlr->cls + ctlr->ntd*Txcopy + ctlr->cls-1); + if(alloc == nil){ + qunlock(&ctlr->alock); + return; + } + ctlr->alloc = alloc; + alloc = (uchar*)ROUNDUP((ulong)alloc, ctlr->cls); + + ctlr->rd = (Ds*)alloc; + + if(waserror()){ + ds = ctlr->rd; + for(i = 0; i < ctlr->nrd; i++){ + if(ds->bp != nil){ + freeb(ds->bp); + ds->bp = nil; + } + if((ds = ds->next) == nil) + break; + } + free(ctlr->alloc); + ctlr->alloc = nil; + qunlock(&ctlr->alock); + nexterror(); + } + + prev = ctlr->rd + ctlr->nrd-1; + for(i = 0; i < ctlr->nrd; i++){ + ds = (Ds*)alloc; + alloc += ctlr->cls; + + ds->control = Rdbsz; + ds->branch = PCIWADDR(alloc); + + ds->bp = iallocb(Rdbsz+3); + if(ds->bp == nil) + error("vt6102: can't allocate receive ring\n"); + ds->bp->rp = (uchar*)ROUNDUP((ulong)ds->bp->rp, 4); + ds->addr = PCIWADDR(ds->bp->rp); + + ds->next = (Ds*)alloc; + ds->prev = prev; + prev = ds; + + ds->status = Own; + } + prev->branch = 0; + prev->next = ctlr->rd; + prev->status = 0; + ctlr->rdh = ctlr->rd; + + ctlr->td = (Ds*)alloc; + prev = ctlr->td + ctlr->ntd-1; + bounce = alloc + ctlr->ntd*ctlr->cls; + for(i = 0; i < ctlr->ntd; i++){ + ds = (Ds*)alloc; + alloc += ctlr->cls; + + ds->bounce = bounce; + bounce += Txcopy; + ds->next = (Ds*)alloc; + ds->prev = prev; + prev = ds; + } + prev->next = ctlr->td; + ctlr->tdh = ctlr->tdt = ctlr->td; + ctlr->tdused = 0; + + ctlr->cr = Dpoll|Rdmd|Txon|Rxon|Strt; + /*Srci|Abti|Norbf|Pktrace|Ovfi|Udfi|Be|Ru|Tu|Txe|Rxe|Ptx|Prx*/ + ctlr->imr = Abti|Norbf|Pktrace|Ovfi|Udfi|Be|Ru|Tu|Txe|Rxe|Ptx|Prx; + + ilock(&ctlr->clock); + csr32w(ctlr, Rxdaddr, PCIWADDR(ctlr->rd)); + csr32w(ctlr, Txdaddr, PCIWADDR(ctlr->td)); + csr16w(ctlr, Isr, ~0); + csr16w(ctlr, Imr, ctlr->imr); + csr16w(ctlr, Cr, ctlr->cr); + iunlock(&ctlr->clock); + + snprint(name, KNAMELEN, "#l%dlproc", edev->ctlrno); + kproc(name, vt6102lproc, edev, 0); + + qunlock(&ctlr->alock); + poperror(); +} + +static void +vt6102transmit(Ether* edev) +{ + Block *bp; + Ctlr *ctlr; + Ds *ds, *next; + int control, i, o, prefix, size, tdused, timeo; + + ctlr = edev->ctlr; + + ilock(&ctlr->tlock); + + /* + * Free any completed packets + */ + ds = ctlr->tdh; + for(tdused = ctlr->tdused; tdused > 0; tdused--){ + /* + * For some errors the chip will turn the Tx engine + * off. Wait for that to happen. + * Could reset and re-init the chip here if it doesn't + * play fair. + * To do: adjust Tx FIFO threshold on underflow. + */ + if(ds->status & (Abt|Tbuff|Udf)){ + for(timeo = 0; timeo < 1000; timeo++){ + if(!(csr16r(ctlr, Cr) & Txon)) + break; + microdelay(1); + } + ds->status = Own; + csr32w(ctlr, Txdaddr, PCIWADDR(ds)); + } + + if(ds->status & Own) + break; + ds->addr = 0; + ds->branch = 0; + + if(ds->bp != nil){ + freeb(ds->bp); + ds->bp = nil; + } + for(i = 0; i < Ntxstats-1; i++){ + if(ds->status & (1<<i)) + ctlr->txstats[i]++; + } + ctlr->txstats[i] += (ds->status & NcrMASK)>>NcrSHIFT; + + ds = ds->next; + } + ctlr->tdh = ds; + + /* + * Try to fill the ring back up. + */ + ds = ctlr->tdt; + while(tdused < ctlr->ntd-2){ + if((bp = qget(edev->oq)) == nil) + break; + tdused++; + + size = BLEN(bp); + prefix = 0; + + if(o = (((int)bp->rp) & 0x03)){ + prefix = Txcopy-o; + if(prefix > size) + prefix = size; + memmove(ds->bounce, bp->rp, prefix); + ds->addr = PCIWADDR(ds->bounce); + bp->rp += prefix; + size -= prefix; + } + + next = ds->next; + ds->branch = PCIWADDR(ds->next); + + if(size){ + if(prefix){ + next->bp = bp; + next->addr = PCIWADDR(bp->rp); + next->branch = PCIWADDR(next->next); + next->control = Edp|Chain|((size<<TbsSHIFT) & TbsMASK); + + control = Stp|Chain|((prefix<<TbsSHIFT) & TbsMASK); + + next = next->next; + tdused++; + ctlr->tsplit++; + } + else{ + ds->bp = bp; + ds->addr = PCIWADDR(bp->rp); + control = Edp|Stp|((size<<TbsSHIFT) & TbsMASK); + ctlr->taligned++; + } + } + else{ + freeb(bp); + control = Edp|Stp|((prefix<<TbsSHIFT) & TbsMASK); + ctlr->tcopied++; + } + + ds->control = control; + if(tdused >= ctlr->ntd-2){ + ds->control |= Ic; + ctlr->txdw++; + } + coherence(); + ds->status = Own; + + ds = next; + } + ctlr->tdt = ds; + ctlr->tdused = tdused; + if(ctlr->tdused) + csr16w(ctlr, Cr, Tdmd|ctlr->cr); + + iunlock(&ctlr->tlock); +} + +static void +vt6102receive(Ether* edev) +{ + Ds *ds; + Block *bp; + Ctlr *ctlr; + int i, len; + + ctlr = edev->ctlr; + + ds = ctlr->rdh; + while(!(ds->status & Own) && ds->status != 0){ + if(ds->status & Rerr){ + for(i = 0; i < Nrxstats; i++){ + if(ds->status & (1<<i)) + ctlr->rxstats[i]++; + } + } + else if(bp = iallocb(Rdbsz+3)){ + len = ((ds->status & LengthMASK)>>LengthSHIFT)-4; + ds->bp->wp = ds->bp->rp+len; + etheriq(edev, ds->bp, 1); + bp->rp = (uchar*)ROUNDUP((ulong)bp->rp, 4); + ds->addr = PCIWADDR(bp->rp); + ds->bp = bp; + } + ds->control = Rdbsz; + ds->branch = 0; + ds->status = 0; + + ds->prev->branch = PCIWADDR(ds); + coherence(); + ds->prev->status = Own; + + ds = ds->next; + } + ctlr->rdh = ds; + + csr16w(ctlr, Cr, ctlr->cr); +} + +static void +vt6102interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Ether *edev; + int imr, isr, r, timeo; + + edev = arg; + ctlr = edev->ctlr; + + ilock(&ctlr->clock); + csr16w(ctlr, Imr, 0); + imr = ctlr->imr; + ctlr->intr++; + for(;;){ + if((isr = csr16r(ctlr, Isr)) != 0) + csr16w(ctlr, Isr, isr); + if((isr & ctlr->imr) == 0) + break; + + if(isr & Srci){ + imr &= ~Srci; + ctlr->lwakeup = isr & Srci; + wakeup(&ctlr->lrendez); + isr &= ~Srci; + ctlr->lintr++; + } + if(isr & (Norbf|Pktrace|Ovfi|Ru|Rxe|Prx)){ + vt6102receive(edev); + isr &= ~(Norbf|Pktrace|Ovfi|Ru|Rxe|Prx); + ctlr->rintr++; + } + if(isr & (Abti|Udfi|Tu|Txe|Ptx)){ + if(isr & (Abti|Udfi|Tu)){ + for(timeo = 0; timeo < 1000; timeo++){ + if(!(csr16r(ctlr, Cr) & Txon)) + break; + microdelay(1); + } + + if((isr & Udfi) && ctlr->tft < CtftSAF){ + ctlr->tft += 1<<CtftSHIFT; + r = csr8r(ctlr, Bcr1) & ~CtftMASK; + csr8w(ctlr, Bcr1, r|ctlr->tft); + } + } + vt6102transmit(edev); + isr &= ~(Abti|Udfi|Tu|Txe|Ptx); + ctlr->tintr++; + } + if(isr) + panic("vt6102: isr %4.4uX\n", isr); + } + ctlr->imr = imr; + csr16w(ctlr, Imr, ctlr->imr); + iunlock(&ctlr->clock); +} + +static int +vt6102miimicmd(Mii* mii, int pa, int ra, int cmd, int data) +{ + Ctlr *ctlr; + int r, timeo; + + ctlr = mii->ctlr; + + csr8w(ctlr, Miicr, 0); + r = csr8r(ctlr, Phyadr); + csr8w(ctlr, Phyadr, (r & ~PhyadMASK)|pa); + csr8w(ctlr, Phyadr, pa); + csr8w(ctlr, Miiadr, ra); + if(cmd == Wcmd) + csr16w(ctlr, Miidata, data); + csr8w(ctlr, Miicr, cmd); + + for(timeo = 0; timeo < 10000; timeo++){ + if(!(csr8r(ctlr, Miicr) & cmd)) + break; + microdelay(1); + } + if(timeo >= 10000) + return -1; + + if(cmd == Wcmd) + return 0; + return csr16r(ctlr, Miidata); +} + +static int +vt6102miimir(Mii* mii, int pa, int ra) +{ + return vt6102miimicmd(mii, pa, ra, Rcmd, 0); +} + +static int +vt6102miimiw(Mii* mii, int pa, int ra, int data) +{ + return vt6102miimicmd(mii, pa, ra, Wcmd, data); +} + +static int +vt6102detach(Ctlr* ctlr) +{ + int timeo; + + /* + * Soft reset the controller. + */ + csr16w(ctlr, Cr, Sfrst); + for(timeo = 0; timeo < 10000; timeo++){ + if(!(csr16r(ctlr, Cr) & Sfrst)) + break; + microdelay(1); + } + if(timeo >= 1000) + return -1; + + return 0; +} + +static int +vt6102reset(Ctlr* ctlr) +{ + MiiPhy *phy; + int i, r, timeo; + + if(vt6102detach(ctlr) < 0) + return -1; + + /* + * Load the MAC address into the PAR[01] + * registers. + */ + r = csr8r(ctlr, Eecsr); + csr8w(ctlr, Eecsr, Autold|r); + for(timeo = 0; timeo < 100; timeo++){ + if(!(csr8r(ctlr, Cr) & Autold)) + break; + microdelay(1); + } + if(timeo >= 100) + return -1; + + for(i = 0; i < Eaddrlen; i++) + ctlr->par[i] = csr8r(ctlr, Par0+i); + + /* + * Configure DMA and Rx/Tx thresholds. + * If the Rx/Tx threshold bits in Bcr[01] are 0 then + * the thresholds are determined by Rcr/Tcr. + */ + r = csr8r(ctlr, Bcr0) & ~(CrftMASK|DmaMASK); + csr8w(ctlr, Bcr0, r|Crft64|Dma64); + r = csr8r(ctlr, Bcr1) & ~CtftMASK; + csr8w(ctlr, Bcr1, r|ctlr->tft); + + r = csr8r(ctlr, Rcr) & ~(RrftMASK|Prom|Ar|Sep); + csr8w(ctlr, Rcr, r|Ab|Am); + + r = csr8r(ctlr, Tcr) & ~(RtsfMASK|Ofset|Lb1|Lb0); + csr8w(ctlr, Tcr, r); + + /* + * Link management. + */ + if((ctlr->mii = malloc(sizeof(Mii))) == nil) + return -1; + ctlr->mii->mir = vt6102miimir; + ctlr->mii->miw = vt6102miimiw; + ctlr->mii->ctlr = ctlr; + + if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){ + free(ctlr->mii); + ctlr->mii = nil; + return -1; + } + // print("oui %X phyno %d\n", phy->oui, phy->phyno); + USED(phy); + + //miiane(ctlr->mii, ~0, ~0, ~0); + + return 0; +} + +static void +vt6102pci(void) +{ + Pcidev *p; + Ctlr *ctlr; + int cls, port; + + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != Pcibcnet || p->ccru != Pciscether) + continue; + + switch((p->did<<16)|p->vid){ + default: + continue; + case (0x3065<<16)|0x1106: /* Rhine II */ + case (0x3106<<16)|0x1106: /* Rhine III */ + break; + } + + port = p->mem[0].bar & ~0x01; + if(ioalloc(port, p->mem[0].size, 0, "vt6102") < 0){ + print("vt6102: port 0x%uX in use\n", port); + continue; + } + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = port; + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + if((cls = pcicfgr8(p, PciCLS)) == 0 || cls == 0xFF) + cls = 0x10; + ctlr->cls = cls*4; + if(ctlr->cls < sizeof(Ds)){ + print("vt6102: cls %d < sizeof(Ds)\n", ctlr->cls); + iofree(port); + free(ctlr); + continue; + } + ctlr->tft = Ctft64; + + if(vt6102reset(ctlr)){ + iofree(port); + free(ctlr); + continue; + } + pcisetbme(p); + + if(vt6102ctlrhead != nil) + vt6102ctlrtail->next = ctlr; + else + vt6102ctlrhead = ctlr; + vt6102ctlrtail = ctlr; + } +} + +static int +vt6102pnp(Ether* edev) +{ + Ctlr *ctlr; + + if(vt6102ctlrhead == nil) + vt6102pci(); + + /* + * Any adapter matches if no edev->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = vt6102ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(edev->port == 0 || edev->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = ctlr->pcidev->intl; + edev->tbdf = ctlr->pcidev->tbdf; + edev->mbps = 100; + memmove(edev->ea, ctlr->par, Eaddrlen); + + /* + * Linkage to the generic ethernet driver. + */ + edev->attach = vt6102attach; + edev->transmit = vt6102transmit; + edev->interrupt = vt6102interrupt; + edev->ifstat = vt6102ifstat; + edev->ctl = nil; + + edev->arg = edev; + edev->promiscuous = vt6102promiscuous; + edev->multicast = vt6102multicast; + + return 0; +} + +void +ethervt6102link(void) +{ + addethercard("vt6102", vt6102pnp); + addethercard("rhine", vt6102pnp); +} diff --git a/os/pc/etherwavelan.c b/os/pc/etherwavelan.c index 6fa9f250..e76905b5 100644 --- a/os/pc/etherwavelan.c +++ b/os/pc/etherwavelan.c @@ -87,6 +87,7 @@ static struct { int did; } wavelanpci[] = { 0x1260, 0x3873, /* Intersil Prism2.5 */ + 0x1737, 0x0019, /* Linksys WPC-11 untested */ }; static Ctlr *ctlrhead, *ctlrtail; @@ -95,7 +96,7 @@ static void wavelanpciscan(void) { int i; - ulong pa; + void *mem; Pcidev *p; Ctlr *ctlr; @@ -117,13 +118,13 @@ wavelanpciscan(void) ctlr = malloc(sizeof(Ctlr)); ctlr->pcidev = p; - pa = upamalloc(p->mem[0].bar&~0xF, p->mem[0].size, 0); - if(pa == 0){ - print("wavelanpci: %.4ux %.4ux: upamalloc 0x%.8lux %d failed\n", p->vid, p->did, p->mem[0].bar&~0xF, p->mem[0].size); + mem = vmap(p->mem[0].bar&~0xF, p->mem[0].size); + if(mem == nil){ + print("wavelanpci: %.4ux %.4ux: vmap 0x%.8lux %d failed\n", p->vid, p->did, p->mem[0].bar&~0xF, p->mem[0].size); free(ctlr); continue; } - ctlr->mmb = (ushort*)KADDR(pa); + ctlr->mmb = mem; if(ctlrhead != nil) ctlrtail->next = ctlr; else diff --git a/os/pc/fns.h b/os/pc/fns.h index 028162ac..d4b61034 100644 --- a/os/pc/fns.h +++ b/os/pc/fns.h @@ -104,6 +104,7 @@ void pcicfgw16(Pcidev*, int, int); void pcicfgw32(Pcidev*, int, int); void pciclrbme(Pcidev*); void pciclrioe(Pcidev*); +void pciclrmwi(Pcidev*); int pcigetpms(Pcidev*); void pcihinv(Pcidev*); uchar pciipin(Pcidev*, uchar); @@ -144,7 +145,10 @@ ulong umbrwmalloc(ulong, int, int); void umbrwfree(ulong, int); ulong upamalloc(ulong, int, int); void upafree(ulong, int); +void upareserve(ulong, int); void vectortable(void); +void* vmap(ulong, int); +void vunmap(void*, int); void wrmsr(ulong, ulong); int xchgw(ushort*, int); ulong kzeromap(ulong, ulong, int); diff --git a/os/pc/i8253.c b/os/pc/i8253.c index d7cff39e..0759088f 100644 --- a/os/pc/i8253.c +++ b/os/pc/i8253.c @@ -201,7 +201,7 @@ i8253timerset(uvlong next) period = want - now; if(period < MinPeriod) period = MinPeriod; - else if(period > (4*MaxPeriod)/5) /* strong attraction to MaxPeriod */ + else if(period > MaxPeriod) period = MaxPeriod; } @@ -95,7 +95,7 @@ enum { /* * PCI support code. */ -enum { /* type 0 and type 1 pre-defined header */ +enum { /* type 0 & type 1 pre-defined header */ PciVID = 0x00, /* vendor ID */ PciDID = 0x02, /* device ID */ PciPCR = 0x04, /* command */ @@ -116,6 +116,54 @@ enum { /* type 0 and type 1 pre-defined header */ PciINTP = 0x3D, /* interrupt pin */ }; +/* ccrb (base class code) values; controller types */ +enum { + Pcibcpci1 = 0, /* pci 1.0; no class codes defined */ + Pcibcstore = 1, /* mass storage */ + Pcibcnet = 2, /* network */ + Pcibcdisp = 3, /* display */ + Pcibcmmedia = 4, /* multimedia */ + Pcibcmem = 5, /* memory */ + Pcibcbridge = 6, /* bridge */ + Pcibccomm = 7, /* simple comms (e.g., serial) */ + Pcibcbasesys = 8, /* base system */ + Pcibcinput = 9, /* input */ + Pcibcdock = 0xa, /* docking stations */ + Pcibcproc = 0xb, /* processors */ + Pcibcserial = 0xc, /* serial bus (e.g., USB) */ + Pcibcwireless = 0xd, /* wireless */ + Pcibcintell = 0xe, /* intelligent i/o */ + Pcibcsatcom = 0xf, /* satellite comms */ + Pcibccrypto = 0x10, /* encryption/decryption */ + Pcibcdacq = 0x11, /* data acquisition & signal proc. */ +}; + +/* ccru (sub-class code) values; common cases only */ +enum { + /* mass storage */ + Pciscscsi = 0, /* SCSI */ + Pciscide = 1, /* IDE (ATA) */ + + /* network */ + Pciscether = 0, /* Ethernet */ + + /* display */ + Pciscvga = 0, /* VGA */ + Pciscxga = 1, /* XGA */ + Pcisc3d = 2, /* 3D */ + + /* bridges */ + Pcischostpci = 0, /* host/pci */ + Pciscpcicpci = 1, /* pci/pci */ + + /* simple comms */ + Pciscserial = 0, /* 16450, etc. */ + Pciscmultiser = 1, /* multiport serial */ + + /* serial bus */ + Pciscusb = 3, /* USB */ +}; + enum { /* type 0 pre-defined header */ PciCIS = 0x28, /* cardbus CIS pointer */ PciSVID = 0x2C, /* subsystem vendor ID */ diff --git a/os/pc/memory.c b/os/pc/memory.c index a58119c6..85658248 100644 --- a/os/pc/memory.c +++ b/os/pc/memory.c @@ -568,3 +568,21 @@ upafree(ulong pa, int size) { mapfree(&xrmapupa, pa, size); } + +void +upareserve(ulong pa, int size) +{ + ulong a; + + a = mapalloc(&rmapupa, pa, size, 0); + if(a != pa){ + /* + * This can happen when we're using the E820 + * map, which might have already reserved some + * of the regions claimed by the pci devices. + */ + // print("upareserve: cannot reserve pa=%#.8lux size=%d\n", pa, size); + if(a != 0) + mapfree(&rmapupa, a, size); + } +} diff --git a/os/pc/mmu.c b/os/pc/mmu.c index 6bd4ddfb..6a9e8089 100644 --- a/os/pc/mmu.c +++ b/os/pc/mmu.c @@ -319,3 +319,19 @@ mmukmap(ulong pa, ulong va, int size) return pa; } + +void* +vmap(ulong pa, int size) +{ + pa = upamalloc(pa, size, 0); + if(pa == 0) + return nil; + return KADDR(pa); +} + +void +vunmap(void *va, int size) +{ + if(va != nil) + upafree(PADDR(va), size); +} diff --git a/os/pc/pci.c b/os/pc/pci.c index 4f7e79b6..d35a306a 100644 --- a/os/pc/pci.c +++ b/os/pc/pci.c @@ -176,7 +176,6 @@ pcibusmap(Pcidev *root, ulong *pmema, ulong *pioa, int wrreg) int ntb, i, size, rno, hole; ulong v, mema, ioa, sioa, smema, base, limit; Pcisiz *table, *tptr, *mtb, *itb; - extern void qsort(void*, long, long, int (*)(void*, void*)); if(!nobios) return; |
