Browse Source

Plan 9 from Bell Labs 2013-07-24

David du Colombier 7 years ago
parent
commit
7137e45891

+ 10 - 15
sys/lib/lp/bin/lpsend.rc

@@ -1,18 +1,13 @@
 #!/bin/rc
-if (! ~ $DEBUG '') { flag x + }
-if (test -e /net/tcp/clone) {
-	dialstring=`{ndb/query sys $1 dom}
-	network=tcp
-	if (~ $#dialstring 0 || ! ~ $dialstring '') {
-		dialstring=$1
-	}
-	if(lpsend $network^!^$dialstring^!printer) exit ''
-	rv='tcp failed'
-}
-if not rv='no tcp'
+# lpsend.rc dialstring - run lpsend network!dialstring!printer
+if (! ~ $DEBUG '')
+	flag x +
+if (! test -e /net/tcp/clone)
+	exit 'no tcp'
 
+dialstring=`{ndb/query sys $1 dom}
+if (~ $#dialstring 0 || ~ $dialstring '')	# no dom for sys in ndb?
+	dialstring=$1				# use arg unchanged
 
-if (! ~ $dialstring '')
-	exit 'lpsend: no dialstring'
-if not
-	exit 'lpsend: '^$rv
+lpsend tcp!^$dialstring^!printer
+exit $status

+ 1 - 0
sys/src/9/mkfile

@@ -5,6 +5,7 @@ ARCH=\
 	omap\
 	pc\
 	ppc\
+	rb\
 	teg2\
 	
 all:V:

+ 3 - 0
sys/src/9/rb/c_fcr0.s

@@ -0,0 +1,3 @@
+TEXT	C_fcr0(SB), $-4
+	MOVW	$0x500, R1	/* claim to be an r4k, thus have ll/sc */
+	RET

+ 299 - 0
sys/src/9/rb/clock.c

@@ -0,0 +1,299 @@
+/*
+ * ar7161 clocks and timers
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+
+#include	"ureg.h"
+
+enum {
+	Cyccntres	= 2, /* counter advances at ½ clock rate (mips 24k) */
+	Basetickfreq	= 680*Mhz / Cyccntres,	/* rb450g */
+};
+
+void (*kproftimer)(ulong);
+
+void
+silencewdog(void)
+{
+	*Rstwdogtimer = Basetickfreq * 2 * 5;	/* pet the dog */
+}
+
+void
+sicwdog(void)
+{
+	*Rstwdogtimer = Basetickfreq * 2;
+	*Rstwdogctl = Wdogreset;		/* wake the dog */
+}
+
+void
+wdogreset(void)
+{
+	*Rstwdogtimer = Basetickfreq / 100;
+	*Rstwdogctl = Wdogreset;		/* wake the dog */
+	coherence();
+	*Rstwdogtimer = Basetickfreq / 10000;
+	coherence();
+}
+
+void
+stopwdog(void)
+{
+	*Rstwdogtimer = ~0;
+	*Rstwdogctl = Wdognoaction;		/* put the dog to sleep */
+}
+
+void
+clockshutdown(void)
+{
+	stopwdog();
+}
+
+/*
+ *  delay for l milliseconds more or less.
+ */
+void
+delay(int l)
+{
+	while(l-- > 0)
+		microdelay(1000);
+}
+
+/*
+ *  microseconds delay
+ */
+void
+microdelay(int l)
+{
+	int s;
+	ulong x, cyc, cnt, speed;
+
+	speed = m->speed;
+	if (speed == 0)
+		speed = Basetickfreq / Mhz * Cyccntres;
+	cyc = (ulong)l * (speed / Cyccntres);
+	s = splhi();
+	cnt = rdcount();
+	x = cnt + cyc;
+	if (x < cnt || x >= ~0ul - Basetickfreq) {
+		/* counter will wrap between now and x, or x is too near ~0 */
+		wrcount(0);			/* somewhat drastic */
+		wrcompare(rdcompare() - cnt);	/* match new count */
+		x = cyc;
+	}
+	while(rdcount() < x)
+		;
+	splx(s);
+	silencewdog();
+}
+
+void
+clock(Ureg *ureg)
+{
+	wrcompare(rdcount()+m->maxperiod);	/* side-effect: dismiss intr */
+	silencewdog();
+	timerintr(ureg, 0);
+}
+
+enum {
+	Instrs		= 10*Mhz,
+};
+
+static long
+issue1loop(void)
+{
+	register int i;
+	long st;
+
+	i = Instrs;
+	st = perfticks();
+	do {
+		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
+		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
+		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
+		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
+		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
+		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
+		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
+		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
+		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
+		--i; --i; --i; --i; --i;
+		/* omit 3 (--i) to account for conditional branch, nop & jump */
+		i -= 1+3;	 /* --i plus 3 omitted (--i) instructions */
+	} while(--i >= 0);
+	return perfticks() - st;
+}
+
+/* estimate instructions/s. */
+static int
+guessmips(long (*loop)(void), char *)
+{
+	int s;
+	long cyc;
+
+	do {
+		s = splhi();
+		cyc = loop();
+		splx(s);
+		if (cyc < 0)
+			iprint("again...");
+	} while (cyc < 0);
+	/*
+	 * Instrs instructions took cyc cycles @ Basetickfreq Hz.
+	 * round the result.
+	 */
+	return (((vlong)Basetickfreq * Instrs) / cyc + Mhz/2) / Mhz;
+}
+
+void
+clockinit(void)
+{
+	int mips;
+
+	silencewdog();
+
+	/*
+	 * calibrate fastclock
+	 */
+	mips = guessmips(issue1loop, "single");
+
+	/*
+	 * m->delayloop should be the number of delay loop iterations
+	 * needed to consume 1 ms, assuming 2 instr'ns in the delay loop.
+	 */
+	m->delayloop = mips*Mhz / (1000 * 2);
+	if(m->delayloop == 0)
+		m->delayloop = 1;
+
+	m->speed = mips;
+	m->hz = m->speed*Mhz;
+
+	m->maxperiod = Basetickfreq / HZ;
+	m->minperiod = Basetickfreq / (100*HZ);
+	wrcompare(rdcount()+m->maxperiod);
+
+	/*
+	 *  desynchronize the processor clocks so that they all don't
+	 *  try to resched at the same time.
+	 */
+	delay(m->machno*2);
+
+	syncclock();
+	intron(INTR7);
+}
+
+/*
+ * Tval is supposed to be in fastticks units.
+ * One fasttick unit is 1/Basetickfreq seconds.
+ */
+void
+timerset(Tval next)
+{
+	int x;
+	long period;
+
+	if(next == 0)
+		return;
+	x = splhi();			/* don't let us get scheduled */
+	period = next - fastticks(nil);
+	if(period > m->maxperiod - m->minperiod)
+		period = m->maxperiod;
+	else if(period < m->minperiod)
+		period = m->minperiod;
+	wrcompare(rdcount()+period);
+	silencewdog();
+	splx(x);
+}
+
+/*
+ *  The rewriting of compare in this routine shouldn't be necessary.
+ *  However, we lose clock interrupts if I don't, either a chip bug
+ *  or one of ours -- presotto
+ */
+uvlong
+fastticks(uvlong *hz)
+{
+	int x;
+	ulong delta, count;
+
+	if(hz)
+		*hz = Basetickfreq;
+
+	/* avoid reentry on interrupt or trap, to prevent recursion */
+	x = splhi();
+	count = rdcount();
+	if(rdcompare() - count > m->maxperiod)
+		wrcompare(count+m->maxperiod);
+	silencewdog();
+
+	if (count < m->lastcount)		/* wrapped around? */
+		delta = count + ((1ull<<32) - m->lastcount);
+	else
+		delta = count - m->lastcount;
+	m->lastcount = count;
+	m->fastticks += delta;
+	splx(x);
+	return m->fastticks;
+}
+
+ulong
+µs(void)
+{
+	return fastticks2us(fastticks(nil));
+}
+
+/*
+ *  performance measurement ticks.  must be low overhead.
+ *  doesn't have to count over a second.
+ */
+ulong
+perfticks(void)
+{
+	return rdcount();
+}
+
+long
+lcycles(void)
+{
+	return perfticks();
+}
+
+/* should use vlong hw counters ideally; lcycles is inadequate */
+void
+cycles(uvlong *cycp)
+{
+	*cycp = fastticks(nil);
+}
+
+Lock mpsynclock;
+
+/*
+ *  synchronize all clocks with processor 0
+ */
+void
+syncclock(void)
+{
+	uvlong x;
+
+	if(m->machno == 0){
+		m->lastcount = rdcount();
+		m->fastticks = 0;
+		m->ticks = 0;
+		wrcompare(rdcount()+m->maxperiod);
+	} else {
+		/* wait for processor 0's soft clock to change and then sync ours */
+		lock(&mpsynclock);
+		x = MACHP(0)->fastticks;
+		while(MACHP(0)->fastticks == x)
+			;
+		m->lastcount = rdcount();
+		m->fastticks = MACHP(0)->fastticks;
+		m->ticks = MACHP(0)->ticks;
+		wrcompare(rdcount()+m->maxperiod);
+		unlock(&mpsynclock);
+	}
+}

+ 225 - 0
sys/src/9/rb/dat.h

@@ -0,0 +1,225 @@
+typedef struct Conf	Conf;
+typedef struct Confmem	Confmem;
+typedef struct FPsave	FPsave;
+typedef struct KMap	KMap;
+typedef struct Lance	Lance;
+typedef struct Lancemem	Lancemem;
+typedef struct Label	Label;
+typedef struct Lock	Lock;
+typedef struct Mach	Mach;
+typedef struct MMU	MMU;
+typedef struct Notsave	Notsave;
+typedef struct Pcidev	Pcidev;
+typedef struct PMMU	PMMU;
+typedef struct Softtlb	Softtlb;
+typedef struct Ureg	Ureg;
+typedef struct Proc	Proc;
+typedef uvlong		Tval;
+
+#pragma incomplete Pcidev
+
+#define MAXSYSARG	5	/* for mount(fd, afd, mpt, flag, arg) */
+
+/*
+ *  parameters for sysproc.c and rebootcmd.c
+ */
+#define AOUT_MAGIC	V_MAGIC || magic==M_MAGIC
+/* r3k or r4k boot images */
+#define BOOT_MAGIC	(0x160<<16) || magic == ((0x160<<16)|3)
+
+/*
+ *  machine dependent definitions used by ../port/dat.h
+ */
+
+struct Lock
+{
+	ulong	key;			/* semaphore (non-zero = locked) */
+	ulong	sr;
+	ulong	pc;
+	Proc	*p;
+	Mach	*m;
+	ushort	isilock;
+};
+
+struct Label
+{
+	ulong	sp;
+	ulong	pc;
+};
+
+struct Confmem
+{
+	ulong	base;
+	ulong	npage;
+	ulong	kbase;
+	ulong	klimit;
+};
+
+struct Conf
+{
+	ulong	nmach;		/* processors */
+	ulong	nproc;		/* processes */
+	Confmem	mem[1];
+	ulong	npage;		/* total physical pages of memory */
+	ulong	upages;		/* user page pool */
+	ulong	nimage;		/* number of page cache image headers */
+	ulong	nswap;		/* number of swap pages */
+	int	nswppo;		/* max # of pageouts per segment pass */
+	ulong	copymode;	/* 0 is copy on write, 1 is copy on reference */
+	ulong	ialloc;		/* bytes available for interrupt-time allocation */
+	ulong	pipeqsize;	/* size in bytes of pipe queues */
+	int	nuart;		/* number of uart devices */
+};
+
+/*
+ * floating point registers
+ */
+enum
+{
+	/* floating point state */
+	FPinit,
+	FPactive,
+	FPinactive,
+	FPemu,
+
+	/* bit meaning floating point illegal */
+	FPillegal= 0x100,
+};
+
+enum {
+	Nfpregs		= 32,		/* floats; half as many doubles */
+};
+
+/*
+ * emulated floating point (mips32r2 with ieee fp regs)
+ * fpstate is separate, kept in Proc
+ */
+struct FPsave
+{
+	/* /dev/proc expects the registers to be first in FPsave */
+	ulong	reg[Nfpregs];		/* the canonical bits */
+	union {
+		ulong	fpstatus;	/* both are fcr31 */
+		ulong	fpcontrol;
+	};
+
+	int	fpdelayexec;		/* executing delay slot of branch */
+	uintptr	fpdelaypc;		/* pc to resume at after */
+	ulong	fpdelaysts;	/* save across user-mode delay-slot execution */
+
+	/* stuck-fault detection */
+	uintptr	fppc;			/* addr of last fault */
+	int	fpcnt;			/* how many consecutive at that addr */
+};
+
+int fpemudebug;
+
+/*
+ *  mmu goo in the Proc structure
+ */
+struct PMMU
+{
+	int	pidonmach[MAXMACH];
+};
+
+/*
+ *  things saved in the Proc structure during a notify
+ */
+struct Notsave
+{
+	ulong	nonempty;
+};
+
+#include "../port/portdat.h"
+
+/* First FIVE members' offsets known by l.s */
+struct Mach
+{
+	/* the following are all known by l.s and cannot be moved */
+	int	machno;			/* physical id of processor FIRST */
+	Softtlb*stb;			/* Software tlb simulation SECOND */
+	Proc*	proc;			/* process on this processor THIRD */
+	ulong	splpc;			/* pc that called splhi() FOURTH */
+	ulong	tlbfault;		/* # of tlb faults FIFTH */
+	ulong	ktlbfault;
+	ulong	utlbfault;
+
+	/* the following is safe to move */
+	ulong	tlbpurge;
+	ulong	ticks;			/* of the clock since boot time */
+	Label	sched;			/* scheduler wakeup */
+	void*	alarm;			/* alarms bound to this clock */
+	int	lastpid;		/* last pid allocated on this machine */
+	Proc*	pidproc[NTLBPID];	/* proc that owns tlbpid on this mach */
+	KMap*	kactive;		/* active on this machine */
+	int	knext;
+	uchar	ktlbx[NTLB];		/* tlb index used for kmap */
+	uchar	ktlbnext;
+	int	speed;			/* cpu speed */
+	ulong	delayloop;		/* for the delay() routine */
+	ulong	fairness;		/* for runproc */
+	int	flushmmu;
+	int	inclockintr;
+	int	ilockdepth;
+	Perf	perf;			/* performance counters */
+	uvlong	cyclefreq;		/* Frequency of user readable cycle counter */
+
+	/* for per-processor timers */
+	ulong	lastcount;
+	uvlong	fastticks;
+	ulong	hz;
+	ulong	maxperiod;
+	ulong	minperiod;
+
+	Proc*	readied;		/* for runproc */
+	ulong	schedticks;		/* next forced context switch */
+
+	int	pfault;
+	int	cs;
+	int	syscall;
+	int	load;
+	int	intr;
+	int	hashcoll;		/* soft-tlb hash collisions */
+	int	paststartup;		/* for putktlb */
+
+	int	stack[1];
+};
+
+struct KMap
+{
+	Ref;
+	ulong	virt;
+	ulong	phys0;
+	ulong	phys1;
+	KMap*	next;
+	KMap*	konmach[MAXMACH];
+	Page*	pg;
+	ulong	pc;			/* of caller to kmap() */
+};
+
+#define	VA(k)		((k)->virt)
+#define PPN(x)		((ulong)(x)>>6)
+
+/* offsets known by l.s */
+struct Softtlb
+{
+	ulong	virt;
+	ulong	phys0;
+	ulong	phys1;
+};
+
+struct
+{
+	Lock;
+	long	machs;		/* bitmap of processors */
+	short	exiting;
+	int	ispanic;
+}active;
+
+extern KMap kpte[];
+extern register Mach	*m;
+extern register Proc	*up;
+
+extern FPsave initfp;
+
+extern	int normalprint;

+ 258 - 0
sys/src/9/rb/devarch.c

@@ -0,0 +1,258 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+#include "../ip/ip.h"
+
+enum {
+	Qdir = 0,
+	Qbase,
+
+	Qmax = 16,
+};
+
+typedef long Rdwrfn(Chan*, void*, long, vlong);
+
+static Rdwrfn *readfn[Qmax];
+static Rdwrfn *writefn[Qmax];
+
+static Dirtab archdir[Qmax] = {
+	".",		{ Qdir, 0, QTDIR },	0,	0555,
+};
+
+Lock archwlock;	/* the lock is only for changing archdir */
+int narchdir = Qbase;
+
+/*
+ * Add a file to the #P listing.  Once added, you can't delete it.
+ * You can't add a file with the same name as one already there,
+ * and you get a pointer to the Dirtab entry so you can do things
+ * like change the Qid version.  Changing the Qid path is disallowed.
+ */
+Dirtab*
+addarchfile(char *name, int perm, Rdwrfn *rdfn, Rdwrfn *wrfn)
+{
+	int i;
+	Dirtab d;
+	Dirtab *dp;
+
+	memset(&d, 0, sizeof d);
+	strcpy(d.name, name);
+	d.perm = perm;
+
+	lock(&archwlock);
+	if(narchdir >= Qmax){
+		unlock(&archwlock);
+		return nil;
+	}
+
+	for(i=0; i<narchdir; i++)
+		if(strcmp(archdir[i].name, name) == 0){
+			unlock(&archwlock);
+			return nil;
+		}
+
+	d.qid.path = narchdir;
+	archdir[narchdir] = d;
+	readfn[narchdir] = rdfn;
+	writefn[narchdir] = wrfn;
+	dp = &archdir[narchdir++];
+	unlock(&archwlock);
+
+	return dp;
+}
+
+static Chan*
+archattach(char* spec)
+{
+	return devattach('P', spec);
+}
+
+Walkqid*
+archwalk(Chan* c, Chan *nc, char** name, int nname)
+{
+	return devwalk(c, nc, name, nname, archdir, narchdir, devgen);
+}
+
+static int
+archstat(Chan* c, uchar* dp, int n)
+{
+	return devstat(c, dp, n, archdir, narchdir, devgen);
+}
+
+static Chan*
+archopen(Chan* c, int omode)
+{
+	return devopen(c, omode, archdir, narchdir, devgen);
+}
+
+static void
+archclose(Chan*)
+{
+}
+
+static long
+archread(Chan *c, void *a, long n, vlong offset)
+{
+	Rdwrfn *fn;
+
+	switch((ulong)c->qid.path){
+	case Qdir:
+		return devdirread(c, a, n, archdir, narchdir, devgen);
+
+	default:
+		if(c->qid.path < narchdir && (fn = readfn[c->qid.path]))
+			return fn(c, a, n, offset);
+		error(Eperm);
+		break;
+	}
+
+	return 0;
+}
+
+static long
+archwrite(Chan *c, void *a, long n, vlong offset)
+{
+	Rdwrfn *fn;
+
+	if(c->qid.path < narchdir && (fn = writefn[c->qid.path]))
+		return fn(c, a, n, offset);
+	error(Eperm);
+
+	return 0;
+}
+
+void archinit(void);
+
+Dev archdevtab = {
+	'P',
+	"arch",
+
+	devreset,
+	archinit,
+	devshutdown,
+	archattach,
+	archwalk,
+	archstat,
+	archopen,
+	devcreate,
+	archclose,
+	archread,
+	devbread,
+	archwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
+
+static long
+cputyperead(Chan*, void *a, long n, vlong offset)
+{
+	char str[128];
+
+	snprint(str, sizeof str, "MIPS 24k %lud\n", m->hz / Mhz);
+	return readstr(offset, a, n, str);
+}
+
+static long
+tbread(Chan*, void *a, long n, vlong offset)
+{
+	char str[16];
+	uvlong tb;
+
+	cycles(&tb);
+
+	snprint(str, sizeof(str), "%16.16llux", tb);
+	return readstr(offset, a, n, str);
+}
+
+static long
+nsread(Chan*, void *a, long n, vlong offset)
+{
+	char str[16];
+	uvlong tb;
+
+	cycles(&tb);
+
+	snprint(str, sizeof(str), "%16.16llux", (tb/700)* 1000);
+	return readstr(offset, a, n, str);
+}
+
+char *cputype = "mips";
+
+char	*faultsprint(char *, char *);
+char	*fpemuprint(char *, char *);
+
+static long
+archctlread(Chan*, void *a, long nn, vlong offset)
+{
+	int n;
+	char *buf, *p, *ep;
+
+	p = buf = malloc(READSTR);
+	if(p == nil)
+		error(Enomem);
+	ep = p + READSTR;
+	p = seprint(p, ep, "cpu %s %lud\n", cputype,
+		(ulong)(m->hz+999999)/1000000);
+	p = seprint(p, ep, "stlb hash collisions");
+	for (n = 0; n < conf.nmach; n++)
+		p = seprint(p, ep, " %d", MACHP(n)->hashcoll);
+	p = seprint(p, ep, "\n");
+	p = seprint(p, ep, "NKTLB %d ktlb misses %ld utlb misses %ld\n",
+		NKTLB, m->ktlbfault, m->utlbfault);
+	p = fpemuprint(p, ep);
+	faultsprint(p, ep);
+	n = readstr(offset, a, nn, buf);
+	free(buf);
+	return n;
+}
+
+enum
+{
+	CMfpemudebug,
+};
+
+static Cmdtab archctlmsg[] =
+{
+#ifdef FPEMUDEBUG
+	CMfpemudebug,	"fpemudebug",	2,
+#else
+	CMfpemudebug,	"dummy",	1,
+#endif
+};
+
+static long
+archctlwrite(Chan*, void *a, long n, vlong)
+{
+	Cmdbuf *cb;
+	Cmdtab *ct;
+
+	cb = parsecmd(a, n);
+	if(waserror()){
+		free(cb);
+		nexterror();
+	}
+	ct = lookupcmd(cb, archctlmsg, nelem(archctlmsg));
+	switch(ct->index){
+	case CMfpemudebug:
+		fpemudebug = atoi(cb->f[1]);
+		break;
+	}
+	free(cb);
+	poperror();
+	return n;
+}
+
+void
+archinit(void)
+{
+	addarchfile("cputype", 0444, cputyperead, nil);
+	addarchfile("timebase",0444, tbread, nil);
+	addarchfile("archctl", 0664, archctlread, archctlwrite);
+//	addarchfile("nsec", 0444, nsread, nil);
+}

+ 1475 - 0
sys/src/9/rb/devether.c

@@ -0,0 +1,1475 @@
+/*
+ * Atheros 71xx ethernets for rb450g.
+ *
+ * all 5 PHYs are accessible only through first ether's register space.
+ *
+ * TODO:
+ *	promiscuous mode.
+ *	make ether1 work: probably needs mii/phy initialisation,
+ *	maybe needs 8316 switch code too (which requires mdio, phy, etc. glop).
+ * to maybe do some day:
+ *	dig mac addresses out & config phy/mii via spi or other grot and swill
+ *	(instead of editing rb config file).
+ */
+#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"
+#include	"ethermii.h"
+#include	<pool.h>
+
+enum {
+	Ntd	= 64,
+	Nrd	= 256,
+	Nrb	= 1024,
+
+	Bufalign= 4,
+	Rbsz	= ETHERMAXTU + 4,	/* 4 for CRC */
+};
+
+extern uchar arge0mac[Eaddrlen];	/* see rb config file */
+extern uchar arge1mac[Eaddrlen];
+
+typedef struct Arge Arge;
+typedef struct Ctlr Ctlr;
+typedef struct Desc Desc;
+typedef struct Etherif Etherif;
+
+/*
+ *  device registers
+ */
+struct Arge {
+	ulong	cfg1;
+	ulong	cfg2;
+	ulong	ifg;
+	ulong	hduplex;
+	ulong	maxframelen;
+	uchar	_pad0[0x20 - 0x14];
+
+	ulong	miicfg;
+	ulong	miicmd;
+	ulong	miiaddr;
+	ulong	miictl;
+	ulong	miists;
+	ulong	miiindic;
+
+	ulong	ifctl;
+	ulong	_pad1;
+	ulong	staaddr1;
+	ulong	staaddr2;
+
+	ulong	fifocfg[3];
+	ulong	fifotxthresh;
+	ulong	fiforxfiltmatch;
+	ulong	fiforxfiltmask;
+	ulong	fiforam[7];
+	uchar	_pad2[0x180 - 0x7c];
+
+	/* dma */
+	ulong	txctl;
+	ulong	txdesc;
+	ulong	txsts;
+	ulong	rxctl;
+	ulong	rxdesc;
+	ulong	rxsts;
+	ulong	dmaintr;
+	ulong	dmaintrsts;
+};
+
+enum {
+	Cfg1softrst		= 1 << 31,
+	Cfg1simulrst		= 1 << 30,
+	Cfg1macrxblkrst		= 1 << 19,
+	Cfg1mactxblkrst		= 1 << 18,
+	Cfg1rxfuncrst		= 1 << 17,
+	Cfg1txfuncrst		= 1 << 16,
+	Cfg1loopback		= 1 <<  8,
+	Cfg1rxflowctl		= 1 <<  5,
+	Cfg1txflowctl		= 1 <<  4,
+	Cfg1syncrx		= 1 <<  3,
+	Cfg1rxen		= 1 <<  2,
+	Cfg1synctx		= 1 <<  1,
+	Cfg1txen		= 1 <<  0,
+
+	Cfg2preamblelenmask	= 0xf,
+	Cfg2preamblelenshift	= 12,
+	Cfg2ifmode1000		= 2 << 8,
+	Cfg2ifmode10_100	= 1 << 8,
+	Cfg2ifmodeshift		= 8,
+	Cfg2ifmodemask		= 3,
+	Cfg2hugeframe		= 1 << 5,
+	Cfg2lenfield		= 1 << 4,
+	Cfg2enpadcrc		= 1 << 2,
+	Cfg2encrc		= 1 << 1,
+	Cfg2fdx			= 1 << 0,
+
+	Miicfgrst		= 1 << 31,
+	Miicfgscanautoinc	= 1 <<  5,
+	Miicfgpreamblesup	= 1 <<  4,
+	Miicfgclkselmask	= 0x7,
+	Miicfgclkdiv4		= 0,
+	Miicfgclkdiv6		= 2,
+	Miicfgclkdiv8		= 3,
+	Miicfgclkdiv10		= 4,
+	Miicfgclkdiv14		= 5,
+	Miicfgclkdiv20		= 6,
+	Miicfgclkdiv28		= 7,
+
+	Miicmdscancycle		= 1 << 1,
+	Miicmdread		= 1,
+	Miicmdwrite		= 0,
+
+	Miiphyaddrshift		= 8,
+	Miiphyaddrmask		= 0xff,
+	Miiregmask		= 0x1f,
+
+	Miictlmask		= 0xffff,
+
+	Miistsmask		= 0xffff,
+
+	Miiindicinvalid		= 1 << 2,
+	Miiindicscanning	= 1 << 1,
+	Miiindicbusy		= 1 << 0,
+
+	Ifctlspeed		= 1 << 16,
+
+	Fifocfg0txfabric	= 1 << 4,
+	Fifocfg0txsys		= 1 << 3,
+	Fifocfg0rxfabric	= 1 << 2,
+	Fifocfg0rxsys		= 1 << 1,
+	Fifocfg0watermark	= 1 << 0,
+	Fifocfg0all		= MASK(5),
+	Fifocfg0enshift		= 8,
+
+	/*
+	 * these flags applicable both to filter mask and to filter match.
+	 * `Ff' is for `fifo filter'.
+	 */
+	Ffunicast		= 1 << 17,
+	Fftruncframe		= 1 << 16,
+	Ffvlantag		= 1 << 15,
+	Ffunsupopcode		= 1 << 14,
+	Ffpauseframe		= 1 << 13,
+	Ffctlframe		= 1 << 12,
+	Fflongevent		= 1 << 11,
+	Ffdribblenibble		= 1 << 10,
+	Ffbcast			= 1 <<  9,
+	Ffmcast			= 1 <<  8,
+	Ffok			= 1 <<  7,
+	Ffoorange		= 1 <<  6,
+	Fflenmsmtch		= 1 <<  5,
+	Ffcrcerr		= 1 <<  4,
+	Ffcodeerr		= 1 <<  3,
+	Fffalsecarrier		= 1 <<  2,
+	Ffrxdvevent		= 1 <<  1,
+	Ffdropevent		= 1 <<  0,
+	/*
+	 * exclude unicast and truncated frames from matching.
+	 */
+	Ffmatchdflt = Ffvlantag | Ffunsupopcode | Ffpauseframe | Ffctlframe |
+		Fflongevent | Ffdribblenibble | Ffbcast | Ffmcast | Ffok |
+		Ffoorange | Fflenmsmtch | Ffcrcerr | Ffcodeerr |
+		Fffalsecarrier | Ffrxdvevent | Ffdropevent,
+
+	/* `Frm' is for `fifo receive mask'. */
+	Frmbytemode		= 1 << 19,
+	Frmnoshortframe		= 1 << 18,
+	Frmbit17		= 1 << 17,
+	Frmbit16		= 1 << 16,
+	Frmtruncframe		= 1 << 15,
+	Frmlongevent		= 1 << 14,
+	Frmvlantag		= 1 << 13,
+	Frmunsupopcode		= 1 << 12,
+	Frmpauseframe		= 1 << 11,
+	Frmctlframe		= 1 << 10,
+	Frmdribblenibble	= 1 <<  9,
+	Frmbcast		= 1 <<  8,
+	Frmmcast		= 1 <<  7,
+	Frmok			= 1 <<  6,
+	Frmoorange		= 1 <<  5,
+	Frmlenmsmtch		= 1 <<  4,
+	Frmcodeerr		= 1 <<  3,
+	Frmfalsecarrier		= 1 <<  2,
+	Frmrxdvevent		= 1 <<  1,
+	Frmdropevent		= 1 <<  0,
+	/*
+	 *  len. mismatch, unsupp. opcode and short frame bits excluded
+	 */
+	Ffmaskdflt = Frmnoshortframe | Frmbit17 | Frmbit16 | Frmtruncframe |
+		Frmlongevent | Frmvlantag | Frmpauseframe | Frmctlframe |
+		Frmdribblenibble | Frmbcast | Frmmcast | Frmok | Frmoorange |
+		Frmcodeerr | Frmfalsecarrier | Frmrxdvevent | Frmdropevent,
+
+	Dmatxctlen	= 1 << 0,
+
+	/* dma tx status */
+	Txpcountmask	= 0xff,
+	Txpcountshift	= 16,
+	Txbuserr	= 1 << 3,
+	Txunderrun	= 1 << 1,
+	Txpktsent	= 1 << 0,
+
+	Dmarxctlen	= 1 << 0,
+
+	/* dma rx status */
+	Rxpcountmask	= 0xff,
+	Rxpcountshift	= 16,
+	Rxbuserr	= 1 << 3,
+	Rxovflo		= 1 << 2,
+	Rxpktrcvd	= 1 << 0,
+
+	/* dmaintr & dmaintrsts bits */
+	Dmarxbuserr	= 1 << 7,
+	Dmarxovflo	= 1 << 6,
+	Dmarxpktrcvd	= 1 << 4,
+	Dmatxbuserr	= 1 << 3,
+	Dmatxunderrun	= 1 << 1,
+	Dmatxpktsent	= 1 << 0,
+	/* we don't really need most tx interrupts */
+	Dmaall		= Dmarxbuserr | Dmarxovflo | Dmarxpktrcvd | Dmatxbuserr,
+
+	Spictlremapdisable	= 1 << 6,
+	Spictlclkdividermask	= MASK(6),
+
+	Spiioctlcs2		= 1 << 18,
+	Spiioctlcs1		= 1 << 17,
+	Spiioctlcs0		= 1 << 16,
+	Spiioctlcsmask		= 7 << 16,
+	Spiioctlclk		= 1 << 8,
+	Spiioctldo		= 1,
+};
+
+struct Spi {			/* at 0x1f000000 */
+	ulong	fs;
+	ulong	ctl;
+	ulong	ioctl;
+	ulong	rds;
+};
+
+/* hw descriptors of buffer rings (rx and tx), need to be uncached */
+struct Desc {
+	ulong	addr;		/* of packet buffer */
+	ulong	ctl;
+	Desc	*next;
+	ulong	_pad;
+};
+
+enum {
+	Descempty	= 1 << 31,
+	Descmore	= 1 << 24,
+	Descszmask	= MASK(12),
+};
+#define DMASIZE(len)	((len) & Descszmask)
+
+struct Ctlr {
+	Arge	*regs;
+	Ether*	edev;			/* backward pointer */
+
+	Lock;				/* attach */
+	int	init;
+	int	attached;
+
+	Mii*	mii;
+	Rendez	lrendez;
+	int	lim;
+	int	link;
+	int	phymask;
+
+	/* receiver */
+	Rendez	rrendez;
+	uint	rintr;			/* count */
+	int	pktstoread;		/* flag */
+	int	discard;
+	/* rx descriptors */
+	Desc*	rdba;			/* base address */
+	Block**	rd;
+	uint	rdh;			/* head */
+	uint	rdt;			/* tail */
+	uint	nrdfree;		/* rd's awaiting pkts (sort of) */
+
+	/* transmitter */
+	Rendez	trendez;
+	uint	tintr;			/* count */
+	int	pktstosend;		/* flag */
+	int	ntq;
+	/* tx descriptors */
+	Desc*	tdba;			/* base address */
+	Block**	td;
+	uint	tdh;			/* head */
+	uint	tdt;			/* tail */
+};
+
+struct Etherif {
+	uintptr	regs;
+	int	irq;
+	uchar	*mac;
+	int	phymask;
+};
+
+static Etherif etherifs[] = {
+	{ 0x1a000000, ILenet0, arge0mac, 1<<4 },
+	{ 0x19000000, ILenet1, arge1mac, MASK(4) },
+};
+
+static Ether *etherxx[MaxEther];
+static Lock athrblock;		/* free receive Blocks */
+static Block* athrbpool;	/* receive Blocks for all ath controllers */
+
+static void	athrbfree(Block* bp);
+
+/*
+ * ar8316 ether switch
+ */
+
+enum {
+	Swrgmii	= 0,
+	Swgmii	= 1,
+	Swphy4cpu = 0, /* flag: port 4 connected to CPU (not internal switch) */
+};
+
+typedef struct Switch Switch;
+struct Switch {
+	int	page;
+	int	scdev;
+};
+
+enum {
+	/* atheros-specific mii registers */
+	Miiathdbgaddr	= 0x1d,
+	Miiathdbgdata	= 0x1e,
+
+	Swregmask	= 0,
+		Swmaskrevmask	= 0x00ff,
+		Swmaskvermask	= 0xff00,
+		Swmaskvershift	= 8,
+		Swmasksoftreset	= 1 << 31,
+
+	Swregmode	= 8,
+		Swdir615uboot	= 0x8d1003e0,
+		/* from ubiquiti rspro */
+		Swrgmiiport4iso	= 0x81461bea,
+		Swrgmiiport4sw	= 0x01261be2,
+		/* avm fritz!box 7390 */
+		Swgmiiavm	= 0x010e5b71,
+
+		Swmac0gmiien	= 1 <<  0,
+		Swmac0rgmiien	= 1 <<  1,
+		Swphy4gmiien	= 1 <<  2,
+		Swphy4rgmiien	= 1 <<  3,
+		Swmac0macmode	= 1 <<  4,
+		Swrgmiirxclkdelayen= 1 <<  6,
+		Swrgmiitxclkdelayen= 1 <<  7,
+		Swmac5macmode	= 1 << 14,
+		Swmac5phymode	= 1 << 15,
+		Swtxdelays0	= 1 << 21,
+		Swtxdelays1	= 1 << 22,
+		Swrxdelays0	= 1 << 23,
+		Swledopenen	= 1 << 24,
+		Swspien		= 1 << 25,
+		Swrxdelays1	= 1 << 26,
+		Swpoweronsel	= 1 << 31,
+
+	Swregfloodmask	= 0x2c,
+		Swfloodmaskbcast2cpu= 1 << 26,
+
+	Swregglobal	= 0x30,
+		Swglobalmtumask	= 0x7fff,
+};
+
+#ifdef NOTYET
+void *
+devicegetparent(int)
+{
+	static int glop;
+
+	return &glop;
+}
+
+static void
+arswsplitsetpage(int dev, ulong addr, ushort *phy, ushort *reg)
+{
+	static Switch ar8316;
+	Switch *sc = &ar8316;
+	ushort page;
+
+	page = ((addr) >> 9) & 0xffff;
+	*phy = (((addr) >> 6) & 0x7) | 0x10;
+	*reg = ((addr) >> 1) & 0x1f;
+	MDIOWRREG(devicegetparent(dev), 0x18, 0, page);
+	sc->page = page;
+}
+
+/*
+ * Read half a register.  Some of the registers define control bits, and
+ * the sequence of half-word accesses matters.  The register addresses
+ * are word-even (mod 4).
+ */
+static int
+arswrdreg16(int dev, int addr)
+{
+	ushort phy, reg;
+
+	arswsplitsetpage(dev, addr, &phy, &reg);
+	return MDIORDREG(devicegetparent(dev), phy, reg);
+}
+
+void
+arswwritedbg(int dev, int phy, ushort dbgaddr, ushort dbgdata)
+{
+	MDIOWRREG(devicegetparent(dev), phy, Miiathdbgaddr, dbgaddr);
+	MDIOWRREG(devicegetparent(dev), phy, Miiathdbgdata, dbgdata);
+}
+
+/*
+ * Write half a register
+ */
+static inline int
+arswwrreg16(int dev, int addr, int data)
+{
+	ushort phy, reg;
+
+	arswsplitsetpage(dev, addr, &phy, &reg);
+	return MDIOWRREG(devicegetparent(dev), phy, reg, data);
+}
+
+/* arsw??reglsb routines operate on lower 16 bits; *msb on upper ones */
+
+int
+arswrdreg(int dev, int addr)
+{
+	return arswrdreglsb(dev, addr) | arswrdregmsb(dev, addr);
+}
+
+int
+arswwrreg(int dev, int addr, int value)
+{
+	arswwrreglsb(dev, addr, value);		/* XXX check this write too? */
+	return arswwrregmsb(dev, addr, value);
+}
+
+int
+arswmodifyreg(int dev, int addr, int mask, int set)
+{
+	return arswwrreg(dev, addr, (arswrdreg(dev, addr) & ~mask) | set);
+}
+
+/*
+ * initialise the switch
+ */
+static int
+ar8316init(Switch *sc)
+{
+	if (Swrgmii && Swphy4cpu) {
+		arswwrreg(sc->scdev, Swregmode, Swrgmiiport4iso);
+		iprint("ar8316: MAC port == RGMII, port 4 = dedicated PHY\n");
+	} else if (Swrgmii) {
+		arswwrreg(sc->scdev, Swregmode, Swrgmiiport4sw);
+		iprint("ar8316: MAC port == RGMII, port 4 = switch port\n");
+	} else if (Swgmii) {
+		arswwrreg(sc->scdev, Swregmode, Swgmiiavm);
+		iprint("ar8316: MAC port == GMII\n");
+	} else {
+		iprint("ar8316: unknown switch PHY config\n");
+		return -1;
+	}
+
+	delay(1);			/* wait for things to settle */
+
+	if (Swrgmii && Swphy4cpu) {
+		iprint("ar8316: port 4 RGMII hack\n");
+
+		/* work around for phy4 rgmii mode */
+		arswwritedbg(sc->scdev, 4, 0x12, 0x480c);
+		arswwritedbg(sc->scdev, 4, 0x0, 0x824e);	/* rx delay */
+		arswwritedbg(sc->scdev, 4, 0x5, 0x3d47);	/* tx delay */
+		delay(1);		/* again to let things settle */
+	}
+	arswwrreg(sc->scdev, 0x38, 0xc000050e);	/* mystery */
+
+	/*
+	 * Flood address table misses to all ports, and enable forwarding of
+	 * broadcasts to the cpu port.
+	 */
+	arswwrreg(sc->scdev, Swregfloodmask, Swfloodmaskbcast2cpu | 0x003f003f);
+	arswmodifyreg(sc->scdev, Swregglobal, Swglobalmtumask, ETHERMAXTU+8+2);
+	return 0;
+}
+#endif			/* NOTYET */
+
+static long
+ifstat(Ether* edev, void* a, long n, ulong offset)
+{
+	int l, i, r;
+	char *p;
+	Ctlr *ctlr;
+
+	ctlr = edev->ctlr;
+	p = malloc(READSTR);
+	if(p == nil)
+		error(Enomem);
+	l = 0;
+	l += snprint(p+l, READSTR-l, "tintr: %ud\n", ctlr->tintr);
+	l += snprint(p+l, READSTR-l, "rintr: %ud\n", ctlr->rintr);
+	l += snprint(p+l, READSTR-l, "discarded: %ud\n", ctlr->discard);
+
+	if(ctlr->mii != nil && ctlr->mii->curphy != nil){
+		l += snprint(p+l, READSTR-l, "phy:   ");
+		for(i = 0; i < NMiiPhyr; i++){
+			if(i && ((i & 0x07) == 0))
+				l += snprint(p+l, READSTR-l, "\n       ");
+			r = miimir(ctlr->mii, i);
+			l += snprint(p+l, READSTR-l, " %4.4uX", r);
+		}
+		snprint(p+l, READSTR-l, "\n");
+	}
+	n = readstr(offset, a, n, p);
+	free(p);
+
+	return n;
+}
+
+static void
+etherrtrace(Netfile* f, Etherpkt* pkt, int len)
+{
+	int i, n;
+	Block *bp;
+
+	if(qwindow(f->in) <= 0)
+		return;
+	if(len > 58)
+		n = 58;
+	else
+		n = len;
+	bp = iallocb(64);
+	if(bp == nil)
+		return;
+	memmove(bp->wp, pkt->d, n);
+	i = TK2MS(MACHP(0)->ticks);
+	bp->wp[58] = len>>8;
+	bp->wp[59] = len;
+	bp->wp[60] = i>>24;
+	bp->wp[61] = i>>16;
+	bp->wp[62] = i>>8;
+	bp->wp[63] = i;
+	bp->wp += 64;
+	qpass(f->in, bp);
+}
+
+Block*
+etheriq(Ether* ether, Block* bp, int fromwire)
+{
+	Etherpkt *pkt;
+	ushort type;
+	int len, multi, tome, fromme;
+	Netfile **ep, *f, **fp, *fx;
+	Block *xbp;
+	Ctlr *ctlr;
+
+	ether->inpackets++;
+	ctlr = ether->ctlr;
+
+	pkt = (Etherpkt*)bp->rp;
+	len = BLEN(bp);
+	type = (pkt->type[0]<<8)|pkt->type[1];
+	fx = 0;
+	ep = &ether->f[Ntypes];
+
+	multi = pkt->d[0] & 1;
+	/* check for valid multicast addresses */
+	if(multi && memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) != 0 &&
+	    ether->prom == 0)
+		if(!activemulti(ether, pkt->d, sizeof(pkt->d))){
+			if(fromwire){
+				ctlr->discard++;
+				freeb(bp);
+				bp = 0;
+			}
+			return bp;
+		}
+
+	/* is it for me? */
+	tome   = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
+	fromme = memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0;
+
+	/*
+	 * Multiplex the packet to all the connections which want it.
+	 * If the packet is not to be used subsequently (fromwire != 0),
+	 * attempt to simply pass it into one of the connections, thereby
+	 * saving a copy of the data (usual case hopefully).
+	 */
+	for(fp = ether->f; fp < ep; fp++)
+		if((f = *fp) != nil && (f->type == type || f->type < 0))
+		if(tome || multi || f->prom)
+			/* Don't want to hear bridged packets */
+			if(f->bridge && !fromwire && !fromme)
+				continue;
+			else if(f->headersonly)
+				etherrtrace(f, pkt, len);
+			else if(fromwire && fx == 0)
+				fx = f;
+			else if(xbp = iallocb(len)){
+				memmove(xbp->wp, pkt, len);
+				xbp->wp += len;
+				if(qpass(f->in, xbp) < 0){
+					iprint("soverflow for f->in\n");
+					ether->soverflows++;
+				}
+			}else{
+				iprint("soverflow iallocb\n");
+				ether->soverflows++;
+			}
+	if(fx){
+		if(qpass(fx->in, bp) < 0){
+			iprint("soverflow for fx->in\n");
+			ether->soverflows++;
+		}
+		return 0;
+	}
+	if(fromwire){
+		ctlr->discard++;
+		freeb(bp);
+		return 0;
+	}
+	return bp;
+}
+
+static void
+athhwreset(Ether *ether)
+{
+	Ctlr *ctlr;
+	Arge *arge;
+
+	ctlr = ether->ctlr;
+	if (ctlr == nil)
+		return;
+	arge = ctlr->regs;
+	if (arge == nil)
+		return;
+
+	arge->dmaintr = 0;
+
+	arge->rxctl = 0;
+	arge->txctl = 0;
+	coherence();
+
+	/*
+	 * give tx & rx time to stop, otherwise clearing desc registers
+	 * too early will cause random memory corruption.
+	 */
+	delay(1);
+
+	arge->rxdesc = 0;
+	arge->txdesc = 0;
+	coherence();
+
+	/* clear all interrupts */
+	while (arge->rxsts & Rxpktrcvd)
+		arge->rxsts = Rxpktrcvd;
+	while (arge->txsts & Txpktsent)
+		arge->txsts = Txpktsent;
+
+	/* and errors */
+	arge->rxsts = Rxbuserr | Rxovflo;
+	arge->txsts = Txbuserr | Txunderrun;
+}
+
+static void
+txreclaim(Ctlr *ctlr)
+{
+	uint tdh;
+	Arge *arge;
+	Block *bp;
+
+	arge = ctlr->regs;
+	tdh = ctlr->tdh;
+	while (tdh != ctlr->tdt && ctlr->tdba[tdh].ctl & Descempty){
+		arge->txsts = Txpktsent;
+
+		bp = ctlr->td[tdh];
+		ctlr->td[tdh] = nil;
+		if (bp)
+			freeb(bp);
+
+		ctlr->tdba[tdh].addr = 0;
+		ctlr->ntq--;
+		tdh = NEXT(tdh, Ntd);
+	}
+	ctlr->tdh = tdh;
+}
+
+static Block*
+athrballoc(void)
+{
+	Block *bp;
+
+	ilock(&athrblock);
+	if((bp = athrbpool) != nil){
+		athrbpool = bp->next;
+		bp->next = nil;
+		_xinc(&bp->ref);	/* prevent bp from being freed */
+	}
+	iunlock(&athrblock);
+	return bp;
+}
+
+static void
+athrbfree(Block* bp)
+{
+	bp->wp = bp->rp = bp->lim - ROUND(Rbsz, BLOCKALIGN);
+	bp->flag &= ~(Bipck | Budpck | Btcpck | Bpktck);
+
+	ilock(&athrblock);
+	bp->next = athrbpool;
+	athrbpool = bp;
+	iunlock(&athrblock);
+}
+
+static void
+rxnewbuf(Ctlr *ctlr, int i)
+{
+	Block *bp;
+	Desc *rd;
+
+	if (ctlr->rd[i] != nil)
+		return;
+	ctlr->rd[i] = bp = athrballoc();
+	if(bp == nil)
+		panic("#l%d: can't allocate receive buffer",
+			ctlr->edev->ctlrno);
+	dcflush(bp->rp, Rbsz);		/* writeback & invalidate */
+
+	rd = &ctlr->rdba[i];
+	rd->addr = PADDR(bp->rp);
+	rd->ctl = Descempty | DMASIZE(Rbsz);
+	ctlr->nrdfree++;
+}
+
+static void
+rxreclaim(Ctlr *ctlr)
+{
+	uint rdt;
+
+	rdt = ctlr->rdt;
+	while (rdt != ctlr->rdh && !(ctlr->rdba[rdt].ctl & Descempty)){
+		rxnewbuf(ctlr, rdt);
+		rdt = NEXT(rdt, Nrd);
+	}
+	ctlr->rdt = rdt;
+}
+
+static void
+etherintr(void *arg)
+{
+	int sts;
+	Arge *arge;
+	Ctlr *ctlr;
+	Ether *ether;
+
+	ether = arg;
+	ctlr = ether->ctlr;
+	arge = ctlr->regs;
+	ilock(ctlr);
+	sts = arge->dmaintrsts;
+	if (sts & Dmarxpktrcvd) {
+		arge->dmaintr &= ~Dmarxpktrcvd;
+		ctlr->pktstoread = 1;
+		wakeup(&ctlr->rrendez);
+		ctlr->rintr++;
+		sts &= ~Dmarxpktrcvd;
+	}
+	if (sts & (Dmatxpktsent | Dmatxunderrun)) {
+		arge->dmaintr &= ~(Dmatxpktsent | Dmatxunderrun);
+		ctlr->pktstosend = 1;
+		wakeup(&ctlr->trendez);
+		ctlr->tintr++;
+		sts &= ~(Dmatxpktsent | Dmatxunderrun);
+	}
+	iunlock(ctlr);
+	if (sts)
+		iprint("#l%d: sts %#ux\n", ether->ctlrno, sts);
+}
+
+static int
+pktstoread(void* v)
+{
+	Ctlr *ctlr = v;
+
+	return ctlr->pktstoread || !(ctlr->rdba[ctlr->rdh].ctl & Descempty);
+}
+
+static void
+rproc(void* arg)
+{
+	uint rdh, sz;
+	Arge *arge;
+	Block *bp;
+	Ctlr *ctlr;
+	Desc *rd;
+	Ether *edev;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+	arge = ctlr->regs;
+	for(;;){
+		/* wait for next interrupt */
+		ilock(ctlr);
+		arge->dmaintr |= Dmarxpktrcvd;
+		iunlock(ctlr);
+
+		sleep(&ctlr->rrendez, pktstoread, ctlr);
+		ctlr->pktstoread = 0;
+
+		rxreclaim(ctlr);
+		rdh = ctlr->rdh;
+		for (rd = &ctlr->rdba[rdh]; !(rd->ctl & Descempty);
+		     rd = &ctlr->rdba[rdh]){
+			bp = ctlr->rd[rdh];
+			assert(bp != nil);
+			ctlr->rd[rdh] = nil;
+
+			/* omit final 4 bytes (crc), pass pkt upstream */
+			sz = DMASIZE(rd->ctl) - 4;
+			assert(sz > 0 && sz <= Rbsz);
+			bp->wp = bp->rp + sz;
+			bp = etheriq(edev, bp, 1);
+			assert(bp == nil);		/* Block was consumed */
+
+			arge->rxsts = Rxpktrcvd;
+
+			ctlr->nrdfree--;
+			rdh = NEXT(rdh, Nrd);
+			if(ctlr->nrdfree < Nrd/2) {
+				/* rxreclaim reads ctlr->rdh */
+				ctlr->rdh = rdh;
+				rxreclaim(edev->ctlr);
+			}
+		}
+		ctlr->rdh = rdh;
+	}
+}
+
+static int
+pktstosend(void* v)
+{
+	Ether *edev = v;
+	Ctlr *ctlr = edev->ctlr;
+
+	return ctlr->pktstosend || ctlr->ntq > 0 || qlen(edev->oq) > 0;
+}
+
+static void
+tproc(void* arg)
+{
+	uint tdt, added;
+	Arge *arge;
+	Block *bp;
+	Ctlr *ctlr;
+	Desc *td;
+	Ether *edev;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+	arge = ctlr->regs;
+	for(;;){
+		/* wait for next free buffer and output queue block */
+		sleep(&ctlr->trendez, pktstosend, edev);
+		ctlr->pktstosend = 0;
+
+		txreclaim(ctlr);
+
+		/* copy as much of my output q as possible into output ring */
+		added = 0;
+		tdt = ctlr->tdt;
+		while(ctlr->ntq < Ntd - 1){
+			td = &ctlr->tdba[tdt];
+			if (!(td->ctl & Descempty))
+				break;
+			bp = qget(edev->oq);
+			if(bp == nil)
+				break;
+
+			/* make sure the whole packet is in ram */
+			dcflush(bp->rp, BLEN(bp));
+
+			/*
+			 * Give ownership of the descriptor to the chip,
+			 * increment the software ring descriptor pointer.
+			 */
+			ctlr->td[tdt] = bp;
+			td->addr = PADDR(bp->rp);
+			td->ctl = DMASIZE(BLEN(bp));
+			coherence();
+
+			added++;
+			ctlr->ntq++;
+			tdt = NEXT(tdt, Ntd);
+		}
+		ctlr->tdt = tdt;
+		/*
+		 * Underrun turns off TX.  Clear underrun indication.
+		 * If there's anything left in the ring, reactivate the tx.
+		 */
+		if (arge->dmaintrsts & Dmatxunderrun)
+			arge->txsts = Txunderrun;
+		if(1 || added)
+			arge->txctl = Dmatxctlen;	/* kick xmiter */
+		ilock(ctlr);
+		if(ctlr->ntq >= Ntd/2)			/* tx ring half-full? */
+			arge->dmaintr |= Dmatxpktsent;
+		else if (ctlr->ntq > 0)
+			arge->dmaintr |= Dmatxunderrun;
+		iunlock(ctlr);
+		txreclaim(ctlr);
+	}
+}
+
+/*
+ *  turn promiscuous mode on/off
+ */
+static void
+promiscuous(void *ve, int on)
+{
+	USED(ve, on);
+}
+
+static void
+multicast(void *ve, uchar*, int on)
+{
+	USED(ve, on);
+}
+
+static void
+linkdescs(Desc *base, int ndesc)
+{
+	int i;
+
+	for(i = 0; i < ndesc - 1; i++)
+		base[i].next = (Desc *)PADDR(&base[i+1]);
+	base[ndesc - 1].next = (Desc *)PADDR(&base[0]);
+}
+
+/*
+ * Initialise the receive and transmit buffer rings.
+ *
+ * This routine is protected by ctlr->init.
+ */
+static void
+ringinit(Ctlr* ctlr)
+{
+	int i;
+	void *v;
+
+	if(ctlr->rdba == 0){
+		v = xspanalloc(Nrd * sizeof(Desc), CACHELINESZ, 0);
+		assert(v);
+		ctlr->rdba = (Desc *)KSEG1ADDR(v);
+		ctlr->rd = xspanalloc(Nrd * sizeof(Block *), 0, 0);
+		assert(ctlr->rd != nil);
+		linkdescs(ctlr->rdba, Nrd);
+		for(i = 0; i < Nrd; i++)
+			rxnewbuf(ctlr, i);
+	}
+	ctlr->rdt = ctlr->rdh = 0;
+
+	if(ctlr->tdba == 0) {
+		v = xspanalloc(Ntd * sizeof(Desc), CACHELINESZ, 0);
+		assert(v);
+		ctlr->tdba = (Desc *)KSEG1ADDR(v);
+		ctlr->td = xspanalloc(Ntd * sizeof(Block *), 0, 0);
+		assert(ctlr->td != nil);
+	}
+	memset(ctlr->td, 0, Ntd * sizeof(Block *));
+
+	linkdescs(ctlr->tdba, Ntd);
+	for(i = 0; i < Ntd; i++)
+		ctlr->tdba[i].ctl = Descempty;
+
+	ctlr->tdh = ctlr->tdt = 0;
+}
+
+static void
+cfgmediaduplex(Ether *ether)
+{
+	Arge *arge, *arge0;
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	arge = ctlr->regs;
+	arge->cfg2 = (arge->cfg2 & ~Cfg2ifmode10_100) | Cfg2ifmode1000 | Cfg2fdx;
+	arge->ifctl &= ~Ifctlspeed;
+	arge->fiforxfiltmask |= Frmbytemode;
+	arge->fifotxthresh = 0x008001ff;	/* undocumented magic */
+
+	if (ether->ctlrno > 0) {
+		/* set PLL registers: copy from arge0 */
+		arge0 = (Arge *)(KSEG1 | etherifs[0].regs);
+		USED(arge0);
+	}
+}
+
+static void
+athmii(Ether *ether, int phymask)
+{
+	USED(ether, phymask);
+}
+
+static void
+athcfg(Ether *ether, int phymask)
+{
+	uchar *eaddr;
+	Arge *arge;
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	arge = ctlr->regs;
+	if(ether->ctlrno > 0){
+		if(0){
+			/* doing this seems to disable both ethers */
+			arge->cfg1 |= Cfg1softrst;		/* stop */
+			delay(20);
+			*Reset |= Rstge1mac;
+			delay(100);
+		}
+		*Reset &= ~Rstge1mac;
+		delay(200);
+	}
+
+	/* configure */
+	arge->cfg1 = Cfg1syncrx | Cfg1rxen | Cfg1synctx | Cfg1txen;
+	arge->cfg2 |= Cfg2enpadcrc | Cfg2lenfield | Cfg2encrc;
+	arge->maxframelen = Rbsz;
+
+	if(ether->ctlrno > 0){
+		arge->miicfg = Miicfgrst;
+		delay(100);
+		arge->miicfg = Miicfgclkdiv28;
+		delay(100);
+	}
+
+	/*
+	 * Set all Ethernet address registers to the same initial values
+	 * set all four addresses to 66-88-aa-cc-dd-ee
+	 */
+	eaddr = ether->ea;
+	arge->staaddr1 = eaddr[2]<<24 | eaddr[3]<<16 | eaddr[4]<<8  | eaddr[5];
+	arge->staaddr2 = eaddr[0]<< 8 | eaddr[1];
+
+	arge->fifocfg[0] = Fifocfg0all << Fifocfg0enshift; /* undocumented magic */
+	arge->fifocfg[1] = 0x0fff0000;	/* undocumented magic */
+	arge->fifocfg[2] = 0x00001fff;	/* undocumented magic */
+
+	arge->fiforxfiltmatch = Ffmatchdflt;
+	arge->fiforxfiltmask  = Ffmaskdflt;
+
+	/* phy goo */
+	athmii(ether, phymask);
+	if (ether->ctlrno > 0)
+		cfgmediaduplex(ether);
+}
+
+static int
+athattach(Ether *ether)
+{
+	int i;
+	char name[32];
+	Arge *arge;
+	Block *bp;
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	if (ctlr->attached)
+		return -1;
+	ilock(ctlr);
+	ctlr->init = 1;
+	for(i = 0; i < Nrb; i++){
+		if((bp = allocb(Rbsz + Bufalign)) == nil)
+			error(Enomem);
+		bp->free = athrbfree;
+		freeb(bp);
+	}
+	ringinit(ctlr);
+	ctlr->init = 0;
+	iunlock(ctlr);
+
+	athcfg(ether, ctlr->phymask);
+
+	/* start */
+	arge = ctlr->regs;
+	arge->txdesc = PADDR(ctlr->tdba);
+	arge->rxdesc = PADDR(ctlr->rdba);
+	coherence();
+	arge->rxctl = Dmarxctlen;
+
+	snprint(name, KNAMELEN, "#l%drproc", ether->ctlrno);
+	kproc(name, rproc, ether);
+
+	snprint(name, KNAMELEN, "#l%dtproc", ether->ctlrno);
+	kproc(name, tproc, ether);
+
+	ilock(ctlr);
+	arge->dmaintr |= Dmaall;
+	iunlock(ctlr);
+
+	ctlr->attached = 1;
+	return 0;
+}
+
+/*
+ * strategy: RouterBOOT has initialised arge0, try to leave it alone.
+ * copy arge0 registers to arge1, with a few exceptions.
+ */
+static int
+athreset(Ether *ether)
+{
+	Arge *arge;
+	Ctlr *ctlr;
+	Etherif *ep;
+
+	if (ether->ctlrno < 0 || ether->ctlrno >= MaxEther)
+		return -1;
+	if (ether->ctlr == nil) {
+		/*
+		 * Allocate a controller structure and start to initialise it.
+		 */
+		ether->ctlr = ctlr = malloc(sizeof(Ctlr));
+		if (ctlr == nil)
+			return -1;
+		ctlr->edev = ether;
+		ep = etherifs + ether->ctlrno;
+		ctlr->regs = arge = (Arge *)(KSEG1 | ep->regs);
+		ctlr->phymask = ep->phymask;
+
+		ether->port = (uint)arge;
+		ether->irq = ep->irq;
+		memmove(ether->ea, ep->mac, Eaddrlen);
+		ether->ifstat = ifstat;
+		ether->promiscuous = promiscuous;
+		ether->multicast = multicast;
+		ether->arg = ether;
+	}
+	athhwreset(ether);
+	return 0;
+}
+
+static Ether*
+etherprobe(int ctlrno)
+{
+	int i, lg;
+	ulong mb, bsz;
+	Ether *ether;
+	char buf[128], name[32];
+
+	ether = malloc(sizeof(Ether));
+	if(ether == nil)
+		error(Enomem);
+	memset(ether, 0, sizeof(Ether));
+	ether->ctlrno = ctlrno;
+	ether->tbdf = BUSUNKNOWN;
+	ether->mbps = 1000;
+	ether->minmtu = ETHERMINTU;
+	ether->maxmtu = ETHERMAXTU;
+	ether->mtu = ETHERMAXTU;
+
+	if(ctlrno >= MaxEther || athreset(ether) < 0){
+		free(ether);
+		return nil;
+	}
+
+	snprint(name, sizeof(name), "ether%d", ctlrno);
+
+	/*
+	 * If ether->irq is <0, it is a hack to indicate no interrupt
+	 * used by ethersink.
+	 * apparently has to be done here and cannot be deferred until attach.
+	 */
+	if(ether->irq >= 0)
+		intrenable(ether->irq, etherintr, ether);
+
+	i = sprint(buf, "#l%d: atheros71xx: ", ctlrno);
+	if(ether->mbps >= 1000)
+		i += sprint(buf+i, "%dGbps", ether->mbps/1000);
+	else
+		i += sprint(buf+i, "%dMbps", ether->mbps);
+	i += sprint(buf+i, " port %#luX irq %d", PADDR(ether->port), ether->irq);
+	i += sprint(buf+i, ": %2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux",
+		ether->ea[0], ether->ea[1], ether->ea[2],
+		ether->ea[3], ether->ea[4], ether->ea[5]);
+	sprint(buf+i, "\n");
+	print(buf);
+
+	/*
+	 * input queues are allocated by ../port/netif.c:/^openfile.
+	 * the size will be the last argument to netifinit() below.
+	 *
+	 * output queues should be small, to minimise `bufferbloat',
+	 * which confuses tcp's feedback loop.  at 1Gb/s, it only takes
+	 * ~15µs to transmit a full-sized non-jumbo packet.
+	 */
+
+	/* compute log10(ether->mbps) into lg */
+	for(lg = 0, mb = ether->mbps; mb >= 10; lg++)
+		mb /= 10;
+	if (lg > 13)			/* sanity cap; 2**(13+16) = 2²⁹ */
+		lg = 13;
+
+	/* allocate larger input queues for higher-speed interfaces */
+	bsz = 1UL << (lg + 16);		/* 2ⁱ⁶ = 64K, bsz = 2ⁿ × 64K */
+	while (bsz > mainmem->maxsize / 8 && bsz > 128*1024)	/* sanity */
+		bsz /= 2;
+	netifinit(ether, name, Ntypes, bsz);
+
+	if(ether->oq == nil)
+		ether->oq = qopen(1 << (lg + 13), Qmsg, 0, 0);
+	if(ether->oq == nil)
+		panic("etherreset %s: can't allocate output queue", name);
+
+	ether->alen = Eaddrlen;
+	memmove(ether->addr, ether->ea, Eaddrlen);
+	memset(ether->bcast, 0xFF, Eaddrlen);
+	return ether;
+}
+
+static void
+etherreset(void)
+{
+	int ctlrno;
+
+	for(ctlrno = 0; ctlrno < MaxEther; ctlrno++)
+		etherxx[ctlrno] = etherprobe(ctlrno);
+}
+
+static void
+ethershutdown(void)
+{
+	Ether *ether;
+	int i;
+
+	for(i = 0; i < MaxEther; i++){
+		ether = etherxx[i];
+		if(ether)
+			athhwreset(ether);
+	}
+}
+
+static Chan *
+etherattach(char* spec)
+{
+	ulong ctlrno;
+	char *p;
+	Chan *chan;
+
+	ctlrno = 0;
+	if(spec && *spec){
+		ctlrno = strtoul(spec, &p, 0);
+		if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther))
+			error(Ebadarg);
+	}
+	if(etherxx[ctlrno] == 0)
+		error(Enodev);
+
+	chan = devattach('l', spec);
+	if(waserror()){
+		chanfree(chan);
+		nexterror();
+	}
+	chan->dev = ctlrno;
+	athattach(etherxx[ctlrno]);
+	poperror();
+	return chan;
+}
+
+static Walkqid*
+etherwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return netifwalk(etherxx[c->dev], c, nc, name, nname);
+}
+
+static Chan*
+etheropen(Chan *c, int omode)
+{
+	return netifopen(etherxx[c->dev], c, omode);
+}
+
+static void
+ethercreate(Chan*, char*, int, ulong)
+{
+}
+
+static void
+etherclose(Chan *c)
+{
+	netifclose(etherxx[c->dev], c);
+}
+
+static long
+etherread(Chan *chan, void *buf, long n, vlong off)
+{
+	Ether *ether;
+	ulong offset = off;
+
+	ether = etherxx[chan->dev];
+	if((chan->qid.type & QTDIR) == 0 && ether->ifstat){
+		/*
+		 * With some controllers it is necessary to reach
+		 * into the chip to extract statistics.
+		 */
+		if(NETTYPE(chan->qid.path) == Nifstatqid)
+			return ether->ifstat(ether, buf, n, offset);
+		else if(NETTYPE(chan->qid.path) == Nstatqid)
+			ether->ifstat(ether, buf, 0, offset);
+	}
+
+	return netifread(ether, chan, buf, n, offset);
+}
+
+static Block*
+etherbread(Chan *c, long n, ulong offset)
+{
+	return netifbread(etherxx[c->dev], c, n, offset);
+}
+
+/* kick the transmitter to drain the output ring */
+static void
+athtransmit(Ether* ether)
+{
+	Ctlr *ctlr;
+
+	ctlr = ether->ctlr;
+	ilock(ctlr);
+	ctlr->pktstosend = 1;
+	wakeup(&ctlr->trendez);
+	iunlock(ctlr);
+}
+
+static long (*athctl)(Ether *, char *, int) = nil;
+
+static int
+etheroq(Ether* ether, Block* bp)
+{
+	int len, loopback, s;
+	Etherpkt *pkt;
+
+	ether->outpackets++;
+
+	/*
+	 * Check if the packet has to be placed back onto the input queue,
+	 * i.e. if it's a loopback or broadcast packet or the interface is
+	 * in promiscuous mode.
+	 * If it's a loopback packet indicate to etheriq that the data isn't
+	 * needed and return, etheriq will pass-on or free the block.
+	 * To enable bridging to work, only packets that were originated
+	 * by this interface are fed back.
+	 */
+	pkt = (Etherpkt*)bp->rp;
+	len = BLEN(bp);
+	loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
+	if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){
+		s = splhi();
+		etheriq(ether, bp, 0);
+		splx(s);
+	}
+
+	if(!loopback){
+		if(qfull(ether->oq))
+			print("etheroq: WARNING: ether->oq full!\n");
+		qbwrite(ether->oq, bp);
+		athtransmit(ether);
+	} else
+		freeb(bp);
+
+	return len;
+}
+
+static long
+etherwrite(Chan* chan, void* buf, long n, vlong)
+{
+	Ether *ether;
+	Block *bp;
+	int nn, onoff;
+	Cmdbuf *cb;
+
+	ether = etherxx[chan->dev];
+	if(NETTYPE(chan->qid.path) != Ndataqid) {
+		nn = netifwrite(ether, chan, buf, n);
+		if(nn >= 0)
+			return nn;
+		cb = parsecmd(buf, n);
+		if(cb->f[0] && strcmp(cb->f[0], "nonblocking") == 0){
+			if(cb->nf <= 1)
+				onoff = 1;
+			else
+				onoff = atoi(cb->f[1]);
+			qnoblock(ether->oq, onoff);
+			free(cb);
+			return n;
+		}
+		free(cb);
+		if(athctl != nil)
+			return athctl(ether, buf, n);
+		error(Ebadctl);
+	}
+
+	assert(ether->ctlr != nil);
+	if(n > ether->mtu)
+		error(Etoobig);
+	if(n < ether->minmtu)
+		error(Etoosmall);
+
+	bp = allocb(n);
+	if(waserror()){
+		freeb(bp);
+		nexterror();
+	}
+	memmove(bp->rp, buf, n);
+	memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen);
+	poperror();
+	bp->wp += n;
+
+	return etheroq(ether, bp);
+}
+
+static long
+etherbwrite(Chan *c, Block *bp, ulong offset)
+{
+	return devbwrite(c, bp, offset);
+}
+
+static int
+etherstat(Chan *c, uchar *dp, int n)
+{
+	return netifstat(etherxx[c->dev], c, dp, n);
+}
+
+static int
+etherwstat(Chan *c, uchar *dp, int n)
+{
+	return netifwstat(etherxx[c->dev], c, dp, n);
+}
+
+Dev etherdevtab = {
+	'l',
+	"ether",
+
+	etherreset,
+	devinit,
+	ethershutdown,
+	etherattach,
+	etherwalk,
+	etherstat,
+	etheropen,
+	ethercreate,
+	etherclose,
+	etherread,
+	etherbread,
+	etherwrite,
+	etherbwrite,
+	devremove,
+	etherwstat,
+	devpower,
+	devconfig,
+};

