Plan 9 from Bell Labs’s /usr/web/sources/contrib/yk/dist/9legacy/applied/pc-etheryuk.diff

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


--- /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<<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;
+	uintptr	io;
+	uchar	*reg8;
+	u16int	*reg16;
+	u32int	*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,	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	struct {
+	union {
+		struct {
+			Lock;
+			Block	*b;
+			uint	starve;
+		};
+		uchar pad[128];		/* cacheline */
+	};
+	Kproc	*k;
+	Block	*x;
+	uint	nfast;
+	uint	nslow;
+} rbtab[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 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<<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;
+	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<<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, "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);
+}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].