/* * Bootstrap driver for * Intel 82563, 82571, 82573, 82575, 82576 * GbE PCI-Express Controllers. */ #include "u.h" #include "lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "etherif.h" /* compatibility with cpu kernels */ #define iallocb allocb #ifndef CACHELINESZ #define CACHELINESZ 32 /* pentium & later */ #endif /* 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, }; /* * these are in the order they appear in the manual, not numeric order. * It was too hard to find them in the book. Ref 21489, rev 2.6 */ enum { /* General */ Ctrl = 0x00000000, /* Device Control */ Status = 0x00000008, /* Device Status */ Eec = 0x00000010, /* EEPROM/Flash Control/Data */ Eerd = 0x00000014, /* EEPROM Read */ Ctrlext = 0x00000018, /* Extended Device Control */ Fla = 0x0000001c, /* Flash Access */ Mdic = 0x00000020, /* MDI Control */ Seresctl = 0x00000024, /* Serdes ana */ Fcal = 0x00000028, /* Flow Control Address Low */ Fcah = 0x0000002C, /* Flow Control Address High */ Fct = 0x00000030, /* Flow Control Type */ Kumctrlsta = 0x00000034, /* MAC-PHY Interface */ Vet = 0x00000038, /* VLAN EtherType */ Fcttv = 0x00000170, /* Flow Control Transmit Timer Value */ Txcw = 0x00000178, /* Transmit Configuration Word */ Rxcw = 0x00000180, /* Receive Configuration Word */ Ledctl = 0x00000E00, /* LED control */ Pba = 0x00001000, /* Packet Buffer Allocation */ Pbs = 0x00001008, /* Packet Buffer Size */ /* Interrupt */ Icr = 0x000000C0, /* Interrupt Cause Read */ Ics = 0x000000C8, /* Interrupt Cause Set */ Ims = 0x000000D0, /* Interrupt Mask Set/Read */ Imc = 0x000000D8, /* Interrupt mask Clear */ Iam = 0x000000E0, /* Interrupt acknowledge Auto Mask */ /* Receive */ Rctl = 0x00000100, /* Receive Control */ Ert = 0x00002008, /* Early Receive Threshold (573[EVL] only) */ Fcrtl = 0x00002160, /* Flow Control RX Threshold Low */ Fcrth = 0x00002168, /* Flow Control Rx Threshold High */ Psrctl = 0x00002170, /* Packet Split Receive Control */ Rdbal = 0x00002800, /* Rdesc Base Address Low Queue 0 */ Rdbah = 0x00002804, /* Rdesc Base Address High Queue 0 */ Rdlen = 0x00002808, /* Receive Descriptor Length Queue 0 */ Rdh = 0x00002810, /* Receive Descriptor Head Queue 0 */ Rdt = 0x00002818, /* Receive Descriptor Tail Queue 0 */ Rdtr = 0x00002820, /* Receive Descriptor Timer Ring */ Rxdctl = 0x00002828, /* Receive Descriptor Control */ Radv = 0x0000282C, /* Receive Interrupt Absolute Delay Timer */ Rdbal1 = 0x00002900, /* Rdesc Base Address Low Queue 1 */ Rdbah1 = 0x00002804, /* Rdesc Base Address High Queue 1 */ Rdlen1 = 0x00002908, /* Receive Descriptor Length Queue 1 */ Rdh1 = 0x00002910, /* Receive Descriptor Head Queue 1 */ Rdt1 = 0x00002918, /* Receive Descriptor Tail Queue 1 */ Rxdctl1 = 0x00002928, /* Receive Descriptor Control Queue 1 */ Rsrpd = 0x00002c00, /* Receive Small Packet Detect */ Raid = 0x00002c08, /* Receive ACK interrupt delay */ Cpuvec = 0x00002c10, /* CPU Vector */ Rxcsum = 0x00005000, /* Receive Checksum Control */ Rfctl = 0x00005008, /* Receive Filter Control */ Mta = 0x00005200, /* Multicast Table Array */ Ral = 0x00005400, /* Receive Address Low */ Rah = 0x00005404, /* Receive Address High */ Vfta = 0x00005600, /* VLAN Filter Table Array */ Mrqc = 0x00005818, /* Multiple Receive Queues Command */ Rssim = 0x00005864, /* RSS Interrupt Mask */ Rssir = 0x00005868, /* RSS Interrupt Request */ Reta = 0x00005c00, /* Redirection Table */ Rssrk = 0x00005c80, /* RSS Random Key */ /* Transmit */ Tctl = 0x00000400, /* Transmit Control */ Tipg = 0x00000410, /* Transmit IPG */ Tdbal = 0x00003800, /* Tdesc Base Address Low */ Tdbah = 0x00003804, /* Tdesc 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 */ Tarc0 = 0x00003840, /* Transmit Arbitration Counter Queue 0 */ Tdbal1 = 0x00003900, /* Transmit Descriptor Base Low Queue 1 */ Tdbah1 = 0x00003904, /* Transmit Descriptor Base High Queue 1 */ Tdlen1 = 0x00003908, /* Transmit Descriptor Length Queue 1 */ Tdh1 = 0x00003910, /* Transmit Descriptor Head Queue 1 */ Tdt1 = 0x00003918, /* Transmit Descriptor Tail Queue 1 */ Txdctl1 = 0x00003928, /* Transmit Descriptor Control 1 */ Tarc1 = 0x00003940, /* Transmit Arbitration Counter Queue 1 */ /* Statistics */ 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, }; enum { /* Ctrl */ GIOmd = 1<<2, /* BIO master disable */ Lrst = 1<<3, /* link reset */ Slu = 1<<6, /* Set Link Up */ SspeedMASK = 3<<8, /* Speed Selection */ SspeedSHIFT = 8, Sspeed10 = 0x00000000, /* 10Mb/s */ Sspeed100 = 0x00000100, /* 100Mb/s */ Sspeed1000 = 0x00000200, /* 1000Mb/s */ Frcspd = 1<<11, /* Force Speed */ Frcdplx = 1<<12, /* Force Duplex */ SwdpinsloMASK = 0x003C0000, /* Software Defined Pins - lo nibble */ SwdpinsloSHIFT = 18, SwdpioloMASK = 0x03C00000, /* Software Defined Pins - I or O */ SwdpioloSHIFT = 22, Devrst = 1<<26, /* Device Reset */ Rfce = 1<<27, /* Receive Flow Control Enable */ Tfce = 1<<28, /* Transmit Flow Control Enable */ Vme = 1<<30, /* VLAN Mode Enable */ Phy_rst = 1<<31, /* Phy Reset */ }; enum { /* Status */ Lu = 1<<1, /* Link Up */ Lanid = 3<<2, /* mask for Lan ID. */ Txoff = 1<<4, /* Transmission Paused */ Tbimode = 1<<5, /* TBI Mode Indication */ SpeedMASK = 0x000000C0, Speed10 = 0x00000000, /* 10Mb/s */ Speed100 = 0x00000040, /* 100Mb/s */ Speed1000 = 0x00000080, /* 1000Mb/s */ Phyra = 1<<10, /* PHY Reset Asserted */ GIOme = 1<<19, /* GIO Master Enable Status */ }; enum { /* Ctrl and Status */ Fd = 0x00000001, /* Full-Duplex */ AsdvMASK = 0x00000300, Asdv10 = 0x00000000, /* 10Mb/s */ Asdv100 = 0x00000100, /* 100Mb/s */ Asdv1000 = 0x00000200, /* 1000Mb/s */ }; enum { /* Eec */ Sk = 1<<0, /* Clock input to the EEPROM */ Cs = 1<<1, /* Chip Select */ Di = 1<<2, /* Data Input to the EEPROM */ Do = 1<<3, /* Data Output from the EEPROM */ Areq = 1<<6, /* EEPROM Access Request */ Agnt = 1<<7, /* EEPROM Access Grant */ }; enum { /* Eerd */ ee_start = 1<<0, /* Start Read */ ee_done = 1<<1, /* Read done */ ee_addr = 0xfff8<<2, /* Read address [15:2] */ ee_data = 0xffff<<16, /* Read Data; Data returned from eeprom/nvm */ }; enum { /* Ctrlext */ Asdchk = 1<<12, /* ASD Check */ Eerst = 1<<13, /* EEPROM Reset */ Spdbyps = 1<<15, /* Speed Select Bypass */ }; enum { /* EEPROM content offsets */ Ea = 0x00, /* Ethernet Address */ Cf = 0x03, /* Compatibility Field */ 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, /* Rdesc 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, Ack = 0x00020000, /* receive ACK frame */ }; enum { /* Txcw */ TxcwFd = 0x00000020, /* Full Duplex */ TxcwHd = 0x00000040, /* Half Duplex */ TxcwPauseMASK = 0x00000180, /* Pause */ TxcwPauseSHIFT = 7, TxcwPs = 1<nic+((r)/4))) #define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v)) static void i82563im(Ctlr* ctlr, int im) { ilock(&ctlr->imlock); ctlr->im |= im; csr32w(ctlr, Ims, ctlr->im); iunlock(&ctlr->imlock); } static void i82563attach(Ether* edev) { int ctl; Ctlr *ctlr; ctlr = edev->ctlr; i82563im(ctlr, 0); ctl = csr32r(ctlr, Rctl)|Ren; csr32w(ctlr, Rctl, ctl); ctl = csr32r(ctlr, Tctl)|Ten; csr32w(ctlr, Tctl, ctl); } static void txstart(Ether *edev) { int tdh, tdt; Ctlr *ctlr = edev->ctlr; Block *bp; Tdesc *tdesc; /* * Try to fill the ring back up, moving buffers from the transmit q. */ tdh = PREV(ctlr->tdh, Ntdesc); for(tdt = ctlr->tdt; tdt != tdh; tdt = NEXT(tdt, Ntdesc)){ /* pull off the head of the transmission queue */ if((bp = ctlr->bqhead) == nil) /* was qget(edev->oq) */ break; ctlr->bqhead = bp->next; if (ctlr->bqtail == bp) ctlr->bqtail = nil; /* set up a descriptor for it */ tdesc = &ctlr->tdba[tdt]; tdesc->addr[0] = PCIWADDR(bp->rp); tdesc->addr[1] = 0; tdesc->control = /* Ide | */ Rs | Ifcs | Teop | BLEN(bp); ctlr->tb[tdt] = bp; } ctlr->tdt = tdt; csr32w(ctlr, Tdt, tdt); i82563im(ctlr, Txdw); } static Block * fromringbuf(Ether *ether) { RingBuf *tb = ðer->tb[ether->ti]; Block *bp = allocb(tb->len); memmove(bp->wp, tb->pkt, tb->len); memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen); bp->wp += tb->len; return bp; } static void i82563transmit(Ether* edev) { Block *bp; Ctlr *ctlr; Tdesc *tdesc; RingBuf *tb; int tdh; ctlr = edev->ctlr; ilock(&ctlr->tdlock); /* * Free any completed packets * - try to get the soft tdh to catch the tdt; * - if the packet had an underrun bump the threshold * - the Tu bit doesn't seem to ever be set, perhaps * because Rs mode is used? */ tdh = ctlr->tdh; for(;;){ tdesc = &ctlr->tdba[tdh]; if(!(tdesc->status & Tdd)) break; if(ctlr->tb[tdh] != nil){ freeb(ctlr->tb[tdh]); ctlr->tb[tdh] = nil; } tdesc->status = 0; tdh = NEXT(tdh, Ntdesc); } ctlr->tdh = tdh; /* copy packets from the software RingBuf to the transmission q */ while((tb = &edev->tb[edev->ti])->owner == Interface){ bp = fromringbuf(edev); // print("#l%d: tx %d %E %E\n", edev->ctlrno, edev->ti, bp->rp, // bp->rp+6); if(ctlr->bqhead) ctlr->bqtail->next = bp; else ctlr->bqhead = bp; ctlr->bqtail = bp; txstart(edev); /* kick transmitter */ tb->owner = Host; /* give descriptor back */ edev->ti = NEXT(edev->ti, edev->ntb); } iunlock(&ctlr->tdlock); } static void i82563replenish(Ctlr* ctlr) { int rdt; Block *bp; Rdesc *rdesc; rdt = ctlr->rdt; while(NEXT(rdt, Nrdesc) != ctlr->rdh){ rdesc = &ctlr->rdba[rdt]; if(ctlr->rb[rdt] != nil){ /* nothing to do */ } else if((bp = iallocb(2048)) != nil){ ctlr->rb[rdt] = bp; rdesc->addr[0] = PCIWADDR(bp->rp); rdesc->addr[1] = 0; } else break; rdesc->status = 0; rdt = NEXT(rdt, Nrdesc); } ctlr->rdt = rdt; csr32w(ctlr, Rdt, rdt); } static void toringbuf(Ether *ether, Block *bp) { RingBuf *rb = ðer->rb[ether->ri]; if (rb->owner == Interface) { rb->len = BLEN(bp); memmove(rb->pkt, bp->rp, rb->len); rb->owner = Host; ether->ri = NEXT(ether->ri, ether->nrb); } else if (debug) print("#l%d: toringbuf: dropping packets @ ri %d\n", ether->ctlrno, ether->ri); } int interesting(void*); static void i82563interrupt(Ureg*, void* arg) { int icr, im, rdh, txdw = 0, rxcnt; Block *bp; Ctlr *ctlr; Ether *edev; Rdesc *rdesc; edev = arg; ctlr = edev->ctlr; ilock(&ctlr->imlock); csr32w(ctlr, Imc, ~0); im = ctlr->im; rxcnt = 0; for(icr = csr32r(ctlr, Icr); icr & ctlr->im; icr = csr32r(ctlr, Icr)){ if(icr & (Rxseq|Lsc)){ /* should be more here */ } rdh = ctlr->rdh; for (;;) { rdesc = &ctlr->rdba[rdh]; if(!(rdesc->status & Rdd)) break; if ((rdesc->status & Reop) && rdesc->errors == 0) { bp = ctlr->rb[rdh]; if(0 && memcmp(bp->rp, broadcast, 6) != 0) print("#l%d: rx %d %E %E %d\n", edev->ctlrno, rdh, bp->rp, bp->rp+6, rdesc->length); ctlr->rb[rdh] = nil; bp->wp += rdesc->length; if (interesting(bp->rp)) toringbuf(edev, bp); freeb(bp); } else if (rdesc->status & Reop && rdesc->errors) print("%s: input packet error %#ux\n", tname[ctlr->type], rdesc->errors); rdesc->status = 0; rdh = NEXT(rdh, Nrdesc); if (++rxcnt >= 16 && ctlr->type == i82576) { i82563replenish(ctlr); rxcnt = 0; } } ctlr->rdh = rdh; if(icr & Rxdmt0 || ctlr->type == i82576) i82563replenish(ctlr); if(icr & Txdw){ im &= ~Txdw; txdw++; } } ctlr->im = im; csr32w(ctlr, Ims, im); iunlock(&ctlr->imlock); if(txdw) i82563transmit(edev); } static void i82563init(Ether* edev) { Ctlr *ctlr; u32int r, rctl; ctlr = edev->ctlr; rctl = Dpf | Bsize2048 | Bam | RdtmsHALF; if(ctlr->type == i82575 || ctlr->type == i82576){ /* * Setting Qenable in Rxdctl does not * appear to stick unless Ren is on. */ csr32w(ctlr, Rctl, Ren|rctl); r = csr32r(ctlr, Rxdctl); r |= Qenable; csr32w(ctlr, Rxdctl, r); } csr32w(ctlr, Rctl, rctl); ctlr->rdba = mallocalign(Nrdesc*sizeof(Rdesc), 256, 0, 0); /* 256 was 128 */ csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba)); csr32w(ctlr, Rdbah, 0); csr32w(ctlr, Rdlen, Nrdesc*sizeof(Rdesc)); ctlr->rdh = 0; csr32w(ctlr, Rdh, ctlr->rdh); ctlr->rdt = 0; csr32w(ctlr, Rdt, ctlr->rdt); ctlr->rb = malloc(sizeof(Block*)*Nrdesc); i82563replenish(ctlr); csr32w(ctlr, Rdtr, 0); csr32w(ctlr, Radv, 0); if(ctlr->type == i82573) csr32w(ctlr, Ert, 1024/8); if(ctlr->type == i82566 || ctlr->type == i82567) csr32w(ctlr, Pbs, 16); i82563im(ctlr, Rxt0 | Rxo | Rxdmt0 | Rxseq | Ack); csr32w(ctlr, Tctl, 0x0F<tdba = mallocalign(Ntdesc*sizeof(Tdesc), 256, 0, 0); /* 256 was 128 */ memset(ctlr->tdba, 0, Ntdesc*sizeof(Tdesc)); csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba)); csr32w(ctlr, Tdbah, 0); csr32w(ctlr, Tdlen, Ntdesc*sizeof(Tdesc)); ctlr->tdh = 0; csr32w(ctlr, Tdh, ctlr->tdh); ctlr->tdt = 0; csr32w(ctlr, Tdt, ctlr->tdt); ctlr->tb = malloc(sizeof(Block*)*Ntdesc); r = csr32r(ctlr, Txdctl); r &= ~(WthreshMASK|PthreshMASK); r |= 4<type == i82575 || ctlr->type == i82576) r |= Qenable; csr32w(ctlr, Txdctl, r); /* * Don't enable checksum offload. In practice, it interferes with * tftp booting on at least the 82575. */ // csr32w(ctlr, Rxcsum, Tuofl | Ipofl | ETHERHDRSIZE<> 16; } static int eeload(Ctlr* ctlr) { ushort sum; int data, adr; sum = 0; for (adr = 0; adr < 0x40; adr++) { data = eeread(ctlr, adr); ctlr->eeprom[adr] = data; sum += data; } return sum; } static void detach(Ctlr *ctlr) { int r; csr32w(ctlr, Imc, ~0); csr32w(ctlr, Rctl, 0); csr32w(ctlr, Tctl, 0); delay(10); r = csr32r(ctlr, Ctrl); if(ctlr->type == i82566 || ctlr->type == i82567) r |= Phy_rst; csr32w(ctlr, Ctrl, Devrst | r); /* apparently needed on multi-GHz processors to avoid infinite loops */ delay(1); while(csr32r(ctlr, Ctrl) & Devrst) ; if(1 || ctlr->type != i82563){ r = csr32r(ctlr, Ctrl); csr32w(ctlr, Ctrl, Slu | r); } csr32w(ctlr, Ctrlext, Eerst | csr32r(ctlr, Ctrlext)); delay(1); while(csr32r(ctlr, Ctrlext) & Eerst) ; csr32w(ctlr, Imc, ~0); delay(1); while(csr32r(ctlr, Icr)) ; } static void i82563detach(Ether *edev) { detach(edev->ctlr); } static void i82563shutdown(Ether* ether) { i82563detach(ether); } static int fcycle(Ctlr *, Flash *f) { ushort s, i; s = f->reg[Fsts]; if((s&Fvalid) == 0) return -1; f->reg[Fsts] |= Fcerr | Ael; for(i = 0; i < 10; i++){ if((s&Scip) == 0) return 0; delay(1); s = f->reg[Fsts]; } return -1; } static int fread(Ctlr *c, Flash *f, int ladr) { ushort s; delay(1); if(fcycle(c, f) == -1) return -1; f->reg[Fsts] |= Fdone; f->reg32[Faddr] = ladr; /* setup flash control register */ s = f->reg[Fctl]; s &= ~(0x1f << 8); s |= (2-1) << 8; /* 2 bytes */ s &= ~(2*Flcycle); /* read */ f->reg[Fctl] = s | Fgo; while((f->reg[Fsts] & Fdone) == 0) ; if(f->reg[Fsts] & (Fcerr|Ael)) return -1; return f->reg32[Fdata] & 0xffff; } static int fload(Ctlr *c) { ulong data, io, r, adr; ushort sum; Flash f; Pcidev *p; // io = c->pcidev->mem[1].bar & ~0x0f; // f.reg = vmap(io, c->pcidev->mem[1].size); // if(f.reg == nil) // return -1; p = c->pcidev; io = upamalloc(p->mem[1].bar & ~0x0F, p->mem[1].size, 0); if(io == 0){ print("igbepcie: can't map flash @ 0x%8.8lux\n", p->mem[1].bar); return -1; } f.reg = KADDR(io); f.reg32 = (ulong*)f.reg; f.sz = f.reg32[Bfpr]; r = f.sz & 0x1fff; if(csr32r(c, Eec) & (1<<22)) ++r; r <<= 12; sum = 0; for (adr = 0; adr < 0x40; adr++) { data = fread(c, &f, r + adr*2); if(data == -1) break; c->eeprom[adr] = data; sum += data; } // vunmap(f.reg, c->pcidev->mem[1].size); return sum; } static int i82563reset(Ctlr* ctlr) { int i, r; detach(ctlr); if(ctlr->type == i82566 || ctlr->type == i82567) r = fload(ctlr); else r = eeload(ctlr); if (r != 0 && r != 0xBABA){ print("%s: bad EEPROM checksum - 0x%4.4ux\n", tname[ctlr->type], r); return -1; } for(i = Ea; i < Eaddrlen/2; i++){ ctlr->ra[2*i] = ctlr->eeprom[i]; ctlr->ra[2*i+1] = ctlr->eeprom[i]>>8; } r = (csr32r(ctlr, Status) & Lanid) >> 2; ctlr->ra[5] += r; /* ea ctlr[1] = ea ctlr[0]+1 */ 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); } for(i = 0; i < 128; i++) csr32w(ctlr, Mta+i*4, 0); 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); ilock(&ctlr->imlock); csr32w(ctlr, Imc, ~0); ctlr->im = 0; /* was = Lsc, which hangs some controllers */ csr32w(ctlr, Ims, ctlr->im); iunlock(&ctlr->imlock); return 0; } static void i82563pci(void) { int port, type, cls; Pcidev *p; Ctlr *ctlr; static int first = 1; if (first) first = 0; else return; p = nil; while(p = pcimatch(p, 0x8086, 0)){ if(p->ccrb != 0x02 || p->ccru != 0) continue; //print("i82563pci: did %4.4#x\n", p->did); switch(p->did){ case 0x1096: case 0x10ba: type = i82563; break; case 0x1049: /* mm */ case 0x104a: /* dm */ case 0x104b: /* dc */ case 0x104d: /* mc */ case 0x10bd: /* dm */ case 0x294c: /* dc-2 */ type = i82566; break; case 0x10cd: /* lf */ case 0x10ce: /* v-2 */ case 0x10de: /* lm-3 */ case 0x10f5: /* lm-2 */ type = i82567; break; case 0x10a4: case 0x105e: type = i82571; break; case 0x10b9: /* sic, 82572gi */ case 0x107d: type = i82572; break; case 0x108b: /* v */ case 0x108c: /* e (iamt) */ case 0x109a: /* l */ type = i82573; break; // case 0x10d3: /* l */ // type = i82574; /* never heard of it */ // break; case 0x10a7: /* 82575eb */ type = i82575; break; case 0x10c9: /* 82576 copper */ case 0x10e6: /* 82576 fiber */ case 0x10e7: /* 82576 serdes */ type = i82576; break; default: continue; } port = upamalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0); if(port == 0){ print("%s: can't map %d @ 0x%8.8lux\n", tname[type], p->mem[0].size, p->mem[0].bar); continue; } if(p->pcr & MemWrInv){ cls = pcicfgr8(p, PciCLS) * 4; if(cls != CACHELINESZ) pcicfgw8(p, PciCLS, CACHELINESZ/4); } cls = pcicfgr8(p, PciCLS); switch(cls){ default: print("%s: unexpected CLS - %d bytes\n", tname[type], cls*sizeof(long)); break; case 0x00: case 0xFF: /* alphapc 164lx returns 0 */ print("%s: unusable PciCLS: %d, using %d longs\n", tname[type], cls, CACHELINESZ/sizeof(long)); cls = CACHELINESZ/sizeof(long); pcicfgw8(p, PciCLS, cls); break; case 0x08: case 0x10: break; } ctlr = malloc(sizeof(Ctlr)); ctlr->port = port; ctlr->pcidev = p; ctlr->cls = cls*4; ctlr->type = type; ctlr->nic = KADDR(ctlr->port); if(i82563reset(ctlr)){ free(ctlr); continue; } pcisetbme(p); if(ctlrhead != nil) ctlrtail->next = ctlr; else ctlrhead = ctlr; ctlrtail = ctlr; } } static uchar nilea[Eaddrlen]; int i82563pnp(Ether* edev) { Ctlr *ctlr; if(ctlrhead == nil) i82563pci(); /* * Any adapter matches if no edev->port is supplied, * otherwise the ports must match. */ for(ctlr = ctlrhead; 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; if(memcmp(edev->ea, nilea, Eaddrlen) == 0) memmove(edev->ea, ctlr->ra, Eaddrlen); i82563init(edev); /* * Linkage to the generic ethernet driver. */ edev->attach = i82563attach; edev->transmit = i82563transmit; edev->interrupt = i82563interrupt; edev->detach = i82563detach; /* * with the current structure, there is no right place for this. * ideally, we recognize the interface, note it's down and move on. * currently either we can skip the interface or note it is down, * but not both. if((csr32r(ctlr, Status)&Lu) == 0){ print("ether#%d: 82563 (%s): link down\n", edev->ctlrno, tname[ctlr->type]); return -1; } */ return 0; }