+ 36 - 0
sys/src/9/rb/etherif.h

@@ -0,0 +1,36 @@
+enum {
+	MaxEther	= 2,
+	Ntypes		= 8,
+};
+
+typedef struct Ether Ether;
+struct Ether {
+	int	ctlrno;
+	int	port;
+	int	irq;
+	int	tbdf;			/* type+busno+devno+funcno */
+
+	void	*ctlr;
+	Queue*	oq;
+	uchar	ea[Eaddrlen];
+
+	long	(*ifstat)(Ether*, void*, long, ulong);
+#ifdef MULTIETHERTYPES
+	void	(*attach)(Ether*);	/* filled in by reset routine */
+	void	(*detach)(Ether*);
+	void	(*transmit)(Ether*);
+	void	(*interrupt)(Ureg*, void*);
+	long 	(*ctl)(Ether*, void*, long); /* custom ctl messages */
+	void	(*power)(Ether*, int);	/* power on/off */
+	void	(*shutdown)(Ether*);	/* shutdown hardware before reboot */
+#endif
+	Netif;
+};
+
+extern Block* etheriq(Ether*, Block*, int);
+extern void addethercard(char*, int(*)(Ether*));
+extern ulong ethercrc(uchar*, int);
+extern int parseether(uchar*, char*);
+
+#define NEXT(x, l)	(((x)+1)%(l))
+#define PREV(x, l)	(((x) == 0) ? (l)-1: (x)-1)

