changelog shortlog tags branches changeset file revisions annotate raw help

Mercurial > hg > plan9front / sys/src/9/bcm64/ethergenet.c

revision 7318: 386fe43162b4
child 7321: 6445d6f4cb53
     1.1new file mode 100644
     1.2--- /dev/null
     1.3+++ b/sys/src/9/bcm64/ethergenet.c
     1.4@@ -0,0 +1,1075 @@
     1.5+#include "u.h"
     1.6+#include "../port/lib.h"
     1.7+#include "mem.h"
     1.8+#include "dat.h"
     1.9+#include "fns.h"
    1.10+#include "io.h"
    1.11+#include "../port/netif.h"
    1.12+#include "../port/etherif.h"
    1.13+#include "../port/ethermii.h"
    1.14+
    1.15+enum
    1.16+{
    1.17+	Rbsz		= 2048,
    1.18+	Maxtu		= 1536,
    1.19+
    1.20+	DmaOWN		= 0x8000,
    1.21+	DmaSOP		= 0x2000,
    1.22+	DmaEOP		= 0x4000,
    1.23+	DmaRxLg		= 0x10,
    1.24+	DmaRxNo		= 0x08,
    1.25+	DmaRxErr	= 0x04,
    1.26+	DmaRxCrc	= 0x02,
    1.27+	DmaRxOv		= 0x01,
    1.28+	DmaRxErrors	= DmaRxLg|DmaRxNo|DmaRxErr|DmaRxCrc|DmaRxOv,
    1.29+
    1.30+	DmaTxQtag	= 0x1F80,
    1.31+	DmaTxUnderrun	= 0x0200,
    1.32+	DmaTxAppendCrc	= 0x0040,
    1.33+	DmaTxOwCrc	= 0x0020,
    1.34+	DmaTxDoCsum	= 0x0010,
    1.35+
    1.36+	/* Ctlr->regs */
    1.37+	SysRevision	= 0x00/4,
    1.38+	SysPortCtrl	= 0x04/4,
    1.39+		PortModeIntEphy	= 0,
    1.40+		PortModeIntGphy = 1,
    1.41+		PortModeExtEphy = 2,
    1.42+		PortModeExtGphy = 3,
    1.43+		PortModeExtRvmii50 = 4,
    1.44+		PortModeExtRvmii25 = 16 | 4,
    1.45+		LedActSourceMac = 1 << 9,
    1.46+
    1.47+	SysRbufFlushCtrl	= 0x08/4,
    1.48+	SysTbufFlushCtrl	= 0x0C/4,
    1.49+
    1.50+	ExtRgmiiOobCtrl	= 0x8C/4,
    1.51+		RgmiiLink	= 1 << 4,
    1.52+		OobDisable	= 1 << 5,
    1.53+		RgmiiModeEn	= 1 << 6,
    1.54+		IdModeDis	= 1 << 16,
    1.55+
    1.56+	Intrl0		= 0x200/4,
    1.57+		IrqScb		= 1 << 0,
    1.58+		IrqEphy		= 1 << 1,
    1.59+		IrqPhyDetR	= 1 << 2,
    1.60+		IrqPhyDetF	= 1 << 3,
    1.61+		IrqLinkUp	= 1 << 4,
    1.62+		IrqLinkDown	= 1 << 5,
    1.63+		IrqUmac		= 1 << 6,
    1.64+		IrqUmacTsv	= 1 << 7,
    1.65+		IrqTbufUnderrun	= 1 << 8,
    1.66+		IrqRbufOverflow	= 1 << 9,
    1.67+		IrqHfbSm	= 1 << 10,
    1.68+		IrqHfbMm	= 1 << 11,
    1.69+		IrqMpdR		= 1 << 12,
    1.70+		IrqRxDmaDone	= 1 << 13,
    1.71+		IrqRxDmaPDone	= 1 << 14,
    1.72+		IrqRxDmaBDone	= 1 << 15,
    1.73+		IrqTxDmaDone	= 1 << 16,
    1.74+		IrqTxDmaPDone	= 1 << 17,
    1.75+		IrqTxDmaBDone	= 1 << 18,
    1.76+		IrqMdioDone	= 1 << 23,
    1.77+		IrqMdioError	= 1 << 24,
    1.78+	Intrl1		= 0x240/4,
    1.79+		/* Intrl0/1 + ... */
    1.80+		IntrSts		= 0x00/4,
    1.81+		IntrSet		= 0x04/4,
    1.82+		IntrClr		= 0x08/4,
    1.83+		IntrMaskSts	= 0x0C/4,
    1.84+		IntrMaskSet	= 0x10/4,
    1.85+		IntrMaskClr	= 0x14/4,
    1.86+
    1.87+	RbufCtrl	= 0x300/4,
    1.88+		Rbuf64En	= 1 << 0,
    1.89+		RbufAlign2B	= 1 << 1,
    1.90+		RbufBadDis	= 1 << 2,
    1.91+
    1.92+	RbufChkCtrl	= 0x314/4,
    1.93+		RbufChkRxChkEn	= 1 << 0,
    1.94+		RbufChkSkipFcs	= 1 << 4,
    1.95+
    1.96+	RbufOvflCnt	= 0x394/4,
    1.97+	RbufErrCnt	= 0x398/4,
    1.98+
    1.99+	RbufEnergyCtrl	= 0x39c/4,
   1.100+		RbufEeeEn	= 1 << 0,
   1.101+		RbufPmEn	= 1 << 1,
   1.102+
   1.103+	RbufTbufSizeCtrl= 0x3b4/4,
   1.104+
   1.105+	TbufCtrl	= 0x600/4,
   1.106+	TbufBpMc	= 0x60C/4,
   1.107+	TbufEnergyCtrl	= 0x614/4,
   1.108+
   1.109+	UmacCmd		= 0x808/4,
   1.110+		CmdTxEn		= 1 << 0,
   1.111+		CmdRxEn		= 1 << 1,
   1.112+		CmdSpeed10	= 0 << 2,
   1.113+		CmdSpeed100	= 1 << 2,
   1.114+		CmdSpeed1000	= 2 << 2,
   1.115+		CmdSpeedMask	= 3 << 2,
   1.116+		CmdProm		= 1 << 4,
   1.117+		CmdPadEn	= 1 << 5,
   1.118+		CmdCrcFwd	= 1 << 6,
   1.119+		CmdPauseFwd	= 1 << 7,
   1.120+		CmdRxPauseIgn	= 1 << 8,
   1.121+		CmdTxAddrIn	= 1 << 9,
   1.122+		CmdHdEn		= 1 << 10,
   1.123+		CmdSwReset	= 1 << 13,
   1.124+		CmdLclLoopEn	= 1 << 15,
   1.125+		CmdAutoConfig	= 1 << 22,
   1.126+		CmdCntlFrmEn	= 1 << 23,
   1.127+		CmdNoLenChk	= 1 << 24,
   1.128+		CmdRmtLoopEn	= 1 << 25,
   1.129+		CmdPrblEn	= 1 << 27,
   1.130+		CmdTxPauseIgn	= 1 << 28,
   1.131+		CmdTxRxEn	= 1 << 29,
   1.132+		CmdRuntFilterDis= 1 << 30,
   1.133+
   1.134+	UmacMac0	= 0x80C/4,
   1.135+	UmacMac1	= 0x810/4,
   1.136+	UmacMaxFrameLen	= 0x814/4,
   1.137+
   1.138+	UmacEeeCtrl	= 0x864/4,	
   1.139+		UmacEeeEn	= 1<<3,
   1.140+
   1.141+	UmacEeeLpiTimer	= 0x868/4,
   1.142+	UmacEeeWakeTimer= 0x86C/4,
   1.143+	UmacEeeRefCount	= 0x870/4,
   1.144+		EeeRefCountMask = 0xFFFF,
   1.145+
   1.146+	UmacTxFlush	= 0xb34/4,
   1.147+
   1.148+	UmacMibCtrl	= 0xd80/4,
   1.149+		MibResetRx	= 1 << 0,
   1.150+		MibResetRunt	= 1 << 1,
   1.151+		MibResetTx	= 1 << 2,
   1.152+
   1.153+	MdioCmd		= 0xe14/4,
   1.154+		MdioStartBusy	= 1 << 29,
   1.155+		MdioReadFail	= 1 << 28,
   1.156+		MdioRead	= 2 << 26,
   1.157+		MdioWrite	= 1 << 26,
   1.158+		MdioPhyShift	= 21,
   1.159+		MdioPhyMask	= 0x1F,
   1.160+		MdioAddrShift	= 16,
   1.161+		MdioAddrMask	= 0x1F,
   1.162+
   1.163+	UmacMpdCtrl	= 0xe20/4,
   1.164+		MpdEn	= 1 << 0,
   1.165+		MpdPwEn	= 1 << 27,
   1.166+
   1.167+	UmacMdfCtrl	= 0xe50/4,
   1.168+	UmacMdfAddr0	= 0xe54/4,
   1.169+
   1.170+	RdmaOffset	= 0x2000/4,
   1.171+	TdmaOffset	= 0x4000/4,
   1.172+	HfbOffset	= 0x8000/4,
   1.173+
   1.174+	HfbCtlr		= 0xFC00/4,
   1.175+	HfbFltEnable	= 0xFC04/4,
   1.176+	HfbFltLen	= 0xFC1C/4,
   1.177+
   1.178+	/* common Ring->regs */
   1.179+	RdmaWP		= 0x00/4,
   1.180+	TdmaRP		= 0x00/4,
   1.181+	RxWP		= 0x08/4,
   1.182+	TxRP		= 0x08/4,
   1.183+	TxWP		= 0x0C/4,
   1.184+	RxRP		= 0x0C/4,
   1.185+	DmaRingBufSize	= 0x10/4,
   1.186+	DmaStart	= 0x14/4,
   1.187+	DmaEnd		= 0x1C/4,
   1.188+	DmaDoneThresh	= 0x24/4,
   1.189+	TdmaFlowPeriod	= 0x28/4,
   1.190+	RdmaXonXoffThresh=0x28/4,
   1.191+	TdmaWP		= 0x2C/4,
   1.192+	RdmaRP		= 0x2C/4,
   1.193+
   1.194+	/*
   1.195+	 * reg offsets only for RING16
   1.196+	 * ctlr->rx->regs / ctlr->tx->regs
   1.197+	 */
   1.198+	RingCfg		= 0x40/4,
   1.199+		RxRingCfgMask	= 0x10000,
   1.200+		TxRingCfgMask	= 0x1000F,
   1.201+
   1.202+	DmaCtrl		= 0x44/4,
   1.203+		DmaCtrlEn	= 1 << 0,
   1.204+	DmaStatus	= 0x48/4,
   1.205+		DmaStatusDis	= 1 << 0,
   1.206+	DmaScbBurstSize	= 0x4C/4,
   1.207+
   1.208+	TdmaArbCtrl	= 0x6C/4,
   1.209+	TdmaPriority0	= 0x70/4,
   1.210+	TdmaPriority1	= 0x74/4,
   1.211+	TdmaPriority2	= 0x78/4,
   1.212+
   1.213+	RdmaTimeout0	= 0x6C/4,
   1.214+	RdmaIndex2Ring0	= 0xB0/4,
   1.215+};
   1.216+
   1.217+typedef struct Desc Desc;
   1.218+typedef struct Ring Ring;
   1.219+typedef struct Ctlr Ctlr;
   1.220+
   1.221+struct Desc
   1.222+{
   1.223+	u32int	*d;	/* hw descriptor */
   1.224+	Block	*b;
   1.225+};
   1.226+
   1.227+struct Ring
   1.228+{
   1.229+	Rendez;
   1.230+	u32int	*regs;
   1.231+	u32int	*intregs;
   1.232+	u32int	intmask;
   1.233+
   1.234+	Desc	*d;
   1.235+
   1.236+	u32int	m;
   1.237+	u32int	cp;
   1.238+	u32int	rp;
   1.239+	u32int	wp;
   1.240+
   1.241+	int	num;
   1.242+};
   1.243+
   1.244+struct Ctlr
   1.245+{
   1.246+	Lock;
   1.247+	u32int	*regs;
   1.248+
   1.249+	Desc	rd[256];
   1.250+	Desc	td[256];
   1.251+
   1.252+	Ring	rx[1+0];
   1.253+	Ring	tx[1+0];
   1.254+
   1.255+	Rendez	avail[1];
   1.256+	Rendez	link[1];
   1.257+	struct {
   1.258+		Mii;
   1.259+		Rendez;
   1.260+	}	mii[1];
   1.261+
   1.262+	QLock;
   1.263+	char	attached;
   1.264+};
   1.265+
   1.266+static Block *scratch;
   1.267+
   1.268+/*
   1.269+this driver causes the ethernet controller to stall the gisb bus
   1.270+which causes other devices to fail.
   1.271+*/
   1.272+#ifdef XXXDEBUG
   1.273+
   1.274+static u32int *lastgenetregaddr;
   1.275+static uintptr lastgenetregpc;
   1.276+static ulong lastgenetregtime;
   1.277+static Ctlr *xxx;
   1.278+
   1.279+#define REG(x)	*(logreg(&(x)))
   1.280+
   1.281+static u32int*
   1.282+logreg(u32int *x)
   1.283+{
   1.284+	coherence();
   1.285+	lastgenetregtime = MACHP(0)->ticks;
   1.286+	lastgenetregpc = getcallerpc(&x);
   1.287+	lastgenetregaddr = x;
   1.288+	return x;
   1.289+}
   1.290+
   1.291+static void
   1.292+dumparb(void)
   1.293+{
   1.294+	static int once;
   1.295+	static u32int *regs = (u32int*)(VIRTIO + 0x9800);
   1.296+
   1.297+	if(!once){
   1.298+		once = 1;
   1.299+		regs[0x00/4] |= 1;
   1.300+		regs[0x40/4] = (regs[0x40/4] & ~0x1F) | 9 | 0x40000000;
   1.301+	}
   1.302+	iprint("arb %.8ux %.8ux %.8ux %.8ux; "
   1.303+		"%.8ux %.8ux %.8ux %.8ux; "
   1.304+		"%.8ux %.8ux %.8ux\n",
   1.305+		regs[0x40/4], regs[0x44/4], regs[0x48/4], regs[0x4C/4],
   1.306+		regs[0x50/4], regs[0x54/4], regs[0x58/4], regs[0x5C/4],
   1.307+		regs[0x60/4], regs[0x64/4], regs[0x68/4]);
   1.308+}
   1.309+
   1.310+void
   1.311+genetclock(void)
   1.312+{
   1.313+	static int ctr;
   1.314+
   1.315+	if(xxx == nil)
   1.316+		return;
   1.317+
   1.318+	if((++ctr & 0xFF) != 0)
   1.319+		return;
   1.320+	iprint("%d %#p @ %#p; "
   1.321+		"rx=(%.2ux %.2ux [%.2ux]); "
   1.322+		"tx=(%.2ux %.2ux %.2ux [%.2ux]); "
   1.323+		"(%lud)\n",
   1.324+		m->machno,
   1.325+		lastgenetregaddr, lastgenetregpc,
   1.326+		xxx->rx->rp, xxx->rx->wp, xxx->rx->wp - xxx->rx->rp,
   1.327+		xxx->tx->cp, xxx->tx->rp, xxx->tx->wp, xxx->tx->wp - xxx->tx->rp,
   1.328+		tk2ms(MACHP(0)->ticks-lastgenetregtime));
   1.329+	dumparb();
   1.330+}
   1.331+
   1.332+#else
   1.333+
   1.334+#define	REG(x)	(x)
   1.335+
   1.336+#endif
   1.337+
   1.338+static void
   1.339+interrupt0(Ureg*, void *arg)
   1.340+{
   1.341+	Ether *edev = arg;
   1.342+	Ctlr *ctlr = edev->ctlr;
   1.343+	u32int sts;
   1.344+
   1.345+	sts = REG(ctlr->regs[Intrl0 + IntrSts]) & ~REG(ctlr->regs[Intrl0 + IntrMaskSts]);
   1.346+	REG(ctlr->regs[Intrl0 + IntrClr]) = sts;
   1.347+	REG(ctlr->regs[Intrl0 + IntrMaskSet]) = sts;
   1.348+
   1.349+	if(sts & ctlr->rx->intmask)
   1.350+		wakeup(ctlr->rx);
   1.351+	if(sts & ctlr->tx->intmask)
   1.352+		wakeup(ctlr->tx);
   1.353+
   1.354+	if(sts & (IrqMdioDone|IrqMdioError))
   1.355+		wakeup(ctlr->mii);
   1.356+	if(sts & (IrqLinkUp|IrqLinkDown))
   1.357+		wakeup(ctlr->link);
   1.358+}
   1.359+
   1.360+static void
   1.361+interrupt1(Ureg*, void *arg)
   1.362+{
   1.363+	Ether *edev = arg;
   1.364+	Ctlr *ctlr = edev->ctlr;
   1.365+	u32int sts;
   1.366+	int i;
   1.367+
   1.368+	sts = REG(ctlr->regs[Intrl1 + IntrSts]) & ~REG(ctlr->regs[Intrl1 + IntrMaskSts]);
   1.369+	REG(ctlr->regs[Intrl1 + IntrClr]) = sts;
   1.370+	REG(ctlr->regs[Intrl1 + IntrMaskSet]) = sts;
   1.371+
   1.372+	for(i = 1; i < nelem(ctlr->rx); i++)
   1.373+		if(sts & ctlr->rx[i].intmask)
   1.374+			wakeup(&ctlr->rx[i]);
   1.375+
   1.376+	for(i = 1; i < nelem(ctlr->tx); i++)
   1.377+		if(sts & ctlr->tx[i].intmask)
   1.378+			wakeup(&ctlr->tx[i]);
   1.379+}
   1.380+
   1.381+static void
   1.382+setdma(Desc *d, void *v)
   1.383+{
   1.384+	u64int pa = PADDR(v);
   1.385+	REG(d->d[1]) = pa;
   1.386+	REG(d->d[2]) = pa >> 32;
   1.387+}
   1.388+
   1.389+static void
   1.390+replenish(Desc *d)
   1.391+{
   1.392+	d->b = allocb(Rbsz);
   1.393+	dmaflush(1, d->b->rp, Rbsz);
   1.394+	setdma(d, d->b->rp);
   1.395+}
   1.396+
   1.397+static int
   1.398+rxdone(void *arg)
   1.399+{
   1.400+	Ring *r = arg;
   1.401+
   1.402+	r->wp = REG(r->regs[RxWP]) & 0xFFFF;
   1.403+	if(r->rp != r->wp)
   1.404+		return 1;
   1.405+	REG(r->intregs[IntrMaskClr]) = r->intmask;
   1.406+	return 0;
   1.407+}
   1.408+
   1.409+static void
   1.410+recvproc(void *arg)
   1.411+{
   1.412+	Ether *edev = arg;
   1.413+	Ctlr *ctlr = edev->ctlr;
   1.414+	Desc *d;
   1.415+	Block *b;
   1.416+	u32int s;
   1.417+
   1.418+#ifdef XXXDEBUG
   1.419+	procwired(up, 1);
   1.420+	sched();
   1.421+#endif
   1.422+
   1.423+	while(waserror())
   1.424+		;
   1.425+
   1.426+	for(;;){
   1.427+		if(ctlr->rx->rp == ctlr->rx->wp){
   1.428+			sleep(ctlr->rx, rxdone, ctlr->rx);
   1.429+			continue;
   1.430+		}
   1.431+		d = &ctlr->rx->d[ctlr->rx->rp & ctlr->rx->m];
   1.432+		b = d->b;
   1.433+		dmaflush(0, b->rp, Rbsz);
   1.434+		s = REG(d->d[0]);
   1.435+		replenish(d);
   1.436+		coherence();
   1.437+		ctlr->rx->rp = (ctlr->rx->rp + 1) & 0xFFFF;
   1.438+		REG(ctlr->rx->regs[RxRP]) = ctlr->rx->rp;
   1.439+		if((s & (DmaSOP|DmaEOP|DmaRxErrors)) != (DmaSOP|DmaEOP)){
   1.440+			freeb(b);
   1.441+			continue;
   1.442+		}
   1.443+		b->wp += (s & 0x0FFF0000) >> 16;
   1.444+		etheriq(edev, b);
   1.445+	}
   1.446+}
   1.447+
   1.448+static int
   1.449+txavail(void *arg)
   1.450+{
   1.451+	Ring *r = arg;
   1.452+
   1.453+	return ((r->wp+1) & r->m) != (r->cp & r->m);
   1.454+}
   1.455+
   1.456+static void
   1.457+sendproc(void *arg)
   1.458+{
   1.459+	Ether *edev = arg;
   1.460+	Ctlr *ctlr = edev->ctlr;
   1.461+	Desc *d;
   1.462+	Block *b;
   1.463+
   1.464+#ifdef XXXDEBUG
   1.465+	procwired(up, 1);
   1.466+	sched();
   1.467+#endif
   1.468+
   1.469+	while(waserror())
   1.470+		;
   1.471+
   1.472+	for(;;){
   1.473+		if(!txavail(ctlr->tx)){
   1.474+			sleep(ctlr->avail, txavail, ctlr->tx);
   1.475+			continue;
   1.476+		}
   1.477+		if((b = qbread(edev->oq, 100000)) == nil)
   1.478+			break;
   1.479+		d = &ctlr->tx->d[ctlr->tx->wp & ctlr->tx->m];
   1.480+		assert(d->b == nil);
   1.481+		d->b = b;
   1.482+		dmaflush(1, b->rp, BLEN(b));
   1.483+		setdma(d, b->rp);
   1.484+		REG(d->d[0]) = BLEN(b)<<16 | DmaTxQtag | DmaSOP | DmaEOP | DmaTxAppendCrc;
   1.485+		coherence();
   1.486+		ctlr->tx->wp = (ctlr->tx->wp+1) & 0xFFFF;
   1.487+		REG(ctlr->tx->regs[TxWP]) = ctlr->tx->wp;
   1.488+	}
   1.489+}
   1.490+
   1.491+static int
   1.492+txdone(void *arg)
   1.493+{
   1.494+	Ring *r = arg;
   1.495+
   1.496+	if(r->cp != r->wp){
   1.497+		r->rp = REG(r->regs[TxRP]) & 0xFFFF;
   1.498+		if(r->cp != r->rp)
   1.499+			return 1;
   1.500+	}
   1.501+	REG(r->intregs[IntrMaskClr]) = r->intmask;
   1.502+	return 0;
   1.503+}
   1.504+
   1.505+static void
   1.506+freeproc(void *arg)
   1.507+{
   1.508+	Ether *edev = arg;
   1.509+	Ctlr *ctlr = edev->ctlr;
   1.510+	Desc *d;
   1.511+
   1.512+#ifdef XXXDEBUG
   1.513+	procwired(up, 1);
   1.514+	sched();
   1.515+#endif
   1.516+
   1.517+	while(waserror())
   1.518+		;
   1.519+
   1.520+	for(;;){
   1.521+		if(ctlr->tx->cp == ctlr->tx->rp){
   1.522+			wakeup(ctlr->avail);
   1.523+			sleep(ctlr->tx, txdone, ctlr->tx);
   1.524+			continue;
   1.525+		}
   1.526+		d = &ctlr->tx->d[ctlr->tx->cp & ctlr->tx->m];
   1.527+		assert(d->b != nil);
   1.528+		freeb(d->b);
   1.529+		d->b = nil;
   1.530+		coherence();
   1.531+		ctlr->tx->cp = (ctlr->tx->cp+1) & 0xFFFF;
   1.532+	}
   1.533+}
   1.534+
   1.535+static void
   1.536+initring(Ring *ring, Desc *desc, int start, int size)
   1.537+{
   1.538+	ring->d = &desc[start];
   1.539+	ring->m = size - 1;
   1.540+	ring->cp = ring->rp = ring->wp = 0;
   1.541+	REG(ring->regs[RxWP]) = 0;
   1.542+	REG(ring->regs[RxRP]) = 0;
   1.543+	REG(ring->regs[DmaStart]) = start*3;
   1.544+	REG(ring->regs[DmaEnd]) = (start+size)*3 - 1;
   1.545+	REG(ring->regs[RdmaWP]) = start*3;
   1.546+	REG(ring->regs[RdmaRP]) = start*3;
   1.547+	REG(ring->regs[DmaRingBufSize]) = (size << 16) | Rbsz;
   1.548+	REG(ring->regs[DmaDoneThresh]) = 1;
   1.549+}
   1.550+
   1.551+static void
   1.552+introff(Ctlr *ctlr)
   1.553+{
   1.554+	REG(ctlr->regs[Intrl0 + IntrMaskSet]) = -1;
   1.555+	REG(ctlr->regs[Intrl0 + IntrClr]) = -1;
   1.556+	REG(ctlr->regs[Intrl1 + IntrMaskSet]) = -1;
   1.557+	REG(ctlr->regs[Intrl1 + IntrClr]) = -1;
   1.558+}
   1.559+
   1.560+static void
   1.561+dmaoff(Ctlr *ctlr)
   1.562+{
   1.563+	REG(ctlr->rx->regs[DmaCtrl]) &= ~(RxRingCfgMask<<1 | DmaCtrlEn);
   1.564+	REG(ctlr->tx->regs[DmaCtrl]) &= ~(TxRingCfgMask<<1 | DmaCtrlEn);
   1.565+
   1.566+	REG(ctlr->regs[UmacTxFlush]) = 1;
   1.567+	microdelay(10);
   1.568+	REG(ctlr->regs[UmacTxFlush]) = 0;
   1.569+
   1.570+	while((REG(ctlr->rx->regs[DmaStatus]) & DmaStatusDis) == 0)
   1.571+		microdelay(10);
   1.572+	while((REG(ctlr->tx->regs[DmaStatus]) & DmaStatusDis) == 0)
   1.573+		microdelay(10);
   1.574+}
   1.575+
   1.576+static void
   1.577+dmaon(Ctlr *ctlr)
   1.578+{
   1.579+	REG(ctlr->rx->regs[DmaCtrl]) |= DmaCtrlEn;
   1.580+	REG(ctlr->tx->regs[DmaCtrl]) |= DmaCtrlEn;
   1.581+
   1.582+	while(REG(ctlr->rx->regs[DmaStatus]) & DmaStatusDis)
   1.583+		microdelay(10);
   1.584+	while(REG(ctlr->tx->regs[DmaStatus]) & DmaStatusDis)
   1.585+		microdelay(10);
   1.586+}
   1.587+
   1.588+static void
   1.589+allocbufs(Ctlr *ctlr)
   1.590+{
   1.591+	int i;
   1.592+
   1.593+	if(scratch == nil){
   1.594+		scratch = allocb(Rbsz);
   1.595+		memset(scratch->rp, 0xFF, Rbsz);
   1.596+		dmaflush(1, scratch->rp, Rbsz);
   1.597+	}
   1.598+
   1.599+	for(i = 0; i < nelem(ctlr->rd); i++){
   1.600+		ctlr->rd[i].d = &ctlr->regs[RdmaOffset + i*3];
   1.601+		replenish(&ctlr->rd[i]);
   1.602+	}
   1.603+
   1.604+	for(i = 0; i < nelem(ctlr->td); i++){
   1.605+		ctlr->td[i].d = &ctlr->regs[TdmaOffset + i*3];
   1.606+		setdma(&ctlr->td[i], scratch->rp);
   1.607+		REG(ctlr->td[i].d[0]) = DmaTxUnderrun;
   1.608+	}
   1.609+}
   1.610+
   1.611+static void
   1.612+freebufs(Ctlr *ctlr)
   1.613+{
   1.614+	int i;
   1.615+
   1.616+	for(i = 0; i < nelem(ctlr->rd); i++){
   1.617+		if(ctlr->rd[i].b != nil){
   1.618+			freeb(ctlr->rd[i].b);
   1.619+			ctlr->rd[i].b = nil;
   1.620+		}
   1.621+	}
   1.622+	for(i = 0; i < nelem(ctlr->td); i++){
   1.623+		if(ctlr->td[i].b != nil){
   1.624+			freeb(ctlr->td[i].b);
   1.625+			ctlr->td[i].b = nil;
   1.626+		}
   1.627+	}
   1.628+}
   1.629+
   1.630+static void
   1.631+initrings(Ctlr *ctlr)
   1.632+{
   1.633+	u32int rcfg, tcfg, dmapri[3];
   1.634+	int i;
   1.635+
   1.636+	ctlr->rx->intregs = &ctlr->regs[Intrl0];
   1.637+	ctlr->rx->intmask = IrqRxDmaDone;
   1.638+	ctlr->rx->num = 16;
   1.639+	rcfg = 1<<16;
   1.640+	for(i = 1; i < nelem(ctlr->rx); i++){
   1.641+		ctlr->rx[i].regs = &ctlr->regs[RdmaOffset + nelem(ctlr->rd)*3 + (i-1)*RingCfg];
   1.642+		ctlr->rx[i].intregs = &ctlr->regs[Intrl1];
   1.643+		ctlr->rx[i].intmask = 0x10000 << (i - 1);
   1.644+		ctlr->rx[i].num = i - 1;
   1.645+		rcfg |= 1<<(i-1);
   1.646+	}
   1.647+	assert(rcfg && (rcfg & ~RxRingCfgMask) == 0);
   1.648+
   1.649+	ctlr->tx->intregs = &ctlr->regs[Intrl0];
   1.650+	ctlr->tx->intmask = IrqTxDmaDone;
   1.651+	ctlr->tx->num = 16;
   1.652+	tcfg = 1<<16;
   1.653+	for(i = 1; i < nelem(ctlr->tx); i++){
   1.654+		ctlr->tx[i].regs = &ctlr->regs[TdmaOffset + nelem(ctlr->td)*3 + (i-1)*RingCfg];
   1.655+		ctlr->tx[i].intregs = &ctlr->regs[Intrl1];
   1.656+		ctlr->tx[i].intmask = 1 << (i - 1);
   1.657+		ctlr->tx[i].num = i - 1;
   1.658+		tcfg |= 1<<(i-1);
   1.659+	}
   1.660+	assert(tcfg && (tcfg & ~TxRingCfgMask) == 0);
   1.661+
   1.662+	REG(ctlr->rx->regs[DmaScbBurstSize]) = 0x08;
   1.663+	for(i = 1; i < nelem(ctlr->rx); i++)
   1.664+		initring(&ctlr->rx[i], ctlr->rd, (i-1)*32, 32);
   1.665+	initring(ctlr->rx, ctlr->rd, (i-1)*32, nelem(ctlr->rd) - (i-1)*32);
   1.666+
   1.667+	for(i = 0; i < nelem(ctlr->rx); i++){		 
   1.668+		REG(ctlr->rx[i].regs[DmaDoneThresh]) = 1;
   1.669+		REG(ctlr->rx[i].regs[RdmaXonXoffThresh]) = (5 << 16) | ((ctlr->rx[i].m+1) >> 4);
   1.670+
   1.671+		// set dma timeout to 50┬Ás
   1.672+		REG(ctlr->rx->regs[RdmaTimeout0 + ctlr->rx[i].num]) = ((50*1000 + 8191)/8192);
   1.673+	}
   1.674+
   1.675+	REG(ctlr->tx->regs[DmaScbBurstSize]) = 0x08;
   1.676+	for(i = 1; i < nelem(ctlr->tx); i++)
   1.677+		initring(&ctlr->tx[i], ctlr->td, (i-1)*32, 32);
   1.678+	initring(ctlr->tx, ctlr->td, (i-1)*32, nelem(ctlr->td) - (i-1)*32);
   1.679+
   1.680+	dmapri[0] = dmapri[1] = dmapri[2] = 0;
   1.681+	for(i = 0; i < nelem(ctlr->tx); i++){
   1.682+		REG(ctlr->tx[i].regs[DmaDoneThresh]) = 10;
   1.683+		REG(ctlr->tx[i].regs[TdmaFlowPeriod]) = i ? 0 : Maxtu << 16;
   1.684+		dmapri[ctlr->tx[i].num/6] |= i << ((ctlr->tx[i].num%6)*5);
   1.685+	}
   1.686+
   1.687+	REG(ctlr->tx->regs[TdmaArbCtrl]) = 2;
   1.688+	REG(ctlr->tx->regs[TdmaPriority0]) = dmapri[0];
   1.689+	REG(ctlr->tx->regs[TdmaPriority1]) = dmapri[1];
   1.690+	REG(ctlr->tx->regs[TdmaPriority2]) = dmapri[2];
   1.691+
   1.692+	REG(ctlr->rx->regs[RingCfg]) = rcfg;
   1.693+	REG(ctlr->tx->regs[RingCfg]) = tcfg;
   1.694+
   1.695+	REG(ctlr->rx->regs[DmaCtrl]) |= rcfg<<1;
   1.696+	REG(ctlr->tx->regs[DmaCtrl]) |= tcfg<<1;
   1.697+}
   1.698+
   1.699+static void
   1.700+umaccmd(Ctlr *ctlr, u32int set, u32int clr)
   1.701+{
   1.702+	ilock(ctlr);
   1.703+	REG(ctlr->regs[UmacCmd]) = (REG(ctlr->regs[UmacCmd]) & ~clr) | set;
   1.704+	iunlock(ctlr);
   1.705+}
   1.706+
   1.707+static void
   1.708+reset(Ctlr *ctlr)
   1.709+{
   1.710+	u32int r;
   1.711+
   1.712+	// reset umac
   1.713+	r = REG(ctlr->regs[SysRbufFlushCtrl]);
   1.714+	REG(ctlr->regs[SysRbufFlushCtrl]) = r | 2;
   1.715+	microdelay(10);
   1.716+	REG(ctlr->regs[SysRbufFlushCtrl]) = r & ~2;
   1.717+	microdelay(10);
   1.718+
   1.719+	// umac reset
   1.720+	REG(ctlr->regs[SysRbufFlushCtrl]) = 0;
   1.721+	microdelay(10);
   1.722+
   1.723+	REG(ctlr->regs[UmacCmd]) = 0;
   1.724+	REG(ctlr->regs[UmacCmd]) = CmdSwReset | CmdLclLoopEn;
   1.725+	microdelay(2);
   1.726+	REG(ctlr->regs[UmacCmd]) = 0;
   1.727+}
   1.728+
   1.729+static void
   1.730+setmac(Ctlr *ctlr, uchar *ea)
   1.731+{
   1.732+	REG(ctlr->regs[UmacMac0]) = ea[0]<<24 | ea[1]<<16 | ea[2]<<8 | ea[3];
   1.733+	REG(ctlr->regs[UmacMac1]) = ea[4]<<8 | ea[5];
   1.734+}
   1.735+
   1.736+static void
   1.737+sethfb(Ctlr *ctlr)
   1.738+{
   1.739+	int i;
   1.740+
   1.741+	REG(ctlr->regs[HfbCtlr]) = 0;
   1.742+	REG(ctlr->regs[HfbFltEnable]) = 0;
   1.743+	REG(ctlr->regs[HfbFltEnable+1]) = 0;
   1.744+
   1.745+	for(i = 0; i < 8; i++)
   1.746+		REG(ctlr->rx->regs[RdmaIndex2Ring0+i]) = 0;
   1.747+
   1.748+	for(i = 0; i < 48/4; i++)
   1.749+		REG(ctlr->regs[HfbFltLen + i]) = 0;
   1.750+
   1.751+	for(i = 0; i < 48*128; i++)
   1.752+		REG(ctlr->regs[HfbOffset + i]) = 0;
   1.753+}
   1.754+
   1.755+static int
   1.756+mdiodone(void *arg)
   1.757+{
   1.758+	Ctlr *ctlr = arg;
   1.759+	REG(ctlr->regs[Intrl0 + IntrMaskClr]) = (IrqMdioDone|IrqMdioError);
   1.760+	return (REG(ctlr->regs[MdioCmd]) & MdioStartBusy) == 0;
   1.761+}
   1.762+
   1.763+static int
   1.764+mdiowait(Ctlr *ctlr)
   1.765+{
   1.766+	REG(ctlr->regs[MdioCmd]) |= MdioStartBusy;
   1.767+	while(REG(ctlr->regs[MdioCmd]) & MdioStartBusy)
   1.768+		tsleep(ctlr->mii, mdiodone, ctlr, 10);
   1.769+	return 0;
   1.770+}
   1.771+
   1.772+static int
   1.773+mdiow(Mii* mii, int phy, int addr, int data)
   1.774+{
   1.775+	Ctlr *ctlr = mii->ctlr;
   1.776+
   1.777+	if(phy > MdioPhyMask)
   1.778+		return -1;
   1.779+	addr &= MdioAddrMask;
   1.780+	REG(ctlr->regs[MdioCmd]) = MdioWrite
   1.781+		| (phy << MdioPhyShift) | (addr << MdioAddrShift) | (data & 0xFFFF);
   1.782+	return mdiowait(ctlr);
   1.783+}
   1.784+
   1.785+static int
   1.786+mdior(Mii* mii, int phy, int addr)
   1.787+{
   1.788+	Ctlr *ctlr = mii->ctlr;
   1.789+
   1.790+	if(phy > MdioPhyMask)
   1.791+		return -1;
   1.792+	addr &= MdioAddrMask;
   1.793+	REG(ctlr->regs[MdioCmd]) = MdioRead
   1.794+		| (phy << MdioPhyShift) | (addr << MdioAddrShift);
   1.795+	if(mdiowait(ctlr) < 0)
   1.796+		return -1;
   1.797+	if(REG(ctlr->regs[MdioCmd]) & MdioReadFail)
   1.798+		return -1;
   1.799+	return REG(ctlr->regs[MdioCmd]) & 0xFFFF;
   1.800+}
   1.801+
   1.802+static int
   1.803+bcmshdr(Mii *mii, int reg)
   1.804+{
   1.805+	miimiw(mii, 0x1C, (reg & 0x1F) << 10);
   1.806+	return miimir(mii, 0x1C) & 0x3FF;
   1.807+}
   1.808+
   1.809+static int
   1.810+bcmshdw(Mii *mii, int reg, int dat)
   1.811+{
   1.812+	return miimiw(mii, 0x1C, 0x8000 | (reg & 0x1F) << 10 | (dat & 0x3FF));
   1.813+}
   1.814+
   1.815+static int
   1.816+linkevent(void *arg)
   1.817+{
   1.818+	Ctlr *ctlr = arg;
   1.819+	REG(ctlr->regs[Intrl0 + IntrMaskClr]) = IrqLinkUp|IrqLinkDown;
   1.820+	return 0;
   1.821+}
   1.822+
   1.823+static void
   1.824+linkproc(void *arg)
   1.825+{
   1.826+	Ether *edev = arg;
   1.827+	Ctlr *ctlr = edev->ctlr;
   1.828+	MiiPhy *phy;
   1.829+	int link = -1;
   1.830+
   1.831+#ifdef XXXDEBUG
   1.832+	procwired(up, 1);
   1.833+	sched();
   1.834+#endif
   1.835+
   1.836+	while(waserror())
   1.837+		;
   1.838+
   1.839+	for(;;){
   1.840+		tsleep(ctlr->link, linkevent, ctlr, 1000);
   1.841+		miistatus(ctlr->mii);
   1.842+		phy = ctlr->mii->curphy;
   1.843+		if(phy == nil || phy->link == link)
   1.844+			continue;
   1.845+		link = phy->link;
   1.846+		if(link){
   1.847+			u32int cmd = CmdRxEn|CmdTxEn;
   1.848+			switch(phy->speed){
   1.849+			case 1000:	cmd |= CmdSpeed1000; break;
   1.850+			case 100:	cmd |= CmdSpeed100; break;
   1.851+			case 10:	cmd |= CmdSpeed10; break;
   1.852+			}
   1.853+			if(!phy->fd)
   1.854+				cmd |= CmdHdEn;
   1.855+			if(!phy->rfc)
   1.856+				cmd |= CmdRxPauseIgn;
   1.857+			if(!phy->tfc)
   1.858+				cmd |= CmdTxPauseIgn;
   1.859+
   1.860+			REG(ctlr->regs[ExtRgmiiOobCtrl]) = (REG(ctlr->regs[ExtRgmiiOobCtrl]) & ~OobDisable) | RgmiiLink;
   1.861+			umaccmd(ctlr, cmd, CmdSpeedMask|CmdHdEn|CmdRxPauseIgn|CmdTxPauseIgn);
   1.862+
   1.863+			edev->mbps = phy->speed;
   1.864+		}
   1.865+		edev->link = link;
   1.866+		// print("#l%d: link %d speed %d\n", edev->ctlrno, edev->link, edev->mbps);
   1.867+	}
   1.868+}
   1.869+
   1.870+static void
   1.871+setmdfaddr(Ctlr *ctlr, int i, uchar *ea)
   1.872+{
   1.873+	REG(ctlr->regs[UmacMdfAddr0 + i*2 + 0]) = ea[0] << 8  | ea[1];
   1.874+	REG(ctlr->regs[UmacMdfAddr0 + i*2 + 1]) = ea[2] << 24 | ea[3] << 16 | ea[4] << 8 | ea[5];
   1.875+}
   1.876+
   1.877+static void
   1.878+rxmode(Ether *edev, int prom)
   1.879+{
   1.880+	Ctlr *ctlr = edev->ctlr;
   1.881+	Netaddr *na;
   1.882+	int i;
   1.883+
   1.884+	if(prom || edev->nmaddr > 16-2){
   1.885+		REG(ctlr->regs[UmacMdfCtrl]) = 0;
   1.886+		umaccmd(ctlr, CmdProm, 0);
   1.887+		return;
   1.888+	}
   1.889+	setmdfaddr(ctlr, 0, edev->bcast);
   1.890+	setmdfaddr(ctlr, 1, edev->ea);
   1.891+	for(i = 2, na = edev->maddr; na != nil; na = na->next, i++)
   1.892+		setmdfaddr(ctlr, i, na->addr);
   1.893+	REG(ctlr->regs[UmacMdfCtrl]) = (-0x10000 >> i) & 0x1FFFF;
   1.894+	umaccmd(ctlr, 0, CmdProm);
   1.895+}
   1.896+
   1.897+static void
   1.898+shutdown(Ether *edev)
   1.899+{
   1.900+	Ctlr *ctlr = edev->ctlr;
   1.901+
   1.902+	dmaoff(ctlr);
   1.903+	introff(ctlr);
   1.904+}
   1.905+
   1.906+static void
   1.907+attach(Ether *edev)
   1.908+{
   1.909+	Ctlr *ctlr = edev->ctlr;
   1.910+
   1.911+	eqlock(ctlr);
   1.912+	if(ctlr->attached){
   1.913+		qunlock(ctlr);
   1.914+		return;
   1.915+	}
   1.916+	if(waserror()){
   1.917+		print("#l%d: %s\n", edev->ctlrno, up->errstr);
   1.918+		shutdown(edev);
   1.919+		freebufs(ctlr);
   1.920+		qunlock(ctlr);
   1.921+		nexterror();
   1.922+	}
   1.923+
   1.924+	// statistics
   1.925+	REG(ctlr->regs[UmacMibCtrl]) = MibResetRx | MibResetTx | MibResetRunt;
   1.926+	REG(ctlr->regs[UmacMibCtrl]) = 0;
   1.927+
   1.928+	// wol
   1.929+	REG(ctlr->regs[UmacMpdCtrl]) &= ~(MpdPwEn|MpdEn);
   1.930+
   1.931+	// power
   1.932+	REG(ctlr->regs[UmacEeeCtrl]) &= ~UmacEeeEn;
   1.933+	REG(ctlr->regs[RbufEnergyCtrl]) &= ~(RbufEeeEn|RbufPmEn);
   1.934+	REG(ctlr->regs[TbufEnergyCtrl]) &= ~(RbufEeeEn|RbufPmEn);
   1.935+	REG(ctlr->regs[TbufBpMc]) = 0;
   1.936+
   1.937+	REG(ctlr->regs[UmacMaxFrameLen]) = Maxtu;
   1.938+
   1.939+	REG(ctlr->regs[RbufTbufSizeCtrl]) = 1;
   1.940+
   1.941+	REG(ctlr->regs[TbufCtrl]) &= ~(Rbuf64En);
   1.942+	REG(ctlr->regs[RbufCtrl]) &= ~(Rbuf64En|RbufAlign2B);
   1.943+	REG(ctlr->regs[RbufChkCtrl]) &= ~(RbufChkRxChkEn|RbufChkSkipFcs);
   1.944+
   1.945+	allocbufs(ctlr);
   1.946+	initrings(ctlr);
   1.947+	dmaon(ctlr);
   1.948+
   1.949+	setmac(ctlr, edev->ea);
   1.950+	sethfb(ctlr);
   1.951+	rxmode(edev, 0);
   1.952+
   1.953+	REG(ctlr->regs[SysPortCtrl]) = PortModeExtGphy;
   1.954+	REG(ctlr->regs[ExtRgmiiOobCtrl]) |= RgmiiModeEn | IdModeDis;
   1.955+
   1.956+	ctlr->mii->ctlr = ctlr;
   1.957+	ctlr->mii->mir = mdior;
   1.958+	ctlr->mii->miw = mdiow;
   1.959+	mii(ctlr->mii, ~0);
   1.960+
   1.961+	if(ctlr->mii->curphy == nil)
   1.962+		error("no phy");
   1.963+
   1.964+	print("#l%d: phy%d id %.8ux oui %x\n", 
   1.965+		edev->ctlrno, ctlr->mii->curphy->phyno, 
   1.966+		ctlr->mii->curphy->id, ctlr->mii->curphy->oui);
   1.967+
   1.968+	miireset(ctlr->mii);
   1.969+
   1.970+	switch(ctlr->mii->curphy->id){
   1.971+	case 0x600d84a2:	/* BCM54312PE */
   1.972+		/* mask interrupts */
   1.973+		miimiw(ctlr->mii, 0x10, miimir(ctlr->mii, 0x10) | 0x1000);
   1.974+
   1.975+		/* SCR3: clear DLLAPD_DIS */
   1.976+		bcmshdw(ctlr->mii, 0x05, bcmshdr(ctlr->mii, 0x05) &~0x0002);
   1.977+		/* APD: set APD_EN */
   1.978+		bcmshdw(ctlr->mii, 0x0a, bcmshdr(ctlr->mii, 0x0a) | 0x0020);
   1.979+
   1.980+		/* blinkenlights */
   1.981+		bcmshdw(ctlr->mii, 0x09, bcmshdr(ctlr->mii, 0x09) | 0x0010);
   1.982+		bcmshdw(ctlr->mii, 0x0d, 3<<0 | 0<<4);
   1.983+		break;
   1.984+	}
   1.985+
   1.986+	/* don't advertise EEE */
   1.987+	miimmdw(ctlr->mii, 7, 60, 0);
   1.988+
   1.989+	miiane(ctlr->mii, ~0, ~0, ~0);
   1.990+
   1.991+#ifdef XXXDEBUG
   1.992+	xxx = ctlr;
   1.993+#endif
   1.994+
   1.995+	ctlr->attached = 1;
   1.996+
   1.997+	kproc("genet-recv", recvproc, edev);
   1.998+	kproc("genet-send", sendproc, edev);
   1.999+	kproc("genet-free", freeproc, edev);
  1.1000+	kproc("genet-link", linkproc, edev);
  1.1001+
  1.1002+	qunlock(ctlr);
  1.1003+	poperror();
  1.1004+}
  1.1005+
  1.1006+static void
  1.1007+prom(void *arg, int on)
  1.1008+{
  1.1009+	Ether *edev = arg;
  1.1010+	rxmode(edev, on);
  1.1011+}
  1.1012+
  1.1013+static void
  1.1014+multi(void *arg, uchar*, int)
  1.1015+{
  1.1016+	Ether *edev = arg;
  1.1017+	rxmode(edev, edev->prom > 0);
  1.1018+}
  1.1019+
  1.1020+static long
  1.1021+ctl(Ether *edev, void *data, long len)
  1.1022+{
  1.1023+	Ctlr *ctlr = edev->ctlr;
  1.1024+	char *s = data;
  1.1025+
  1.1026+	if(len >= 4 && strncmp(s, "tron", 4) == 0){
  1.1027+		umaccmd(ctlr, CmdTxEn, 0);
  1.1028+	} else if(len >= 5 && strncmp(s, "troff", 5) == 0){
  1.1029+		umaccmd(ctlr, 0, CmdTxEn);
  1.1030+	} else if(len >= 3 && strncmp(s, "ron", 3) == 0){
  1.1031+		umaccmd(ctlr, CmdRxEn, 0);
  1.1032+	} else if(len >= 4 && strncmp(s, "roff", 4) == 0){
  1.1033+		umaccmd(ctlr, 0, CmdRxEn);
  1.1034+	}
  1.1035+
  1.1036+	return len;
  1.1037+}
  1.1038+
  1.1039+static int
  1.1040+pnp(Ether *edev)
  1.1041+{
  1.1042+	static Ctlr ctlr[1];
  1.1043+
  1.1044+	if(ctlr->regs != nil)
  1.1045+		return -1;
  1.1046+
  1.1047+	ctlr->regs = (u32int*)(VIRTIO1 + 0x580000);
  1.1048+	ctlr->rx->regs = &ctlr->regs[RdmaOffset + nelem(ctlr->rd)*3 + 16*RingCfg];
  1.1049+	ctlr->tx->regs = &ctlr->regs[TdmaOffset + nelem(ctlr->td)*3 + 16*RingCfg];
  1.1050+
  1.1051+	edev->port = (uintptr)ctlr->regs;
  1.1052+	edev->irq = IRQether;
  1.1053+	edev->ctlr = ctlr;
  1.1054+	edev->attach = attach;
  1.1055+	edev->shutdown = shutdown;
  1.1056+	edev->promiscuous = prom;
  1.1057+	edev->multicast = multi;
  1.1058+	edev->ctl = ctl;
  1.1059+	edev->arg = edev;
  1.1060+	edev->mbps = 1000;
  1.1061+	edev->maxmtu = Maxtu;
  1.1062+
  1.1063+	parseether(edev->ea, getethermac());
  1.1064+
  1.1065+	reset(ctlr);
  1.1066+	dmaoff(ctlr);
  1.1067+	introff(ctlr);
  1.1068+
  1.1069+	intrenable(edev->irq+0, interrupt0, edev, BUSUNKNOWN, edev->name);
  1.1070+	intrenable(edev->irq+1, interrupt1, edev, BUSUNKNOWN, edev->name);
  1.1071+
  1.1072+	return 0;
  1.1073+}
  1.1074+
  1.1075+void
  1.1076+ethergenetlink(void)
  1.1077+{
  1.1078+	addethercard("genet", pnp);
  1.1079+}