static void wpiinterrupt(Ureg*, void *arg) { u32int isr, fhisr; Ether *edev; Ctlr *ctlr; edev = arg; ctlr = edev->ctlr; ilock(ctlr); csr32w(ctlr, Imr, 0); isr = csr32r(ctlr, Isr); fhisr = csr32r(ctlr, FhIsr); if(isr == 0xffffffff || (isr & 0xfffffff0) == 0xa5a5a5a0){ iunlock(ctlr); return; } if(isr == 0 && fhisr == 0) goto done; csr32w(ctlr, Isr, isr); csr32w(ctlr, FhIsr, fhisr); if((isr & (Iswrx | Ifhrx)) || (fhisr & Ifhrx)) receive(ctlr); if(isr & Ierr){ ctlr->broken = 1; iprint("#l%d: fatal firmware error, lastcmd %ud\n", edev->ctlrno, ctlr->tx[4].lastcmd); } ctlr->wait.m |= isr; if(ctlr->wait.m & ctlr->wait.w) wakeup(&ctlr->wait); done: csr32w(ctlr, Imr, ctlr->ie); iunlock(ctlr); }
static char* eepromread(Ctlr *ctlr, void *data, int count, uint off) { uchar *out = data; char *err; u32int w = 0; int i; if((err = niclock(ctlr)) != nil) return err; for(; count > 0; count -= 2, off++){ csr32w(ctlr, Eeprom, off << 2); csr32w(ctlr, Eeprom, csr32r(ctlr, Eeprom) & ~(1<<1)); for(i = 0; i < 10; i++){ w = csr32r(ctlr, Eeprom); if(w & 1) break; delay(5); } if(i == 10) break; *out++ = w >> 16; if(count > 1) *out++ = w >> 24; } nicunlock(ctlr); if(count > 0) return "eeprompread: timeout"; return nil; }
static char* poweron(Ctlr *ctlr) { char *err; if(ctlr->power) return nil; csr32w(ctlr, AnaPll, csr32r(ctlr, AnaPll) | Init); /* Disable L0s. */ csr32w(ctlr, GioChicken, csr32r(ctlr, GioChicken) | L1AnoL0Srx); if((err = clockwait(ctlr)) != nil) return err; if((err = niclock(ctlr)) != nil) return err; prphwrite(ctlr, ApmgClkEna, DmaClkRqt | BsmClkRqt); delay(20); /* Disable L1. */ prphwrite(ctlr, ApmgPciStt, prphread(ctlr, ApmgPciStt) | (1<<11)); nicunlock(ctlr); ctlr->power = 1; return nil; }
static void i82563interrupt(Ureg*, void* arg) { int icr, im, rdh, txdw = 0; Block *bp; Ctlr *ctlr; Ether *edev; Rdesc *rdesc; edev = arg; ctlr = edev->ctlr; ilock(&ctlr->imlock); csr32w(ctlr, Imc, ~0); im = ctlr->im; 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)) 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); } ctlr->rdh = rdh; if(icr & Rxdmt0) i82563replenish(ctlr); if(icr & Txdw){ im &= ~Txdw; txdw++; } } ctlr->im = im; csr32w(ctlr, Ims, im); iunlock(&ctlr->imlock); if(txdw) i82563transmit(edev); }
static char* niclock(Ctlr *ctlr) { int i; csr32w(ctlr, Gpc, csr32r(ctlr, Gpc) | MacAccessReq); for(i=0; i<1000; i++){ if((csr32r(ctlr, Gpc) & (NicSleep | MacAccessEna)) == MacAccessEna) return 0; delay(10); } return "niclock: timeout"; }
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 char* clockwait(Ctlr *ctlr) { int i; /* Set "initialization complete" bit. */ csr32w(ctlr, Gpc, csr32r(ctlr, Gpc) | InitDone); for(i=0; i<2500; i++){ if(csr32r(ctlr, Gpc) & MacClockReady) return nil; delay(10); } return "clockwait: timeout"; }
static void detach(Ctlr *ctlr) { int r; /* * 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. */ csr32w(ctlr, Imc, ~0); csr32w(ctlr, Rctl, 0); csr32w(ctlr, Tctl, 0); delay(20); csr32w(ctlr, Ctrl, Devrst); /* apparently needed on multi-GHz processors to avoid infinite loops */ delay(1); while(csr32r(ctlr, Ctrl) & Devrst) ; csr32w(ctlr, Ctrlext, Eerst | csr32r(ctlr, Ctrlext)); delay(1); while(csr32r(ctlr, Ctrlext) & Eerst) ; switch(ctlr->id){ default: break; case i82540em: case i82540eplp: case i82541gi: case i82541pi: case i82547gi: case i82546gb: case i82546eb: r = csr32r(ctlr, Manc); r &= ~Arpen; csr32w(ctlr, Manc, r); break; } csr32w(ctlr, Imc, ~0); delay(1); while(csr32r(ctlr, Icr)) ; }
static u32int prphread(Ctlr *ctlr, uint off) { csr32w(ctlr, PrphRaddr, ((sizeof(u32int)-1)<<24) | off); coherence(); return csr32r(ctlr, PrphRdata); }
static ushort eeread(Ctlr* ctlr, int adr) { csr32w(ctlr, Eerd, ee_start | adr << 2); while ((csr32r(ctlr, Eerd) & ee_done) == 0) ; return csr32r(ctlr, Eerd) >> 16; }
static void wpiattach(Ether *edev) { FWImage *fw; Ctlr *ctlr; char *err; ctlr = edev->ctlr; eqlock(ctlr); if(waserror()){ print("#l%d: %s\n", edev->ctlrno, up->errstr); if(ctlr->power) poweroff(ctlr); qunlock(ctlr); nexterror(); } if(ctlr->attached == 0){ if((csr32r(ctlr, Gpc) & RfKill) == 0) error("wifi disabled by switch"); if(ctlr->wifi == nil){ ctlr->wifi = wifiattach(edev, transmit); ctlr->wifi->rates = wpirates; } if(ctlr->fw == nil){ fw = readfirmware(); print("#l%d: firmware: %ux, size: %ux+%ux+%ux+%ux+%ux\n", edev->ctlrno, fw->version, fw->main.text.size, fw->main.data.size, fw->init.text.size, fw->init.data.size, fw->boot.text.size); ctlr->fw = fw; } if((err = reset(ctlr)) != nil) error(err); if((err = boot(ctlr)) != nil) error(err); ctlr->bcastnodeid = -1; ctlr->bssnodeid = -1; ctlr->channel = 1; ctlr->aid = 0; setoptions(edev); ctlr->attached = 1; kproc("wpirecover", wpirecover, edev); } qunlock(ctlr); poperror(); }
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<<i); csr32w(ctlr, Ctrl, Mdc|r); csr32w(ctlr, Ctrl, r); } csr32w(ctlr, Ctrl, ctrl); return data; }
static void igbeattach(Ether* edev) { int ctl; Ctlr *ctlr; /* * To do here: * one-time stuff; * start off a kproc for link status change: * adjust queue length depending on speed; * flow control. * more needed here... */ ctlr = edev->ctlr; igbeim(ctlr, 0); ctl = csr32r(ctlr, Rctl)|Ren; csr32w(ctlr, Rctl, ctl); ctl = csr32r(ctlr, Tctl)|Ten; csr32w(ctlr, Tctl, ctl); }
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 poweroff(Ctlr *ctlr) { int i, j; csr32w(ctlr, Reset, Nevo); /* Disable interrupts. */ csr32w(ctlr, Imr, 0); csr32w(ctlr, Isr, ~0); csr32w(ctlr, FhIsr, ~0); if(niclock(ctlr) == nil){ /* Stop TX scheduler. */ prphwrite(ctlr, AlmSchedMode, 0); prphwrite(ctlr, AlmSchedTxfact, 0); /* Stop all DMA channels */ for(i = 0; i < 6; i++){ csr32w(ctlr, FhTxConfig + i*32, 0); for(j = 0; j < 100; j++){ if((csr32r(ctlr, FhTxStatus) & (0x1010000<<i)) == (0x1010000<<i)) break; delay(10); } } nicunlock(ctlr); } /* Stop RX ring. */ if(niclock(ctlr) == nil){ csr32w(ctlr, FhRxConfig, 0); for(j = 0; j < 100; j++){ if(csr32r(ctlr, FhRxStatus) & (1<<24)) break; delay(10); } nicunlock(ctlr); } if(niclock(ctlr) == nil){ prphwrite(ctlr, ApmgClkDis, DmaClkRqt); nicunlock(ctlr); } delay(5); csr32w(ctlr, Reset, csr32r(ctlr, Reset) | StopMaster); if((csr32r(ctlr, Gpc) & (7<<24)) != (4<<24)){ for(j = 0; j < 100; j++){ if(csr32r(ctlr, Reset) & MasterDisabled) break; delay(10); } } csr32w(ctlr, Reset, csr32r(ctlr, Reset) | SW); ctlr->power = 0; }
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 char* postboot(Ctlr *ctlr) { while((ctlr->temp = (int)csr32r(ctlr, UcodeGp2)) == 0) delay(10); if(0){ char *err; if((err = btcoex(ctlr)) != nil) print("btcoex: %s\n", err); if((err = powermode(ctlr)) != nil) print("powermode: %s\n", err); } return nil; }
static int igbemiimiw(Mii* mii, int pa, int ra, int data) { Ctlr *ctlr; int mdic, timo; ctlr = mii->ctlr; data &= MDIdMASK; csr32w(ctlr, Mdic, MDIwop|(pa<<MDIpSHIFT)|(ra<<MDIrSHIFT)|data); mdic = 0; for(timo = 64; timo; timo--){ mdic = csr32r(ctlr, Mdic); if(mdic & (MDIe|MDIready)) break; microdelay(1); } if((mdic & (MDIe|MDIready)) == MDIready) return 0; return -1; }
static int igbemiimir(Mii* mii, int pa, int ra) { Ctlr *ctlr; int mdic, timo; ctlr = mii->ctlr; csr32w(ctlr, Mdic, MDIrop|(pa<<MDIpSHIFT)|(ra<<MDIrSHIFT)); mdic = 0; for(timo = 64; timo; timo--){ mdic = csr32r(ctlr, Mdic); if(mdic & (MDIe|MDIready)) break; microdelay(1); } if((mdic & (MDIe|MDIready)) == MDIready) return mdic & 0xFFFF; return -1; }
static int i82543mdiow(Ctlr* ctlr, int bits, int n) { int ctrl, i, r; /* * Write n bits to the Management Data I/O Interface. */ ctrl = csr32r(ctlr, Ctrl); r = Mdco|Mddo|ctrl; for(i = n-1; i >= 0; i--){ if(bits & (1<<i)) r |= Mdd; else r &= ~Mdd; csr32w(ctlr, Ctrl, Mdc|r); csr32w(ctlr, Ctrl, r); } csr32w(ctlr, Ctrl, ctrl); return 0; }
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 void wpirecover(void *arg) { Ether *edev; Ctlr *ctlr; edev = arg; ctlr = edev->ctlr; while(waserror()) ; for(;;){ tsleep(&up->sleep, return0, 0, 4000); qlock(ctlr); for(;;){ if(ctlr->broken == 0) break; if(ctlr->power) poweroff(ctlr); if((csr32r(ctlr, Gpc) & RfKill) == 0) break; if(reset(ctlr) != nil) break; if(boot(ctlr) != nil) break; ctlr->bcastnodeid = -1; ctlr->bssnodeid = -1; ctlr->aid = 0; rxon(edev, ctlr->wifi->bss); break; } qunlock(ctlr); } }
static int igbemii(Ctlr* ctlr) { MiiPhy *phy = (MiiPhy *)1; int ctrl, p, r; USED(phy); r = csr32r(ctlr, Status); if(r & Tbimode) return -1; if((ctlr->mii = malloc(sizeof(Mii))) == 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 i82541pi: 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){ if (0) print("phy trouble: phy = 0x%lux\n", (uint32_t)phy); free(ctlr->mii); ctlr->mii = nil; return -1; } //print("oui %X phyno %d\n", phy->oui, phy->phyno); USED(phy); /* * 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 i82541pi: 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); break; } p = 0; if(ctlr->txcw & TxcwPs) p |= AnaP; if(ctlr->txcw & TxcwAs) p |= AnaAP; miiane(ctlr->mii, ~0, p, ~0); return 0; }
static void igbeinterrupt(Ureg*, void* arg) { Block *bp; Ctlr *ctlr; Ether *edev; Rdesc *rdesc; int icr, im, rdh, txdw = 0; edev = arg; ctlr = edev->ctlr; ilock(&ctlr->imlock); csr32w(ctlr, Imc, ~0); im = ctlr->im; if((icr = csr32r(ctlr, Icr)) & ctlr->im){ /* * Link status changed. */ if(icr & (Rxseq|Lsc)){ /* * More here... */ } /* * Process any received packets. */ rdh = ctlr->rdh; for(;;){ rdesc = &ctlr->rdba[rdh]; if(!(rdesc->status & Rdd)) break; if ((rdesc->status & Reop) && rdesc->errors == 0) { bp = ctlr->rb[rdh]; ctlr->rb[rdh] = nil; /* * it appears that the original 82543 needed * to have the Ethernet CRC excluded, but that * the newer chips do not? */ bp->wp += rdesc->length /* -4 */; toringbuf(edev, bp); freeb(bp); } else if ((rdesc->status & Reop) && rdesc->errors) print("igbe: input packet error 0x%ux\n", rdesc->errors); rdesc->status = 0; rdh = NEXT(rdh, Nrdesc); } ctlr->rdh = rdh; if(icr & Rxdmt0) igbereplenish(ctlr); if(icr & Txdw){ im &= ~Txdw; txdw++; } } ctlr->im = im; csr32w(ctlr, Ims, im); iunlock(&ctlr->imlock); if(txdw) igbetransmit(edev); }
static int igbeinit(Ether* edev) { int csr, i, r, ctrl, timeo; MiiPhy *phy; Ctlr *ctlr; ctlr = edev->ctlr; /* * 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). */ csr = (edev->ea[3]<<24)|(edev->ea[2]<<16)|(edev->ea[1]<<8)|edev->ea[0]; csr32w(ctlr, Ral, csr); csr = 0x80000000|(edev->ea[5]<<8)|edev->ea[4]; csr32w(ctlr, Rah, csr); 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); /* * Receive initialisation. * Mostly defaults from the datasheet, will * need some tuning for performance: * Rctl descriptor mimimum threshold size * discard pause frames * strip CRC * Rdtr interrupt delay * Rxdctl all the thresholds */ csr32w(ctlr, Rctl, 0); /* * Allocate the descriptor ring and load its * address and length into the NIC. */ ctlr->rdba = xspanalloc(Nrdesc*sizeof(Rdesc), 128 /* was 16 */, 0); csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba)); csr32w(ctlr, Rdbah, 0); csr32w(ctlr, Rdlen, Nrdesc*sizeof(Rdesc)); /* * Initialise the ring head and tail pointers and * populate the ring with Blocks. * The datasheet says the tail pointer is set to beyond the last * descriptor hardware can process, which implies the initial * condition is Rdh == Rdt. However, experience shows Rdt must * always be 'behind' Rdh; the replenish routine ensures this. */ ctlr->rdh = 0; csr32w(ctlr, Rdh, ctlr->rdh); ctlr->rdt = 0; csr32w(ctlr, Rdt, ctlr->rdt); ctlr->rb = malloc(sizeof(Block*)*Nrdesc); igbereplenish(ctlr); /* * Set up Rctl but don't enable receiver (yet). */ csr32w(ctlr, Rdtr, 0); switch(ctlr->id){ case i82540em: case i82540eplp: case i82541gi: case i82541pi: case i82546gb: case i82546eb: case i82547gi: csr32w(ctlr, Radv, 64); break; } csr32w(ctlr, Rxdctl, (8<<WthreshSHIFT)|(8<<HthreshSHIFT)|4); /* * Enable checksum offload. */ csr32w(ctlr, Rxcsum, Tuofl|Ipofl|(ETHERHDRSIZE<<PcssSHIFT)); csr32w(ctlr, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF); /* * VirtualBox does not like Rxt0, * it continually interrupts. */ ctlr->im |= /*Rxt0|*/Rxo|Rxdmt0|Rxseq; /* * Transmit initialisation. * Mostly defaults from the datasheet, will * need some tuning for performance. The normal mode will * be full-duplex and things to tune for half-duplex are * Tctl re-transmit on late collision * Tipg all IPG times * Tbt burst timer * Ait adaptive IFS throttle * and in general * Txdmac packet prefetching * Ett transmit early threshold * Tidv interrupt delay value * Txdctl all the thresholds */ csr32w(ctlr, Tctl, (0x0F<<CtSHIFT)|Psp|(66<<ColdSHIFT)); /* Fd */ switch(ctlr->id){ default: r = 6; break; case i82543gc: case i82544ei: case i82547ei: case i82540em: case i82540eplp: case i82541gi: case i82541pi: 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, Tidv, 128); /* * Allocate the descriptor ring and load its * address and length into the NIC. */ ctlr->tdba = xspanalloc(Ntdesc*sizeof(Tdesc), 128 /* was 16 */, 0); csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba)); csr32w(ctlr, Tdbah, 0); csr32w(ctlr, Tdlen, Ntdesc*sizeof(Tdesc)); /* * Initialise the ring head and tail pointers. */ ctlr->tdh = 0; csr32w(ctlr, Tdh, ctlr->tdh); ctlr->tdt = 0; csr32w(ctlr, Tdt, ctlr->tdt); ctlr->tb = malloc(sizeof(Block*)*Ntdesc); // ctlr->im |= Txqe|Txdw; r = (4<<WthreshSHIFT)|(4<<HthreshSHIFT)|(8<<PthreshSHIFT); switch(ctlr->id){ default: break; case i82540em: case i82540eplp: case i82547gi: case i82541pi: case i82546gb: case i82546eb: case i82541gi: r = csr32r(ctlr, Txdctl); r &= ~WthreshMASK; r |= Gran|(4<<WthreshSHIFT); csr32w(ctlr, Tadv, 64); break; } csr32w(ctlr, Txdctl, r); r = csr32r(ctlr, Tctl); r |= Ten; csr32w(ctlr, Tctl, r); igbeim(ctlr, ctlr->im); if(ctlr->mii == nil || ctlr->mii->curphy == nil) { print("igbe: no mii (yet)\n"); return 0; } /* wait for the link to come up */ for(timeo = 0; timeo < 3500; timeo++){ if(miistatus(ctlr->mii) == 0) break; delay(10); } print("igbe: phy: "); phy = ctlr->mii->curphy; if (phy->fd) print("full duplex"); else print("half duplex"); print(", %d Mb/s\n", phy->speed); /* * Flow control. */ ctrl = csr32r(ctlr, Ctrl); if(phy->rfc) ctrl |= Rfce; if(phy->tfc) ctrl |= Tfce; csr32w(ctlr, Ctrl, ctrl); return 0; }
static long ifstat(Ether* ether, void* a, long n, ulong offset) { Ctlr *ctlr; char *buf, *p; int i, l, len; ctlr = ether->ctlr; ether->crcs = ctlr->ce; ether->frames = ctlr->rf+ctlr->cs; ether->buffs = ctlr->de+ctlr->tl; ether->overflows = ctlr->of; if(n == 0) return 0; p = malloc(READSTR); l = snprint(p, READSTR, "Overflow: %lud\n", ctlr->of); l += snprint(p+l, READSTR-l, "Ru: %lud\n", ctlr->ru); l += snprint(p+l, READSTR-l, "Rps: %lud\n", ctlr->rps); l += snprint(p+l, READSTR-l, "Rwt: %lud\n", ctlr->rwt); l += snprint(p+l, READSTR-l, "Tps: %lud\n", ctlr->tps); l += snprint(p+l, READSTR-l, "Tu: %lud\n", ctlr->tu); l += snprint(p+l, READSTR-l, "Tjt: %lud\n", ctlr->tjt); l += snprint(p+l, READSTR-l, "Unf: %lud\n", ctlr->unf); l += snprint(p+l, READSTR-l, "CRC Error: %lud\n", ctlr->ce); l += snprint(p+l, READSTR-l, "Collision Seen: %lud\n", ctlr->cs); l += snprint(p+l, READSTR-l, "Frame Too Long: %lud\n", ctlr->tl); l += snprint(p+l, READSTR-l, "Runt Frame: %lud\n", ctlr->rf); l += snprint(p+l, READSTR-l, "Descriptor Error: %lud\n", ctlr->de); l += snprint(p+l, READSTR-l, "Underflow Error: %lud\n", ctlr->uf); l += snprint(p+l, READSTR-l, "Excessive Collisions: %lud\n", ctlr->ec); l += snprint(p+l, READSTR-l, "Late Collision: %lud\n", ctlr->lc); l += snprint(p+l, READSTR-l, "No Carrier: %lud\n", ctlr->nc); l += snprint(p+l, READSTR-l, "Loss of Carrier: %lud\n", ctlr->lo); l += snprint(p+l, READSTR-l, "Transmit Jabber Timeout: %lud\n", ctlr->to); l += snprint(p+l, READSTR-l, "csr6: %luX %uX\n", csr32r(ctlr, 6), ctlr->csr6); snprint(p+l, READSTR-l, "ntqmax: %d\n", ctlr->ntqmax); ctlr->ntqmax = 0; buf = a; len = readstr(offset, buf, n, p); if(offset > l) offset -= l; else offset = 0; buf += len; n -= len; l = snprint(p, READSTR, "srom:"); for(i = 0; i < (1<<(ctlr->sromsz)*sizeof(ushort)); i++){ if(i && ((i & 0x0F) == 0)) l += snprint(p+l, READSTR-l, "\n "); l += snprint(p+l, READSTR-l, " %2.2uX", ctlr->srom[i]); } snprint(p+l, READSTR-l, "\n"); len += readstr(offset, buf, n, p); free(p); return len; }
static int igbereset(Ctlr* ctlr) { int ctrl, i, pause, r, swdpio, txcw; detach(ctlr); /* * Read the EEPROM, validate the checksum * then get the device back to a power-on state. */ r = at93c46r(ctlr); /* zero return means no SPI EEPROM access */ if (r != 0 && r != 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) && BUSFNO(ctlr->pcidev->tbdf) == 1) ctlr->eeprom[Ea+2] += 0x100; // second interface for(i = Ea; i < Eaddrlen/2; i++){ ctlr->ra[2*i] = ctlr->eeprom[i]; ctlr->ra[2*i+1] = ctlr->eeprom[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. */ memset(ctlr->mta, 0, sizeof(ctlr->mta)); 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) { 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<<SwdpioloSHIFT; csr32w(ctlr, Ctrl, ctrl); ctrl = csr32r(ctlr, Ctrlext); ctrl &= ~(Ips|SwdpiohiMASK); swdpio = (ctlr->eeprom[Icw2] & 0x00F0)>>4; if(ctlr->eeprom[Icw1] & 0x1000) ctrl |= Ips; ctrl |= swdpio<<SwdpiohiSHIFT; csr32w(ctlr, Ctrlext, ctrl); if(ctlr->eeprom[Icw2] & 0x0800) txcw |= TxcwAne; pause = (ctlr->eeprom[Icw2] & 0x3000)>>12; txcw |= pause<<TxcwPauseSHIFT; switch(pause){ default: ctlr->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); }
static void interrupt(Ureg*, void* arg) { Ctlr *ctlr; Ether *ether; int len, status; Des *des; Block *bp; ether = arg; ctlr = ether->ctlr; while((status = csr32r(ctlr, 5)) & (Nis|Ais)){ /* * Acknowledge the interrupts and mask-out * the ones that are implicitly handled. */ csr32w(ctlr, 5, status); status &= (ctlr->mask & ~(Nis|Ti)); if(status & Ais){ if(status & Tps) ctlr->tps++; if(status & Tu) ctlr->tu++; if(status & Tjt) ctlr->tjt++; if(status & Ru) ctlr->ru++; if(status & Rps) ctlr->rps++; if(status & Rwt) ctlr->rwt++; status &= ~(Ais|Rwt|Rps|Ru|Tjt|Tu|Tps); } /* * Received packets. */ if(status & Ri){ des = &ctlr->rdr[ctlr->rdrx]; while(!(des->status & Own)){ if(des->status & Es){ if(des->status & Of) ctlr->of++; if(des->status & Ce) ctlr->ce++; if(des->status & Cs) ctlr->cs++; if(des->status & Tl) ctlr->tl++; if(des->status & Rf) ctlr->rf++; if(des->status & De) ctlr->de++; } else if(bp = iallocb(Rbsz)){ len = ((des->status & Fl)>>16)-4; des->bp->wp = des->bp->rp+len; etheriq(ether, des->bp, 1); des->bp = bp; des->addr = PCIWADDR(bp->rp); } des->control &= Er; des->control |= Rbsz; coherence(); des->status = Own; ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr); des = &ctlr->rdr[ctlr->rdrx]; } status &= ~Ri; }
static int at93c46r(Ctlr* ctlr) { uint16_t 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 i82540em: case i82540eplp: case i82541gi: case i82541pi: case i82547gi: 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<<bits)|addr) != 0){ print("igbe: can't set EEPROM address 0x%2.2X\n", addr); goto release; } data = at93c46io(ctlr, ":16COc;", 0); at93c46io(ctlr, "sic", 0); ctlr->eeprom[addr] = data; sum += data; //if(addr && ((addr & 0x07) == 0)) // print("\n"); //print(" %4.4ux", data); } //print("\n"); release: if(areq) csr32w(ctlr, Eecd, eecd & ~Areq); return sum; }
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 = strtol(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<<loop)) eecd |= Di; else eecd &= ~Di; break; case 'O': /* collect data output */ i = (csr32r(ctlr, Eecd) & Do) != 0; if(loop >= 0) r |= (i<<loop); else r = i; continue; case 'I': /* assert data input */ eecd |= Di; break; case 'i': /* deassert data input */ eecd &= ~Di; break; case 'S': /* enable chip select */ eecd |= Cs; break; case 's': /* disable chip select */ eecd &= ~Cs; break; } csr32w(ctlr, Eecd, eecd); microdelay(50); } if(loop >= 0) return -1; return r; }