+ 235 - 0
sys/src/9/rb/ethermii.c

@@ -0,0 +1,235 @@
+#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"
+#include "ethermii.h"
+
+int
+mii(Mii* mii, int mask)
+{
+	MiiPhy *miiphy;
+	int bit, oui, phyno, r, rmask;
+
+	/*
+	 * Probe through mii for PHYs in mask;
+	 * return the mask of those found in the current probe.
+	 * If the PHY has not already been probed, update
+	 * the Mii information.
+	 */
+	rmask = 0;
+	for(phyno = 0; phyno < NMiiPhy; phyno++){
+		bit = 1<<phyno;
+		if(!(mask & bit))
+			continue;
+		if(mii->mask & bit){
+			rmask |= bit;
+			continue;
+		}
+		if(mii->mir(mii, phyno, Bmsr) == -1)
+			continue;
+		r = mii->mir(mii, phyno, Phyidr1);
+		oui = (r & 0x3FFF)<<6;
+		r = mii->mir(mii, phyno, Phyidr2);
+		oui |= r>>10;
+		if(oui == 0xFFFFF || oui == 0)
+			continue;
+
+		if((miiphy = malloc(sizeof(MiiPhy))) == nil)
+			continue;
+
+		miiphy->mii = mii;
+		miiphy->oui = oui;
+		miiphy->phyno = phyno;
+
+		miiphy->anar = ~0;
+		miiphy->fc = ~0;
+		miiphy->mscr = ~0;
+
+		mii->phy[phyno] = miiphy;
+		if(mii->curphy == nil)
+			mii->curphy = miiphy;
+		mii->mask |= bit;
+		mii->nphy++;
+
+		rmask |= bit;
+	}
+	return rmask;
+}
+
+int
+miimir(Mii* mii, int r)
+{
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	return mii->mir(mii, mii->curphy->phyno, r);
+}
+
+int
+miimiw(Mii* mii, int r, int data)
+{
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	return mii->miw(mii, mii->curphy->phyno, r, data);
+}
+
+int
+miireset(Mii* mii)
+{
+	int bmcr;
+
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	bmcr = mii->mir(mii, mii->curphy->phyno, Bmcr);
+	bmcr |= BmcrR;
+	mii->miw(mii, mii->curphy->phyno, Bmcr, bmcr);
+	microdelay(1);
+
+	return 0;
+}
+
+int
+miiane(Mii* mii, int a, int p, int e)
+{
+	int anar, bmsr, mscr, r, phyno;
+
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	phyno = mii->curphy->phyno;
+
+	bmsr = mii->mir(mii, phyno, Bmsr);
+	if(!(bmsr & BmsrAna))
+		return -1;
+
+	if(a != ~0)
+		anar = (AnaTXFD|AnaTXHD|Ana10FD|Ana10HD) & a;
+	else if(mii->curphy->anar != ~0)
+		anar = mii->curphy->anar;
+	else{
+		anar = mii->mir(mii, phyno, Anar);
+		anar &= ~(AnaAP|AnaP|AnaT4|AnaTXFD|AnaTXHD|Ana10FD|Ana10HD);
+		if(bmsr & Bmsr10THD)
+			anar |= Ana10HD;
+		if(bmsr & Bmsr10TFD)
+			anar |= Ana10FD;
+		if(bmsr & Bmsr100TXHD)
+			anar |= AnaTXHD;
+		if(bmsr & Bmsr100TXFD)
+			anar |= AnaTXFD;
+	}
+	mii->curphy->anar = anar;
+
+	if(p != ~0)
+		anar |= (AnaAP|AnaP) & p;
+	else if(mii->curphy->fc != ~0)
+		anar |= mii->curphy->fc;
+	mii->curphy->fc = (AnaAP|AnaP) & anar;
+
+	if(bmsr & BmsrEs){
+		mscr = mii->mir(mii, phyno, Mscr);
+		mscr &= ~(Mscr1000TFD|Mscr1000THD);
+		if(e != ~0)
+			mscr |= (Mscr1000TFD|Mscr1000THD) & e;
+		else if(mii->curphy->mscr != ~0)
+			mscr = mii->curphy->mscr;
+		else{
+			r = mii->mir(mii, phyno, Esr);
+			if(r & Esr1000THD)
+				mscr |= Mscr1000THD;
+			if(r & Esr1000TFD)
+				mscr |= Mscr1000TFD;
+		}
+		mii->curphy->mscr = mscr;
+		mii->miw(mii, phyno, Mscr, mscr);
+	}
+	mii->miw(mii, phyno, Anar, anar);
+
+	r = mii->mir(mii, phyno, Bmcr);
+	if(!(r & BmcrR)){
+		r |= BmcrAne|BmcrRan;
+		mii->miw(mii, phyno, Bmcr, r);
+	}
+
+	return 0;
+}
+
+int
+miistatus(Mii* mii)
+{
+	MiiPhy *phy;
+	int anlpar, bmsr, p, r, phyno;
+
+	if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+		return -1;
+	phy = mii->curphy;
+	phyno = phy->phyno;
+
+	/*
+	 * Check Auto-Negotiation is complete and link is up.
+	 * (Read status twice as the Ls bit is sticky).
+	 */
+	bmsr = mii->mir(mii, phyno, Bmsr);
+	if(!(bmsr & (BmsrAnc|BmsrAna))) {
+		// print("miistatus: auto-neg incomplete\n");
+		return -1;
+	}
+
+	bmsr = mii->mir(mii, phyno, Bmsr);
+	if(!(bmsr & BmsrLs)){
+		// print("miistatus: link down\n");
+		phy->link = 0;
+		return -1;
+	}
+
+	phy->speed = phy->fd = phy->rfc = phy->tfc = 0;
+	if(phy->mscr){
+		r = mii->mir(mii, phyno, Mssr);
+		if((phy->mscr & Mscr1000TFD) && (r & Mssr1000TFD)){
+			phy->speed = 1000;
+			phy->fd = 1;
+		}
+		else if((phy->mscr & Mscr1000THD) && (r & Mssr1000THD))
+			phy->speed = 1000;
+	}
+
+	anlpar = mii->mir(mii, phyno, Anlpar);
+	if(phy->speed == 0){
+		r = phy->anar & anlpar;
+		if(r & AnaTXFD){
+			phy->speed = 100;
+			phy->fd = 1;
+		}
+		else if(r & AnaTXHD)
+			phy->speed = 100;
+		else if(r & Ana10FD){
+			phy->speed = 10;
+			phy->fd = 1;
+		}
+		else if(r & Ana10HD)
+			phy->speed = 10;
+	}
+	if(phy->speed == 0) {
+		// print("miistatus: phy speed 0\n");
+		return -1;
+	}
+
+	if(phy->fd){
+		p = phy->fc;
+		r = anlpar & (AnaAP|AnaP);
+		if(p == AnaAP && r == (AnaAP|AnaP))
+			phy->tfc = 1;
+		else if(p == (AnaAP|AnaP) && r == AnaAP)
+			phy->rfc = 1;
+		else if((p & AnaP) && (r & AnaP))
+			phy->rfc = phy->tfc = 1;
+	}
+
+	phy->link = 1;
+
+	return 0;
+}

