--- /sys/src/9/pc/etheryuk.c Wed Feb 24 00:00:00 2016 +++ /sys/src/9/pc/etheryuk.c Wed Feb 24 00:00:00 2016 @@ -0,0 +1,2330 @@ +/* + * 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(uintptr) == 8) +#define dprint(...) if(debug) print(__VA_ARGS__); else {} +#define Pciwaddrh(va) (sizeof(uintptr)>4? (uvlong)PCIWADDR(va)>>32: 0) +#define Pciwaddrl(va) PCIWADDR(va) + +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); + le32put(u.u, Pciwaddrh(r->r)); + prwrite32(c, queue + Paddrh, u.l); + le32put(u.u, Pciwaddrl(r->r)); + 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); +} + +extern void sfence(void); + +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; + le32put(t->status, Pciwaddrh(b->rp)); + } + t = getslot(r, k); + c->tbring[t - r->r] = b; + le32put(t->status, Pciwaddrl(b->rp)); + le16put(t->l, BLEN(b)); + 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); + le32put(t->status, 14<<16 | 14); + 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()){ + le32put(t->status, Pciwaddrh(b->wp)); + 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; + + le32put(t->status, Pciwaddrl(b->wp)); + le16put(t->l, c->rbsz); + 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 = gbit32(s->status) & 0xffff; + break; + case Orxstat: + l = gbit16(s->l); + x = gbit32(s->status); + rx(e, l, x, cksum(c, ck, s->ctl)); + ck = Badck; + p++; + break; + case Otxidx: + l = gbit16(s->l); + x = gbit32(s->status); + 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; + mem = vmap(c->io, p->mem[0].size); + if(mem == nil){ + print("yuk: cant map %#ulx\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->io = p->mem[0].bar & ~(uintptr)0xf; + 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); +} --- /sys/src/9/pc/yukdump.h Wed Feb 24 00:00:00 2016 +++ /sys/src/9/pc/yukdump.h Wed Feb 24 00:00:00 2016 @@ -0,0 +1,319 @@ +typedef struct Regdump Regdump; +struct Regdump { + uint offset; + uint size; + char *name; +}; + +Regdump pcireg[] = { + Pciphy, 32, "Pciphy", + Pciclk, 32, "Pciclk", + Pcistate, 32, "Pcistate", +}; + +static Regdump gmacreg[] = { + Stat, 16, "Stat", + Ctl, 16, "Ctl", + Txctl, 16, "Txctl", + Rxctl, 16, "Rxctl", + Txflow, 16, "Txflow", + Txparm, 16, "Txparm", + Serctl, 16, "Serctl", + Txirq, 16, "Txirq", + Rxirq, 16, "Rxirq", + Trirq, 16, "Trirq", + Txmask, 16, "Txmask", + Rxmask, 16, "Rxmask", + Trmask, 16, "Trmask", + Smictl, 16, "Smictl", + Smidata, 16, "Smidata", + Phyaddr, 16, "Phyaddr", + Mchash+0, 16, "Mchash", + Mchash+2, 16, "Mchash", + Mchash+4, 16, "Mchash", + Mchash+6, 16, "Mchash", + Ea0, 16, "Ea0", + Ea0+2, 16, "Ea0", + Ea0+4, 16, "Ea0", + Ea1, 16, "Ea1", + Ea1+2, 16, "Ea1", + Ea1+4, 16, "Ea1", +}; + +static Regdump macreg[] = { + Txactl, 8, "Txactl", + Gfrxctl, 32, "Gfrxctl", + Grxfm, 32, "Grxfm", + Grxft, 32, "Grxft", + Grxtt, 32, "Grxtt", + Gmfctl, 32, "Gmfctl", + Mac, 32, "Mac", + Phy, 32, "Phy", + Irqm, 8, "Irqm", + Linkctl, 8, "Linkctl", + + Rxwp, 32, "Rxwp", + Rxrp, 32, "Rxrp", + Rxrlev, 32, "Rxrlev", + +}; + +static Regdump reg[] = { + Ctst, 16, "Ctst", + Pwrctl, 8, "Pwrctl", + Isr, 32, "Isr", + Ism, 32, "Ism", + Hwe, 32, "Hwe", + Hwem, 32, "Hwem", + Isrc2, 32, "Isrc2", + + Macadr/2, 16, "Macadr", + Macadr/2+1, 16, "Macadr", + Macadr/2+2, 16, "Macadr", + + Pmd, 8, "Pmd", + Maccfg, 8, "Maccfg", + Chip, 8, "Chip", + Ramcnt, 8, "Ramcnt", + Clkgate, 8, "Clkgate", + Hres, 8, "Hres", + Clkctl, 32, "Clkctl", + Tstctl1, 8, "Tstctl1", + + Asfcs, 8, "Asfcs", + Asfhost, 32, "Asfhost", + Statctl, 32, "Statctl", + Stattl, 16, "Stattl", + Stataddr, 32, "Stataddr", + Statth, 16, "Statth", + Stathd, 16, "Stathd", + Statwm, 8, "Statwm", + Statiwm, 8, "Statiwm", +}; + +static char* +dumppci(Ctlr *c, char *p, char *e) +{ + int i; + Regdump *r; + + r = pcireg; + p = seprint(p, e, "/* pci reg */\n"); + for(i = 0; i < nelem(pcireg); i++) + switch(r[i].size){ + default: + p = seprint(p, e, "%s: bug size %d\n", r[i].name, r[i].size); + break; + case 32: + p = seprint(p, e, "%s: %.8ux\n", r[i].name, pcicfgr32(c->p, r[i].offset)); + break; + } + return p; +} + +static char* +dumpgmac(Ctlr *c, char *p, char *e) +{ + int i; + Regdump *r; + + r = gmacreg; + p = seprint(p, e, "/* gmac reg */\n"); + for(i = 0; i < nelem(gmacreg); i++) + switch(r[i].size){ + default: + p = seprint(p, e, "%s: bug size %d\n", r[i].name, r[i].size); + break; + case 16: + p = seprint(p, e, "%s: %.4ux\n", r[i].name, gmacread(c, r[i].offset)); + break; + case 32: + p = seprint(p, e, "%s: %.8ux\n", r[i].name, gmacread32(c, r[i].offset)); + break; + } + return p; +} + +static char* +dumpmac(Ctlr *c, char *p, char *e) +{ + int i; + Regdump *r; + + r = macreg; + p = seprint(p, e, "/* mac reg */\n"); + for(i = 0; i < nelem(macreg); i++) + switch(r[i].size){ + default: + p = seprint(p, e, "%s: bug size %d\n", r[i].name, r[i].size); + break; + case 8: + p = seprint(p, e, "%s: %.2ux\n", r[i].name, macread8(c, r[i].offset)); + break; + case 32: + p = seprint(p, e, "%s: %.8ux\n", r[i].name, macread32(c, r[i].offset)); + break; + } + return p; +} + +static char* +dumpreg(Ctlr *c, char *p, char *e) +{ + int i; + Regdump *r; + + r = reg; + p = seprint(p, e, "/* reg */\n"); + for(i = 0; i < nelem(reg); i++) + switch(r[i].size){ + default: + p = seprint(p, e, "%s: bug size %d\n", r[i].name, r[i].size); + break; + case 8: + p = seprint(p, e, "%s: %.2ux\n", r[i].name, c->reg8[r[i].offset]); + break; + case 16: + p = seprint(p, e, "%s: %.4ux\n", r[i].name, c->reg16[r[i].offset]); + break; + case 32: + p = seprint(p, e, "%s: %.8ux\n", r[i].name, c->reg[r[i].offset]); + break; + } + return p; +} + +static char *optab[256] = { +[Orxchks] "rxsum", +[Orxstat] "rxstat", +[Otxidx] "txidx", +}; + +static char* +rs(uint r) +{ + char *s; + + s = optab[r & 0xff]; + if(s == nil) + s = ""; + return s; +} + +static int +nblocktab(int qno) +{ + uint i; + Block *b; + + ilock(&rbtab[qno]); + b = rbtab[qno].b; + for(i = 0;; i++){ + if(b == nil) + break; + b = b->next; + } + iunlock(&rbtab[qno]); + return i; +} + +static char* +dumpring(Sring *r, Block **t, int qno, char *p, char *e) +{ + int m, n; + uint i; + + p = seprint(p, e, + "bring: rp %ud wp %ud cnt %ud m %ud : ", + r->rp, r->wp, r->cnt, r->m); + m = -1; + n = 0; + for(i = 0; i < r->cnt; i++){ + n += t[i] != nil; + if(m>=0 && t[i] == nil){ + p = seprint(p, e, "%ud", m); + if(i != m + 1) + p = seprint(p, e, "-%ud", i-1); + p = seprint(p, e, " "); + m = -1; + }else if(m<0 && t[i] != nil) + m = i; + } + if(m>=0){ + p = seprint(p, e, "%ud", m); + if(i != m + 1) + p = seprint(p, e, "-%ud ", i-1); + } + p = seprint(p, e, "n=%d/%d", n, r->cnt); + return seprint(p, e, " list %d\n", nblocktab(qno)); +} + +static ushort +gbit16(void *a) +{ + uchar *i; + + i = a; + return i[1]<<8 | i[0]; +} + +static ulong +gbit32(void *a) +{ + ulong j; + uchar *i; + + i = a; + j = i[3] << 24; + j |= i[2] << 16; + j |= i[1] << 8; + j |= i[0]; + return j; +} + +/* debug; remove */ +static void +descriptorfu(Ether *e, uint qoff) +{ + char buf[PRINTSIZE], *p, *f; + uint q, qm1; + Block *b, *a, **bring; + Ctlr *c; + Status *t, *v; + Sring *r; + + c = e->ctlr; + f = buf + sizeof buf; + if(qoff == Qtx){ + bring = c->tbring; + r = &c->tx; + p = seprint(buf, f, "tx "); + }else{ + bring = c->rbring; + r = &c->rx; + p = seprint(buf, f, "rx "); + } + + q = prread16(c, qoff + Pgetidx); + print(" getidx %ud\n", q); + if(q >= r->cnt){ + q &= r->m; + print(" try getidx %ud\n", q); + } + qm1 = q-1 & r->m; + t = r->r + q; + v = r->r + qm1; + b = bring[q]; + a = bring[qm1]; + print(" %0.4d %.2ux l=%d p=%#p @%#p\n", q, t->op, + (uint)gbit16(t->l), KZERO+(ulong)gbit32(t->status), Pciwaddrl(t)); + print(" %0.4d %.2ux l=%d p=%#p @%#p\n", qm1, t->op, + (uint)gbit16(v->l), KZERO+(ulong)gbit32(v->status), Pciwaddrl(v)); + if(r == &c->rx) + print(" %#p %#p (wp)\n", b? b->wp: 0, a? a->wp: 0); + else + print(" %#p %#p (rp)\n", b? b->rp: 0, a? a->rp: 0); + + dumpring(r, bring, c->qno, p, f); + print(" %s", buf); +}