summaryrefslogtreecommitdiff
path: root/sys/src/9/mtx
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/mtx
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/mtx')
-rwxr-xr-xsys/src/9/mtx/clock.c112
-rwxr-xr-xsys/src/9/mtx/cycintr.c27
-rwxr-xr-xsys/src/9/mtx/dat.h213
-rwxr-xr-xsys/src/9/mtx/devarch.c401
-rwxr-xr-xsys/src/9/mtx/devether.c472
-rwxr-xr-xsys/src/9/mtx/devrtc.c438
-rwxr-xr-xsys/src/9/mtx/ether2114x.c1671
-rwxr-xr-xsys/src/9/mtx/etherif.h35
-rwxr-xr-xsys/src/9/mtx/fns.h110
-rwxr-xr-xsys/src/9/mtx/i8259.c213
-rwxr-xr-xsys/src/9/mtx/inb.s119
-rwxr-xr-xsys/src/9/mtx/initcode25
-rwxr-xr-xsys/src/9/mtx/io.h180
-rwxr-xr-xsys/src/9/mtx/kbd.c435
-rwxr-xr-xsys/src/9/mtx/l.s603
-rwxr-xr-xsys/src/9/mtx/main.c465
-rwxr-xr-xsys/src/9/mtx/mem.h195
-rwxr-xr-xsys/src/9/mtx/mkfile96
-rwxr-xr-xsys/src/9/mtx/mmu.c257
-rwxr-xr-xsys/src/9/mtx/mtx46
-rwxr-xr-xsys/src/9/mtx/mtxcpu46
-rwxr-xr-xsys/src/9/mtx/pci.c908
-rwxr-xr-xsys/src/9/mtx/random.c138
-rwxr-xr-xsys/src/9/mtx/raven.c153
-rwxr-xr-xsys/src/9/mtx/trap.c836
-rwxr-xr-xsys/src/9/mtx/uarti8250.c636
26 files changed, 8830 insertions, 0 deletions
diff --git a/sys/src/9/mtx/clock.c b/sys/src/9/mtx/clock.c
new file mode 100755
index 000000000..dacef88ed
--- /dev/null
+++ b/sys/src/9/mtx/clock.c
@@ -0,0 +1,112 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+
+static ulong clkreload;
+
+void
+delayloopinit(void)
+{
+ ulong v;
+ uvlong x;
+
+ m->loopconst = 5000;
+ v = getdec();
+ delay(1000);
+ v -= getdec();
+
+ x = m->loopconst;
+ x *= m->dechz;
+ x /= v;
+ m->loopconst = x;
+}
+
+void
+clockinit(void)
+{
+ /* XXX this should not be hard coded! */
+ m->cpuhz = 300000000;
+ m->bushz = 66666666;
+
+ m->dechz = m->bushz/4; /* true for all 604e */
+ m->tbhz = m->dechz; /* conjecture; manual doesn't say */
+
+ delayloopinit();
+
+ clkreload = m->dechz/HZ-1;
+ putdec(clkreload);
+}
+
+void
+clockintr(Ureg *ureg)
+{
+ long v;
+
+ v = -getdec();
+ if(v > clkreload/2){
+ if(v > clkreload)
+ m->ticks += v/clkreload;
+ v = 0;
+ }
+ putdec(clkreload-v);
+
+ timerintr(ureg, 0);
+}
+
+void
+timerset(Tval)
+{
+}
+
+void
+delay(int l)
+{
+ ulong i, j;
+
+ j = m->loopconst;
+ while(l-- > 0)
+ for(i=0; i < j; i++)
+ ;
+}
+
+void
+microdelay(int l)
+{
+ ulong i;
+
+ l *= m->loopconst;
+ l += 500;
+ l /= 1000;
+ if(l <= 0)
+ l = 1;
+ for(i = 0; i < l; i++)
+ ;
+}
+
+uvlong
+fastticks(uvlong *hz)
+{
+ if(hz)
+ *hz = HZ;
+ return m->ticks;
+}
+
+ulong
+µs(void)
+{
+ return fastticks2us(m->ticks);
+}
+
+/*
+ * performance measurement ticks. must be low overhead.
+ * doesn't have to count over a second.
+ */
+ulong
+perfticks(void)
+{
+ return m->ticks;
+}
diff --git a/sys/src/9/mtx/cycintr.c b/sys/src/9/mtx/cycintr.c
new file mode 100755
index 000000000..d56edba80
--- /dev/null
+++ b/sys/src/9/mtx/cycintr.c
@@ -0,0 +1,27 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+int
+havetimer(void)
+{
+ return 0;
+}
+
+void
+timeradd(Timer *)
+{
+}
+
+void
+timerdel(Timer *)
+{
+}
+
+void
+clockintrsched(void)
+{
+}
diff --git a/sys/src/9/mtx/dat.h b/sys/src/9/mtx/dat.h
new file mode 100755
index 000000000..294bca8c5
--- /dev/null
+++ b/sys/src/9/mtx/dat.h
@@ -0,0 +1,213 @@
+typedef struct Conf Conf;
+typedef struct Confmem Confmem;
+typedef struct FPsave FPsave;
+typedef struct ISAConf ISAConf;
+typedef struct Label Label;
+typedef struct Lock Lock;
+typedef struct Mach Mach;
+typedef struct Notsave Notsave;
+typedef struct Page Page;
+typedef struct PCArch PCArch;
+typedef struct Pcidev Pcidev;
+typedef struct PMMU PMMU;
+typedef struct Proc Proc;
+typedef struct Sys Sys;
+typedef struct Ureg Ureg;
+typedef struct Vctl Vctl;
+typedef long Tval;
+
+#pragma incomplete Ureg
+
+#define MAXSYSARG 5 /* for mount(fd, mpt, flag, arg, srv) */
+
+/*
+ * parameters for sysproc.c
+ */
+#define AOUT_MAGIC Q_MAGIC
+
+/*
+ * machine dependent definitions used by ../port/dat.h
+ */
+
+struct Lock
+{
+ ulong key;
+ ulong sr;
+ ulong pc;
+ Proc *p;
+ Mach *m;
+ ushort isilock;
+};
+
+struct Label
+{
+ ulong sp;
+ ulong pc;
+};
+
+/*
+ * Proc.fpstate
+ */
+enum
+{
+ FPinit,
+ FPactive,
+ FPinactive,
+};
+
+/*
+ * This structure must agree with fpsave and fprestore asm routines
+ */
+struct FPsave
+{
+ double fpreg[32];
+ union {
+ double fpscrd;
+ struct {
+ ulong pad;
+ ulong fpscr;
+ };
+ };
+};
+
+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 */
+ int monitor; /* has display? */
+ ulong ialloc; /* bytes available for interrupt time allocation */
+ ulong pipeqsize; /* size in bytes of pipe queues */
+};
+
+/*
+ * mmu goo in the Proc structure
+ */
+#define NCOLOR 1
+struct PMMU
+{
+ int mmupid;
+};
+
+/*
+ * things saved in the Proc structure during a notify
+ */
+struct Notsave
+{
+ ulong UNUSED;
+};
+
+#include "../port/portdat.h"
+
+/*
+ * machine dependent definitions not used by ../port/dat.h
+ */
+/*
+ * Fake kmap
+ */
+typedef void KMap;
+#define VA(k) ((ulong)(k))
+#define kmap(p) (KMap*)((p)->pa|KZERO)
+#define kunmap(k)
+
+struct Mach
+{
+ /* OFFSETS OF THE FOLLOWING KNOWN BY l.s */
+ int machno; /* physical id of processor */
+ ulong splpc; /* pc that called splhi() */
+ Proc *proc; /* current process on this processor */
+
+ /* ordering from here on irrelevant */
+
+ ulong ticks; /* of the clock since boot time */
+ Label sched; /* scheduler wakeup */
+ Lock alarmlock; /* access to alarm list */
+ void *alarm; /* alarms bound to this clock */
+ int inclockintr;
+ int cputype;
+ ulong loopconst;
+
+ Proc* readied; /* for runproc */
+ ulong schedticks; /* next forced context switch */
+
+ vlong cpuhz;
+ ulong bushz;
+ ulong dechz;
+ ulong tbhz;
+ uvlong cyclefreq; /* Frequency of user readable cycle counter */
+
+ ulong pcclast;
+ uvlong fastclock;
+ Perf perf; /* performance counters */
+
+ int tlbfault; /* only used by devproc; no access to tlb */
+ int tlbpurge; /* ... */
+ int pfault;
+ int cs;
+ int syscall;
+ int load;
+ int intr;
+ int flushmmu; /* make current proc flush it's mmu state */
+ int ilockdepth;
+
+ ulong ptabbase; /* start of page table in kernel virtual space */
+ int slotgen; /* next pte (byte offset) when pteg is full */
+ int mmupid; /* next mmu pid to use */
+ int sweepcolor;
+ int trigcolor;
+ Rendez sweepr;
+
+ ulong spuriousintr;
+ int lastintr;
+
+ /* MUST BE LAST */
+ int stack[1];
+};
+
+struct
+{
+ Lock;
+ short machs;
+ short exiting;
+ short ispanic;
+}active;
+
+/*
+ * a parsed plan9.ini line
+ */
+#define NISAOPT 8
+
+struct ISAConf {
+ char *type;
+ ulong port;
+ int irq;
+ ulong dma;
+ ulong mem;
+ ulong size;
+ ulong freq;
+
+ int nopt;
+ char *opt[NISAOPT];
+};
+
+#define MACHP(n) ((Mach *)((int)&mach0+n*BY2PG))
+extern Mach mach0;
+
+extern register Mach *m;
+extern register Proc *up;
+
+extern FPsave initfp;
diff --git a/sys/src/9/mtx/devarch.c b/sys/src/9/mtx/devarch.c
new file mode 100755
index 000000000..cd5eea88f
--- /dev/null
+++ b/sys/src/9/mtx/devarch.c
@@ -0,0 +1,401 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+typedef struct IOMap IOMap;
+struct IOMap
+{
+ IOMap *next;
+ char tag[13];
+ ulong start;
+ ulong end;
+};
+
+static struct
+{
+ Lock;
+ IOMap *m;
+ IOMap *free;
+ IOMap maps[32]; // some initial free maps
+
+ QLock ql; // lock for reading map
+} iomap;
+
+enum {
+ Qdir = 0,
+ Qioalloc = 1,
+ Qiob,
+ Qiow,
+ Qiol,
+ Qbase,
+
+ Qmax = 16,
+};
+
+typedef long Rdwrfn(Chan*, void*, long, vlong);
+
+static Rdwrfn *readfn[Qmax];
+static Rdwrfn *writefn[Qmax];
+
+static Dirtab archdir[] = {
+ ".", { Qdir, 0, QTDIR }, 0, 0555,
+ "ioalloc", { Qioalloc, 0 }, 0, 0444,
+ "iob", { Qiob, 0 }, 0, 0660,
+ "iow", { Qiow, 0 }, 0, 0660,
+ "iol", { Qiol, 0 }, 0, 0660,
+};
+Lock archwlock; /* the lock is only for changing archdir */
+int narchdir = Qbase;
+int (*_pcmspecial)(char *, ISAConf *);
+void (*_pcmspecialclose)(int);
+
+/*
+ * 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;
+}
+
+void
+ioinit(void)
+{
+ int i;
+
+ for(i = 0; i < nelem(iomap.maps)-1; i++)
+ iomap.maps[i].next = &iomap.maps[i+1];
+ iomap.maps[i].next = nil;
+ iomap.free = iomap.maps;
+
+ // a dummy entry at 2^17
+ ioalloc(0x20000, 1, 0, "dummy");
+}
+
+//
+// alloc some io port space and remember who it was
+// alloced to. if port < 0, find a free region.
+//
+int
+ioalloc(int port, int size, int align, char *tag)
+{
+ IOMap *m, **l;
+ int i;
+
+ lock(&iomap);
+ if(port < 0){
+ // find a free port above 0x400 and below 0x1000
+ port = 0x400;
+ for(l = &iomap.m; *l; l = &(*l)->next){
+ m = *l;
+ i = m->start - port;
+ if(i > size)
+ break;
+ if(align > 0)
+ port = ((port+align-1)/align)*align;
+ else
+ port = m->end;
+ }
+ if(*l == nil){
+ unlock(&iomap);
+ return -1;
+ }
+ } else {
+ // see if the space clashes with previously allocated ports
+ for(l = &iomap.m; *l; l = &(*l)->next){
+ m = *l;
+ if(m->end <= port)
+ continue;
+ if(m->start >= port+size)
+ break;
+ unlock(&iomap);
+ return -1;
+ }
+ }
+ m = iomap.free;
+ if(m == nil){
+ print("ioalloc: out of maps");
+ unlock(&iomap);
+ return port;
+ }
+ iomap.free = m->next;
+ m->next = *l;
+ m->start = port;
+ m->end = port + size;
+ strncpy(m->tag, tag, sizeof(m->tag));
+ m->tag[sizeof(m->tag)-1] = 0;
+ *l = m;
+
+ archdir[0].qid.vers++;
+
+ unlock(&iomap);
+ return m->start;
+}
+
+void
+iofree(int port)
+{
+ IOMap *m, **l;
+
+ lock(&iomap);
+ for(l = &iomap.m; *l; l = &(*l)->next){
+ if((*l)->start == port){
+ m = *l;
+ *l = m->next;
+ m->next = iomap.free;
+ iomap.free = m;
+ break;
+ }
+ if((*l)->start > port)
+ break;
+ }
+ archdir[0].qid.vers++;
+ unlock(&iomap);
+}
+
+int
+iounused(int start, int end)
+{
+ IOMap *m;
+
+ for(m = iomap.m; m; m = m->next){
+ if(start >= m->start && start < m->end
+ || start <= m->start && end > m->start)
+ return 0;
+ }
+ return 1;
+}
+
+static void
+checkport(int start, int end)
+{
+ /* standard vga regs are OK */
+ if(start >= 0x2b0 && end <= 0x2df+1)
+ return;
+ if(start >= 0x3c0 && end <= 0x3da+1)
+ return;
+
+ if(iounused(start, end))
+ return;
+ error(Eperm);
+}
+
+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, nelem(archdir), devgen);
+}
+
+static void
+archclose(Chan*)
+{
+}
+
+enum
+{
+ Linelen= 31,
+};
+
+static long
+archread(Chan *c, void *a, long n, vlong offset)
+{
+ char buf[Linelen+1], *p;
+ int port;
+ ushort *sp;
+ ulong *lp;
+ IOMap *m;
+ Rdwrfn *fn;
+
+ switch((ulong)c->qid.path){
+
+ case Qdir:
+ return devdirread(c, a, n, archdir, nelem(archdir), devgen);
+
+ case Qiob:
+ port = offset;
+ checkport(offset, offset+n);
+ for(p = a; port < offset+n; port++)
+ *p++ = inb(port);
+ return n;
+
+ case Qiow:
+ if((n & 0x01) || (offset & 0x01))
+ error(Ebadarg);
+ checkport(offset, offset+n+1);
+ n /= 2;
+ sp = a;
+ for(port = offset; port < offset+n; port += 2)
+ *sp++ = ins(port);
+ return n*2;
+
+ case Qiol:
+ if((n & 0x03) || (offset & 0x03))
+ error(Ebadarg);
+ checkport(offset, offset+n+3);
+ n /= 4;
+ lp = a;
+ for(port = offset; port < offset+n; port += 4)
+ *lp++ = inl(port);
+ return n*4;
+
+ case Qioalloc:
+ break;
+
+ default:
+ if(c->qid.path < narchdir && (fn = readfn[c->qid.path]))
+ return fn(c, a, n, offset);
+ error(Eperm);
+ break;
+ }
+
+ offset = offset/Linelen;
+ n = n/Linelen;
+ p = a;
+ lock(&iomap);
+ for(m = iomap.m; n > 0 && m != nil; m = m->next){
+ if(offset-- > 0)
+ continue;
+ if(strcmp(m->tag, "dummy") == 0)
+ break;
+ sprint(buf, "%8lux %8lux %-12.12s\n", m->start, m->end-1, m->tag);
+ memmove(p, buf, Linelen);
+ p += Linelen;
+ n--;
+ }
+ unlock(&iomap);
+
+ return p - (char*)a;
+}
+
+static long
+archwrite(Chan *c, void *a, long n, vlong offset)
+{
+ char *p;
+ int port;
+ ushort *sp;
+ ulong *lp;
+ Rdwrfn *fn;
+
+ switch((ulong)c->qid.path){
+
+ case Qiob:
+ p = a;
+ checkport(offset, offset+n);
+ for(port = offset; port < offset+n; port++)
+ outb(port, *p++);
+ return n;
+
+ case Qiow:
+ if((n & 01) || (offset & 01))
+ error(Ebadarg);
+ checkport(offset, offset+n+1);
+ n /= 2;
+ sp = a;
+ for(port = offset; port < offset+n; port += 2)
+ outs(port, *sp++);
+ return n*2;
+
+ case Qiol:
+ if((n & 0x03) || (offset & 0x03))
+ error(Ebadarg);
+ checkport(offset, offset+n+3);
+ n /= 4;
+ lp = a;
+ for(port = offset; port < offset+n; port += 4)
+ outl(port, *lp++);
+ return n*4;
+
+ default:
+ if(c->qid.path < narchdir && (fn = writefn[c->qid.path]))
+ return fn(c, a, n, offset);
+ error(Eperm);
+ break;
+ }
+ return 0;
+}
+
+Dev archdevtab = {
+ 'P',
+ "arch",
+
+ devreset,
+ devinit,
+ devshutdown,
+ archattach,
+ archwalk,
+ archstat,
+ archopen,
+ devcreate,
+ archclose,
+ archread,
+ devbread,
+ archwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
+
+int
+pcmspecial(char *idstr, ISAConf *isa)
+{
+ return (_pcmspecial != nil)? _pcmspecial(idstr, isa): -1;
+}
+
+void
+pcmspecialclose(int a)
+{
+ if (_pcmspecialclose != nil)
+ _pcmspecialclose(a);
+}
diff --git a/sys/src/9/mtx/devether.c b/sys/src/9/mtx/devether.c
new file mode 100755
index 000000000..73c8bac4e
--- /dev/null
+++ b/sys/src/9/mtx/devether.c
@@ -0,0 +1,472 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+static Ether *etherxx[MaxEther];
+
+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);
+ chan->dev = ctlrno;
+ if(etherxx[ctlrno]->attach)
+ etherxx[ctlrno]->attach(etherxx[ctlrno]);
+ return chan;
+}
+
+static Walkqid*
+etherwalk(Chan* chan, Chan* nchan, char** name, int nname)
+{
+ return netifwalk(etherxx[chan->dev], chan, nchan, name, nname);
+}
+
+static int
+etherstat(Chan* chan, uchar* dp, int n)
+{
+ return netifstat(etherxx[chan->dev], chan, dp, n);
+}
+
+static Chan*
+etheropen(Chan* chan, int omode)
+{
+ return netifopen(etherxx[chan->dev], chan, omode);
+}
+
+static void
+ethercreate(Chan*, char*, int, ulong)
+{
+}
+
+static void
+etherclose(Chan* chan)
+{
+ netifclose(etherxx[chan->dev], chan);
+}
+
+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* chan, long n, ulong offset)
+{
+ return netifbread(etherxx[chan->dev], chan, n, offset);
+}
+
+static int
+etherwstat(Chan* chan, uchar* dp, int n)
+{
+ return netifwstat(etherxx[chan->dev], chan, dp, 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;
+
+ ether->inpackets++;
+
+ 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)) && ether->prom == 0){
+ if(!activemulti(ether, pkt->d, sizeof(pkt->d))){
+ if(fromwire){
+ 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)
+ if(f->type == type || f->type < 0)
+ if(tome || multi || f->prom){
+ /* Don't want to hear bridged packets */
+ if(f->bridge && !fromwire && !fromme)
+ continue;
+ if(!f->headersonly){
+ if(fromwire && fx == 0)
+ fx = f;
+ else if(xbp = iallocb(len)){
+ memmove(xbp->wp, pkt, len);
+ xbp->wp += len;
+ qpass(f->in, xbp);
+ }
+ else
+ ether->soverflows++;
+ }
+ else
+ etherrtrace(f, pkt, len);
+ }
+ }
+
+ if(fx){
+ if(qpass(fx->in, bp) < 0)
+ ether->soverflows++;
+ return 0;
+ }
+ if(fromwire){
+ freeb(bp);
+ return 0;
+ }
+
+ return bp;
+}
+
+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){
+ qbwrite(ether->oq, bp);
+ ether->transmit(ether);
+ } else
+ freeb(bp);
+
+ return len;
+}
+
+static long
+etherwrite(Chan* chan, void* buf, long n, vlong)
+{
+ Ether *ether;
+ Block *bp;
+ int nn;
+
+ ether = etherxx[chan->dev];
+ if(NETTYPE(chan->qid.path) != Ndataqid) {
+ nn = netifwrite(ether, chan, buf, n);
+ if(nn >= 0)
+ return nn;
+
+ if(n == sizeof("nonblocking")-1 && strncmp((char*)buf, "nonblocking", n) == 0){
+ qnoblock(ether->oq, 1);
+ return n;
+ }
+
+ if(ether->ctl!=nil)
+ return ether->ctl(ether,buf,n);
+
+ error(Ebadctl);
+ }
+
+ if(n > ether->maxmtu)
+ 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* chan, Block* bp, ulong)
+{
+ Ether *ether;
+ long n;
+
+ n = BLEN(bp);
+ if(NETTYPE(chan->qid.path) != Ndataqid){
+ if(waserror()) {
+ freeb(bp);
+ nexterror();
+ }
+ n = etherwrite(chan, bp->rp, n, 0);
+ poperror();
+ freeb(bp);
+ return n;
+ }
+ ether = etherxx[chan->dev];
+
+ if(n > ether->maxmtu){
+ freeb(bp);
+ error(Etoobig);
+ }
+ if(n < ether->minmtu){
+ freeb(bp);
+ error(Etoosmall);
+ }
+
+ return etheroq(ether, bp);
+}
+
+static struct {
+ char* type;
+ int (*reset)(Ether*);
+} cards[MaxEther+1];
+
+void
+addethercard(char* t, int (*r)(Ether*))
+{
+ static int ncard;
+
+ if(ncard == MaxEther)
+ panic("too many ether cards");
+ cards[ncard].type = t;
+ cards[ncard].reset = r;
+ ncard++;
+}
+
+int
+parseether(uchar *to, char *from)
+{
+ char nip[4];
+ char *p;
+ int i;
+
+ p = from;
+ for(i = 0; i < 6; i++){
+ if(*p == 0)
+ return -1;
+ nip[0] = *p++;
+ if(*p == 0)
+ return -1;
+ nip[1] = *p++;
+ nip[2] = 0;
+ to[i] = strtoul(nip, 0, 16);
+ if(*p == ':')
+ p++;
+ }
+ return 0;
+}
+
+static void
+etherreset(void)
+{
+ Ether *ether;
+ int i, n, ctlrno;
+ char name[32], buf[128];
+
+ for(ether = 0, ctlrno = 0; ctlrno < MaxEther; ctlrno++){
+ if(ether == 0)
+ ether = malloc(sizeof(Ether));
+ memset(ether, 0, sizeof(Ether));
+ ether->ctlrno = ctlrno;
+ ether->tbdf = BUSUNKNOWN;
+ ether->mbps = 10;
+ ether->minmtu = ETHERMINTU;
+ ether->maxmtu = ETHERMAXTU;
+ if(isaconfig("ether", ctlrno, ether) == 0)
+ continue;
+ for(n = 0; cards[n].type; n++){
+ if(cistrcmp(cards[n].type, ether->type))
+ continue;
+ for(i = 0; i < ether->nopt; i++){
+ if(strncmp(ether->opt[i], "ea=", 3))
+ continue;
+ if(parseether(ether->ea, &ether->opt[i][3]) == -1)
+ memset(ether->ea, 0, Eaddrlen);
+ }
+ if(cards[n].reset(ether))
+ break;
+
+ /*
+ * IRQ2 doesn't really exist, it's used to gang the interrupt
+ * controllers together. A device set to IRQ2 will appear on
+ * the second interrupt controller as IRQ9.
+ */
+ if(ether->irq == 2 && BUSTYPE(ether->tbdf) != BusPCI)
+ ether->irq = 9;
+ snprint(name, sizeof(name), "ether%d", ctlrno);
+
+ /*
+ * If ether->irq is <0, it is a hack to indicate no interrupt
+ * used by ethersink.
+ */
+ if(ether->irq >= 0)
+ intrenable(ether->irq, ether->interrupt, ether, ether->tbdf, name);
+
+ i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %d",
+ ctlrno, ether->type, ether->mbps, ether->port, ether->irq);
+ if(ether->mem)
+ i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem));
+ if(ether->size)
+ i += sprint(buf+i, " size 0x%luX", ether->size);
+ 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);
+
+ if(ether->mbps >= 100){
+ netifinit(ether, name, Ntypes, 256*1024);
+ if(ether->oq == 0)
+ ether->oq = qopen(256*1024, Qmsg, 0, 0);
+ }
+ else{
+ netifinit(ether, name, Ntypes, 65*1024);
+ if(ether->oq == 0)
+ ether->oq = qopen(65*1024, Qmsg, 0, 0);
+ }
+ if(ether->oq == 0)
+ panic("etherreset %s", name);
+ ether->alen = Eaddrlen;
+ memmove(ether->addr, ether->ea, Eaddrlen);
+ memset(ether->bcast, 0xFF, Eaddrlen);
+
+ etherxx[ctlrno] = ether;
+ ether = 0;
+ break;
+ }
+ }
+ if(ether)
+ free(ether);
+}
+
+#define POLY 0xedb88320
+
+/* really slow 32 bit crc for ethers */
+ulong
+ethercrc(uchar *p, int len)
+{
+ int i, j;
+ ulong crc, b;
+
+ crc = 0xffffffff;
+ for(i = 0; i < len; i++){
+ b = *p++;
+ for(j = 0; j < 8; j++){
+ crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0);
+ b >>= 1;
+ }
+ }
+ return crc;
+}
+
+Dev etherdevtab = {
+ 'l',
+ "ether",
+
+ etherreset,
+ devinit,
+ devshutdown,
+ etherattach,
+ etherwalk,
+ etherstat,
+ etheropen,
+ ethercreate,
+ etherclose,
+ etherread,
+ etherbread,
+ etherwrite,
+ etherbwrite,
+ devremove,
+ etherwstat,
+};
diff --git a/sys/src/9/mtx/devrtc.c b/sys/src/9/mtx/devrtc.c
new file mode 100755
index 000000000..5b6a2399c
--- /dev/null
+++ b/sys/src/9/mtx/devrtc.c
@@ -0,0 +1,438 @@
+/*
+ * M48T59/559 Timekeeper
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include "io.h"
+
+enum{
+ STB0 = 0x74,
+ STB1 = 0x75,
+ Data = 0x77,
+
+ NVOFF= 0,
+ NVLEN= 0x1ff0, /* length in bytes of NV RAM */
+
+ /*
+ * register offsets into time of day clock
+ */
+ NVflags= 0x1ff0,
+ NVwatchdog= 0x1ff7,
+ NVctl= 0x1ff8,
+ NVsec,
+ NVmin,
+ NVhour,
+ NVday, /* (1 = Sun) */
+ NVmday, /* (1-31) */
+ NVmon, /* (1-12) */
+ NVyear, /* (0-99) */
+
+ /* NVctl */
+ RTwrite = (1<<7),
+ RTread = (1<<6),
+ RTsign = (1<<5),
+ RTcal = 0x1f,
+
+ /* NVwatchdog */
+ WDsteer = (1<<7), /* 0 -> intr, 1 -> reset */
+ WDmult = (1<<2), /* 5 bits of multiplier */
+ WDres0 = (0<<0), /* 1/16 sec resolution */
+ WDres1 = (1<<0), /* 1/4 sec resolution */
+ WDres2 = (2<<0), /* 1 sec resolution */
+ WDres3 = (3<<0), /* 4 sec resolution */
+
+ Qdir = 0,
+ Qrtc,
+ Qnvram,
+};
+
+/*
+ * broken down time
+ */
+typedef struct
+{
+ int sec;
+ int min;
+ int hour;
+ int mday;
+ int mon;
+ int year;
+} Rtc;
+
+QLock rtclock; /* mutex on nvram operations */
+
+static Dirtab rtcdir[]={
+ ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
+ "rtc", {Qrtc, 0}, 0, 0644,
+ "nvram", {Qnvram, 0}, 0, 0600,
+};
+
+static ulong rtc2sec(Rtc*);
+static void sec2rtc(ulong, Rtc*);
+static void setrtc(Rtc*);
+static void nvcksum(void);
+static void nvput(int, uchar);
+static uchar nvget(int);
+
+static Chan*
+rtcattach(char *spec)
+{
+ return devattach('r', spec);
+}
+
+static Walkqid*
+rtcwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, rtcdir, nelem(rtcdir), devgen);
+}
+
+static int
+rtcstat(Chan *c, uchar *dp, int n)
+{
+ return devstat(c, dp, n, rtcdir, nelem(rtcdir), devgen);
+}
+
+static Chan*
+rtcopen(Chan *c, int omode)
+{
+ omode = openmode(omode);
+ switch((ulong)c->qid.path){
+ case Qrtc:
+ if(strcmp(up->user, eve)!=0 && omode!=OREAD)
+ error(Eperm);
+ break;
+ case Qnvram:
+ if(strcmp(up->user, eve)!=0 || !cpuserver)
+ error(Eperm);
+ }
+ return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
+}
+
+static void
+rtcclose(Chan*)
+{
+}
+
+static long
+rtcread(Chan *c, void *buf, long n, vlong off)
+{
+ char *p;
+ ulong t;
+ int i;
+ ulong offset = off;
+
+ if(c->qid.type & QTDIR)
+ return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen);
+
+ switch((ulong)c->qid.path){
+ case Qrtc:
+ qlock(&rtclock);
+ t = rtctime();
+ qunlock(&rtclock);
+ n = readnum(offset, buf, n, t, 12);
+ return n;
+ case Qnvram:
+ offset += NVOFF;
+ if(offset > NVLEN)
+ return 0;
+ if(n > NVLEN - offset)
+ n = NVLEN - offset;
+ p = buf;
+ qlock(&rtclock);
+ for(i = 0; i < n; i++)
+ p[i] = nvget(i+offset);
+ qunlock(&rtclock);
+ return n;
+ }
+ error(Egreg);
+ return -1; /* never reached */
+}
+
+static long
+rtcwrite(Chan *c, void *buf, long n, vlong off)
+{
+ Rtc rtc;
+ ulong secs;
+ char *cp, *ep;
+ int i;
+ ulong offset = off;
+
+ switch((ulong)c->qid.path){
+ case Qrtc:
+ if(offset!=0)
+ error(Ebadarg);
+ /*
+ * read the time
+ */
+ cp = ep = buf;
+ ep += n;
+ while(cp < ep){
+ if(*cp>='0' && *cp<='9')
+ break;
+ cp++;
+ }
+ secs = strtoul(cp, 0, 0);
+ /*
+ * convert to bcd
+ */
+ sec2rtc(secs, &rtc);
+ /*
+ * write it
+ */
+ qlock(&rtclock);
+ setrtc(&rtc);
+ qunlock(&rtclock);
+ return n;
+ case Qnvram:
+ offset += NVOFF;
+ if(offset > NVLEN)
+ return 0;
+ if(n > NVLEN - offset)
+ n = NVLEN - offset;
+ qlock(&rtclock);
+ for(i = 0; i < n; i++)
+ nvput(i+offset, ((uchar*)buf)[i]);
+ nvcksum();
+ qunlock(&rtclock);
+ return n;
+ }
+ error(Egreg);
+ return -1; /* never reached */
+}
+
+long
+rtcbwrite(Chan *c, Block *bp, ulong offset)
+{
+ return devbwrite(c, bp, offset);
+}
+
+Dev rtcdevtab = {
+ 'r',
+ "rtc",
+
+ devreset,
+ devinit,
+ devshutdown,
+ rtcattach,
+ rtcwalk,
+ rtcstat,
+ rtcopen,
+ devcreate,
+ rtcclose,
+ rtcread,
+ devbread,
+ rtcwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
+
+static void
+nvput(int offset, uchar val)
+{
+ outb(STB0, offset);
+ outb(STB1, offset>>8);
+ outb(Data, val);
+}
+
+static uchar
+nvget(int offset)
+{
+ outb(STB0, offset);
+ outb(STB1, offset>>8);
+ return inb(Data);
+}
+
+static void
+nvcksum(void)
+{
+}
+
+void
+watchreset(void)
+{
+ splhi();
+ nvput(NVwatchdog, WDsteer|(1*WDmult)|WDres0);
+ for(;;);
+}
+
+static int
+getbcd(int bcd)
+{
+ return (bcd&0x0f) + 10 * (bcd>>4);
+}
+
+static int
+putbcd(int val)
+{
+ return (val % 10) | (((val/10) % 10) << 4);
+}
+
+long
+rtctime(void)
+{
+ int ctl;
+ Rtc rtc;
+
+ /*
+ * convert from BCD
+ */
+ ctl = nvget(NVctl);
+ ctl &= RTsign|RTcal;
+ nvput(NVctl, ctl|RTread);
+
+ rtc.sec = getbcd(nvget(NVsec) & 0x7f);
+ rtc.min = getbcd(nvget(NVmin));
+ rtc.hour = getbcd(nvget(NVhour));
+ rtc.mday = getbcd(nvget(NVmday));
+ rtc.mon = getbcd(nvget(NVmon));
+ rtc.year = getbcd(nvget(NVyear));
+ if(rtc.year < 70)
+ rtc.year += 2000;
+ else
+ rtc.year += 1900;
+
+ nvput(NVctl, ctl);
+
+ return rtc2sec(&rtc);
+}
+
+static void
+setrtc(Rtc *rtc)
+{
+ int ctl;
+
+ ctl = nvget(NVctl);
+ ctl &= RTsign|RTcal;
+ nvput(NVctl, ctl|RTwrite);
+
+ nvput(NVsec, putbcd(rtc->sec));
+ nvput(NVmin, putbcd(rtc->min));
+ nvput(NVhour, putbcd(rtc->hour));
+ nvput(NVmday, putbcd(rtc->mday));
+ nvput(NVmon, putbcd(rtc->mon));
+ nvput(NVyear, putbcd(rtc->year % 100));
+
+ nvput(NVctl, ctl);
+}
+
+#define SEC2MIN 60L
+#define SEC2HOUR (60L*SEC2MIN)
+#define SEC2DAY (24L*SEC2HOUR)
+
+/*
+ * days per month plus days/year
+ */
+static int dmsize[] =
+{
+ 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+static int ldmsize[] =
+{
+ 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+/*
+ * return the days/month for the given year
+ */
+static int *
+yrsize(int y)
+{
+
+ if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0))
+ return ldmsize;
+ else
+ return dmsize;
+}
+
+/*
+ * compute seconds since Jan 1 1970
+ */
+static ulong
+rtc2sec(Rtc *rtc)
+{
+ ulong secs;
+ int i;
+ int *d2m;
+
+ secs = 0;
+
+ /*
+ * seconds per year
+ */
+ for(i = 1970; i < rtc->year; i++){
+ d2m = yrsize(i);
+ secs += d2m[0] * SEC2DAY;
+ }
+
+ /*
+ * seconds per month
+ */
+ d2m = yrsize(rtc->year);
+ for(i = 1; i < rtc->mon; i++)
+ secs += d2m[i] * SEC2DAY;
+
+ secs += (rtc->mday-1) * SEC2DAY;
+ secs += rtc->hour * SEC2HOUR;
+ secs += rtc->min * SEC2MIN;
+ secs += rtc->sec;
+
+ return secs;
+}
+
+/*
+ * compute rtc from seconds since Jan 1 1970
+ */
+static void
+sec2rtc(ulong secs, Rtc *rtc)
+{
+ int d;
+ long hms, day;
+ int *d2m;
+
+ /*
+ * break initial number into days
+ */
+ hms = secs % SEC2DAY;
+ day = secs / SEC2DAY;
+ if(hms < 0) {
+ hms += SEC2DAY;
+ day -= 1;
+ }
+
+ /*
+ * generate hours:minutes:seconds
+ */
+ rtc->sec = hms % 60;
+ d = hms / 60;
+ rtc->min = d % 60;
+ d /= 60;
+ rtc->hour = d;
+
+ /*
+ * year number
+ */
+ if(day >= 0)
+ for(d = 1970; day >= *yrsize(d); d++)
+ day -= *yrsize(d);
+ else
+ for (d = 1970; day < 0; d--)
+ day += *yrsize(d-1);
+ rtc->year = d;
+
+ /*
+ * generate month
+ */
+ d2m = yrsize(rtc->year);
+ for(d = 1; day >= d2m[d]; d++)
+ day -= d2m[d];
+ rtc->mday = day + 1;
+ rtc->mon = d;
+
+ return;
+}
diff --git a/sys/src/9/mtx/ether2114x.c b/sys/src/9/mtx/ether2114x.c
new file mode 100755
index 000000000..b5dbfff06
--- /dev/null
+++ b/sys/src/9/mtx/ether2114x.c
@@ -0,0 +1,1671 @@
+/*
+ * Digital Semiconductor DECchip 2114x PCI Fast Ethernet LAN Controller.
+ * To do:
+ * thresholds;
+ * ring sizing;
+ * handle more error conditions;
+ * tidy setup packet mess;
+ * push initialisation back to attach;
+ * full SROM decoding.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+#define DEBUG (0)
+#define debug if(DEBUG)print
+
+enum {
+ Nrde = 64,
+ Ntde = 64,
+};
+
+#define Rbsz ROUNDUP(sizeof(Etherpkt)+4, 4)
+
+enum { /* CRS0 - Bus Mode */
+ Swr = 0x00000001, /* Software Reset */
+ Bar = 0x00000002, /* Bus Arbitration */
+ Dsl = 0x0000007C, /* Descriptor Skip Length (field) */
+ Ble = 0x00000080, /* Big/Little Endian */
+ Pbl = 0x00003F00, /* Programmable Burst Length (field) */
+ Cal = 0x0000C000, /* Cache Alignment (field) */
+ Cal8 = 0x00004000, /* 8 longword boundary alignment */
+ Cal16 = 0x00008000, /* 16 longword boundary alignment */
+ Cal32 = 0x0000C000, /* 32 longword boundary alignment */
+ Tap = 0x000E0000, /* Transmit Automatic Polling (field) */
+ Dbo = 0x00100000, /* Descriptor Byte Ordering Mode */
+ Rml = 0x00200000, /* Read Multiple */
+};
+
+enum { /* CSR[57] - Status and Interrupt Enable */
+ Ti = 0x00000001, /* Transmit Interrupt */
+ Tps = 0x00000002, /* Transmit Process Stopped */
+ Tu = 0x00000004, /* Transmit buffer Unavailable */
+ Tjt = 0x00000008, /* Transmit Jabber Timeout */
+ Unf = 0x00000020, /* transmit UNderFlow */
+ Ri = 0x00000040, /* Receive Interrupt */
+ Ru = 0x00000080, /* Receive buffer Unavailable */
+ Rps = 0x00000100, /* Receive Process Stopped */
+ Rwt = 0x00000200, /* Receive Watchdog Timeout */
+ Eti = 0x00000400, /* Early Transmit Interrupt */
+ Gte = 0x00000800, /* General purpose Timer Expired */
+ Fbe = 0x00002000, /* Fatal Bus Error */
+ Ais = 0x00008000, /* Abnormal Interrupt Summary */
+ Nis = 0x00010000, /* Normal Interrupt Summary */
+ Rs = 0x000E0000, /* Receive process State (field) */
+ Ts = 0x00700000, /* Transmit process State (field) */
+ Eb = 0x03800000, /* Error bits */
+};
+
+enum { /* CSR6 - Operating Mode */
+ Hp = 0x00000001, /* Hash/Perfect receive filtering mode */
+ Sr = 0x00000002, /* Start/stop Receive */
+ Ho = 0x00000004, /* Hash-Only filtering mode */
+ Pb = 0x00000008, /* Pass Bad frames */
+ If = 0x00000010, /* Inverse Filtering */
+ Sb = 0x00000020, /* Start/stop Backoff counter */
+ Pr = 0x00000040, /* Promiscuous Mode */
+ Pm = 0x00000080, /* Pass all Multicast */
+ Fd = 0x00000200, /* Full Duplex mode */
+ Om = 0x00000C00, /* Operating Mode (field) */
+ Fc = 0x00001000, /* Force Collision */
+ St = 0x00002000, /* Start/stop Transmission Command */
+ Tr = 0x0000C000, /* ThReshold control bits (field) */
+ Tr128 = 0x00000000,
+ Tr256 = 0x00004000,
+ Tr512 = 0x00008000,
+ Tr1024 = 0x0000C000,
+ Ca = 0x00020000, /* CApture effect enable */
+ Ps = 0x00040000, /* Port Select */
+ Hbd = 0x00080000, /* HeartBeat Disable */
+ Imm = 0x00100000, /* IMMediate mode */
+ Sf = 0x00200000, /* Store and Forward */
+ Ttm = 0x00400000, /* Transmit Threshold Mode */
+ Pcs = 0x00800000, /* PCS function */
+ Scr = 0x01000000, /* SCRambler mode */
+ Mbo = 0x02000000, /* Must Be One */
+ Ra = 0x40000000, /* Receive All */
+ Sc = 0x80000000, /* Special Capture effect enable */
+
+ TrMODE = Tr512, /* default transmission threshold */
+};
+
+enum { /* CSR9 - ROM and MII Management */
+ Scs = 0x00000001, /* serial ROM chip select */
+ Sclk = 0x00000002, /* serial ROM clock */
+ Sdi = 0x00000004, /* serial ROM data in */
+ Sdo = 0x00000008, /* serial ROM data out */
+ Ss = 0x00000800, /* serial ROM select */
+ Wr = 0x00002000, /* write */
+ Rd = 0x00004000, /* read */
+
+ Mdc = 0x00010000, /* MII management clock */
+ Mdo = 0x00020000, /* MII management write data */
+ Mii = 0x00040000, /* MII management operation mode (W) */
+ Mdi = 0x00080000, /* MII management data in */
+};
+
+enum { /* CSR12 - General-Purpose Port */
+ Gpc = 0x00000100, /* General Purpose Control */
+};
+
+typedef struct Des {
+ int status;
+ int control;
+ ulong addr;
+ Block* bp;
+} Des;
+
+enum { /* status */
+ Of = 0x00000001, /* Rx: OverFlow */
+ Ce = 0x00000002, /* Rx: CRC Error */
+ Db = 0x00000004, /* Rx: Dribbling Bit */
+ Re = 0x00000008, /* Rx: Report on MII Error */
+ Rw = 0x00000010, /* Rx: Receive Watchdog */
+ Ft = 0x00000020, /* Rx: Frame Type */
+ Cs = 0x00000040, /* Rx: Collision Seen */
+ Tl = 0x00000080, /* Rx: Frame too Long */
+ Ls = 0x00000100, /* Rx: Last deScriptor */
+ Fs = 0x00000200, /* Rx: First deScriptor */
+ Mf = 0x00000400, /* Rx: Multicast Frame */
+ Rf = 0x00000800, /* Rx: Runt Frame */
+ Dt = 0x00003000, /* Rx: Data Type (field) */
+ De = 0x00004000, /* Rx: Descriptor Error */
+ Fl = 0x3FFF0000, /* Rx: Frame Length (field) */
+ Ff = 0x40000000, /* Rx: Filtering Fail */
+
+ Def = 0x00000001, /* Tx: DEFerred */
+ Uf = 0x00000002, /* Tx: UnderFlow error */
+ Lf = 0x00000004, /* Tx: Link Fail report */
+ Cc = 0x00000078, /* Tx: Collision Count (field) */
+ Hf = 0x00000080, /* Tx: Heartbeat Fail */
+ Ec = 0x00000100, /* Tx: Excessive Collisions */
+ Lc = 0x00000200, /* Tx: Late Collision */
+ Nc = 0x00000400, /* Tx: No Carrier */
+ Lo = 0x00000800, /* Tx: LOss of carrier */
+ To = 0x00004000, /* Tx: Transmission jabber timeOut */
+
+ Es = 0x00008000, /* [RT]x: Error Summary */
+ Own = 0x80000000, /* [RT]x: OWN bit */
+};
+
+enum { /* control */
+ Bs1 = 0x000007FF, /* [RT]x: Buffer 1 Size */
+ Bs2 = 0x003FF800, /* [RT]x: Buffer 2 Size */
+
+ Ch = 0x01000000, /* [RT]x: second address CHained */
+ Er = 0x02000000, /* [RT]x: End of Ring */
+
+ Ft0 = 0x00400000, /* Tx: Filtering Type 0 */
+ Dpd = 0x00800000, /* Tx: Disabled PaDding */
+ Ac = 0x04000000, /* Tx: Add CRC disable */
+ Set = 0x08000000, /* Tx: SETup packet */
+ Ft1 = 0x10000000, /* Tx: Filtering Type 1 */
+ Fseg = 0x20000000, /* Tx: First SEGment */
+ Lseg = 0x40000000, /* Tx: Last SEGment */
+ Ic = 0x80000000, /* Tx: Interrupt on Completion */
+};
+
+enum { /* PHY registers */
+ Bmcr = 0, /* Basic Mode Control */
+ Bmsr = 1, /* Basic Mode Status */
+ Phyidr1 = 2, /* PHY Identifier #1 */
+ Phyidr2 = 3, /* PHY Identifier #2 */
+ Anar = 4, /* Auto-Negotiation Advertisment */
+ Anlpar = 5, /* Auto-Negotiation Link Partner Ability */
+ Aner = 6, /* Auto-Negotiation Expansion */
+};
+
+enum { /* Variants */
+ Tulip0 = (0x0009<<16)|0x1011,
+ Tulip3 = (0x0019<<16)|0x1011,
+ Pnic = (0x0002<<16)|0x11AD,
+ Pnic2 = (0xC115<<16)|0x11AD,
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+ int port;
+ Pcidev* pcidev;
+ Ctlr* next;
+ int active;
+ int id; /* (pcidev->did<<16)|pcidev->vid */
+
+ uchar* srom;
+ int sromsz; /* address size in bits */
+ uchar* sromea; /* MAC address */
+ uchar* leaf;
+ int sct; /* selected connection type */
+ int k; /* info block count */
+ uchar* infoblock[16];
+ int sctk; /* sct block index */
+ int curk; /* current block index */
+ uchar* type5block;
+
+ int phy[32]; /* logical to physical map */
+ int phyreset; /* reset bitmap */
+ int curphyad;
+ int fdx;
+ int ttm;
+
+ uchar fd; /* option */
+ int medium; /* option */
+
+ int csr6; /* CSR6 - operating mode */
+ int mask; /* CSR[57] - interrupt mask */
+ int mbps;
+
+ Lock lock;
+
+ Des* rdr; /* receive descriptor ring */
+ int nrdr; /* size of rdr */
+ int rdrx; /* index into rdr */
+
+ Lock tlock;
+ Des* tdr; /* transmit descriptor ring */
+ int ntdr; /* size of tdr */
+ int tdrh; /* host index into tdr */
+ int tdri; /* interface index into tdr */
+ int ntq; /* descriptors active */
+ int ntqmax;
+ Block* setupbp;
+
+ ulong of; /* receive statistics */
+ ulong ce;
+ ulong cs;
+ ulong tl;
+ ulong rf;
+ ulong de;
+
+ ulong ru;
+ ulong rps;
+ ulong rwt;
+
+ ulong uf; /* transmit statistics */
+ ulong ec;
+ ulong lc;
+ ulong nc;
+ ulong lo;
+ ulong to;
+
+ ulong tps;
+ ulong tu;
+ ulong tjt;
+ ulong unf;
+} Ctlr;
+
+static Ctlr* ctlrhead;
+static Ctlr* ctlrtail;
+
+#define csr32r(c, r) (inl((c)->port+((r)*8)))
+#define csr32w(c, r, l) (outl((c)->port+((r)*8), (ulong)(l)))
+
+static void
+promiscuous(void* arg, int on)
+{
+ Ctlr *ctlr;
+
+ ctlr = ((Ether*)arg)->ctlr;
+ ilock(&ctlr->lock);
+ if(on)
+ ctlr->csr6 |= Pr;
+ else
+ ctlr->csr6 &= ~Pr;
+ csr32w(ctlr, 6, ctlr->csr6);
+ iunlock(&ctlr->lock);
+}
+
+static void
+attach(Ether* ether)
+{
+ Ctlr *ctlr;
+
+ ctlr = ether->ctlr;
+ ilock(&ctlr->lock);
+ if(!(ctlr->csr6 & Sr)){
+ ctlr->csr6 |= Sr;
+ csr32w(ctlr, 6, ctlr->csr6);
+ }
+ iunlock(&ctlr->lock);
+}
+
+static long
+ifstat(Ether* ether, void* a, long n, ulong offset)
+{
+ Ctlr *ctlr;
+ char *buf, *p;
+ int i, l, len;
+
+ ctlr = ether->ctlr;
+
+ ether->crcs = ctlr->ce;
+ ether->frames = ctlr->rf+ctlr->cs;
+ ether->buffs = ctlr->de+ctlr->tl;
+ ether->overflows = ctlr->of;
+
+ if(n == 0)
+ return 0;
+
+ p = malloc(READSTR);
+ l = snprint(p, READSTR, "Overflow: %lud\n", ctlr->of);
+ l += snprint(p+l, READSTR-l, "Ru: %lud\n", ctlr->ru);
+ l += snprint(p+l, READSTR-l, "Rps: %lud\n", ctlr->rps);
+ l += snprint(p+l, READSTR-l, "Rwt: %lud\n", ctlr->rwt);
+ l += snprint(p+l, READSTR-l, "Tps: %lud\n", ctlr->tps);
+ l += snprint(p+l, READSTR-l, "Tu: %lud\n", ctlr->tu);
+ l += snprint(p+l, READSTR-l, "Tjt: %lud\n", ctlr->tjt);
+ l += snprint(p+l, READSTR-l, "Unf: %lud\n", ctlr->unf);
+ l += snprint(p+l, READSTR-l, "CRC Error: %lud\n", ctlr->ce);
+ l += snprint(p+l, READSTR-l, "Collision Seen: %lud\n", ctlr->cs);
+ l += snprint(p+l, READSTR-l, "Frame Too Long: %lud\n", ctlr->tl);
+ l += snprint(p+l, READSTR-l, "Runt Frame: %lud\n", ctlr->rf);
+ l += snprint(p+l, READSTR-l, "Descriptor Error: %lud\n", ctlr->de);
+ l += snprint(p+l, READSTR-l, "Underflow Error: %lud\n", ctlr->uf);
+ l += snprint(p+l, READSTR-l, "Excessive Collisions: %lud\n", ctlr->ec);
+ l += snprint(p+l, READSTR-l, "Late Collision: %lud\n", ctlr->lc);
+ l += snprint(p+l, READSTR-l, "No Carrier: %lud\n", ctlr->nc);
+ l += snprint(p+l, READSTR-l, "Loss of Carrier: %lud\n", ctlr->lo);
+ l += snprint(p+l, READSTR-l, "Transmit Jabber Timeout: %lud\n",
+ ctlr->to);
+ l += snprint(p+l, READSTR-l, "csr6: %luX %uX\n", csr32r(ctlr, 6),
+ ctlr->csr6);
+ snprint(p+l, READSTR-l, "ntqmax: %d\n", ctlr->ntqmax);
+ ctlr->ntqmax = 0;
+ buf = a;
+ len = readstr(offset, buf, n, p);
+ if(offset > l)
+ offset -= l;
+ else
+ offset = 0;
+ buf += len;
+ n -= len;
+
+ l = snprint(p, READSTR, "srom:");
+ for(i = 0; i < (1<<(ctlr->sromsz)*sizeof(ushort)); i++){
+ if(i && ((i & 0x0F) == 0))
+ l += snprint(p+l, READSTR-l, "\n ");
+ l += snprint(p+l, READSTR-l, " %2.2uX", ctlr->srom[i]);
+ }
+
+ snprint(p+l, READSTR-l, "\n");
+ len += readstr(offset, buf, n, p);
+ free(p);
+
+ return len;
+}
+
+static void
+txstart(Ether* ether)
+{
+ Ctlr *ctlr;
+ Block *bp;
+ Des *des;
+ int control;
+
+ ctlr = ether->ctlr;
+ while(ctlr->ntq < (ctlr->ntdr-1)){
+ if(ctlr->setupbp){
+ bp = ctlr->setupbp;
+ ctlr->setupbp = 0;
+ control = Ic|Set|BLEN(bp);
+ }
+ else{
+ bp = qget(ether->oq);
+ if(bp == nil)
+ break;
+ control = Ic|Lseg|Fseg|BLEN(bp);
+ }
+
+ ctlr->tdr[PREV(ctlr->tdrh, ctlr->ntdr)].control &= ~Ic;
+ des = &ctlr->tdr[ctlr->tdrh];
+ des->bp = bp;
+ des->addr = PCIWADDR(bp->rp);
+ des->control |= control;
+ ctlr->ntq++;
+ coherence();
+ des->status = Own;
+ csr32w(ctlr, 1, 0);
+ ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr);
+ }
+
+ if(ctlr->ntq > ctlr->ntqmax)
+ ctlr->ntqmax = ctlr->ntq;
+}
+
+static void
+transmit(Ether* ether)
+{
+ Ctlr *ctlr;
+
+ ctlr = ether->ctlr;
+ ilock(&ctlr->tlock);
+ txstart(ether);
+ iunlock(&ctlr->tlock);
+}
+
+static void
+interrupt(Ureg*, void* arg)
+{
+ Ctlr *ctlr;
+ Ether *ether;
+ int len, status;
+ Des *des;
+ Block *bp;
+
+ ether = arg;
+ ctlr = ether->ctlr;
+
+ while((status = csr32r(ctlr, 5)) & (Nis|Ais)){
+ /*
+ * Acknowledge the interrupts and mask-out
+ * the ones that are implicitly handled.
+ */
+ csr32w(ctlr, 5, status);
+ status &= (ctlr->mask & ~(Nis|Ti));
+
+ if(status & Ais){
+ if(status & Tps)
+ ctlr->tps++;
+ if(status & Tu)
+ ctlr->tu++;
+ if(status & Tjt)
+ ctlr->tjt++;
+ if(status & Ru)
+ ctlr->ru++;
+ if(status & Rps)
+ ctlr->rps++;
+ if(status & Rwt)
+ ctlr->rwt++;
+ status &= ~(Ais|Rwt|Rps|Ru|Tjt|Tu|Tps);
+ }
+
+ /*
+ * Received packets.
+ */
+ if(status & Ri){
+ des = &ctlr->rdr[ctlr->rdrx];
+ while(!(des->status & Own)){
+ if(des->status & Es){
+ if(des->status & Of)
+ ctlr->of++;
+ if(des->status & Ce)
+ ctlr->ce++;
+ if(des->status & Cs)
+ ctlr->cs++;
+ if(des->status & Tl)
+ ctlr->tl++;
+ if(des->status & Rf)
+ ctlr->rf++;
+ if(des->status & De)
+ ctlr->de++;
+ }
+ else if(bp = iallocb(Rbsz)){
+ len = ((des->status & Fl)>>16)-4;
+ des->bp->wp = des->bp->rp+len;
+ etheriq(ether, des->bp, 1);
+ des->bp = bp;
+ des->addr = PCIWADDR(bp->rp);
+ }
+
+ des->control &= Er;
+ des->control |= Rbsz;
+ coherence();
+ des->status = Own;
+
+ ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr);
+ des = &ctlr->rdr[ctlr->rdrx];
+ }
+ status &= ~Ri;
+ }
+
+ /*
+ * Check the transmit side:
+ * check for Transmit Underflow and Adjust
+ * the threshold upwards;
+ * free any transmitted buffers and try to
+ * top-up the ring.
+ */
+ if(status & Unf){
+ ctlr->unf++;
+ ilock(&ctlr->lock);
+ csr32w(ctlr, 6, ctlr->csr6 & ~St);
+ switch(ctlr->csr6 & Tr){
+ case Tr128:
+ len = Tr256;
+ break;
+ case Tr256:
+ len = Tr512;
+ break;
+ case Tr512:
+ len = Tr1024;
+ break;
+ default:
+ case Tr1024:
+ len = Sf;
+ break;
+ }
+ ctlr->csr6 = (ctlr->csr6 & ~Tr)|len;
+ csr32w(ctlr, 6, ctlr->csr6);
+ iunlock(&ctlr->lock);
+ csr32w(ctlr, 5, Tps);
+ status &= ~(Unf|Tps);
+ }
+
+ ilock(&ctlr->tlock);
+ while(ctlr->ntq){
+ des = &ctlr->tdr[ctlr->tdri];
+ if(des->status & Own)
+ break;
+
+ if(des->status & Es){
+ if(des->status & Uf)
+ ctlr->uf++;
+ if(des->status & Ec)
+ ctlr->ec++;
+ if(des->status & Lc)
+ ctlr->lc++;
+ if(des->status & Nc)
+ ctlr->nc++;
+ if(des->status & Lo)
+ ctlr->lo++;
+ if(des->status & To)
+ ctlr->to++;
+ ether->oerrs++;
+ }
+
+ freeb(des->bp);
+ des->control &= Er;
+
+ ctlr->ntq--;
+ ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr);
+ }
+ txstart(ether);
+ iunlock(&ctlr->tlock);
+
+ /*
+ * Anything left not catered for?
+ */
+ if(status)
+ panic("#l%d: status %8.8uX\n", ether->ctlrno, status);
+ }
+}
+
+static void
+ctlrinit(Ether* ether)
+{
+ Ctlr *ctlr;
+ Des *des;
+ Block *bp;
+ int i;
+ uchar bi[Eaddrlen*2];
+
+ ctlr = ether->ctlr;
+
+ /*
+ * Allocate and initialise the receive ring;
+ * allocate and initialise the transmit ring;
+ * unmask interrupts and start the transmit side;
+ * create and post a setup packet to initialise
+ * the physical ethernet address.
+ */
+ ctlr->rdr = xspanalloc(ctlr->nrdr*sizeof(Des), 8*sizeof(ulong), 0);
+ for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){
+ des->bp = iallocb(Rbsz);
+ if(des->bp == nil)
+ panic("can't allocate ethernet receive ring\n");
+ des->status = Own;
+ des->control = Rbsz;
+ des->addr = PCIWADDR(des->bp->rp);
+ }
+ ctlr->rdr[ctlr->nrdr-1].control |= Er;
+ ctlr->rdrx = 0;
+ csr32w(ctlr, 3, PCIWADDR(ctlr->rdr));
+
+ ctlr->tdr = xspanalloc(ctlr->ntdr*sizeof(Des), 8*sizeof(ulong), 0);
+ ctlr->tdr[ctlr->ntdr-1].control |= Er;
+ ctlr->tdrh = 0;
+ ctlr->tdri = 0;
+ csr32w(ctlr, 4, PCIWADDR(ctlr->tdr));
+
+ /*
+ * Clear any bits in the Status Register (CSR5) as
+ * the PNIC has a different reset value from a true 2114x.
+ */
+ ctlr->mask = Nis|Ais|Fbe|Rwt|Rps|Ru|Ri|Unf|Tjt|Tps|Ti;
+ csr32w(ctlr, 5, ctlr->mask);
+ csr32w(ctlr, 7, ctlr->mask);
+ ctlr->csr6 |= St;
+ csr32w(ctlr, 6, ctlr->csr6);
+
+ for(i = 0; i < Eaddrlen/2; i++){
+ bi[i*4] = ether->ea[i*2];
+ bi[i*4+1] = ether->ea[i*2+1];
+ bi[i*4+2] = ether->ea[i*2+1];
+ bi[i*4+3] = ether->ea[i*2];
+ }
+ bp = iallocb(Eaddrlen*2*16);
+ if(bp == nil)
+ panic("can't allocate ethernet setup buffer\n");
+ memset(bp->rp, 0xFF, sizeof(bi));
+ for(i = sizeof(bi); i < sizeof(bi)*16; i += sizeof(bi))
+ memmove(bp->rp+i, bi, sizeof(bi));
+ bp->wp += sizeof(bi)*16;
+
+ ctlr->setupbp = bp;
+ ether->oq = qopen(256*1024, Qmsg, 0, 0);
+ transmit(ether);
+}
+
+static void
+csr9w(Ctlr* ctlr, int data)
+{
+ csr32w(ctlr, 9, data);
+ microdelay(1);
+}
+
+static int
+miimdi(Ctlr* ctlr, int n)
+{
+ int data, i;
+
+ /*
+ * Read n bits from the MII Management Register.
+ */
+ data = 0;
+ for(i = n-1; i >= 0; i--){
+ if(csr32r(ctlr, 9) & Mdi)
+ data |= (1<<i);
+ csr9w(ctlr, Mii|Mdc);
+ csr9w(ctlr, Mii);
+ }
+ csr9w(ctlr, 0);
+
+ return data;
+}
+
+static void
+miimdo(Ctlr* ctlr, int bits, int n)
+{
+ int i, mdo;
+
+ /*
+ * Write n bits to the MII Management Register.
+ */
+ for(i = n-1; i >= 0; i--){
+ if(bits & (1<<i))
+ mdo = Mdo;
+ else
+ mdo = 0;
+ csr9w(ctlr, mdo);
+ csr9w(ctlr, mdo|Mdc);
+ csr9w(ctlr, mdo);
+ }
+}
+
+static int
+miir(Ctlr* ctlr, int phyad, int regad)
+{
+ int data, i;
+
+ if(ctlr->id == Pnic){
+ i = 1000;
+ csr32w(ctlr, 20, 0x60020000|(phyad<<23)|(regad<<18));
+ do{
+ microdelay(1);
+ data = csr32r(ctlr, 20);
+ }while((data & 0x80000000) && --i);
+
+ if(i == 0)
+ return -1;
+ return data & 0xFFFF;
+ }
+
+ /*
+ * Preamble;
+ * ST+OP+PHYAD+REGAD;
+ * TA + 16 data bits.
+ */
+ miimdo(ctlr, 0xFFFFFFFF, 32);
+ miimdo(ctlr, 0x1800|(phyad<<5)|regad, 14);
+ data = miimdi(ctlr, 18);
+
+ if(data & 0x10000)
+ return -1;
+
+ return data & 0xFFFF;
+}
+
+static void
+miiw(Ctlr* ctlr, int phyad, int regad, int data)
+{
+ /*
+ * Preamble;
+ * ST+OP+PHYAD+REGAD+TA + 16 data bits;
+ * Z.
+ */
+ miimdo(ctlr, 0xFFFFFFFF, 32);
+ data &= 0xFFFF;
+ data |= (0x05<<(5+5+2+16))|(phyad<<(5+2+16))|(regad<<(2+16))|(0x02<<16);
+ miimdo(ctlr, data, 32);
+ csr9w(ctlr, Mdc);
+ csr9w(ctlr, 0);
+}
+
+static int
+sromr(Ctlr* ctlr, int r)
+{
+ int i, op, data, size;
+
+ if(ctlr->id == Pnic){
+ i = 1000;
+ csr32w(ctlr, 19, 0x600|r);
+ do{
+ microdelay(1);
+ data = csr32r(ctlr, 19);
+ }while((data & 0x80000000) && --i);
+
+ if(ctlr->sromsz == 0)
+ ctlr->sromsz = 6;
+
+ return csr32r(ctlr, 9) & 0xFFFF;
+ }
+
+ /*
+ * This sequence for reading a 16-bit register 'r'
+ * in the EEPROM is taken straight from Section
+ * 7.4 of the 21140 Hardware Reference Manual.
+ */
+reread:
+ csr9w(ctlr, Rd|Ss);
+ csr9w(ctlr, Rd|Ss|Scs);
+ csr9w(ctlr, Rd|Ss|Sclk|Scs);
+ csr9w(ctlr, Rd|Ss);
+
+ op = 0x06;
+ for(i = 3-1; i >= 0; i--){
+ data = Rd|Ss|(((op>>i) & 0x01)<<2)|Scs;
+ csr9w(ctlr, data);
+ csr9w(ctlr, data|Sclk);
+ csr9w(ctlr, data);
+ }
+
+ /*
+ * First time through must work out the EEPROM size.
+ */
+ if((size = ctlr->sromsz) == 0)
+ size = 8;
+
+ for(size = size-1; size >= 0; size--){
+ data = Rd|Ss|(((r>>size) & 0x01)<<2)|Scs;
+ csr9w(ctlr, data);
+ csr9w(ctlr, data|Sclk);
+ csr9w(ctlr, data);
+ microdelay(1);
+ if(!(csr32r(ctlr, 9) & Sdo))
+ break;
+ }
+
+ data = 0;
+ for(i = 16-1; i >= 0; i--){
+ csr9w(ctlr, Rd|Ss|Sclk|Scs);
+ if(csr32r(ctlr, 9) & Sdo)
+ data |= (1<<i);
+ csr9w(ctlr, Rd|Ss|Scs);
+ }
+
+ csr9w(ctlr, 0);
+
+ if(ctlr->sromsz == 0){
+ ctlr->sromsz = 8-size;
+ goto reread;
+ }
+
+ return data & 0xFFFF;
+}
+
+static void
+softreset(Ctlr* ctlr)
+{
+ /*
+ * Soft-reset the controller and initialise bus mode.
+ * Delay should be >= 50 PCI cycles (2×S @ 25MHz).
+ */
+ csr32w(ctlr, 0, Swr);
+ microdelay(10);
+ csr32w(ctlr, 0, Rml|Cal16|Dbo);
+ delay(1);
+}
+
+static int
+type5block(Ctlr* ctlr, uchar* block)
+{
+ int csr15, i, len;
+
+ /*
+ * Reset or GPR sequence. Reset should be once only,
+ * before the GPR sequence.
+ * Note 'block' is not a pointer to the block head but
+ * a pointer to the data in the block starting at the
+ * reset length value so type5block can be used for the
+ * sequences contained in type 1 and type 3 blocks.
+ * The SROM docs state the 21140 type 5 block is the
+ * same as that for the 21143, but the two controllers
+ * use different registers and sequence-element lengths
+ * so the 21140 code here is a guess for a real type 5
+ * sequence.
+ */
+ len = *block++;
+ if(ctlr->id != Tulip3){
+ for(i = 0; i < len; i++){
+ csr32w(ctlr, 12, *block);
+ block++;
+ }
+ return len;
+ }
+
+ for(i = 0; i < len; i++){
+ csr15 = *block++<<16;
+ csr15 |= *block++<<24;
+ csr32w(ctlr, 15, csr15);
+ debug("%8.8uX ", csr15);
+ }
+ return 2*len;
+}
+
+static int
+typephylink(Ctlr* ctlr, uchar*)
+{
+ int an, bmcr, bmsr, csr6, x;
+
+ /*
+ * Fail if
+ * auto-negotiataion enabled but not complete;
+ * no valid link established.
+ */
+ bmcr = miir(ctlr, ctlr->curphyad, Bmcr);
+ miir(ctlr, ctlr->curphyad, Bmsr);
+ bmsr = miir(ctlr, ctlr->curphyad, Bmsr);
+ debug("bmcr 0x%2.2uX bmsr 0x%2.2uX\n", bmcr, bmsr);
+ if(((bmcr & 0x1000) && !(bmsr & 0x0020)) || !(bmsr & 0x0004))
+ return 0;
+
+ if(bmcr & 0x1000){
+ an = miir(ctlr, ctlr->curphyad, Anar);
+ an &= miir(ctlr, ctlr->curphyad, Anlpar) & 0x3E0;
+ debug("an 0x%2.uX 0x%2.2uX 0x%2.2uX\n",
+ miir(ctlr, ctlr->curphyad, Anar),
+ miir(ctlr, ctlr->curphyad, Anlpar),
+ an);
+
+ if(an & 0x0100)
+ x = 0x4000;
+ else if(an & 0x0080)
+ x = 0x2000;
+ else if(an & 0x0040)
+ x = 0x1000;
+ else if(an & 0x0020)
+ x = 0x0800;
+ else
+ x = 0;
+ }
+ else if((bmcr & 0x2100) == 0x2100)
+ x = 0x4000;
+ else if(bmcr & 0x2000){
+ /*
+ * If FD capable, force it if necessary.
+ */
+ if((bmsr & 0x4000) && ctlr->fd){
+ miiw(ctlr, ctlr->curphyad, Bmcr, 0x2100);
+ x = 0x4000;
+ }
+ else
+ x = 0x2000;
+ }
+ else if(bmcr & 0x0100)
+ x = 0x1000;
+ else
+ x = 0x0800;
+
+ csr6 = Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE;
+ if(ctlr->fdx & x)
+ csr6 |= Fd;
+ if(ctlr->ttm & x)
+ csr6 |= Ttm;
+ debug("csr6 0x%8.8uX 0x%8.8uX 0x%8.8luX\n",
+ csr6, ctlr->csr6, csr32r(ctlr, 6));
+ if(csr6 != ctlr->csr6){
+ ctlr->csr6 = csr6;
+ csr32w(ctlr, 6, csr6);
+ }
+
+ return 1;
+}
+
+static int
+typephymode(Ctlr* ctlr, uchar* block, int wait)
+{
+ uchar *p;
+ int len, mc, nway, phyx, timeo;
+
+ if(DEBUG){
+ int i;
+
+ len = (block[0] & ~0x80)+1;
+ for(i = 0; i < len; i++)
+ debug("%2.2uX ", block[i]);
+ debug("\n");
+ }
+
+ if(block[1] == 1)
+ len = 1;
+ else if(block[1] == 3)
+ len = 2;
+ else
+ return -1;
+
+ /*
+ * Snarf the media capabilities, nway advertisment,
+ * FDX and TTM bitmaps.
+ */
+ p = &block[5+len*block[3]+len*block[4+len*block[3]]];
+ mc = *p++;
+ mc |= *p++<<8;
+ nway = *p++;
+ nway |= *p++<<8;
+ ctlr->fdx = *p++;
+ ctlr->fdx |= *p++<<8;
+ ctlr->ttm = *p++;
+ ctlr->ttm |= *p<<8;
+ debug("mc %4.4uX nway %4.4uX fdx %4.4uX ttm %4.4uX\n",
+ mc, nway, ctlr->fdx, ctlr->ttm);
+ USED(mc);
+
+ phyx = block[2];
+ ctlr->curphyad = ctlr->phy[phyx];
+
+ ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE;
+ //csr32w(ctlr, 6, ctlr->csr6);
+ if(typephylink(ctlr, block))
+ return 0;
+
+ if(!(ctlr->phyreset & (1<<phyx))){
+ debug("reset seq: len %d: ", block[3]);
+ if(ctlr->type5block)
+ type5block(ctlr, &ctlr->type5block[2]);
+ else
+ type5block(ctlr, &block[4+len*block[3]]);
+ debug("\n");
+ ctlr->phyreset |= (1<<phyx);
+ }
+
+ /*
+ * GPR sequence.
+ */
+ debug("gpr seq: len %d: ", block[3]);
+ type5block(ctlr, &block[3]);
+ debug("\n");
+
+ ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE;
+ //csr32w(ctlr, 6, ctlr->csr6);
+ if(typephylink(ctlr, block))
+ return 0;
+
+ /*
+ * Turn off auto-negotiation, set the auto-negotiation
+ * advertisment register then start the auto-negotiation
+ * process again.
+ */
+ miiw(ctlr, ctlr->curphyad, Bmcr, 0);
+ miiw(ctlr, ctlr->curphyad, Anar, nway|1);
+ miiw(ctlr, ctlr->curphyad, Bmcr, 0x1000);
+
+ if(!wait)
+ return 0;
+
+ for(timeo = 0; timeo < 30; timeo++){
+ if(typephylink(ctlr, block))
+ return 0;
+ delay(100);
+ }
+
+ return -1;
+}
+
+static int
+typesymmode(Ctlr *ctlr, uchar *block, int wait)
+{
+ uint gpmode, gpdata, command;
+
+ USED(wait);
+ gpmode = block[3] | ((uint) block[4] << 8);
+ gpdata = block[5] | ((uint) block[6] << 8);
+ command = (block[7] | ((uint) block[8] << 8)) & 0x71;
+ if (command & 0x8000) {
+ print("ether2114x.c: FIXME: handle type 4 mode blocks where cmd.active_invalid != 0\n");
+ return -1;
+ }
+ csr32w(ctlr, 15, gpmode);
+ csr32w(ctlr, 15, gpdata);
+ ctlr->csr6 = (command & 0x71) << 18;
+ csr32w(ctlr, 6, ctlr->csr6);
+ return 0;
+}
+
+static int
+type2mode(Ctlr* ctlr, uchar* block, int)
+{
+ uchar *p;
+ int csr6, csr13, csr14, csr15, gpc, gpd;
+
+ csr6 = Sc|Mbo|Ca|Sb|TrMODE;
+ debug("type2mode: medium 0x%2.2uX\n", block[2]);
+
+ /*
+ * Don't attempt full-duplex
+ * unless explicitly requested.
+ */
+ if((block[2] & 0x3F) == 0x04){ /* 10BASE-TFD */
+ if(!ctlr->fd)
+ return -1;
+ csr6 |= Fd;
+ }
+
+ /*
+ * Operating mode programming values from the datasheet
+ * unless media specific data is explicitly given.
+ */
+ p = &block[3];
+ if(block[2] & 0x40){
+ csr13 = (block[4]<<8)|block[3];
+ csr14 = (block[6]<<8)|block[5];
+ csr15 = (block[8]<<8)|block[7];
+ p += 6;
+ }
+ else switch(block[2] & 0x3F){
+ default:
+ return -1;
+ case 0x00: /* 10BASE-T */
+ csr13 = 0x00000001;
+ csr14 = 0x00007F3F;
+ csr15 = 0x00000008;
+ break;
+ case 0x01: /* 10BASE-2 */
+ csr13 = 0x00000009;
+ csr14 = 0x00000705;
+ csr15 = 0x00000006;
+ break;
+ case 0x02: /* 10BASE-5 (AUI) */
+ csr13 = 0x00000009;
+ csr14 = 0x00000705;
+ csr15 = 0x0000000E;
+ break;
+ case 0x04: /* 10BASE-TFD */
+ csr13 = 0x00000001;
+ csr14 = 0x00007F3D;
+ csr15 = 0x00000008;
+ break;
+ }
+ gpc = *p++<<16;
+ gpc |= *p++<<24;
+ gpd = *p++<<16;
+ gpd |= *p<<24;
+
+ csr32w(ctlr, 13, 0);
+ csr32w(ctlr, 14, csr14);
+ csr32w(ctlr, 15, gpc|csr15);
+ delay(10);
+ csr32w(ctlr, 15, gpd|csr15);
+ csr32w(ctlr, 13, csr13);
+
+ ctlr->csr6 = csr6;
+ csr32w(ctlr, 6, ctlr->csr6);
+
+ debug("type2mode: csr13 %8.8uX csr14 %8.8uX csr15 %8.8uX\n",
+ csr13, csr14, csr15);
+ debug("type2mode: gpc %8.8uX gpd %8.8uX csr6 %8.8uX\n",
+ gpc, gpd, csr6);
+
+ return 0;
+}
+
+static int
+type0link(Ctlr* ctlr, uchar* block)
+{
+ int m, polarity, sense;
+
+ m = (block[3]<<8)|block[2];
+ sense = 1<<((m & 0x000E)>>1);
+ if(m & 0x0080)
+ polarity = sense;
+ else
+ polarity = 0;
+
+ return (csr32r(ctlr, 12) & sense)^polarity;
+}
+
+static int
+type0mode(Ctlr* ctlr, uchar* block, int wait)
+{
+ int csr6, m, timeo;
+
+ csr6 = Sc|Mbo|Hbd|Ca|Sb|TrMODE;
+debug("type0: medium 0x%uX, fd %d: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n",
+ ctlr->medium, ctlr->fd, block[0], block[1], block[2], block[3]);
+ switch(block[0]){
+ default:
+ break;
+
+ case 0x04: /* 10BASE-TFD */
+ case 0x05: /* 100BASE-TXFD */
+ case 0x08: /* 100BASE-FXFD */
+ /*
+ * Don't attempt full-duplex
+ * unless explicitly requested.
+ */
+ if(!ctlr->fd)
+ return -1;
+ csr6 |= Fd;
+ break;
+ }
+
+ m = (block[3]<<8)|block[2];
+ if(m & 0x0001)
+ csr6 |= Ps;
+ if(m & 0x0010)
+ csr6 |= Ttm;
+ if(m & 0x0020)
+ csr6 |= Pcs;
+ if(m & 0x0040)
+ csr6 |= Scr;
+
+ csr32w(ctlr, 12, block[1]);
+ microdelay(10);
+ csr32w(ctlr, 6, csr6);
+ ctlr->csr6 = csr6;
+
+ if(!wait)
+ return 0;
+
+ for(timeo = 0; timeo < 30; timeo++){
+ if(type0link(ctlr, block))
+ return 0;
+ delay(100);
+ }
+
+ return -1;
+}
+
+static int
+mediaxx(Ether* ether, int wait)
+{
+ Ctlr* ctlr;
+ uchar *block;
+
+ ctlr = ether->ctlr;
+ block = ctlr->infoblock[ctlr->curk];
+ if(block[0] & 0x80){
+ switch(block[1]){
+ default:
+ return -1;
+ case 0:
+ if(ctlr->medium >= 0 && block[2] != ctlr->medium)
+ return 0;
+/* need this test? */ if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[2])
+ return 0;
+ if(type0mode(ctlr, block+2, wait))
+ return 0;
+ break;
+ case 1:
+ if(typephymode(ctlr, block, wait))
+ return 0;
+ break;
+ case 2:
+ debug("type2: medium %d block[2] %d\n",
+ ctlr->medium, block[2]);
+ if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium))
+ return 0;
+ if(type2mode(ctlr, block, wait))
+ return 0;
+ break;
+ case 3:
+ if(typephymode(ctlr, block, wait))
+ return 0;
+ break;
+ case 4:
+ debug("type4: medium %d block[2] %d\n",
+ ctlr->medium, block[2]);
+ if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium))
+ return 0;
+ if(typesymmode(ctlr, block, wait))
+ return 0;
+ break;
+ }
+ }
+ else{
+ if(ctlr->medium >= 0 && block[0] != ctlr->medium)
+ return 0;
+/* need this test? */if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[0])
+ return 0;
+ if(type0mode(ctlr, block, wait))
+ return 0;
+ }
+
+ if(ctlr->csr6){
+ if(!(ctlr->csr6 & Ps) || (ctlr->csr6 & Ttm))
+ return 10;
+ return 100;
+ }
+
+ return 0;
+}
+
+static int
+media(Ether* ether, int wait)
+{
+ Ctlr* ctlr;
+ int k, mbps;
+
+ ctlr = ether->ctlr;
+ for(k = 0; k < ctlr->k; k++){
+ mbps = mediaxx(ether, wait);
+ if(mbps > 0)
+ return mbps;
+ if(ctlr->curk == 0)
+ ctlr->curk = ctlr->k-1;
+ else
+ ctlr->curk--;
+ }
+
+ return 0;
+}
+
+static char* mediatable[9] = {
+ "10BASE-T", /* TP */
+ "10BASE-2", /* BNC */
+ "10BASE-5", /* AUI */
+ "100BASE-TX",
+ "10BASE-TFD",
+ "100BASE-TXFD",
+ "100BASE-T4",
+ "100BASE-FX",
+ "100BASE-FXFD",
+};
+
+static uchar en1207[] = { /* Accton EN1207-COMBO */
+ 0x00, 0x00, 0xE8, /* [0] vendor ethernet code */
+ 0x00, /* [3] spare */
+
+ 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */
+ 0x1F, /* [6] general purpose control */
+ 2, /* [7] block count */
+
+ 0x00, /* [8] media code (10BASE-TX) */
+ 0x0B, /* [9] general purpose port data */
+ 0x9E, 0x00, /* [10] command (LSB+MSB = 0x009E) */
+
+ 0x03, /* [8] media code (100BASE-TX) */
+ 0x1B, /* [9] general purpose port data */
+ 0x6D, 0x00, /* [10] command (LSB+MSB = 0x006D) */
+
+ /* There is 10BASE-2 as well, but... */
+};
+
+static uchar ana6910fx[] = { /* Adaptec (Cogent) ANA-6910FX */
+ 0x00, 0x00, 0x92, /* [0] vendor ethernet code */
+ 0x00, /* [3] spare */
+
+ 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */
+ 0x3F, /* [6] general purpose control */
+ 1, /* [7] block count */
+
+ 0x07, /* [8] media code (100BASE-FX) */
+ 0x03, /* [9] general purpose port data */
+ 0x2D, 0x00 /* [10] command (LSB+MSB = 0x000D) */
+};
+
+static uchar smc9332[] = { /* SMC 9332 */
+ 0x00, 0x00, 0xC0, /* [0] vendor ethernet code */
+ 0x00, /* [3] spare */
+
+ 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */
+ 0x1F, /* [6] general purpose control */
+ 2, /* [7] block count */
+
+ 0x00, /* [8] media code (10BASE-TX) */
+ 0x00, /* [9] general purpose port data */
+ 0x9E, 0x00, /* [10] command (LSB+MSB = 0x009E) */
+
+ 0x03, /* [8] media code (100BASE-TX) */
+ 0x09, /* [9] general purpose port data */
+ 0x6D, 0x00, /* [10] command (LSB+MSB = 0x006D) */
+};
+
+static uchar* leaf21140[] = {
+ en1207, /* Accton EN1207-COMBO */
+ ana6910fx, /* Adaptec (Cogent) ANA-6910FX */
+ smc9332, /* SMC 9332 */
+ nil,
+};
+
+/*
+ * Copied to ctlr->srom at offset 20.
+ */
+static uchar leafpnic[] = {
+ 0x00, 0x00, 0x00, 0x00, /* MAC address */
+ 0x00, 0x00,
+ 0x00, /* controller 0 device number */
+ 0x1E, 0x00, /* controller 0 info leaf offset */
+ 0x00, /* reserved */
+ 0x00, 0x08, /* selected connection type */
+ 0x00, /* general purpose control */
+ 0x01, /* block count */
+
+ 0x8C, /* format indicator and count */
+ 0x01, /* block type */
+ 0x00, /* PHY number */
+ 0x00, /* GPR sequence length */
+ 0x00, /* reset sequence length */
+ 0x00, 0x78, /* media capabilities */
+ 0xE0, 0x01, /* Nway advertisment */
+ 0x00, 0x50, /* FDX bitmap */
+ 0x00, 0x18, /* TTM bitmap */
+};
+
+static int
+srom(Ctlr* ctlr)
+{
+ int i, k, oui, phy, x;
+ uchar *p;
+
+ /*
+ * This is a partial decoding of the SROM format described in
+ * 'Digital Semiconductor 21X4 Serial ROM Format, Version 4.05,
+ * 2-Mar-98'. Only the 2114[03] are handled, support for other
+ * controllers can be added as needed.
+ * Do a dummy read first to get the size and allocate ctlr->srom.
+ */
+ sromr(ctlr, 0);
+ if(ctlr->srom == nil)
+ ctlr->srom = malloc((1<<ctlr->sromsz)*sizeof(ushort));
+ for(i = 0; i < (1<<ctlr->sromsz); i++){
+ x = sromr(ctlr, i);
+ ctlr->srom[2*i] = x;
+ ctlr->srom[2*i+1] = x>>8;
+ }
+
+ /*
+ * There are 2 SROM layouts:
+ * e.g. Digital EtherWORKS station address at offset 20;
+ * this complies with the 21140A SROM
+ * application note from Digital;
+ * e.g. SMC9332 station address at offset 0 followed by
+ * 2 additional bytes, repeated at offset
+ * 6; the 8 bytes are also repeated in
+ * reverse order at offset 8.
+ * To check which it is, read the SROM and check for the repeating
+ * patterns of the non-compliant cards; if that fails use the one at
+ * offset 20.
+ */
+ ctlr->sromea = ctlr->srom;
+ for(i = 0; i < 8; i++){
+ x = ctlr->srom[i];
+ if(x != ctlr->srom[15-i] || x != ctlr->srom[16+i]){
+ ctlr->sromea = &ctlr->srom[20];
+ break;
+ }
+ }
+
+ /*
+ * Fake up the SROM for the PNIC.
+ * It looks like a 21140 with a PHY.
+ * The MAC address is byte-swapped in the orginal SROM data.
+ */
+ if(ctlr->id == Pnic){
+ memmove(&ctlr->srom[20], leafpnic, sizeof(leafpnic));
+ for(i = 0; i < Eaddrlen; i += 2){
+ ctlr->srom[20+i] = ctlr->srom[i+1];
+ ctlr->srom[20+i+1] = ctlr->srom[i];
+ }
+ }
+
+ /*
+ * Next, try to find the info leaf in the SROM for media detection.
+ * If it's a non-conforming card try to match the vendor ethernet code
+ * and point p at a fake info leaf with compact 21140 entries.
+ */
+ if(ctlr->sromea == ctlr->srom){
+ p = nil;
+ for(i = 0; leaf21140[i] != nil; i++){
+ if(memcmp(leaf21140[i], ctlr->sromea, 3) == 0){
+ p = &leaf21140[i][4];
+ break;
+ }
+ }
+ if(p == nil)
+ return -1;
+ }
+ else
+ p = &ctlr->srom[(ctlr->srom[28]<<8)|ctlr->srom[27]];
+
+ /*
+ * Set up the info needed for later media detection.
+ * For the 21140, set the general-purpose mask in CSR12.
+ * The info block entries are stored in order of increasing
+ * precedence, so detection will work backwards through the
+ * stored indexes into ctlr->srom.
+ * If an entry is found which matches the selected connection
+ * type, save the index. Otherwise, start at the last entry.
+ * If any MII entries are found (type 1 and 3 blocks), scan
+ * for PHYs.
+ */
+ ctlr->leaf = p;
+ ctlr->sct = *p++;
+ ctlr->sct |= *p++<<8;
+ if(ctlr->id != Tulip3){
+ csr32w(ctlr, 12, Gpc|*p++);
+ delay(200);
+ }
+ ctlr->k = *p++;
+ if(ctlr->k >= nelem(ctlr->infoblock))
+ ctlr->k = nelem(ctlr->infoblock)-1;
+ ctlr->sctk = ctlr->k-1;
+ phy = 0;
+ for(k = 0; k < ctlr->k; k++){
+ ctlr->infoblock[k] = p;
+ /*
+ * The RAMIX PMC665 has a badly-coded SROM,
+ * hence the test for 21143 and type 3.
+ */
+ if((*p & 0x80) || (ctlr->id == Tulip3 && *(p+1) == 3)){
+ *p |= 0x80;
+ if(*(p+1) == 1 || *(p+1) == 3)
+ phy = 1;
+ if(*(p+1) == 5)
+ ctlr->type5block = p;
+ p += (*p & ~0x80)+1;
+ }
+ else{
+ debug("type0: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n",
+ p[0], p[1], p[2], p[3]);
+ if(ctlr->sct != 0x0800 && *p == (ctlr->sct & 0xFF))
+ ctlr->sctk = k;
+ p += 4;
+ }
+ }
+ ctlr->curk = ctlr->sctk;
+ debug("sct 0x%uX medium 0x%uX k %d curk %d phy %d\n",
+ ctlr->sct, ctlr->medium, ctlr->k, ctlr->curk, phy);
+
+ if(phy){
+ x = 0;
+ for(k = 0; k < nelem(ctlr->phy); k++){
+ if((oui = miir(ctlr, k, 2)) == -1 || oui == 0)
+ continue;
+ if(DEBUG){
+ oui = (oui & 0x3FF)<<6;
+ oui |= miir(ctlr, k, 3)>>10;
+ miir(ctlr, k, 1);
+ debug("phy%d: index %d oui %uX reg1 %uX\n",
+ x, k, oui, miir(ctlr, k, 1));
+ USED(oui);
+ }
+ ctlr->phy[x] = k;
+ }
+ }
+
+ ctlr->fd = 0;
+ ctlr->medium = -1;
+
+ return 0;
+}
+
+static void
+dec2114xpci(void)
+{
+ Ctlr *ctlr;
+ Pcidev *p;
+ int x;
+
+ p = nil;
+ while(p = pcimatch(p, 0, 0)){
+ if(p->ccrb != 0x02 || p->ccru != 0)
+ continue;
+ switch((p->did<<16)|p->vid){
+ default:
+ continue;
+
+ case Tulip3: /* 21143 */
+ /*
+ * Exit sleep mode.
+ */
+ x = pcicfgr32(p, 0x40);
+ x &= ~0xc0000000;
+ pcicfgw32(p, 0x40, x);
+ /*FALLTHROUGH*/
+
+ case Pnic: /* PNIC */
+ case Pnic2: /* PNIC-II */
+ case Tulip0: /* 21140 */
+ break;
+ }
+
+ /*
+ * bar[0] is the I/O port register address and
+ * bar[1] is the memory-mapped register address.
+ */
+ ctlr = malloc(sizeof(Ctlr));
+ ctlr->port = p->mem[0].bar & ~0x01;
+ ctlr->pcidev = p;
+ ctlr->id = (p->did<<16)|p->vid;
+
+ if(ioalloc(ctlr->port, p->mem[0].size, 0, "dec2114x") < 0){
+ print("dec2114x: port 0x%uX in use\n", ctlr->port);
+ free(ctlr);
+ continue;
+ }
+
+ /*
+ * Some cards (e.g. ANA-6910FX) seem to need the Ps bit
+ * set or they don't always work right after a hardware
+ * reset.
+ */
+ csr32w(ctlr, 6, Mbo|Ps);
+ softreset(ctlr);
+
+ if(srom(ctlr)){
+ iofree(ctlr->port);
+ free(ctlr);
+ continue;
+ }
+
+ switch(ctlr->id){
+ default:
+ break;
+
+ case Pnic: /* PNIC */
+ /*
+ * Turn off the jabber timer.
+ */
+ csr32w(ctlr, 15, 0x00000001);
+ break;
+ }
+
+ if(ctlrhead != nil)
+ ctlrtail->next = ctlr;
+ else
+ ctlrhead = ctlr;
+ ctlrtail = ctlr;
+ }
+}
+
+static int
+reset(Ether* ether)
+{
+ Ctlr *ctlr;
+ int i, x;
+ uchar ea[Eaddrlen];
+ static int scandone;
+
+ if(scandone == 0){
+ dec2114xpci();
+ scandone = 1;
+ }
+
+ /*
+ * Any adapter matches if no ether->port is supplied,
+ * otherwise the ports must match.
+ */
+ for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+ if(ctlr->active)
+ continue;
+ if(ether->port == 0 || ether->port == ctlr->port){
+ ctlr->active = 1;
+ break;
+ }
+ }
+ if(ctlr == nil)
+ return -1;
+
+ ether->ctlr = ctlr;
+ ether->port = ctlr->port;
+// ether->irq = ctlr->pcidev->intl;
+ether->irq = 2; /* arrrrrgh */
+ ether->tbdf = ctlr->pcidev->tbdf;
+
+ /*
+ * Check if the adapter's station address is to be overridden.
+ * If not, read it from the EEPROM and set in ether->ea prior to
+ * loading the station address in the hardware.
+ */
+ memset(ea, 0, Eaddrlen);
+ if(memcmp(ea, ether->ea, Eaddrlen) == 0)
+ memmove(ether->ea, ctlr->sromea, Eaddrlen);
+
+ /*
+ * Look for a medium override in case there's no autonegotiation
+ * (no MII) or the autonegotiation fails.
+ */
+ for(i = 0; i < ether->nopt; i++){
+ if(cistrcmp(ether->opt[i], "FD") == 0){
+ ctlr->fd = 1;
+ continue;
+ }
+ for(x = 0; x < nelem(mediatable); x++){
+ debug("compare <%s> <%s>\n", mediatable[x],
+ ether->opt[i]);
+ if(cistrcmp(mediatable[x], ether->opt[i]))
+ continue;
+ ctlr->medium = x;
+
+ switch(ctlr->medium){
+ default:
+ ctlr->fd = 0;
+ break;
+
+ case 0x04: /* 10BASE-TFD */
+ case 0x05: /* 100BASE-TXFD */
+ case 0x08: /* 100BASE-FXFD */
+ ctlr->fd = 1;
+ break;
+ }
+ break;
+ }
+ }
+
+ ether->mbps = media(ether, 1);
+
+ /*
+ * Initialise descriptor rings, ethernet address.
+ */
+ ctlr->nrdr = Nrde;
+ ctlr->ntdr = Ntde;
+ pcisetbme(ctlr->pcidev);
+ ctlrinit(ether);
+
+ /*
+ * Linkage to the generic ethernet driver.
+ */
+ ether->attach = attach;
+ ether->transmit = transmit;
+ ether->interrupt = interrupt;
+ ether->ifstat = ifstat;
+
+ ether->arg = ether;
+ ether->promiscuous = promiscuous;
+
+ return 0;
+}
+
+void
+ether2114xlink(void)
+{
+ addethercard("21140", reset);
+ addethercard("2114x", reset);
+}
diff --git a/sys/src/9/mtx/etherif.h b/sys/src/9/mtx/etherif.h
new file mode 100755
index 000000000..34fa2cc02
--- /dev/null
+++ b/sys/src/9/mtx/etherif.h
@@ -0,0 +1,35 @@
+enum {
+ MaxEther = 24,
+ Ntypes = 8,
+};
+
+typedef struct Ether Ether;
+struct Ether {
+ ISAConf; /* hardware info */
+
+ int ctlrno;
+ int tbdf; /* type+busno+devno+funcno */
+ int minmtu;
+ int maxmtu;
+ uchar ea[Eaddrlen];
+
+ void (*attach)(Ether*); /* filled in by reset routine */
+ void (*transmit)(Ether*);
+ void (*interrupt)(Ureg*, void*);
+ long (*ifstat)(Ether*, void*, long, ulong);
+ long (*ctl)(Ether*, void*, long); /* custom ctl messages */
+ void *ctlr;
+
+ Queue* oq;
+
+ Netif;
+};
+
+extern Block* etheriq(Ether*, Block*, int);
+extern void addethercard(char*, int(*)(Ether*));
+extern ulong ethercrc(uchar*, int);
+
+#define NEXT(x, l) (((x)+1)%(l))
+#define PREV(x, l) (((x) == 0) ? (l)-1: (x)-1)
+#define HOWMANY(x, y) (((x)+((y)-1))/(y))
+#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y))
diff --git a/sys/src/9/mtx/fns.h b/sys/src/9/mtx/fns.h
new file mode 100755
index 000000000..d88d0d636
--- /dev/null
+++ b/sys/src/9/mtx/fns.h
@@ -0,0 +1,110 @@
+#include "../port/portfns.h"
+
+ulong cankaddr(ulong);
+int cistrcmp(char*, char*);
+int cistrncmp(char*, char*, int);
+void clockinit(void);
+void clockintr(Ureg*);
+void clockintrsched(void);
+int cmpswap(long*, long, long);
+#define coherence() eieio()
+void cpuidprint(void);
+#define cycles(x) do{}while(0)
+void dcflush(void*, ulong);
+void delay(int);
+void dumpregs(Ureg*);
+void delayloopinit(void);
+void eieio(void);
+void evenaddr(ulong);
+void faultpower(Ureg*, ulong addr, int read);
+void fprestore(FPsave*);
+void fpsave(FPsave*);
+char* getconf(char*);
+ulong getdar(void);
+ulong getdec(void);
+ulong getdsisr(void);
+ulong gethid0(void);
+ulong gethid1(void);
+ulong getmsr(void);
+ulong getpvr(void);
+void gotopc(ulong);
+int havetimer(void);
+void hwintrinit(void);
+void i8250console(void);
+void i8259init(void);
+int i8259intack(void);
+int i8259enable(Vctl*);
+int i8259vecno(int);
+int i8259disable(int);
+void icflush(void*, ulong);
+#define idlehands() /* nothing to do in the runproc */
+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 intr(Ureg*);
+void intrenable(int, void (*)(Ureg*, void*), void*, int, char*);
+int ioalloc(int, int, int, char*);
+void iofree(int);
+void ioinit(void);
+int iprint(char*, ...);
+int isaconfig(char*, int, ISAConf*);
+void kbdinit(void);
+#define kexit(a)
+#define kmapinval()
+void links(void);
+void mmuinit(void);
+void mmusweep(void*);
+void mpicdisable(int);
+void mpicenable(int, Vctl*);
+int mpiceoi(int);
+int mpicintack(void);
+int newmmupid(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);
+int pciscan(int, Pcidev **);
+ulong pcibarsize(Pcidev *, int);
+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 pcihinv(Pcidev*);
+uchar pciipin(Pcidev *, uchar);
+Pcidev* pcimatch(Pcidev*, int, int);
+Pcidev* pcimatchtbdf(int);
+void pcireset(void);
+void pcisetbme(Pcidev*);
+#define procrestore(p)
+void procsave(Proc*);
+void procsetup(Proc*);
+void putdec(ulong);
+void puthid0(ulong);
+void puthid1(ulong);
+void putmsr(ulong);
+void putsdr1(ulong);
+void putsr(int, ulong);
+void raveninit(void);
+void sync(void);
+int tas(void*);
+void timeradd(Timer *);
+void timerdel(Timer *);
+void touser(void*);
+void trapinit(void);
+void trapvec(void);
+void tlbflush(ulong);
+void tlbflushall(void);
+#define userureg(ur) (((ur)->status & MSR_PR) != 0)
+void watchreset(void);
+
+#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
+#define KADDR(a) ((void*)((ulong)(a)|KZERO))
+#define PADDR(a) ((ulong)(a)&~KZERO)
diff --git a/sys/src/9/mtx/i8259.c b/sys/src/9/mtx/i8259.c
new file mode 100755
index 000000000..605cd3fc0
--- /dev/null
+++ b/sys/src/9/mtx/i8259.c
@@ -0,0 +1,213 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+/*
+ * 8259 interrupt controllers
+ */
+enum
+{
+ Int0ctl= 0x20, /* control port (ICW1, OCW2, OCW3) */
+ Int0aux= 0x21, /* everything else (ICW2, ICW3, ICW4, OCW1) */
+ Int1ctl= 0xA0, /* control port */
+ Int1aux= 0xA1, /* everything else (ICW2, ICW3, ICW4, OCW1) */
+
+ Icw1= 0x10, /* select bit in ctl register */
+ Ocw2= 0x00,
+ Ocw3= 0x08,
+
+ EOI= 0x20, /* non-specific end of interrupt */
+
+ Elcr1= 0x4D0, /* Edge/Level Triggered Register */
+ Elcr2= 0x4D1,
+};
+
+static Lock i8259lock;
+static int i8259mask = 0xFFFF; /* disabled interrupts */
+int i8259elcr; /* mask of level-triggered interrupts */
+
+void
+i8259init(void)
+{
+ int x;
+
+ ioalloc(Int0ctl, 2, 0, "i8259.0");
+ ioalloc(Int1ctl, 2, 0, "i8259.1");
+ ilock(&i8259lock);
+
+ /*
+ * Set up the first 8259 interrupt processor.
+ * Make 8259 interrupts start at CPU vector VectorPIC.
+ * Set the 8259 as master with edge triggered
+ * input with fully nested interrupts.
+ */
+ outb(Int0ctl, (1<<4)|(0<<3)|(1<<0)); /* ICW1 - master, edge triggered,
+ ICW4 will be sent */
+ outb(Int0aux, VectorPIC); /* ICW2 - interrupt vector offset */
+ outb(Int0aux, 0x04); /* ICW3 - have slave on level 2 */
+ outb(Int0aux, 0x01); /* ICW4 - 8086 mode, not buffered */
+
+ /*
+ * Set up the second 8259 interrupt processor.
+ * Make 8259 interrupts start at CPU vector VectorPIC+8.
+ * Set the 8259 as slave with edge triggered
+ * input with fully nested interrupts.
+ */
+ outb(Int1ctl, (1<<4)|(0<<3)|(1<<0)); /* ICW1 - master, edge triggered,
+ ICW4 will be sent */
+ outb(Int1aux, VectorPIC+8); /* ICW2 - interrupt vector offset */
+ outb(Int1aux, 0x02); /* ICW3 - I am a slave on level 2 */
+ outb(Int1aux, 0x01); /* ICW4 - 8086 mode, not buffered */
+ outb(Int1aux, (i8259mask>>8) & 0xFF);
+
+ /*
+ * pass #2 8259 interrupts to #1
+ */
+ i8259mask &= ~0x04;
+ outb(Int0aux, i8259mask & 0xFF);
+
+ /*
+ * Set Ocw3 to return the ISR when ctl read.
+ * After initialisation status read is set to IRR.
+ * Read IRR first to possibly deassert an outstanding
+ * interrupt.
+ */
+ inb(Int0ctl);
+ outb(Int0ctl, Ocw3|0x03);
+ inb(Int1ctl);
+ outb(Int1ctl, Ocw3|0x03);
+
+ /*
+ * Check for Edge/Level register.
+ * This check may not work for all chipsets.
+ * First try a non-intrusive test - the bits for
+ * IRQs 13, 8, 2, 1 and 0 must be edge (0). If
+ * that's OK try a R/W test.
+ */
+ x = (inb(Elcr2)<<8)|inb(Elcr1);
+ if(!(x & 0x2107)){
+ outb(Elcr1, 0);
+ if(inb(Elcr1) == 0){
+ outb(Elcr1, 0x20);
+ if(inb(Elcr1) == 0x20)
+ i8259elcr = x;
+ outb(Elcr1, x & 0xFF);
+ print("ELCR: %4.4uX\n", i8259elcr);
+ }
+ }
+ iunlock(&i8259lock);
+}
+
+int
+i8259isr(int vno)
+{
+ int irq, isr;
+
+ if(vno < VectorPIC || vno > VectorPIC+MaxIrqPIC)
+ return 0;
+ irq = vno-VectorPIC;
+
+ /*
+ * tell the 8259 that we're done with the
+ * highest level interrupt (interrupts are still
+ * off at this point)
+ */
+ ilock(&i8259lock);
+ isr = inb(Int0ctl);
+ outb(Int0ctl, EOI);
+ if(irq >= 8){
+ isr |= inb(Int1ctl)<<8;
+ outb(Int1ctl, EOI);
+ }
+ iunlock(&i8259lock);
+
+ return isr & (1<<irq);
+}
+
+int
+i8259enable(Vctl* v)
+{
+ int irq, irqbit;
+
+ /*
+ * Given an IRQ, enable the corresponding interrupt in the i8259
+ * and return the vector to be used. The i8259 is set to use a fixed
+ * range of vectors starting at VectorPIC.
+ */
+ irq = v->irq;
+ if(irq < 0 || irq > MaxIrqPIC){
+ print("i8259enable: irq %d out of range\n", irq);
+ return -1;
+ }
+ irqbit = 1<<irq;
+
+ ilock(&i8259lock);
+ if(!(i8259mask & irqbit) && !(i8259elcr & irqbit)){
+ print("i8259enable: irq %d shared but not level\n", irq);
+ iunlock(&i8259lock);
+ return -1;
+ }
+ i8259mask &= ~irqbit;
+ if(irq < 8)
+ outb(Int0aux, i8259mask & 0xFF);
+ else
+ outb(Int1aux, (i8259mask>>8) & 0xFF);
+
+ if(i8259elcr & irqbit)
+ v->eoi = i8259isr;
+ else
+ v->isr = i8259isr;
+ iunlock(&i8259lock);
+
+ return VectorPIC+irq;
+}
+
+int
+i8259intack(void)
+{
+ int irq;
+
+ outb(Int0ctl, Ocw3|0x07); /* intr ack on first 8259 */
+ irq = inb(Int0ctl) & 7;
+ if(irq == 2) { /* cascade */
+ outb(Int1ctl, Ocw3|0x07); /* intr ack on second 8259 */
+ irq = (inb(Int1ctl) & 7) + 8;
+ }
+ return irq+VectorPIC;
+}
+
+int
+i8259vecno(int irq)
+{
+ return VectorPIC+irq;
+}
+
+int
+i8259disable(int irq)
+{
+ int irqbit;
+
+ /*
+ * Given an IRQ, disable the corresponding interrupt
+ * in the 8259.
+ */
+ if(irq < 0 || irq > MaxIrqPIC){
+ print("i8259disable: irq %d out of range\n", irq);
+ return -1;
+ }
+ irqbit = 1<<irq;
+
+ ilock(&i8259lock);
+ if(!(i8259mask & irqbit)){
+ i8259mask |= irqbit;
+ if(irq < 8)
+ outb(Int0aux, i8259mask & 0xFF);
+ else
+ outb(Int1aux, (i8259mask>>8) & 0xFF);
+ }
+ iunlock(&i8259lock);
+ return 0;
+}
diff --git a/sys/src/9/mtx/inb.s b/sys/src/9/mtx/inb.s
new file mode 100755
index 000000000..9f32a8de9
--- /dev/null
+++ b/sys/src/9/mtx/inb.s
@@ -0,0 +1,119 @@
+#include "mem.h"
+
+#define BDNZ BC 16,0,
+#define BDNE BC 0,2,
+
+TEXT inb(SB), $0
+ OR $IOMEM, R3
+ MOVBZ (R3), R3
+ RETURN
+
+TEXT insb(SB), $0
+ MOVW v+4(FP), R4
+ MOVW n+8(FP), R5
+ MOVW R5, CTR
+ OR $IOMEM, R3
+ SUB $1, R4
+insb1:
+ MOVBZ (R3), R7
+ MOVBU R7, 1(R4)
+ BDNZ insb1
+ RETURN
+
+TEXT outb(SB), $0
+ MOVW v+4(FP), R4
+ OR $IOMEM, R3
+ EIEIO
+ MOVB R4, (R3)
+ RETURN
+
+TEXT outsb(SB), $0
+ MOVW v+4(FP), R4
+ MOVW n+8(FP), R5
+ MOVW R5, CTR
+ OR $IOMEM, R3
+ SUB $1, R4
+outsb1:
+ EIEIO
+ MOVBZU 1(R4), R7
+ MOVB R7, (R3)
+ BDNZ outsb1
+ RETURN
+
+TEXT ins(SB), $0
+ OR $IOMEM, R3
+ EIEIO
+ MOVHBR (R3), R3
+ RETURN
+
+TEXT inss(SB), $0
+ MOVW v+4(FP), R4
+ MOVW n+8(FP), R5
+ MOVW R5, CTR
+ OR $IOMEM, R3
+ SUB $2, R4
+inss1:
+ EIEIO
+ MOVHZ (R3), R7
+ MOVHU R7, 2(R4)
+ BDNZ inss1
+ RETURN
+
+TEXT outs(SB), $0
+ MOVW v+4(FP), R4
+ OR $IOMEM, R3
+ EIEIO
+ MOVHBR R4, (R3)
+ RETURN
+
+TEXT outss(SB), $0
+ MOVW v+4(FP), R4
+ MOVW n+8(FP), R5
+ MOVW R5, CTR
+ OR $IOMEM, R3
+ SUB $2, R4
+outss1:
+ EIEIO
+ MOVHZU 2(R4), R7
+ MOVH R7, (R3)
+ BDNZ outss1
+ RETURN
+
+TEXT inl(SB), $0
+ OR $IOMEM, R3
+ EIEIO
+ MOVWBR (R3), R3
+ RETURN
+
+TEXT insl(SB), $0
+ MOVW v+4(FP), R4
+ MOVW n+8(FP), R5
+ MOVW R5, CTR
+ OR $IOMEM, R3
+ SUB $4, R4
+insl1:
+ EIEIO
+ MOVW (R3), R7
+ MOVWU R7, 4(R4)
+ BDNZ insl1
+ RETURN
+
+TEXT outl(SB), $0
+ MOVW v+4(FP), R4
+ OR $IOMEM, R3
+ EIEIO
+ MOVWBR R4, (R3)
+ RETURN
+
+TEXT outsl(SB), $0
+ MOVW v+4(FP), R4
+ MOVW n+8(FP), R5
+ MOVW R5, CTR
+ OR $IOMEM, R3
+ SUB $4, R4
+outsl1:
+ EIEIO
+ MOVWU 4(R4), R7
+ MOVW R7, (R3)
+ BDNZ outsl1
+ RETURN
diff --git a/sys/src/9/mtx/initcode b/sys/src/9/mtx/initcode
new file mode 100755
index 000000000..1d846abe5
--- /dev/null
+++ b/sys/src/9/mtx/initcode
@@ -0,0 +1,25 @@
+#include "/sys/src/libc/9syscall/sys.h"
+
+/*
+ * we pass in the argument of the exec parameters as 0(FP)
+ */
+
+TEXT main(SB),$8
+
+ MOVW $setSB(SB), R2
+ MOVW $boot(SB), R3
+ ADD $12, R1, R4 /* get a pointer to 0(FP) */
+ MOVW R3, 4(R1)
+ MOVW R4, 8(R1)
+ MOVW $EXEC, R3
+ SYSCALL
+
+ /* should never get here */
+loop:
+ BR loop
+
+DATA boot+0(SB)/5,$"/boot"
+DATA boot+5(SB)/5,$"/boot"
+DATA bootv+0(SB)/4,$boot+6(SB)
+GLOBL boot+0(SB),$11
+GLOBL bootv+0(SB),$8
diff --git a/sys/src/9/mtx/io.h b/sys/src/9/mtx/io.h
new file mode 100755
index 000000000..59f13d815
--- /dev/null
+++ b/sys/src/9/mtx/io.h
@@ -0,0 +1,180 @@
+enum {
+ IrqCLOCK = 0,
+ IrqKBD = 1,
+ IrqUART1 = 3,
+ IrqUART0 = 4,
+ IrqPCMCIA = 5,
+ IrqFLOPPY = 6,
+ IrqLPT = 7,
+ IrqIRQ7 = 7,
+ IrqAUX = 12, /* PS/2 port */
+ IrqIRQ13 = 13, /* coprocessor on 386 */
+ IrqATA0 = 14,
+ IrqATA1 = 15,
+ MaxIrqPIC = 15,
+
+ VectorPIC = 32,
+ MaxVectorPIC = VectorPIC+MaxIrqPIC,
+};
+
+typedef struct Vctl {
+ Vctl* next; /* handlers on this vector */
+
+ char name[KNAMELEN]; /* of driver */
+ int isintr; /* interrupt or fault/trap */
+ int irq;
+ int tbdf;
+ int (*isr)(int); /* get isr bit for this irq */
+ int (*eoi)(int); /* eoi */
+
+ void (*f)(Ureg*, void*); /* handler to call */
+ void* a; /* argument to call it with */
+} Vctl;
+
+enum {
+ BusCBUS = 0, /* Corollary CBUS */
+ BusCBUSII, /* Corollary CBUS II */
+ BusEISA, /* Extended ISA */
+ BusFUTURE, /* IEEE Futurebus */
+ BusINTERN, /* Internal bus */
+ BusISA, /* Industry Standard Architecture */
+ BusMBI, /* Multibus I */
+ BusMBII, /* Multibus II */
+ BusMCA, /* Micro Channel Architecture */
+ BusMPI, /* MPI */
+ BusMPSA, /* MPSA */
+ BusNUBUS, /* Apple Macintosh NuBus */
+ BusPCI, /* Peripheral Component Interconnect */
+ BusPCMCIA, /* PC Memory Card International Association */
+ BusTC, /* DEC TurboChannel */
+ BusVL, /* VESA Local bus */
+ BusVME, /* VMEbus */
+ BusXPRESS, /* Express System Bus */
+};
+
+#define MKBUS(t,b,d,f) (((t)<<24)|(((b)&0xFF)<<16)|(((d)&0x1F)<<11)|(((f)&0x07)<<8))
+#define BUSFNO(tbdf) (((tbdf)>>8)&0x07)
+#define BUSDNO(tbdf) (((tbdf)>>11)&0x1F)
+#define BUSBNO(tbdf) (((tbdf)>>16)&0xFF)
+#define BUSTYPE(tbdf) ((tbdf)>>24)
+#define BUSDF(tbdf) ((tbdf)&0x000FF00)
+#define BUSBDF(tbdf) ((tbdf)&0x0FFFF00)
+#define BUSUNKNOWN (-1)
+
+enum {
+ MaxEISA = 16,
+ EISAconfig = 0xC80,
+};
+
+/*
+ * PCI support code.
+ */
+enum { /* type 0 and type 1 pre-defined header */
+ PciVID = 0x00, /* vendor ID */
+ PciDID = 0x02, /* device ID */
+ PciPCR = 0x04, /* command */
+ PciPSR = 0x06, /* status */
+ PciRID = 0x08, /* revision ID */
+ PciCCRp = 0x09, /* programming interface class code */
+ PciCCRu = 0x0A, /* sub-class code */
+ PciCCRb = 0x0B, /* base class code */
+ PciCLS = 0x0C, /* cache line size */
+ PciLTR = 0x0D, /* latency timer */
+ PciHDT = 0x0E, /* header type */
+ PciBST = 0x0F, /* BIST */
+
+ PciBAR0 = 0x10, /* base address */
+ PciBAR1 = 0x14,
+
+ PciINTL = 0x3C, /* interrupt line */
+ PciINTP = 0x3D, /* interrupt pin */
+};
+
+enum { /* type 0 pre-defined header */
+ PciCIS = 0x28, /* cardbus CIS pointer */
+ PciSVID = 0x2C, /* subsystem vendor ID */
+ PciSID = 0x2E, /* cardbus CIS pointer */
+ PciEBAR0 = 0x30, /* expansion ROM base address */
+ PciMGNT = 0x3E, /* burst period length */
+ PciMLT = 0x3F, /* maximum latency between bursts */
+};
+
+enum { /* type 1 pre-defined header */
+ PciPBN = 0x18, /* primary bus number */
+ PciSBN = 0x19, /* secondary bus number */
+ PciUBN = 0x1A, /* subordinate bus number */
+ PciSLTR = 0x1B, /* secondary latency timer */
+ PciIBR = 0x1C, /* I/O base */
+ PciILR = 0x1D, /* I/O limit */
+ PciSPSR = 0x1E, /* secondary status */
+ PciMBR = 0x20, /* memory base */
+ PciMLR = 0x22, /* memory limit */
+ PciPMBR = 0x24, /* prefetchable memory base */
+ PciPMLR = 0x26, /* prefetchable memory limit */
+ PciPUBR = 0x28, /* prefetchable base upper 32 bits */
+ PciPULR = 0x2C, /* prefetchable limit upper 32 bits */
+ PciIUBR = 0x30, /* I/O base upper 16 bits */
+ PciIULR = 0x32, /* I/O limit upper 16 bits */
+ PciEBAR1 = 0x28, /* expansion ROM base address */
+ PciBCR = 0x3E, /* bridge control register */
+};
+
+enum { /* type 2 pre-defined header */
+ PciCBExCA = 0x10,
+ PciCBSPSR = 0x16,
+ PciCBPBN = 0x18, /* primary bus number */
+ PciCBSBN = 0x19, /* secondary bus number */
+ PciCBUBN = 0x1A, /* subordinate bus number */
+ PciCBSLTR = 0x1B, /* secondary latency timer */
+ PciCBMBR0 = 0x1C,
+ PciCBMLR0 = 0x20,
+ PciCBMBR1 = 0x24,
+ PciCBMLR1 = 0x28,
+ PciCBIBR0 = 0x2C, /* I/O base */
+ PciCBILR0 = 0x30, /* I/O limit */
+ PciCBIBR1 = 0x34, /* I/O base */
+ PciCBILR1 = 0x38, /* I/O limit */
+ PciCBSVID = 0x40, /* subsystem vendor ID */
+ PciCBSID = 0x42, /* subsystem ID */
+ PciCBLMBAR = 0x44, /* legacy mode base address */
+};
+
+typedef struct Pcisiz Pcisiz;
+struct Pcisiz
+{
+ Pcidev* dev;
+ int siz;
+ int bar;
+};
+
+typedef struct Pcidev Pcidev;
+typedef struct Pcidev {
+ int tbdf; /* type+bus+device+function */
+ ushort vid; /* vendor ID */
+ ushort did; /* device ID */
+
+ uchar rid;
+ uchar ccrp;
+ uchar ccru;
+ uchar ccrb;
+
+ struct {
+ ulong bar; /* base address */
+ int size;
+ } mem[6];
+
+ uchar intl; /* interrupt line */
+
+ Pcidev* list;
+ Pcidev* link; /* next device on this bno */
+
+ Pcidev* bridge; /* down a bus */
+ struct {
+ ulong bar;
+ int size;
+ } ioa, mema;
+ ulong pcr;
+};
+
+#define PCIWINDOW 0x80000000
+#define PCIWADDR(va) (PADDR(va)+PCIWINDOW)
diff --git a/sys/src/9/mtx/kbd.c b/sys/src/9/mtx/kbd.c
new file mode 100755
index 000000000..fe96fc345
--- /dev/null
+++ b/sys/src/9/mtx/kbd.c
@@ -0,0 +1,435 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+enum {
+ Data= 0x60, /* data port */
+
+ Status= 0x64, /* status port */
+ Inready= 0x01, /* input character ready */
+ Outbusy= 0x02, /* output busy */
+ Sysflag= 0x04, /* system flag */
+ Cmddata= 0x08, /* cmd==0, data==1 */
+ Inhibit= 0x10, /* keyboard/mouse inhibited */
+ Minready= 0x20, /* mouse character ready */
+ Rtimeout= 0x40, /* general timeout */
+ Parity= 0x80,
+
+ Cmd= 0x64, /* command port (write only) */
+
+ Spec= 0x80,
+
+ PF= Spec|0x20, /* num pad function key */
+ View= Spec|0x00, /* view (shift window up) */
+ KF= 0xF000, /* function key (begin Unicode private space) */
+ Shift= Spec|0x60,
+ Break= Spec|0x61,
+ Ctrl= Spec|0x62,
+ Latin= Spec|0x63,
+ Caps= Spec|0x64,
+ Num= Spec|0x65,
+ Middle= Spec|0x66,
+ No= 0x00, /* peter */
+
+ Home= KF|13,
+ Up= KF|14,
+ Pgup= KF|15,
+ Print= KF|16,
+ Left= KF|17,
+ Right= KF|18,
+ End= '\r',
+ Down= View,
+ Pgdown= KF|19,
+ Ins= KF|20,
+ Del= 0x7F,
+ Scroll= KF|21,
+};
+
+/*
+ * The codes at 0x79 and 0x81 are produed by the PFU Happy Hacking keyboard.
+ * A 'standard' keyboard doesn't produce anything above 0x58.
+ */
+Rune kbtab[] =
+{
+[0x00] No, 0x1b, '1', '2', '3', '4', '5', '6',
+[0x08] '7', '8', '9', '0', '-', '=', '\b', '\t',
+[0x10] 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
+[0x18] 'o', 'p', '[', ']', '\n', Ctrl, 'a', 's',
+[0x20] 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
+[0x28] '\'', '`', Shift, '\\', 'z', 'x', 'c', 'v',
+[0x30] 'b', 'n', 'm', ',', '.', '/', Shift, '*',
+[0x38] Latin, ' ', Ctrl, KF|1, KF|2, KF|3, KF|4, KF|5,
+[0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, Scroll, '7',
+[0x48] '8', '9', '-', '4', '5', '6', '+', '1',
+[0x50] '2', '3', '0', '.', No, No, No, KF|11,
+[0x58] KF|12, No, No, No, No, No, No, No,
+[0x60] No, No, No, No, No, No, No, No,
+[0x68] No, No, No, No, No, No, No, No,
+[0x70] No, No, No, No, No, No, No, No,
+[0x78] No, View, No, Up, No, No, No, No,
+};
+
+Rune kbtabshift[] =
+{
+[0x00] No, 0x1b, '!', '@', '#', '$', '%', '^',
+[0x08] '&', '*', '(', ')', '_', '+', '\b', '\t',
+[0x10] 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
+[0x18] 'O', 'P', '{', '}', '\n', Ctrl, 'A', 'S',
+[0x20] 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':',
+[0x28] '"', '~', Shift, '|', 'Z', 'X', 'C', 'V',
+[0x30] 'B', 'N', 'M', '<', '>', '?', Shift, '*',
+[0x38] Latin, ' ', Ctrl, KF|1, KF|2, KF|3, KF|4, KF|5,
+[0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, Scroll, '7',
+[0x48] '8', '9', '-', '4', '5', '6', '+', '1',
+[0x50] '2', '3', '0', '.', No, No, No, KF|11,
+[0x58] KF|12, No, No, No, No, No, No, No,
+[0x60] No, No, No, No, No, No, No, No,
+[0x68] No, No, No, No, No, No, No, No,
+[0x70] No, No, No, No, No, No, No, No,
+[0x78] No, Up, No, Up, No, No, No, No,
+};
+
+Rune kbtabesc1[] =
+{
+[0x00] No, No, No, No, No, No, No, No,
+[0x08] No, No, No, No, No, No, No, No,
+[0x10] No, No, No, No, No, No, No, No,
+[0x18] No, No, No, No, '\n', Ctrl, No, No,
+[0x20] No, No, No, No, No, No, No, No,
+[0x28] No, No, Shift, No, No, No, No, No,
+[0x30] No, No, No, No, No, '/', No, Print,
+[0x38] Latin, No, No, No, No, No, No, No,
+[0x40] No, No, No, No, No, No, Break, Home,
+[0x48] Up, Pgup, No, Left, No, Right, No, End,
+[0x50] Down, Pgdown, Ins, Del, No, No, No, No,
+[0x58] No, No, No, No, No, No, No, No,
+[0x60] No, No, No, No, No, No, No, No,
+[0x68] No, No, No, No, No, No, No, No,
+[0x70] No, No, No, No, No, No, No, No,
+[0x78] No, Up, No, No, No, No, No, No,
+};
+
+enum
+{
+ /* controller command byte */
+ Cscs1= (1<<6), /* scan code set 1 */
+ Cauxdis= (1<<5), /* mouse disable */
+ Ckbddis= (1<<4), /* kbd disable */
+ Csf= (1<<2), /* system flag */
+ Cauxint= (1<<1), /* mouse interrupt enable */
+ Ckbdint= (1<<0), /* kbd interrupt enable */
+};
+
+static Lock i8042lock;
+static uchar ccc;
+static void (*auxputc)(int, int);
+
+/*
+ * wait for output no longer busy
+ */
+static int
+outready(void)
+{
+ int tries;
+
+ for(tries = 0; (inb(Status) & Outbusy); tries++){
+ if(tries > 500)
+ return -1;
+ delay(2);
+ }
+ return 0;
+}
+
+/*
+ * wait for input
+ */
+static int
+inready(void)
+{
+ int tries;
+
+ for(tries = 0; !(inb(Status) & Inready); tries++){
+ if(tries > 500)
+ return -1;
+ delay(2);
+ }
+ return 0;
+}
+
+/*
+ * ask 8042 to reset the machine
+ */
+void
+i8042reset(void)
+{
+ ushort *s = KADDR(0x472);
+ int i, x;
+
+ *s = 0x1234; /* BIOS warm-boot flag */
+
+ /*
+ * newer reset the machine command
+ */
+ outready();
+ outb(Cmd, 0xFE);
+ outready();
+
+ /*
+ * Pulse it by hand (old somewhat reliable)
+ */
+ x = 0xDF;
+ for(i = 0; i < 5; i++){
+ x ^= 1;
+ outready();
+ outb(Cmd, 0xD1);
+ outready();
+ outb(Data, x); /* toggle reset */
+ delay(100);
+ }
+}
+
+int
+i8042auxcmd(int cmd)
+{
+ unsigned int c;
+ int tries;
+
+ c = 0;
+ tries = 0;
+
+ ilock(&i8042lock);
+ do{
+ if(tries++ > 2)
+ break;
+ if(outready() < 0)
+ break;
+ outb(Cmd, 0xD4);
+ if(outready() < 0)
+ break;
+ outb(Data, cmd);
+ if(outready() < 0)
+ break;
+ if(inready() < 0)
+ break;
+ c = inb(Data);
+ } while(c == 0xFE || c == 0);
+ iunlock(&i8042lock);
+
+ if(c != 0xFA){
+ print("i8042: %2.2ux returned to the %2.2ux command\n", c, cmd);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * keyboard interrupt
+ */
+static void
+i8042intr(Ureg*, void*)
+{
+ int s, c, i;
+ static int esc1, esc2;
+ static int alt, caps, ctl, num, shift;
+ static int collecting, nk;
+ static Rune kc[5];
+ int keyup;
+
+ /*
+ * get status
+ */
+ lock(&i8042lock);
+ s = inb(Status);
+ if(!(s&Inready)){
+ unlock(&i8042lock);
+ return;
+ }
+
+ /*
+ * get the character
+ */
+ c = inb(Data);
+ unlock(&i8042lock);
+
+ /*
+ * if it's the aux port...
+ */
+ if(s & Minready){
+ if(auxputc != nil)
+ auxputc(c, shift);
+ return;
+ }
+
+ /*
+ * e0's is the first of a 2 character sequence
+ */
+ if(c == 0xe0){
+ esc1 = 1;
+ return;
+ } else if(c == 0xe1){
+ esc2 = 2;
+ return;
+ }
+
+ keyup = c&0x80;
+ c &= 0x7f;
+ if(c > sizeof kbtab){
+ c |= keyup;
+ if(c != 0xFF) /* these come fairly often: CAPSLOCK U Y */
+ print("unknown key %ux\n", c);
+ return;
+ }
+
+ if(esc1){
+ c = kbtabesc1[c];
+ esc1 = 0;
+ } else if(esc2){
+ esc2--;
+ return;
+ } else if(shift)
+ c = kbtabshift[c];
+ else
+ c = kbtab[c];
+
+ if(caps && c<='z' && c>='a')
+ c += 'A' - 'a';
+
+ /*
+ * keyup only important for shifts
+ */
+ if(keyup){
+ switch(c){
+ case Latin:
+ alt = 0;
+ break;
+ case Shift:
+ shift = 0;
+ break;
+ case Ctrl:
+ ctl = 0;
+ break;
+ }
+ return;
+ }
+
+ /*
+ * normal character
+ */
+ if(!(c & (Spec|KF))){
+ if(ctl){
+ if(alt && c == Del)
+ exit(0);
+ c &= 0x1f;
+ }
+ if(!collecting){
+ kbdputc(kbdq, c);
+ return;
+ }
+ kc[nk++] = c;
+ c = latin1(kc, nk);
+ if(c < -1) /* need more keystrokes */
+ return;
+ if(c != -1) /* valid sequence */
+ kbdputc(kbdq, c);
+ else /* dump characters */
+ for(i=0; i<nk; i++)
+ kbdputc(kbdq, kc[i]);
+ nk = 0;
+ collecting = 0;
+ return;
+ } else {
+ switch(c){
+ case Caps:
+ caps ^= 1;
+ return;
+ case Num:
+ num ^= 1;
+ return;
+ case Shift:
+ shift = 1;
+ return;
+ case Latin:
+ alt = 1;
+ collecting = 1;
+ nk = 0;
+ return;
+ case Ctrl:
+ ctl = 1;
+ return;
+ }
+ }
+ kbdputc(kbdq, c);
+}
+
+void
+i8042auxenable(void (*putc)(int, int))
+{
+ char *err = "i8042: aux init failed\n";
+
+ /* enable kbd/aux xfers and interrupts */
+ ccc &= ~Cauxdis;
+ ccc |= Cauxint;
+
+ ilock(&i8042lock);
+ if(outready() < 0)
+ print(err);
+ outb(Cmd, 0x60); /* write control register */
+ if(outready() < 0)
+ print(err);
+ outb(Data, ccc);
+ if(outready() < 0)
+ print(err);
+ outb(Cmd, 0xA8); /* auxilliary device enable */
+ if(outready() < 0){
+ iunlock(&i8042lock);
+ return;
+ }
+ auxputc = putc;
+ intrenable(IrqAUX, i8042intr, 0, BUSUNKNOWN, "kbdaux");
+ iunlock(&i8042lock);
+}
+
+void
+kbdinit(void)
+{
+ int c;
+
+ kbdq = qopen(4*1024, 0, 0, 0);
+ if(kbdq == nil)
+ panic("kbdinit");
+ qnoblock(kbdq, 1);
+
+ ioalloc(Data, 1, 0, "kbd");
+ ioalloc(Cmd, 1, 0, "kbd");
+
+ intrenable(IrqKBD, i8042intr, 0, BUSUNKNOWN, "kbd");
+
+ /* wait for a quiescent controller */
+ while((c = inb(Status)) & (Outbusy | Inready))
+ if(c & Inready)
+ inb(Data);
+
+ /* get current controller command byte */
+ outb(Cmd, 0x20);
+ if(inready() < 0){
+ print("kbdinit: can't read ccc\n");
+ ccc = 0;
+ } else
+ ccc = inb(Data);
+
+ /* enable kbd xfers and interrupts */
+ /* disable mouse */
+ ccc &= ~Ckbddis;
+ ccc |= Csf | Ckbdint | Cscs1;
+ if(outready() < 0)
+ print("kbd init failed\n");
+ outb(Cmd, 0x60);
+ if(outready() < 0)
+ print("kbd init failed\n");
+ outb(Data, ccc);
+ outready();
+}
diff --git a/sys/src/9/mtx/l.s b/sys/src/9/mtx/l.s
new file mode 100755
index 000000000..c880c5390
--- /dev/null
+++ b/sys/src/9/mtx/l.s
@@ -0,0 +1,603 @@
+#include "mem.h"
+
+/* use of SPRG registers in save/restore */
+#define SAVER0 SPRG0
+#define SAVER1 SPRG1
+#define SAVELR SPRG2
+#define SAVEXX SPRG3
+
+/* special instruction definitions */
+#define BDNZ BC 16,0,
+#define BDNE BC 0,2,
+
+#define TLBIA WORD $((31<<26)|(307<<1))
+#define TLBSYNC WORD $((31<<26)|(566<<1))
+
+/* on some models mtmsr doesn't synchronise enough (eg, 603e) */
+#define MSRSYNC SYNC; ISYNC
+
+#define UREGSPACE (UREGSIZE+8)
+
+ TEXT start(SB), $-4
+
+ /*
+ * setup MSR
+ * turn off interrupts
+ * use 0x000 as exception prefix
+ * enable machine check
+ */
+ MOVW MSR, R3
+ MOVW $(MSR_EE|MSR_IP), R4
+ ANDN R4, R3
+ OR $(MSR_ME), R3
+ ISYNC
+ MOVW R3, MSR
+ MSRSYNC
+
+ /* except during trap handling, R0 is zero from now on */
+ MOVW $0, R0
+
+ /* setup SB for pre mmu */
+ MOVW $setSB(SB), R2
+ MOVW $KZERO, R3
+ ANDN R3, R2
+
+ BL mmuinit0(SB)
+
+ /* running with MMU on!! */
+
+ /* set R2 to correct value */
+ MOVW $setSB(SB), R2
+
+ /* debugger sets R1 to top of usable memory +1 */
+ MOVW R1, memsize(SB)
+
+ BL kfpinit(SB)
+
+ /* set up Mach */
+ MOVW $mach0(SB), R(MACH)
+ ADD $(MACHSIZE-8), R(MACH), R1 /* set stack */
+
+ MOVW R0, R(USER)
+ MOVW R0, 0(R(MACH))
+
+ BL main(SB)
+
+ RETURN /* not reached */
+
+GLOBL mach0(SB), $(MAXMACH*BY2PG)
+GLOBL memsize(SB), $4
+
+/*
+ * on return from this function we will be running in virtual mode.
+ * We set up the Block Address Translation (BAT) registers thus:
+ * 1) first 3 BATs are 256M blocks, starting from KZERO->0
+ * 2) remaining BAT maps last 256M directly
+ */
+TEXT mmuinit0(SB), $0
+ /* reset all the tlbs */
+ MOVW $64, R3
+ MOVW R3, CTR
+ MOVW $0, R4
+tlbloop:
+ TLBIE R4
+ ADD $BIT(19), R4
+ BDNZ tlbloop
+ TLBSYNC
+
+ /* KZERO -> 0 */
+ MOVW $(KZERO|(0x7ff<<2)|2), R3
+ MOVW $(PTEVALID|PTEWRITE), R4
+ MOVW R3, SPR(IBATU(0))
+ MOVW R4, SPR(IBATL(0))
+ MOVW R3, SPR(DBATU(0))
+ MOVW R4, SPR(DBATL(0))
+
+ /* KZERO+256M -> 256M */
+ ADD $(1<<28), R3
+ ADD $(1<<28), R4
+ MOVW R3, SPR(IBATU(1))
+ MOVW R4, SPR(IBATL(1))
+ MOVW R3, SPR(DBATU(1))
+ MOVW R4, SPR(DBATL(1))
+
+ /* KZERO+512M -> 512M */
+ ADD $(1<<28), R3
+ ADD $(1<<28), R4
+ MOVW R3, SPR(IBATU(2))
+ MOVW R4, SPR(IBATL(2))
+ MOVW R3, SPR(DBATU(2))
+ MOVW R4, SPR(DBATL(2))
+
+ /* direct map last block, uncached, (?guarded) */
+ MOVW $((0xf<<28)|(0x7ff<<2)|2), R3
+ MOVW $((0xf<<28)|PTE1_I|PTE1_G|PTE1_RW), R4
+ MOVW R3, SPR(DBATU(3))
+ MOVW R4, SPR(DBATL(3))
+
+ /* IBAT 3 unused */
+ MOVW R0, SPR(IBATU(3))
+ MOVW R0, SPR(IBATL(3))
+
+ /* enable MMU */
+ MOVW LR, R3
+ OR $KZERO, R3
+ MOVW R3, SPR(SRR0)
+ MOVW MSR, R4
+ OR $(MSR_IR|MSR_DR), R4
+ MOVW R4, SPR(SRR1)
+ RFI /* resume in kernel mode in caller */
+
+ RETURN
+
+TEXT kfpinit(SB), $0
+ MOVFL $0,FPSCR(7)
+ MOVFL $0xD,FPSCR(6) /* VE, OE, ZE */
+ MOVFL $0, FPSCR(5)
+ MOVFL $0, FPSCR(3)
+ MOVFL $0, FPSCR(2)
+ MOVFL $0, FPSCR(1)
+ MOVFL $0, FPSCR(0)
+
+ FMOVD $4503601774854144.0, F27
+ FMOVD $0.5, F29
+ FSUB F29, F29, F28
+ FADD F29, F29, F30
+ FADD F30, F30, F31
+ FMOVD F28, F0
+ FMOVD F28, F1
+ FMOVD F28, F2
+ FMOVD F28, F3
+ FMOVD F28, F4
+ FMOVD F28, F5
+ FMOVD F28, F6
+ FMOVD F28, F7
+ FMOVD F28, F8
+ FMOVD F28, F9
+ FMOVD F28, F10
+ FMOVD F28, F11
+ FMOVD F28, F12
+ FMOVD F28, F13
+ FMOVD F28, F14
+ FMOVD F28, F15
+ FMOVD F28, F16
+ FMOVD F28, F17
+ FMOVD F28, F18
+ FMOVD F28, F19
+ FMOVD F28, F20
+ FMOVD F28, F21
+ FMOVD F28, F22
+ FMOVD F28, F23
+ FMOVD F28, F24
+ FMOVD F28, F25
+ FMOVD F28, F26
+ RETURN
+
+TEXT splhi(SB), $0
+ MOVW LR, R31
+ MOVW R31, 4(R(MACH)) /* save PC in m->splpc */
+ MOVW MSR, R3
+ RLWNM $0, R3, $~MSR_EE, R4
+ SYNC
+ MOVW R4, MSR
+ MSRSYNC
+ RETURN
+
+TEXT splx(SB), $0
+ /* fall though */
+
+TEXT splxpc(SB), $0
+ MOVW LR, R31
+ MOVW R31, 4(R(MACH)) /* save PC in m->splpc */
+ MOVW MSR, R4
+ RLWMI $0, R3, $MSR_EE, R4
+ SYNC
+ MOVW R4, MSR
+ MSRSYNC
+ RETURN
+
+TEXT spllo(SB), $0
+ MOVW MSR, R3
+ OR $MSR_EE, R3, R4
+ SYNC
+ MOVW R4, MSR
+ MSRSYNC
+ RETURN
+
+TEXT spldone(SB), $0
+ RETURN
+
+TEXT islo(SB), $0
+ MOVW MSR, R3
+ RLWNM $0, R3, $MSR_EE, R3
+ RETURN
+
+TEXT setlabel(SB), $-4
+ MOVW LR, R31
+ MOVW R1, 0(R3)
+ MOVW R31, 4(R3)
+ MOVW $0, R3
+ RETURN
+
+TEXT gotolabel(SB), $-4
+ MOVW 4(R3), R31
+ MOVW R31, LR
+ MOVW 0(R3), R1
+ MOVW $1, R3
+ RETURN
+
+TEXT touser(SB), $-4
+ MOVW $(UTZERO+32), R5 /* header appears in text */
+ MOVW $(MSR_EE|MSR_PR|MSR_ME|MSR_IR|MSR_DR|MSR_RI), R4
+ MOVW R4, SPR(SRR1)
+ MOVW R3, R1
+ MOVW R5, SPR(SRR0)
+ RFI
+
+TEXT icflush(SB), $-4 /* icflush(virtaddr, count) */
+ MOVW n+4(FP), R4
+ RLWNM $0, R3, $~(CACHELINESZ-1), R5
+ SUB R5, R3
+ ADD R3, R4
+ ADD $(CACHELINESZ-1), R4
+ SRAW $CACHELINELOG, R4
+ MOVW R4, CTR
+icf0: ICBI (R5)
+ ADD $CACHELINESZ, R5
+ BDNZ icf0
+ ISYNC
+ RETURN
+
+TEXT dcflush(SB), $-4 /* dcflush(virtaddr, count) */
+ MOVW n+4(FP), R4
+ RLWNM $0, R3, $~(CACHELINESZ-1), R5
+ CMP R4, $0
+ BLE dcf1
+ SUB R5, R3
+ ADD R3, R4
+ ADD $(CACHELINESZ-1), R4
+ SRAW $CACHELINELOG, R4
+ MOVW R4, CTR
+dcf0: DCBF (R5)
+ ADD $CACHELINESZ, R5
+ BDNZ dcf0
+dcf1:
+ SYNC
+ RETURN
+
+TEXT tas(SB), $0
+ SYNC
+ MOVW R3, R4
+ MOVW $0xdead,R5
+tas1:
+ DCBF (R4) /* fix for 603x bug */
+ LWAR (R4), R3
+ CMP R3, $0
+ BNE tas0
+ STWCCC R5, (R4)
+ BNE tas1
+tas0:
+ SYNC
+ ISYNC
+ RETURN
+
+TEXT _xinc(SB),$0 /* void _xinc(long *); */
+ MOVW R3, R4
+xincloop:
+ DCBF (R4) /* fix for 603x bug */
+ LWAR (R4), R3
+ ADD $1, R3
+ STWCCC R3, (R4)
+ BNE xincloop
+ RETURN
+
+TEXT _xdec(SB),$0 /* long _xdec(long *); */
+ MOVW R3, R4
+xdecloop:
+ DCBF (R4) /* fix for 603x bug */
+ LWAR (R4), R3
+ ADD $-1, R3
+ STWCCC R3, (R4)
+ BNE xdecloop
+ RETURN
+
+TEXT cmpswap(SB),$0 /* int cmpswap(long*, long, long) */
+ MOVW R3, R4 /* addr */
+ MOVW old+4(FP), R5
+ MOVW new+8(FP), R6
+ DCBF (R4) /* fix for 603x bug? */
+ LWAR (R4), R3
+ CMP R3, R5
+ BNE fail
+ STWCCC R6, (R4)
+ BNE fail
+ MOVW $1, R3
+ RETURN
+fail:
+ MOVW $0, R3
+ RETURN
+
+TEXT getpvr(SB), $0
+ MOVW SPR(PVR), R3
+ RETURN
+
+TEXT getdec(SB), $0
+ MOVW SPR(DEC), R3
+ RETURN
+
+TEXT putdec(SB), $0
+ MOVW R3, SPR(DEC)
+ RETURN
+
+TEXT getdar(SB), $0
+ MOVW SPR(DAR), R3
+ RETURN
+
+TEXT getdsisr(SB), $0
+ MOVW SPR(DSISR), R3
+ RETURN
+
+TEXT getmsr(SB), $0
+ MOVW MSR, R3
+ RETURN
+
+TEXT putmsr(SB), $0
+ SYNC
+ MOVW R3, MSR
+ MSRSYNC
+ RETURN
+
+TEXT putsdr1(SB), $0
+ MOVW R3, SPR(SDR1)
+ RETURN
+
+TEXT putsr(SB), $0
+ MOVW 4(FP), R4
+ MOVW R4, SEG(R3)
+ RETURN
+
+TEXT gethid0(SB), $0
+ MOVW SPR(HID0), R3
+ RETURN
+
+TEXT gethid1(SB), $0
+ MOVW SPR(HID1), R3
+ RETURN
+
+TEXT puthid0(SB), $0
+ MOVW R3, SPR(HID0)
+ RETURN
+
+TEXT puthid1(SB), $0
+ MOVW R3, SPR(HID1)
+ RETURN
+
+TEXT eieio(SB), $0
+ EIEIO
+ RETURN
+
+TEXT sync(SB), $0
+ SYNC
+ RETURN
+
+TEXT tlbflushall(SB), $0
+ MOVW $64, R3
+ MOVW R3, CTR
+ MOVW $0, R4
+tlbflushall0:
+ TLBIE R4
+ ADD $BIT(19), R4
+ BDNZ tlbflushall0
+ EIEIO
+ TLBSYNC
+ SYNC
+ RETURN
+
+TEXT tlbflush(SB), $0
+ TLBIE R3
+ RETURN
+
+TEXT gotopc(SB), $0
+ MOVW R3, CTR
+ MOVW LR, R31 /* for trace back */
+ BR (CTR)
+
+/*
+ * traps force memory mapping off.
+ * the following code has been executed at the exception
+ * vector location
+ * MOVW R0, SPR(SAVER0)
+ * MOVW LR, R0
+ * MOVW R0, SPR(SAVELR)
+ * bl trapvec(SB)
+ */
+TEXT trapvec(SB), $-4
+ MOVW LR, R0
+ MOVW R1, SPR(SAVER1)
+ MOVW R0, SPR(SAVEXX) /* vector */
+
+ /* did we come from user space */
+ MOVW SPR(SRR1), R0
+ MOVW CR, R1
+ MOVW R0, CR
+ BC 4,17,ktrap
+
+ /* switch to kernel stack */
+ MOVW R1, CR
+ MOVW R2, R0
+ MOVW $setSB(SB), R2
+ RLWNM $0, R2, $~KZERO, R2 /* PADDR(setSB) */
+ MOVW $mach0(SB), R1 /* m-> */
+ RLWNM $0, R1, $~KZERO, R1 /* PADDR(m->) */
+ MOVW 8(R1), R1 /* m->proc */
+ RLWNM $0, R1, $~KZERO, R1 /* PADDR(m->proc) */
+ MOVW 8(R1), R1 /* m->proc->kstack */
+ RLWNM $0, R1, $~KZERO, R1 /* PADDR(m->proc->kstack) */
+ ADD $(KSTACK-UREGSIZE), R1
+ MOVW R0, R2
+ BL saveureg(SB)
+ BL trap(SB)
+ BR restoreureg
+ktrap:
+ MOVW R1, CR
+ MOVW SPR(SAVER1), R1
+ RLWNM $0, R1, $~KZERO, R1 /* PADDR(R1) */
+ SUB $UREGSPACE, R1
+ BL saveureg(SB)
+ BL trap(SB)
+ BR restoreureg
+
+/*
+ * enter with stack set and mapped.
+ * on return, SB (R2) has been set, and R3 has the Ureg*,
+ * the MMU has been re-enabled, kernel text and PC are in KSEG,
+ * R(MACH) has been set, and R0 contains 0.
+ *
+ */
+TEXT saveureg(SB), $-4
+/*
+ * save state
+ */
+ MOVMW R2, 48(R1) /* r2:r31 */
+ MOVW $setSB(SB), R2
+ RLWNM $0, R2, $~KZERO, R2 /* PADDR(setSB) */
+ MOVW $mach0(SB), R(MACH)
+ RLWNM $0, R(MACH), $~KZERO, R(MACH) /* PADDR(m->) */
+ MOVW 8(R(MACH)), R(USER)
+ MOVW $mach0(SB), R(MACH)
+ MOVW $setSB(SB), R2
+ MOVW SPR(SAVER1), R4
+ MOVW R4, 44(R1)
+ MOVW SPR(SAVER0), R5
+ MOVW R5, 40(R1)
+ MOVW CTR, R6
+ MOVW R6, 36(R1)
+ MOVW XER, R4
+ MOVW R4, 32(R1)
+ MOVW CR, R5
+ MOVW R5, 28(R1)
+ MOVW SPR(SAVELR), R6 /* LR */
+ MOVW R6, 24(R1)
+ /* pad at 20(R1) */
+ MOVW SPR(SRR0), R0
+ MOVW R0, 16(R1) /* old PC */
+ MOVW SPR(SRR1), R0
+ MOVW R0, 12(R1) /* old status */
+ MOVW SPR(SAVEXX), R0
+ MOVW R0, 8(R1) /* cause/vector */
+ ADD $8, R1, R3 /* Ureg* */
+ OR $KZERO, R3 /* fix ureg */
+ STWCCC R3, (R1) /* break any pending reservations */
+ MOVW $0, R0 /* compiler/linker expect R0 to be zero */
+
+ MOVW MSR, R5
+ OR $(MSR_IR|MSR_DR|MSR_FP|MSR_RI), R5 /* enable MMU */
+ MOVW R5, SPR(SRR1)
+ MOVW LR, R31
+ OR $KZERO, R31 /* return PC in KSEG0 */
+ MOVW R31, SPR(SRR0)
+ OR $KZERO, R1 /* fix stack pointer */
+ RFI /* returns to trap handler */
+
+/*
+ * restore state from Ureg and return from trap/interrupt
+ */
+TEXT forkret(SB), $0
+ BR restoreureg
+
+restoreureg:
+ MOVMW 48(R1), R2 /* r2:r31 */
+ /* defer R1 */
+ MOVW 40(R1), R0
+ MOVW R0, SPR(SAVER0)
+ MOVW 36(R1), R0
+ MOVW R0, CTR
+ MOVW 32(R1), R0
+ MOVW R0, XER
+ MOVW 28(R1), R0
+ MOVW R0, CR /* CR */
+ MOVW 24(R1), R0
+ MOVW R0, LR
+ /* pad, skip */
+ MOVW 16(R1), R0
+ MOVW R0, SPR(SRR0) /* old PC */
+ MOVW 12(R1), R0
+ MOVW R0, SPR(SRR1) /* old MSR */
+ /* cause, skip */
+ MOVW 44(R1), R1 /* old SP */
+ MOVW SPR(SAVER0), R0
+ RFI
+
+TEXT fpsave(SB), $0
+ FMOVD F0, (0*8)(R3)
+ FMOVD F1, (1*8)(R3)
+ FMOVD F2, (2*8)(R3)
+ FMOVD F3, (3*8)(R3)
+ FMOVD F4, (4*8)(R3)
+ FMOVD F5, (5*8)(R3)
+ FMOVD F6, (6*8)(R3)
+ FMOVD F7, (7*8)(R3)
+ FMOVD F8, (8*8)(R3)
+ FMOVD F9, (9*8)(R3)
+ FMOVD F10, (10*8)(R3)
+ FMOVD F11, (11*8)(R3)
+ FMOVD F12, (12*8)(R3)
+ FMOVD F13, (13*8)(R3)
+ FMOVD F14, (14*8)(R3)
+ FMOVD F15, (15*8)(R3)
+ FMOVD F16, (16*8)(R3)
+ FMOVD F17, (17*8)(R3)
+ FMOVD F18, (18*8)(R3)
+ FMOVD F19, (19*8)(R3)
+ FMOVD F20, (20*8)(R3)
+ FMOVD F21, (21*8)(R3)
+ FMOVD F22, (22*8)(R3)
+ FMOVD F23, (23*8)(R3)
+ FMOVD F24, (24*8)(R3)
+ FMOVD F25, (25*8)(R3)
+ FMOVD F26, (26*8)(R3)
+ FMOVD F27, (27*8)(R3)
+ FMOVD F28, (28*8)(R3)
+ FMOVD F29, (29*8)(R3)
+ FMOVD F30, (30*8)(R3)
+ FMOVD F31, (31*8)(R3)
+ MOVFL FPSCR, F0
+ FMOVD F0, (32*8)(R3)
+ RETURN
+
+TEXT fprestore(SB), $0
+ FMOVD (32*8)(R3), F0
+ MOVFL F0, FPSCR
+ FMOVD (0*8)(R3), F0
+ FMOVD (1*8)(R3), F1
+ FMOVD (2*8)(R3), F2
+ FMOVD (3*8)(R3), F3
+ FMOVD (4*8)(R3), F4
+ FMOVD (5*8)(R3), F5
+ FMOVD (6*8)(R3), F6
+ FMOVD (7*8)(R3), F7
+ FMOVD (8*8)(R3), F8
+ FMOVD (9*8)(R3), F9
+ FMOVD (10*8)(R3), F10
+ FMOVD (11*8)(R3), F11
+ FMOVD (12*8)(R3), F12
+ FMOVD (13*8)(R3), F13
+ FMOVD (14*8)(R3), F14
+ FMOVD (15*8)(R3), F15
+ FMOVD (16*8)(R3), F16
+ FMOVD (17*8)(R3), F17
+ FMOVD (18*8)(R3), F18
+ FMOVD (19*8)(R3), F19
+ FMOVD (20*8)(R3), F20
+ FMOVD (21*8)(R3), F21
+ FMOVD (22*8)(R3), F22
+ FMOVD (23*8)(R3), F23
+ FMOVD (24*8)(R3), F24
+ FMOVD (25*8)(R3), F25
+ FMOVD (26*8)(R3), F26
+ FMOVD (27*8)(R3), F27
+ FMOVD (28*8)(R3), F28
+ FMOVD (29*8)(R3), F29
+ FMOVD (30*8)(R3), F30
+ FMOVD (31*8)(R3), F31
+ RETURN
diff --git a/sys/src/9/mtx/main.c b/sys/src/9/mtx/main.c
new file mode 100755
index 000000000..eebad4444
--- /dev/null
+++ b/sys/src/9/mtx/main.c
@@ -0,0 +1,465 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "init.h"
+#include "pool.h"
+
+Conf conf;
+FPsave initfp;
+
+void
+main(void)
+{
+ memset(edata, 0, (ulong)end-(ulong)edata);
+ conf.nmach = 1;
+ machinit();
+ ioinit();
+ i8250console();
+ quotefmtinstall();
+ print("\nPlan 9\n");
+ confinit();
+ xinit();
+ raveninit();
+ trapinit();
+ printinit();
+ cpuidprint();
+ mmuinit();
+ hwintrinit();
+ clockinit();
+ kbdinit();
+ procinit0();
+ initseg();
+ timersinit();
+ links();
+ chandevreset();
+ pageinit();
+ swapinit();
+ fpsave(&initfp);
+ initfp.fpscr = 0;
+ userinit();
+ schedinit();
+}
+
+void
+machinit(void)
+{
+ memset(m, 0, sizeof(Mach));
+ m->cputype = getpvr()>>16;
+
+ /*
+ * For polled uart output at boot, need
+ * a default delay constant. 100000 should
+ * be enough for a while. Cpuidentify will
+ * calculate the real value later.
+ */
+ m->loopconst = 100000;
+
+ /* turn on caches */
+ puthid0(gethid0() | BIT(16) | BIT(17));
+
+ active.machs = 1;
+ active.exiting = 0;
+}
+
+void
+cpuidprint(void)
+{
+ char *id;
+
+ id = "unknown PowerPC";
+ switch(m->cputype) {
+ case 9:
+ id = "PowerPC 604e";
+ break;
+ }
+ print("cpu0: %s\n", id);
+}
+
+static struct
+{
+ char *name;
+ char *val;
+}
+plan9ini[] =
+{
+ { "console", "0" },
+ { "ether0", "type=2114x" },
+};
+
+char*
+getconf(char *name)
+{
+ int i;
+
+ for(i = 0; i < nelem(plan9ini); i++)
+ if(cistrcmp(name, plan9ini[i].name) == 0)
+ return plan9ini[i].val;
+ return nil;
+}
+
+void
+init0(void)
+{
+// char **p, *q, name[KNAMELEN];
+// int n;
+ char buf[2*KNAMELEN];
+
+ up->nerrlab = 0;
+
+ spllo();
+
+ /*
+ * These are o.k. because rootinit is null.
+ * Then early kproc's will have a root and dot.
+ */
+ up->slash = namec("#/", Atodir, 0, 0);
+ pathclose(up->slash->path);
+ up->slash->path = newpath("/");
+ up->dot = cclone(up->slash);
+
+ chandevinit();
+
+ if(!waserror()){
+ snprint(buf, sizeof(buf), "power %s mtx", conffile);
+ ksetenv("terminal", buf, 0);
+ ksetenv("cputype", "power", 0);
+ if(cpuserver)
+ ksetenv("service", "cpu", 0);
+ else
+ ksetenv("service", "terminal", 0);
+
+/*
+ for(p = confenv; *p; p++) {
+ q = strchr(p[0], '=');
+ if(q == 0)
+ continue;
+ n = q-p[0];
+ if(n >= KNAMELEN)
+ n = KNAMELEN-1;
+ memmove(name, p[0], n);
+ name[n] = 0;
+ if(name[0] != '*')
+ ksetenv(name, q+1, 0);
+ ksetenv(name, q+1, 1);
+ }
+*/
+ poperror();
+ }
+ kproc("alarm", alarmkproc, 0);
+ kproc("mmusweep", mmusweep, 0);
+ touser((void*)(USTKTOP-8));
+}
+
+void
+userinit(void)
+{
+ Proc *p;
+ Segment *s;
+ KMap *k;
+ Page *pg;
+
+ p = newproc();
+ p->pgrp = newpgrp();
+ p->egrp = smalloc(sizeof(Egrp));
+ p->egrp->ref = 1;
+ p->fgrp = dupfgrp(nil);
+ p->rgrp = newrgrp();
+ p->procmode = 0640;
+
+ kstrdup(&eve, "");
+ kstrdup(&p->text, "*init*");
+ kstrdup(&p->user, eve);
+
+ p->fpstate = FPinit;
+
+ /*
+ * Kernel Stack
+ *
+ * N.B. The -12 for the stack pointer is important.
+ * 4 bytes for gotolabel's return PC
+ */
+ p->sched.pc = (ulong)init0;
+ p->sched.sp = (ulong)p->kstack+KSTACK-(sizeof(Sargs)+BY2WD);
+
+ /*
+ * User Stack
+ */
+ s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG);
+ p->seg[SSEG] = s;
+ pg = newpage(1, 0, USTKTOP-BY2PG);
+ segpage(s, pg);
+
+ /*
+ * Text
+ */
+ s = newseg(SG_TEXT, UTZERO, 1);
+ s->flushme++;
+ p->seg[TSEG] = s;
+ pg = newpage(1, 0, UTZERO);
+ memset(pg->cachectl, PG_TXTFLUSH, sizeof(pg->cachectl));
+ segpage(s, pg);
+ k = kmap(s->map[0]->pages[0]);
+ memmove((ulong*)VA(k), initcode, sizeof initcode);
+ kunmap(k);
+
+ ready(p);
+}
+
+/* still to do */
+void
+reboot(void*, void*, ulong)
+{
+ exit(0);
+}
+
+void
+exit(int ispanic)
+{
+ int ms, once;
+
+ lock(&active);
+ if(ispanic)
+ active.ispanic = ispanic;
+ else if(m->machno == 0 && (active.machs & (1<<m->machno)) == 0)
+ active.ispanic = 0;
+ once = active.machs & (1<<m->machno);
+ active.machs &= ~(1<<m->machno);
+ active.exiting = 1;
+ unlock(&active);
+
+ if(once)
+ print("cpu%d: exiting\n", m->machno);
+ spllo();
+ for(ms = 5*1000; ms > 0; ms -= TK2MS(2)){
+ delay(TK2MS(2));
+ if(active.machs == 0 && consactive() == 0)
+ break;
+ }
+
+ if(active.ispanic && m->machno == 0){
+ if(cpuserver)
+ delay(10000);
+ else if(conf.monitor)
+ for(;;);
+ }
+ else
+ delay(1000);
+
+ watchreset();
+}
+
+/*
+ * set up floating point for a new process
+ */
+void
+procsetup(Proc *p)
+{
+ p->fpstate = FPinit;
+}
+
+/*
+ * Save the mach dependent part of the process state.
+ */
+void
+procsave(Proc *p)
+{
+ if(p->fpstate == FPactive){
+ if(p->state != Moribund)
+ fpsave(&up->fpsave);
+ p->fpstate = FPinactive;
+ }
+}
+
+void
+confinit(void)
+{
+ char *p;
+ int userpcnt;
+ ulong pa, kpages;
+ extern ulong memsize; /* passed in from ROM monitor */
+
+ if(p = getconf("*kernelpercent"))
+ userpcnt = 100 - strtol(p, 0, 0);
+ else
+ userpcnt = 0;
+
+ pa = PGROUND(PADDR(end));
+
+ conf.mem[0].npage = memsize/BY2PG;
+ conf.mem[0].base = pa;
+ conf.npage = conf.mem[0].npage;
+
+ conf.nmach = 1;
+ conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
+ if(cpuserver)
+ conf.nproc *= 3;
+ if(conf.nproc > 2000)
+ conf.nproc = 2000;
+ conf.nimage = 200;
+ conf.nswap = conf.nproc*80;
+ conf.nswppo = 4096;
+ conf.copymode = 0; /* copy on write */
+
+ if(cpuserver) {
+ if(userpcnt < 10)
+ userpcnt = 70;
+ kpages = conf.npage - (conf.npage*userpcnt)/100;
+
+ /*
+ * Hack for the big boys. Only good while physmem < 4GB.
+ * Give the kernel a max. of 16MB + enough to allocate the
+ * page pool.
+ * This is an overestimate as conf.upages < conf.npages.
+ * The patch of nimage is a band-aid, scanning the whole
+ * page list in imagereclaim just takes too long.
+ */
+ if(kpages > (16*MB + conf.npage*sizeof(Page))/BY2PG){
+ kpages = (16*MB + conf.npage*sizeof(Page))/BY2PG;
+ conf.nimage = 2000;
+ kpages += (conf.nproc*KSTACK)/BY2PG;
+ }
+ } else {
+ if(userpcnt < 10) {
+ if(conf.npage*BY2PG < 16*MB)
+ userpcnt = 40;
+ else
+ userpcnt = 60;
+ }
+ kpages = conf.npage - (conf.npage*userpcnt)/100;
+
+ /*
+ * Make sure terminals with low memory get at least
+ * 4MB on the first Image chunk allocation.
+ */
+ if(conf.npage*BY2PG < 16*MB)
+ imagmem->minarena = 4*1024*1024;
+ }
+ conf.upages = conf.npage - kpages;
+ conf.ialloc = (kpages/2)*BY2PG;
+
+ /*
+ * Guess how much is taken by the large permanent
+ * datastructures. Mntcache and Mntrpc are not accounted for
+ * (probably ~300KB).
+ */
+ kpages *= BY2PG;
+ kpages -= conf.upages*sizeof(Page)
+ + conf.nproc*sizeof(Proc)
+ + conf.nimage*sizeof(Image)
+ + conf.nswap
+ + conf.nswppo*sizeof(Page);
+ mainmem->maxsize = kpages;
+ if(!cpuserver){
+ /*
+ * give terminals lots of image memory, too; the dynamic
+ * allocation will balance the load properly, hopefully.
+ * be careful with 32-bit overflow.
+ */
+ imagmem->maxsize = kpages;
+ }
+
+// conf.monitor = 1; /* BUG */
+}
+
+static int
+getcfields(char* lp, char** fields, int n, char* sep)
+{
+ int i;
+
+ for(i = 0; lp && *lp && i < n; i++){
+ while(*lp && strchr(sep, *lp) != 0)
+ *lp++ = 0;
+ if(*lp == 0)
+ break;
+ fields[i] = lp;
+ while(*lp && strchr(sep, *lp) == 0){
+ if(*lp == '\\' && *(lp+1) == '\n')
+ *lp++ = ' ';
+ lp++;
+ }
+ }
+
+ return i;
+}
+
+int
+isaconfig(char *class, int ctlrno, ISAConf *isa)
+{
+ int i;
+ char cc[KNAMELEN], *p;
+
+ sprint(cc, "%s%d", class, ctlrno);
+
+ p = getconf(cc);
+ if(p == 0)
+ return 0;
+ isa->nopt = tokenize(p, isa->opt, NISAOPT);
+ for(i = 0; i < isa->nopt; i++){
+ p = isa->opt[i];
+ if(cistrncmp(p, "type=", 5) == 0)
+ isa->type = p + 5;
+ else if(cistrncmp(p, "port=", 5) == 0)
+ isa->port = strtoul(p+5, &p, 0);
+ else if(cistrncmp(p, "irq=", 4) == 0)
+ isa->irq = strtoul(p+4, &p, 0);
+ else if(cistrncmp(p, "dma=", 4) == 0)
+ isa->dma = strtoul(p+4, &p, 0);
+ else if(cistrncmp(p, "mem=", 4) == 0)
+ isa->mem = strtoul(p+4, &p, 0);
+ else if(cistrncmp(p, "size=", 5) == 0)
+ isa->size = strtoul(p+5, &p, 0);
+ else if(cistrncmp(p, "freq=", 5) == 0)
+ isa->freq = strtoul(p+5, &p, 0);
+ }
+ return 1;
+}
+
+int
+cistrcmp(char *a, char *b)
+{
+ int ac, bc;
+
+ for(;;){
+ ac = *a++;
+ bc = *b++;
+
+ if(ac >= 'A' && ac <= 'Z')
+ ac = 'a' + (ac - 'A');
+ if(bc >= 'A' && bc <= 'Z')
+ bc = 'a' + (bc - 'A');
+ ac -= bc;
+ if(ac)
+ return ac;
+ if(bc == 0)
+ break;
+ }
+ return 0;
+}
+
+int
+cistrncmp(char *a, char *b, int n)
+{
+ unsigned ac, bc;
+
+ while(n > 0){
+ ac = *a++;
+ bc = *b++;
+ n--;
+
+ if(ac >= 'A' && ac <= 'Z')
+ ac = 'a' + (ac - 'A');
+ if(bc >= 'A' && bc <= 'Z')
+ bc = 'a' + (bc - 'A');
+
+ ac -= bc;
+ if(ac)
+ return ac;
+ if(bc == 0)
+ break;
+ }
+
+ return 0;
+}
diff --git a/sys/src/9/mtx/mem.h b/sys/src/9/mtx/mem.h
new file mode 100755
index 000000000..05c01ff8a
--- /dev/null
+++ b/sys/src/9/mtx/mem.h
@@ -0,0 +1,195 @@
+/*
+ * Memory and machine-specific definitions. Used in C and assembler.
+ */
+
+/*
+ * Sizes
+ */
+
+#define BI2BY 8 /* bits per byte */
+#define BI2WD 32 /* bits per word */
+#define BY2WD 4 /* bytes per word */
+#define BY2V 8 /* bytes per vlong */
+#define BY2PG 4096 /* bytes per page */
+#define WD2PG (BY2PG/BY2WD) /* words per page */
+#define PGSHIFT 12 /* log(BY2PG) */
+#define ROUND(s, sz) (((s)+(sz-1))&~(sz-1))
+#define PGROUND(s) ROUND(s, BY2PG)
+#define CACHELINELOG 4
+#define CACHELINESZ (1<<CACHELINELOG)
+#define BLOCKALIGN CACHELINESZ
+
+#define MHz 1000000
+
+#define BY2PTE 8 /* bytes per pte entry */
+#define BY2PTEG 64 /* bytes per pte group */
+
+#define MAXMACH 1 /* max # cpus system can run */
+#define MACHSIZE BY2PG
+#define KSTACK 4096 /* Size of kernel stack */
+
+/*
+ * Time
+ */
+#define HZ 100 /* clock frequency */
+#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */
+
+/*
+ * Standard PPC Special Purpose Registers (OEA and VEA)
+ */
+#define DSISR 18
+#define DAR 19 /* Data Address Register */
+#define DEC 22 /* Decrementer */
+#define SDR1 25
+#define SRR0 26 /* Saved Registers (exception) */
+#define SRR1 27
+#define SPRG0 272 /* Supervisor Private Registers */
+#define SPRG1 273
+#define SPRG2 274
+#define SPRG3 275
+#define ASR 280 /* Address Space Register */
+#define EAR 282 /* External Access Register (optional) */
+#define TBRU 269 /* Time base Upper/Lower (Reading) */
+#define TBRL 268
+#define TBWU 284 /* Time base Upper/Lower (Writing) */
+#define TBWL 285
+#define PVR 287 /* Processor Version */
+#define IABR 1010 /* Instruction Address Breakpoint Register (optional) */
+#define DABR 1013 /* Data Address Breakpoint Register (optional) */
+#define FPECR 1022 /* Floating-Point Exception Cause Register (optional) */
+#define PIR 1023 /* Processor Identification Register (optional) */
+
+#define IBATU(i) (528+2*(i)) /* Instruction BAT register (upper) */
+#define IBATL(i) (529+2*(i)) /* Instruction BAT register (lower) */
+#define DBATU(i) (536+2*(i)) /* Data BAT register (upper) */
+#define DBATL(i) (537+2*(i)) /* Data BAT register (lower) */
+
+/*
+ * PPC604e-specific Special Purpose Registers (OEA)
+ */
+#define HID0 1008 /* Hardware Implementation Dependant Register 0 */
+#define HID1 1009 /* Hardware Implementation Dependant Register 1 */
+#define PMC1 953 /* Performance Monitor Counter 1 */
+#define PMC2 954 /* Performance Monitor Counter 2 */
+#define PMC3 957 /* Performance Monitor Counter 3 */
+#define PMC4 958 /* Performance Monitor Counter 4 */
+#define MMCR0 952 /* Monitor Control Register 0 */
+#define MMCR1 956 /* Monitor Control Register 0 */
+#define SIA 955 /* Sampled Instruction Address */
+#define SDA 959 /* Sampled Data Address */
+
+#define BIT(i) (1<<(31-(i))) /* Silly backwards register bit numbering scheme */
+
+/*
+ * Bit encodings for Machine State Register (MSR)
+ */
+#define MSR_POW BIT(13) /* Enable Power Management */
+#define MSR_ILE BIT(15) /* Interrupt Little-Endian enable */
+#define MSR_EE BIT(16) /* External Interrupt enable */
+#define MSR_PR BIT(17) /* Supervisor/User privelege */
+#define MSR_FP BIT(18) /* Floating Point enable */
+#define MSR_ME BIT(19) /* Machine Check enable */
+#define MSR_FE0 BIT(20) /* Floating Exception mode 0 */
+#define MSR_SE BIT(21) /* Single Step (optional) */
+#define MSR_BE BIT(22) /* Branch Trace (optional) */
+#define MSR_FE1 BIT(23) /* Floating Exception mode 1 */
+#define MSR_IP BIT(25) /* Exception prefix 0x000/0xFFF */
+#define MSR_IR BIT(26) /* Instruction MMU enable */
+#define MSR_DR BIT(27) /* Data MMU enable */
+#define MSR_PM BIT(29) /* Performance Monitor marked mode (604e specific) */
+#define MSR_RI BIT(30) /* Recoverable Exception */
+#define MSR_LE BIT(31) /* Little-Endian enable */
+
+/*
+ * Exception codes (trap vectors)
+ */
+#define CRESET 0x01
+#define CMCHECK 0x02
+#define CDSI 0x03
+#define CISI 0x04
+#define CEI 0x05
+#define CALIGN 0x06
+#define CPROG 0x07
+#define CFPU 0x08
+#define CDEC 0x09
+#define CSYSCALL 0x0C
+#define CTRACE 0x0D /* optional */
+#define CFPA 0x0E /* optional */
+
+/* PPC604e-specific: */
+#define CPERF 0x0F /* performance monitoring */
+#define CIBREAK 0x13
+#define CSMI 0x14
+
+/*
+ * Magic registers
+ */
+
+#define MACH 30 /* R30 is m-> */
+#define USER 29 /* R29 is up-> */
+
+
+/*
+ * virtual MMU
+ */
+#define PTEMAPMEM (1024*1024)
+#define PTEPERTAB (PTEMAPMEM/BY2PG)
+#define SEGMAPSIZE 1984
+#define SSEGMAPSIZE 16
+#define PPN(x) ((x)&~(BY2PG-1))
+
+/*
+ * First pte word
+ */
+#define PTE0(v, vsid, h, va) (((v)<<31)|((vsid)<<7)|((h)<<6)|(((va)>>22)&0x3f))
+
+/*
+ * Second pte word; WIMG & PP(RW/RO) common to page table and BATs
+ */
+#define PTE1_W BIT(25)
+#define PTE1_I BIT(26)
+#define PTE1_M BIT(27)
+#define PTE1_G BIT(28)
+
+#define PTE1_RW BIT(30)
+#define PTE1_RO BIT(31)
+
+/*
+ * PTE bits for fault.c. These belong to the second PTE word. Validity is
+ * implied for putmmu(), and we always set PTE0_V. PTEVALID is used
+ * here to set cache policy bits on a global basis.
+ */
+#define PTEVALID 0
+#define PTEWRITE PTE1_RW
+#define PTERONLY PTE1_RO
+#define PTEUNCACHED PTE1_I
+
+/*
+ * Address spaces
+ */
+
+#define UZERO 0 /* base of user address space */
+#define UTZERO (UZERO+BY2PG) /* first address in user text */
+#define USTKTOP (TSTKTOP-TSTKSIZ*BY2PG) /* byte just beyond user stack */
+#define TSTKTOP KZERO /* top of temporary stack */
+#define TSTKSIZ 100
+#define KZERO 0x80000000 /* base of kernel address space */
+#define KTZERO (KZERO+0x4000) /* first address in kernel text */
+#define USTKSIZE (4*1024*1024) /* size of user stack */
+#define UREGSIZE ((8+32)*4)
+
+#define PCIMEM0 0xf0000000
+#define PCISIZE0 0x0e000000
+#define PCIMEM1 0xc0000000
+#define PCISIZE1 0x30000000
+#define IOMEM 0xfe000000
+#define IOSIZE 0x00800000
+#define FALCON 0xfef80000
+#define RAVEN 0xfeff0000
+#define FLASHA 0xff000000
+#define FLASHB 0xff800000
+#define FLASHAorB 0xfff00000
+
+#define isphys(x) (((ulong)x&KZERO)!=0)
+
+#define getpgcolor(a) 0
diff --git a/sys/src/9/mtx/mkfile b/sys/src/9/mtx/mkfile
new file mode 100755
index 000000000..af7f50f02
--- /dev/null
+++ b/sys/src/9/mtx/mkfile
@@ -0,0 +1,96 @@
+CONF=mtx
+CONFLIST=mtx mtxcpu
+
+objtype=power
+</$objtype/mkfile
+p=9
+
+DEVS=`{rc ../port/mkdevlist $CONF}
+
+PORT=\
+ alarm.$O\
+ alloc.$O\
+ allocb.$O\
+ auth.$O\
+ cache.$O\
+ chan.$O\
+ dev.$O\
+ edf.$O\
+ fault.$O\
+ latin1.$O\
+ log.$O\
+ mul64fract.$O\
+ rebootcmd.$O\
+ page.$O\
+ parse.$O\
+ pgrp.$O\
+ portclock.$O\
+ print.$O\
+ proc.$O\
+ qio.$O\
+ qlock.$O\
+ rdb.$O\
+ segment.$O\
+ swap.$O\
+ sysfile.$O\
+ sysproc.$O\
+ taslock.$O\
+ tod.$O\
+ xalloc.$O\
+
+OBJ=\
+ l.$O\
+ inb.$O\
+ clock.$O\
+ i8259.$O\
+ kbd.$O\
+ main.$O\
+ mmu.$O\
+ random.$O\
+ raven.$O\
+ trap.$O\
+ $CONF.root.$O\
+ $CONF.rootc.$O\
+ $DEVS\
+ $PORT\
+
+LIB=\
+ /$objtype/lib/libmemlayer.a\
+ /$objtype/lib/libmemdraw.a\
+ /$objtype/lib/libdraw.a\
+ /$objtype/lib/libip.a\
+ /$objtype/lib/libc.a\
+ /$objtype/lib/libsec.a\
+
+ETHER=`{echo devether.c ether*.c | sed 's/\.c/.'$O'/g'}
+VGA=`{echo devvga.c screen.c vga*.c | sed 's/\.c/.'$O'/g'}
+SDEV=`{echo devsd.c sd*.c | sed 's/\.c/.'$O'/g'}
+
+loadaddr = 0x80004020
+
+$p$CONF: $CONF.c $OBJ $LIB
+ $CC $CFLAGS '-DKERNDATE='`{date -n} $CONF.c
+ $LD -R4 -o $target -T$loadaddr -l $OBJ $CONF.$O $LIB
+ ls -l $target
+
+install:V: $p$CONF
+ cp $p$CONF /$objtype/$p$CONF
+
+<../boot/bootmkfile
+<../port/portmkfile
+<|../port/mkbootrules $CONF
+
+clock.$O: /$objtype/include/ureg.h
+devether.$O: /$objtype/include/ureg.h
+main.$O: /$objtype/include/ureg.h errstr.h init.h
+trap.$O: /$objtype/include/ureg.h
+
+$ETHER: etherif.h ../port/netif.h
+
+init.h: initcode /sys/src/libc/9syscall/sys.h
+ $AS initcode
+ $LD -l -s -R4 -o init.out initcode.$O -lc
+ {echo 'uchar initcode[]={'
+ xd -r -1x init.out |
+ sed -e 's/^[0-9a-f]+ //' -e 's/ ([0-9a-f][0-9a-f])/0x\1,/g'
+ echo '};'} > init.h
diff --git a/sys/src/9/mtx/mmu.c b/sys/src/9/mtx/mmu.c
new file mode 100755
index 000000000..c43a1a7dd
--- /dev/null
+++ b/sys/src/9/mtx/mmu.c
@@ -0,0 +1,257 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+/*
+ * We have one page table per processor.
+ *
+ * Different processes are distinguished via the VSID field in
+ * the segment registers. As flushing the entire page table is an
+ * expensive operation, we implement an aging algorithm for
+ * mmu pids, with a background kproc to purge stale pids en mass.
+ *
+ * This needs modifications to run on a multiprocessor.
+ */
+
+static ulong ptabsize; /* number of bytes in page table */
+static ulong ptabmask; /* hash mask */
+
+/*
+ * VSID is 24 bits. 3 are required to distinguish segments in user
+ * space (kernel space only uses the BATs). pid 0 is reserved.
+ * The top 2 bits of the pid are used as a `color' for the background
+ * pid reclaimation algorithm.
+ */
+
+enum {
+ PIDBASE = 1,
+ PIDBITS = 21,
+ COLBITS = 2,
+ PIDMAX = ((1<<PIDBITS)-1),
+ COLMASK = ((1<<COLBITS)-1),
+};
+
+#define VSID(pid, i) (((pid)<<3)|i)
+#define PIDCOLOR(pid) ((pid)>>(PIDBITS-COLBITS))
+#define PTECOL(color) PTE0(1, VSID(((color)<<(PIDBITS-COLBITS)), 0), 0, 0)
+
+void
+mmuinit(void)
+{
+ int lhash, mem;
+ extern ulong memsize; /* passed in from ROM monitor */
+
+ if(ptabsize == 0) {
+ /* heuristically size the hash table */
+ lhash = 10;
+ mem = (1<<23);
+ while(mem < memsize) {
+ lhash++;
+ mem <<= 1;
+ }
+ ptabsize = (1<<(lhash+6));
+ ptabmask = (1<<lhash)-1;
+ }
+
+ m->ptabbase = (ulong)xspanalloc(ptabsize, 0, ptabsize);
+ putsdr1(PADDR(m->ptabbase) | (ptabmask>>10));
+ m->mmupid = PIDBASE;
+ m->sweepcolor = 0;
+ m->trigcolor = COLMASK;
+}
+
+static int
+work(void*)
+{
+ return PIDCOLOR(m->mmupid) == m->trigcolor;
+}
+
+void
+mmusweep(void*)
+{
+ Proc *p;
+ int i, x, sweepcolor;
+ ulong *ptab, *ptabend, ptecol;
+
+ for(;;) {
+ if(PIDCOLOR(m->mmupid) != m->trigcolor)
+ sleep(&m->sweepr, work, nil);
+
+ sweepcolor = m->sweepcolor;
+ x = splhi();
+ p = proctab(0);
+ for(i = 0; i < conf.nproc; i++, p++)
+ if(PIDCOLOR(p->mmupid) == sweepcolor)
+ p->mmupid = 0;
+ splx(x);
+
+ ptab = (ulong*)m->ptabbase;
+ ptabend = (ulong*)(m->ptabbase+ptabsize);
+ ptecol = PTECOL(sweepcolor);
+ while(ptab < ptabend) {
+ if((*ptab & PTECOL(3)) == ptecol)
+ *ptab = 0;
+ ptab += 2;
+ }
+ tlbflushall();
+
+ m->sweepcolor = (sweepcolor+1) & COLMASK;
+ m->trigcolor = (m->trigcolor+1) & COLMASK;
+ }
+}
+
+int
+newmmupid(void)
+{
+ int pid, newcolor;
+
+ pid = m->mmupid++;
+ if(m->mmupid > PIDMAX)
+ m->mmupid = PIDBASE;
+ newcolor = PIDCOLOR(m->mmupid);
+ if(newcolor != PIDCOLOR(pid)) {
+ if(newcolor == m->sweepcolor) {
+ /* desperation time. can't block here. punt to fault/putmmu */
+ print("newmmupid: %uld: no free mmu pids\n", up->pid);
+ if(m->mmupid == PIDBASE)
+ m->mmupid = PIDMAX;
+ else
+ m->mmupid--;
+ pid = 0;
+ }
+ else if(newcolor == m->trigcolor)
+ wakeup(&m->sweepr);
+ }
+ up->mmupid = pid;
+ return pid;
+}
+
+void
+flushmmu(void)
+{
+ int x;
+
+ x = splhi();
+ up->newtlb = 1;
+ mmuswitch(up);
+ splx(x);
+}
+
+/*
+ * called with splhi
+ */
+void
+mmuswitch(Proc *p)
+{
+ int i, mp;
+
+ if(p->kp) {
+ for(i = 0; i < 8; i++)
+ putsr(i<<28, 0);
+ return;
+ }
+
+ if(p->newtlb) {
+ p->mmupid = 0;
+ p->newtlb = 0;
+ }
+ mp = p->mmupid;
+ if(mp == 0)
+ mp = newmmupid();
+
+ for(i = 0; i < 8; i++)
+ putsr(i<<28, VSID(mp, i)|BIT(1)|BIT(2));
+}
+
+void
+mmurelease(Proc* p)
+{
+ p->mmupid = 0;
+}
+
+void
+putmmu(ulong va, ulong pa, Page *pg)
+{
+ int mp;
+ char *ctl;
+ ulong *p, *ep, *q, pteg;
+ ulong vsid, ptehi, x, hash;
+
+ /*
+ * If mmupid is 0, mmuswitch/newmmupid was unable to assign us
+ * a pid, hence we faulted. Keep calling sched() until the mmusweep
+ * proc catches up, and we are able to get a pid.
+ */
+ while((mp = up->mmupid) == 0)
+ sched();
+
+ vsid = VSID(mp, va>>28);
+ hash = (vsid ^ (va>>12)&0xffff) & ptabmask;
+ ptehi = PTE0(1, vsid, 0, va);
+
+ pteg = m->ptabbase + BY2PTEG*hash;
+ p = (ulong*)pteg;
+ ep = (ulong*)(pteg+BY2PTEG);
+ q = nil;
+ tlbflush(va);
+ while(p < ep) {
+ x = p[0];
+ if(x == ptehi) {
+ q = p;
+ break;
+ }
+ if(q == nil && (x & BIT(0)) == 0)
+ q = p;
+ p += 2;
+ }
+ if(q == nil) {
+ q = (ulong*)(pteg+m->slotgen);
+ m->slotgen = (m->slotgen + BY2PTE) & (BY2PTEG-1);
+ }
+ q[0] = ptehi;
+ q[1] = pa;
+ sync();
+
+ ctl = &pg->cachectl[m->machno];
+ switch(*ctl) {
+ case PG_NEWCOL:
+ default:
+ panic("putmmu: %d\n", *ctl);
+ break;
+ case PG_NOFLUSH:
+ break;
+ case PG_TXTFLUSH:
+ dcflush((void*)pg->va, BY2PG);
+ icflush((void*)pg->va, BY2PG);
+ *ctl = PG_NOFLUSH;
+ break;
+ }
+}
+
+void
+checkmmu(ulong, ulong)
+{
+}
+
+void
+countpagerefs(ulong*, int)
+{
+}
+
+/*
+ * Return the number of bytes that can be accessed via KADDR(pa).
+ * If pa is not a valid argument to KADDR, return 0.
+ */
+ulong
+cankaddr(ulong pa)
+{
+ ulong kzero;
+
+ kzero = -KZERO;
+ if(pa >= kzero)
+ return 0;
+ return kzero - pa;
+}
diff --git a/sys/src/9/mtx/mtx b/sys/src/9/mtx/mtx
new file mode 100755
index 000000000..47c2441f5
--- /dev/null
+++ b/sys/src/9/mtx/mtx
@@ -0,0 +1,46 @@
+dev
+ root
+ cons
+ arch
+ pnp pci
+ env
+ pipe
+ proc
+ mnt
+ srv
+ dup
+ ssl
+ cap
+ kprof
+ uart
+ rtc
+
+ ether netif
+ ip arp chandial ip ipv6 ipaux iproute netif netlog nullmedium pktmedium ptclbsum inferno
+
+link
+ ether2114x pci
+ ethermedium
+ netdevmedium
+
+misc
+ uarti8250
+
+ip
+ tcp
+ udp
+ ipifc
+ icmp
+ icmp6
+
+port
+ int cpuserver = 0;
+
+boot
+ tcp
+
+bootdir
+ bootmtx.out boot
+ /power/bin/ip/ipconfig
+ /power/bin/auth/factotum
+
diff --git a/sys/src/9/mtx/mtxcpu b/sys/src/9/mtx/mtxcpu
new file mode 100755
index 000000000..b64090725
--- /dev/null
+++ b/sys/src/9/mtx/mtxcpu
@@ -0,0 +1,46 @@
+dev
+ root
+ cons
+ arch
+ pnp pci
+ env
+ pipe
+ proc
+ mnt
+ srv
+ dup
+ ssl
+ cap
+ kprof
+ uart
+ rtc
+
+ ether netif
+ ip arp chandial ip ipv6 ipaux iproute netif netlog nullmedium pktmedium ptclbsum inferno
+
+link
+ ether2114x pci
+ ethermedium
+ netdevmedium
+
+misc
+ uarti8250
+
+ip
+ tcp
+ udp
+ ipifc
+ icmp
+ icmp6
+
+port
+ int cpuserver = 1;
+
+boot cpu
+ tcp
+
+bootdir
+ bootmtxcpu.out boot
+ ipconfig.hack ipconfig
+ factotum.hack factotum
+
diff --git a/sys/src/9/mtx/pci.c b/sys/src/9/mtx/pci.c
new file mode 100755
index 000000000..73c4d641e
--- /dev/null
+++ b/sys/src/9/mtx/pci.c
@@ -0,0 +1,908 @@
+/*
+ * PCI support code.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#define DBG if(0) pcilog
+
+struct
+{
+ char output[16384];
+ int ptr;
+}PCICONS;
+
+int
+pcilog(char *fmt, ...)
+{
+ int n;
+ va_list arg;
+ char buf[PRINTSIZE];
+
+ va_start(arg, fmt);
+ n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
+ va_end(arg);
+
+ memmove(PCICONS.output+PCICONS.ptr, buf, n);
+ PCICONS.ptr += n;
+ return n;
+}
+
+enum
+{ /* configuration mechanism #1 */
+ PciADDR = 0xCF8, /* CONFIG_ADDRESS */
+ PciDATA = 0xCFC, /* CONFIG_DATA */
+
+ /* configuration mechanism #2 */
+ PciCSE = 0xCF8, /* configuration space enable */
+ PciFORWARD = 0xCFA, /* which bus */
+
+ MaxFNO = 7,
+ MaxUBN = 255,
+};
+
+enum
+{ /* command register */
+ IOen = (1<<0),
+ MEMen = (1<<1),
+ MASen = (1<<2),
+ MemWrInv = (1<<4),
+ PErrEn = (1<<6),
+ SErrEn = (1<<8),
+};
+
+static Lock pcicfglock;
+static QLock pcicfginitlock;
+static int pcicfgmode = -1;
+static int pcimaxbno = 7;
+static int pcimaxdno;
+static Pcidev* pciroot;
+static Pcidev* pcilist;
+static Pcidev* pcitail;
+
+static int pcicfgrw32(int, int, int, int);
+static int pcicfgrw8(int, int, int, int);
+
+static char* bustypes[] = {
+ "CBUSI",
+ "CBUSII",
+ "EISA",
+ "FUTURE",
+ "INTERN",
+ "ISA",
+ "MBI",
+ "MBII",
+ "MCA",
+ "MPI",
+ "MPSA",
+ "NUBUS",
+ "PCI",
+ "PCMCIA",
+ "TC",
+ "VL",
+ "VME",
+ "XPRESS",
+};
+
+#pragma varargck type "T" int
+
+static int
+tbdffmt(Fmt* fmt)
+{
+ char *p;
+ int l, r, type, tbdf;
+
+ if((p = malloc(READSTR)) == nil)
+ return fmtstrcpy(fmt, "(tbdfconv)");
+
+ switch(fmt->r){
+ case 'T':
+ tbdf = va_arg(fmt->args, int);
+ type = BUSTYPE(tbdf);
+ if(type < nelem(bustypes))
+ l = snprint(p, READSTR, bustypes[type]);
+ else
+ l = snprint(p, READSTR, "%d", type);
+ snprint(p+l, READSTR-l, ".%d.%d.%d",
+ BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
+ break;
+
+ default:
+ snprint(p, READSTR, "(tbdfconv)");
+ break;
+ }
+ r = fmtstrcpy(fmt, p);
+ free(p);
+
+ return r;
+}
+
+ulong
+pcibarsize(Pcidev *p, int rno)
+{
+ ulong v, size;
+
+ v = pcicfgrw32(p->tbdf, rno, 0, 1);
+ pcicfgrw32(p->tbdf, rno, 0xFFFFFFF0, 0);
+ size = pcicfgrw32(p->tbdf, rno, 0, 1);
+ if(v & 1)
+ size |= 0xFFFF0000;
+ pcicfgrw32(p->tbdf, rno, v, 0);
+
+ return -(size & ~0x0F);
+}
+
+static int
+pcisizcmp(void *a, void *b)
+{
+ Pcisiz *aa, *bb;
+
+ aa = a;
+ bb = b;
+ return aa->siz - bb->siz;
+}
+
+static ulong
+pcimask(ulong v)
+{
+ ulong m;
+
+ m = BI2BY*sizeof(v);
+ for(m = 1<<(m-1); m != 0; m >>= 1) {
+ if(m & v)
+ break;
+ }
+
+ m--;
+ if((v & m) == 0)
+ return v;
+
+ v |= m;
+ return v+1;
+}
+
+static void
+pcibusmap(Pcidev *root, ulong *pmema, ulong *pioa, int wrreg)
+{
+ Pcidev *p;
+ int ntb, i, size, rno, hole;
+ ulong v, mema, ioa, sioa, smema, base, limit;
+ Pcisiz *table, *tptr, *mtb, *itb;
+ extern void qsort(void*, long, long, int (*)(void*, void*));
+
+ ioa = *pioa;
+ mema = *pmema;
+
+ DBG("pcibusmap wr=%d %T mem=%luX io=%luX\n",
+ wrreg, root->tbdf, mema, ioa);
+
+ ntb = 0;
+ for(p = root; p != nil; p = p->link)
+ ntb++;
+
+ ntb *= (PciCIS-PciBAR0)/4;
+ table = malloc(2*ntb*sizeof(Pcisiz));
+ itb = table;
+ mtb = table+ntb;
+
+ /*
+ * Build a table of sizes
+ */
+ for(p = root; p != nil; p = p->link) {
+ if(p->ccrb == 0x06) {
+ if(p->ccru == 0x04 && p->bridge != nil) {
+ sioa = ioa;
+ smema = mema;
+ pcibusmap(p->bridge, &smema, &sioa, 0);
+
+ hole = pcimask(smema-mema);
+ if(hole < (1<<20))
+ hole = 1<<20;
+ p->mema.size = hole;
+
+ hole = pcimask(sioa-ioa);
+ if(hole < (1<<12))
+ hole = 1<<12;
+
+ p->ioa.size = hole;
+
+ itb->dev = p;
+ itb->bar = -1;
+ itb->siz = p->ioa.size;
+ itb++;
+
+ mtb->dev = p;
+ mtb->bar = -1;
+ mtb->siz = p->mema.size;
+ mtb++;
+ }
+ if((pcicfgr8(p, PciHDT)&0x7f) != 0)
+ continue;
+ }
+
+ for(i = 0; i <= 5; i++) {
+ rno = PciBAR0 + i*4;
+ v = pcicfgrw32(p->tbdf, rno, 0, 1);
+ size = pcibarsize(p, rno);
+ if(size == 0)
+ continue;
+
+ if(v & 1) {
+ itb->dev = p;
+ itb->bar = i;
+ itb->siz = size;
+ itb++;
+ }
+ else {
+ mtb->dev = p;
+ mtb->bar = i;
+ mtb->siz = size;
+ mtb++;
+ }
+
+ p->mem[i].size = size;
+ }
+ }
+
+ /*
+ * Sort both tables IO smallest first, Memory largest
+ */
+ qsort(table, itb-table, sizeof(Pcisiz), pcisizcmp);
+ tptr = table+ntb;
+ qsort(tptr, mtb-tptr, sizeof(Pcisiz), pcisizcmp);
+
+ /*
+ * Allocate IO address space on this bus
+ */
+ for(tptr = table; tptr < itb; tptr++) {
+ hole = tptr->siz;
+ if(tptr->bar == -1)
+ hole = 1<<12;
+ ioa = (ioa+hole-1) & ~(hole-1);
+
+ p = tptr->dev;
+ if(tptr->bar == -1)
+ p->ioa.bar = ioa;
+ else {
+ p->pcr |= IOen;
+ p->mem[tptr->bar].bar = ioa|1;
+ if(wrreg)
+ pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), ioa|1, 0);
+ }
+
+ ioa += tptr->siz;
+ }
+
+ /*
+ * Allocate Memory address space on this bus
+ */
+ for(tptr = table+ntb; tptr < mtb; tptr++) {
+ hole = tptr->siz;
+ if(tptr->bar == -1)
+ hole = 1<<20;
+ mema = (mema+hole-1) & ~(hole-1);
+
+ p = tptr->dev;
+ if(tptr->bar == -1)
+ p->mema.bar = mema;
+ else {
+ p->pcr |= MEMen;
+ p->mem[tptr->bar].bar = mema;
+ if(wrreg)
+ pcicfgrw32(p->tbdf, PciBAR0+(tptr->bar*4), mema, 0);
+ }
+ mema += tptr->siz;
+ }
+
+ *pmema = mema;
+ *pioa = ioa;
+ free(table);
+
+ if(wrreg == 0)
+ return;
+
+ /*
+ * Finally set all the bridge addresses & registers
+ */
+ for(p = root; p != nil; p = p->link) {
+ if(p->bridge == nil) {
+ pcicfgrw8(p->tbdf, PciLTR, 64, 0);
+
+ p->pcr |= MASen;
+ pcicfgrw32(p->tbdf, PciPCR, p->pcr, 0);
+ continue;
+ }
+
+ base = p->ioa.bar;
+ limit = base+p->ioa.size-1;
+ v = pcicfgrw32(p->tbdf, PciIBR, 0, 1);
+ v = (v&0xFFFF0000)|(limit & 0xF000)|((base & 0xF000)>>8);
+ pcicfgrw32(p->tbdf, PciIBR, v, 0);
+ v = (limit & 0xFFFF0000)|(base>>16);
+ pcicfgrw32(p->tbdf, PciIUBR, v, 0);
+
+ base = p->mema.bar;
+ limit = base+p->mema.size-1;
+ v = (limit & 0xFFF00000)|((base & 0xFFF00000)>>16);
+ pcicfgrw32(p->tbdf, PciMBR, v, 0);
+
+ /*
+ * Disable memory prefetch
+ */
+ pcicfgrw32(p->tbdf, PciPMBR, 0x0000FFFF, 0);
+ pcicfgrw8(p->tbdf, PciLTR, 64, 0);
+
+ /*
+ * Enable the bridge
+ */
+ v = 0xFFFF0000 | IOen | MEMen | MASen;
+ pcicfgrw32(p->tbdf, PciPCR, v, 0);
+
+ sioa = p->ioa.bar;
+ smema = p->mema.bar;
+ pcibusmap(p->bridge, &smema, &sioa, 1);
+ }
+}
+
+static int
+pcilscan(int bno, Pcidev** list)
+{
+ Pcidev *p, *head, *tail;
+ int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn;
+
+ maxubn = bno;
+ head = nil;
+ tail = nil;
+ for(dno = 0; dno <= pcimaxdno; dno++){
+ maxfno = 0;
+ for(fno = 0; fno <= maxfno; fno++){
+ /*
+ * For this possible device, form the
+ * bus+device+function triplet needed to address it
+ * and try to read the vendor and device ID.
+ * If successful, allocate a device struct and
+ * start to fill it in with some useful information
+ * from the device's configuration space.
+ */
+ tbdf = MKBUS(BusPCI, bno, dno, fno);
+ l = pcicfgrw32(tbdf, PciVID, 0, 1);
+ if(l == 0xFFFFFFFF || l == 0)
+ continue;
+ p = malloc(sizeof(*p));
+ p->tbdf = tbdf;
+ p->vid = l;
+ p->did = l>>16;
+
+ if(pcilist != nil)
+ pcitail->list = p;
+ else
+ pcilist = p;
+ pcitail = p;
+
+ p->rid = pcicfgr8(p, PciRID);
+ p->ccrp = pcicfgr8(p, PciCCRp);
+ p->ccru = pcicfgr8(p, PciCCRu);
+ p->ccrb = pcicfgr8(p, PciCCRb);
+ p->pcr = pcicfgr32(p, PciPCR);
+
+ p->intl = pcicfgr8(p, PciINTL);
+
+ /*
+ * If the device is a multi-function device adjust the
+ * loop count so all possible functions are checked.
+ */
+ hdt = pcicfgr8(p, PciHDT);
+ if(hdt & 0x80)
+ maxfno = MaxFNO;
+
+ /*
+ * If appropriate, read the base address registers
+ * and work out the sizes.
+ */
+ switch(p->ccrb) {
+ case 0x01: /* mass storage controller */
+ case 0x02: /* network controller */
+ case 0x03: /* display controller */
+ case 0x04: /* multimedia device */
+ case 0x06: /* bridge device */
+ case 0x07: /* simple comm. controllers */
+ case 0x08: /* base system peripherals */
+ case 0x09: /* input devices */
+ case 0x0A: /* docking stations */
+ case 0x0B: /* processors */
+ case 0x0C: /* serial bus controllers */
+ if((hdt & 0x7F) != 0)
+ break;
+ rno = PciBAR0 - 4;
+ for(i = 0; i < nelem(p->mem); i++) {
+ rno += 4;
+ p->mem[i].bar = pcicfgr32(p, rno);
+ p->mem[i].size = pcibarsize(p, rno);
+ }
+ break;
+
+ case 0x00:
+ case 0x05: /* memory controller */
+ default:
+ break;
+ }
+
+ if(head != nil)
+ tail->link = p;
+ else
+ head = p;
+ tail = p;
+ }
+ }
+
+ *list = head;
+ for(p = head; p != nil; p = p->link){
+ /*
+ * Find PCI-PCI bridges and recursively descend the tree.
+ */
+ if(p->ccrb != 0x06 || p->ccru != 0x04)
+ continue;
+
+ /*
+ * If the secondary or subordinate bus number is not
+ * initialised try to do what the PCI BIOS should have
+ * done and fill in the numbers as the tree is descended.
+ * On the way down the subordinate bus number is set to
+ * the maximum as it's not known how many buses are behind
+ * this one; the final value is set on the way back up.
+ */
+ sbn = pcicfgr8(p, PciSBN);
+ ubn = pcicfgr8(p, PciUBN);
+
+ if(sbn == 0 || ubn == 0) {
+ sbn = maxubn+1;
+ /*
+ * Make sure memory, I/O and master enables are
+ * off, set the primary, secondary and subordinate
+ * bus numbers and clear the secondary status before
+ * attempting to scan the secondary bus.
+ *
+ * Initialisation of the bridge should be done here.
+ */
+ pcicfgw32(p, PciPCR, 0xFFFF0000);
+ l = (MaxUBN<<16)|(sbn<<8)|bno;
+ pcicfgw32(p, PciPBN, l);
+ pcicfgw16(p, PciSPSR, 0xFFFF);
+ maxubn = pcilscan(sbn, &p->bridge);
+ l = (maxubn<<16)|(sbn<<8)|bno;
+
+ pcicfgw32(p, PciPBN, l);
+ }
+ else {
+ maxubn = ubn;
+ pcilscan(sbn, &p->bridge);
+ }
+ }
+
+ return maxubn;
+}
+
+int
+pciscan(int bno, Pcidev **list)
+{
+ int ubn;
+
+ qlock(&pcicfginitlock);
+ ubn = pcilscan(bno, list);
+ qunlock(&pcicfginitlock);
+ return ubn;
+}
+
+static void
+pcicfginit(void)
+{
+ char *p;
+ int bno;
+ Pcidev **list;
+ ulong mema, ioa;
+
+ qlock(&pcicfginitlock);
+ if(pcicfgmode != -1)
+ goto out;
+
+ /*
+ * Try to determine which PCI configuration mode is implemented.
+ * Mode2 uses a byte at 0xCF8 and another at 0xCFA; Mode1 uses
+ * a DWORD at 0xCF8 and another at 0xCFC and will pass through
+ * any non-DWORD accesses as normal I/O cycles. There shouldn't be
+ * a device behind these addresses so if Mode2 accesses fail try
+ * for Mode1 (which is preferred, Mode2 is deprecated).
+ */
+ outb(PciCSE, 0);
+ if(inb(PciCSE) == 0){
+ pcicfgmode = 2;
+ pcimaxdno = 15;
+ }
+ else {
+ outl(PciADDR, 0);
+ if(inl(PciADDR) == 0){
+ pcicfgmode = 1;
+ pcimaxdno = 31;
+ }
+ }
+
+ if(pcicfgmode < 0)
+ goto out;
+
+ fmtinstall('T', tbdffmt);
+
+ if(p = getconf("*pcimaxbno"))
+ pcimaxbno = strtoul(p, 0, 0);
+ if(p = getconf("*pcimaxdno"))
+ pcimaxdno = strtoul(p, 0, 0);
+
+ list = &pciroot;
+ for(bno = 0; bno <= pcimaxbno; bno++) {
+ int sbno = bno;
+ bno = pcilscan(bno, list);
+
+ while(*list)
+ list = &(*list)->link;
+
+ if (sbno == 0) {
+ Pcidev *pci;
+
+ /*
+ * If we have found a PCI-to-Cardbus bridge, make sure
+ * it has no valid mappings anymore.
+ */
+ pci = pciroot;
+ while (pci) {
+ if (pci->ccrb == 6 && pci->ccru == 7) {
+ ushort bcr;
+
+ /* reset the cardbus */
+ bcr = pcicfgr16(pci, PciBCR);
+ pcicfgw16(pci, PciBCR, 0x40 | bcr);
+ delay(50);
+ }
+ pci = pci->link;
+ }
+ }
+ }
+
+ if(pciroot == nil)
+ goto out;
+
+ /*
+ * Work out how big the top bus is
+ */
+ mema = 0;
+ ioa = 0;
+ pcibusmap(pciroot, &mema, &ioa, 0);
+
+ DBG("Sizes: mem=%8.8lux size=%8.8lux io=%8.8lux\n",
+ mema, pcimask(mema), ioa);
+
+ /*
+ * Align the windows and map it
+ */
+ ioa = 0x1000;
+ mema = 0;
+
+ pcilog("Mask sizes: mem=%lux io=%lux\n", mema, ioa);
+
+ pcibusmap(pciroot, &mema, &ioa, 1);
+ DBG("Sizes2: mem=%lux io=%lux\n", mema, ioa);
+
+out:
+ qunlock(&pcicfginitlock);
+}
+
+static int
+pcicfgrw8(int tbdf, int rno, int data, int read)
+{
+ int o, type, x;
+
+ if(pcicfgmode == -1)
+ pcicfginit();
+
+ if(BUSBNO(tbdf))
+ type = 0x01;
+ else
+ type = 0x00;
+ x = -1;
+ if(BUSDNO(tbdf) > pcimaxdno)
+ return x;
+
+ lock(&pcicfglock);
+ switch(pcicfgmode){
+
+ case 1:
+ o = rno & 0x03;
+ rno &= ~0x03;
+ outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
+ if(read)
+ x = inb(PciDATA+o);
+ else
+ outb(PciDATA+o, data);
+ outl(PciADDR, 0);
+ break;
+
+ case 2:
+ outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
+ outb(PciFORWARD, BUSBNO(tbdf));
+ if(read)
+ x = inb((0xC000|(BUSDNO(tbdf)<<8)) + rno);
+ else
+ outb((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
+ outb(PciCSE, 0);
+ break;
+ }
+ unlock(&pcicfglock);
+
+ return x;
+}
+
+int
+pcicfgr8(Pcidev* pcidev, int rno)
+{
+ return pcicfgrw8(pcidev->tbdf, rno, 0, 1);
+}
+
+void
+pcicfgw8(Pcidev* pcidev, int rno, int data)
+{
+ pcicfgrw8(pcidev->tbdf, rno, data, 0);
+}
+
+static int
+pcicfgrw16(int tbdf, int rno, int data, int read)
+{
+ int o, type, x;
+
+ if(pcicfgmode == -1)
+ pcicfginit();
+
+ if(BUSBNO(tbdf))
+ type = 0x01;
+ else
+ type = 0x00;
+ x = -1;
+ if(BUSDNO(tbdf) > pcimaxdno)
+ return x;
+
+ lock(&pcicfglock);
+ switch(pcicfgmode){
+
+ case 1:
+ o = rno & 0x02;
+ rno &= ~0x03;
+ outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
+ if(read)
+ x = ins(PciDATA+o);
+ else
+ outs(PciDATA+o, data);
+ outl(PciADDR, 0);
+ break;
+
+ case 2:
+ outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
+ outb(PciFORWARD, BUSBNO(tbdf));
+ if(read)
+ x = ins((0xC000|(BUSDNO(tbdf)<<8)) + rno);
+ else
+ outs((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
+ outb(PciCSE, 0);
+ break;
+ }
+ unlock(&pcicfglock);
+
+ return x;
+}
+
+int
+pcicfgr16(Pcidev* pcidev, int rno)
+{
+ return pcicfgrw16(pcidev->tbdf, rno, 0, 1);
+}
+
+void
+pcicfgw16(Pcidev* pcidev, int rno, int data)
+{
+ pcicfgrw16(pcidev->tbdf, rno, data, 0);
+}
+
+static int
+pcicfgrw32(int tbdf, int rno, int data, int read)
+{
+ int type, x;
+
+ if(pcicfgmode == -1)
+ pcicfginit();
+
+ if(BUSBNO(tbdf))
+ type = 0x01;
+ else
+ type = 0x00;
+ x = -1;
+ if(BUSDNO(tbdf) > pcimaxdno)
+ return x;
+
+ lock(&pcicfglock);
+ switch(pcicfgmode){
+
+ case 1:
+ rno &= ~0x03;
+ outl(PciADDR, 0x80000000|BUSBDF(tbdf)|rno|type);
+ if(read)
+ x = inl(PciDATA);
+ else
+ outl(PciDATA, data);
+ outl(PciADDR, 0);
+ break;
+
+ case 2:
+ outb(PciCSE, 0x80|(BUSFNO(tbdf)<<1));
+ outb(PciFORWARD, BUSBNO(tbdf));
+ if(read)
+ x = inl((0xC000|(BUSDNO(tbdf)<<8)) + rno);
+ else
+ outl((0xC000|(BUSDNO(tbdf)<<8)) + rno, data);
+ outb(PciCSE, 0);
+ break;
+ }
+ unlock(&pcicfglock);
+
+ return x;
+}
+
+int
+pcicfgr32(Pcidev* pcidev, int rno)
+{
+ return pcicfgrw32(pcidev->tbdf, rno, 0, 1);
+}
+
+void
+pcicfgw32(Pcidev* pcidev, int rno, int data)
+{
+ pcicfgrw32(pcidev->tbdf, rno, data, 0);
+}
+
+Pcidev*
+pcimatch(Pcidev* prev, int vid, int did)
+{
+ if(pcicfgmode == -1)
+ pcicfginit();
+
+ if(prev == nil)
+ prev = pcilist;
+ else
+ prev = prev->list;
+
+ while(prev != nil){
+ if((vid == 0 || prev->vid == vid)
+ && (did == 0 || prev->did == did))
+ break;
+ prev = prev->list;
+ }
+ return prev;
+}
+
+Pcidev*
+pcimatchtbdf(int tbdf)
+{
+ Pcidev *pcidev;
+
+ if(pcicfgmode == -1)
+ pcicfginit();
+
+ for(pcidev = pcilist; pcidev != nil; pcidev = pcidev->list) {
+ if(pcidev->tbdf == tbdf)
+ break;
+ }
+ return pcidev;
+}
+
+uchar
+pciipin(Pcidev *pci, uchar pin)
+{
+ if (pci == nil)
+ pci = pcilist;
+
+ while (pci) {
+ uchar intl;
+
+ if (pcicfgr8(pci, PciINTP) == pin && pci->intl != 0 && pci->intl != 0xff)
+ return pci->intl;
+
+ if (pci->bridge && (intl = pciipin(pci->bridge, pin)) != 0)
+ return intl;
+
+ pci = pci->list;
+ }
+ return 0;
+}
+
+static void
+pcilhinv(Pcidev* p)
+{
+ int i;
+ Pcidev *t;
+
+ if(p == nil) {
+ putstrn(PCICONS.output, PCICONS.ptr);
+ p = pciroot;
+ print("bus dev type vid did intl memory\n");
+ }
+ for(t = p; t != nil; t = t->link) {
+ print("%d %2d/%d %.2ux %.2ux %.2ux %.4ux %.4ux %3d ",
+ BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf),
+ t->ccrb, t->ccru, t->ccrp, t->vid, t->did, t->intl);
+
+ for(i = 0; i < nelem(p->mem); i++) {
+ if(t->mem[i].size == 0)
+ continue;
+ print("%d:%.8lux %d ", i,
+ t->mem[i].bar, t->mem[i].size);
+ }
+ if(t->ioa.bar || t->ioa.size)
+ print("ioa:%.8lux %d ", t->ioa.bar, t->ioa.size);
+ if(t->mema.bar || t->mema.size)
+ print("mema:%.8lux %d ", t->mema.bar, t->mema.size);
+ if(t->bridge)
+ print("->%d", BUSBNO(t->bridge->tbdf));
+ print("\n");
+ }
+ while(p != nil) {
+ if(p->bridge != nil)
+ pcilhinv(p->bridge);
+ p = p->link;
+ }
+}
+
+void
+pcihinv(Pcidev* p)
+{
+ if(pcicfgmode == -1)
+ pcicfginit();
+ qlock(&pcicfginitlock);
+ pcilhinv(p);
+ qunlock(&pcicfginitlock);
+}
+
+void
+pcireset(void)
+{
+ Pcidev *p;
+ int pcr;
+
+ if(pcicfgmode == -1)
+ pcicfginit();
+
+ for(p = pcilist; p != nil; p = p->list){
+ pcr = pcicfgr16(p, PciPCR);
+ pcr &= ~0x0004;
+ pcicfgw16(p, PciPCR, pcr);
+ }
+}
+
+void
+pcisetbme(Pcidev* p)
+{
+ int pcr;
+
+ pcr = pcicfgr16(p, PciPCR);
+ pcr |= MASen;
+ pcicfgw16(p, PciPCR, pcr);
+}
+
+void
+pciclrbme(Pcidev* p)
+{
+ int pcr;
+
+ pcr = pcicfgr16(p, PciPCR);
+ pcr &= ~MASen;
+ pcicfgw16(p, PciPCR, pcr);
+}
diff --git a/sys/src/9/mtx/random.c b/sys/src/9/mtx/random.c
new file mode 100755
index 000000000..3c204b8d1
--- /dev/null
+++ b/sys/src/9/mtx/random.c
@@ -0,0 +1,138 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+
+struct Rb
+{
+ QLock;
+ Rendez producer;
+ Rendez consumer;
+ ulong randomcount;
+ uchar buf[1024];
+ uchar *ep;
+ uchar *rp;
+ uchar *wp;
+ uchar next;
+ uchar wakeme;
+ ushort bits;
+ ulong randn;
+} rb;
+
+static int
+rbnotfull(void*)
+{
+ int i;
+
+ i = rb.rp - rb.wp;
+ return i != 1 && i != (1 - sizeof(rb.buf));
+}
+
+static int
+rbnotempty(void*)
+{
+ return rb.wp != rb.rp;
+}
+
+static void
+genrandom(void*)
+{
+ up->basepri = PriNormal;
+ up->priority = up->basepri;
+
+ for(;;){
+ for(;;)
+ if(++rb.randomcount > 100000)
+ break;
+ if(anyhigher())
+ sched();
+ if(!rbnotfull(0))
+ sleep(&rb.producer, rbnotfull, 0);
+ }
+}
+
+/*
+ * produce random bits in a circular buffer
+ */
+static void
+randomclock(void)
+{
+ if(rb.randomcount == 0 || !rbnotfull(0))
+ return;
+
+ rb.bits = (rb.bits<<2) ^ rb.randomcount;
+ rb.randomcount = 0;
+
+ rb.next++;
+ if(rb.next != 8/2)
+ return;
+ rb.next = 0;
+
+ *rb.wp ^= rb.bits;
+ if(rb.wp+1 == rb.ep)
+ rb.wp = rb.buf;
+ else
+ rb.wp = rb.wp+1;
+
+ if(rb.wakeme)
+ wakeup(&rb.consumer);
+}
+
+void
+randominit(void)
+{
+ addclock0link(randomclock, 1000/HZ);
+ rb.ep = rb.buf + sizeof(rb.buf);
+ rb.rp = rb.wp = rb.buf;
+ kproc("genrandom", genrandom, 0);
+}
+
+/*
+ * consume random bytes from a circular buffer
+ */
+ulong
+randomread(void *xp, ulong n)
+{
+ uchar *e, *p;
+ ulong x;
+
+ p = xp;
+
+ if(waserror()){
+ qunlock(&rb);
+ nexterror();
+ }
+
+ qlock(&rb);
+ for(e = p + n; p < e; ){
+ if(rb.wp == rb.rp){
+ rb.wakeme = 1;
+ wakeup(&rb.producer);
+ sleep(&rb.consumer, rbnotempty, 0);
+ rb.wakeme = 0;
+ continue;
+ }
+
+ /*
+ * beating clocks will be precictable if
+ * they are synchronized. Use a cheap pseudo
+ * random number generator to obscure any cycles.
+ */
+ x = rb.randn*1103515245 ^ *rb.rp;
+ *p++ = rb.randn = x;
+
+ if(rb.rp+1 == rb.ep)
+ rb.rp = rb.buf;
+ else
+ rb.rp = rb.rp+1;
+ }
+ qunlock(&rb);
+ poperror();
+
+ wakeup(&rb.producer);
+
+ return n;
+}
diff --git a/sys/src/9/mtx/raven.c b/sys/src/9/mtx/raven.c
new file mode 100755
index 000000000..d6fcb0179
--- /dev/null
+++ b/sys/src/9/mtx/raven.c
@@ -0,0 +1,153 @@
+/*
+ * ``Nevermore!''
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+typedef struct Raven Raven;
+struct Raven
+{
+ ushort vid;
+ ushort did;
+ uchar _pad4;
+ uchar rev;
+ uchar _pad6[2];
+ ushort gcsr;
+ ushort feat;
+ uchar _padC[7];
+ uchar padj;
+ uchar _pad14[12];
+ ushort errtst;
+ ushort erren;
+ uchar _pad24[3];
+ uchar errst;
+ ulong errad;
+ uchar _pad2C[2];
+ ushort errat;
+ ulong piack;
+ uchar _pad34[12];
+
+ struct {
+ ushort start;
+ ushort end;
+ ushort off;
+ uchar _pad;
+ uchar attr;
+ } map[4];
+
+ struct {
+ ulong cntl;
+ uchar _pad[3];
+ uchar stat;
+ } wdt[2];
+
+ ulong gpr[4];
+};
+
+enum {
+ /* map[] attr bits */
+ Iom = (1<<0),
+ Mem = (1<<1),
+ Wpen = (1<<4),
+ Wen = (1<<6),
+ Ren = (1<<7),
+};
+
+static Raven *raven = (Raven*)RAVEN;
+static ulong mpic;
+
+static void
+setmap(int i, ulong addr, ulong len, ulong busaddr, int attr)
+{
+ raven->map[i].start = addr>>16;
+ raven->map[i].end = (addr+len-1)>>16;
+ raven->map[i].off = (busaddr-addr)>>16;
+ raven->map[i].attr = attr;
+}
+
+static ulong
+swap32(ulong x)
+{
+ return (x>>24)|((x>>8)&0xff00)|((x<<8)&0xff0000)|(x<<24);
+}
+
+static ulong
+mpic32r(int rno)
+{
+ return swap32(*(ulong*)(mpic+rno));
+}
+
+static void
+mpic32w(int rno, ulong x)
+{
+ *(ulong*)(mpic+rno) = swap32(x);
+ eieio();
+}
+
+void
+raveninit(void)
+{
+ int i;
+ Pcidev *p;
+
+ if(raven->vid != 0x1057 || raven->did !=0x4801)
+ panic("raven not found");
+
+ /* set up a sensible hardware memory/IO map */
+ setmap(0, PCIMEM0, PCISIZE0, 0, Wen|Ren|Mem);
+ setmap(1, KZERO, IOSIZE, 0, Wen|Ren); /* keeps PPCbug happy */
+ setmap(2, PCIMEM1, PCISIZE1, PCISIZE0, Wen|Ren|Mem);
+ setmap(3, IOMEM, IOSIZE, 0, Wen|Ren); /* I/O must be slot 3 for PCI cfg space */
+
+ p = pcimatch(nil, 0x1057, 0x4801);
+ if(p == nil)
+ panic("raven PCI regs not found");
+ mpic = (p->mem[1].bar+PCIMEM0);
+
+ /* ensure all interrupts are off, and routed to cpu 0 */
+ for(i = 0; i < 16; i++) {
+ mpic32w(0x10000+0x20*i, (1<<31)); /* mask */
+ mpic32w(0x10010+0x20*i, 1); /* route to cpu 0 */
+ }
+
+ mpic32w(0x20080, 1); /* cpu 0 task pri */
+// mpic32w(0x21080, 1); /* cpu 1 task pri */
+ mpic32w(0x1020, (1<<29)); /* Mixed mode (8259 & Raven intrs both available) */
+}
+
+void
+mpicenable(int vec, Vctl *v)
+{
+ ulong x;
+
+ x = (1<<22)|(15<<16)|vec;
+ if(vec == 0)
+ x |= (1<<23);
+ mpic32w(0x10000+0x20*vec, x);
+ if(vec != 0)
+ v->eoi = mpiceoi;
+}
+
+void
+mpicdisable(int vec)
+{
+ mpic32w(0x10000+0x20*vec, (1<<31));
+}
+
+int
+mpicintack(void)
+{
+ return mpic32r(0x200A0 + (m->machno<<12));
+}
+
+int
+mpiceoi(int vec)
+{
+ USED(vec);
+ mpic32w(0x200B0 + (m->machno<<12), 0);
+ return 0;
+}
diff --git a/sys/src/9/mtx/trap.c b/sys/src/9/mtx/trap.c
new file mode 100755
index 000000000..767e96c3b
--- /dev/null
+++ b/sys/src/9/mtx/trap.c
@@ -0,0 +1,836 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "ureg.h"
+#include "io.h"
+#include "tos.h"
+#include "../port/error.h"
+
+static Lock vctllock;
+static Vctl *vctl[256];
+
+void
+hwintrinit(void)
+{
+ i8259init();
+ mpicenable(0, nil); /* 8259 interrupts are routed through MPIC intr 0 */
+}
+
+static int
+hwintrenable(Vctl *v)
+{
+ int vec, irq;
+
+ irq = v->irq;
+ if(BUSTYPE(v->tbdf) == BusPCI) { /* MPIC? */
+ if(irq > 15) {
+ print("intrenable: pci irq %d out of range\n", v->irq);
+ return -1;
+ }
+ vec = irq;
+ mpicenable(vec, v);
+ }
+ else {
+ if(irq > MaxIrqPIC) {
+ print("intrenable: irq %d out of range\n", v->irq);
+ return -1;
+ }
+ vec = irq+VectorPIC;
+ if(i8259enable(v) == -1)
+ return -1;
+ }
+ return vec;
+}
+
+static int
+hwintrdisable(Vctl *v)
+{
+ int vec, irq;
+
+ irq = v->irq;
+ if(BUSTYPE(v->tbdf) == BusPCI) { /* MPIC? */
+ if(irq > 15) {
+ print("intrdisable: pci irq %d out of range\n", v->irq);
+ return -1;
+ }
+ vec = irq;
+ mpicdisable(vec);
+ }
+ else {
+ if(irq > MaxIrqPIC) {
+ print("intrdisable: irq %d out of range\n", v->irq);
+ return -1;
+ }
+ vec = irq+VectorPIC;
+ if(i8259disable(irq) == -1)
+ return -1;
+ }
+ return vec;
+}
+
+static int
+hwvecno(int irq, int tbdf)
+{
+ if(BUSTYPE(tbdf) == BusPCI) /* MPIC? */
+ return irq;
+ else
+ return irq+VectorPIC;
+}
+
+void
+intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
+{
+ int vno;
+ Vctl *v;
+
+ if(f == nil){
+ print("intrenable: nil handler for %d, tbdf 0x%uX for %s\n",
+ irq, tbdf, name);
+ return;
+ }
+
+ v = xalloc(sizeof(Vctl));
+ v->isintr = 1;
+ v->irq = irq;
+ v->tbdf = tbdf;
+ v->f = f;
+ v->a = a;
+ strncpy(v->name, name, KNAMELEN-1);
+ v->name[KNAMELEN-1] = 0;
+
+ ilock(&vctllock);
+ vno = hwintrenable(v);
+ if(vno == -1){
+ iunlock(&vctllock);
+ print("intrenable: couldn't enable irq %d, tbdf 0x%uX for %s\n",
+ irq, tbdf, v->name);
+ xfree(v);
+ return;
+ }
+ if(vctl[vno]){
+ if(vctl[vno]->isr != v->isr || vctl[vno]->eoi != v->eoi)
+ panic("intrenable: handler: %s %s %#p %#p %#p %#p",
+ vctl[vno]->name, v->name,
+ vctl[vno]->isr, v->isr, vctl[vno]->eoi, v->eoi);
+ v->next = vctl[vno];
+ }
+ vctl[vno] = v;
+ iunlock(&vctllock);
+}
+
+void
+intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int tbdf, char *name)
+{
+ Vctl **pv, *v;
+ int vno;
+
+ vno = hwvecno(irq, tbdf);
+ ilock(&vctllock);
+ pv = &vctl[vno];
+ while (*pv &&
+ ((*pv)->irq != irq || (*pv)->tbdf != tbdf || (*pv)->f != f || (*pv)->a != a ||
+ strcmp((*pv)->name, name)))
+ pv = &((*pv)->next);
+ assert(*pv);
+
+ v = *pv;
+ *pv = (*pv)->next; /* Link out the entry */
+
+ if(vctl[vno] == nil)
+ hwintrdisable(v);
+ iunlock(&vctllock);
+ xfree(v);
+}
+
+void syscall(Ureg*);
+void noted(Ureg*, ulong);
+static void _dumpstack(Ureg*);
+
+char *excname[] =
+{
+ "reserved 0",
+ "system reset",
+ "machine check",
+ "data access",
+ "instruction access",
+ "external interrupt",
+ "alignment",
+ "program exception",
+ "floating-point unavailable",
+ "decrementer",
+ "reserved A",
+ "reserved B",
+ "system call",
+ "trace trap",
+ "floating point assist",
+ "reserved F",
+ "reserved 10",
+ "reserved 11",
+ "reserved 12",
+ "instruction address breakpoint",
+ "system management interrupt",
+};
+
+char *fpcause[] =
+{
+ "inexact operation",
+ "division by zero",
+ "underflow",
+ "overflow",
+ "invalid operation",
+};
+char *fpexcname(Ureg*, ulong, char*);
+#define FPEXPMASK 0xfff80300 /* Floating exception bits in fpscr */
+
+
+char *regname[]={
+ "CAUSE", "SRR1",
+ "PC", "GOK",
+ "LR", "CR",
+ "XER", "CTR",
+ "R0", "R1",
+ "R2", "R3",
+ "R4", "R5",
+ "R6", "R7",
+ "R8", "R9",
+ "R10", "R11",
+ "R12", "R13",
+ "R14", "R15",
+ "R16", "R17",
+ "R18", "R19",
+ "R20", "R21",
+ "R22", "R23",
+ "R24", "R25",
+ "R26", "R27",
+ "R28", "R29",
+ "R30", "R31",
+};
+
+void
+trap(Ureg *ureg)
+{
+ ulong dsisr;
+ int ecode, user;
+ char buf[ERRMAX], *s;
+
+ ecode = (ureg->cause >> 8) & 0xff;
+ user = (ureg->srr1 & MSR_PR) != 0;
+ if(user)
+ up->dbgreg = ureg;
+
+ if(ureg->status & MSR_RI == 0)
+ print("double fault?: ecode = %d\n", ecode);
+
+ switch(ecode) {
+ case CEI:
+ intr(ureg);
+ break;
+ case CDEC:
+ clockintr(ureg);
+ break;
+ case CSYSCALL:
+ if(!user)
+ panic("syscall in kernel: srr1 0x%4.4luX", ureg->srr1);
+ syscall(ureg);
+ return; /* syscall() calls notify itself, don't do it again */
+ case CFPU:
+ if(!user || up == nil) {
+ dumpregs(ureg);
+ panic("floating point in kernel");
+ }
+ switch(up->fpstate){
+ case FPinit:
+ fprestore(&initfp);
+ up->fpstate = FPactive;
+ break;
+ case FPinactive:
+ fprestore(&up->fpsave);
+ up->fpstate = FPactive;
+ break;
+ default:
+ panic("fpstate");
+ }
+ ureg->srr1 |= MSR_FP;
+ break;
+ case CISI:
+ faultpower(ureg, ureg->pc, 1);
+ break;
+ case CDSI:
+ dsisr = getdsisr();
+ if(dsisr & BIT(6))
+ faultpower(ureg, getdar(), 0);
+ else
+ faultpower(ureg, getdar(), 1);
+ break;
+ case CPROG:
+ if(ureg->status & (1<<19))
+ s = "floating point exception";
+ else if(ureg->status & (1<<18))
+ s = "illegal instruction";
+ else if(ureg->status & (1<<17))
+ s = "privileged instruction";
+ else
+ s = "undefined program exception";
+ if(user){
+ spllo();
+ sprint(buf, "sys: trap: %s", s);
+ postnote(up, 1, buf, NDebug);
+ break;
+ }
+ dumpregs(ureg);
+ panic(s);
+ break;
+ default:
+ if(ecode < nelem(excname) && user){
+ spllo();
+ sprint(buf, "sys: trap: %s", excname[ecode]);
+ postnote(up, 1, buf, NDebug);
+ break;
+ }
+ dumpregs(ureg);
+ if(ecode < nelem(excname))
+ panic("%s", excname[ecode]);
+ panic("unknown trap/intr: %d", ecode);
+ }
+
+ /* restoreureg must execute at high IPL */
+ splhi();
+
+ /* delaysched set because we held a lock or because our quantum ended */
+ if(up && up->delaysched && ecode == CDEC){
+ sched();
+ splhi();
+ }
+
+ if(user) {
+ notify(ureg);
+ if(up->fpstate == FPinactive)
+ ureg->srr1 &= ~MSR_FP;
+ kexit(ureg);
+ }
+}
+
+void
+faultpower(Ureg *ureg, ulong addr, int read)
+{
+ int user, insyscall, n;
+ char buf[ERRMAX];
+
+ user = (ureg->srr1 & MSR_PR) != 0;
+ insyscall = up->insyscall;
+ up->insyscall = 1;
+ n = fault(addr, read);
+ if(n < 0){
+ if(!user){
+ dumpregs(ureg);
+ panic("fault: 0x%lux", addr);
+ }
+ sprint(buf, "sys: trap: fault %s addr=0x%lux", read? "read" : "write", addr);
+ postnote(up, 1, buf, NDebug);
+ }
+ up->insyscall = insyscall;
+}
+
+void
+sethvec(int v, void (*r)(void))
+{
+ ulong *vp, pa, o;
+
+ vp = KADDR(v);
+ vp[0] = 0x7c1043a6; /* MOVW R0, SPR(SPRG0) */
+ vp[1] = 0x7c0802a6; /* MOVW LR, R0 */
+ vp[2] = 0x7c1243a6; /* MOVW R0, SPR(SPRG2) */
+ pa = PADDR(r);
+ o = pa >> 25;
+ if(o != 0 && o != 0x7F){
+ /* a branch too far */
+ vp[3] = (15<<26)|(pa>>16); /* MOVW $r&~0xFFFF, R0 */
+ vp[4] = (24<<26)|(pa&0xFFFF); /* OR $r&0xFFFF, R0 */
+ vp[5] = 0x7c0803a6; /* MOVW R0, LR */
+ vp[6] = 0x4e800021; /* BL (LR) */
+ }else
+ vp[3] = (18<<26)|(pa&0x3FFFFFC)|3; /* bla */
+ dcflush(vp, 8*sizeof(ulong));
+}
+
+void
+trapinit(void)
+{
+ int i;
+
+ /*
+ * set all exceptions to trap
+ */
+ for(i = 0; i < 0x2000; i += 0x100)
+ sethvec(i, trapvec);
+
+ putmsr(getmsr() & ~MSR_IP);
+}
+
+void
+intr(Ureg *ureg)
+{
+ int vno;
+ Vctl *ctl, *v;
+
+ vno = mpicintack();
+ if(vno == 0) { /* 8259, wired through MPIC vec 0 */
+ vno = i8259intack();
+ mpiceoi(0);
+ }
+
+ if(vno > nelem(vctl) || (ctl = vctl[vno]) == 0) {
+ panic("spurious intr %d", vno);
+ return;
+ }
+
+ if(ctl->isr)
+ ctl->isr(vno);
+ for(v = ctl; v != nil; v = v->next){
+ if(v->f)
+ v->f(ureg, v->a);
+ }
+ if(ctl->eoi)
+ ctl->eoi(vno);
+
+ if(up)
+ preempted();
+}
+
+char*
+fpexcname(Ureg *ur, ulong fpscr, char *buf)
+{
+ int i;
+ char *s;
+ ulong fppc;
+
+ fppc = ur->pc;
+ s = 0;
+ fpscr >>= 3; /* trap enable bits */
+ fpscr &= (fpscr>>22); /* anded with exceptions */
+ for(i=0; i<5; i++)
+ if(fpscr & (1<<i))
+ s = fpcause[i];
+ if(s == 0)
+ return "no floating point exception";
+ sprint(buf, "%s fppc=0x%lux", s, fppc);
+ return buf;
+}
+
+/*
+ * Fill in enough of Ureg to get a stack trace, and call a function.
+ * Used by debugging interface rdb.
+ */
+
+static void
+getpcsp(ulong *pc, ulong *sp)
+{
+ *pc = getcallerpc(&pc);
+ *sp = (ulong)&pc-4;
+}
+
+void
+callwithureg(void (*fn)(Ureg*))
+{
+ Ureg ureg;
+
+ getpcsp((ulong*)&ureg.pc, (ulong*)&ureg.sp);
+ ureg.lr = getcallerpc(&fn);
+ fn(&ureg);
+}
+
+static void
+_dumpstack(Ureg *ureg)
+{
+ ulong l, sl, el, v;
+ int i;
+
+ l = (ulong)&l;
+ if(up == 0){
+ el = (ulong)m+BY2PG;
+ sl = el-KSTACK;
+ }
+ else{
+ sl = (ulong)up->kstack;
+ el = sl + KSTACK;
+ }
+ if(l > el || l < sl){
+ el = (ulong)m+BY2PG;
+ sl = el-KSTACK;
+ }
+ if(l > el || l < sl)
+ return;
+ print("ktrace /kernel/path %.8lux %.8lux %.8lux\n", ureg->pc, ureg->sp, ureg->lr);
+ i = 0;
+ for(; l < el; l += 4){
+ v = *(ulong*)l;
+ if(KTZERO < v && v < (ulong)etext){
+ print("%.8lux=%.8lux ", l, v);
+ if(i++ == 4){
+ print("\n");
+ i = 0;
+ }
+ }
+ }
+}
+
+void
+dumpstack(void)
+{
+ callwithureg(_dumpstack);
+}
+
+void
+dumpregs(Ureg *ur)
+{
+ int i;
+ ulong *l;
+
+ if(up) {
+ print("registers for %s %ld\n", up->text, up->pid);
+ if(ur->srr1 & MSR_PR == 0)
+ if(ur->usp < (ulong)up->kstack || ur->usp > (ulong)up->kstack+KSTACK)
+ print("invalid stack ptr\n");
+ }
+ else
+ print("registers for kernel\n");
+
+ print("dsisr\t%.8lux\tdar\t%.8lux\n", getdsisr(), getdar());
+ l = &ur->cause;
+ for(i=0; i<sizeof regname/sizeof(char*); i+=2, l+=2)
+ print("%s\t%.8lux\t%s\t%.8lux\n", regname[i], l[0], regname[i+1], l[1]);
+}
+
+static void
+linkproc(void)
+{
+ spllo();
+ (*up->kpfun)(up->kparg);
+ pexit("", 0);
+}
+
+void
+kprocchild(Proc *p, void (*func)(void*), void *arg)
+{
+ p->sched.pc = (ulong)linkproc;
+ p->sched.sp = (ulong)p->kstack+KSTACK;
+
+ p->kpfun = func;
+ p->kparg = arg;
+}
+
+/*
+ * called in sysfile.c
+ */
+void
+evenaddr(ulong addr)
+{
+ if(addr & 3){
+ postnote(up, 1, "sys: odd address", NDebug);
+ error(Ebadarg);
+ }
+}
+
+long
+execregs(ulong entry, ulong ssize, ulong nargs)
+{
+ ulong *sp;
+ Ureg *ureg;
+
+ sp = (ulong*)(USTKTOP - ssize);
+ *--sp = nargs;
+
+ ureg = up->dbgreg;
+ ureg->usp = (ulong)sp;
+ ureg->pc = entry;
+ ureg->srr1 &= ~MSR_FP;
+ return USTKTOP-sizeof(Tos); /* address of kernel/user shared data */
+}
+
+void
+forkchild(Proc *p, Ureg *ur)
+{
+ Ureg *cur;
+
+ p->sched.sp = (ulong)p->kstack+KSTACK-UREGSIZE;
+ p->sched.pc = (ulong)forkret;
+
+ cur = (Ureg*)(p->sched.sp+2*BY2WD);
+ memmove(cur, ur, sizeof(Ureg));
+ cur->r3 = 0;
+
+ /* Things from bottom of syscall we never got to execute */
+ p->psstate = 0;
+ p->insyscall = 0;
+}
+
+ulong
+userpc(void)
+{
+ Ureg *ureg;
+
+ ureg = (Ureg*)up->dbgreg;
+ return ureg->pc;
+}
+
+
+/* This routine must save the values of registers the user is not
+ * permitted to write from devproc and then restore the saved values
+ * before returning
+ */
+void
+setregisters(Ureg *xp, char *pureg, char *uva, int n)
+{
+ ulong status;
+
+ status = xp->status;
+ memmove(pureg, uva, n);
+ xp->status = status;
+}
+
+/* Give enough context in the ureg to produce a kernel stack for
+ * a sleeping process
+ */
+void
+setkernur(Ureg* ureg, Proc* p)
+{
+ ureg->pc = p->sched.pc;
+ ureg->sp = p->sched.sp+4;
+}
+
+ulong
+dbgpc(Proc *p)
+{
+ Ureg *ureg;
+
+ ureg = p->dbgreg;
+ if(ureg == 0)
+ return 0;
+
+ return ureg->pc;
+}
+
+/*
+ * system calls
+ */
+#include "../port/systab.h"
+
+/* TODO: make this trap a separate asm entry point, like on other RISC architectures */
+void
+syscall(Ureg* ureg)
+{
+ int i;
+ char *e;
+ long ret;
+ ulong sp, scallnr;
+
+ m->syscall++;
+ up->insyscall = 1;
+ up->pc = ureg->pc;
+ up->dbgreg = ureg;
+
+ scallnr = ureg->r3;
+ up->scallnr = ureg->r3;
+ spllo();
+
+ sp = ureg->usp;
+ up->nerrlab = 0;
+ ret = -1;
+ if(!waserror()){
+ if(scallnr >= nsyscall || systab[scallnr] == nil){
+ pprint("bad sys call number %d pc %lux\n", scallnr, ureg->pc);
+ postnote(up, 1, "sys: bad sys call", NDebug);
+ error(Ebadarg);
+ }
+
+ if(sp<(USTKTOP-BY2PG) || sp>(USTKTOP-sizeof(Sargs)-BY2WD))
+ validaddr(sp, sizeof(Sargs)+BY2WD, 0);
+
+ up->s = *((Sargs*)(sp+BY2WD));
+ up->psstate = sysctab[scallnr];
+
+ ret = systab[scallnr](up->s.args);
+ poperror();
+ }else{
+ /* failure: save the error buffer for errstr */
+ e = up->syserrstr;
+ up->syserrstr = up->errstr;
+ up->errstr = e;
+ }
+ if(up->nerrlab){
+ print("bad errstack [%uld]: %d extra\n", scallnr, up->nerrlab);
+ print("scall %s lr =%lux\n", sysctab[scallnr], ureg->lr);
+ for(i = 0; i < NERR; i++)
+ print("sp=%lux pc=%lux\n", up->errlab[i].sp, up->errlab[i].pc);
+ panic("error stack");
+ }
+
+ up->insyscall = 0;
+ up->psstate = 0;
+
+ /*
+ * Put return value in frame. On the x86 the syscall is
+ * just another trap and the return value from syscall is
+ * ignored. On other machines the return value is put into
+ * the results register by caller of syscall.
+ */
+ ureg->r3 = ret;
+
+ if(scallnr == NOTED)
+ noted(ureg, *(ulong*)(sp+BY2WD));
+
+ /* restoreureg must execute at high IPL */
+ splhi();
+ if(scallnr!=RFORK)
+ notify(ureg);
+ if(up->fpstate == FPinactive)
+ ureg->srr1 &= ~MSR_FP;
+}
+
+/*
+ * Call user, if necessary, with note.
+ * Pass user the Ureg struct and the note on his stack.
+ */
+int
+notify(Ureg* ur)
+{
+ int l;
+ ulong s, sp;
+ Note *n;
+
+ if(up->procctl)
+ procctl(up);
+ if(up->nnote == 0)
+ return 0;
+
+ s = spllo();
+ qlock(&up->debug);
+ up->notepending = 0;
+ n = &up->note[0];
+ if(strncmp(n->msg, "sys:", 4) == 0){
+ l = strlen(n->msg);
+ if(l > ERRMAX-15) /* " pc=0x12345678\0" */
+ l = ERRMAX-15;
+ sprint(n->msg+l, " pc=0x%.8lux", ur->pc);
+ }
+
+ if(n->flag!=NUser && (up->notified || up->notify==0)){
+ if(n->flag == NDebug)
+ pprint("suicide: %s\n", n->msg);
+ qunlock(&up->debug);
+ pexit(n->msg, n->flag!=NDebug);
+ }
+
+ if(up->notified) {
+ qunlock(&up->debug);
+ splhi();
+ return 0;
+ }
+
+ if(!up->notify) {
+ qunlock(&up->debug);
+ pexit(n->msg, n->flag!=NDebug);
+ }
+ sp = ur->usp & ~(BY2V-1);
+ sp -= sizeof(Ureg);
+
+ if(!okaddr((ulong)up->notify, BY2WD, 0) ||
+ !okaddr(sp-ERRMAX-4*BY2WD, sizeof(Ureg)+ERRMAX+4*BY2WD, 1)) {
+ pprint("suicide: bad address or sp in notify\n");
+ qunlock(&up->debug);
+ pexit("Suicide", 0);
+ }
+
+ memmove((Ureg*)sp, ur, sizeof(Ureg));
+ *(Ureg**)(sp-BY2WD) = up->ureg; /* word under Ureg is old up->ureg */
+ up->ureg = (void*)sp;
+ sp -= BY2WD+ERRMAX;
+ memmove((char*)sp, up->note[0].msg, ERRMAX);
+ sp -= 3*BY2WD;
+ *(ulong*)(sp+2*BY2WD) = sp+3*BY2WD; /* arg 2 is string */
+ ur->r1 = (long)up->ureg; /* arg 1 is ureg* */
+ ((ulong*)sp)[1] = (ulong)up->ureg; /* arg 1 0(FP) is ureg* */
+ ((ulong*)sp)[0] = 0; /* arg 0 is pc */
+ ur->usp = sp;
+ ur->pc = (ulong)up->notify;
+ up->notified = 1;
+ up->nnote--;
+ memmove(&up->lastnote, &up->note[0], sizeof(Note));
+ memmove(&up->note[0], &up->note[1], up->nnote*sizeof(Note));
+
+ qunlock(&up->debug);
+ splx(s);
+ return 1;
+}
+
+
+/*
+ * Return user to state before notify()
+ */
+void
+noted(Ureg* ureg, ulong arg0)
+{
+ Ureg *nureg;
+ ulong oureg, sp;
+
+ qlock(&up->debug);
+ if(arg0!=NRSTR && !up->notified) {
+ qunlock(&up->debug);
+ pprint("call to noted() when not notified\n");
+ pexit("Suicide", 0);
+ }
+ up->notified = 0;
+
+ nureg = up->ureg; /* pointer to user returned Ureg struct */
+
+ /* sanity clause */
+ oureg = (ulong)nureg;
+ if(!okaddr((ulong)oureg-BY2WD, BY2WD+sizeof(Ureg), 0)){
+ pprint("bad ureg in noted or call to noted when not notified\n");
+ qunlock(&up->debug);
+ pexit("Suicide", 0);
+ }
+
+ memmove(ureg, nureg, sizeof(Ureg));
+
+ switch(arg0){
+ case NCONT:
+ case NRSTR:
+ if(!okaddr(nureg->pc, 1, 0) || !okaddr(nureg->usp, BY2WD, 0)){
+ pprint("suicide: trap in noted\n");
+ qunlock(&up->debug);
+ pexit("Suicide", 0);
+ }
+ up->ureg = (Ureg*)(*(ulong*)(oureg-BY2WD));
+ qunlock(&up->debug);
+ break;
+
+ case NSAVE:
+ if(!okaddr(nureg->pc, BY2WD, 0)
+ || !okaddr(nureg->usp, BY2WD, 0)){
+ pprint("suicide: trap in noted\n");
+ qunlock(&up->debug);
+ pexit("Suicide", 0);
+ }
+ qunlock(&up->debug);
+ sp = oureg-4*BY2WD-ERRMAX;
+ splhi();
+ ureg->sp = sp;
+ ((ulong*)sp)[1] = oureg; /* arg 1 0(FP) is ureg* */
+ ((ulong*)sp)[0] = 0; /* arg 0 is pc */
+ break;
+
+ default:
+ pprint("unknown noted arg 0x%lux\n", arg0);
+ up->lastnote.flag = NDebug;
+ /* fall through */
+
+ case NDFLT:
+ if(up->lastnote.flag == NDebug)
+ pprint("suicide: %s\n", up->lastnote.msg);
+ qunlock(&up->debug);
+ pexit(up->lastnote.msg, up->lastnote.flag!=NDebug);
+ }
+}
diff --git a/sys/src/9/mtx/uarti8250.c b/sys/src/9/mtx/uarti8250.c
new file mode 100755
index 000000000..ce68e6868
--- /dev/null
+++ b/sys/src/9/mtx/uarti8250.c
@@ -0,0 +1,636 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+/*
+ * 8250 UART and compatibles.
+ */
+enum {
+ Uart0 = 0x3F8, /* COM1 */
+ Uart0IRQ = 4,
+ Uart1 = 0x2F8, /* COM2 */
+ Uart1IRQ = 3,
+
+ UartFREQ = 1843200,
+};
+
+enum { /* I/O ports */
+ Rbr = 0, /* Receiver Buffer (RO) */
+ Thr = 0, /* Transmitter Holding (WO) */
+ Ier = 1, /* Interrupt Enable */
+ Iir = 2, /* Interrupt Identification (RO) */
+ Fcr = 2, /* FIFO Control (WO) */
+ Lcr = 3, /* Line Control */
+ Mcr = 4, /* Modem Control */
+ Lsr = 5, /* Line Status */
+ Msr = 6, /* Modem Status */
+ Scr = 7, /* Scratch Pad */
+ Dll = 0, /* Divisor Latch LSB */
+ Dlm = 1, /* Divisor Latch MSB */
+};
+
+enum { /* Ier */
+ Erda = 0x01, /* Enable Received Data Available */
+ Ethre = 0x02, /* Enable Thr Empty */
+ Erls = 0x04, /* Enable Receiver Line Status */
+ Ems = 0x08, /* Enable Modem Status */
+};
+
+enum { /* Iir */
+ Ims = 0x00, /* Ms interrupt */
+ Ip = 0x01, /* Interrupt Pending (not) */
+ Ithre = 0x02, /* Thr Empty */
+ Irda = 0x04, /* Received Data Available */
+ Irls = 0x06, /* Receiver Line Status */
+ Ictoi = 0x0C, /* Character Time-out Indication */
+ IirMASK = 0x3F,
+ Ife = 0xC0, /* FIFOs enabled */
+};
+
+enum { /* Fcr */
+ FIFOena = 0x01, /* FIFO enable */
+ FIFOrclr = 0x02, /* clear Rx FIFO */
+ FIFOtclr = 0x04, /* clear Tx FIFO */
+ FIFO1 = 0x00, /* Rx FIFO trigger level 1 byte */
+ FIFO4 = 0x40, /* 4 bytes */
+ FIFO8 = 0x80, /* 8 bytes */
+ FIFO14 = 0xC0, /* 14 bytes */
+};
+
+enum { /* Lcr */
+ Wls5 = 0x00, /* Word Length Select 5 bits/byte */
+ Wls6 = 0x01, /* 6 bits/byte */
+ Wls7 = 0x02, /* 7 bits/byte */
+ Wls8 = 0x03, /* 8 bits/byte */
+ WlsMASK = 0x03,
+ Stb = 0x04, /* 2 stop bits */
+ Pen = 0x08, /* Parity Enable */
+ Eps = 0x10, /* Even Parity Select */
+ Stp = 0x20, /* Stick Parity */
+ Brk = 0x40, /* Break */
+ Dlab = 0x80, /* Divisor Latch Access Bit */
+};
+
+enum { /* Mcr */
+ Dtr = 0x01, /* Data Terminal Ready */
+ Rts = 0x02, /* Ready To Send */
+ Out1 = 0x04, /* no longer in use */
+ Ie = 0x08, /* IRQ Enable */
+ Dm = 0x10, /* Diagnostic Mode loopback */
+};
+
+enum { /* Lsr */
+ Dr = 0x01, /* Data Ready */
+ Oe = 0x02, /* Overrun Error */
+ Pe = 0x04, /* Parity Error */
+ Fe = 0x08, /* Framing Error */
+ Bi = 0x10, /* Break Interrupt */
+ Thre = 0x20, /* Thr Empty */
+ Temt = 0x40, /* Tramsmitter Empty */
+ FIFOerr = 0x80, /* error in receiver FIFO */
+};
+
+enum { /* Msr */
+ Dcts = 0x01, /* Delta Cts */
+ Ddsr = 0x02, /* Delta Dsr */
+ Teri = 0x04, /* Trailing Edge of Ri */
+ Ddcd = 0x08, /* Delta Dcd */
+ Cts = 0x10, /* Clear To Send */
+ Dsr = 0x20, /* Data Set Ready */
+ Ri = 0x40, /* Ring Indicator */
+ Dcd = 0x80, /* Data Set Ready */
+};
+
+typedef struct Ctlr {
+ int io;
+ int irq;
+ int tbdf;
+ int iena;
+
+ uchar sticky[8];
+
+ Lock;
+ int fifo;
+ int fena;
+} Ctlr;
+
+extern PhysUart i8250physuart;
+
+static Ctlr i8250ctlr[2] = {
+{ .io = Uart0,
+ .irq = Uart0IRQ,
+ .tbdf = BUSUNKNOWN, },
+
+{ .io = Uart1,
+ .irq = Uart1IRQ,
+ .tbdf = BUSUNKNOWN, },
+};
+
+static Uart i8250uart[2] = {
+{ .regs = &i8250ctlr[0],
+ .name = "COM1",
+ .freq = UartFREQ,
+ .phys = &i8250physuart,
+ .special=0,
+ .next = &i8250uart[1], },
+
+{ .regs = &i8250ctlr[1],
+ .name = "COM2",
+ .freq = UartFREQ,
+ .phys = &i8250physuart,
+ .special=0,
+ .next = nil, },
+};
+
+#define csr8r(c, r) inb((c)->io+(r))
+#define csr8w(c, r, v) outb((c)->io+(r), (c)->sticky[(r)]|(v))
+
+static long
+i8250status(Uart* uart, void* buf, long n, long offset)
+{
+ char *p;
+ Ctlr *ctlr;
+ uchar ier, lcr, mcr, msr;
+
+ ctlr = uart->regs;
+ p = malloc(READSTR);
+ mcr = ctlr->sticky[Mcr];
+ msr = csr8r(ctlr, Msr);
+ ier = ctlr->sticky[Ier];
+ lcr = ctlr->sticky[Lcr];
+ snprint(p, READSTR,
+ "b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d\n"
+ "dev(%d) type(%d) framing(%d) overruns(%d)%s%s%s%s\n",
+
+ uart->baud,
+ uart->hup_dcd,
+ (msr & Dsr) != 0,
+ uart->hup_dsr,
+ (lcr & WlsMASK) + 5,
+ (ier & Ems) != 0,
+ (lcr & Pen) ? ((lcr & Eps) ? 'e': 'o'): 'n',
+ (mcr & Rts) != 0,
+ (lcr & Stb) ? 2: 1,
+ ctlr->fena,
+
+ uart->dev,
+ uart->type,
+ uart->ferr,
+ uart->oerr,
+ (msr & Cts) ? " cts": "",
+ (msr & Dsr) ? " dsr": "",
+ (msr & Dcd) ? " dcd": "",
+ (msr & Ri) ? " ring": ""
+ );
+ n = readstr(offset, buf, n, p);
+ free(p);
+
+ return n;
+}
+
+static void
+i8250fifo(Uart* uart, int on)
+{
+ int i;
+ Ctlr *ctlr;
+
+ /*
+ * Toggle FIFOs:
+ * if none, do nothing;
+ * reset the Rx and Tx FIFOs;
+ * empty the Rx buffer and clear any interrupt conditions;
+ * if enabling, try to turn them on.
+ */
+ ctlr = uart->regs;
+
+ ilock(ctlr);
+ if(!ctlr->fifo){
+ csr8w(ctlr, Fcr, FIFOtclr|FIFOrclr);
+ for(i = 0; i < 16; i++){
+ csr8r(ctlr, Iir);
+ csr8r(ctlr, Rbr);
+ }
+
+ ctlr->fena = 0;
+ if(on){
+ csr8w(ctlr, Fcr, FIFO4|FIFOena);
+ if(!(csr8r(ctlr, Iir) & Ife))
+ ctlr->fifo = 1;
+ ctlr->fena = 1;
+ }
+ }
+ iunlock(ctlr);
+}
+
+static void
+i8250dtr(Uart* uart, int on)
+{
+ Ctlr *ctlr;
+
+ /*
+ * Toggle DTR.
+ */
+ ctlr = uart->regs;
+ if(on)
+ ctlr->sticky[Mcr] |= Dtr;
+ else
+ ctlr->sticky[Mcr] &= ~Dtr;
+ csr8w(ctlr, Mcr, 0);
+}
+
+static void
+i8250rts(Uart* uart, int on)
+{
+ Ctlr *ctlr;
+
+ /*
+ * Toggle RTS.
+ */
+ ctlr = uart->regs;
+ if(on)
+ ctlr->sticky[Mcr] |= Rts;
+ else
+ ctlr->sticky[Mcr] &= ~Rts;
+ csr8w(ctlr, Mcr, 0);
+}
+
+static void
+i8250modemctl(Uart* uart, int on)
+{
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ ilock(&uart->tlock);
+ if(on){
+ ctlr->sticky[Ier] |= Ems;
+ csr8w(ctlr, Ier, 0);
+ uart->modem = 1;
+ uart->cts = csr8r(ctlr, Msr) & Cts;
+ }
+ else{
+ ctlr->sticky[Ier] &= ~Ems;
+ csr8w(ctlr, Ier, 0);
+ uart->modem = 0;
+ uart->cts = 1;
+ }
+ iunlock(&uart->tlock);
+
+ /* modem needs fifo */
+ (*uart->phys->fifo)(uart, on);
+}
+
+static int
+i8250parity(Uart* uart, int parity)
+{
+ int lcr;
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ lcr = ctlr->sticky[Lcr] & ~(Eps|Pen);
+
+ switch(parity){
+ case 'e':
+ lcr |= Eps|Pen;
+ break;
+ case 'o':
+ lcr |= Pen;
+ break;
+ case 'n':
+ default:
+ break;
+ }
+ ctlr->sticky[Lcr] = lcr;
+ csr8w(ctlr, Lcr, 0);
+
+ uart->parity = parity;
+
+ return 0;
+}
+
+static int
+i8250stop(Uart* uart, int stop)
+{
+ int lcr;
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ lcr = ctlr->sticky[Lcr] & ~Stb;
+
+ switch(stop){
+ case 1:
+ break;
+ case 2:
+ lcr |= Stb;
+ break;
+ default:
+ return -1;
+ }
+ ctlr->sticky[Lcr] = lcr;
+ csr8w(ctlr, Lcr, 0);
+
+ uart->stop = stop;
+
+ return 0;
+}
+
+static int
+i8250bits(Uart* uart, int bits)
+{
+ int lcr;
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ lcr = ctlr->sticky[Lcr] & ~WlsMASK;
+
+ switch(bits){
+ case 5:
+ lcr |= Wls5;
+ break;
+ case 6:
+ lcr |= Wls6;
+ break;
+ case 7:
+ lcr |= Wls7;
+ break;
+ case 8:
+ lcr |= Wls8;
+ break;
+ default:
+ return -1;
+ }
+ ctlr->sticky[Lcr] = lcr;
+ csr8w(ctlr, Lcr, 0);
+
+ uart->bits = bits;
+
+ return 0;
+}
+
+static int
+i8250baud(Uart* uart, int baud)
+{
+ ulong bgc;
+ Ctlr *ctlr;
+
+ /*
+ * Set the Baud rate by calculating and setting the Baud rate
+ * Generator Constant. This will work with fairly non-standard
+ * Baud rates.
+ */
+ if(uart->freq == 0 || baud <= 0)
+ return -1;
+ bgc = (uart->freq+8*baud-1)/(16*baud);
+
+ ctlr = uart->regs;
+ csr8w(ctlr, Lcr, Dlab);
+ outb(ctlr->io+Dlm, bgc>>8);
+ outb(ctlr->io+Dll, bgc);
+ csr8w(ctlr, Lcr, 0);
+
+ uart->baud = baud;
+
+ return 0;
+}
+
+static void
+i8250break(Uart* uart, int ms)
+{
+ Ctlr *ctlr;
+
+ /*
+ * Send a break.
+ */
+ if(ms == 0)
+ ms = 200;
+
+ ctlr = uart->regs;
+ csr8w(ctlr, Lcr, Brk);
+ tsleep(&up->sleep, return0, 0, ms);
+ csr8w(ctlr, Lcr, 0);
+}
+
+static void
+i8250kick(Uart* uart)
+{
+ int i;
+ Ctlr *ctlr;
+
+ if(uart->cts == 0 || uart->blocked)
+ return;
+
+ /*
+ * 128 here is an arbitrary limit to make sure
+ * we don't stay in this loop too long. If the
+ * chip's output queue is longer than 128, too
+ * bad -- presotto
+ */
+ ctlr = uart->regs;
+ for(i = 0; i < 128; i++){
+ if(!(csr8r(ctlr, Lsr) & Thre))
+ break;
+ if(uart->op >= uart->oe && uartstageoutput(uart) == 0)
+ break;
+ outb(ctlr->io+Thr, *(uart->op++));
+ }
+}
+
+static void
+i8250interrupt(Ureg*, void* arg)
+{
+ Ctlr *ctlr;
+ Uart *uart;
+ int iir, lsr, old, r;
+
+ uart = arg;
+
+ ctlr = uart->regs;
+ for(iir = csr8r(ctlr, Iir); !(iir & Ip); iir = csr8r(ctlr, Iir)){
+ switch(iir & IirMASK){
+ case Ims: /* Ms interrupt */
+ r = csr8r(ctlr, Msr);
+ if(r & Dcts){
+ ilock(&uart->tlock);
+ old = uart->cts;
+ uart->cts = r & Cts;
+ if(old == 0 && uart->cts)
+ uart->ctsbackoff = 2;
+ iunlock(&uart->tlock);
+ }
+ if(r & Ddsr){
+ old = r & Dsr;
+ if(uart->hup_dsr && uart->dsr && !old)
+ uart->dohup = 1;
+ uart->dsr = old;
+ }
+ if(r & Ddcd){
+ old = r & Dcd;
+ if(uart->hup_dcd && uart->dcd && !old)
+ uart->dohup = 1;
+ uart->dcd = old;
+ }
+ break;
+ case Ithre: /* Thr Empty */
+ uartkick(uart);
+ break;
+ case Irda: /* Received Data Available */
+ case Ictoi: /* Character Time-out Indication */
+ /*
+ * Consume any received data.
+ * If the received byte came in with a break,
+ * parity or framing error, throw it away;
+ * overrun is an indication that something has
+ * already been tossed.
+ */
+ while((lsr = csr8r(ctlr, Lsr)) & Dr){
+ if(lsr & Oe)
+ uart->oerr++;
+ if(lsr & Pe)
+ uart->perr++;
+ if(lsr & Fe)
+ uart->ferr++;
+ r = csr8r(ctlr, Rbr);
+ if(!(lsr & (Bi|Fe|Pe)))
+ uartrecv(uart, r);
+ }
+ break;
+ default:
+ iprint("weird uart interrupt 0x%2.2uX\n", iir);
+ break;
+ }
+ }
+}
+
+static void
+i8250disable(Uart* uart)
+{
+ Ctlr *ctlr;
+
+ /*
+ * Turn off DTR and RTS, disable interrupts and fifos.
+ */
+ (*uart->phys->dtr)(uart, 0);
+ (*uart->phys->rts)(uart, 0);
+ (*uart->phys->fifo)(uart, 0);
+
+ ctlr = uart->regs;
+ ctlr->sticky[Ier] = 0;
+ csr8w(ctlr, Ier, 0);
+}
+
+static void
+i8250enable(Uart* uart, int ie)
+{
+ Ctlr *ctlr;
+
+ /*
+ * Enable interrupts and turn on DTR and RTS.
+ * Be careful if this is called to set up a polled serial line
+ * early on not to try to enable interrupts as interrupt-
+ * -enabling mechanisms might not be set up yet.
+ */
+ ctlr = uart->regs;
+ if(ie){
+ if(ctlr->iena == 0){
+ intrenable(ctlr->irq, i8250interrupt, uart, ctlr->tbdf, uart->name);
+ ctlr->iena = 1;
+ }
+ ctlr->sticky[Ier] = Ethre|Erda;
+ ctlr->sticky[Mcr] |= Ie;
+ }
+ else{
+ ctlr->sticky[Ier] = 0;
+ ctlr->sticky[Mcr] = 0;
+ }
+ csr8w(ctlr, Ier, ctlr->sticky[Ier]);
+ csr8w(ctlr, Mcr, ctlr->sticky[Mcr]);
+
+ (*uart->phys->dtr)(uart, 1);
+ (*uart->phys->rts)(uart, 1);
+}
+
+static Uart*
+i8250pnp(void)
+{
+ return i8250uart;
+}
+
+static int
+i8250getc(Uart *uart)
+{
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ while(!(csr8r(ctlr, Lsr)&Dr))
+ delay(1);
+ return csr8r(ctlr, Rbr);
+}
+
+static void
+i8250putc(Uart *uart, int c)
+{
+ int i;
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ for(i = 0; !(csr8r(ctlr, Lsr)&Thre) && i < 128; i++)
+ delay(1);
+ outb(ctlr->io+Thr, c);
+ for(i = 0; !(csr8r(ctlr, Lsr)&Thre) && i < 128; i++)
+ delay(1);
+}
+
+PhysUart i8250physuart = {
+ .name = "i8250",
+ .pnp = i8250pnp,
+ .enable = i8250enable,
+ .disable = i8250disable,
+ .kick = i8250kick,
+ .dobreak = i8250break,
+ .baud = i8250baud,
+ .bits = i8250bits,
+ .stop = i8250stop,
+ .parity = i8250parity,
+ .modemctl = i8250modemctl,
+ .rts = i8250rts,
+ .dtr = i8250dtr,
+ .status = i8250status,
+ .fifo = i8250fifo,
+ .getc = i8250getc,
+ .putc = i8250putc,
+};
+
+void
+i8250console(void)
+{
+ Uart *uart;
+ int n;
+ char *cmd, *p;
+
+ if((p = getconf("console")) == nil)
+ return;
+ n = strtoul(p, &cmd, 0);
+ if(p == cmd)
+ return;
+ switch(n){
+ default:
+ return;
+ case 0:
+ uart = &i8250uart[0];
+ break;
+ case 1:
+ uart = &i8250uart[1];
+ break;
+ }
+
+ uartctl(uart, "b9600 l8 pn s1");
+ if(*cmd != '\0')
+ uartctl(uart, cmd);
+ (*uart->phys->enable)(uart, 0);
+
+ consuart = uart;
+ uart->console = 1;
+}