+ 116 - 0
sys/src/9/rb/ethermii.h

@@ -0,0 +1,116 @@
+typedef struct Mii Mii;
+typedef struct MiiPhy MiiPhy;
+
+enum {					/* registers */
+	Bmcr		= 0x00,		/* Basic Mode Control */
+	Bmsr		= 0x01,		/* Basic Mode Status */
+	Phyidr1		= 0x02,		/* PHY Identifier #1 */
+	Phyidr2		= 0x03,		/* PHY Identifier #2 */
+	Anar		= 0x04,		/* Auto-Negotiation Advertisement */
+	Anlpar		= 0x05,		/* AN Link Partner Ability */
+	Aner		= 0x06,		/* AN Expansion */
+	Annptr		= 0x07,		/* AN Next Page TX */
+	Annprr		= 0x08,		/* AN Next Page RX */
+	Mscr		= 0x09,		/* MASTER-SLAVE Control */
+	Mssr		= 0x0A,		/* MASTER-SLAVE Status */
+	Esr		= 0x0F,		/* Extended Status */
+
+	NMiiPhyr	= 32,
+	NMiiPhy		= 32,
+};
+
+enum {					/* Bmcr */
+	BmcrSs1		= 0x0040,	/* Speed Select[1] */
+	BmcrCte		= 0x0080,	/* Collision Test Enable */
+	BmcrDm		= 0x0100,	/* Duplex Mode */
+	BmcrRan		= 0x0200,	/* Restart Auto-Negotiation */
+	BmcrI		= 0x0400,	/* Isolate */
+	BmcrPd		= 0x0800,	/* Power Down */
+	BmcrAne		= 0x1000,	/* Auto-Negotiation Enable */
+	BmcrSs0		= 0x2000,	/* Speed Select[0] */
+	BmcrLe		= 0x4000,	/* Loopback Enable */
+	BmcrR		= 0x8000,	/* Reset */
+};
+
+enum {					/* Bmsr */
+	BmsrEc		= 0x0001,	/* Extended Capability */
+	BmsrJd		= 0x0002,	/* Jabber Detect */
+	BmsrLs		= 0x0004,	/* Link Status */
+	BmsrAna		= 0x0008,	/* Auto-Negotiation Ability */
+	BmsrRf		= 0x0010,	/* Remote Fault */
+	BmsrAnc		= 0x0020,	/* Auto-Negotiation Complete */
+	BmsrPs		= 0x0040,	/* Preamble Suppression Capable */
+	BmsrEs		= 0x0100,	/* Extended Status */
+	Bmsr100T2HD	= 0x0200,	/* 100BASE-T2 HD Capable */
+	Bmsr100T2FD	= 0x0400,	/* 100BASE-T2 FD Capable */
+	Bmsr10THD	= 0x0800,	/* 10BASE-T HD Capable */
+	Bmsr10TFD	= 0x1000,	/* 10BASE-T FD Capable */
+	Bmsr100TXHD	= 0x2000,	/* 100BASE-TX HD Capable */
+	Bmsr100TXFD	= 0x4000,	/* 100BASE-TX FD Capable */
+	Bmsr100T4	= 0x8000,	/* 100BASE-T4 Capable */
+};
+
+enum {					/* Anar/Anlpar */
+	Ana10HD		= 0x0020,	/* Advertise 10BASE-T */
+	Ana10FD		= 0x0040,	/* Advertise 10BASE-T FD */
+	AnaTXHD		= 0x0080,	/* Advertise 100BASE-TX */
+	AnaTXFD		= 0x0100,	/* Advertise 100BASE-TX FD */
+	AnaT4		= 0x0200,	/* Advertise 100BASE-T4 */
+	AnaP		= 0x0400,	/* Pause */
+	AnaAP		= 0x0800,	/* Asymmetrical Pause */
+	AnaRf		= 0x2000,	/* Remote Fault */
+	AnaAck		= 0x4000,	/* Acknowledge */
+	AnaNp		= 0x8000,	/* Next Page Indication */
+};
+
+enum {					/* Mscr */
+	Mscr1000THD	= 0x0100,	/* Advertise 1000BASE-T HD */
+	Mscr1000TFD	= 0x0200,	/* Advertise 1000BASE-T FD */
+};
+
+enum {					/* Mssr */
+	Mssr1000THD	= 0x0400,	/* Link Partner 1000BASE-T HD able */
+	Mssr1000TFD	= 0x0800,	/* Link Partner 1000BASE-T FD able */
+};
+
+enum {					/* Esr */
+	Esr1000THD	= 0x1000,	/* 1000BASE-T HD Capable */
+	Esr1000TFD	= 0x2000,	/* 1000BASE-T FD Capable */
+	Esr1000XHD	= 0x4000,	/* 1000BASE-X HD Capable */
+	Esr1000XFD	= 0x8000,	/* 1000BASE-X FD Capable */
+};
+
+typedef struct Mii {
+	Lock;
+	int	nphy;
+	int	mask;
+	MiiPhy*	phy[NMiiPhy];
+	MiiPhy*	curphy;
+
+	void*	ctlr;
+	int	(*mir)(Mii*, int, int);
+	int	(*miw)(Mii*, int, int, int);
+} Mii;
+
+typedef struct MiiPhy {
+	Mii*	mii;
+	int	oui;
+	int	phyno;
+
+	int	anar;
+	int	fc;
+	int	mscr;
+
+	int	link;
+	int	speed;
+	int	fd;
+	int	rfc;
+	int	tfc;
+};
+
+extern int mii(Mii*, int);
+extern int miiane(Mii*, int, int, int);
+extern int miimir(Mii*, int);
+extern int miimiw(Mii*, int, int);
+extern int miireset(Mii*);
+extern int miistatus(Mii*);

