/* * Intel 8254[340]NN Gigabit Ethernet Controller * as found on the Intel PRO/1000 series of adapters: * 82543GC Intel PRO/1000 T * 82544EI Intel PRO/1000 XT * 82540EM Intel PRO/1000 MT * 82541[GP]I * 82547GI * 82546GB * 82546EB * To Do: * finish autonegotiation code; * integrate fiber stuff back in (this ONLY handles * the CAT5 cards at the moment); * add checksum-offload; * add tuning control via ctl file; * this driver is little-endian specific. */ #include "all.h" #include "io.h" #include "mem.h" #include "../ip/ip.h" #include "etherif.h" #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, i82545gmc = (0x1026<<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, }; /* from pci.c */ enum { /* command register (pcidev->pcr) */ IOen = (1<<0), MEMen = (1<<1), MASen = (1<<2), MemWrInv = (1<<4), PErrEn = (1<<6), SErrEn = (1<<8), }; enum { Ctrl = 0x00000000, /* Device Control */ Ctrldup = 0x00000004, /* Device Control Duplicate */ Status = 0x00000008, /* Device Status */ Eecd = 0x00000010, /* EEPROM/Flash Control/Data */ Ctrlext = 0x00000018, /* Extended Device Control */ Mdic = 0x00000020, /* MDI Control */ Fcal = 0x00000028, /* Flow Control Address Low */ Fcah = 0x0000002C, /* Flow Control Address High */ Fct = 0x00000030, /* Flow Control Type */ Icr = 0x000000C0, /* Interrupt Cause Read */ Ics = 0x000000C8, /* Interrupt Cause Set */ Ims = 0x000000D0, /* Interrupt Mask Set/Read */ Imc = 0x000000D8, /* Interrupt mask Clear */ Rctl = 0x00000100, /* Receive Control */ Fcttv = 0x00000170, /* Flow Control Transmit Timer Value */ Txcw = 0x00000178, /* Transmit Configuration Word */ Rxcw = 0x00000180, /* Receive Configuration Word */ Tctl = 0x00000400, /* Transmit Control */ Tipg = 0x00000410, /* Transmit IPG */ Tbt = 0x00000448, /* Transmit Burst Timer */ Ait = 0x00000458, /* Adaptive IFS Throttle */ Fcrtl = 0x00002160, /* Flow Control RX Threshold Low */ Fcrth = 0x00002168, /* Flow Control Rx Threshold High */ Rdfh = 0x00002410, /* Receive data fifo head */ Rdft = 0x00002418, /* Receive data fifo tail */ Rdfhs = 0x00002420, /* Receive data fifo head saved */ Rdfts = 0x00002428, /* Receive data fifo tail saved */ Rdfpc = 0x00002430, /* Receive data fifo packet count */ Rdbal = 0x00002800, /* Rd Base Address Low */ Rdbah = 0x00002804, /* Rd Base Address High */ Rdlen = 0x00002808, /* Receive Descriptor Length */ Rdh = 0x00002810, /* Receive Descriptor Head */ Rdt = 0x00002818, /* Receive Descriptor Tail */ Rdtr = 0x00002820, /* Receive Descriptor Timer Ring */ Rxdctl = 0x00002828, /* Receive Descriptor Control */ Radv = 0x0000282C, /* Receive Interrupt Absolute Delay Timer */ Txdmac = 0x00003000, /* Transfer DMA Control */ Ett = 0x00003008, /* Early Transmit Control */ Tdfh = 0x00003410, /* Transmit data fifo head */ Tdft = 0x00003418, /* Transmit data fifo tail */ Tdfhs = 0x00003420, /* Transmit data Fifo Head saved */ Tdfts = 0x00003428, /* Transmit data fifo tail saved */ Tdfpc = 0x00003430, /* Trasnmit data Fifo packet count */ Tdbal = 0x00003800, /* Td Base Address Low */ Tdbah = 0x00003804, /* Td Base Address High */ Tdlen = 0x00003808, /* Transmit Descriptor Length */ Tdh = 0x00003810, /* Transmit Descriptor Head */ Tdt = 0x00003818, /* Transmit Descriptor Tail */ Tidv = 0x00003820, /* Transmit Interrupt Delay Value */ Txdctl = 0x00003828, /* Transmit Descriptor Control */ Tadv = 0x0000382C, /* Transmit Interrupt Absolute Delay Timer */ Statistics = 0x00004000, /* Start of Statistics Area */ Gorcl = 0x88/4, /* Good Octets Received Count */ Gotcl = 0x90/4, /* Good Octets Transmitted Count */ Torl = 0xC0/4, /* Total Octets Received */ Totl = 0xC8/4, /* Total Octets Transmitted */ Nstatistics = 64, Rxcsum = 0x00005000, /* Receive Checksum Control */ Mta = 0x00005200, /* Multicast Table Array */ Ral = 0x00005400, /* Receive Address Low */ Rah = 0x00005404, /* Receive Address High */ Manc = 0x00005820, /* Management Control */ }; enum { /* Ctrl */ Bem = 0x00000002, /* Big Endian Mode */ Prior = 0x00000004, /* Priority on the PCI bus */ Lrst = 0x00000008, /* Link Reset */ Asde = 0x00000020, /* Auto-Speed Detection Enable */ Slu = 0x00000040, /* Set Link Up */ Ilos = 0x00000080, /* Invert Loss of Signal (LOS) */ SspeedMASK = 0x00000300, /* Speed Selection */ SspeedSHIFT = 8, Sspeed10 = 0x00000000, /* 10Mb/s */ Sspeed100 = 0x00000100, /* 100Mb/s */ Sspeed1000 = 0x00000200, /* 1000Mb/s */ Frcspd = 0x00000800, /* Force Speed */ Frcdplx = 0x00001000, /* Force Duplex */ SwdpinsloMASK = 0x003C0000, /* Software Defined Pins - lo nibble */ SwdpinsloSHIFT = 18, SwdpioloMASK = 0x03C00000, /* Software Defined Pins - I or O */ SwdpioloSHIFT = 22, Devrst = 0x04000000, /* Device Reset */ Rfce = 0x08000000, /* Receive Flow Control Enable */ Tfce = 0x10000000, /* Transmit Flow Control Enable */ Vme = 0x40000000, /* VLAN Mode Enable */ }; enum { /* Status */ Lu = 0x00000002, /* Link Up */ Tckok = 0x00000004, /* Transmit clock is running */ Rbcok = 0x00000008, /* Receive clock is running */ Txoff = 0x00000010, /* Transmission Paused */ Tbimode = 0x00000020, /* TBI Mode Indication */ LspeedMASK = 0x000000C0, /* Link Speed Setting */ LspeedSHIFT = 6, Lspeed10 = 0x00000000, /* 10Mb/s */ Lspeed100 = 0x00000040, /* 100Mb/s */ Lspeed1000 = 0x00000080, /* 1000Mb/s */ Mtxckok = 0x00000400, /* MTX clock is running */ Pci66 = 0x00000800, /* PCI Bus speed indication */ Bus64 = 0x00001000, /* PCI Bus width indication */ Pcixmode = 0x00002000, /* PCI-X mode */ PcixspeedMASK = 0x0000C000, /* PCI-X bus speed */ PcixspeedSHIFT = 14, Pcix66 = 0x00000000, /* 50-66MHz */ Pcix100 = 0x00004000, /* 66-100MHz */ Pcix133 = 0x00008000, /* 100-133MHz */ }; enum { /* Ctrl and Status */ Fd = 0x00000001, /* Full-Duplex */ AsdvMASK = 0x00000300, AsdvSHIFT = 8, Asdv10 = 0x00000000, /* 10Mb/s */ Asdv100 = 0x00000100, /* 100Mb/s */ Asdv1000 = 0x00000200, /* 1000Mb/s */ }; enum { /* Eecd */ Sk = 0x00000001, /* Clock input to the EEPROM */ Cs = 0x00000002, /* Chip Select */ Di = 0x00000004, /* Data Input to the EEPROM */ 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 */ }; enum { /* Ctrlext */ Gpien = 0x0000000F, /* General Purpose Interrupt Enables */ SwdpinshiMASK = 0x000000F0, /* Software Defined Pins - hi nibble */ SwdpinshiSHIFT = 4, SwdpiohiMASK = 0x00000F00, /* Software Defined Pins - I or O */ SwdpiohiSHIFT = 8, Asdchk = 0x00001000, /* ASD Check */ Eerst = 0x00002000, /* EEPROM Reset */ Ips = 0x00004000, /* Invert Power State */ Spdbyps = 0x00008000, /* Speed Select Bypass */ }; enum { /* EEPROM content offsets */ Ea = 0x00, /* Ethernet Address */ Cf = 0x03, /* Compatibility Field */ Pba = 0x08, /* Printed Board Assembly number */ /* in fs kernel, Icw1 is defined in io.h; changed it here */ #define Icw1 Igbe_icw1 Icw1 = 0x0A, /* Initialization Control Word 1 */ Sid = 0x0B, /* Subsystem ID */ Svid = 0x0C, /* Subsystem Vendor ID */ Did = 0x0D, /* Device ID */ Vid = 0x0E, /* Vendor ID */ Icw2 = 0x0F, /* Initialization Control Word 2 */ }; enum { /* Mdic */ MDIdMASK = 0x0000FFFF, /* Data */ MDIdSHIFT = 0, MDIrMASK = 0x001F0000, /* PHY Register Address */ MDIrSHIFT = 16, MDIpMASK = 0x03E00000, /* PHY Address */ MDIpSHIFT = 21, MDIwop = 0x04000000, /* Write Operation */ MDIrop = 0x08000000, /* Read Operation */ MDIready = 0x10000000, /* End of Transaction */ MDIie = 0x20000000, /* Interrupt Enable */ MDIe = 0x40000000, /* Error */ }; enum { /* Icr, Ics, Ims, Imc */ Txdw = 0x00000001, /* Transmit Descriptor Written Back */ Txqe = 0x00000002, /* Transmit Queue Empty */ Lsc = 0x00000004, /* Link Status Change */ Rxseq = 0x00000008, /* Receive Sequence Error */ Rxdmt0 = 0x00000010, /* Rd Minimum Threshold Reached */ Rxo = 0x00000040, /* Receiver Overrun */ Rxt0 = 0x00000080, /* Receiver Timer Interrupt */ Mdac = 0x00000200, /* MDIO Access Completed */ Rxcfg = 0x00000400, /* Receiving /C/ ordered sets */ Gpi0 = 0x00000800, /* General Purpose Interrupts */ Gpi1 = 0x00001000, Gpi2 = 0x00002000, Gpi3 = 0x00004000, }; /* * The Mdic register isn't implemented on the 82543GC, * the software defined pins are used instead. * These definitions work for the Intel PRO/1000 T Server Adapter. * The direction pin bits are read from the EEPROM. */ enum { Mdd = ((1<<2)<nic+((r)/4))) #define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v)) static Ctlr* igbectlrhead; static Ctlr* igbectlrtail; static Lock igberblock; /* free receive Msgbufs */ static Msgbuf* igberbpool; static Msgbuf* igberballoc(void) { Msgbuf *m; ilock(&igberblock); if((m = igberbpool) != nil){ igberbpool = m->next; m->next = nil; } iunlock(&igberblock); m->flags &= ~FREE; m->count = 0; m->data = (uchar*)PGROUND((uintptr)m->xdata); return m; } static void igberbfree(Msgbuf *m) { m->flags |= FREE; ilock(&igberblock); m->next = igberbpool; igberbpool = m; iunlock(&igberblock); } static void igbeim(Ctlr* ctlr, int im) { ilock(&ctlr->imlock); ctlr->im |= im; csr32w(ctlr, Ims, ctlr->im); iunlock(&ctlr->imlock); } static int igbelim(void* ctlr) { return ((Ctlr*)ctlr)->lim != 0; } static void igbelproc(void) { Ctlr *ctlr; Ether *edev; MiiPhy *phy; int ctrl, r; edev = u->arg; ctlr = edev->ctlr; for(;;){ if(ctlr->mii == nil || ctlr->mii->curphy == nil) continue; /* * To do: * logic to manage status change, * this is incomplete but should work * one time to set up the hardware. * * MiiPhy.speed, etc. should be in Mii. */ if(miistatus(ctlr->mii) < 0) //continue; goto enable; phy = ctlr->mii->curphy; ctrl = csr32r(ctlr, Ctrl); switch(ctlr->id){ case i82543gc: case i82544ei: default: if(!(ctrl & Asde)){ ctrl &= ~(SspeedMASK|Ilos|Fd); ctrl |= Frcdplx|Frcspd; if(phy->speed == 1000) ctrl |= Sspeed1000; else if(phy->speed == 100) ctrl |= Sspeed100; if(phy->fd) ctrl |= Fd; } break; case i82540em: case i82540eplp: case i82547gi: case i82541gi: case i82541gi2: case i82541pi: break; } /* * Collision Distance. */ r = csr32r(ctlr, Tctl); r &= ~ColdMASK; if(phy->fd) r |= 64<rfc) ctrl |= Rfce; if(phy->tfc) ctrl |= Tfce; csr32w(ctlr, Ctrl, ctrl); enable: ctlr->lim = 0; igbeim(ctlr, Lsc); ctlr->lsleep++; sleep(&ctlr->lrendez, igbelim, ctlr); } } static void igbetxinit(Ctlr* ctlr) { int i, r; Msgbuf *bp; csr32w(ctlr, Tctl, (0x0F<id){ default: r = 6; break; case i82543gc: case i82544ei: case i82547ei: case i82540em: case i82540eplp: case i82541gi: case i82541gi2: case i82541pi: case i82545gmc: case i82546gb: case i82546eb: case i82547gi: r = 8; break; } csr32w(ctlr, Tipg, (6<<20)|(8<<10)|r); csr32w(ctlr, Ait, 0); csr32w(ctlr, Txdmac, 0); csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba)); csr32w(ctlr, Tdbah, 0); csr32w(ctlr, Tdlen, ctlr->ntd*sizeof(Td)); ctlr->tdh = PREV(0, ctlr->ntd); csr32w(ctlr, Tdh, 0); ctlr->tdt = 0; csr32w(ctlr, Tdt, 0); for(i = 0; i < ctlr->ntd; i++){ if((bp = ctlr->tb[i]) != nil){ ctlr->tb[i] = nil; mbfree(bp); } memset(&ctlr->tdba[i], 0, sizeof(Td)); } ctlr->tdfree = ctlr->ntd; csr32w(ctlr, Tidv, 128); r = (4<id){ default: break; case i82540em: case i82540eplp: case i82547gi: case i82545gmc: case i82546gb: case i82546eb: case i82541gi: case i82541gi2: case i82541pi: r = csr32r(ctlr, Txdctl); r &= ~WthreshMASK; r |= Gran|(4<tdh; m = c->ntd-1; while(c->tdba[n = Next(tdh, m)].status&Tdd){ tdh = n; if((b = c->tb[tdh]) != nil){ c->tb[tdh] = nil; mbfree(b); } c->tdba[tdh].status = 0; c->cleaned++; } return c->tdh = tdh; } static void igbetransmit(Ether* edev) { Td *td; Msgbuf *b; Ctlr *c; int tdh, tdt, m; c = edev->ctlr; qlock(&c->tlock); tdh = igbecleanup(c); /* * Try to fill the ring back up. */ tdt = c->tdt; m = c->ntd-1; if(Next(tdt, m) != tdh) for(;;){ if((b = etheroq(edev)) == nil) break; td = &c->tdba[tdt]; td->addr[0] = PCIWADDR(b->data); td->control = DtypeDD|Ide|Rs|Ifcs|Teop|b->count; c->tb[tdt] = b; c->sent++; tdt = Next(tdt, m); if(Next(tdt, m) == tdh){ td->control |= Rs; c->txdw++; c->tdt = tdt; csr32w(c, Tdt, tdt); igbeim(c, Txdw); break; } c->tdt = tdt; csr32w(c, Tdt, tdt); } qunlock(&c->tlock); } static void igbereplenish(Ctlr* c) { Rd *rd; int rdt, m; Msgbuf *b; rdt = c->rdt; m = c->nrd-1; while(Next(rdt, m) != c->rdh){ rd = &c->rdba[rdt]; if(c->rb[rdt] == nil){ b = igberballoc(); if(b == nil){ print("igbe: no available buffers\n"); break; } c->rb[rdt] = b; rd->addr[0] = PCIWADDR(b->data); // rd->addr[1] = 0; } // coherence(); rd->status = 0; rdt = Next(rdt, m); c->rdfree++; } c->rdt = rdt; csr32w(c, Rdt, rdt); } static void igberxinit(Ctlr* ctlr) { int i; Msgbuf *bp; csr32w(ctlr, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF); csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba)); csr32w(ctlr, Rdbah, 0); csr32w(ctlr, Rdlen, ctlr->nrd*sizeof(Rd)); ctlr->rdh = 0; csr32w(ctlr, Rdh, 0); ctlr->rdt = 0; csr32w(ctlr, Rdt, 0); ctlr->rdtr = 0; csr32w(ctlr, Rdtr, Fpd|0); for(i = 0; i < ctlr->nrd; i++){ if((bp = ctlr->rb[i]) != nil){ ctlr->rb[i] = nil; mbfree(bp); } } igbereplenish(ctlr); switch(ctlr->id){ case i82540em: case i82540eplp: case i82541gi: case i82541gi2: case i82541pi: case i82545gmc: case i82546gb: case i82546eb: case i82547gi: csr32w(ctlr, Radv, 64); break; } csr32w(ctlr, Rxdctl, (8<rim != 0; } static void igberproc(void) { Rd *rd; Msgbuf *bp; Ctlr *ctlr; int r, rdh; Ether *edev; edev = u->arg; ctlr = edev->ctlr; igberxinit(ctlr); r = csr32r(ctlr, Rctl); r |= Ren; csr32w(ctlr, Rctl, r); for(;;){ ctlr->rim = 0; igbeim(ctlr, Rxt0|Rxo|Rxdmt0|Rxseq); ctlr->rsleep++; sleep(&ctlr->rrendez, igberim, ctlr); 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, * the descriptor status Tpcs and Ipcs bits give * an indication of whether the checksums were * calculated and valid. */ if((rd->status & Reop) && rd->errors == 0){ bp = ctlr->rb[rdh]; ctlr->rb[rdh] = nil; bp->count += rd->length; bp->next = nil; if(!(rd->status & Ixsm)){ ctlr->ixsm++; if(rd->status & Ipcs){ /* * IP checksum calculated * (and valid as errors == 0). */ ctlr->ipcs++; // bp->flag |= Bipck; } if(rd->status & Tcpcs){ /* * TCP/UDP checksum calculated * (and valid as errors == 0). */ ctlr->tcpcs++; // bp->flag |= Btcpck|Budpck; } // bp->checksum = rd->checksum; // bp->flag |= Bpktck; } etheriq(edev, bp); } else if(ctlr->rb[rdh] != nil){ mbfree(ctlr->rb[rdh]); ctlr->rb[rdh] = nil; } // memset(rd, 0, sizeof(Rd)); // coherence(); ctlr->rdfree--; rdh = NEXT(rdh, ctlr->nrd); } ctlr->rdh = rdh; if(ctlr->rdfree < ctlr->nrd/2 || (ctlr->rim & Rxdmt0)) igbereplenish(ctlr); } } static int txcansleep(void *v) { Ctlr *c; c = v; return NEXT(c->tdh, c->ntd) != csr32r(c, Tdh); } static void igbetproc(void) { Ether *e; Ctlr *c; e = u->arg; c = e->ctlr; for(;;){ sleep(&c->trendez, txcansleep, c); igbetransmit(e); } } static void igbeattach(Ether* edev) { Ctlr *ctlr; ctlr = edev->ctlr; ctlr->nrd = Nrd; ctlr->ntd = Ntd; ctlr->alloc = ialloc(ctlr->nrd*sizeof(Rd)+ctlr->ntd*sizeof(Td) + 127, 128); ctlr->rdba = (Rd*)ctlr->alloc; ctlr->tdba = (Td*)(ctlr->rdba+ctlr->nrd); ctlr->rb = ialloc(ctlr->nrd*sizeof(Msgbuf*), 0); ctlr->tb = ialloc(ctlr->ntd*sizeof(Msgbuf*), 0); mballocpool(Nrb, Rbsz, BY2PG, Mbeth1, igberbfree); snprint(ctlr->rname, sizeof ctlr->rname, "#l%dl", edev->ctlrno); userinit(igbelproc, edev, ctlr->rname); snprint(ctlr->dname, sizeof ctlr->dname, "#l%dd", edev->ctlrno); userinit(igberproc, edev, ctlr->dname); snprint(ctlr->tname, sizeof ctlr->tname, "#l%dt", edev->ctlrno); userinit(igbetproc, edev, ctlr->tname); igbetxinit(ctlr); } static void igbeinterrupt(Ureg*, void* arg) { Ctlr *ctlr; Ether *edev; int icr, im; edev = arg; ctlr = edev->ctlr; ilock(&ctlr->imlock); csr32w(ctlr, Imc, ~0); im = ctlr->im; while((icr = csr32r(ctlr, Icr) & ctlr->im) != 0){ if(icr & Lsc){ im &= ~Lsc; ctlr->lim = icr & Lsc; wakeup(&ctlr->lrendez); ctlr->lintr++; } if(icr & (Rxt0|Rxo|Rxdmt0|Rxseq)){ im &= ~(Rxt0|Rxo|Rxdmt0|Rxseq); ctlr->rim = icr & (Rxt0|Rxo|Rxdmt0|Rxseq); wakeup(&ctlr->rrendez); ctlr->rintr++; } if(icr & Txdw){ im &= ~Txdw; wakeup(&ctlr->trendez); ctlr->tintr++; } } ctlr->im = im; csr32w(ctlr, Ims, im); iunlock(&ctlr->imlock); } static int i82543mdior(Ctlr* ctlr, int n) { int ctrl, data, i, r; /* * Read n bits from the Management Data I/O Interface. */ ctrl = csr32r(ctlr, Ctrl); r = (ctrl & ~Mddo)|Mdco; data = 0; for(i = n-1; i >= 0; i--){ if(csr32r(ctlr, Ctrl) & Mdd) data |= (1<= 0; i--){ if(bits & (1<ctlr; /* * MII Management Interface Read. * * Preamble; * ST+OP+PHYAD+REGAD; * TA + 16 data bits. */ i82543mdiow(ctlr, 0xFFFFFFFF, 32); i82543mdiow(ctlr, 0x1800|(pa<<5)|ra, 14); data = i82543mdior(ctlr, 18); if(data & 0x10000) return -1; return data & 0xFFFF; } static int i82543miimiw(Mii* mii, int pa, int ra, int data) { Ctlr *ctlr; ctlr = mii->ctlr; /* * MII Management Interface Write. * * Preamble; * ST+OP+PHYAD+REGAD+TA + 16 data bits; * Z. */ i82543mdiow(ctlr, 0xFFFFFFFF, 32); data &= 0xFFFF; data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16); i82543mdiow(ctlr, data, 32); return 0; } static int igbemiimir(Mii* mii, int pa, int ra) { Ctlr *ctlr; int mdic, timo; ctlr = mii->ctlr; csr32w(ctlr, Mdic, MDIrop|(pa<ctlr; data &= MDIdMASK; csr32w(ctlr, Mdic, MDIwop|(pa<mii = ialloc(sizeof(Mii), 0)) == nil) return -1; ctlr->mii->ctlr = ctlr; ctrl = csr32r(ctlr, Ctrl); ctrl |= Slu; switch(ctlr->id){ case i82543gc: ctrl |= Frcdplx|Frcspd; csr32w(ctlr, Ctrl, ctrl); /* * The reset pin direction (Mdro) should already * be set from the EEPROM load. * If it's not set this configuration is unexpected * so bail. */ r = csr32r(ctlr, Ctrlext); if(!(r & Mdro)) return -1; csr32w(ctlr, Ctrlext, r); delay(20); r = csr32r(ctlr, Ctrlext); r &= ~Mdr; csr32w(ctlr, Ctrlext, r); delay(20); r = csr32r(ctlr, Ctrlext); r |= Mdr; csr32w(ctlr, Ctrlext, r); delay(20); ctlr->mii->mir = i82543miimir; ctlr->mii->miw = i82543miimiw; break; case i82544ei: case i82547ei: case i82540em: case i82540eplp: case i82547gi: case i82541gi: case i82541gi2: case i82541pi: case i82545gmc: case i82546gb: case i82546eb: ctrl &= ~(Frcdplx|Frcspd); csr32w(ctlr, Ctrl, ctrl); ctlr->mii->mir = igbemiimir; ctlr->mii->miw = igbemiimiw; break; default: // free(ctlr->mii); ctlr->mii = nil; return -1; } if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){ // free(ctlr->mii); ctlr->mii = nil; return -1; } USED(phy); // print("oui %X phyno %d\n", phy->oui, phy->phyno); /* * 8254X-specific PHY registers not in 802.3: * 0x10 PHY specific control * 0x14 extended PHY specific control * Set appropriate values then reset the PHY to have * changes noted. */ switch(ctlr->id){ case i82547gi: case i82541gi: case i82541gi2: case i82541pi: case i82545gmc: case i82546gb: case i82546eb: break; default: r = miimir(ctlr->mii, 16); r |= 0x0800; /* assert CRS on Tx */ 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) p |= AnaP; if(ctlr->txcw & TxcwAs) p |= AnaAP; miiane(ctlr->mii, ~0, p, ~0); break; } return 0; } static int at93c46io(Ctlr* ctlr, char* op, int data) { char *lp, *p; int i, loop, eecd, r; eecd = csr32r(ctlr, Eecd); r = 0; loop = -1; lp = nil; for(p = op; *p != '\0'; p++){ switch(*p){ default: return -1; case ' ': continue; case ':': /* start of loop */ loop = strtoul(p+1, &lp, 0)-1; lp--; if(p == lp) loop = 7; p = lp; continue; case ';': /* end of loop */ if(lp == nil) return -1; loop--; if(loop >= 0) p = lp; else lp = nil; continue; case 'C': /* assert clock */ eecd |= Sk; break; case 'c': /* deassert clock */ eecd &= ~Sk; break; case 'D': /* next bit in 'data' byte */ if(loop < 0) return -1; if(data & (1<= 0) r |= (i<= 0) return -1; return r; } static int at93c46r(Ctlr* ctlr) { ushort sum; char rop[20]; int addr, areq, bits, data, eecd, i; eecd = csr32r(ctlr, Eecd); if(eecd & Spi){ print("igbe: SPI EEPROM access not implemented\n"); return 0; } if(eecd & (Eeszaddr|Eesz256)) bits = 8; else bits = 6; sum = 0; switch(ctlr->id){ default: areq = 0; break; case i82541gi: case i82547gi: case i82540em: case i82540eplp: case i82541pi: case i82541gi2: case i82545gmc: case i82546gb: case i82546eb: areq = 1; csr32w(ctlr, Eecd, eecd|Areq); for(i = 0; i < 1000; i++){ if((eecd = csr32r(ctlr, Eecd)) & Agnt) break; microdelay(5); } if(!(eecd & Agnt)){ print("igbe: not granted EEPROM access\n"); goto release; } break; } snprint(rop, sizeof(rop), "S :%dDCc;", bits+3); for(addr = 0; addr < 0x40; addr++){ /* * Read a word at address 'addr' from the Atmel AT93C46 * 3-Wire Serial EEPROM or compatible. The EEPROM access is * controlled by 4 bits in Eecd. See the AT93C46 datasheet * for protocol details. */ if(at93c46io(ctlr, rop, (0x06<eeprom[addr] = data; sum += data; } release: if(areq) csr32w(ctlr, Eecd, eecd & ~Areq); return sum; } static int igbedetach(Ctlr* ctlr) { int r, timeo; if (ctlr == nil) return -1; /* * Perform a device reset to get the chip back to the * power-on state, followed by an EEPROM reset to read * the defaults for some internal registers. * * The alpha needs the rest of this routine to run at splhi * or else the card interrupts and resets the processor. Don't know * why. Since the alpha isn't important any more, let's ignore that. */ csr32w(ctlr, Imc, ~0); csr32w(ctlr, Rctl, 0); csr32w(ctlr, Tctl, 0); delay(10); /* was 10 then 100 */ csr32w(ctlr, Ctrl, Devrst); delay(1); /* was 100 */ for(timeo = 0; timeo < 1000; timeo++){ if(!(csr32r(ctlr, Ctrl) & Devrst)) break; delay(1); } if(csr32r(ctlr, Ctrl) & Devrst) return -1; r = csr32r(ctlr, Ctrlext); csr32w(ctlr, Ctrlext, r|Eerst); delay(1); for(timeo = 0; timeo < 1000; timeo++){ if(!(csr32r(ctlr, Ctrlext) & Eerst)) break; delay(1); } if(csr32r(ctlr, Ctrlext) & Eerst) return -1; switch(ctlr->id){ default: break; case i82540em: case i82540eplp: case i82541gi: case i82541pi: case i82547gi: case i82541gi2: case i82545gmc: case i82546gb: case i82546eb: r = csr32r(ctlr, Manc); r &= ~Arpen; csr32w(ctlr, Manc, r); break; } csr32w(ctlr, Imc, ~0); delay(1); /* was 100 */ for(timeo = 0; timeo < 1000; timeo++){ if(!csr32r(ctlr, Icr)) break; delay(1); } if(csr32r(ctlr, Icr)) return -1; return 0; } int etherigbereset(Ctlr* ctlr) { int ctrl, i, pause, r, swdpio, txcw; if(igbedetach(ctlr)) return -1; /* * Read the EEPROM, validate the checksum * then get the device back to a power-on state. */ if((r = at93c46r(ctlr)) != 0xBABA){ print("igbe: bad EEPROM checksum - 0x%4.4uX\n", r); return -1; } /* * Snarf and set up the receive addresses. * There are 16 addresses. The first should be the MAC address. * The others are cleared and not marked valid (MS bit of Rah). */ if(ctlr->id == i82546gb || ctlr->id == i82546eb) if(BUSFNO(ctlr->pcidev->tbdf) == 1) ctlr->eeprom[Ea+2] += 0x100; // second interface if(ctlr->id == i82541gi) if(ctlr->eeprom[Ea] == 0xFFFF) ctlr->eeprom[Ea] = 0xD000; for(i = 0; i < Easize/2; i++){ ctlr->ra[2*i] = ctlr->eeprom[Ea+i]; ctlr->ra[2*i+1] = ctlr->eeprom[Ea+i]>>8; } r = (ctlr->ra[3]<<24)|(ctlr->ra[2]<<16)|(ctlr->ra[1]<<8)|ctlr->ra[0]; csr32w(ctlr, Ral, r); r = 0x80000000|(ctlr->ra[5]<<8)|ctlr->ra[4]; csr32w(ctlr, Rah, r); for(i = 1; i < 16; i++){ csr32w(ctlr, Ral+i*8, 0); csr32w(ctlr, Rah+i*8, 0); } /* * Clear the Multicast Table Array. * It's a 4096 bit vector accessed as 128 32-bit registers. */ for(i = 0; i < 128; i++) csr32w(ctlr, Mta+i*4, 0); /* * Just in case the Eerst didn't load the defaults * (doesn't appear to fully on the 8243GC), do it manually. */ if (ctlr->id == i82543gc) { // 82543 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; } if(ctlr->eeprom[Icw1] & 0x0200) ctrl |= Lrst; if(ctlr->eeprom[Icw1] & 0x0010) ctrl |= Ilos; if(ctlr->eeprom[Icw1] & 0x0800) ctrl |= Frcspd; swdpio = (ctlr->eeprom[Icw1] & 0x01E0)>>5; ctrl |= swdpio<eeprom[Icw2] & 0x00F0)>>4; if(ctlr->eeprom[Icw1] & 0x1000) ctrl |= Ips; ctrl |= swdpio<eeprom[Icw2] & 0x0800) txcw |= TxcwAne; pause = (ctlr->eeprom[Icw2] & 0x3000)>>12; txcw |= pause<fcrtl = 0x00002000; ctlr->fcrth = 0x00004000; txcw |= TxcwAs|TxcwPs; break; case 0: ctlr->fcrtl = 0x00002000; ctlr->fcrth = 0x00004000; break; case 2: ctlr->fcrtl = 0; ctlr->fcrth = 0; txcw |= TxcwAs; break; } ctlr->txcw = txcw; csr32w(ctlr, Txcw, txcw); } /* * Flow control - values from the datasheet. */ csr32w(ctlr, Fcal, 0x00C28001); csr32w(ctlr, Fcah, 0x00000100); csr32w(ctlr, Fct, 0x00008808); csr32w(ctlr, Fcttv, 0x00000100); csr32w(ctlr, Fcrtl, ctlr->fcrtl); csr32w(ctlr, Fcrth, ctlr->fcrth); if(!(csr32r(ctlr, Status) & Tbimode) && igbemii(ctlr) < 0) return -1; return 0; } static void igbepci(void) { int cls; Pcidev *p; Ctlr *ctlr; void *mem; p = nil; while(p = pcimatch(p, 0, 0)){ switch((p->did<<16)|p->vid){ default: continue; case i82543gc: case i82544ei: case i82547ei: case i82540em: case i82540eplp: case i82541gi: case i82547gi: case i82541gi2: case i82541pi: case i82545gmc: case i82546gb: case i82546eb: break; } /* cast for FS */ mem = (void *)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; } switch(cls = pcicfgr8(p, PciCLS)){ default: print("igbe: unexpected CLS - %d\n", cls*4); break; case 0x00: case 0xFF: print("igbe: unusable CLS\n"); continue; case 0x08: case 0x10: break; } ctlr = ialloc(sizeof(Ctlr), 0); if (ctlr == nil) panic("ibgepci: no mem"); ctlr->port = p->mem[0].bar & ~0x0F; ctlr->pcidev = p; ctlr->id = (p->did<<16)|p->vid; ctlr->cls = cls*4; ctlr->nic = mem; if(etherigbereset(ctlr)){ // free(ctlr); continue; } pcisetbme(p); if(igbectlrhead != nil) igbectlrtail->next = ctlr; else igbectlrhead = ctlr; igbectlrtail = ctlr; } } int igbepnp(Ether* edev) { Ctlr *ctlr; if(igbectlrhead == nil) igbepci(); /* * Any adapter matches if no edev->port is supplied, * otherwise the ports must match. */ for(ctlr = igbectlrhead; 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; memmove(edev->ea, ctlr->ra, Easize); /* * Linkage to the generic ethernet driver. */ edev->attach = igbeattach; edev->transmit = igbetransmit; edev->interrupt = igbeinterrupt; return 0; }