ref: d4124d48f9c7e85fdaa50640035080174c94196e
dir: /sys/src/9/pc/etheryuk.c/
/* * 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/pci.h" #include "../port/error.h" #include "../port/netif.h" #include "../port/etherif.h" #define Pciwaddrh(x) 0 #define Pciwaddrl(x) PCIWADDR(x) #define is64() (sizeof(uintptr) == 8) #define dprint(...) if(debug) print(__VA_ARGS__); else {} extern void sfence(void); enum { Nctlr = 4, Nrb = 1024, Rbalign = 64, Fprobe = 1<<0, Sringcnt = 2048, Tringcnt = 512, Rringcnt = 512, Rringl = Rringcnt - 8, }; 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 */ Icr = 0x002c/4, 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<<Iphy2base), /* hwe interrupts: Hwe Hwem */ Htsof = 1<<29, /* timer stamp overflow */ Hsensor = 1<<28, Hmerr = 1<<27, /* master error */ Hstatus = 1<<26, /* status exception */ Hpcie = 1<<25, /* pcie error */ Hpcie2 = 1<<24, /* " */ Hrparity = 1<<5, /* ram read parity error */ Hwparity = 1<<4, /* ram write parity error */ Hmfault = 1<<3, /* mac fault */ Hrxparity = 1<<2, /* rx parity */ Htcptxs = 1<<1, /* tcp length mismatch */ Htcptxa = 1<<0, /* tcp length mismatch */ H1base = 1<<0, H2base = 1<<8, Hmask = 0x3f, Hdflt = Htsof | Hmerr | Hstatus | Hmask*(H1base | H2base), /* Clkctl */ Clkdiven = 1<<1, Clkdivdis = 1<<0, /* Clkgate */ Link2inactive = 1<<7, /* Phyctl */ Phyrst = 1<<15, Phy100 = 1<<14, /* manual enable 100mbit */ Aneen = 1<<12, /* auto negotiation enable */ Phyoff = 1<<11, /* turn phy off */ Anerst = 1<<9, /* auto neg reset */ Phydpx = 1<<8, Phy1000 = 1<<5, /* manual enable gbe */ /* Phyana */ Annp = 1<<15, /* request next page */ Anack = 1<<14, /* ack rx (read only) */ Anrf = 1<<13, /* remote fault */ Anpa = 1<<11, /* try asymmetric pause */ Anp = 1<<10, /* try pause */ An100f = 1<<8, An100h = 1<<7, An10f = 1<<6, An10h = 1<<5, Anonly = 1<<0, Anall = An100f | An100h | An10f | An10h | Anonly, /* Gbectl */ Gbef = 1<<9, /* auto neg gbe full */ Gbeh = 1<<8, /* auto neg gbe half */ Gbexf = 1<<6, /* auto neg gbe full fiber */ Gbexh = 1<<5, /* auto neg gbe full fiber */ /* Phyphy */ Pptf = 3<<14, /* tx fifo depth */ Pprf = 3<<12, /* rx fifo depth */ Pped = 3<<8, /* energy detect */ Ppmdix = 3<<5, /* mdix conf */ Ppmdixa = 3<<5, /* automdix */ Ppengy = 1<<14, /* fe+ enable energy detect */ Ppscrdis = 1<<9, /* fe+ scrambler disable */ Ppnpe = 1<<12, /* fe+ enable next page */ /* Phylstat */ Physpd = 3<<14, Phydupx = 1<<13, Phypr = 1<<12, /* page rx */ Phydone = 1<<11, /* speed and duplex neg. done */ Plink = 1<<10, Pwirelen = 7<<7, Pmdi = 1<<6, Pdwnsh = 1<<5, /* downshift */ Penergy = 1<<4, /* energy detect */ Ptxpause = 1<<3, /* tx pause enabled */ Prxpause = 1<<2, /* rx pause enabled */ Ppol = 1<<2, /* polarity */ Pjarjar = 1<<1, /* mesa no understasa */ /* Phyintm */ Anerr = 1<<15, /* an error */ Lsp = 1<<14, /* link speed change */ Andc = 1<<13, /* an duplex change */ Anok = 1<<11, Lsc = 1<<10, /* link status change */ Symerr = 1<<9, /* symbol error */ Fcarr = 1<<8, /* false carrier */ Fifoerr = 1<<7, Mdich = 1<<6, Downsh = 1<<5, Engych = 1<<4, /* energy change */ Dtech = 1<<2, /* dte power det status */ Polch = 1<<1, /* polarity change */ Jabber = 1<<0, /* Phyextctl */ Dnmstr = 1<<9, /* master downshift; 0: 1x; 1: 2x; 2: 3x */ Dnslv = 1<<8, /* Tgc */ Tgstart = 1<<2, Tgstop = 1<<1, Tgclr = 1<<0, /* clear irq */ /* Tstctl1 */ Tstwen = 1<<1, /* enable config reg r/w */ Tstwdis = 1<<0, /* disable config reg r/w */ /* Gpio */ Norace = 1<<13, /* Rictl */ Rirpclr = 1<<9, Riwpclr = 1<<8, Riclr = 1<<1, Rirst = 1<<0, /* Rbase opcodes */ Rsfon = 1<<5, /* enable store/fwd */ Rsfoff = 1<<4, Renable = 1<<3, Rdisable = 1<<2, Rrstclr = 1<<1, Rrst = 1<<0, /* Qbase opcodes */ Qidle = 1<<31, Qtcprx = 1<<30, Qiprx = 1<<29, Qrssen = 1<<15, Qrssdis = 1<<14, Qsumen = 1<<13, /* tcp/ip cksum */ Qsumdis = 1<<12, Qcirqpar = 1<<11, /* clear irq on parity errors */ Qcirqck = 1<<10, Qstop = 1<<9, Qstart = 1<<8, Qfifoon = 1<<7, Qfifooff = 1<<6, Qfifoen = 1<<5, Qfiforst = 1<<4, Qenable = 1<<3, Qdisable = 1<<2, Qrstclr = 1<<1, Qrst = 1<<0, Qallclr = Qfiforst | Qfifooff | Qrstclr, Qgo = Qcirqpar | Qcirqck | Qstart | Qfifoen | Qenable, /* Qtest bits */ Qckoff = 1<<31, /* tx: auto checksum off */ Qckon = 1<<30, Qramdis = 1<<24, /* rx: ram disable */ /* Pbase opcodes */ Prefon = 1<<3, /* prefetch on */ Prefoff = 1<<2, Prefrstclr = 1<<1, Prefrst = 1<<0, /* ring opcodes */ Hw = 0x80, /* bitmask */ Ock = 0x12, /* tcp checksum start */ Oaddr64 = 0x21, Obuf = 0x40, Opkt = 0x41, Orxstat = 0x60, Orxts = 0x61, /* rx timestamp */ Orxvlan = 0x62, Orxchks = 0x64, Otxidx = 0x68, Omacs = 0x6c, /* macsec */ Oputidx = 0x70, /* ring status */ Eop = 0x80, /* Gfrxctl */ Gftrunc = 1<<27, Gftroff = 1<<26, Gfroon = 1<<19, /* flush on rx overrun */ Gfrooff = 1<<18, Gffon = 1<<7, /* rx fifo flush mode on */ Gffoff = 1<<6, Gfon = 1<<3, Gfoff = 1<<2, Gfrstclr = 1<<1, Gfrst = 1<<0, /* Gmfctl */ Gmfsfoff = 1<<31, /* disable store-forward (ec ultra) */ Gmfsfon = 1<<30, /* able store-forward (ec ultra) */ Gmfvon = 1<<25, /* vlan tag on */ Gmfvoff = 1<<24, /* vlan off */ Gmfjon = 1<<23, /* jumbo on (ec ultra) */ Gmfjoff = 1<<22, /* jumbo off */ Gmfcfu = 1<<6, /* clear fifio underrun irq */ Gmfcfc = 1<<5, /* clear frame complete irq */ Gmfcpe = 1<<4, /* clear parity error irq */ Gmfon = 1<<3, Gmfoff = 1<<2, Gmfclr = 1<<1, Gmfrst = 1<<0, /* rx frame */ Flen = 0x7fff<<17, Fvlan = 1<<13, Fjabbr = 1<<12, Ftoosm = 1<<11, Fmc = 1<<10, /* multicast */ Fbc = 1<<9, Fok = 1<<8, /* good frame */ Fokfc = 1<<7, Fbadfc = 1<<6, Fmiierr = 1<<5, Ftoobg = 1<<4, /* oversized */ Ffrag = 1<<3, /* fragment */ Fcrcerr = 1<<1, Ffifoof = 1<<0, /* fifo overflow */ Ferror = Ffifoof | Fcrcerr | Ffrag | Ftoobg | Fmiierr | Fbadfc | Ftoosm | Fjabbr, /* rx checksum bits in Status.ctl */ Badck = 5, /* arbitrary bad checksum */ Ctcpok = 1<<7, /* tcp or udp cksum ok */ Cisip6 = 1<<3, Cisip4 = 1<<1, /* more status ring rx bits */ Rxvlan = 1<<13, Rxjab = 1<<12, /* jabber */ Rxsmall = 1<<11, /* too small */ Rxmc = 1<<10, /* multicast */ Rxbc = 1<<9, /* bcast */ Rxok = 1<<8, Rxfcok = 1<<7, /* flow control pkt */ Rxfcbad = 1<<6, Rxmiierr = 1<<5, Rxbig = 1<<4, /* too big */ Rxfrag = 1<<3, Rxcrcerr = 1<<1, Rxfov = 1<<0, /* fifo overflow */ Rxerror = Rxfov | Rxcrcerr | Rxfrag | Rxbig | Rxmiierr | Rxfcbad | Rxsmall | Rxjab, }; enum { Ffiber = 1<<0, Fgbe = 1<<1, Fnewphy = 1<<2, Fapwr = 1<<3, Fnewle = 1<<4, Fram = 1<<5, Fancy =Fgbe | Fnewphy | Fapwr, Yukxl = 0, Yukecu, Yukex, Yukec, Yukfe, Yukfep, Yuksup, Yukul2, Yukba, /* doesn't exist */ Yukopt, Nyuk, }; typedef struct Chipid Chipid; typedef struct Ctlr Ctlr; typedef void (*Freefn)(Block*); typedef struct Kproc Kproc; typedef struct Mc Mc; typedef struct Stattab Stattab; typedef struct Status Status; typedef struct Sring Sring; typedef struct Vtab Vtab; struct Chipid { uchar feat; uchar okrev; uchar mhz; char *name; }; struct Kproc { Rendez; uint event; }; struct Sring { uint wp; uint rp; uint cnt; uint m; Status *r; }; struct Ctlr { Pcidev *p; Ctlr *oport; /* port 2 */ uchar qno; uchar attach; uchar rxinit; uchar txinit; uchar flag; uchar feat; uchar type; uchar rev; uchar nports; uchar portno; uvlong io; uchar *reg8; ushort *reg16; uint *reg; uint rbsz; uchar ra[Eaddrlen]; uint mca; uint nmc; Mc *mc; void *alloc; Sring status; Sring tx; Block *tbring[Tringcnt]; Sring rx; Block *rbring[Rringcnt]; Kproc txmit; Kproc rxmit; Kproc iproc; }; struct Mc { Mc *next; uchar ea[Eaddrlen]; }; struct Stattab { uint offset; char *name; }; struct Status { uchar status[4]; uchar l[2]; uchar ctl; uchar op; }; struct Vtab { int vid; int did; int mtu; char *name; }; static Chipid idtab[] = { [Yukxl] Fgbe | Fnewphy, 0xff, 156, "yukon-2 xl", [Yukecu] Fancy, 0xff, 125, "yukon-2 ec ultra", [Yukex] Fancy | Fnewle, 0xff, 125, "yukon-2 extreme", [Yukec] Fgbe, 2, 125, "yukon-2 ec", [Yukfe] 0, 0xff, 100, "yukon-2 fe", [Yukfep] Fnewphy|Fapwr | Fnewle, 0xff, 50, "yukon-2 fe+", [Yuksup] Fgbe | Fnewphy | Fnewle, 0xff, 125, "yukon-2 supreme", [Yukul2] Fgbe |Fapwr, 0xff, 125, "yukon-2 ultra2", [Yukba] 0, 0, 0, "??", [Yukopt] Fancy, 0xff, 125, "yukon-2 optima", }; static Vtab vtab[] = { 0x11ab, 0x4354, 1514, "88e8040", /* unsure on mtu */ 0x11ab, 0x4362, 1514, "88e8053", 0x11ab, 0x4363, 1514, "88e8055", 0x11ab, 0x4364, 1514, "88e8056", 0x11ab, 0x4380, 1514, "88e8057", 0x11ab, 0x436b, 1514, "88e8071", /* unsure on mtu */ 0x1186, 0x4b00, 9000, "dge-560t", 0x1186, 0x4b02, 1514, "dge-550sx", 0x1186, 0x4b03, 1514, "dge-550t", }; static Stattab stattab[] = { 0, "rx ucast", 8, "rx bcast", 16, "rx pause", 24, "rx mcast", 32, "rx chk seq", 48, "rx ok low", 56, "rx ok high", 64, "rx bad low", 72, "rx bad high", 80, "rx frames < 64", 88, "rx frames < 64 fcs", 96, "rx frames 64", 104, "rx frames 65-127", 112, "rx frames 128-255", 120, "rx frames 256-511", 128, "rx frames 512-1023", 136, "rx frames 1024-1518", 144, "rx frames 1519-mtu", 152, "rx frames too long", 160, "rx jabber", 176, "rx fifo oflow", 192, "tx ucast", 200, "tx bcast", 208, "tx pause", 216, "tx mcast", 224, "tx ok low", 232, "tx ok hi", 240, "tx frames 64", 248, "tx frames 65-127", 256, "tx frames 128-255", 264, "tx frames 256-511", 272, "tx frames 512-1023", 280, "tx frames 1024-1518", 288, "tx frames 1519-mtu", 304, "tx coll", 312, "tx late coll", 320, "tx excess coll", 328, "tx mul col", 336, "tx single col", 344, "tx underrun", }; static uint phypwr[] = {1<<26, 1<<27}; static uint coma[] = {1<<28, 1<<29}; static uchar nilea[Eaddrlen]; static int debug; static Ctlr *ctlrtab[Nctlr]; static int nctlr; static int icansleep(void *v) { Kproc *k; k = v; return k->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 int getnslot(Sring *r, uint *wp, Status **t, uint n) { int i; if(r->m - (int)(wp[0] - r->rp) < n) return -1; for(i = 0; i < n; i++) t[i] = r->r + (wp[0]++ & r->m); return 0; } 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 uvlong lorder = 0x0706050403020100ull; static uvlong getle(uchar *t, int w) { uint i; uvlong r; r = 0; for(i = w; i != 0; ) r = r<<8 | t[--i]; return r; } static void putle(uchar *t, uvlong r, int w) { uchar *o, *f; uint i; f = (uchar*)&r; o = (uchar*)&lorder; for(i = 0; i < w; i++) t[o[i]] = f[i]; } 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; Sring *r; Status *tab[2], *t; e = v; c = e->ctlr; r = &c->tx; txinit(e); linkup(c, Txen); while(waserror()) ; for(;;){ if((b = qbread(e->oq, 100000)) == nil) break; while(getnslot(r, &r->wp, tab, 1 + is64()) == -1) starve(&c->txmit); t = tab[is64()]; c->tbring[t - r->r] = b; if(is64()){ Status *t = tab[0]; t->ctl = 0; t->op = Oaddr64 | Hw; putle(t->status, Pciwaddrh(b->rp), 4); } 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) { Ctlr *c; Sring *r; Status *t; c = e->ctlr; r = &c->rx; if(c->rxinit == 1) return; c->rxinit = 1; 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){ while(getnslot(r, &r->wp, &t, 1) == -1) starve(&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((int)(wp - r->rp) >= r->cnt){ print("rxscrew1 wp %ud(%ud) rp %ud %zd\n", wp, r->wp, r->rp, t-r->r); return -1; } if(c->rbring[t - r->r]){ print("rxscrew2 wp %ud rp %ud %zd\n", wp, r->rp, t-r->r); descriptorfu(e, Qr); return -1; } return 0; } static int replenish(Ether *e, Ctlr *c) { int n, lim; uint wp; Block *b; Sring *r; Status *tab[2], *t; r = &c->rx; wp = r->wp; lim = r->cnt/2; if(lim > 128) lim = 128; /* hw limit? */ for(n = 0; n < lim; n++){ b = iallocb(c->rbsz + Rbalign); if(b == nil || getnslot(r, &wp, tab, 1 + is64()) == -1){ freeb(b); break; } b->rp = b->wp = (uchar*)ROUND((uintptr)b->base, Rbalign); t = tab[is64()]; if(rxscrew(e, r, t, wp) == -1){ freeb(b); break; } c->rbring[t - r->r] = b; if(is64()){ Status *t = tab[0]; putle(t->status, Pciwaddrh(b->wp), 4); t->ctl = 0; t->op = Oaddr64 | Hw; } putle(t->status, Pciwaddrl(b->wp), 4); putle(t->l, c->rbsz, 2); t->ctl = 0; t->op = Opkt | Hw; } if(n>0){ r->wp = wp; sfence(); prwrite16(c, Qr + Pputidx, wp & r->m); dprint("yuk: replenish %d %ud-%ud [%d-%d]\n", n, r->rp, wp, r->rp&r->m, wp&r->m); } return n == lim; } static void rproc(void *v) { Ctlr *c; Ether *e; e = v; c = e->ctlr; rxinit(e); linkup(c, Rxen); while(waserror()) ; for(;;){ if(replenish(e, c) == 0) starve(&c->rxmit); } } 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 rp; Block *b; Sring *r; Status *t; r = &c->tx; end &= r->m; for(rp = r->rp & r->m; 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); } 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;;){ if(rp == r->wp){ print("#l%d: yuk rx empty\n", e->ctlrno); return; } i = rp++&r->m; b = c->rbring[i]; c->rbring[i] = nil; if(b != nil) break; } r->rp = rp; 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->flag |= flag; etheriq(e, b); } unstarve(&c->rxmit); } 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, lim, op, l, x; Ctlr *c; Sring *r; Status *s; static uint ck = Badck; c = e->ctlr; r = &c->status; lim = c->reg16[Stathd] & r->m; for(;;){ i = r->rp & r->m; if(i == lim){ lim = c->reg16[Stathd] & r->m; if(i == lim) break; } 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; 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++; } 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<<Iphy2base, Qr + 0x80, "qr1", Itxs<<Iphy2base, Qtxs + 0x100, "qtxs1", Itx<<Iphy2base, Qtx + 0x100, "qtx1", }; static void eerror(Ether *e, uint cause) { uint i, o, q; Ctlr *c; c = e->ctlr; if(cause & Imac){ macintr(e); cause &= ~Imac; } if(cause & (Irx | Itxs | Itx)*(1 | 1<<Iphy2base)) for(i = 0; i < nelem(emap); i++){ if((cause & emap[i].i) == 0) continue; q = emap[i].q; o = prread16(c, q + Pgetidx); print("#l%d: yuk: bug: %s: @%d ca=%.8ux\n", e->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; e = v; c = e->ctlr; while(waserror()) ; for(;;){ starve(&c->iproc); 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; /* reading Isrc2 masks interrupts */ cause = c->reg[Isrc2]; if(cause == 0 || cause == ~0){ /* reenable interrupts */ c->reg[Icr] = 2; return; } 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<<Iphy2base*c->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, "pref %.8ux %.4ux\n", prread32(c, Qr + Pctl), prread16(c, Qr + Pgetidx)); 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? */ if(c->type == Yukfep && c->rev == 0){ } } 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); // phywrite(c, 0x16, 0x0b54); /* write to fe_led_par */ /* yukfep and rev 0: apply workaround for integrated resistor calibration */ 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; if(p->mem[0].bar & 1) return -1; c->io = p->mem[0].bar&~0xf; mem = vmap(c->io, p->mem[0].size); if(mem == nil){ print("yuk: cant map %llux\n", c->io); return -1; } pcienable(p); 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 */ if(reset(c)){ print("yuk: cant reset\n"); free(c->alloc); vunmap(mem, p->mem[0].size); pcidisable(p); return -1; } macinit(c); pcisetbme(p); 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); if(c == nil){ print("yuk: no memory for Ctlr\n"); return; } c->p = p; c->qno = nctlr; 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)(uintptr)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->multicast = multicast; e->promiscuous = promiscuous; e->shutdown = shutdown; e->transmit = nil; intrenable(e->irq, interrupt, e, e->tbdf, e->name); return 0; } void etheryuklink(void) { addethercard("yuk", pnp); }