+ 238 - 0
sys/src/9/rb/faultmips.c

@@ -0,0 +1,238 @@
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"ureg.h"
+#include	"../port/error.h"
+#include	"io.h"
+
+enum {
+	Debug = 0,
+};
+
+typedef struct Fault Fault;
+struct Fault {
+	uintptr	va;
+	ulong	pid;
+	uintptr	pc;
+	int	cnt;
+	char	*prog;
+	int	code;
+};
+
+extern char *excname[];
+
+static Fault lflt, maxflt;
+
+/*
+ * Ask if the instruction at EPC could have cause this badvaddr
+ */
+int
+tstbadvaddr(Ureg *ur)
+{
+	int rn;
+	ulong iw, off, ea;
+
+	iw = ur->pc;
+	if(ur->cause & BD)
+		iw += 4;
+
+	if(seg(up, iw, 0) == 0)
+		return 0;
+
+	iw = *(ulong*)iw;
+
+/*	print("iw: %#lux\n", iw);	/**/
+
+	switch((iw>>26) & 0x3f) {
+	default:
+		return 1;
+	case 0x20:	/* LB */
+	case 0x24:	/* LBU */
+			/* LD */
+	case 0x35:
+	case 0x36:
+	case 0x37:	/* LDCz */
+	case 0x1A:	/* LDL */
+	case 0x1B:	/* LDR */
+	case 0x21:	/* LH */
+	case 0x25:	/* LHU */
+	case 0x30:	/* LL */
+	case 0x34:	/* LLD */
+	case 0x23:	/* LW */
+	case 0x31:
+	case 0x32:	/* LWCz possible 0x33 */
+	case 0x27:	/* LWU */
+	case 0x22:	/* LWL */
+	case 0x26:	/* LWR */
+		break;
+
+	case 0x28:	/* SB */
+	case 0x38:	/* SC */
+	case 0x3C:	/* SCD */
+	case 0x3D:
+	case 0x3E:
+	case 0x3F:	/* SDCz */
+	case 0x2C:	/* SDL */
+	case 0x2D:	/* SDR */
+	case 0x29:	/* SH */
+	case 0x2B:	/* SW */
+	case 0x39:
+	case 0x3A:	/* SWCz */
+	case 0x2A:	/* SWL */
+	case 0x2E:	/* SWR */
+		break;
+	}
+
+	off = iw & 0xffff;
+	if(off & 0x8000)
+		off |= ~0xffff;
+
+	rn = (iw>>21) & 0x1f;
+	ea = *reg(ur, rn);
+	if(rn == 0)
+		ea = 0;
+	ea += off;
+
+	/* print("ea %#lux %#lux(R%d) bv %#lux pc %#lux\n", ea, off, rn, ur->badvaddr, ur->pc); /**/
+
+	if(ur->badvaddr == ea)
+		return 0;
+
+	return 1;
+}
+
+/*
+ * we think we get consecutive page faults from unlucky combinations of
+ * scheduling and stlb hashes, and they only happen with 16K pages.
+ * however, we also get page faults while servicing the exact same fault.
+ * more than 5 consecutive faults is unusual, now that we have a better
+ * hash function.
+ *
+ * this can be helpful during mmu and cache debugging.
+ */
+static int
+ckfaultstuck(Ureg *ur, int read, int code)
+{
+	uintptr pc, va;
+
+	va = ur->badvaddr;
+	pc = ur->pc;
+	if (va != lflt.va || up->pid != lflt.pid || pc != lflt.pc ||
+	    code != lflt.code) {
+		/* at least one address or cause is different from last time */
+		lflt.cnt = 1;
+		lflt.va = va;
+		lflt.pid = up->pid;
+		lflt.pc = pc;
+		lflt.code = code;
+		return 0;
+	}
+	++lflt.cnt;
+	if (lflt.cnt >= 1000)	/* fixfault() isn't fixing underlying cause? */
+		panic("fault: %d consecutive faults for va %#p", lflt.cnt, va);
+	if (lflt.cnt > maxflt.cnt) {
+		maxflt.cnt = lflt.cnt;
+		maxflt.va = va;
+		maxflt.pid = up->pid;
+		maxflt.pc = pc;
+		kstrdup(&maxflt.prog, up->text);
+	}
+
+	/* we're servicing that fault now! */
+	/* adjust the threshold and program name to suit */
+	if (lflt.cnt < 5 || strncmp(up->text, "8l", 2) != 0)
+		return 0;
+	iprint("%d consecutive faults for va %#p at pc %#p in %s "
+		"pid %ld\n", lflt.cnt, lflt.va, pc, up->text, lflt.pid);
+	iprint("\t%s: %s%s r31 %#lux tlbvirt %#lux\n",
+		excname[code], va == pc? "[instruction] ": "",
+		(read? "read": "write"), ur->r31, tlbvirt());
+	return 0;
+}
+
+char *
+faultsprint(char *p, char *ep)
+{
+	if (Debug)
+		p = seprint(p, ep,
+			"max consecutive faults %d for va %#p in %s\n",
+			maxflt.cnt, maxflt.va, maxflt.prog);
+	return p;
+}
+
+/*
+ *  find out fault address and type of access.
+ *  Call common fault handler.
+ */
+void
+faultmips(Ureg *ur, int user, int code)
+{
+	int read;
+	ulong addr;
+	char *p, buf[ERRMAX];
+	static int infault, printed;
+
+	if (0 && infault && !printed) {
+		printed = 1;
+		print("fault: recursive fault (%d deep) pc %#p va %#p\n",
+			infault+1, ur->pc, ur->badvaddr);
+	}
+	infault++;
+	if(waserror()){
+		infault--;
+		nexterror();
+	}
+
+	addr = ur->badvaddr;
+	addr &= ~(BY2PG-1);
+
+	read = !(code==CTLBM || code==CTLBS);
+
+/*	print("fault: %s code %d va %#p pc %#p r31 %#lux tlbvirt %#lux\n",
+		up->text, code, ur->badvaddr, ur->pc, ur->r31, tlbvirt());/**/
+
+	if (Debug && ckfaultstuck(ur, read, code) || fault(addr, read) == 0){
+		infault--;
+		poperror();
+		return;
+	}
+
+	infault--;
+	poperror();
+
+	if(tstbadvaddr(ur)) {
+		print("fault: spurious badvaddr %#lux in %s at pc %#lux\n",
+			ur->badvaddr, up->text, ur->pc);/**/
+		return;
+	}
+
+	if(user) {
+		p = "store";
+		if(read)
+			p = "load";
+		snprint(buf, sizeof buf, "sys: trap: fault %s addr=%#lux r31=%#lux",
+			p, ur->badvaddr, ur->r31);
+		postnote(up, 1, buf, NDebug);
+		return;
+	}
+
+	print("kernel %s vaddr=%#lux\n", excname[code], ur->badvaddr);
+	print("st=%#lux pc=%#lux r31=%#lux sp=%#lux\n",
+		ur->status, ur->pc, ur->r31, ur->sp);
+	dumpregs(ur);
+	panic("fault");
+}
+
+/*
+ * called in sysfile.c
+ */
+void
+evenaddr(ulong addr)
+{
+	if(addr & 3){
+		postnote(up, 1, "sys: odd address", NDebug);
+		error(Ebadarg);
+	}
+}

