/* * marvell 88e8057 yukon2 * copyright © 2009-10 erik quanstrom */ #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" #define is64() (sizeof(uintmem) == 8) #define dprint(...) if(debug) print(__VA_ARGS__); else {} enum { Nctlr = 4, Nrb = 1024, Rbalign = 64, Fprobe = 1<<0, Sringcnt = 2048, Tringcnt = 512, // Rringcnt = Nrb, Rringcnt = 512, }; enum { /* pci registers */ Pciphy = 0x40, Pciclk = 0x80, Pciasp = 0x84, Pcistate = 0x88, Pcicf0 = 0x90, Pcicf1 = 0x94, /* “csr” registers */ Ctst = 0x0004/2, /* control and status */ Pwrctl = 0x0007, /* power control */ Isr = 0x0008/4, /* interrupt src */ Ism = 0x000c/4, /* interrupt mask */ Hwe = 0x0010/4, /* hw error */ Hwem = 0x0014/4, /* hw error mask*/ Isrc2 = 0x001c/4, Eisr = 0x0024/4, Lisr = 0x0028/4, /* leave isr */ Macadr = 0x0100, /* mac address 2ports*3 */ Pmd = 0x0119, Maccfg = 0x011a, Chip = 0x011b, Ramcnt = 0x011c, /* # of 4k blocks */ Hres = 0x011e, Clkgate = 0x011d, Clkctl = 0x0120/4, Tstctl1 = 0x0158, Tstctl2 = 0x0159, Gpio = 0x015c/4, Rictl = 0x01a0, /* ri ram buffer ctl */ Rib = 0x0190, /* ri buffer0 */ /* other unoffset registers */ Asfcs = 0x0e68, /* asf command and status */ Asfhost = 0x0e6c/4, Statctl = 0x0e80/4, /* status */ Stattl = 0x0e84/2, /* tail (previous) status addr */ Stataddr = 0x0e88/4, /* status address low */ Statth = 0x0e98/2, Stathd = 0x0e9c/2, Statwm = 0x0eac, /* stat watermark */ Statiwm = 0x0ead, /* isr stat watermark */ Dpolltm = 0x0e08/4, /* descriptor pool timer */ /* timers */ Tgv = 0x0e14/4, /* gmac timer current value */ Tgc = 0x0e18, /* gmac timer ctl */ Tgt = 0x0e1a, /* gmac timer test */ Tsti = 0x0ec0/4, /* stat tx timer ini */ Tlti = 0x0eb0/4, /* level */ Titi = 0x0ed0/4, /* isr */ Tstc = 0x0ec8, /* stat tx timer ctl */ Tltc = 0x0eb8, /* level timer ctl */ Titc = 0x0ed8, /* isr timer ctl */ /* “gmac” registers */ Stat = 0x000/2, Ctl = 0x004/2, Txctl = 0x008/2, Rxctl = 0x00c/2, Txflow = 0x010/2, Txparm = 0x014/2, Serctl = 0x018/2, /* serial mode */ Mchash = 0x034/2, /* 4 registers; 4 bytes apart */ /* interrupt sources and masks */ Txirq = 0x044/2, Rxirq = 0x048/2, Trirq = 0x04c/2, /* tx/rx overflow irq source */ Txmask = 0x050/2, Rxmask = 0x054/2, Trmask = 0x058/2, Smictl = 0x080/2, /* serial mode control */ Smidata = 0x084/2, Phyaddr = 0x088/2, Ea0 = 0x01c/2, /* 3 16 bit gmac registers */ Ea1 = 0x028/2, Stats = 0x0100/4, /* mac registers */ Txactl = 0x210, /* transmit arbiter ctl */ Grxea = 0x0c40/4, /* rx fifo end address */ Gfrxctl = 0x0c48/4, /* gmac rxfifo ctl */ Grxfm = 0x0c4c/4, /* fifo flush mask */ Grxft = 0x0c50/4, /* fifo flush threshold */ Grxtt = 0x0c54/4, /* rx truncation threshold */ Gmfea = 0x0d40/4, /* end address */ Gmfae = 0x0d44/4, /* almost empty thresh */ Gmfctl = 0x0d48/4, /* tx gmac fifo ctl */ Rxphi = 0x0c58, /* pause high watermark */ Rxplo = 0x0c5c, /* pause low watermark */ Rxwp = 0x0c60/4, Rxwlev = 0x0c68/4, Rxrp = 0x0c70/4, Rxrlev = 0x0c78/4, Mac = 0x0f00/4, /* global mac control */ Phy = 0x0f04/4, /* phy control register */ Irq = 0x0f08, /* irq source */ Irqm = 0x0f0c, /* irq mask */ Linkctl = 0x0f10, /* queue registers; all offsets from Qbase*/ Qbase = 0x0400, Qportsz = 0x0080, /* BOTCH; tx diff is 2x rx diff */ Qr = 0x000, Qtxs = 0x200, Qtx = 0x280, /* queue offsets */ Qd = 0x00, Qvlan = 0x20, Qdone = 0x24, Qaddrl = 0x28, Qaddrh = 0x2c, Qbc = 0x30, Qcsr = 0x34, /* 32bit */ Qtest = 0x38, Qwm = 0x40, /* buffer registers; all offsets from Rbase */ Rbase = 0x0800, Rstart = 0x00, Rend = 0x04, Rwp = 0x08, Rrp = 0x0c, Rpon = 0x10, /* pause frames on */ Rpoff = 0x14, /* pause frames off */ Rhon = 0x18, /* high-priority frames on */ Rhoff = 0x1c, /* high-priority frames off */ Rctl = 0x28, /* prefetch */ Pbase = 0x450, Pctl = 0x00, Plidx = 0x04, /* last addr; 16 bit */ Paddrl = 0x08, Paddrh = 0x0c, Pgetidx = 0x10, /* 16 bit */ Pputidx = 0x14, /* 16 bit */ Pfifow = 0x20, /* 8 bit */ Pfifor = 0x24, /* 8 bit */ Pfifowm = 0x20, /* 8 bit */ /* indirect phy registers */ Phyctl = 0x000, Phystat = 0x001, Phyid0 = 0x002, Phyid1 = 0x003, Phyana = 0x004, /* auto neg advertisement */ Phylpa = 0x005, /* link partner ability */ Phyanee = 0x006, /* auto neg adv expansion */ Phynp = 0x007, /* next page */ Phylnp = 0x008, /* link partner next page */ Gbectl = 0x009, Gbestat = 0x00a, Phyphy = 0x010, /* phy specific ctl */ Phylstat = 0x011, Phyintm = 0x012, /* phy interrupt mask */ Phyint = 0x013, Phyextctl = 0x014, Phyrxe = 0x015, /* rx error counter */ Phypage = 0x016, /* external address */ Phypadr = 0x01d, /* phy page address */ }; enum { /* Pciasp */ Aspforce = 1<<15, Aspglinkdn = 1<<14, /* gphy link down */ Aspfempty = 1<<13, Aspclkrun = 1<<12, Aspmsk = Aspforce | Aspglinkdn | Aspfempty | Aspclkrun, /* Pcistate */ Vmain = 3<<27, /* Stat */ Sfast = 1<<15, /* 100mbit */ Duplex = 1<<14, Txnofc = 1<<13, /* tx flow control disabled */ Link = 1<<12, /* link up */ Pausest = 1<<11, /* pause state */ Txactive = 1<<10, Excesscol = 1<<9, Latecol = 1<<8, Physc = 1<<5, /* phy status change */ Sgbe = 1<<4, /* gbe speed */ Rxnofc = 1<<2, /* rx flow control disabled */ Promisc = 1<<1, /* promiscuous mode enabled */ /* Ctl */ Promiscen = 1<<14, Txfcdis = 1<<13, Txen = 1<<12, Rxen = 1<<11, Bursten = 1<<10, Loopen = 1<<9, Gbeen = 1<<7, Fpass = 1<<6, /* "force link pass" ? */ Duplexen = 1<<5, Rxfcdis = 1<<4, Fasten = 1<<3, /* enable 100mbit */ Adudis = 1<<2, /* disable auto upd duplex */ Afcdis = 1<<1, /* disable auto upd flow ctl */ Aspddis = 1<<0, /* disable auto upd speed */ /* Rxctl */ Ufilter = 1<<15, /* unicast filter */ Mfilter = 1<<14, /* multicast filter */ Rmcrc = 1<<13, /* remove frame crc */ /* Serctl */ Vlanen = 1<<9, Jumboen = 1<<8, /* Txactl */ Txaclr = 1<<1, Txarst = 1<<0, /* Asfcs: yukex only */ Asfbrrst = 1<<9, /* bridge reset */ Asfcpurst = 1<<8, /* cpu reset */ Asfucrst = 3<<0, /* µctlr reset */ /* Asfcs */ Asfhvos = 1<<4, /* os present */ Asfrst = 1<<3, Asfrun = 1<<2, Asfcirq = 1<<1, Afsirq = 1<<0, /* Statctl */ Statirqclr = 1<<4, Staton = 1<<3, Statoff = 1<<2, Statclr = 1<<1, Statrst = 1<<0, /* Mac */ Nomacsec = 1<<13 | 1<<11, Nortx = 1<<9, Macpause = 1<<3, Macpauseoff = 1<<2, Macrstclr = 1<<1, Macrst = 1<<0, /* Phy */ Gphyrstclr = 1<<1, Gphyrst = 1<<0, /* Irqm */ Txovfl = 1<<5, /* tx counter overflow */ Rxovfl = 1<<4, /* rx counter overflow */ Txurun = 1<<3, /* transmit fifo underrun */ Txdone = 1<<2, /* frame tx done */ Rxorun = 1<<1, /* rx fifo overrun */ Rxdone = 1<<0, /* frame rx done */ /* Linkctl */ Linkclr = 1<<1, Linkrst = 1<<0, /* Smictl */ Smiread = 1<<5, Smiwrite = 0<<5, Smirdone = 1<<4, Smibusy = 1<<3, /* Phyaddr */ Mibclear = 1<<5, /* Ctst */ Asfdis = 1<<12, /* asf disable */ Clken = 1<<11, /* enable clock */ Swirq = 1<<7, Swirqclr = 1<<6, Mstopped = 1<<5, /* master is stopped */ Mstop = 1<<4, /* stop master */ Mstrclr = 1<<3, /* master reset clear */ Mstrrset = 1<<2, /* master reset */ Swclr = 1<<1, Swrst = 1<<0, /* Pwrctl */ Vauxen = 1<<7, Vauxdis = 1<<6, Vccen = 1<<5, Vccdis = 1<<4, Vauxon = 1<<3, Vauxoff = 1<<2, Vccon = 1<<1, Vccoff = 1<<0, /* timers */ Tstart = 1<<2, Tstop = 1<<1, Tclrirq = 1<<0, /* Dpolltm */ Pollstart = 1<<1, Pollstop = 1<<0, /* csr interrupts: Isrc2, Eisr, etc. */ Ihwerr = 1<<31, Ibmu = 1<<30, /* sring irq */ Isoftware = 1<<25, Iphy = 1<<4, Imac = 1<<3, Irx = 1<<2, Itxs = 1<<1, /* descriptor error */ Itx = 1<<0, /* descriptor error */ Iport = 0x1f, Iphy2base = 8, Ierror = (Imac | Itx | Irx)*(1 | 1<event != 0; } static void unstarve(Kproc *k) { k->event = 1; wakeup(k); } static void starve(Kproc *k) { sleep(k, icansleep, k); k->event = 0; } static Status* getslot(Sring *r, Kproc *k) { if(r->rp + r->m - r->wp & ~r->m) starve(k); return r->r + (r->wp++ & r->m); } static int getnslot(Sring *r, uint *wp, Status **t, uint n) { int i; if(r->rp + r->m - (n - 1) - wp[0] & ~r->m) return -1; for(i = 0; i < n; i++) t[i] = r->r + (wp[0]++ & r->m); return 0; } /* assume allocs come from a single thread; 30*0.999x speedup */ static Block* rballoc(int t) { Block *b; if((b = rbtab[t].x) != nil){ rbtab[t].nfast++; rbtab[t].x = b->next; b->next = nil; return b; } ilock(&rbtab[t]); b = rbtab[t].x = rbtab[t].b; rbtab[t].b = nil; if(b == nil) rbtab[t].starve = 1; iunlock(&rbtab[t]); rbtab[t].nslow++; if(b != nil){ rbtab[t].x = b->next; b->next = nil; } return b; } static void rbfree(Block *b, int t) { b->rp = b->wp = (uchar*)ROUND((uintptr)b->base, Rbalign); b->flag &= ~(Bipck | Budpck | Btcpck | Bpktck); ilock(&rbtab[t]); b->next = rbtab[t].b; if(b->next == nil && rbtab[t].starve){ rbtab[t].starve = 0; unstarve(rbtab[t].k); } rbtab[t].b = b; iunlock(&rbtab[t]); } static void rbfree0(Block *b) { rbfree(b, 0); } static void rbfree1(Block *b) { rbfree(b, 1); } static void rbfree2(Block *b) { rbfree(b, 2); } static void rbfree3(Block *b) { rbfree(b, 3); } static Freefn freetab[Nctlr] = { rbfree0, rbfree1, rbfree2, rbfree3, }; static uint macread32(Ctlr *c, uint r) { return c->reg[c->portno*0x20 + r]; } static void macwrite32(Ctlr *c, uint r, uint v) { c->reg[c->portno*0x20 + r] = v; } static uint macread16(Ctlr *c, uint r) { return c->reg16[c->portno*0x40 + r]; } static void macwrite16(Ctlr *c, uint r, uint v) { c->reg16[c->portno*0x40 + r] = v; } static uint macread8(Ctlr *c, uint r) { return c->reg8[c->portno*0x80 + r]; } static void macwrite8(Ctlr *c, uint r, uint v) { c->reg8[c->portno*0x80 + r] = v; } static uint gmac32[2] = { 0x2800/4, 0x3800/4, }; static ushort gmacread32(Ctlr *c, uint r) { return c->reg[gmac32[c->portno] + r]; } static void gmacwrite32(Ctlr *c, uint r, uint v) { c->reg[gmac32[c->portno] + r] = v; } static uint gmac[2] = { 0x2800/2, 0x3800/2, }; static ushort gmacread(Ctlr *c, uint r) { return c->reg16[gmac[c->portno] + r]; } static void gmacwrite(Ctlr *c, uint r, ushort v) { c->reg16[gmac[c->portno] + r] = v; } static uint qrread(Ctlr *c, uint r) { return c->reg[Qbase + c->portno*Qportsz + r>>2]; } static void qrwrite(Ctlr *c, uint r, uint v) { c->reg[Qbase + c->portno*Qportsz + r>>2] = v; } static uint qrread16(Ctlr *c, uint r) { return c->reg16[Qbase + c->portno*Qportsz + r>>1]; } static void qrwrite16(Ctlr *c, uint r, uint v) { c->reg16[Qbase + c->portno*Qportsz + r>>1] = v; } static uint qrread8(Ctlr *c, uint r) { return c->reg8[Qbase + c->portno*Qportsz + r>>0]; } static void qrwrite8(Ctlr *c, uint r, uint v) { c->reg8[Qbase + c->portno*Qportsz + r>>0] = v; } static uint rrread32(Ctlr *c, uint r) { return c->reg[Rbase + c->portno*Qportsz + r>>2]; } static void rrwrite32(Ctlr *c, uint r, uint v) { c->reg[Rbase + c->portno*Qportsz + r>>2] = v; } static void rrwrite8(Ctlr *c, uint r, uint v) { c->reg8[Rbase + c->portno*Qportsz + r] = v; } static uint rrread8(Ctlr *c, uint r) { return c->reg8[Rbase + c->portno*Qportsz + r]; } static uint prread32(Ctlr *c, uint r) { return c->reg[Pbase + c->portno*Qportsz + r>>2]; } static void prwrite32(Ctlr *c, uint r, uint v) { c->reg[Pbase + c->portno*Qportsz + r>>2] = v; } static uint prread16(Ctlr *c, uint r) { return c->reg16[Pbase + c->portno*Qportsz + r>>1]; } static void prwrite16(Ctlr *c, uint r, uint v) { c->reg16[Pbase + c->portno*Qportsz + r>>1] = v; } static ushort phyread(Ctlr *c, uint r) { ushort v; gmacwrite(c, Smictl, Smiread | r<<6); for(;;){ v = gmacread(c, Smictl); if(v == 0xffff) error("phy read"); if(v & Smirdone) return gmacread(c, Smidata); microdelay(10); } } static ushort phywrite(Ctlr *c, uint r, ushort v) { gmacwrite(c, Smidata, v); gmacwrite(c, Smictl, Smiwrite | r<<6); for(;;){ v = gmacread(c, Smictl); if(v == 0xffff) error("phy write"); if((v & Smibusy) == 0) return gmacread(c, Smidata); microdelay(10); } } static void bufinit(Ctlr *c, uint q, uint start, uint end) { uint t; rrwrite8(c, q + Rctl, Rrstclr); rrwrite32(c, q + Rstart, start); rrwrite32(c, q + Rend, end-1); rrwrite32(c, q + Rwp, start); rrwrite32(c, q + Rrp, start); if(q == Qr || q == Qr + Qportsz){ t = start-end; rrwrite32(c, q + Rpon, t - 8192/8); rrwrite32(c, q + Rpoff, t - 16384/8); } else rrwrite8(c, q + Rctl, Rsfon); rrwrite8(c, q + Rctl, Renable); rrread8(c, q + Rctl); } static void qinit(Ctlr *c, uint queue) { qrwrite(c, queue + Qcsr, Qallclr); qrwrite(c, queue + Qcsr, Qgo); qrwrite(c, queue + Qcsr, Qfifoon); qrwrite16(c, queue + Qwm, 0x600); /* magic */ // qrwrite16(c, queue + Qwm, 0x80); /* pcie magic; assume pcie; no help */ } /* initialized prefetching */ static void pinit(Ctlr *c, uint queue, Sring *r) { union { uchar u[4]; uint l; } u; prwrite32(c, queue + Pctl, Prefrst); prwrite32(c, queue + Pctl, Prefrstclr); putle(u.u, Pciwaddrh(r->r), 4); prwrite32(c, queue + Paddrh, u.l); putle(u.u, Pciwaddrl(r->r), 4); prwrite32(c, queue + Paddrl, u.l); prwrite16(c, queue + Plidx, r->m); prwrite32(c, queue + Pctl, Prefon); prread32(c, queue + Pctl); } static void txinit(Ether *e) { Ctlr *c; Sring *r; c = e->ctlr; r = &c->tx; if(c->txinit == 1) return; c->txinit = 1; r->wp = 0; r->rp = 0; qinit(c, Qtx); pinit(c, Qtx, &c->tx); } static void linkup(Ctlr *c, uint w) { static Lock l; lock(&l); gmacwrite(c, Ctl, w|gmacread(c, Ctl)); unlock(&l); } static void tproc(void *v) { Block *b; Ctlr *c; Ether *e; Kproc *k; Sring *r; Status *t; e = v; c = e->ctlr; k = &c->txmit; r = &c->tx; txinit(e); linkup(c, Txen); for(;;){ if((b = qbread(e->oq, 100000)) == nil) break; if(Pciwaddrh(b->rp) != 0){ t = getslot(r, k); t->ctl = 0; t->op = Oaddr64 | Hw; putle(t->status, Pciwaddrh(b->rp), 4); } t = getslot(r, k); c->tbring[t - r->r] = b; putle(t->status, Pciwaddrl(b->rp), 4); putle(t->l, BLEN(b), 2); t->op = Opkt | Hw; t->ctl = Eop; sfence(); prwrite16(c, Qtx + Pputidx, r->wp & r->m); } print("#l%d: tproc: queue closed\n", e->ctlrno); pexit("queue closed", 1); } static void rxinit(Ether *e) { int i; Ctlr *c; Block *b; Sring *r; Status *t; c = e->ctlr; r = &c->rx; if(c->rxinit == 1) return; c->rxinit = 1; for(i = 0; i < Nrb; i++){ b = allocb(c->rbsz + Rbalign); b->free = freetab[c->qno]; freeb(b); } qinit(c, Qr); if(c->type == Yukecu && (c->rev == 2 || c->rev == 3)) qrwrite(c, Qr + Qtest, Qramdis); pinit(c, Qr, &c->rx); if((c->flag & Fnewle) == 0){ t = getslot(r, &c->rxmit); putle(t->status, 14<<16 | 14, 4); t->ctl = 0; t->op = Ock | Hw; qrwrite(c, Qr + Qcsr, Qsumen); } macwrite32(c, Gfrxctl, Gftroff); } /* debug; remove */ #include "yukdump.h" static int rxscrew(Ether *e, Sring *r, Status *t, uint wp) { Ctlr *c; c = e->ctlr; if(wp - r->rp > r->cnt){ print("rxscrew1 wp %ud(%ud) rp %ud %lud\n", wp, r->wp, r->rp, t-r->r); return -1; } if(c->rbring[t - r->r]){ print("rxscrew2 wp %ud rp %ud %lud\n", wp, r->rp, t-r->r); descriptorfu(e, Qr); return -1; } return 0; } static int replenish(Ether *e, Ctlr *c) { int req, n, lim; uint wp; Block *b; Sring *r; Status *tab[2], *t; r = &c->rx; wp = r->wp; req = 1 + is64(); lim = r->cnt/2; if(lim > 128) lim = 128; /* hw limit? */ for(n = 0; n < lim; n++){ b = rballoc(c->qno); if(b == nil || getnslot(r, &wp, tab, req) == -1){ freeb(b); break; } t = tab[0]; if(is64()){ putle(t->status, Pciwaddrh(b->wp), 4); t->ctl = 0; t->op = Oaddr64 | Hw; t = tab[1]; } if(rxscrew(e, r, t, wp) == -1) break; assert(c->rbring[t - r->r] == nil); c->rbring[t - r->r] = b; putle(t->status, Pciwaddrl(b->wp), 4); putle(t->l, c->rbsz, 2); t->ctl = 0; t->op = Opkt | Hw; } if(n>0){ sfence(); prwrite16(c, Qr + Pputidx, wp & r->m); r->wp = wp; dprint("yuk: replenish %d %ud-%ud [%d-%d]\n", n, r->rp, wp, r->rp&r->m, wp&r->m); } return lim - n == 0; } static void rproc(void *v) { Ctlr *c; Ether *e; Kproc *k; e = v; c = e->ctlr; k = &c->rxmit; rxinit(e); linkup(c, Rxen); for(;;) if(replenish(e, c) == 0){ starve(k); print("yuk: rx unstarve?\n"); } } static void promiscuous(void *a, int on) { uint r; Ether *e; Ctlr *c; e = a; c = e->ctlr; r = gmacread(c, Rxctl); if(on) r &= ~(Ufilter|Mfilter); else r |= Ufilter|Mfilter; gmacwrite(c, Rxctl, r); } static uchar pauseea[] = {1, 0x80, 0xc2, 0, 0, 1}; static void multicast(void *a, uchar *ea, int on) { uchar f[8]; uint i, r, b; Ctlr *c; Ether *e; Mc **ll, *l, *p; e = a; c = e->ctlr; r = gmacread(c, Rxctl); if(on){ for(ll = &c->mc; *ll != nil; ll = &(*ll)->next) if(memcmp((*ll)->ea, ea, Eaddrlen) == 0) return; *ll = malloc(sizeof **ll); memmove((*ll)->ea, ea, Eaddrlen); }else{ for(p = nil, l = c->mc; l != nil; p = l, l = l->next) if(memcmp(l->ea, ea, Eaddrlen) == 0) break; if(l == nil) return; if(p != nil) p->next = l->next; else c->mc = l->next; free(l); } memset(f, 0, sizeof f); if(0 /* flow control */){ b = ethercrc(pauseea, Eaddrlen) & 0x3f; f[b>>3] |= 1 << (b & 7); } for(l = c->mc; l != nil; l = l->next){ b = ethercrc(l->ea, Eaddrlen) & 0x3f; f[b>>3] |= 1 << (b & 7); } for(i = 0; i < sizeof f / 2; i++) gmacwrite(c, Mchash + 2*i, f[i] | f[i+1]<<8); gmacwrite(c, Rxctl, r | Mfilter); } static int spdtab[4] = { 10, 100, 1000, 0, }; static void link(Ether *e) { uint i, s, spd; Ctlr *c; c = e->ctlr; i = phyread(c, Phyint); s = phyread(c, Phylstat); dprint("#l%d: yuk: link %.8ux %.8ux\n", e->ctlrno, i, s); spd = 0; e->link = (s & Plink) != 0; if(e->link && c->feat&Ffiber) spd = 1000; else if(e->link){ spd = s & Physpd; spd >>= 14; spd = spdtab[spd]; } e->mbps = spd; dprint("#l%d: yuk: link %d spd %d\n", e->ctlrno, e->link, e->mbps); } static void txcleanup(Ctlr *c, uint end) { uint rp0, rp; Block *b; Sring *r; Status *t; r = &c->tx; rp0 = r->rp & r->m; for(rp = rp0; rp != end; rp = r->rp & r->m){ t = r->r + rp; r->rp++; if((t->ctl & Eop) == 0) continue; b = c->tbring[rp]; c->tbring[rp] = nil; if(b != nil) freeb(b); } if(r->wp - r->rp > 16){ /* BOTCH */ print("TX unstarve %ud - %ud \n", r->wp, r->rp); unstarve(&c->txmit); } } static void rx(Ether *e, uint l, uint x, uint flag) { uint cnt, i, rp; Block *b; Ctlr *c; Sring *r; c = e->ctlr; r = &c->rx; for(rp = r->rp;;){ i = rp++&r->m; b = c->rbring[i]; c->rbring[i] = nil; if(b != nil) break; } cnt = x>>16 & 0x7fff; if(cnt != l || x&Rxerror && !(c->type == Yukfep && c->rev == 0)){ print("#l%d: yuk rx error %.4ux\n", e->ctlrno, x&0xffff); freeb(b); }else{ b->wp += l; b->lim = b->wp; /* lie like a dog */ b->flag |= flag; etheriq(e, b, 1); } r->rp = rp; } static uint cksum(Ctlr *c, uint ck, uint css) { if(c->flag & Fnewle && css&(Cisip4|Cisip6) && css&Ctcpok) return Bipck | Btcpck | Budpck; else if(ck == 0xffff || ck == 0) return Bipck; return 0; } static void sring(Ether *e) { uint i, p, lim, op, l, x; Ctlr *c; Sring *r; Status *s; static uint ck = Badck; c = e->ctlr; r = &c->status; lim = r->rp & r->m; p = 0; for(;;){ if((r->rp & r->m) == lim){ lim = c->reg16[Stathd]; if((r->rp & r->m) == lim) break; } i = r->rp & r->m; s = r->r + i; op = s->op; if((op & Hw) == 0) break; op &= ~Hw; switch(op){ case Orxchks: ck = getle(s->status, 4) & 0xffff; break; case Orxstat: l = getle(s->l, 2); x = getle(s->status, 4); rx(e, l, x, cksum(c, ck, s->ctl)); ck = Badck; p++; break; case Otxidx: l = getle(s->l, 2); x = getle(s->status, 4); txcleanup(c, x & 0xfff); x = l>>24 & 0xff | l<< 8; x &= 0xfff; if(x != 0 && c->oport) txcleanup(c->oport, x); break; default: print("#l%d: yuk: funny opcode %.2ux\n", e->ctlrno, op); break; } s->op = 0; r->rp++; } while(p && replenish(e, c) != 0) ; c->reg[Statctl] = Statirqclr; } enum { Pciaer = 0x1d00, Pciunc = 0x0004, }; static void hwerror(Ether *e, uint cause) { uint u; Ctlr *c; c = e->ctlr; cause = c->reg[Hwe]; if(cause == 0) print("hwe: no cause\n"); if(cause & Htsof){ c->reg8[Tgc] = Tgclr; cause &= ~Htsof; } if(cause & (Hmerr | Hstatus)){ c->reg8[Tstctl1] = Tstwen; u = pcicfgr16(c->p, PciPSR) | 0x7800; pcicfgw16(c->p, PciPSR, u); c->reg8[Tstctl1] = Tstwdis; cause &= ~(Hmerr | Hstatus); } if(cause & Hpcie){ c->reg8[Tstctl1] = Tstwen; c->reg[Pciaer + Pciunc>>2] = ~0; u = c->reg[Pciaer + Pciunc>>2]; USED(u); print("#l%d: pcierror %.8ux\n", e->ctlrno, u); c->reg8[Tstctl1] = Tstwdis; cause &= ~Hpcie; } if(cause & Hrxparity){ print("#l%d: ram parity read error. bug? ca %.8ux\n", e->ctlrno, cause); qrwrite(c, Qtx + Qcsr, Qcirqpar); cause &= ~Hrxparity; } if(cause & Hrparity){ print("#l%d: ram parity read error. bug? ca %.8ux\n", e->ctlrno, cause); descriptorfu(e, Qr); descriptorfu(e, Qtx); c->reg16[Rictl + c->portno*0x40>>1] = Rirpclr; cause &= ~Hrparity; } if(cause & Hwparity){ print("#l%d: ram parity write error. bug? ca %.8ux\n", e->ctlrno, cause); descriptorfu(e, Qr); descriptorfu(e, Qtx); c->reg16[Rictl + c->portno*0x40>>1] = Riwpclr; cause &= ~Hwparity; } if(cause & Hmfault){ print("#l%d: mac parity error\n", e->ctlrno); macwrite32(c, Gmfctl, Gmfcpe); cause &= ~Hmfault; } if(cause) print("#l%d: leftover hwe %.8ux\n", e->ctlrno, cause); } static void macintr(Ether *e) { uint cause; Ctlr *c; c = e->ctlr; cause = macread8(c, Irq); cause &= ~(Rxdone | Txdone); if(cause == 0) return; print("#l%d: mac error %.8ux\n", e->ctlrno, cause); if(cause & Txovfl){ gmacread32(c, Txirq); cause &= ~Txovfl; } if(cause & Rxovfl){ gmacread32(c, Rxirq); cause &= ~Rxovfl; } if(cause & Rxorun){ macwrite32(c, Gfrxctl, Gmfcfu); cause &= ~Rxorun; } if(cause & Txurun){ macwrite32(c, Gmfctl, Gmfcfu); cause &= ~Txurun; } if(cause) print("#l%d: leftover mac error %.8ux\n", e->ctlrno, cause); } static struct { uint i; uint q; char *s; } emap[] = { Irx, Qr, "qr", Itxs, Qtxs, "qtxs", Itx, Qtx, "qtx", Irx<ctlr; if(cause & Imac){ macintr(e); cause &= ~Imac; } if(cause & (Irx | Itxs | Itx)*(1 | 1<ctlrno, emap[i].s, o, cause); descriptorfu(e, q); qrwrite(c, emap[i].q + Qcsr, Qcirqck); cause &= ~emap[i].i; } if(cause) print("#l%d: leftover error %.8ux\n", e->ctlrno, cause); } static void iproc(void *v) { uint cause, d; Ether *e; Ctlr *c; Kproc *k; e = v; c = e->ctlr; k = &c->iproc; for(;;){ starve(k); cause = c->reg[Eisr]; if(cause & Iphy) link(e); if(cause & Ihwerr) hwerror(e, cause); if(cause & Ierror) eerror(e, cause & Ierror); if(cause & Ibmu) sring(e); d = c->reg[Lisr]; USED(d); } } static void interrupt(Ureg*, void *v) { uint cause; Ctlr *c; Ether *e; e = v; c = e->ctlr; cause = c->reg[Isrc2]; if(cause != 0 && cause != ~0) unstarve(&c->iproc); } static void storefw(Ctlr *c) { if(c->type == Yukex && c->rev != 1 || c->type == Yukfep || c->type == Yuksup) macwrite32(c, Gmfctl, Gmfjon | Gmfsfon); else{ macwrite32(c, Gmfae, 0x8000 | 0x70); /* tx gmac fifo */ macwrite32(c, Gmfctl, Gmfsfoff); } } static void raminit(Ctlr *c) { uint ram, rx; if(ram = c->reg8[Ramcnt] * 4096/8){ /* in qwords */ c->flag |= Fram; rx = ROUNDUP((2*ram)/3, 1024/8); bufinit(c, Qr, 0, rx); bufinit(c, Qtx, rx, ram); rrwrite8(c, Qtxs + Rctl, Rrst); /* sync tx off */ }else{ macwrite8(c, Rxplo, 768/8); macwrite8(c, Rxphi, 1024/8); storefw(c); } } static void attach(Ether *e) { char buf[KNAMELEN]; Ctlr *c; static Lock l; c = e->ctlr; if(c->attach == 1) return; lock(&l); if(c->attach == 1){ unlock(&l); return; } c->attach = 1; unlock(&l); snprint(buf, sizeof buf, "#l%dtproc", e->ctlrno); kproc(buf, tproc, e); snprint(buf, sizeof buf, "#l%drproc", e->ctlrno); kproc(buf, rproc, e); snprint(buf, sizeof buf, "#l%diproc", e->ctlrno); kproc(buf, iproc, e); c->reg[Ism] |= Ibmu | Iport<portno; } static long ifstat(Ether *e0, void *a, long n, ulong offset) { char *s, *e, *p; int i; uint u; Ctlr *c; c = e0->ctlr; p = s = malloc(READSTR); e = p + READSTR; for(i = 0; i < nelem(stattab); i++){ u = gmacread32(c, Stats + stattab[i].offset/4); if(u > 0) p = seprint(p, e, "%s\t%ud\n", stattab[i].name, u); } p = seprint(p, e, "stat %.4ux ctl %.3ux\n", gmacread(c, Stat), gmacread(c, Ctl)); p = seprint(p, e, "irq %.8ux\n", c->reg[Isrc2]); p = seprint(p, e, "pref %.8ux %.4ux\n", prread32(c, Qr + Pctl), prread16(c, Qr + Pgetidx)); p = seprint(p, e, "nfast %ud nslow %ud\n", rbtab[c->qno].nfast, rbtab[c->qno].nslow); if(debug){ p = dumppci(c, p, e); p = dumpgmac(c, p, e); p = dumpmac(c, p, e); p = dumpreg(c, p, e); } seprint(p, e, "%s rev %d phy %s\n", idtab[c->type].name, c->rev, c->feat&Ffiber? "fiber": "copper"); n = readstr(offset, a, n, s); free(s); return n; } static Cmdtab ctltab[] = { 1, "debug", 1, 2, "descriptorfu", 1, }; static long ctl(Ether *e, void *buf, long n) { Cmdbuf *cb; Cmdtab *t; cb = parsecmd(buf, n); if(waserror()){ free(cb); nexterror(); } t = lookupcmd(cb, ctltab, nelem(ctltab)); switch(t->index){ case 0: debug ^= 1; break; case 1: descriptorfu(e, Qr); break; } free(cb); poperror(); return n; } static uint yukpcicfgr32(Ctlr *c, uint r) { return c->reg[r + 0x1c00>>2]; } static void yukpcicfgw32(Ctlr *c, uint r, uint v) { c->reg[r + 0x1c00>>2] = v; } static void phypower(Ctlr *c) { uint u, u0; u = u0 = yukpcicfgr32(c, Pciphy); u &= ~phypwr[c->portno]; if(c->type == Yukxl && c->rev > 1) u |= coma[c->portno]; if(u != u0 || 1){ c->reg8[Tstctl1] = Tstwen; yukpcicfgw32(c, Pciphy, u); c->reg8[Tstctl1] = Tstwdis; } if(c->type == Yukfe) c->reg8[Phyctl] = Aneen; else if(c->flag & Fapwr) macwrite32(c, Phy, Gphyrstclr); } static void phyinit(Ctlr *c) { uint u; if((c->feat & Fnewphy) == 0){ u = phyread(c, Phyextctl); u &= ~0xf70; /* clear downshift counters */ u |= 0x7<<4; /* mac tx clock = 25mhz */ if(c->type == Yukec) u |= 2*Dnmstr | Dnslv; else u |= Dnslv; phywrite(c, Phyextctl, u); } u = phyread(c, Phyphy); /* questionable value */ if(c->feat & Ffiber) u &= ~Ppmdix; else if(c->feat & Fgbe){ u &= ~Pped; u |= Ppmdixa; if(c->flag & Fnewphy){ // u &= ~(7<<12); // u |= 2*(1<<12) | 1<<11; /* like 2*Dnmstr | Dnslv */ u |= 2*(1<<9) | 1<<11; } }else u |= Ppmdixa >> 1; /* why the shift? */ phywrite(c, Phyphy, u); /* copper/fiber specific stuff gmacwrite(c, Ctl, 0); */ gmacwrite(c, Ctl, 0); if(c->feat & Fgbe) if(c->feat & Ffiber) phywrite(c, Gbectl, Gbexf | Gbexh); else phywrite(c, Gbectl, Gbef | Gbeh); phywrite(c, Phyana, Anall); phywrite(c, Phyctl, Phyrst | Anerst | Aneen); /* chip specific stuff? */ if(c->type == Yukfep){ u = phyread(c, Phyphy) | Ppnpe; u &= ~(Ppengy | Ppscrdis); phywrite(c, Phyphy, u); /* yukfep and rev 0: apply workaround for integrated resistor calibration */ // phywrite(c, 0x16, 0x0b54); /* write to fe_led_par */ phywrite(c, Phypadr, 17); phywrite(c, 0x1e, 0x3f60); } phywrite(c, Phyintm, Anok | Anerr | Lsc); dprint("phyid %.4ux step %.4ux\n", phyread(c, 2), phyread(c, 3)); } static int identify(Ctlr *c) { char t; pcicfgw32(c->p, Pciclk, 0); c->reg16[Ctst] = Swclr; c->type = c->reg8[Chip] - 0xb3; c->rev = c->reg8[Maccfg]>>4 & 0xf; if(c->type >= Nyuk) return -1; if(idtab[c->type].okrev != 0xff) if(c->rev != idtab[c->type].okrev) return -1; c->feat |= idtab[c->type].feat; t = c->reg8[Pmd]; if(t == 'L' || t == 'S' || t == 'P') c->feat |= Ffiber; c->portno = 0; /* check second port ... whatever */ return 0; } static uint µ2clk(Ctlr *c, int µs) { return idtab[c->type].mhz * µs; } static void gmacsetea(Ctlr *c, uint r) { uchar *ra; int i; ra = c->ra; for(i = 0; i < Eaddrlen; i += 2) gmacwrite(c, r + i, ra[i + 0] | ra[i + 1]<<8); } static int reset(Ctlr *c) { uint i, j; Block *b; identify(c); if(c->type == Yukex) c->reg16[Asfcs/2] &= ~(Asfbrrst | Asfcpurst | Asfucrst); else c->reg8[Asfcs] = Asfrst; c->reg16[Ctst] = Asfdis; c->reg16[Ctst] = Swrst; c->reg16[Ctst] = Swclr; c->reg8[Tstctl1] = Tstwen; pcicfgw16(c->p, PciPSR, pcicfgr16(c->p, PciPSR) | 0xf100); c->reg16[Ctst] = Mstrclr; /* fixup pcie extended error goes here */ c->reg8[Pwrctl] = Vauxen | Vccen | Vauxoff | Vccon; c->reg[Clkctl] = Clkdivdis; if(c->type == Yukxl && c->rev > 1) c->reg8[Clkgate] = ~Link2inactive; else c->reg8[Clkgate] = 0; if(c->flag & Fapwr){ pcicfgw32(c->p, Pciclk, 0); pcicfgw32(c->p, Pciasp, pcicfgr32(c->p, Pciasp) & Aspmsk); pcicfgw32(c->p, Pcistate, pcicfgr32(c->p, Pcistate) & Vmain); pcicfgw32(c->p, Pcicf1, 0); c->reg[Gpio] |= Norace; print("yuk2: advanced power %.8ux\n", c->reg[Gpio]); } c->reg8[Tstctl1] = Tstwdis; for(i = 0; i < c->nports; i++){ macwrite8(c, Linkctl, Linkrst); macwrite8(c, Linkctl, Linkclr); if(c->type == Yukex || c->type == Yuksup) macwrite16(c, Mac, Nomacsec | Nortx); } c->reg[Dpolltm] = Pollstop; for(i = 0; i < c->nports; i++) macwrite8(c, Txactl, Txaclr); for(i = 0; i < c->nports; i++){ c->reg8[i*64 + Rictl] = Riclr; for(j = 0; j < 12; j++) c->reg8[i*64 + Rib + j] = 36; /* qword times */ } c->reg[Hwem] = Hdflt; macwrite8(c, Irqm, 0); for(i = 0; i < 4; i++) gmacwrite(c, Mchash + 2*i, 0); gmacwrite(c, Rxctl, Ufilter | Mfilter | Rmcrc); for(i = 0; i < nelem(c->tbring); i++) if(b = c->tbring[i]){ c->tbring[i] = nil; freeb(b); } for(i = 0; i < nelem(c->rbring); i++) if(b = c->rbring[i]){ c->rbring[i] = nil; freeb(b); } memset(c->tbring, 0, sizeof c->tbring[0] * nelem(c->tbring)); memset(c->rbring, 0, sizeof c->rbring[0] * nelem(c->rbring)); memset(c->tx.r, 0, sizeof c->tx.r[0] * c->tx.cnt); memset(c->rx.r, 0, sizeof c->rx.r[0] * c->rx.cnt); memset(c->status.r, 0, sizeof c->status.r[0] * c->status.cnt); c->reg[Statctl] = Statrst; c->reg[Statctl] = Statclr; c->reg[Stataddr + 0] = Pciwaddrl(c->status.r); c->reg[Stataddr + 4] = Pciwaddrh(c->status.r); c->reg16[Stattl] = c->status.m; c->reg16[Statth] = 10; c->reg8[Statwm] = 16; if(c->type == Yukxl && c->rev == 0) c->reg8[Statiwm] = 4; else c->reg8[Statiwm] = 4; //16; /* set transmit, isr, level timers */ c->reg[Tsti] = µ2clk(c, 1000); c->reg[Titi] = µ2clk(c, 20); c->reg[Tlti] = µ2clk(c, 100); c->reg[Statctl] = Staton; c->reg8[Tstc] = Tstart; c->reg8[Tltc] = Tstart; c->reg8[Titc] = Tstart; return 0; } static void macinit(Ctlr *c) { uint r, i; r = macread32(c, Phy) & ~(Gphyrst | Gphyrstclr); macwrite32(c, Phy, r | Gphyrst); macwrite32(c, Phy, r | Gphyrstclr); /* macwrite32(c, Mac, Macrst); ? */ macwrite32(c, Mac, Macrstclr); if(c->type == Yukxl && c->rev == 0 && c->portno == 1){ } macread8(c, Irq); macwrite8(c, Irqm, Txurun); phypower(c); phyinit(c); gmacwrite(c, Phyaddr, (r = gmacread(c, Phyaddr)) | Mibclear); for(i = 0; i < nelem(stattab); i++) gmacread32(c, Stats + stattab[i].offset/4); gmacwrite(c, Phyaddr, r); gmacwrite(c, Txctl, 4<<10); /* collision distance */ gmacwrite(c, Txflow, 0xffff); /* flow control */ gmacwrite(c, Txparm, 3<<14 | 0xb<<9 | 0x1c<<4 | 4); gmacwrite(c, Rxctl, Ufilter | Mfilter | Rmcrc); gmacwrite(c, Serctl, 0x04<<11 /* blind */ | Jumboen | 0x1e /* ipig */); gmacsetea(c, Ea0); gmacsetea(c, Ea1); gmacwrite(c, Txmask, 0); gmacwrite(c, Rxmask, 0); gmacwrite(c, Trmask, 0); macwrite32(c, Gfrxctl, Gfrstclr); r = Gfon | Gffon; if(c->type == Yukex || c->type == Yukfep) r |= Gfroon; macwrite32(c, Gfrxctl, r); if(c->type == Yukxl) macwrite32(c, Grxfm, 0); else macwrite32(c, Grxfm, Ferror); if(c->type == Yukfep && c->rev == 0) macwrite32(c, Grxft, 0x178); else macwrite32(c, Grxft, 0xb); macwrite32(c, Gmfctl, Gmfclr); /* clear reset */ macwrite32(c, Gmfctl, Gmfon); /* on */ raminit(c); if(c->type == Yukfep && c->rev == 0) c->reg[Gmfea] = c->reg[Gmfea] & ~3; c->rxinit = 0; c->txinit = 0; } static void* slice(void **v, uint r, uint sz) { uintptr a; a = (uintptr)*v; a = ROUNDUP(a, r); *v = (void*)(a + sz); return (void*)a; } static void setupr(Sring *r, uint cnt) { r->rp = 0; r->wp = 0; r->cnt = cnt; r->m = cnt - 1; } static int setup(Ctlr *c) { uint n; void *v, *mem; Pcidev *p; p = c->p; c->io = p->mem[0].bar&~0xf; mem = vmap(c->io, p->mem[0].size); if(mem == nil){ print("yuk: cant map %#p\n", c->io); return -1; } c->p = p; c->reg = (uint*)mem; c->reg8 = (uchar*)mem; c->reg16 = (ushort*)mem; if(memcmp(c->ra, nilea, sizeof c->ra) == 0) memmove(c->ra, c->reg8 + Macadr + 8*c->portno, Eaddrlen); setupr(&c->status, Sringcnt); setupr(&c->tx, Tringcnt); setupr(&c->rx, Rringcnt); n = sizeof c->status.r[0] * (c->status.cnt + c->tx.cnt + c->rx.cnt); n += 16*4096*2; /* rounding slop */ c->alloc = xspanalloc(n, 16*4096, 0); /* unknown alignment constraints */ memset(c->alloc, 0, n); v = c->alloc; c->status.r = slice(&v, 16*4096, sizeof c->status.r[0] * c->status.cnt); c->tx.r = slice(&v, 16*4096, sizeof c->tx.r[0] * c->tx.cnt); c->rx.r = slice(&v, 16*4096, sizeof c->rx.r[0] * c->rx.cnt); c->nports = 1; /* BOTCH */ pcisetbme(p); if(reset(c)){ print("yuk: cant reset\n"); pciclrbme(p); free(c->alloc); vunmap(mem, p->mem[0].size); return -1; } macinit(c); return 0; } static void shutdown(Ether *e) { Ctlr *c; Pcidev *p; c = e->ctlr; reset(c); if(0){ p = c->p; vunmap(c->reg, p->mem[0].size); free(c->alloc); } } static void scan(void) { int i; Pcidev *p; Ctlr *c; for(p = nil; p = pcimatch(p, 0, 0); ){ for(i = 0; i < nelem(vtab); i++) if(vtab[i].vid == p->vid) if(vtab[i].did == p->did) break; if(i == nelem(vtab)) continue; if(nctlr == nelem(ctlrtab)){ print("yuk: too many controllers\n"); return; } c = malloc(sizeof *c); c->p = p; c->qno = nctlr; rbtab[c->qno].k = &c->rxmit; c->rbsz = vtab[i].mtu; ctlrtab[nctlr++] = c; } } static int pnp(Ether *e) { int i; Ctlr *c; if(nctlr == 0) scan(); for(i = 0;; i++){ if(i == nctlr) return -1; c = ctlrtab[i]; if(c == nil || c->flag&Fprobe) continue; if(e->port != 0 && e->port != (ulong)c->reg) continue; c->flag |= Fprobe; if(setup(c) != 0) continue; break; } e->ctlr = c; e->port = c->io; e->irq = c->p->intl; e->tbdf = c->p->tbdf; e->mbps = 1000; e->maxmtu = c->rbsz; memmove(e->ea, c->ra, Eaddrlen); e->arg = e; e->attach = attach; e->ctl = ctl; e->ifstat = ifstat; e->interrupt = interrupt; e->multicast = multicast; e->promiscuous = promiscuous; e->shutdown = shutdown; e->transmit = nil; return 0; } void etheryuklink(void) { addethercard("yuk", pnp); }