+ 146 - 0
sys/src/9/rb/fns.h

@@ -0,0 +1,146 @@
+#include "../port/portfns.h"
+
+void	arginit(void);
+int	busprobe(ulong);
+ulong	cankaddr(ulong);
+void	cleancache(void);
+void	clearmmucache(void);
+void	clock(Ureg*);
+void	clockinit(void);
+void	clockshutdown(void);
+int	cmpswap(long*, long, long);
+void	coherence(void);
+void	cycles(uvlong *);
+void	dcflush(void*, ulong);
+void	evenaddr(ulong);
+void	faultmips(Ureg*, int, int);
+ulong	fcr31(void);
+void	firmware(int);
+void	fpclear(void);
+void	fpsave(FPsave *);
+void	fptrap(Ureg*);
+int	fpuemu(Ureg *);
+void	fpwatch(Ureg *);
+ulong	getcause(void);
+char*	getconf(char*);
+ulong	getconfig(void);
+ulong	getconfig1(void);
+ulong	getconfig2(void);
+ulong	getconfig3(void);
+ulong	getconfig7(void);
+ulong	gethwreg3(void);
+ulong	getpagemask(void);
+ulong	getstatus(void);
+int	gettlbp(ulong, ulong*);
+ulong	gettlbvirt(int);
+void	gotopc(ulong);
+void	hinv(void);
+int	i8250console(void);
+void	icflush(void *, ulong);
+void	idle(void);
+void	idlehands(void);
+int	inb(int);
+void	insb(int, void*, int);
+ushort	ins(int);
+void	inss(int, void*, int);
+ulong	inl(int);
+void	insl(int, void*, int);
+void	ioinit(void);
+void	introff(int);
+void	intron(int);
+void	intrshutdown(void);
+void	kfault(Ureg*);
+KMap*	kmap(Page*);
+void	kmapinit(void);
+void	kmapinval(void);
+void	kunmap(KMap*);
+void	launchinit(void);
+void	launch(int);
+void	links(void);
+ulong	machstatus(void);
+void	newstart(void);
+int	newtlbpid(Proc*);
+void	online(void);
+void	outb(int, int);
+void	outsb(int, void*, int);
+void	outs(int, ushort);
+void	outss(int, void*, int);
+void	outl(int, ulong);
+void	outsl(int, void*, int);
+ulong	pcibarsize(Pcidev*, int);
+void	pcibussize(Pcidev*, ulong*, ulong*);
+int	pcicfgr8(Pcidev*, int);
+int	pcicfgr16(Pcidev*, int);
+int	pcicfgr32(Pcidev*, int);
+void	pcicfgw8(Pcidev*, int, int);
+void	pcicfgw16(Pcidev*, int, int);
+void	pcicfgw32(Pcidev*, int, int);
+void	pciclrbme(Pcidev*);
+void	pciclrioe(Pcidev*);
+void	pciclrmwi(Pcidev*);
+int	pcigetpms(Pcidev*);
+void	pcihinv(Pcidev*);
+uchar	pciipin(Pcidev*, uchar);
+Pcidev* pcimatch(Pcidev*, int, int);
+Pcidev* pcimatchtbdf(int);
+void	pcireset(void);
+int	pciscan(int, Pcidev**);
+void	pcisetbme(Pcidev*);
+void	pcisetioe(Pcidev*);
+void	pcisetmwi(Pcidev*);
+int	pcisetpms(Pcidev*, int);
+ulong	prid(void);
+void	procrestore(Proc *);
+void	procsave(Proc *);
+#define	procsetup(p)	((p)->fpstate = FPinit)
+void	purgetlb(int);
+Softtlb*	putstlb(ulong, ulong);
+int	puttlb(ulong, ulong, ulong);
+void	puttlbx(int, ulong, ulong, ulong, int);
+ulong	rdcompare(void);
+ulong	rdcount(void);