summaryrefslogtreecommitdiff
path: root/sys/src/9/kw
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/kw
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/kw')
-rwxr-xr-xsys/src/9/kw/arch.c222
-rwxr-xr-xsys/src/9/kw/archkw.c519
-rwxr-xr-xsys/src/9/kw/arm.h194
-rwxr-xr-xsys/src/9/kw/arm.s72
-rwxr-xr-xsys/src/9/kw/cga.c132
-rwxr-xr-xsys/src/9/kw/clock.c205
-rwxr-xr-xsys/src/9/kw/coproc.c153
-rwxr-xr-xsys/src/9/kw/dat.h320
-rwxr-xr-xsys/src/9/kw/devarch.c265
-rwxr-xr-xsys/src/9/kw/devether.c528
-rwxr-xr-xsys/src/9/kw/devrtc.c359
-rwxr-xr-xsys/src/9/kw/devtwsi.c305
-rwxr-xr-xsys/src/9/kw/devusb.c1460
-rwxr-xr-xsys/src/9/kw/ether1116.c1747
-rwxr-xr-xsys/src/9/kw/etherif.h55
-rwxr-xr-xsys/src/9/kw/ethermii.c235
-rwxr-xr-xsys/src/9/kw/ethermii.h143
-rwxr-xr-xsys/src/9/kw/flashkw.c671
-rwxr-xr-xsys/src/9/kw/fns.h203
-rwxr-xr-xsys/src/9/kw/fpi.c300
-rwxr-xr-xsys/src/9/kw/fpi.h61
-rwxr-xr-xsys/src/9/kw/fpiarm.c576
-rwxr-xr-xsys/src/9/kw/fpimem.c136
-rwxr-xr-xsys/src/9/kw/init9.s25
-rwxr-xr-xsys/src/9/kw/io.h425
-rwxr-xr-xsys/src/9/kw/l.s774
-rwxr-xr-xsys/src/9/kw/lexception.s187
-rwxr-xr-xsys/src/9/kw/lproc.s47
-rwxr-xr-xsys/src/9/kw/main.c678
-rwxr-xr-xsys/src/9/kw/mem.h142
-rwxr-xr-xsys/src/9/kw/mkfile156
-rwxr-xr-xsys/src/9/kw/mmu.c522
-rwxr-xr-xsys/src/9/kw/notes/movm.w22
-rwxr-xr-xsys/src/9/kw/nvrambin0 -> 512 bytes
-rwxr-xr-xsys/src/9/kw/openrd.words21
-rwxr-xr-xsys/src/9/kw/plug80
-rwxr-xr-xsys/src/9/kw/plug.words104
-rwxr-xr-xsys/src/9/kw/random.c138
-rwxr-xr-xsys/src/9/kw/rebootcode.s172
-rwxr-xr-xsys/src/9/kw/softfpu.c119
-rwxr-xr-xsys/src/9/kw/syscall.c354
-rwxr-xr-xsys/src/9/kw/trap.c671
-rwxr-xr-xsys/src/9/kw/uartkw.c362
-rwxr-xr-xsys/src/9/kw/ucalloc.c135
-rwxr-xr-xsys/src/9/kw/uncached.h36
-rwxr-xr-xsys/src/9/kw/usbehci.h242
-rwxr-xr-xsys/src/9/kw/usbehcikw.c350
47 files changed, 14623 insertions, 0 deletions
diff --git a/sys/src/9/kw/arch.c b/sys/src/9/kw/arch.c
new file mode 100755
index 000000000..c800ffc77
--- /dev/null
+++ b/sys/src/9/kw/arch.c
@@ -0,0 +1,222 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include <tos.h>
+#include "ureg.h"
+
+#include "arm.h"
+
+/*
+ * A lot of this stuff doesn't belong here
+ * but this is a convenient dumping ground for
+ * later sorting into the appropriate buckets.
+ */
+
+/* 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;
+ ureg->r14 = PTR2UINT(sched);
+}
+
+/*
+ * called in sysfile.c
+ */
+void
+evenaddr(uintptr addr)
+{
+ if(addr & 3){
+ postnote(up, 1, "sys: odd address", NDebug);
+ error(Ebadarg);
+ }
+}
+
+/* go to user space */
+void
+kexit(Ureg*)
+{
+ uvlong t;
+ Tos *tos;
+
+ /* precise time accounting, kernel exit */
+ tos = (Tos*)(USTKTOP-sizeof(Tos));
+ t = fastticks(nil);
+ tos->kcycles += t - up->kentry;
+ tos->pcycles = up->pcycles;
+ tos->cyclefreq = Frequency;
+ tos->pid = up->pid;
+
+ /* make visible immediately to user proc */
+ cachedwbinvse(tos, sizeof *tos);
+}
+
+/*
+ * return the userpc the last exception happened at
+ */
+uintptr
+userpc(void)
+{
+ 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* ureg, char* pureg, char* uva, int n)
+{
+ USED(ureg, pureg, uva, n);
+}
+
+/*
+ * this is the body for all kproc's
+ */
+static void
+linkproc(void)
+{
+ spllo();
+ up->kpfun(up->kparg);
+ pexit("kproc exiting", 0);
+}
+
+/*
+ * setup stack and initial PC for a new kernel proc. This is architecture
+ * dependent because of the starting stack location
+ */
+void
+kprocchild(Proc *p, void (*func)(void*), void *arg)
+{
+ p->sched.pc = PTR2UINT(linkproc);
+ p->sched.sp = PTR2UINT(p->kstack+KSTACK);
+
+ p->kpfun = func;
+ p->kparg = arg;
+}
+
+/*
+ * pc output by dumpaproc
+ */
+uintptr
+dbgpc(Proc* p)
+{
+ Ureg *ureg;
+
+ ureg = p->dbgreg;
+ if(ureg == 0)
+ return 0;
+
+ return ureg->pc;
+}
+
+/*
+ * set mach dependent process state for a new process
+ */
+void
+procsetup(Proc* p)
+{
+ fpusysprocsetup(p);
+}
+
+/*
+ * Save the mach dependent part of the process state.
+ */
+void
+procsave(Proc* p)
+{
+ uvlong t;
+
+ cycles(&t);
+ p->pcycles += t;
+
+ fpuprocsave(p);
+}
+
+void
+procrestore(Proc* p)
+{
+ uvlong t;
+
+ if(p->kp)
+ return;
+ t = lcycles();
+ p->pcycles -= t;
+
+ fpuprocrestore(p);
+}
+
+int
+userureg(Ureg* ureg)
+{
+ return (ureg->psr & PsrMask) == PsrMusr;
+}
+
+/*
+ * atomic ops
+ * make sure that we don't drag in the C library versions
+ */
+
+long
+_xdec(long *p)
+{
+ int s, v;
+
+ s = splhi();
+ v = --*p;
+ splx(s);
+ return v;
+}
+
+void
+_xinc(long *p)
+{
+ int s;
+
+ s = splhi();
+ ++*p;
+ splx(s);
+}
+
+int
+ainc(int *p)
+{
+ int s, v;
+
+ s = splhi();
+ v = ++*p;
+ splx(s);
+ return v;
+}
+
+int
+adec(int *p)
+{
+ int s, v;
+
+ s = splhi();
+ v = --*p;
+ splx(s);
+ return v;
+}
+
+int
+cas32(void* addr, u32int old, u32int new)
+{
+ int r, s;
+
+ s = splhi();
+ if(r = (*(u32int*)addr == old))
+ *(u32int*)addr = new;
+ splx(s);
+ if (r)
+ coherence();
+ return r;
+}
diff --git a/sys/src/9/kw/archkw.c b/sys/src/9/kw/archkw.c
new file mode 100755
index 000000000..d6fd700fa
--- /dev/null
+++ b/sys/src/9/kw/archkw.c
@@ -0,0 +1,519 @@
+/*
+ * stuff specific to marvell's kirkwood architecture
+ * as seen in the sheevaplug
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+#include "../port/netif.h"
+#include "etherif.h"
+#include "../port/flashif.h"
+
+#include "arm.h"
+
+enum {
+ L2writeback = 1,
+ Debug = 0,
+};
+
+typedef struct GpioReg GpioReg;
+struct GpioReg {
+ ulong dataout;
+ ulong dataoutena;
+ ulong blinkena;
+ ulong datainpol;
+ ulong datain;
+ ulong intrcause;
+ ulong intrmask;
+ ulong intrlevelmask;
+};
+
+typedef struct L2uncache L2uncache;
+typedef struct L2win L2win;
+struct L2uncache {
+ struct L2win {
+ ulong base; /* phys addr */
+ ulong size;
+ } win[4];
+};
+
+enum {
+ /* L2win->base bits */
+ L2enable = 1<<0,
+};
+
+typedef struct Dramctl Dramctl;
+struct Dramctl {
+ ulong ctl;
+ ulong ddrctllo;
+ struct {
+ ulong lo;
+ ulong hi;
+ } time;
+ ulong addrctl;
+ ulong opagectl;
+ ulong oper;
+ ulong mode;
+ ulong extmode;
+ ulong ddrctlhi;
+ ulong ddr2timelo;
+ ulong operctl;
+ struct {
+ ulong lo;
+ ulong hi;
+ } mbusctl;
+ ulong mbustimeout;
+ ulong ddrtimehi;
+ ulong sdinitctl;
+ ulong extsdmode1;
+ ulong extsdmode2;
+ struct {
+ ulong lo;
+ ulong hi;
+ } odtctl;
+ ulong ddrodtctl;
+ ulong rbuffsel;
+
+ ulong accalib;
+ ulong dqcalib;
+ ulong dqscalib;
+};
+
+/* unused so far */
+typedef struct SDramdReg SDramdReg;
+struct SDramdReg {
+ struct {
+ ulong base;
+ ulong size;
+ } win[4];
+};
+
+typedef struct Addrmap Addrmap;
+typedef struct Addrwin Addrwin;
+struct Addrmap {
+ struct Addrwin {
+ ulong ctl; /* see Winenable in io.h */
+ ulong base;
+ ulong remaplo;
+ ulong remaphi;
+ } win[8];
+ ulong dirba; /* device internal reg's base addr.: PHYSIO */
+};
+
+Soc soc = {
+ .cpu = PHYSIO+0x20100,
+ .devid = PHYSIO+0x10034,
+ .l2cache = PHYSIO+0x20a00, /* uncachable addrs for L2 */
+ .sdramc = PHYSIO+0x01400,
+// .sdramd = PHYSIO+0x01500, /* unused */
+
+ .iocfg = PHYSIO+0x100e0,
+ .addrmap = PHYSIO+0x20000,
+ .intr = PHYSIO+0x20200,
+ .nand = PHYSIO+0x10418,
+ .cesa = PHYSIO+0x30000, /* crypto accelerator */
+ .ehci = PHYSIO+0x50000,
+ .spi = PHYSIO+0x10600,
+ .twsi = PHYSIO+0x11000,
+
+ .analog = PHYSIO+0x1007c,
+ .pci = PHYSIO+0x40000,
+ .pcibase = PHYSIO+0x41800,
+
+ .rtc = PHYSIO+0x10300,
+ .clock = PHYSIO+0x20300,
+// .clockctl = PHYSIO+0x1004c, /* unused */
+
+ .ether = { PHYSIO+0x72000, PHYSIO+0x76000, },
+ .sata = { PHYSIO+0x80000, /* sata config reg here */
+ PHYSIO+0x82000, /* edma config reg here */
+ PHYSIO+0x84000, /* edma config reg here */
+ },
+ .uart = { PHYSIO+0x12000, PHYSIO+0x12100, },
+ .gpio = { PHYSIO+0x10100, PHYSIO+0x10140, },
+};
+
+/*
+ * sheeva/openrd u-boot leaves us with this address map:
+ *
+ * 0 targ 4 attr 0xe8 size 256MB addr 0x9:: remap addr 0x9:: pci mem
+ * 1 targ 1 attr 0x2f size 8MB addr 0xf9:: remap addr 0xf9:: nand flash
+ * 2 targ 4 attr 0xe0 size 16MB addr 0xf:: remap addr 0xc:: pci i/o
+ * 3 targ 1 attr 0x1e size 16MB addr 0xf8:: remap addr 0x0 spi flash
+ * 4 targ 1 attr 0x1d size 16MB addr 0xff:: boot rom
+ * 5 targ 1 attr 0x1e size 128MB addr 0xe8:: disabled spi flash
+ * 6 targ 1 attr 0x1d size 128MB addr 0xf:: disabled boot rom
+ * 7 targ 3 attr 0x1 size 64K addr 0xfb:: crypto sram
+ */
+#define WINTARG(ctl) (((ctl) >> 4) & 017)
+#define WINATTR(ctl) (((ctl) >> 8) & 0377)
+#define WIN64KSIZE(ctl) (((ctl) >> 16) + 1)
+
+static void
+praddrwin(Addrwin *win, int i)
+{
+ ulong ctl, targ, attr, size64k;
+
+ if (!Debug) {
+ USED(win, i);
+ return;
+ }
+ ctl = win->ctl;
+ targ = WINTARG(ctl);
+ attr = WINATTR(ctl);
+ size64k = WIN64KSIZE(ctl);
+ print("cpu addr map: %s window %d: targ %ld attr %#lux size %,ld addr %#lux",
+ ctl & Winenable? "enabled": "disabled", i, targ, attr,
+ size64k * 64*1024, win->base);
+ if (i < 4)
+ print(" remap addr %#llux", (uvlong)win->remaphi<<32 |
+ win->remaplo);
+ print("\n");
+}
+
+static void
+fixaddrmap(void)
+{
+ int i;
+ ulong ctl, targ, attr, size64k;
+ Addrmap *map;
+ Addrwin *win;
+
+ map = (Addrmap *)soc.addrmap;
+ for (i = 0; i < nelem(map->win); i++) {
+ win = &map->win[i];
+ ctl = win->ctl;
+ targ = WINTARG(ctl);
+ attr = WINATTR(ctl);
+ size64k = WIN64KSIZE(ctl);
+
+ USED(attr, size64k);
+ if (targ == Targcesasram) {
+ win->ctl |= Winenable;
+ win->base = PHYSCESASRAM;
+ coherence();
+ praddrwin(win, i);
+ }
+ }
+ if (map->dirba != PHYSIO)
+ panic("dirba not %#ux", PHYSIO);
+}
+
+static void
+praddrmap(void)
+{
+ int i;
+ Addrmap *map;
+
+ map = (Addrmap *)soc.addrmap;
+ for (i = 0; i < nelem(map->win); i++)
+ praddrwin(&map->win[i], i);
+}
+
+int
+ispow2(uvlong ul)
+{
+ /* see Hacker's Delight if this isn't obvious */
+ return (ul & (ul - 1)) == 0;
+}
+
+/*
+ * return exponent of smallest power of 2 ≥ n
+ */
+int
+log2(ulong n)
+{
+ int i;
+
+ i = 31 - clz(n);
+ if (!ispow2(n) || n == 0)
+ i++;
+ return i;
+}
+
+void
+cacheinfo(int level, int kind, Memcache *cp) /* l1 only */
+{
+ uint len, assoc, size;
+ ulong setsways;
+
+ /* get cache types & sizes (read-only reg) */
+ setsways = cprdsc(0, CpID, CpIDidct, CpIDct);
+
+ cp->level = level;
+ cp->kind = kind;
+
+ if ((setsways & (1<<24)) == 0)
+ kind = Unified;
+ if (kind != Icache)
+ setsways >>= 12;
+
+ assoc = (setsways >> 3) & MASK(3);
+ cp->nways = 1 << assoc;
+ size = (setsways >> 6) & MASK(4);
+ cp->size = 1 << (size + 9);
+ len = setsways & MASK(2);
+ cp->log2linelen = len + 3;
+ cp->linelen = 1 << cp->log2linelen;
+ cp->setsways = setsways;
+
+ cp->nsets = 1 << (size + 6 - assoc - len);
+ cp->setsh = cp->log2linelen;
+ cp->waysh = 32 - log2(cp->nways);
+}
+
+static char *
+wbtype(uint type)
+{
+ static char *types[] = {
+ "write-through",
+ "read data block",
+ "reg 7 ops, no lock-down",
+ [06] "reg 7 ops, format A",
+ [07] "reg 7 ops, format B deprecated",
+ [016] "reg 7 ops, format C",
+ [05] "reg 7 ops, format D",
+ };
+
+ if (type >= nelem(types) || types[type] == nil)
+ return "GOK";
+ return types[type];
+}
+
+static void
+prcache(Memcache *mcp)
+{
+ int type;
+ char id;
+
+ if (mcp->kind == Unified)
+ id = 'U';
+ else if (mcp->kind == Icache)
+ id = 'I';
+ else if (mcp->kind == Dcache)
+ id = 'D';
+ else
+ id = '?';
+ print("l%d %c: %d bytes, %d ways %d sets %d bytes/line",
+ mcp->level, id, mcp->size, mcp->nways, mcp->nsets,
+ mcp->linelen);
+ if (mcp->linelen != CACHELINESZ)
+ print(" *should* be %d", CACHELINESZ);
+ type = (mcp->setsways >> 25) & MASK(4);
+ if (type == 0)
+ print("; write-through only");
+ else
+ print("; write-back type `%s' (%#o) possible",
+ wbtype(type), type);
+ if (mcp->setsways & (1<<11))
+ print("; page table mapping restrictions apply");
+ if (mcp->setsways & (1<<2))
+ print("; M bit is set in cache type reg");
+ print("\n");
+}
+
+static void
+prcachecfg(void)
+{
+ Memcache mc;
+
+ cacheinfo(1, Dcache, &mc);
+ prcache(&mc);
+ cacheinfo(1, Icache, &mc);
+ prcache(&mc);
+}
+
+void
+l2cacheon(void)
+{
+ ulong cfg;
+ CpucsReg *cpu;
+ L2uncache *l2p;
+
+ cacheuwbinv();
+ l2cacheuwbinv();
+ l1cachesoff(); /* turns off L2 as a side effect */
+
+ cpwrsc(CpDef, CpCLD, 0, 0, 0); /* GL-CPU-100: set D cache lockdown reg. */
+
+ /* marvell guideline GL-CPU-130 */
+ cpu = (CpucsReg *)soc.cpu;
+ cfg = cpu->cpucfg | L2exists | L2ecc | Cfgiprefetch | Cfgdprefetch;
+
+ if (L2writeback)
+ cfg &= ~L2writethru; /* see PTE Cached & Buffered bits */
+ else
+ cfg |= L2writethru;
+ cpu->l2cfg = cfg;
+ coherence(); /* force l2 cache to pay attention */
+ cpu->l2tm1 = cpu->l2tm0 = 0x66666666; /* marvell guideline GL-CPU-120 */
+ coherence();
+
+ cpwrsc(CpL2, CpTESTCFG, CpTCl2waylck, CpTCl2waylock, 0);
+
+ cachedinv();
+ l2cacheuinv();
+
+ /* disable l2 caching of i/o registers */
+ l2p = (L2uncache *)soc.l2cache;
+ memset(l2p, 0, sizeof *l2p);
+ /*
+ * l2: don't cache upper half of address space.
+ * the L2 cache is PIPT, so the addresses are physical.
+ */
+ l2p->win[0].base = 0x80000000 | L2enable; /* 64K multiple */
+ l2p->win[0].size = (32*1024-1) << 16; /* 64K multiples */
+ coherence();
+
+ l2cachecfgon();
+ l1cacheson(); /* turns L2 on as a side effect */
+ print("l2 cache: 256K or 512K: 4 ways, 32-byte lines, write-%s, sdram only\n",
+ cpu->l2cfg & L2writethru? "through": "back");
+}
+
+/* called late in main */
+void
+archconfinit(void)
+{
+ m->cpuhz = Frequency;
+ m->delayloop = m->cpuhz/2000; /* initial estimate */
+ fixaddrmap();
+ if (Debug)
+ praddrmap();
+ prcachecfg();
+
+ l2cacheon();
+}
+
+void
+archkwlink(void)
+{
+}
+
+int
+archether(unsigned ctlno, Ether *ether)
+{
+ if(ctlno >= 2)
+ return -1;
+ ether->type = "88e1116";
+ ether->port = ctlno;
+// ether->mbps = 1000;
+ return 1;
+}
+
+/* LED/USB gpios */
+enum {
+ /*
+ * the bit assignments are MPP pin numbers from the last page of the
+ * sheevaplug 6.0.1 schematic.
+ */
+ KWOEValHigh = 1<<(49-32), /* pin 49: LED pin */
+ KWOEValLow = 1<<29, /* pin 29: USB_PWEN, pin 28: usb_pwerr */
+ KWOELow = ~0,
+ KWOEHigh = ~0,
+};
+
+/* called early in main */
+void
+archreset(void)
+{
+ ulong clocks;
+ CpucsReg *cpu;
+ Dramctl *dram;
+ GpioReg *gpio;
+
+ clockshutdown(); /* watchdog disabled */
+
+ /* configure gpios */
+ gpio = (GpioReg*)soc.gpio[0];
+ gpio->dataout = KWOEValLow;
+ coherence();
+ gpio->dataoutena = KWOELow;
+
+ gpio = (GpioReg*)soc.gpio[1];
+ gpio->dataout = KWOEValHigh;
+ coherence();
+ gpio->dataoutena = KWOEHigh;
+ coherence();
+
+ cpu = (CpucsReg *)soc.cpu;
+ cpu->mempm = 0; /* turn everything on */
+ coherence();
+
+ clocks = MASK(10);
+ clocks |= MASK(21) & ~MASK(14);
+ clocks &= ~(1<<18 | 1<<1); /* reserved bits */
+ cpu->clockgate |= clocks; /* enable all the clocks */
+ cpu->l2cfg |= L2exists; /* when L2exists is 0, the l2 ignores us */
+ coherence();
+
+ dram = (Dramctl *)soc.sdramc;
+ dram->ddrctllo &= ~(1<<6); /* marvell guideline GL-MEM-70 */
+
+ *(ulong *)soc.analog = 0x68; /* marvell guideline GL-MISC-40 */
+ coherence();
+}
+
+void
+archreboot(void)
+{
+ CpucsReg *cpu;
+
+ iprint("reset!\n");
+ delay(10);
+
+ cpu = (CpucsReg *)soc.cpu;
+ cpu->rstout = RstoutSoft;
+ cpu->softreset = ResetSystem;
+ coherence();
+ cpu->cpucsr = Reset;
+ coherence();
+ delay(500);
+
+ splhi();
+ iprint("waiting...");
+ for(;;)
+ idlehands();
+}
+
+void
+archconsole(void)
+{
+// uartconsole(0, "b115200");
+//serialputs("uart0 console @ 115200\n", strlen("uart0 console @ 115200\n"));
+}
+
+void
+archflashwp(Flash*, int)
+{
+}
+
+int flashat(Flash *f, uintptr pa);
+
+/*
+ * for ../port/devflash.c:/^flashreset
+ * retrieve flash type, virtual base and length and return 0;
+ * return -1 on error (no flash)
+ */
+int
+archflashreset(int bank, Flash *f)
+{
+ if(bank != 0)
+ return -1;
+ f->type = "nand";
+ if (flashat(f, PHYSNAND1))
+ f->addr = (void*)PHYSNAND1;
+ else if (flashat(f, PHYSNAND2))
+ f->addr = (void*)PHYSNAND2;
+ else
+ f->addr = nil;
+ f->size = 0; /* done by probe */
+ f->width = 1;
+ f->interleave = 0;
+ return 0;
+}
diff --git a/sys/src/9/kw/arm.h b/sys/src/9/kw/arm.h
new file mode 100755
index 000000000..a9e2ff100
--- /dev/null
+++ b/sys/src/9/kw/arm.h
@@ -0,0 +1,194 @@
+/*
+ * Program Status Registers
+ */
+#define PsrMusr 0x00000010 /* mode */
+#define PsrMfiq 0x00000011
+#define PsrMirq 0x00000012
+#define PsrMsvc 0x00000013
+#define PsrMabt 0x00000017
+#define PsrMund 0x0000001B
+#define PsrMsys 0x0000001F
+#define PsrMask 0x0000001F
+
+#define PsrDfiq 0x00000040 /* disable FIQ interrupts */
+#define PsrDirq 0x00000080 /* disable IRQ interrupts */
+
+#define PsrV 0x10000000 /* overflow */
+#define PsrC 0x20000000 /* carry/borrow/extend */
+#define PsrZ 0x40000000 /* zero */
+#define PsrN 0x80000000 /* negative/less than */
+
+/*
+ * Coprocessors
+ */
+#define CpFP 10 /* float FP, VFP cfg. */
+#define CpDFP 11 /* double FP */
+#define CpSC 15 /* System Control */
+
+/*
+ * opcode 1
+ */
+#define CpDef 0 /* default */
+#define CpL2 1 /* L2 cache operations */
+
+/*
+ * Primary (CRn) CpSC registers.
+ */
+#define CpID 0 /* ID and cache type */
+#define CpCONTROL 1 /* miscellaneous control */
+#define CpTTB 2 /* Translation Table Base */
+#define CpDAC 3 /* Domain Access Control */
+#define CpFSR 5 /* Fault Status */
+#define CpFAR 6 /* Fault Address */
+#define CpCACHE 7 /* cache/write buffer control */
+#define CpTLB 8 /* TLB control */
+#define CpCLD 9 /* Cache Lockdown */
+#define CpTLD 10 /* TLB Lockdown */
+#define CpPID 13 /* Process ID */
+#define CpTESTCFG 15 /* test config. (arm926) */
+
+/*
+ * CpID Secondary (CRm) registers.
+ */
+#define CpIDidct 0
+/*
+ * CpID op1==0 opcode2 fields.
+ */
+#define CpIDid 0 /* main ID */
+#define CpIDct 1 /* cache type */
+
+/*
+ * CpCONTROL
+ */
+#define CpCmmu 0x00000001 /* M: MMU enable */
+#define CpCalign 0x00000002 /* A: alignment fault enable */
+#define CpCdcache 0x00000004 /* C: data cache on */
+#define CpCwb 0x00000008 /* W: write buffer turned on */
+#define CpCi32 0x00000010 /* P: 32-bit program space */
+#define CpCd32 0x00000020 /* D: 32-bit data space */
+#define CpCbe 0x00000080 /* B: big-endian operation */
+#define CpCsystem 0x00000100 /* S: system permission */
+#define CpCrom 0x00000200 /* R: ROM permission */
+#define CpCicache 0x00001000 /* I: instruction cache on */
+#define CpChv 0x00002000 /* V: high vectors */
+
+/*
+ * CpCACHE Secondary (CRm) registers and opcode2 fields.
+ * In ARM-speak, 'flush' means invalidate and 'clean' means writeback.
+ * In arm arch v6, these must be available in user mode:
+ * CpCACHEinvi, CpCACHEwait (prefetch flush)
+ * CpCACHEwb, CpCACHEwait (DSB: data sync barrier)
+ * CpCACHEwb, CpCACHEdmbarr (DMB: data memory barrier)
+ */
+#define CpCACHEintr 0 /* interrupt */
+#define CpCACHEinvi 5 /* instruction */
+#define CpCACHEinvd 6 /* data */
+#define CpCACHEinvu 7 /* unified (I+D) */
+#define CpCACHEwb 10 /* writeback D */
+#define CpCACHEwbu 11 /* writeback U (not 926ejs) */
+#define CpCACHEwbi 14 /* writeback D + invalidate */
+#define CpCACHEwbui 15 /* writeback U + inval (not 926ejs) */
+
+/*
+ * the 926ejs manual says that we can't use CpCACHEall nor CpCACHEwait
+ * for writeback operations on the 926ejs, except for CpCACHEwb + CpCACHEwait,
+ * which means `drain write buffer'.
+ */
+#define CpCACHEall 0 /* entire */
+#define CpCACHEse 1 /* single entry */
+#define CpCACHEsi 2 /* set/index */
+#define CpCACHEtest 3 /* test loop */
+#define CpCACHEwait 4 /* wait */
+#define CpCACHEdmbarr 5 /* wb: data memory barrier */
+
+/*
+ * CpTLB Secondary (CRm) registers and opcode2 fields.
+ */
+#define CpTLBinvi 5 /* instruction */
+#define CpTLBinvd 6 /* data */
+#define CpTLBinvu 7 /* unified */
+
+#define CpTLBinv 0 /* invalidate all */
+#define CpTLBinvse 1 /* invalidate single entry */
+
+/*
+ * CpTESTCFG Secondary (CRm) registers and opcode2 fields; sheeva only.
+ * opcode1 == CpL2 (1). L2 cache operations block the CPU until finished.
+ * Specifically, write-back (clean) blocks until all dirty lines have been
+ * drained from the L2 buffers.
+ */
+#define CpTCl2cfg 1
+#define CpTCl2flush 9 /* cpu blocks until flush done */
+#define CpTCl2waylck 10
+#define CpTCl2inv 11
+#define CpTCl2perfctl 12
+#define CpTCl2perfcnt 13
+
+/* CpTCl2cfg */
+#define CpTCl2conf 0
+
+/* CpTCl2conf bits */
+#define CpTCldcstream (1<<29) /* D cache streaming switch */
+#define CpTCl2wralloc (1<<28) /* cache write allocate */
+#define CpTCl2prefdis (1<<24) /* l2 cache prefetch disable */
+#define CpTCl2ena (1<<22) /* l2 cache enable */
+
+/* CpTCl2flush & CpTCl2inv */
+#define CpTCl2all 0
+#define CpTCl2seva 1
+#define CpTCl2way 2
+#define CpTCl2sepa 3
+#define CpTCl2valow 4
+#define CpTCl2vahigh 5 /* also triggers flush or inv */
+
+/* CpTCl2flush
+#define CpTCecccnt 6 /* ecc error count */
+#define CpTCeccthr 7 /* ecc error threshold */
+
+/* CpTCl2waylck */
+#define CpTCl2waylock 7
+
+/* CpTCl2inv */
+#define CpTCl2erraddr 7 /* ecc error address */
+
+/* CpTCl2perfctl */
+#define CpTCl2perf0ctl 0
+#define CpTCl2perf1ctl 1
+
+/* CpTCl2perfcnt */
+#define CpTCl2perf0low 0
+#define CpTCl2perf0high 1
+#define CpTCl2perf1low 2
+#define CpTCl2perf1high 3
+
+/*
+ * MMU page table entries.
+ * Mbo (0x10) bit is implementation-defined and mandatory on some pre-v7 arms.
+ */
+#define Mbo 0x10 /* must be 1 on earlier arms */
+#define Fault 0x00000000 /* L[12] pte: unmapped */
+
+#define Coarse (Mbo|1) /* L1 */
+#define Section (Mbo|2) /* L1 1MB */
+#define Fine (Mbo|3) /* L1 */
+
+#define Large 0x00000001u /* L2 64KB */
+#define Small 0x00000002u /* L2 4KB */
+#define Tiny 0x00000003u /* L2 1KB, deprecated */
+#define Buffered 0x00000004u /* L[12]: write-back not -thru */
+#define Cached 0x00000008u /* L[12] */
+
+#define Dom0 0
+#define Noaccess 0 /* AP, DAC */
+#define Krw 1 /* AP */
+#define Uro 2 /* AP */
+#define Urw 3 /* AP */
+#define Client 1 /* DAC */
+#define Manager 3 /* DAC */
+
+#define AP(n, v) F((v), ((n)*2)+4, 2)
+#define L1AP(ap) (AP(3, (ap))) /* in L1, only Sections have AP */
+#define L2AP(ap) (AP(3, (ap))|AP(2, (ap))|AP(1, (ap))|AP(0, (ap))) /* pre-armv7 */
+#define DAC(n, v) F((v), (n)*2, 2)
+
+#define HVECTORS 0xffff0000 /* addr of vectors */
diff --git a/sys/src/9/kw/arm.s b/sys/src/9/kw/arm.s
new file mode 100755
index 000000000..d48ce4a8e
--- /dev/null
+++ b/sys/src/9/kw/arm.s
@@ -0,0 +1,72 @@
+/*
+ * sheevaplug machine assist, definitions
+ * arm926ej-s processor at 1.2GHz
+ *
+ * loader uses R11 as scratch.
+ */
+#include "mem.h"
+#include "arm.h"
+
+#undef B /* B is for 'botch' */
+
+#define PADDR(a) ((a) & ~KZERO)
+#define KADDR(a) (KZERO|(a))
+
+#define L1X(va) (((((va))>>20) & 0x0fff)<<2)
+
+#define MACHADDR (L1-MACHSIZE)
+
+#define PTEDRAM (Dom0|L1AP(Krw)|Section|Cached|Buffered)
+#define PTEIO (Dom0|L1AP(Krw)|Section)
+
+/* wave at the user; clobbers R1 & R7; needs R12 (SB) set */
+#define WAVE(c) \
+ ISB; \
+ MOVW $PHYSCONS, R7; \
+ MOVW $(c), R1; \
+ MOVW R1, (R7); \
+ ISB
+
+/* new instructions */
+#define CLZ(s, d) WORD $(0xe16f0f10 | (d) << 12 | (s)) /* count leading 0s */
+
+#define DMB \
+ MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEwb), CpCACHEdmbarr
+/*
+ * data synchronisation barrier (formerly drain write buffer).
+ * waits for cache flushes, eviction buffer drain, tlb flushes,
+ * branch-prediction flushes, writes to memory; the lot.
+ * on sheeva, also flushes L2 eviction buffer.
+ * zeroes R0.
+ */
+#define DSB \
+ MOVW $0, R0; \
+ MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEwb), CpCACHEwait
+/*
+ * prefetch flush; zeroes R0.
+ * arm926ej-s manual says we need to sync with l2 cache in isb,
+ * and uncached load is the easiest way. doesn't seem to matter.
+ */
+#define ISB \
+ MOVW $0, R0; \
+ MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEwait
+// MOVW (R0), R0; MOVW $0, R0
+
+/* zeroes R0 */
+#define BARRIERS ISB; DSB
+
+/*
+ * invoked with PTE bits in R2, pa in R3, PTE pointed to by R4.
+ * fill PTE pointed to by R4 and increment R4 past it.
+ * increment R3 by a MB. clobbers R1.
+ */
+#define FILLPTE() \
+ ORR R3, R2, R1; /* pte bits in R2, pa in R3 */ \
+ MOVW R1, (R4); \
+ ADD $4, R4; /* bump PTE address */ \
+ ADD $MiB, R3; /* bump pa */ \
+
+/* zero PTE pointed to by R4 and increment R4 past it. assumes R0 is 0. */
+#define ZEROPTE() \
+ MOVW R0, (R4); \
+ ADD $4, R4; /* bump PTE address */
diff --git a/sys/src/9/kw/cga.c b/sys/src/9/kw/cga.c
new file mode 100755
index 000000000..f9cba1904
--- /dev/null
+++ b/sys/src/9/kw/cga.c
@@ -0,0 +1,132 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+enum {
+ Black,
+ Blue,
+ Green,
+ Cyan,
+ Red,
+ Magenta,
+ Brown,
+ Grey,
+
+ Bright = 0x08,
+ Blinking = 0x80,
+
+ Yellow = Bright|Brown,
+ White = Bright|Grey,
+};
+
+enum {
+ Width = 80*2,
+ Height = 25,
+
+ Attr = (Black<<4)|Grey, /* high nibble background
+ * low foreground
+ */
+};
+
+#define CGASCREENBASE ((uchar*)KADDR(0xB8000))
+
+#define inb(x) 0 /* TODO */
+#define outb(x, y) /* TODO */
+
+static int cgapos;
+static Lock cgascreenlock;
+
+static uchar
+cgaregr(int index)
+{
+ USED(index);
+ outb(0x3D4, index);
+ return inb(0x3D4+1) & 0xFF;
+}
+
+static void
+cgaregw(int index, int data)
+{
+ USED(index, data);
+ outb(0x3D4, index);
+ outb(0x3D4+1, data);
+}
+
+static void
+movecursor(void)
+{
+ cgaregw(0x0E, (cgapos/2>>8) & 0xFF);
+ cgaregw(0x0F, cgapos/2 & 0xFF);
+ CGASCREENBASE[cgapos+1] = Attr;
+}
+
+static void
+cgascreenputc(int c)
+{
+ int i;
+ uchar *p;
+
+ if(c == '\n'){
+ cgapos = cgapos/Width;
+ cgapos = (cgapos+1)*Width;
+ }
+ else if(c == '\t'){
+ i = 8 - ((cgapos/2)&7);
+ while(i-->0)
+ cgascreenputc(' ');
+ }
+ else if(c == '\b'){
+ if(cgapos >= 2)
+ cgapos -= 2;
+ cgascreenputc(' ');
+ cgapos -= 2;
+ }
+ else{
+ CGASCREENBASE[cgapos++] = c;
+ CGASCREENBASE[cgapos++] = Attr;
+ }
+ if(cgapos >= Width*Height){
+ memmove(CGASCREENBASE, &CGASCREENBASE[Width], Width*(Height-1));
+ p = &CGASCREENBASE[Width*(Height-1)];
+ for(i=0; i<Width/2; i++){
+ *p++ = ' ';
+ *p++ = Attr;
+ }
+ cgapos = Width*(Height-1);
+ }
+ movecursor();
+}
+
+static void
+cgascreenputs(char* s, int n)
+{
+ if(!islo()){
+ /*
+ * Don't deadlock trying to
+ * print in an interrupt.
+ */
+ if(!canlock(&cgascreenlock))
+ return;
+ }
+ else
+ lock(&cgascreenlock);
+
+ while(n-- > 0)
+ cgascreenputc(*s++);
+
+ unlock(&cgascreenlock);
+}
+
+void
+screeninit(void)
+{
+
+ cgapos = cgaregr(0x0E)<<8;
+ cgapos |= cgaregr(0x0F);
+ cgapos *= 2;
+
+ screenputs = cgascreenputs;
+}
diff --git a/sys/src/9/kw/clock.c b/sys/src/9/kw/clock.c
new file mode 100755
index 000000000..2f8bc36a1
--- /dev/null
+++ b/sys/src/9/kw/clock.c
@@ -0,0 +1,205 @@
+/*
+ * kirkwood clocks
+ *
+ * timers count down to zero.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "ureg.h"
+
+enum {
+ Tcycles = CLOCKFREQ / HZ, /* cycles per clock tick */
+ Dogperiod = 15 * CLOCKFREQ, /* at most 21 s.; must fit in ulong */
+ MaxPeriod = Tcycles,
+ MinPeriod = MaxPeriod / 100,
+
+ /* timer ctl bits */
+ Tmr0enable = 1<<0,
+ Tmr0reload = 1<<1, /* at 0 count, load timer0 from reload0 */
+ Tmr1enable = 1<<2,
+ Tmr1reload = 1<<3, /* at 0 count, load timer1 from reload1 */
+ TmrWDenable = 1<<4,
+ TmrWDreload = 1<<5,
+};
+
+typedef struct TimerReg TimerReg;
+struct TimerReg
+{
+ ulong ctl;
+ ulong pad[3];
+ ulong reload0;
+ ulong timer0; /* cycles until zero */
+ ulong reload1;
+ ulong timer1; /* cycles until zero */
+ ulong reloadwd;
+ ulong timerwd;
+};
+
+static int ticks; /* for sanity checking; m->ticks doesn't always get updated */
+
+static void
+clockintr(Ureg *ureg, void *arg)
+{
+ TimerReg *tmr = arg;
+ static int nesting;
+
+ tmr->timerwd = Dogperiod; /* reassure the watchdog */
+ ticks++;
+ coherence();
+
+ if (nesting == 0) { /* if the clock interrupted itself, bail out */
+ ++nesting;
+ timerintr(ureg, 0);
+ --nesting;
+ }
+
+ intrclear(Irqbridge, IRQcputimer0);
+}
+
+/* stop clock interrupts and disable the watchdog timer */
+void
+clockshutdown(void)
+{
+ TimerReg *tmr = (TimerReg *)soc.clock;
+
+ tmr->ctl = 0;
+ coherence();
+}
+
+void
+clockinit(void)
+{
+ int i, s;
+ CpucsReg *cpu = (CpucsReg *)soc.cpu;
+ TimerReg *tmr = (TimerReg *)soc.clock;
+
+ clockshutdown();
+
+ /*
+ * verify sanity of timer0
+ */
+
+ intrenable(Irqbridge, IRQcputimer0, clockintr, tmr, "clock0");
+ s = spllo(); /* risky */
+ /* take any deferred clock (& other) interrupts here */
+ splx(s);
+
+ /* adjust m->bootdelay, used by delay()? */
+ m->ticks = ticks = 0;
+ m->fastclock = 0;
+
+ tmr->timer0 = 1;
+ tmr->ctl = Tmr0enable; /* just once */
+ coherence();
+
+ s = spllo(); /* risky */
+ for (i = 0; i < 10 && ticks == 0; i++) {
+ delay(1);
+ coherence();
+ }
+ splx(s);
+ if (ticks == 0) {
+ serialputc('?');
+ if (tmr->timer0 == 0)
+ panic("clock not interrupting");
+ else if (tmr->timer0 == tmr->reload0)
+ panic("clock not ticking");
+ else
+ panic("clock running very slowly");
+ }
+
+ /*
+ * configure all timers
+ */
+ clockshutdown();
+ tmr->reload0 = tmr->timer0 = Tcycles; /* tick clock */
+ tmr->reload1 = tmr->timer1 = ~0; /* cycle clock */
+ tmr->timerwd = Dogperiod; /* watch dog timer */
+ coherence();
+ tmr->ctl = Tmr0enable | Tmr0reload | Tmr1enable | Tmr1reload |
+ TmrWDenable;
+ cpu->rstout |= RstoutWatchdog;
+ coherence();
+}
+
+void
+timerset(Tval next)
+{
+ int offset;
+ TimerReg *tmr = (TimerReg *)soc.clock;
+
+ offset = next - fastticks(nil);
+ if(offset < MinPeriod)
+ offset = MinPeriod;
+ else if(offset > MaxPeriod)
+ offset = MaxPeriod;
+ tmr->timer0 = offset;
+ coherence();
+}
+
+uvlong
+fastticks(uvlong *hz)
+{
+ uvlong now;
+ int s;
+
+ if(hz)
+ *hz = CLOCKFREQ;
+ s = splhi();
+ /* zero low ulong of fastclock */
+ now = (m->fastclock & ~(uvlong)~0ul) | perfticks();
+ if(now < m->fastclock) /* low bits must have wrapped */
+ now += 1ll << 32;
+ m->fastclock = now;
+ splx(s);
+ return now;
+}
+
+ulong
+perfticks(void)
+{
+ TimerReg *tmr = (TimerReg *)soc.clock;
+
+ return ~tmr->timer1;
+}
+
+long
+lcycles(void)
+{
+ return perfticks();
+}
+
+ulong
+µs(void)
+{
+ return fastticks2us(fastticks(nil));
+}
+
+void
+microdelay(int l)
+{
+ int i;
+
+ l *= m->delayloop;
+ l /= 1000;
+ if(l <= 0)
+ l = 1;
+ for(i = 0; i < l; i++)
+ ;
+}
+
+void
+delay(int l)
+{
+ ulong i, j;
+
+ j = m->delayloop;
+ while(l-- > 0)
+ for(i=0; i < j; i++)
+ ;
+}
diff --git a/sys/src/9/kw/coproc.c b/sys/src/9/kw/coproc.c
new file mode 100755
index 000000000..6002649f1
--- /dev/null
+++ b/sys/src/9/kw/coproc.c
@@ -0,0 +1,153 @@
+/*
+ * arm co-processors
+ * CP15 (system control) is the one that gets used the most in practice.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "arm.h"
+
+#define MAP2PCSPACE(va, pc) ((uintptr)(va) & ~KSEGM | (pc) & KSEGM)
+
+enum {
+ /* alternates: 0xe12fff1e BX (R14); last e is R14 */
+ /* 0xe28ef000 B 0(R14); second e is R14 (ken) */
+ Retinst = 0xe1a0f00e, /* MOVW R14, R15 */
+
+ Fpproc = 10, /* for vfp 3+; also 11 for doubles */
+};
+
+void
+cpwr(int cp, int op1, int crn, int crm, int op2, ulong val)
+{
+ int s;
+ volatile ulong instr[2];
+ void *pcaddr;
+ void (*fp)(ulong);
+
+ s = splhi();
+ op1 &= 7;
+ op2 &= 7;
+ crn &= 017;
+ crm &= 017;
+ cp &= 017;
+ /* MCR. Rt will be R0. */
+ instr[0] = 0xee000010 |
+ op1 << 21 | crn << 16 | cp << 8 | op2 << 5 | crm;
+ instr[1] = Retinst;
+ coherence();
+
+ pcaddr = (void *)MAP2PCSPACE(instr, getcallerpc(&cp));
+ cachedwbse(pcaddr, sizeof instr);
+ cacheiinv();
+
+ fp = (void (*)(ulong))pcaddr;
+ (*fp)(val);
+ coherence();
+ splx(s);
+}
+
+void
+cpwrsc(int op1, int crn, int crm, int op2, ulong val)
+{
+ cpwr(CpSC, op1, crn, crm, op2, val);
+}
+
+ulong
+cprd(int cp, int op1, int crn, int crm, int op2)
+{
+ int s;
+ ulong res;
+ volatile ulong instr[2];
+ void *pcaddr;
+ ulong (*fp)(void);
+
+ s = splhi();
+ op1 &= 7;
+ op2 &= 7;
+ crn &= 017;
+ crm &= 017;
+ /*
+ * MRC. return value will be in R0, which is convenient.
+ * Rt will be R0.
+ */
+ instr[0] = 0xee100010 |
+ op1 << 21 | crn << 16 | cp << 8 | op2 << 5 | crm;
+ instr[1] = Retinst;
+ coherence();
+
+ pcaddr = (void *)MAP2PCSPACE(instr, getcallerpc(&cp));
+ cachedwbse(pcaddr, sizeof instr);
+ cacheiinv();
+
+ fp = (ulong (*)(void))pcaddr;
+ res = (*fp)();
+ splx(s);
+ return res;
+}
+
+ulong
+cprdsc(int op1, int crn, int crm, int op2)
+{
+ return cprd(CpSC, op1, crn, crm, op2);
+}
+
+/* floating point */
+
+ulong
+fprd(int fpreg)
+{
+ int s;
+ ulong res;
+ volatile ulong instr[2];
+ void *pcaddr;
+ ulong (*fp)(void);
+
+ s = splhi();
+ fpreg &= 017;
+ /*
+ * VMRS. return value will be in R0, which is convenient.
+ * Rt will be R0.
+ */
+ instr[0] = 0xeef00010 | fpreg << 16 | 0 << 12 | Fpproc << 8;
+ instr[1] = Retinst;
+ coherence();
+
+ pcaddr = (void *)MAP2PCSPACE(instr, getcallerpc(&fpreg));
+ cachedwbse(pcaddr, sizeof instr);
+ cacheiinv();
+
+ fp = (ulong (*)(void))pcaddr;
+ res = (*fp)();
+ splx(s);
+ return res;
+}
+
+void
+fpwr(int fpreg, ulong val)
+{
+ int s;
+ volatile ulong instr[2];
+ void *pcaddr;
+ void (*fp)(ulong);
+
+ s = splhi();
+ fpreg &= 017;
+ /* VMSR. Rt will be R0. */
+ instr[0] = 0xeee00010 | fpreg << 16 | 0 << 12 | Fpproc << 8;
+ instr[1] = Retinst;
+ coherence();
+
+ pcaddr = (void *)MAP2PCSPACE(instr, getcallerpc(&fpreg));
+ cachedwbse(pcaddr, sizeof instr);
+ cacheiinv();
+
+ fp = (void (*)(ulong))pcaddr;
+ (*fp)(val);
+ coherence();
+ splx(s);
+}
diff --git a/sys/src/9/kw/dat.h b/sys/src/9/kw/dat.h
new file mode 100755
index 000000000..88b901cf6
--- /dev/null
+++ b/sys/src/9/kw/dat.h
@@ -0,0 +1,320 @@
+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 Memcache Memcache;
+typedef struct MMMU MMMU;
+typedef struct Mach Mach;
+typedef struct Notsave Notsave;
+typedef struct Page Page;
+typedef struct Pcidev Pcidev;
+typedef struct PhysUart PhysUart;
+typedef struct PMMU PMMU;
+typedef struct Proc Proc;
+typedef u32int PTE;
+typedef struct Soc Soc;
+typedef struct Uart Uart;
+typedef struct Ureg Ureg;
+typedef uvlong Tval;
+
+#pragma incomplete Pcidev
+#pragma incomplete Ureg
+
+#define MAXSYSARG 5 /* for mount(fd, mpt, flag, arg, srv) */
+
+/*
+ * parameters for sysproc.c
+ */
+#define AOUT_MAGIC (E_MAGIC)
+
+struct Lock
+{
+ ulong key;
+ u32int sr;
+ uintptr pc;
+ Proc* p;
+ Mach* m;
+ int isilock;
+};
+
+struct Label
+{
+ uintptr sp;
+ uintptr pc;
+};
+
+/*
+ * emulated floating point
+ */
+struct FPsave
+{
+ ulong status;
+ ulong control;
+ ulong regs[8][3];
+
+ int fpstate;
+};
+
+/*
+ * FPsave.status
+ */
+enum
+{
+ FPinit,
+ FPactive,
+ FPinactive,
+};
+
+struct Confmem
+{
+ uintptr base;
+ usize npage;
+ uintptr limit;
+ uintptr kbase;
+ uintptr klimit;
+};
+
+struct Conf
+{
+ ulong nmach; /* processors */
+ ulong nproc; /* processes */
+ ulong monitor; /* has monitor? */
+ Confmem mem[1]; /* physical memory */
+ ulong npage; /* total physical pages of memory */
+ usize upages; /* user page pool */
+ ulong copymode; /* 0 is copy on write, 1 is copy on reference */
+ ulong ialloc; /* max interrupt time allocation in bytes */
+ ulong pipeqsize; /* size in bytes of pipe queues */
+ ulong nimage; /* number of page cache image headers */
+ ulong nswap; /* number of swap pages */
+ int nswppo; /* max # of pageouts per segment pass */
+// ulong hz; /* processor cycle freq */
+// ulong mhz;
+};
+
+/*
+ * things saved in the Proc structure during a notify
+ */
+struct Notsave {
+ int emptiness;
+};
+
+/*
+ * MMU stuff in Mach.
+ */
+struct MMMU
+{
+ PTE* mmul1; /* l1 for this processor */
+ int mmul1lo;
+ int mmul1hi;
+ int mmupid;
+};
+
+/*
+ * MMU stuff in proc
+ */
+#define NCOLOR 1 /* 1 level cache, don't worry about VCE's */
+struct PMMU
+{
+ Page* mmul2;
+ Page* mmul2cache; /* free mmu pages */
+};
+
+#include "../port/portdat.h"
+
+struct Mach
+{
+ int machno; /* physical id of processor */
+ uintptr splpc; /* pc of last caller to splhi */
+
+ Proc* proc; /* current process */
+
+ MMMU;
+ int flushmmu; /* flush current proc mmu state */
+
+ 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;
+
+ Proc* readied; /* for runproc */
+ ulong schedticks; /* next forced context switch */
+
+ int cputype;
+ int socrev; /* system-on-chip revision */
+ ulong delayloop;
+
+ /* stats */
+ int tlbfault;
+ int tlbpurge;
+ int pfault;
+ int cs;
+ int syscall;
+ int load;
+ int intr;
+ vlong fastclock; /* last sampled value */
+ uvlong inidle; /* time spent in idlehands() */
+ ulong spuriousintr;
+ int lastintr;
+ int ilockdepth;
+ Perf perf; /* performance counters */
+
+// int cpumhz;
+ uvlong cpuhz; /* speed of cpu */
+ uvlong cyclefreq; /* Frequency of user readable cycle counter */
+
+ /* save areas for exceptions */
+ u32int sfiq[5];
+ u32int sirq[5];
+ u32int sund[5];
+ u32int sabt[5];
+#define fiqstack sfiq
+#define irqstack sirq
+#define abtstack sabt
+#define undstack sund
+
+ int stack[1];
+};
+
+/*
+ * Fake kmap.
+ */
+typedef void KMap;
+#define VA(k) ((uintptr)(k))
+#define kmap(p) (KMap*)((p)->pa|kseg0)
+#define kunmap(k)
+
+struct
+{
+ Lock;
+ int machs; /* bitmap of active CPUs */
+ int exiting; /* shutdown */
+ int ispanic; /* shutdown in response to a panic */
+}active;
+
+enum {
+ Frequency = 1200*1000*1000, /* the processor clock */
+};
+
+extern register Mach* m; /* R10 */
+extern register Proc* up; /* R9 */
+
+extern uintptr kseg0;
+extern Mach* machaddr[MAXMACH];
+
+enum {
+ Nvec = 8, /* # of vectors at start of lexception.s */
+};
+
+/*
+ * Layout of physical 0.
+ */
+typedef struct Vectorpage {
+ void (*vectors[Nvec])(void);
+ uint vtable[Nvec];
+} Vectorpage;
+
+/*
+ * 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) (machaddr[n])
+
+/*
+ * Horrid. But the alternative is 'defined'.
+ */
+#ifdef _DBGC_
+#define DBGFLG (dbgflg[_DBGC_])
+#else
+#define DBGFLG (0)
+#endif /* _DBGC_ */
+
+int vflag;
+extern char dbgflg[256];
+
+#define dbgprint print /* for now */
+
+/*
+ * hardware info about a device
+ */
+typedef struct {
+ ulong port;
+ int size;
+} Devport;
+
+struct DevConf
+{
+ ulong intnum; /* interrupt number */
+ char *type; /* card type, malloced */
+ int nports; /* Number of ports */
+ Devport *ports; /* The ports themselves */
+};
+
+enum {
+ Dcache,
+ Icache,
+ Unified,
+};
+
+/* characteristics of a given cache level */
+struct Memcache {
+ uint level; /* 1 is nearest processor, 2 further away */
+ uint kind; /* I, D or unified */
+
+ uint size;
+ uint nways; /* associativity */
+ uint nsets;
+ uint linelen; /* bytes per cache line */
+ uint setsways;
+
+ uint log2linelen;
+ uint waysh; /* shifts for set/way register */
+ uint setsh;
+};
+
+struct Soc { /* addr's of SoC controllers */
+ uintptr cpu;
+ uintptr devid;
+ uintptr l2cache;
+ uintptr sdramc;
+// uintptr sdramd; /* unused */
+
+ uintptr iocfg;
+ uintptr addrmap;
+ uintptr intr;
+ uintptr nand;
+ uintptr cesa; /* crypto accel. */
+ uintptr ehci;
+ uintptr spi;
+ uintptr twsi;
+
+ uintptr analog;
+ uintptr pci;
+ uintptr pcibase;
+
+ uintptr rtc; /* real-time clock */
+ uintptr clock;
+
+ uintptr ether[2];
+ uintptr sata[3];
+ uintptr uart[2];
+ uintptr gpio[2];
+} soc;
+extern Soc soc;
diff --git a/sys/src/9/kw/devarch.c b/sys/src/9/kw/devarch.c
new file mode 100755
index 000000000..08c76e02c
--- /dev/null
+++ b/sys/src/9/kw/devarch.c
@@ -0,0 +1,265 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+#include "../ip/ip.h"
+
+enum {
+ Qdir = 0,
+ Qbase,
+
+ Qmax = 16,
+};
+
+typedef long Rdwrfn(Chan*, void*, long, vlong);
+
+static Rdwrfn *readfn[Qmax];
+static Rdwrfn *writefn[Qmax];
+
+static Dirtab archdir[Qmax] = {
+ ".", { Qdir, 0, QTDIR }, 0, 0555,
+};
+
+Lock archwlock; /* the lock is only for changing archdir */
+int narchdir = Qbase;
+
+/*
+ * Add a file to the #P listing. Once added, you can't delete it.
+ * You can't add a file with the same name as one already there,
+ * and you get a pointer to the Dirtab entry so you can do things
+ * like change the Qid version. Changing the Qid path is disallowed.
+ */
+Dirtab*
+addarchfile(char *name, int perm, Rdwrfn *rdfn, Rdwrfn *wrfn)
+{
+ int i;
+ Dirtab d;
+ Dirtab *dp;
+
+ memset(&d, 0, sizeof d);
+ strcpy(d.name, name);
+ d.perm = perm;
+
+ lock(&archwlock);
+ if(narchdir >= Qmax){
+ unlock(&archwlock);
+ return nil;
+ }
+
+ for(i=0; i<narchdir; i++)
+ if(strcmp(archdir[i].name, name) == 0){
+ unlock(&archwlock);
+ return nil;
+ }
+
+ d.qid.path = narchdir;
+ archdir[narchdir] = d;
+ readfn[narchdir] = rdfn;
+ writefn[narchdir] = wrfn;
+ dp = &archdir[narchdir++];
+ unlock(&archwlock);
+
+ return dp;
+}
+
+static Chan*
+archattach(char* spec)
+{
+ return devattach('P', spec);
+}
+
+Walkqid*
+archwalk(Chan* c, Chan *nc, char** name, int nname)
+{
+ return devwalk(c, nc, name, nname, archdir, narchdir, devgen);
+}
+
+static int
+archstat(Chan* c, uchar* dp, int n)
+{
+ return devstat(c, dp, n, archdir, narchdir, devgen);
+}
+
+static Chan*
+archopen(Chan* c, int omode)
+{
+ return devopen(c, omode, archdir, narchdir, devgen);
+}
+
+static void
+archclose(Chan*)
+{
+}
+
+static long
+archread(Chan *c, void *a, long n, vlong offset)
+{
+ Rdwrfn *fn;
+
+ switch((ulong)c->qid.path){
+ case Qdir:
+ return devdirread(c, a, n, archdir, narchdir, devgen);
+
+ default:
+ if(c->qid.path < narchdir && (fn = readfn[c->qid.path]))
+ return fn(c, a, n, offset);
+ error(Eperm);
+ break;
+ }
+
+ return 0;
+}
+
+static long
+archwrite(Chan *c, void *a, long n, vlong offset)
+{
+ Rdwrfn *fn;
+
+ if(c->qid.path < narchdir && (fn = writefn[c->qid.path]))
+ return fn(c, a, n, offset);
+ error(Eperm);
+
+ return 0;
+}
+
+void archinit(void);
+
+Dev archdevtab = {
+ 'P',
+ "arch",
+
+ devreset,
+ archinit,
+ devshutdown,
+ archattach,
+ archwalk,
+ archstat,
+ archopen,
+ devcreate,
+ archclose,
+ archread,
+ devbread,
+ archwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
+
+/* convert AddrDevid register to a string in buf and return buf */
+char *
+cputype2name(char *buf, int size)
+{
+ ulong id, archid, rev;
+ char *manu, *arch, *socrev;
+ char unk[32], socnm[32], revname[32];
+ Pciex *pci;
+
+ m->cputype = *(ulong *)soc.devid;
+#ifdef OLD
+ switch(m->cputype & 3) {
+ case 0:
+ socnm = "88F6[12]80";
+ break;
+ case 1:
+ socnm = "88F619[02]";
+ break;
+ case 2:
+ socnm = "88F6281";
+ break;
+ default:
+ socnm = "unknown";
+ break;
+ }
+#endif
+ /* strange way to get this information, but it's what u-boot does */
+ pci = (Pciex *)soc.pci;
+ snprint(socnm, sizeof socnm, "88F%ux", pci->devid);
+ /* stash rev for benefit of later usb initialisation */
+ m->socrev = rev = pci->revid & MASK(4);
+
+ id = cpidget();
+ if ((id >> 24) == 0x56 && pci->venid == 0x11ab)
+ manu = "Marvell";
+ else
+ manu = "unknown";
+ archid = (id >> 16) & MASK(4);
+ switch (archid) {
+ case 5:
+ arch = "v5te";
+ break;
+ default:
+ snprint(unk, sizeof unk, "unknown (%ld)", archid);
+ arch = unk;
+ break;
+ }
+ if (pci->devid != 0x6281)
+ socrev = "unknown";
+ else
+ switch (rev) {
+ case Socrevz0:
+ socrev = "Z0";
+ break;
+ case Socreva0:
+ socrev = "A0";
+ break;
+ case Socreva1:
+ socrev = "A1";
+ break;
+ default:
+ snprint(revname, sizeof revname, "unknown rev (%ld)",
+ rev);
+ socrev = revname;
+ break;
+ }
+ seprint(buf, buf + size,
+ "%s %s %s; arm926ej-s arch %s rev %ld.%ld part %lux",
+ manu, socnm, socrev, arch, (id >> 20) & MASK(4),
+ id & MASK(4), (id >> 4) & MASK(12));
+ return buf;
+}
+
+static long
+cputyperead(Chan*, void *a, long n, vlong offset)
+{
+ char name[64], str[128];
+
+ cputype2name(name, sizeof name);
+ snprint(str, sizeof str, "ARM %s %llud\n", name, m->cpuhz / 1000000);
+ return readstr(offset, a, n, str);
+}
+
+static long
+tbread(Chan*, void *a, long n, vlong offset)
+{
+ char str[16];
+ uvlong tb;
+
+ cycles(&tb);
+
+ snprint(str, sizeof(str), "%16.16llux", tb);
+ return readstr(offset, a, n, str);
+}
+
+static long
+nsread(Chan*, void *a, long n, vlong offset)
+{
+ char str[16];
+ uvlong tb;
+
+ cycles(&tb);
+
+ snprint(str, sizeof(str), "%16.16llux", (tb/700)* 1000);
+ return readstr(offset, a, n, str);
+}
+
+void
+archinit(void)
+{
+ addarchfile("cputype", 0444, cputyperead, nil);
+ addarchfile("timebase",0444, tbread, nil);
+// addarchfile("nsec", 0444, nsread, nil);
+}
diff --git a/sys/src/9/kw/devether.c b/sys/src/9/kw/devether.c
new file mode 100755
index 000000000..4d0a51e4c
--- /dev/null
+++ b/sys/src/9/kw/devether.c
@@ -0,0 +1,528 @@
+#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"
+
+extern int archether(unsigned ctlno, Ether *ether);
+
+static Ether *etherxx[MaxEther];
+
+Chan*
+etherattach(char* spec)
+{
+ int ctlrno;
+ char *p;
+ Chan *chan;
+
+ ctlrno = 0;
+ if(spec && *spec){
+ ctlrno = strtoul(spec, &p, 0);
+ if((ctlrno == 0 && p == spec) || *p != 0)
+ error(Ebadarg);
+ if(ctlrno < 0 || ctlrno >= MaxEther)
+ error(Ebadarg);
+ }
+ if(etherxx[ctlrno] == 0)
+ error(Enodev);
+
+ chan = devattach('l', spec);
+ if(waserror()){
+ chanfree(chan);
+ nexterror();
+ }
+ chan->dev = ctlrno;
+ if(etherxx[ctlrno]->attach)
+ etherxx[ctlrno]->attach(etherxx[ctlrno]);
+ poperror();
+ 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)) != 0 && 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;
+ if(qpass(f->in, xbp) < 0)
+ ether->soverflows++;
+ }
+ 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);
+ if(ether->transmit != nil)
+ ether->transmit(ether);
+ } else
+ freeb(bp);
+
+ return len;
+}
+
+static long
+etherwrite(Chan* chan, void* buf, long n, vlong)
+{
+ Ether *ether;
+ Block *bp;
+ int nn, onoff;
+ Cmdbuf *cb;
+
+ ether = etherxx[chan->dev];
+ if(NETTYPE(chan->qid.path) != Ndataqid) {
+ nn = netifwrite(ether, chan, buf, n);
+ if(nn >= 0)
+ return nn;
+ cb = parsecmd(buf, n);
+ if(strcmp(cb->f[0], "nonblocking") == 0){
+ if(cb->nf <= 1)
+ onoff = 1;
+ else
+ onoff = atoi(cb->f[1]);
+ qnoblock(ether->oq, onoff);
+ free(cb);
+ return n;
+ }
+ free(cb);
+ if(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 < Eaddrlen; 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[KNAMELEN], 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->mbps = 10;
+ ether->minmtu = ETHERMINTU;
+ ether->maxmtu = ETHERMAXTU;
+
+ if(archether(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(cistrncmp(ether->opt[i], "ea=", 3) == 0){
+ if(parseether(ether->ea, &ether->opt[i][3]) == -1)
+ memset(ether->ea, 0, Eaddrlen);
+ }else if(cistrcmp(ether->opt[i], "fullduplex") == 0 ||
+ cistrcmp(ether->opt[i], "10BASE-TFD") == 0)
+ ether->fullduplex = 1;
+ else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0)
+ ether->mbps = 100;
+ }
+ if(cards[n].reset(ether))
+ break;
+ snprint(name, sizeof(name), "ether%d", ctlrno);
+
+ if(ether->interrupt != nil)
+ intrenable(Irqlo, ether->irq, ether->interrupt,
+ ether, name);
+
+ i = snprint(buf, sizeof buf,
+ "#l%d: %s: %dMbps port %#lux irq %d",
+ ctlrno, ether->type, ether->mbps, ether->port,
+ ether->irq);
+ if(ether->mem)
+ i += snprint(buf+i, sizeof buf - i,
+ " addr %#lux", PADDR(ether->mem));
+ if(ether->size)
+ i += snprint(buf+i, sizeof buf - i,
+ " size %#luX", ether->size);
+ i += snprint(buf+i, sizeof 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]);
+ snprint(buf+i, sizeof buf - i, "\n");
+ print("%s", buf);
+
+ if(ether->mbps >= 1000)
+ netifinit(ether, name, Ntypes, 4*1024*1024);
+ else if(ether->mbps >= 100)
+ netifinit(ether, name, Ntypes, 1024*1024);
+ else
+ netifinit(ether, name, Ntypes, 65*1024);
+ if(ether->oq == 0)
+ ether->oq = qopen(ether->limit, 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);
+}
+
+static void
+ethershutdown(void)
+{
+ Ether *ether;
+ int i;
+
+ for(i = 0; i < MaxEther; i++){
+ ether = etherxx[i];
+ if(ether == nil)
+ continue;
+ if(ether->shutdown == nil) {
+ print("#l%d: no shutdown function\n", i);
+ continue;
+ }
+ (*ether->shutdown)(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;
+}
+
+void
+dumpoq(Queue *oq)
+{
+ if (oq == nil)
+ print("no outq! ");
+ else if (qisclosed(oq))
+ print("outq closed ");
+ else if (qfull(oq))
+ print("outq full ");
+ else
+ print("outq %d ", qlen(oq));
+}
+
+void
+dumpnetif(Netif *netif)
+{
+ print("netif %s ", netif->name);
+ print("limit %d mbps %d link %d ",
+ netif->limit, netif->mbps, netif->link);
+ print("inpkts %lld outpkts %lld errs %d\n",
+ netif->inpackets, netif->outpackets,
+ netif->crcs + netif->oerrs + netif->frames + netif->overflows +
+ netif->buffs + netif->soverflows);
+}
+
+Dev etherdevtab = {
+ 'l',
+ "ether",
+
+ etherreset,
+ devinit,
+ ethershutdown,
+ etherattach,
+ etherwalk,
+ etherstat,
+ etheropen,
+ ethercreate,
+ etherclose,
+ etherread,
+ etherbread,
+ etherwrite,
+ etherbwrite,
+ devremove,
+ etherwstat,
+};
diff --git a/sys/src/9/kw/devrtc.c b/sys/src/9/kw/devrtc.c
new file mode 100755
index 000000000..a47fc3033
--- /dev/null
+++ b/sys/src/9/kw/devrtc.c
@@ -0,0 +1,359 @@
+/*
+ * devrtc - real-time clock, for kirkwood
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+typedef struct RtcReg RtcReg;
+typedef struct Rtc Rtc;
+
+struct RtcReg
+{
+ ulong time;
+ ulong date;
+ ulong alarmtm;
+ ulong alarmdt;
+ ulong intrmask;
+ ulong intrcause;
+};
+
+struct Rtc
+{
+ int sec;
+ int min;
+ int hour;
+ int wday;
+ int mday;
+ int mon;
+ int year;
+};
+
+enum {
+ Qdir,
+ Qrtc,
+};
+
+static Dirtab rtcdir[] = {
+ ".", {Qdir, 0, QTDIR}, 0, 0555,
+ "rtc", {Qrtc}, NUMSIZE, 0664,
+};
+static RtcReg *rtcreg; /* filled in by attach */
+static Lock rtclock;
+
+#define SEC2MIN 60
+#define SEC2HOUR (60*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 yr)
+{
+ if((yr % 4) == 0)
+ return ldmsize;
+ else
+ return dmsize;
+}
+
+/*
+ * compute seconds since Jan 1 1970
+ */
+static ulong
+rtc2sec(Rtc *rtc)
+{
+ ulong secs;
+ int i;
+ int *d2m;
+
+ /*
+ * seconds per year
+ */
+ secs = 0;
+ 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;
+ }
+
+ /*
+ * 19700101 was thursday
+ */
+ rtc->wday = (day + 7340036L) % 7;
+
+ /*
+ * 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;
+}
+
+enum {
+ Rtcsec = 0x00007f,
+ Rtcmin = 0x007f00,
+ Rtcms = 8,
+ Rtchr12 = 0x1f0000,
+ Rtchr24 = 0x3f0000,
+ Rtchrs = 16,
+
+ Rdmday = 0x00003f,
+ Rdmon = 0x001f00,
+ Rdms = 8,
+ Rdyear = 0x7f0000,
+ Rdys = 16,
+
+ Rtcpm = 1<<21, /* pm bit */
+ Rtc12 = 1<<22, /* 12 hr clock */
+};
+
+static ulong
+bcd2dec(ulong bcd)
+{
+ ulong d, m, i;
+
+ d = 0;
+ m = 1;
+ for(i = 0; i < 2 * sizeof d; i++){
+ d += ((bcd >> (4*i)) & 0xf) * m;
+ m *= 10;
+ }
+ return d;
+}
+
+static ulong
+dec2bcd(ulong d)
+{
+ ulong bcd, i;
+
+ bcd = 0;
+ for(i = 0; d != 0; i++){
+ bcd |= (d%10) << (4*i);
+ d /= 10;
+ }
+ return bcd;
+}
+
+static long
+_rtctime(void)
+{
+ ulong t, d;
+ Rtc rtc;
+
+ t = rtcreg->time;
+ d = rtcreg->date;
+
+ rtc.sec = bcd2dec(t & Rtcsec);
+ rtc.min = bcd2dec((t & Rtcmin) >> Rtcms);
+
+ if(t & Rtc12){
+ rtc.hour = bcd2dec((t & Rtchr12) >> Rtchrs) - 1; /* 1—12 */
+ if(t & Rtcpm)
+ rtc.hour += 12;
+ }else
+ rtc.hour = bcd2dec((t & Rtchr24) >> Rtchrs); /* 0—23 */
+
+ rtc.mday = bcd2dec(d & Rdmday); /* 1—31 */
+ rtc.mon = bcd2dec((d & Rdmon) >> Rdms); /* 1—12 */
+ rtc.year = bcd2dec((d & Rdyear) >> Rdys) + 2000; /* year%100 */
+
+// print("%0.2d:%0.2d:%.02d %0.2d/%0.2d/%0.2d\n", /* HH:MM:SS YY/MM/DD */
+// rtc.hour, rtc.min, rtc.sec, rtc.year, rtc.mon, rtc.mday);
+ return rtc2sec(&rtc);
+}
+
+long
+rtctime(void)
+{
+ int i;
+ long t, ot;
+
+ ilock(&rtclock);
+
+ /* loop until we get two reads in a row the same */
+ t = _rtctime();
+ ot = ~t;
+ for(i = 0; i < 100 && ot != t; i++){
+ ot = t;
+ t = _rtctime();
+ }
+ if(ot != t)
+ print("rtctime: we are boofheads\n");
+
+ iunlock(&rtclock);
+ return t;
+}
+
+static void
+setrtc(Rtc *rtc)
+{
+ ilock(&rtclock);
+ rtcreg->time = dec2bcd(rtc->wday) << 24 | dec2bcd(rtc->hour) << 16 |
+ dec2bcd(rtc->min) << 8 | dec2bcd(rtc->sec);
+ rtcreg->date = dec2bcd(rtc->year - 2000) << 16 |
+ dec2bcd(rtc->mon) << 8 | dec2bcd(rtc->mday);
+ iunlock(&rtclock);
+}
+
+static Chan*
+rtcattach(char *spec)
+{
+ rtcreg = (RtcReg*)soc.rtc;
+ return devattach(L'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)
+{
+ return devopen(c, omode, rtcdir, nelem(rtcdir), devgen);
+}
+
+static void
+rtcclose(Chan*)
+{
+}
+
+static long
+rtcread(Chan *c, void *buf, long n, vlong off)
+{
+ if(c->qid.type & QTDIR)
+ return devdirread(c, buf, n, rtcdir, nelem(rtcdir), devgen);
+
+ switch((ulong)c->qid.path){
+ default:
+ error(Egreg);
+ case Qrtc:
+ return readnum(off, buf, n, rtctime(), NUMSIZE);
+ }
+}
+
+static long
+rtcwrite(Chan *c, void *buf, long n, vlong off)
+{
+ ulong offset = off;
+ char *cp, sbuf[32];
+ Rtc rtc;
+
+ switch((ulong)c->qid.path){
+ default:
+ error(Egreg);
+ case Qrtc:
+ if(offset != 0 || n >= sizeof(sbuf)-1)
+ error(Ebadarg);
+ memmove(sbuf, buf, n);
+ sbuf[n] = '\0';
+ for(cp = sbuf; *cp != '\0'; cp++)
+ if(*cp >= '0' && *cp <= '9')
+ break;
+ sec2rtc(strtoul(cp, 0, 0), &rtc);
+ setrtc(&rtc);
+ return n;
+ }
+}
+
+Dev rtcdevtab = {
+ L'r',
+ "rtc",
+
+ devreset,
+ devinit,
+ devshutdown,
+ rtcattach,
+ rtcwalk,
+ rtcstat,
+ rtcopen,
+ devcreate,
+ rtcclose,
+ rtcread,
+ devbread,
+ rtcwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+ devpower,
+};
diff --git a/sys/src/9/kw/devtwsi.c b/sys/src/9/kw/devtwsi.c
new file mode 100755
index 000000000..ec4a268c9
--- /dev/null
+++ b/sys/src/9/kw/devtwsi.c
@@ -0,0 +1,305 @@
+/*
+ * kirkwood two-wire serial interface (TWSI) and
+ * inter-integrated circuit (I⁲C) driver
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+enum {
+ Qdir,
+ Qtwsi,
+};
+
+typedef struct Kwtwsi Kwtwsi;
+typedef struct Twsi Twsi;
+
+struct Kwtwsi { /* device registers */
+ ulong saddr;
+ ulong data;
+ ulong ctl;
+ union {
+ ulong status; /* ro */
+ ulong rate; /* wo: baud rate */
+ };
+
+ ulong saddrext;
+ uchar _pad0[0x1c-0x14];
+ ulong reset;
+ uchar _pad1[0x98-0x20];
+ ulong initlastdata;
+};
+
+enum {
+ Twsidowrite,
+ Twsidoread,
+
+ /* ctl bits */
+ Twsiack = 1<<2, /* recv'd data; clear to ack */
+ Twsiint = 1<<3, /* interrupt conditions true */
+ Twsistop = 1<<4,
+ Twsistart = 1<<5,
+ Twsislaveen = 1<<6,
+ Twsiinten = 1<<7, /* interrupts enabled */
+
+ /* status codes */
+ SStart = 0x08,
+ SWa = 0x18,
+ SWda = 0x28,
+ SRa = 0x40,
+ SRda = 0x50,
+ SRna = 0x58,
+};
+
+struct Twsi {
+ QLock;
+ Rendez nextbyte;
+
+ /* remainder is state needed to track the operation in progress */
+ int intr;
+ int done;
+
+ uchar *bp; /* current ptr into buf */
+ uchar *end;
+
+ ulong addr; /* device address */
+ char *error;
+};
+
+static Twsi twsi;
+
+static Dirtab twsidir[] = {
+ ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
+ "twsi", {Qtwsi}, 0, 0660,
+};
+
+static char Eabsts[] = "abnormal status";
+
+static void
+twsifinish(void)
+{
+ Kwtwsi *krp = (Kwtwsi *)soc.twsi;
+
+ twsi.done = 1;
+ krp->ctl |= Twsistop;
+ coherence();
+}
+
+static void
+twsidoread(void)
+{
+ Kwtwsi *krp = (Kwtwsi *)soc.twsi;
+
+ switch(krp->status){
+ case SStart:
+ krp->data = twsi.addr << 1 | Twsidoread;
+ break;
+ case SRa:
+ krp->ctl |= Twsiack;
+ break;
+ case SRda:
+ if(twsi.bp < twsi.end) {
+ *twsi.bp++ = krp->data;
+ krp->ctl |= Twsiack;
+ } else
+ krp->ctl &= ~Twsiack;
+ break;
+ case SRna:
+ twsifinish();
+ break;
+ default:
+ twsifinish();
+ twsi.error = Eabsts;
+ break;
+ }
+}
+
+static void
+twsidowrite(void)
+{
+ Kwtwsi *krp = (Kwtwsi *)soc.twsi;
+
+ switch(krp->status){
+ case SStart:
+ krp->data = twsi.addr << 1 | Twsidowrite;
+ break;
+ case SWa:
+ case SWda:
+ if(twsi.bp < twsi.end)
+ krp->data = *twsi.bp++;
+ else
+ twsifinish();
+ break;
+ default:
+ twsifinish();
+ twsi.error = Eabsts;
+ break;
+ }
+}
+
+static int
+twsigotintr(void *)
+{
+ return twsi.intr;
+}
+
+static long
+twsixfer(uchar *buf, ulong len, ulong offset, void (*op)(void))
+{
+ ulong off;
+ char *err;
+ Kwtwsi *krp = (Kwtwsi *)soc.twsi;
+
+ qlock(&twsi);
+ twsi.bp = buf;
+ twsi.end = buf + len;
+
+ twsi.addr = offset;
+ twsi.done = twsi.intr = 0;
+ twsi.error = nil;
+
+ krp->ctl = (krp->ctl & ~Twsiint) | Twsistart;
+ coherence();
+ while (!twsi.done) {
+ sleep(&twsi.nextbyte, twsigotintr, 0);
+ twsi.intr = 0;
+ (*op)();
+ /* signal to start new op & extinguish intr source */
+ krp->ctl &= ~Twsiint;
+ coherence();
+ krp->ctl |= Twsiinten;
+ coherence();
+ }
+ twsifinish();
+ err = twsi.error;
+ off = twsi.bp - buf;
+ twsi.bp = nil; /* prevent accidents */
+ qunlock(&twsi);
+
+ if(err)
+ error(err);
+ return off;
+}
+
+static void
+interrupt(Ureg *, void *)
+{
+ Kwtwsi *krp = (Kwtwsi *)soc.twsi;
+
+ twsi.intr = 1;
+ wakeup(&twsi.nextbyte);
+
+ krp->ctl &= ~Twsiinten; /* stop further interrupts */
+ coherence();
+ intrclear(Irqlo, IRQ0twsi);
+}
+
+static void
+twsiinit(void)
+{
+ Kwtwsi *krp = (Kwtwsi *)soc.twsi;
+
+ intrenable(Irqlo, IRQ0twsi, interrupt, nil, "twsi");
+ krp->ctl &= ~Twsiint;
+ krp->ctl |= Twsiinten;
+ coherence();
+}
+
+static void
+twsishutdown(void)
+{
+ Kwtwsi *krp = (Kwtwsi *)soc.twsi;
+
+ krp->ctl &= ~Twsiinten;
+ coherence();
+ intrdisable(Irqlo, IRQ0twsi, interrupt, nil, "twsi");
+}
+
+static Chan*
+twsiattach(char *param)
+{
+ return devattach(L'⁲', param);
+}
+
+static Walkqid*
+twsiwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, twsidir, nelem(twsidir), devgen);
+}
+
+static int
+twsistat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, twsidir, nelem(twsidir), devgen);
+}
+
+static Chan*
+twsiopen(Chan *c, int omode)
+{
+ switch((ulong)c->qid.path){
+ default:
+ error(Eperm);
+ case Qdir:
+ case Qtwsi:
+ break;
+ }
+ c = devopen(c, omode, twsidir, nelem(twsidir), devgen);
+ c->mode = openmode(omode);
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+}
+
+static void
+twsiclose(Chan *)
+{
+}
+
+static long
+twsiread(Chan *c, void *v, long n, vlong off)
+{
+ switch((ulong)c->qid.path){
+ default:
+ error(Eperm);
+ case Qdir:
+ return devdirread(c, v, n, twsidir, nelem(twsidir), devgen);
+ case Qtwsi:
+ return twsixfer(v, n, off, twsidoread);
+ }
+}
+
+static long
+twsiwrite(Chan *c, void *v, long n, vlong off)
+{
+ switch((ulong)c->qid.path){
+ default:
+ error(Eperm);
+ case Qtwsi:
+ return twsixfer(v, n, off, twsidowrite);
+ }
+}
+
+Dev twsidevtab = {
+ L'⁲',
+ "twsi",
+
+ devreset,
+ twsiinit,
+ twsishutdown,
+ twsiattach,
+ twsiwalk,
+ twsistat,
+ twsiopen,
+ devcreate,
+ twsiclose,
+ twsiread,
+ devbread,
+ twsiwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
diff --git a/sys/src/9/kw/devusb.c b/sys/src/9/kw/devusb.c
new file mode 100755
index 000000000..de5826165
--- /dev/null
+++ b/sys/src/9/kw/devusb.c
@@ -0,0 +1,1460 @@
+/*
+ * USB device driver framework.
+ *
+ * This is in charge of providing access to actual HCIs
+ * and providing I/O to the various endpoints of devices.
+ * A separate user program (usbd) is in charge of
+ * enumerating the bus, setting up endpoints and
+ * starting devices (also user programs).
+ *
+ * The interface provided is a violation of the standard:
+ * you're welcome.
+ *
+ * The interface consists of a root directory with several files
+ * plus a directory (epN.M) with two files per endpoint.
+ * A device is represented by its first endpoint, which
+ * is a control endpoint automatically allocated for each device.
+ * Device control endpoints may be used to create new endpoints.
+ * Devices corresponding to hubs may also allocate new devices,
+ * perhaps also hubs. Initially, a hub device is allocated for
+ * each controller present, to represent its root hub. Those can
+ * never be removed.
+ *
+ * All endpoints refer to the first endpoint (epN.0) of the device,
+ * which keeps per-device information, and also to the HCI used
+ * to reach them. Although all endpoints cache that information.
+ *
+ * epN.M/data files permit I/O and are considered DMEXCL.
+ * epN.M/ctl files provide status info and accept control requests.
+ *
+ * Endpoints may be given file names to be listed also at #u,
+ * for those drivers that have nothing to do after configuring the
+ * device and its endpoints.
+ *
+ * Drivers for different controllers are kept at usb[oue]hci.c
+ * It's likely we could factor out much from controllers into
+ * a generic controller driver, the problem is that details
+ * regarding how to handle toggles, tokens, Tds, etc. will
+ * get in the way. Thus, code is probably easier the way it is.
+ */
+
+#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/usb.h"
+
+typedef struct Hcitype Hcitype;
+
+enum
+{
+ /* Qid numbers */
+ Qdir = 0, /* #u */
+ Qusbdir, /* #u/usb */
+ Qctl, /* #u/usb/ctl - control requests */
+
+ Qep0dir, /* #u/usb/ep0.0 - endpoint 0 dir */
+ Qep0io, /* #u/usb/ep0.0/data - endpoint 0 I/O */
+ Qep0ctl, /* #u/usb/ep0.0/ctl - endpoint 0 ctl. */
+ Qep0dummy, /* give 4 qids to each endpoint */
+
+ Qepdir = 0, /* (qid-qep0dir)&3 is one of these */
+ Qepio, /* to identify which file for the endpoint */
+ Qepctl,
+
+ /* ... */
+
+ /* Usb ctls. */
+ CMdebug = 0, /* debug on|off */
+ CMdump, /* dump (data structures for debug) */
+
+ /* Ep. ctls */
+ CMnew = 0, /* new nb ctl|bulk|intr|iso r|w|rw (endpoint) */
+ CMnewdev, /* newdev full|low|high portnb (allocate new devices) */
+ CMhub, /* hub (set the device as a hub) */
+ CMspeed, /* speed full|low|high|no */
+ CMmaxpkt, /* maxpkt size */
+ CMntds, /* ntds nb (max nb. of tds per µframe) */
+ CMclrhalt, /* clrhalt (halt was cleared on endpoint) */
+ CMpollival, /* pollival interval (interrupt/iso) */
+ CMhz, /* hz n (samples/sec; iso) */
+ CMsamplesz, /* samplesz n (sample size; iso) */
+ CMinfo, /* info infostr (ke.ep info for humans) */
+ CMdetach, /* detach (abort I/O forever on this ep). */
+ CMaddress, /* address (address is assigned) */
+ CMdebugep, /* debug n (set/clear debug for this ep) */
+ CMname, /* name str (show up as #u/name as well) */
+ CMtmout, /* timeout n (activate timeouts for ep) */
+ CMpreset, /* reset the port */
+
+ /* Hub feature selectors */
+ Rportenable = 1,
+ Rportreset = 4,
+
+};
+
+struct Hcitype
+{
+ char* type;
+ int (*reset)(Hci*);
+};
+
+#define QID(q) ((int)(q).path)
+
+static char Edetach[] = "device is detached";
+static char Enotconf[] = "endpoint not configured";
+char Estalled[] = "endpoint stalled";
+
+static Cmdtab usbctls[] =
+{
+ {CMdebug, "debug", 2},
+ {CMdump, "dump", 1},
+};
+
+static Cmdtab epctls[] =
+{
+ {CMnew, "new", 4},
+ {CMnewdev, "newdev", 3},
+ {CMhub, "hub", 1},
+ {CMspeed, "speed", 2},
+ {CMmaxpkt, "maxpkt", 2},
+ {CMntds, "ntds", 2},
+ {CMpollival, "pollival", 2},
+ {CMsamplesz, "samplesz", 2},
+ {CMhz, "hz", 2},
+ {CMinfo, "info", 0},
+ {CMdetach, "detach", 1},
+ {CMaddress, "address", 1},
+ {CMdebugep, "debug", 2},
+ {CMclrhalt, "clrhalt", 1},
+ {CMname, "name", 2},
+ {CMtmout, "timeout", 2},
+ {CMpreset, "reset", 1},
+};
+
+static Dirtab usbdir[] =
+{
+ "ctl", {Qctl}, 0, 0666,
+};
+
+char *usbmodename[] =
+{
+ [OREAD] "r",
+ [OWRITE] "w",
+ [ORDWR] "rw",
+};
+
+static char *ttname[] =
+{
+ [Tnone] "none",
+ [Tctl] "control",
+ [Tiso] "iso",
+ [Tintr] "interrupt",
+ [Tbulk] "bulk",
+};
+
+static char *spname[] =
+{
+ [Fullspeed] "full",
+ [Lowspeed] "low",
+ [Highspeed] "high",
+ [Nospeed] "no",
+};
+
+static int debug;
+static Hcitype hcitypes[Nhcis];
+static Hci* hcis[Nhcis];
+static QLock epslck; /* add, del, lookup endpoints */
+static Ep* eps[Neps]; /* all endpoints known */
+static int epmax; /* 1 + last endpoint index used */
+static int usbidgen; /* device address generator */
+
+/*
+ * Is there something like this in a library? should it be?
+ */
+char*
+seprintdata(char *s, char *se, uchar *d, int n)
+{
+ int i, l;
+
+ s = seprint(s, se, " %#p[%d]: ", d, n);
+ l = n;
+ if(l > 10)
+ l = 10;
+ for(i=0; i<l; i++)
+ s = seprint(s, se, " %2.2ux", d[i]);
+ if(l < n)
+ s = seprint(s, se, "...");
+ return s;
+}
+
+static int
+name2speed(char *name)
+{
+ int i;
+
+ for(i = 0; i < nelem(spname); i++)
+ if(strcmp(name, spname[i]) == 0)
+ return i;
+ return Nospeed;
+}
+
+static int
+name2ttype(char *name)
+{
+ int i;
+
+ for(i = 0; i < nelem(ttname); i++)
+ if(strcmp(name, ttname[i]) == 0)
+ return i;
+ /* may be a std. USB ep. type */
+ i = strtol(name, nil, 0);
+ switch(i+1){
+ case Tctl:
+ case Tiso:
+ case Tbulk:
+ case Tintr:
+ return i+1;
+ default:
+ return Tnone;
+ }
+}
+
+static int
+name2mode(char *mode)
+{
+ int i;
+
+ for(i = 0; i < nelem(usbmodename); i++)
+ if(strcmp(mode, usbmodename[i]) == 0)
+ return i;
+ return -1;
+}
+
+static int
+qid2epidx(int q)
+{
+ q = (q-Qep0dir)/4;
+ if(q < 0 || q >= epmax || eps[q] == nil)
+ return -1;
+ return q;
+}
+
+static int
+isqtype(int q, int type)
+{
+ if(q < Qep0dir)
+ return 0;
+ q -= Qep0dir;
+ return (q & 3) == type;
+}
+
+void
+addhcitype(char* t, int (*r)(Hci*))
+{
+ static int ntype;
+
+ if(ntype == Nhcis)
+ panic("too many USB host interface types");
+ hcitypes[ntype].type = t;
+ hcitypes[ntype].reset = r;
+ ntype++;
+}
+
+static char*
+seprintep(char *s, char *se, Ep *ep, int all)
+{
+ static char* dsnames[] = { "config", "enabled", "detached", "reset" };
+ Udev *d;
+ int i;
+ int di;
+
+ d = ep->dev;
+
+ qlock(ep);
+ if(waserror()){
+ qunlock(ep);
+ nexterror();
+ }
+ di = ep->dev->nb;
+ if(all)
+ s = seprint(s, se, "dev %d ep %d ", di, ep->nb);
+ s = seprint(s, se, "%s", dsnames[ep->dev->state]);
+ s = seprint(s, se, " %s", ttname[ep->ttype]);
+ assert(ep->mode == OREAD || ep->mode == OWRITE || ep->mode == ORDWR);
+ s = seprint(s, se, " %s", usbmodename[ep->mode]);
+ s = seprint(s, se, " speed %s", spname[d->speed]);
+ s = seprint(s, se, " maxpkt %ld", ep->maxpkt);
+ s = seprint(s, se, " pollival %ld", ep->pollival);
+ s = seprint(s, se, " samplesz %ld", ep->samplesz);
+ s = seprint(s, se, " hz %ld", ep->hz);
+ s = seprint(s, se, " hub %d", ep->dev->hub);
+ s = seprint(s, se, " port %d", ep->dev->port);
+ if(ep->inuse)
+ s = seprint(s, se, " busy");
+ else
+ s = seprint(s, se, " idle");
+ if(all){
+ s = seprint(s, se, " load %uld", ep->load);
+ s = seprint(s, se, " ref %ld addr %#p", ep->ref, ep);
+ s = seprint(s, se, " idx %d", ep->idx);
+ if(ep->name != nil)
+ s = seprint(s, se, " name '%s'", ep->name);
+ if(ep->tmout != 0)
+ s = seprint(s, se, " tmout");
+ if(ep == ep->ep0){
+ s = seprint(s, se, " ctlrno %#x", ep->hp->ctlrno);
+ s = seprint(s, se, " eps:");
+ for(i = 0; i < nelem(d->eps); i++)
+ if(d->eps[i] != nil)
+ s = seprint(s, se, " ep%d.%d", di, i);
+ }
+ }
+ if(ep->info != nil)
+ s = seprint(s, se, "\n%s %s\n", ep->info, ep->hp->type);
+ else
+ s = seprint(s, se, "\n");
+ qunlock(ep);
+ poperror();
+ return s;
+}
+
+static Ep*
+epalloc(Hci *hp)
+{
+ Ep *ep;
+ int i;
+
+ ep = smalloc(sizeof(Ep));
+ ep->ref = 1;
+ qlock(&epslck);
+ for(i = 0; i < Neps; i++)
+ if(eps[i] == nil)
+ break;
+ if(i == Neps){
+ qunlock(&epslck);
+ free(ep);
+ print("usb: bug: too few endpoints.\n");
+ return nil;
+ }
+ ep->idx = i;
+ if(epmax <= i)
+ epmax = i+1;
+ eps[i] = ep;
+ ep->hp = hp;
+ ep->maxpkt = 8;
+ ep->ntds = 1;
+ ep->samplesz = ep->pollival = ep->hz = 0; /* make them void */
+ qunlock(&epslck);
+ return ep;
+}
+
+static Ep*
+getep(int i)
+{
+ Ep *ep;
+
+ if(i < 0 || i >= epmax || eps[i] == nil)
+ return nil;
+ qlock(&epslck);
+ ep = eps[i];
+ if(ep != nil)
+ incref(ep);
+ qunlock(&epslck);
+ return ep;
+}
+
+static void
+putep(Ep *ep)
+{
+ Udev *d;
+
+ if(ep != nil && decref(ep) == 0){
+ d = ep->dev;
+ deprint("usb: ep%d.%d %#p released\n", d->nb, ep->nb, ep);
+ qlock(&epslck);
+ eps[ep->idx] = nil;
+ if(ep->idx == epmax-1)
+ epmax--;
+ if(ep == ep->ep0 && ep->dev != nil && ep->dev->nb == usbidgen)
+ usbidgen--;
+ qunlock(&epslck);
+ if(d != nil){
+ qlock(ep->ep0);
+ d->eps[ep->nb] = nil;
+ qunlock(ep->ep0);
+ }
+ if(ep->ep0 != ep){
+ putep(ep->ep0);
+ ep->ep0 = nil;
+ }
+ free(ep->info);
+ free(ep->name);
+ free(ep);
+ }
+}
+
+static void
+dumpeps(void)
+{
+ int i;
+ static char buf[512];
+ char *s;
+ char *e;
+ Ep *ep;
+
+ print("usb dump eps: epmax %d Neps %d (ref=1+ for dump):\n", epmax, Neps);
+ for(i = 0; i < epmax; i++){
+ s = buf;
+ e = buf+sizeof(buf);
+ ep = getep(i);
+ if(ep != nil){
+ if(waserror()){
+ putep(ep);
+ nexterror();
+ }
+ s = seprint(s, e, "ep%d.%d ", ep->dev->nb, ep->nb);
+ seprintep(s, e, ep, 1);
+ print("%s", buf);
+ ep->hp->seprintep(buf, e, ep);
+ print("%s", buf);
+ poperror();
+ putep(ep);
+ }
+ }
+ print("usb dump hcis:\n");
+ for(i = 0; i < Nhcis; i++)
+ if(hcis[i] != nil)
+ hcis[i]->dump(hcis[i]);
+}
+
+static int
+newusbid(Hci *)
+{
+ int id;
+
+ qlock(&epslck);
+ id = ++usbidgen;
+ if(id >= 0x7F)
+ print("#u: too many device addresses; reuse them more\n");
+ qunlock(&epslck);
+ return id;
+}
+
+/*
+ * Create endpoint 0 for a new device
+ */
+static Ep*
+newdev(Hci *hp, int ishub, int isroot)
+{
+ Ep *ep;
+ Udev *d;
+
+ ep = epalloc(hp);
+ d = ep->dev = smalloc(sizeof(Udev));
+ d->nb = newusbid(hp);
+ d->eps[0] = ep;
+ ep->nb = 0;
+ ep->toggle[0] = ep->toggle[1] = 0;
+ d->ishub = ishub;
+ d->isroot = isroot;
+ if(hp->highspeed != 0)
+ d->speed = Highspeed;
+ else
+ d->speed = Fullspeed;
+ d->state = Dconfig; /* address not yet set */
+ ep->dev = d;
+ ep->ep0 = ep; /* no ref counted here */
+ ep->ttype = Tctl;
+ ep->tmout = Xfertmout;
+ ep->mode = ORDWR;
+ dprint("newdev %#p ep%d.%d %#p\n", d, d->nb, ep->nb, ep);
+ return ep;
+}
+
+/*
+ * Create a new endpoint for the device
+ * accessed via the given endpoint 0.
+ */
+static Ep*
+newdevep(Ep *ep, int i, int tt, int mode)
+{
+ Ep *nep;
+ Udev *d;
+
+ d = ep->dev;
+ if(d->eps[i] != nil)
+ error("endpoint already in use");
+ nep = epalloc(ep->hp);
+ incref(ep);
+ d->eps[i] = nep;
+ nep->nb = i;
+ nep->toggle[0] = nep->toggle[1] = 0;
+ nep->ep0 = ep;
+ nep->dev = ep->dev;
+ nep->mode = mode;
+ nep->ttype = tt;
+ nep->debug = ep->debug;
+ /* set defaults */
+ switch(tt){
+ case Tctl:
+ nep->tmout = Xfertmout;
+ break;
+ case Tintr:
+ nep->pollival = 10;
+ break;
+ case Tiso:
+ nep->tmout = Xfertmout;
+ nep->pollival = 10;
+ nep->samplesz = 4;
+ nep->hz = 44100;
+ break;
+ }
+ deprint("newdevep ep%d.%d %#p\n", d->nb, nep->nb, nep);
+ return ep;
+}
+
+static int
+epdataperm(int mode)
+{
+
+ switch(mode){
+ case OREAD:
+ return 0440|DMEXCL;
+ break;
+ case OWRITE:
+ return 0220|DMEXCL;
+ break;
+ default:
+ return 0660|DMEXCL;
+ }
+}
+
+static int
+usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
+{
+ Qid q;
+ Dirtab *dir;
+ int perm;
+ char *se;
+ Ep *ep;
+ int nb;
+ int mode;
+
+ if(0)ddprint("usbgen q %#x s %d...", QID(c->qid), s);
+ if(s == DEVDOTDOT){
+ if(QID(c->qid) <= Qusbdir){
+ mkqid(&q, Qdir, 0, QTDIR);
+ devdir(c, q, "#u", 0, eve, 0555, dp);
+ }else{
+ mkqid(&q, Qusbdir, 0, QTDIR);
+ devdir(c, q, "usb", 0, eve, 0555, dp);
+ }
+ if(0)ddprint("ok\n");
+ return 1;
+ }
+
+ switch(QID(c->qid)){
+ case Qdir: /* list #u */
+ if(s == 0){
+ mkqid(&q, Qusbdir, 0, QTDIR);
+ devdir(c, q, "usb", 0, eve, 0555, dp);
+ if(0)ddprint("ok\n");
+ return 1;
+ }
+ s--;
+ if(s < 0 || s >= epmax)
+ goto Fail;
+ ep = getep(s);
+ if(ep == nil || ep->name == nil){
+ if(ep != nil)
+ putep(ep);
+ if(0)ddprint("skip\n");
+ return 0;
+ }
+ if(waserror()){
+ putep(ep);
+ nexterror();
+ }
+ mkqid(&q, Qep0io+s*4, 0, QTFILE);
+ devdir(c, q, ep->name, 0, eve, epdataperm(ep->mode), dp);
+ putep(ep);
+ poperror();
+ if(0)ddprint("ok\n");
+ return 1;
+
+ case Qusbdir: /* list #u/usb */
+ Usbdir:
+ if(s < nelem(usbdir)){
+ dir = &usbdir[s];
+ mkqid(&q, dir->qid.path, 0, QTFILE);
+ devdir(c, q, dir->name, dir->length, eve, dir->perm, dp);
+ if(0)ddprint("ok\n");
+ return 1;
+ }
+ s -= nelem(usbdir);
+ if(s < 0 || s >= epmax)
+ goto Fail;
+ ep = getep(s);
+ if(ep == nil){
+ if(0)ddprint("skip\n");
+ return 0;
+ }
+ if(waserror()){
+ putep(ep);
+ nexterror();
+ }
+ se = up->genbuf+sizeof(up->genbuf);
+ seprint(up->genbuf, se, "ep%d.%d", ep->dev->nb, ep->nb);
+ mkqid(&q, Qep0dir+4*s, 0, QTDIR);
+ putep(ep);
+ poperror();
+ devdir(c, q, up->genbuf, 0, eve, 0755, dp);
+ if(0)ddprint("ok\n");
+ return 1;
+
+ case Qctl:
+ s = 0;
+ goto Usbdir;
+
+ default: /* list #u/usb/epN.M */
+ nb = qid2epidx(QID(c->qid));
+ ep = getep(nb);
+ if(ep == nil)
+ goto Fail;
+ mode = ep->mode;
+ putep(ep);
+ if(isqtype(QID(c->qid), Qepdir)){
+ Epdir:
+ switch(s){
+ case 0:
+ mkqid(&q, Qep0io+nb*4, 0, QTFILE);
+ perm = epdataperm(mode);
+ devdir(c, q, "data", 0, eve, perm, dp);
+ break;
+ case 1:
+ mkqid(&q, Qep0ctl+nb*4, 0, QTFILE);
+ devdir(c, q, "ctl", 0, eve, 0664, dp);
+ break;
+ default:
+ goto Fail;
+ }
+ }else if(isqtype(QID(c->qid), Qepctl)){
+ s = 1;
+ goto Epdir;
+ }else{
+ s = 0;
+ goto Epdir;
+ }
+ if(0)ddprint("ok\n");
+ return 1;
+ }
+Fail:
+ if(0)ddprint("fail\n");
+ return -1;
+}
+
+static Hci*
+hciprobe(int cardno, int ctlrno)
+{
+ Hci *hp;
+ char *type;
+ char name[64];
+ static int epnb = 1; /* guess the endpoint nb. for the controller */
+
+ ddprint("hciprobe %d %d\n", cardno, ctlrno);
+ hp = smalloc(sizeof(Hci));
+ hp->ctlrno = ctlrno;
+ hp->tbdf = BUSUNKNOWN;
+
+ if(cardno < 0)
+ for(cardno = 0; cardno < Nhcis; cardno++){
+ if(hcitypes[cardno].type == nil)
+ break;
+ type = hp->type;
+ if(type==nil || *type==0)
+ type = "uhci";
+ if(cistrcmp(hcitypes[cardno].type, type) == 0)
+ break;
+ }
+
+ if(cardno >= Nhcis || hcitypes[cardno].type == nil){
+ free(hp);
+ return nil;
+ }
+ dprint("%s...", hcitypes[cardno].type);
+ if(hcitypes[cardno].reset(hp) < 0){
+ free(hp);
+ return nil;
+ }
+
+ snprint(name, sizeof(name), "usb%s", hcitypes[cardno].type);
+ intrenable(Irqlo, hp->irq, hp->interrupt, hp, name);
+ print("#u/usb/ep%d.0: %s: port %#luX irq %d\n",
+ epnb, hcitypes[cardno].type, hp->port, hp->irq);
+ epnb++;
+ return hp;
+}
+
+static void
+usbreset(void)
+{
+ int cardno, ctlrno;
+ Hci *hp;
+
+ dprint("usbreset\n");
+
+ for(ctlrno = 0; ctlrno < Nhcis; ctlrno++)
+ if((hp = hciprobe(-1, ctlrno)) != nil)
+ hcis[ctlrno] = hp;
+ cardno = ctlrno = 0;
+ while(cardno < Nhcis && ctlrno < Nhcis && hcitypes[cardno].type != nil)
+ if(hcis[ctlrno] != nil)
+ ctlrno++;
+ else{
+ hp = hciprobe(cardno, ctlrno);
+ if(hp == nil)
+ cardno++;
+ hcis[ctlrno++] = hp;
+ }
+ if(hcis[Nhcis-1] != nil)
+ print("usbreset: bug: Nhcis too small\n");
+}
+
+static void
+usbinit(void)
+{
+ Hci *hp;
+ int ctlrno;
+ Ep *d;
+ char info[40];
+
+ dprint("usbinit\n");
+ for(ctlrno = 0; ctlrno < Nhcis; ctlrno++){
+ hp = hcis[ctlrno];
+ if(hp != nil){
+ if(hp->init != nil)
+ hp->init(hp);
+ d = newdev(hp, 1, 1); /* new root hub */
+ d->dev->state = Denabled; /* although addr == 0 */
+ d->maxpkt = 64;
+ snprint(info, sizeof(info), "ports %d", hp->nports);
+ kstrdup(&d->info, info);
+ }
+ }
+}
+
+static Chan*
+usbattach(char *spec)
+{
+ return devattach(L'u', spec);
+}
+
+static Walkqid*
+usbwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, nil, 0, usbgen);
+}
+
+static int
+usbstat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, nil, 0, usbgen);
+}
+
+/*
+ * µs for the given transfer, for bandwidth allocation.
+ * This is a very rough worst case for what 5.11.3
+ * of the usb 2.0 spec says.
+ * Also, we are using maxpkt and not actual transfer sizes.
+ * Only when we are sure we
+ * are not exceeding b/w might we consider adjusting it.
+ */
+static ulong
+usbload(int speed, int maxpkt)
+{
+ enum{ Hostns = 1000, Hubns = 333 };
+ ulong l;
+ ulong bs;
+
+ l = 0;
+ bs = 10UL * maxpkt;
+ switch(speed){
+ case Highspeed:
+ l = 55*8*2 + 2 * (3 + bs) + Hostns;
+ break;
+ case Fullspeed:
+ l = 9107 + 84 * (4 + bs) + Hostns;
+ break;
+ case Lowspeed:
+ l = 64107 + 2 * Hubns + 667 * (3 + bs) + Hostns;
+ break;
+ default:
+ print("usbload: bad speed %d\n", speed);
+ /* let it run */
+ }
+ return l / 1000UL; /* in µs */
+}
+
+static Chan*
+usbopen(Chan *c, int omode)
+{
+ int q;
+ Ep *ep;
+ int mode;
+
+ mode = openmode(omode);
+ q = QID(c->qid);
+
+ if(q >= Qep0dir && qid2epidx(q) < 0)
+ error(Eio);
+ if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
+ return devopen(c, omode, nil, 0, usbgen);
+
+ ep = getep(qid2epidx(q));
+ if(ep == nil)
+ error(Eio);
+ deprint("usbopen q %#x fid %d omode %d\n", q, c->fid, mode);
+ if(waserror()){
+ putep(ep);
+ nexterror();
+ }
+ qlock(ep);
+ if(ep->inuse){
+ qunlock(ep);
+ error(Einuse);
+ }
+ ep->inuse = 1;
+ qunlock(ep);
+ if(waserror()){
+ ep->inuse = 0;
+ nexterror();
+ }
+ if(mode != OREAD && ep->mode == OREAD)
+ error(Eperm);
+ if(mode != OWRITE && ep->mode == OWRITE)
+ error(Eperm);
+ if(ep->ttype == Tnone)
+ error(Enotconf);
+ ep->clrhalt = 0;
+ ep->rhrepl = -1;
+ if(ep->load == 0)
+ ep->load = usbload(ep->dev->speed, ep->maxpkt);
+ ep->hp->epopen(ep);
+
+ poperror(); /* ep->inuse */
+ poperror(); /* don't putep(): ref kept for fid using the ep. */
+
+ c->mode = mode;
+ c->flag |= COPEN;
+ c->offset = 0;
+ c->aux = nil; /* paranoia */
+ return c;
+}
+
+static void
+epclose(Ep *ep)
+{
+ qlock(ep);
+ if(waserror()){
+ qunlock(ep);
+ nexterror();
+ }
+ if(ep->inuse){
+ ep->hp->epclose(ep);
+ ep->inuse = 0;
+ }
+ qunlock(ep);
+ poperror();
+}
+
+static void
+usbclose(Chan *c)
+{
+ int q;
+ Ep *ep;
+
+ q = QID(c->qid);
+ if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
+ return;
+
+ ep = getep(qid2epidx(q));
+ if(ep == nil)
+ return;
+ deprint("usbclose q %#x fid %d ref %ld\n", q, c->fid, ep->ref);
+ if(waserror()){
+ putep(ep);
+ nexterror();
+ }
+ if(c->flag & COPEN){
+ free(c->aux);
+ c->aux = nil;
+ epclose(ep);
+ putep(ep); /* release ref kept since usbopen */
+ c->flag &= ~COPEN;
+ }
+ poperror();
+ putep(ep);
+}
+
+static long
+ctlread(Chan *c, void *a, long n, vlong offset)
+{
+ int q;
+ char *s;
+ char *us;
+ char *se;
+ Ep *ep;
+ int i;
+
+ q = QID(c->qid);
+ us = s = smalloc(READSTR);
+ se = s + READSTR;
+ if(waserror()){
+ free(us);
+ nexterror();
+ }
+ if(q == Qctl)
+ for(i = 0; i < epmax; i++){
+ ep = getep(i);
+ if(ep != nil){
+ if(waserror()){
+ putep(ep);
+ nexterror();
+ }
+ s = seprint(s, se, "ep%d.%d ", ep->dev->nb, ep->nb);
+ s = seprintep(s, se, ep, 0);
+ poperror();
+ }
+ putep(ep);
+ }
+ else{
+ ep = getep(qid2epidx(q));
+ if(ep == nil)
+ error(Eio);
+ if(waserror()){
+ putep(ep);
+ nexterror();
+ }
+ if(c->aux != nil){
+ /* After a new endpoint request we read
+ * the new endpoint name back.
+ */
+ strecpy(s, se, c->aux);
+ free(c->aux);
+ c->aux = nil;
+ }else
+ seprintep(s, se, ep, 0);
+ poperror();
+ putep(ep);
+ }
+ n = readstr(offset, a, n, us);
+ poperror();
+ free(us);
+ return n;
+}
+
+/*
+ * Fake root hub emulation.
+ */
+static long
+rhubread(Ep *ep, void *a, long n)
+{
+ char *b;
+
+ if(ep->dev->isroot == 0 || ep->nb != 0 || n < 2)
+ return -1;
+ if(ep->rhrepl < 0)
+ return -1;
+
+ b = a;
+ memset(b, 0, n);
+ PUT2(b, ep->rhrepl);
+ ep->rhrepl = -1;
+ return n;
+}
+
+static long
+rhubwrite(Ep *ep, void *a, long n)
+{
+ uchar *s;
+ int cmd;
+ int feature;
+ int port;
+ Hci *hp;
+
+ if(ep->dev == nil || ep->dev->isroot == 0 || ep->nb != 0)
+ return -1;
+ if(n != Rsetuplen)
+ error("root hub is a toy hub");
+ ep->rhrepl = -1;
+ s = a;
+ if(s[Rtype] != (Rh2d|Rclass|Rother) && s[Rtype] != (Rd2h|Rclass|Rother))
+ error("root hub is a toy hub");
+ hp = ep->hp;
+ cmd = s[Rreq];
+ feature = GET2(s+Rvalue);
+ port = GET2(s+Rindex);
+ if(port < 1 || port > hp->nports)
+ error("bad hub port number");
+ switch(feature){
+ case Rportenable:
+ ep->rhrepl = hp->portenable(hp, port, cmd == Rsetfeature);
+ break;
+ case Rportreset:
+ ep->rhrepl = hp->portreset(hp, port, cmd == Rsetfeature);
+ break;
+ case Rgetstatus:
+ ep->rhrepl = hp->portstatus(hp, port);
+ break;
+ default:
+ ep->rhrepl = 0;
+ }
+ return n;
+}
+
+static long
+usbread(Chan *c, void *a, long n, vlong offset)
+{
+ int q;
+ Ep *ep;
+ int nr;
+
+ q = QID(c->qid);
+
+ if(c->qid.type == QTDIR)
+ return devdirread(c, a, n, nil, 0, usbgen);
+
+ if(q == Qctl || isqtype(q, Qepctl))
+ return ctlread(c, a, n, offset);
+
+ ep = getep(qid2epidx(q));
+ if(ep == nil)
+ error(Eio);
+ if(waserror()){
+ putep(ep);
+ nexterror();
+ }
+ if(ep->dev->state == Ddetach)
+ error(Edetach);
+ if(ep->mode == OWRITE || ep->inuse == 0)
+ error(Ebadusefd);
+ switch(ep->ttype){
+ case Tnone:
+ error("endpoint not configured");
+ case Tctl:
+ nr = rhubread(ep, a, n);
+ if(nr >= 0){
+ n = nr;
+ break;
+ }
+ /* else fall */
+ default:
+ ddeprint("\nusbread q %#x fid %d cnt %ld off %lld\n",q,c->fid,n,offset);
+ n = ep->hp->epread(ep, a, n);
+ break;
+ }
+ poperror();
+ putep(ep);
+ return n;
+}
+
+static long
+pow2(int n)
+{
+ return 1 << n;
+}
+
+static void
+setmaxpkt(Ep *ep, char* s)
+{
+ long spp; /* samples per packet */
+
+ if(ep->dev->speed == Highspeed)
+ spp = (ep->hz * ep->pollival * ep->ntds + 7999) / 8000;
+ else
+ spp = (ep->hz * ep->pollival + 999) / 1000;
+ ep->maxpkt = spp * ep->samplesz;
+ deprint("usb: %s: setmaxpkt: hz %ld poll %ld"
+ " ntds %d %s speed -> spp %ld maxpkt %ld\n", s,
+ ep->hz, ep->pollival, ep->ntds, spname[ep->dev->speed],
+ spp, ep->maxpkt);
+ if(ep->maxpkt > 1024){
+ print("usb: %s: maxpkt %ld > 1024. truncating\n", s, ep->maxpkt);
+ ep->maxpkt = 1024;
+ }
+}
+
+/*
+ * Many endpoint ctls. simply update the portable representation
+ * of the endpoint. The actual controller driver will look
+ * at them to setup the endpoints as dictated.
+ */
+static long
+epctl(Ep *ep, Chan *c, void *a, long n)
+{
+ int i, l, mode, nb, tt;
+ char *b, *s;
+ Cmdbuf *cb;
+ Cmdtab *ct;
+ Ep *nep;
+ Udev *d;
+ static char *Info = "info ";
+
+ d = ep->dev;
+
+ cb = parsecmd(a, n);
+ if(waserror()){
+ free(cb);
+ nexterror();
+ }
+ ct = lookupcmd(cb, epctls, nelem(epctls));
+ if(ct == nil)
+ error(Ebadctl);
+ i = ct->index;
+ if(i == CMnew || i == CMspeed || i == CMhub || i == CMpreset)
+ if(ep != ep->ep0)
+ error("allowed only on a setup endpoint");
+ if(i != CMclrhalt && i != CMdetach && i != CMdebugep && i != CMname)
+ if(ep != ep->ep0 && ep->inuse != 0)
+ error("must configure before using");
+ switch(i){
+ case CMnew:
+ deprint("usb epctl %s\n", cb->f[0]);
+ nb = strtol(cb->f[1], nil, 0);
+ if(nb < 0 || nb >= Ndeveps)
+ error("bad endpoint number");
+ tt = name2ttype(cb->f[2]);
+ if(tt == Tnone)
+ error("unknown endpoint type");
+ mode = name2mode(cb->f[3]);
+ if(mode < 0)
+ error("unknown i/o mode");
+ newdevep(ep, nb, tt, mode);
+ break;
+ case CMnewdev:
+ deprint("usb epctl %s\n", cb->f[0]);
+ if(ep != ep->ep0 || d->ishub == 0)
+ error("not a hub setup endpoint");
+ l = name2speed(cb->f[1]);
+ if(l == Nospeed)
+ error("speed must be full|low|high");
+ nep = newdev(ep->hp, 0, 0);
+ nep->dev->speed = l;
+ if(nep->dev->speed != Lowspeed)
+ nep->maxpkt = 64; /* assume full speed */
+ nep->dev->hub = d->nb;
+ nep->dev->port = atoi(cb->f[2]);
+ /* next read request will read
+ * the name for the new endpoint
+ */
+ l = sizeof(up->genbuf);
+ snprint(up->genbuf, l, "ep%d.%d", nep->dev->nb, nep->nb);
+ kstrdup(&c->aux, up->genbuf);
+ break;
+ case CMhub:
+ deprint("usb epctl %s\n", cb->f[0]);
+ d->ishub = 1;
+ break;
+ case CMspeed:
+ l = name2speed(cb->f[1]);
+ deprint("usb epctl %s %d\n", cb->f[0], l);
+ if(l == Nospeed)
+ error("speed must be full|low|high");
+ qlock(ep->ep0);
+ d->speed = l;
+ qunlock(ep->ep0);
+ break;
+ case CMmaxpkt:
+ l = strtoul(cb->f[1], nil, 0);
+ deprint("usb epctl %s %d\n", cb->f[0], l);
+ if(l < 1 || l > 1024)
+ error("maxpkt not in [1:1024]");
+ qlock(ep);
+ ep->maxpkt = l;
+ qunlock(ep);
+ break;
+ case CMntds:
+ l = strtoul(cb->f[1], nil, 0);
+ deprint("usb epctl %s %d\n", cb->f[0], l);
+ if(l < 1 || l > 3)
+ error("ntds not in [1:3]");
+ qlock(ep);
+ ep->ntds = l;
+ qunlock(ep);
+ break;
+ case CMpollival:
+ if(ep->ttype != Tintr && ep->ttype != Tiso)
+ error("not an intr or iso endpoint");
+ l = strtoul(cb->f[1], nil, 0);
+ deprint("usb epctl %s %d\n", cb->f[0], l);
+ if(ep->ttype == Tiso ||
+ (ep->ttype == Tintr && ep->dev->speed == Highspeed)){
+ if(l < 1 || l > 16)
+ error("pollival power not in [1:16]");
+ l = pow2(l-1);
+ }else
+ if(l < 1 || l > 255)
+ error("pollival not in [1:255]");
+ qlock(ep);
+ ep->pollival = l;
+ if(ep->ttype == Tiso)
+ setmaxpkt(ep, "pollival");
+ qunlock(ep);
+ break;
+ case CMsamplesz:
+ if(ep->ttype != Tiso)
+ error("not an iso endpoint");
+ l = strtoul(cb->f[1], nil, 0);
+ deprint("usb epctl %s %d\n", cb->f[0], l);
+ if(l <= 0 || l > 8)
+ error("samplesz not in [1:8]");
+ qlock(ep);
+ ep->samplesz = l;
+ setmaxpkt(ep, "samplesz");
+ qunlock(ep);
+ break;
+ case CMhz:
+ if(ep->ttype != Tiso)
+ error("not an iso endpoint");
+ l = strtoul(cb->f[1], nil, 0);
+ deprint("usb epctl %s %d\n", cb->f[0], l);
+ if(l <= 0 || l > 100000)
+ error("hz not in [1:100000]");
+ qlock(ep);
+ ep->hz = l;
+ setmaxpkt(ep, "hz");
+ qunlock(ep);
+ break;
+ case CMclrhalt:
+ qlock(ep);
+ deprint("usb epctl %s\n", cb->f[0]);
+ ep->clrhalt = 1;
+ qunlock(ep);
+ break;
+ case CMinfo:
+ deprint("usb epctl %s\n", cb->f[0]);
+ l = strlen(Info);
+ s = a;
+ if(n < l+2 || strncmp(Info, s, l) != 0)
+ error(Ebadctl);
+ if(n > 1024)
+ n = 1024;
+ b = smalloc(n);
+ memmove(b, s+l, n-l);
+ b[n-l] = 0;
+ if(b[n-l-1] == '\n')
+ b[n-l-1] = 0;
+ qlock(ep);
+ free(ep->info);
+ ep->info = b;
+ qunlock(ep);
+ break;
+ case CMaddress:
+ deprint("usb epctl %s\n", cb->f[0]);
+ ep->dev->state = Denabled;
+ break;
+ case CMdetach:
+ if(ep->dev->isroot != 0)
+ error("can't detach a root hub");
+ deprint("usb epctl %s ep%d.%d\n",
+ cb->f[0], ep->dev->nb, ep->nb);
+ ep->dev->state = Ddetach;
+ /* Release file system ref. for its endpoints */
+ for(i = 0; i < nelem(ep->dev->eps); i++)
+ putep(ep->dev->eps[i]);
+ break;
+ case CMdebugep:
+ if(strcmp(cb->f[1], "on") == 0)
+ ep->debug = 1;
+ else if(strcmp(cb->f[1], "off") == 0)
+ ep->debug = 0;
+ else
+ ep->debug = strtoul(cb->f[1], nil, 0);
+ print("usb: ep%d.%d debug %d\n",
+ ep->dev->nb, ep->nb, ep->debug);
+ break;
+ case CMname:
+ deprint("usb epctl %s %s\n", cb->f[0], cb->f[1]);
+ validname(cb->f[1], 0);
+ kstrdup(&ep->name, cb->f[1]);
+ break;
+ case CMtmout:
+ deprint("usb epctl %s\n", cb->f[0]);
+ if(ep->ttype == Tiso || ep->ttype == Tctl)
+ error("ctl ignored for this endpoint type");
+ ep->tmout = strtoul(cb->f[1], nil, 0);
+ if(ep->tmout != 0 && ep->tmout < Xfertmout)
+ ep->tmout = Xfertmout;
+ break;
+ case CMpreset:
+ deprint("usb epctl %s\n", cb->f[0]);
+ if(ep->ttype != Tctl)
+ error("not a control endpoint");
+ if(ep->dev->state != Denabled)
+ error("forbidden on devices not enabled");
+ ep->dev->state = Dreset;
+ break;
+ default:
+ panic("usb: unknown epctl %d", ct->index);
+ }
+ free(cb);
+ poperror();
+ return n;
+}
+
+static long
+usbctl(void *a, long n)
+{
+ Cmdtab *ct;
+ Cmdbuf *cb;
+ Ep *ep;
+ int i;
+
+ cb = parsecmd(a, n);
+ if(waserror()){
+ free(cb);
+ nexterror();
+ }
+ ct = lookupcmd(cb, usbctls, nelem(usbctls));
+ dprint("usb ctl %s\n", cb->f[0]);
+ switch(ct->index){
+ case CMdebug:
+ if(strcmp(cb->f[1], "on") == 0)
+ debug = 1;
+ else if(strcmp(cb->f[1], "off") == 0)
+ debug = 0;
+ else
+ debug = strtol(cb->f[1], nil, 0);
+ print("usb: debug %d\n", debug);
+ for(i = 0; i < epmax; i++)
+ if((ep = getep(i)) != nil){
+ ep->hp->debug(ep->hp, debug);
+ putep(ep);
+ }
+ break;
+ case CMdump:
+ dumpeps();
+ break;
+ }
+ free(cb);
+ poperror();
+ return n;
+}
+
+static long
+ctlwrite(Chan *c, void *a, long n)
+{
+ int q;
+ Ep *ep;
+
+ q = QID(c->qid);
+ if(q == Qctl)
+ return usbctl(a, n);
+
+ ep = getep(qid2epidx(q));
+ if(ep == nil)
+ error(Eio);
+ if(waserror()){
+ putep(ep);
+ nexterror();
+ }
+ if(ep->dev->state == Ddetach)
+ error(Edetach);
+ if(isqtype(q, Qepctl) && c->aux != nil){
+ /* Be sure we don't keep a cloned ep name */
+ free(c->aux);
+ c->aux = nil;
+ error("read, not write, expected");
+ }
+ n = epctl(ep, c, a, n);
+ putep(ep);
+ poperror();
+ return n;
+}
+
+static long
+usbwrite(Chan *c, void *a, long n, vlong off)
+{
+ int nr, q;
+ Ep *ep;
+
+ if(c->qid.type == QTDIR)
+ error(Eisdir);
+
+ q = QID(c->qid);
+
+ if(q == Qctl || isqtype(q, Qepctl))
+ return ctlwrite(c, a, n);
+
+ ep = getep(qid2epidx(q));
+ if(ep == nil)
+ error(Eio);
+ if(waserror()){
+ putep(ep);
+ nexterror();
+ }
+ if(ep->dev->state == Ddetach)
+ error(Edetach);
+ if(ep->mode == OREAD || ep->inuse == 0)
+ error(Ebadusefd);
+
+ switch(ep->ttype){
+ case Tnone:
+ error("endpoint not configured");
+ case Tctl:
+ nr = rhubwrite(ep, a, n);
+ if(nr >= 0){
+ n = nr;
+ break;
+ }
+ /* else fall */
+ default:
+ ddeprint("\nusbwrite q %#x fid %d cnt %ld off %lld\n",q, c->fid, n, off);
+ ep->hp->epwrite(ep, a, n);
+ }
+ putep(ep);
+ poperror();
+ return n;
+}
+
+void
+usbshutdown(void)
+{
+ Hci *hp;
+ int i;
+
+ for(i = 0; i < Nhcis; i++){
+ hp = hcis[i];
+ if(hp == nil)
+ continue;
+ if(hp->shutdown == nil)
+ print("#u: no shutdown function for %s\n", hp->type);
+ else
+ hp->shutdown(hp);
+ }
+}
+
+Dev usbdevtab = {
+ L'u',
+ "usb",
+
+ usbreset,
+ usbinit,
+ usbshutdown,
+ usbattach,
+ usbwalk,
+ usbstat,
+ usbopen,
+ devcreate,
+ usbclose,
+ usbread,
+ devbread,
+ usbwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
diff --git a/sys/src/9/kw/ether1116.c b/sys/src/9/kw/ether1116.c
new file mode 100755
index 000000000..b60c70a49
--- /dev/null
+++ b/sys/src/9/kw/ether1116.c
@@ -0,0 +1,1747 @@
+/*
+ * marvell kirkwood gigabit ethernet (88e1116 and 88e1121) driver
+ * (as found in the sheevaplug, openrd and guruplug).
+ * the main difference is the flavour of phy kludgery necessary.
+ *
+ * from /public/doc/marvell/88f61xx.kirkwood.pdf,
+ * /public/doc/marvell/88e1116.pdf, and
+ * /public/doc/marvell/88e1121r.pdf.
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+#include "ethermii.h"
+#include "../ip/ip.h"
+
+#define MIIDBG if(0)iprint
+
+#define WINATTR(v) (((v) & MASK(8)) << 8)
+#define WINSIZE(v) (((v)/(64*1024) - 1) << 16)
+
+enum {
+ Nrx = 512,
+ Ntx = 32,
+ Nrxblks = 1024,
+ Rxblklen = 2+1522, /* ifc. supplies first 2 bytes as padding */
+
+ Maxrxintrsec = 20*1000, /* max. rx intrs. / sec */
+ Etherstuck = 70, /* must send or receive a packet in this many sec.s */
+
+ Descralign = 16,
+ Bufalign = 8,
+
+ Pass = 1, /* accept packets */
+
+ Qno = 0, /* do everything on queue zero */
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Gbereg Gbereg;
+typedef struct Mibstats Mibstats;
+typedef struct Rx Rx;
+typedef struct Tx Tx;
+
+static struct {
+ Lock;
+ Block *head;
+} freeblocks;
+
+/* hardware receive buffer descriptor */
+struct Rx {
+ ulong cs;
+ ulong countsize; /* bytes, buffer size */
+ ulong buf; /* phys. addr. of packet buffer */
+ ulong next; /* phys. addr. of next Rx */
+};
+
+/* hardware transmit buffer descriptor */
+struct Tx {
+ ulong cs;
+ ulong countchk; /* bytes, checksum */
+ ulong buf; /* phys. addr. of packet buffer */
+ ulong next; /* phys. addr. of next Tx */
+};
+
+/* fixed by hw; part of Gberegs */
+struct Mibstats {
+ union {
+ uvlong rxby; /* good bytes rcv'd */
+ struct {
+ ulong rxbylo;
+ ulong rxbyhi;
+ };
+ };
+ ulong badrxby; /* bad bytes rcv'd */
+ ulong mactxerr; /* tx err pkts */
+ ulong rxpkt; /* good pkts rcv'd */
+ ulong badrxpkt; /* bad pkts rcv'd */
+ ulong rxbcastpkt; /* b'cast pkts rcv'd */
+ ulong rxmcastpkt; /* m'cast pkts rcv'd */
+
+ ulong rx64; /* pkts <= 64 bytes */
+ ulong rx65_127; /* pkts 65—127 bytes */
+ ulong rx128_255; /* pkts 128—255 bytes */
+ ulong rx256_511; /* pkts 256—511 bytes */
+ ulong rx512_1023; /* pkts 512—1023 bytes */
+ ulong rx1024_max; /* pkts >= 1024 bytes */
+
+ union {
+ uvlong txby; /* good bytes sent */
+ struct {
+ ulong txbylo;
+ ulong txbyhi;
+ };
+ };
+ ulong txpkt; /* good pkts sent */
+ /* half-duplex: pkts dropped due to excessive collisions */
+ ulong txcollpktdrop;
+ ulong txmcastpkt; /* m'cast pkts sent */
+ ulong txbcastpkt; /* b'cast pkts sent */
+
+ ulong badmacctlpkts; /* bad mac ctl pkts */
+ ulong txflctl; /* flow-control pkts sent */
+ ulong rxflctl; /* good flow-control pkts rcv'd */
+ ulong badrxflctl; /* bad flow-control pkts rcv'd */
+
+ ulong rxundersized; /* runts */
+ ulong rxfrags; /* fragments rcv'd */
+ ulong rxtoobig; /* oversized pkts rcv'd */
+ ulong rxjabber; /* jabber pkts rcv'd */
+ ulong rxerr; /* rx error events */
+ ulong crcerr; /* crc error events */
+ ulong collisions; /* collision events */
+ ulong latecoll; /* late collisions */
+};
+
+struct Ctlr {
+ Lock;
+ Ether *ether;
+ Gbereg *reg;
+
+ Lock initlock;
+ int init;
+
+ Rx *rx; /* receive descriptors */
+ Block *rxb[Nrx]; /* blocks belonging to the descriptors */
+ int rxhead; /* descr ethernet will write to next */
+ int rxtail; /* next descr that might need a buffer */
+ Rendez rrendez; /* interrupt wakes up read process */
+ int haveinput;
+
+ Tx *tx;
+ Block *txb[Ntx];
+ int txhead; /* next descr we can use for new packet */
+ int txtail; /* next descr to reclaim on tx complete */
+
+ Mii *mii;
+ int port;
+
+ /* stats */
+ ulong intrs;
+ ulong newintrs;
+ ulong txunderrun;
+ ulong txringfull;
+ ulong rxdiscard;
+ ulong rxoverrun;
+ ulong nofirstlast;
+
+ Mibstats;
+};
+
+#define Rxqon(q) (1<<(q))
+#define Txqon(q) (1<<(q))
+
+enum {
+ /* euc bits */
+ Portreset = 1 << 20,
+
+ /* sdma config, sdc bits */
+ Burst1 = 0,
+ Burst2,
+ Burst4,
+ Burst8,
+ Burst16,
+ SDCrifb = 1<<0, /* rx intr on pkt boundaries */
+#define SDCrxburst(v) ((v)<<1)
+ SDCrxnobyteswap = 1<<4,
+ SDCtxnobyteswap = 1<<5,
+ SDCswap64byte = 1<<6,
+#define SDCtxburst(v) ((v)<<22)
+ /* rx intr ipg (inter packet gap) */
+#define SDCipgintrx(v) ((((v)>>15) & 1)<<25) | (((v) & MASK(15))<<7)
+
+ /* portcfg bits */
+ PCFGupromisc = 1<<0, /* unicast promiscuous mode */
+#define Rxqdefault(q) ((q)<<1)
+#define Rxqarp(q) ((q)<<4)
+ PCFGbcrejectnoiparp = 1<<7,
+ PCFGbcrejectip = 1<<8,
+ PCFGbcrejectarp = 1<<9,
+ PCFGamnotxes = 1<<12, /* auto mode, no summary update on tx */
+ PCFGtcpq = 1<<14, /* capture tcp frames to tcpq */
+ PCFGudpq = 1<<15, /* capture udp frames to udpq */
+#define Rxqtcp(q) ((q)<<16)
+#define Rxqudp(q) ((q)<<19)
+#define Rxqbpdu(q) ((q)<<22)
+ PCFGrxcs = 1<<25, /* rx tcp checksum mode with header */
+
+ /* portcfgx bits */
+ PCFGXspanq = 1<<1,
+ PCFGXcrcoff = 1<<2, /* no ethernet crc */
+
+ /* port serial control0, psc0 bits */
+ PSC0porton = 1<<0,
+ PSC0forcelinkup = 1<<1,
+ PSC0an_dplxoff = 1<<2, /* an_ = auto. negotiate */
+ PSC0an_flctloff = 1<<3,
+ PSC0an_pauseadv = 1<<4,
+ PSC0nofrclinkdown = 1<<10,
+ PSC0an_spdoff = 1<<13,
+ PSC0dteadv = 1<<14, /* dte advertise */
+
+ /* max. input pkt size */
+#define PSC0mru(v) ((v)<<17)
+ PSC0mrumask = PSC0mru(MASK(3)),
+ PSC0mru1518 = 0, /* 1500+2* 6(addrs) +2 + 4(crc) */
+ PSC0mru1522, /* 1518 + 4(vlan tags) */
+ PSC0mru1552, /* `baby giant' */
+ PSC0mru9022, /* `jumbo' */
+ PSC0mru9192, /* bigger jumbo */
+ PSC0mru9700, /* still bigger jumbo */
+
+ PSC0fd_frc = 1<<21, /* force full duplex */
+ PSC0flctlfrc = 1<<22,
+ PSC0gmiispd_gbfrc = 1<<23,
+ PSC0miispdfrc100mbps = 1<<24,
+
+ /* port status 0, ps0 bits */
+ PS0linkup = 1<<1,
+ PS0fd = 1<<2, /* full duplex */
+ PS0flctl = 1<<3,
+ PS0gmii_gb = 1<<4,
+ PS0mii100mbps = 1<<5,
+ PS0txbusy = 1<<7,
+ PS0txfifoempty = 1<<10,
+ PS0rxfifo1empty = 1<<11,
+ PS0rxfifo2empty = 1<<12,
+
+ /* port serial control 1, psc1 bits */
+ PSC1loopback = 1<<1,
+ PSC1mii = 0<<2,
+ PSC1rgmii = 1<<3, /* enable RGMII */
+ PSC1portreset = 1<<4,
+ PSC1clockbypass = 1<<5,
+ PSC1iban = 1<<6,
+ PSC1iban_bypass = 1<<7,
+ PSC1iban_restart= 1<<8,
+ PSC1_gbonly = 1<<11,
+ PSC1encolonbp = 1<<15, /* "collision during back-pressure mib counting" */
+ PSC1coldomlimmask= MASK(6)<<16,
+#define PSC1coldomlim(v) (((v) & MASK(6))<<16)
+ PSC1miiallowoddpreamble = 1<<22,
+
+ /* port status 1, ps1 bits */
+ PS1rxpause = 1<<0,
+ PS1txpause = 1<<1,
+ PS1pressure = 1<<2,
+ PS1syncfail10ms = 1<<3,
+ PS1an_done = 1<<4,
+ PS1inbandan_bypassed = 1<<5,
+ PS1serdesplllocked = 1<<6,
+ PS1syncok = 1<<7,
+ PS1nosquelch = 1<<8,
+
+ /* irq bits */
+ /* rx buf returned to cpu ownership, or frame reception finished */
+ Irx = 1<<0,
+ Iextend = 1<<1, /* IEsum of irqe set */
+#define Irxbufferq(q) (1<<((q)+2)) /* rx buf returned to cpu ownership */
+ Irxerr = 1<<10, /* input ring full, usually */
+#define Irxerrq(q) (1<<((q)+11))
+#define Itxendq(q) (1<<((q)+19)) /* tx dma stopped for q */
+ Isum = 1<<31,
+
+ /* irq extended, irqe bits */
+#define IEtxbufferq(q) (1<<((q)+0)) /* tx buf returned to cpu ownership */
+#define IEtxerrq(q) (1<<((q)+8))
+ IEphystschg = 1<<16,
+ IEptp = 1<<17,
+ IErxoverrun = 1<<18,
+ IEtxunderrun = 1<<19,
+ IElinkchg = 1<<20,
+ IEintaddrerr = 1<<23,
+ IEprbserr = 1<<25,
+ IEsum = 1<<31,
+
+ /* tx fifo urgent threshold (tx interrupt coalescing), pxtfut */
+#define TFUTipginttx(v) (((v) & MASK(16))<<4);
+
+ /* minimal frame size, mfs */
+ MFS40by = 10<<2,
+ MFS44by = 11<<2,
+ MFS48by = 12<<2,
+ MFS52by = 13<<2,
+ MFS56by = 14<<2,
+ MFS60by = 15<<2,
+ MFS64by = 16<<2,
+
+ /* receive descriptor status */
+ RCSmacerr = 1<<0,
+ RCSmacmask = 3<<1,
+ RCSmacce = 0<<1,
+ RCSmacor = 1<<1,
+ RCSmacmf = 2<<1,
+ RCSl4chkshift = 3,
+ RCSl4chkmask = MASK(16),
+ RCSvlan = 1<<17,
+ RCSbpdu = 1<<18,
+ RCSl4mask = 3<<21,
+ RCSl4tcp4 = 0<<21,
+ RCSl4udp4 = 1<<21,
+ RCSl4other = 2<<21,
+ RCSl4rsvd = 3<<21,
+ RCSl2ev2 = 1<<23,
+ RCSl3ip4 = 1<<24,
+ RCSip4headok = 1<<25,
+ RCSlast = 1<<26,
+ RCSfirst = 1<<27,
+ RCSunknownaddr = 1<<28,
+ RCSenableintr = 1<<29,
+ RCSl4chkok = 1<<30,
+ RCSdmaown = 1<<31,
+
+ /* transmit descriptor status */
+ TCSmacerr = 1<<0,
+ TCSmacmask = 3<<1,
+ TCSmaclc = 0<<1,
+ TCSmacur = 1<<1,
+ TCSmacrl = 2<<1,
+ TCSllc = 1<<9,
+ TCSl4chkmode = 1<<10,
+ TCSipv4hdlenshift= 11,
+ TCSvlan = 1<<15,
+ TCSl4type = 1<<16,
+ TCSgl4chk = 1<<17,
+ TCSgip4chk = 1<<18,
+ TCSpadding = 1<<19,
+ TCSlast = 1<<20,
+ TCSfirst = 1<<21,
+ TCSenableintr = 1<<23,
+ TCSautomode = 1<<30,
+ TCSdmaown = 1<<31,
+};
+
+enum {
+ /* SMI regs */
+ PhysmiTimeout = 10000, /* what units? in ms. */
+ Physmidataoff = 0, /* Data */
+ Physmidatamask = 0xffff<<Physmidataoff,
+
+ Physmiaddroff = 16, /* PHY device addr */
+ Physmiaddrmask = 0x1f << Physmiaddroff,
+
+ Physmiop = 26,
+ Physmiopmask = 3<<Physmiop,
+ PhysmiopWr = 0<<Physmiop,
+ PhysmiopRd = 1<<Physmiop,
+
+ PhysmiReadok = 1<<27,
+ PhysmiBusy = 1<<28,
+
+ SmiRegaddroff = 21, /* PHY device register addr */
+ SmiRegaddrmask = 0x1f << SmiRegaddroff,
+};
+
+struct Gbereg {
+ ulong phy; /* PHY address */
+ ulong smi; /* serial mgmt. interface */
+ ulong euda; /* ether default address */
+ ulong eudid; /* ether default id */
+ uchar _pad0[0x80-0x10];
+
+ /* dma stuff */
+ ulong euirq; /* interrupt cause */
+ ulong euirqmask; /* interrupt mask */
+ uchar _pad1[0x94-0x88];
+ ulong euea; /* error address */
+ ulong euiae; /* internal error address */
+ uchar _pad2[0xb0-0x9c];
+ ulong euc; /* control */
+ uchar _pad3[0x200-0xb4];
+ struct {
+ ulong base; /* window base */
+ ulong size; /* window size */
+ } base[6];
+ uchar _pad4[0x280-0x230];
+ ulong harr[4]; /* high address remap */
+ ulong bare; /* base address enable */
+ ulong epap; /* port access protect */
+ uchar _pad5[0x400-0x298];
+
+ ulong portcfg; /* port configuration */
+ ulong portcfgx; /* port config. extend */
+ ulong mii; /* mii serial parameters */
+ ulong _pad6;
+ ulong evlane; /* vlan ether type */
+ ulong macal; /* mac address low */
+ ulong macah; /* mac address high */
+ ulong sdc; /* sdma config. */
+ ulong dscp[7]; /* ip diff. serv. code point -> pri */
+ ulong psc0; /* port serial control 0 */
+ ulong vpt2p; /* vlan priority tag -> pri */
+ ulong ps0; /* ether port status 0 */
+ ulong tqc; /* transmit queue command */
+ ulong psc1; /* port serial control 1 */
+ ulong ps1; /* ether port status 1 */
+ ulong mvhdr; /* marvell header */
+ ulong _pad8[2];
+
+ /* interrupts */
+ ulong irq; /* interrupt cause; some rw0c bits */
+ ulong irqe; /* " " extended; some rw0c bits */
+ ulong irqmask; /* interrupt mask (actually enable) */
+ ulong irqemask; /* " " extended */
+
+ ulong _pad9;
+ ulong pxtfut; /* port tx fifo urgent threshold */
+ ulong _pad10;
+ ulong pxmfs; /* port rx minimum frame size */
+ ulong _pad11;
+
+ /*
+ * # of input frames discarded by addr filtering or lack of resources;
+ * zeroed upon read.
+ */
+ ulong pxdfc; /* port rx discard frame counter */
+ ulong pxofc; /* port overrun frame counter */
+ ulong _pad12[2];
+ ulong piae; /* port internal address error */
+ uchar _pad13[0x4bc-0x498];
+ ulong etherprio; /* ether type priority */
+ uchar _pad14[0x4dc-0x4c0];
+ ulong tqfpc; /* tx queue fixed priority config. */
+ ulong pttbrc; /* port tx token-bucket rate config. */
+ ulong tqc1; /* tx queue command 1 */
+ ulong pmtu; /* port maximum transmit unit */
+ ulong pmtbs; /* port maximum token bucket size */
+ uchar _pad15[0x600-0x4f0];
+
+ struct {
+ ulong _pad[3];
+ ulong r; /* phys. addr.: cur. rx desc. ptrs */
+ } crdp[8];
+ ulong rqc; /* rx queue command */
+ ulong tcsdp; /* phys. addr.: cur. tx desc. ptr */
+ uchar _pad16[0x6c0-0x688];
+
+ ulong tcqdp[8]; /* phys. addr.: cur. tx q. desc. ptr */
+ uchar _pad17[0x700-0x6e0];
+
+ struct {
+ ulong tbctr; /* queue tx token-bucket counter */
+ ulong tbcfg; /* tx queue token-bucket config. */
+ ulong acfg; /* tx queue arbiter config. */
+ ulong _pad;
+ } tq[8];
+ ulong pttbc; /* port tx token-bucket counter */
+ uchar _pad18[0x7a8-0x784];
+
+ ulong ipg2; /* tx queue ipg */
+ ulong _pad19[3];
+ ulong ipg3;
+ ulong _pad20;
+ ulong htlp; /* high token in low packet */
+ ulong htap; /* high token in async packet */
+ ulong ltap; /* low token in async packet */
+ ulong _pad21;
+ ulong ts; /* tx speed */
+ uchar _pad22[0x1000-0x7d4];
+
+ /* mac mib counters: statistics */
+ Mibstats;
+ uchar _pad23[0x1400-0x1080];
+
+ /* multicast filtering; each byte: Qno<<1 | Pass */
+ ulong dfsmt[64]; /* dest addr filter special m'cast table */
+ ulong dfomt[64]; /* dest addr filter other m'cast table */
+ /* unicast filtering */
+ ulong dfut[4]; /* dest addr filter unicast table */
+};
+
+static Ctlr *ctlrs[MaxEther];
+static uchar zeroea[Eaddrlen];
+
+static void getmibstats(Ctlr *);
+
+static void
+rxfreeb(Block *b)
+{
+ /* freeb(b) will have previously decremented b->ref to 0; raise to 1 */
+ _xinc(&b->ref);
+ b->wp = b->rp =
+ (uchar*)((uintptr)(b->lim - Rxblklen) & ~(Bufalign - 1));
+ assert(((uintptr)b->rp & (Bufalign - 1)) == 0);
+ b->free = rxfreeb;
+
+ ilock(&freeblocks);
+ b->next = freeblocks.head;
+ freeblocks.head = b;
+ iunlock(&freeblocks);
+}
+
+static Block *
+rxallocb(void)
+{
+ Block *b;
+
+ ilock(&freeblocks);
+ b = freeblocks.head;
+ if(b != nil) {
+ freeblocks.head = b->next;
+ b->next = nil;
+ b->free = rxfreeb;
+ }
+ iunlock(&freeblocks);
+ return b;
+}
+
+static void
+rxkick(Ctlr *ctlr)
+{
+ Gbereg *reg = ctlr->reg;
+
+ if (reg->crdp[Qno].r == 0)
+ reg->crdp[Qno].r = PADDR(&ctlr->rx[ctlr->rxhead]);
+ if ((reg->rqc & 0xff) == 0) /* all queues are stopped? */
+ reg->rqc = Rxqon(Qno); /* restart */
+ coherence();
+}
+
+static void
+txkick(Ctlr *ctlr)
+{
+ Gbereg *reg = ctlr->reg;
+
+ if (reg->tcqdp[Qno] == 0)
+ reg->tcqdp[Qno] = PADDR(&ctlr->tx[ctlr->txhead]);
+ if ((reg->tqc & 0xff) == 0) /* all q's stopped? */
+ reg->tqc = Txqon(Qno); /* restart */
+ coherence();
+}
+
+static void
+rxreplenish(Ctlr *ctlr)
+{
+ Rx *r;
+ Block *b;
+
+ while(ctlr->rxb[ctlr->rxtail] == nil) {
+ b = rxallocb();
+ if(b == nil) {
+ iprint("#l%d: rxreplenish out of buffers\n",
+ ctlr->ether->ctlrno);
+ break;
+ }
+
+ ctlr->rxb[ctlr->rxtail] = b;
+
+ /* set up uncached receive descriptor */
+ r = &ctlr->rx[ctlr->rxtail];
+ assert(((uintptr)r & (Descralign - 1)) == 0);
+ r->countsize = ROUNDUP(Rxblklen, 8);
+ r->buf = PADDR(b->rp);
+ coherence();
+
+ /* and fire */
+ r->cs = RCSdmaown | RCSenableintr;
+ coherence();
+
+ ctlr->rxtail = NEXT(ctlr->rxtail, Nrx);
+ }
+}
+
+static void
+dump(uchar *bp, long max)
+{
+ if (max > 64)
+ max = 64;
+ for (; max > 0; max--, bp++)
+ iprint("%02.2ux ", *bp);
+ print("...\n");
+}
+
+static void
+etheractive(Ether *ether)
+{
+ ether->starttime = TK2MS(MACHP(0)->ticks)/1000;
+}
+
+static void
+ethercheck(Ether *ether)
+{
+ if (ether->starttime != 0 &&
+ TK2MS(MACHP(0)->ticks)/1000 - ether->starttime > Etherstuck) {
+ etheractive(ether);
+ if (ether->ctlrno == 0) /* only complain about main ether */
+ iprint("#l%d: ethernet stuck\n", ether->ctlrno);
+ }
+}
+
+static void
+receive(Ether *ether)
+{
+ int i;
+ ulong n;
+ Block *b;
+ Ctlr *ctlr = ether->ctlr;
+ Rx *r;
+
+ ethercheck(ether);
+ for (i = Nrx-2; i > 0; i--) {
+ r = &ctlr->rx[ctlr->rxhead]; /* *r is uncached */
+ assert(((uintptr)r & (Descralign - 1)) == 0);
+ if(r->cs & RCSdmaown) /* descriptor busy? */
+ break;
+
+ b = ctlr->rxb[ctlr->rxhead]; /* got input buffer? */
+ if (b == nil)
+ panic("ether1116: nil ctlr->rxb[ctlr->rxhead] "
+ "in receive");
+ ctlr->rxb[ctlr->rxhead] = nil;
+ ctlr->rxhead = NEXT(ctlr->rxhead, Nrx);
+
+ if((r->cs & (RCSfirst|RCSlast)) != (RCSfirst|RCSlast)) {
+ ctlr->nofirstlast++; /* partial packet */
+ freeb(b);
+ continue;
+ }
+ if(r->cs & RCSmacerr) {
+ freeb(b);
+ continue;
+ }
+
+ n = r->countsize >> 16; /* TODO includes 2 pad bytes? */
+ assert(n >= 2 && n < 2048);
+
+ /* clear any cached packet or part thereof */
+ l2cacheuinvse(b->rp, n+2);
+ cachedinvse(b->rp, n+2);
+ b->wp = b->rp + n;
+ /*
+ * skip hardware padding intended to align ipv4 address
+ * in memory (mv-s104860-u0 §8.3.4.1)
+ */
+ b->rp += 2;
+ etheriq(ether, b, 1);
+ etheractive(ether);
+ if (i % (Nrx / 2) == 0) {
+ rxreplenish(ctlr);
+ rxkick(ctlr);
+ }
+ }
+ rxreplenish(ctlr);
+ rxkick(ctlr);
+}
+
+static void
+txreplenish(Ether *ether) /* free transmitted packets */
+{
+ Ctlr *ctlr;
+
+ ctlr = ether->ctlr;
+ while(ctlr->txtail != ctlr->txhead) {
+ /* ctlr->tx is uncached */
+ if(ctlr->tx[ctlr->txtail].cs & TCSdmaown)
+ break;
+ if(ctlr->txb[ctlr->txtail] == nil)
+ panic("no block for sent packet?!");
+ freeb(ctlr->txb[ctlr->txtail]);
+ ctlr->txb[ctlr->txtail] = nil;
+
+ ctlr->txtail = NEXT(ctlr->txtail, Ntx);
+ etheractive(ether);
+ }
+}
+
+/*
+ * transmit strategy: fill the output ring as far as possible,
+ * perhaps leaving a few spare; kick off the output and take
+ * an interrupt only when the transmit queue is empty.
+ */
+static void
+transmit(Ether *ether)
+{
+ int i, kick, len;
+ Block *b;
+ Ctlr *ctlr = ether->ctlr;
+ Gbereg *reg = ctlr->reg;
+ Tx *t;
+
+ ethercheck(ether);
+ ilock(ctlr);
+ txreplenish(ether); /* reap old packets */
+
+ /* queue new packets; use at most half the tx descs to avoid livelock */
+ kick = 0;
+ for (i = Ntx/2 - 2; i > 0; i--) {
+ t = &ctlr->tx[ctlr->txhead]; /* *t is uncached */
+ assert(((uintptr)t & (Descralign - 1)) == 0);
+ if(t->cs & TCSdmaown) { /* descriptor busy? */
+ ctlr->txringfull++;
+ break;
+ }
+
+ b = qget(ether->oq); /* outgoing packet? */
+ if (b == nil)
+ break;
+ len = BLEN(b);
+ if(len < ether->minmtu || len > ether->maxmtu) {
+ freeb(b);
+ continue;
+ }
+ ctlr->txb[ctlr->txhead] = b;
+
+ /* make sure the whole packet is in memory */
+ cachedwbse(b->rp, len);
+ l2cacheuwbse(b->rp, len);
+
+ /* set up the transmit descriptor */
+ t->buf = PADDR(b->rp);
+ t->countchk = len << 16;
+ coherence();
+
+ /* and fire */
+ t->cs = TCSpadding | TCSfirst | TCSlast | TCSdmaown |
+ TCSenableintr;
+ coherence();
+
+ kick++;
+ ctlr->txhead = NEXT(ctlr->txhead, Ntx);
+ }
+ if (kick) {
+ txkick(ctlr);
+
+ reg->irqmask |= Itxendq(Qno);
+ reg->irqemask |= IEtxerrq(Qno) | IEtxunderrun;
+ }
+ iunlock(ctlr);
+}
+
+static void
+dumprxdescs(Ctlr *ctlr)
+{
+ int i;
+ Gbereg *reg = ctlr->reg;
+
+ iprint("\nrxhead %d rxtail %d; txcdp %#p rxcdp %#p\n",
+ ctlr->rxhead, ctlr->rxtail, reg->tcqdp[Qno], reg->crdp[Qno].r);
+ for (i = 0; i < Nrx; i++) {
+ iprint("rxb %d @ %#p: %#p\n", i, &ctlr->rxb[i], ctlr->rxb[i]);
+ delay(50);
+ }
+ for (i = 0; i < Nrx; i++) {
+ iprint("rx %d @ %#p: cs %#lux countsize %lud buf %#lux next %#lux\n",
+ i, &ctlr->rx[i], ctlr->rx[i].cs,
+ ctlr->rx[i].countsize >> 3, ctlr->rx[i].buf,
+ ctlr->rx[i].next);
+ delay(50);
+ }
+ delay(1000);
+}
+
+static int
+gotinput(void* ctlr)
+{
+ return ((Ctlr*)ctlr)->haveinput != 0;
+}
+
+/*
+ * process any packets in the input ring.
+ * also sum mib stats frequently to avoid the overflow
+ * mentioned in the errata.
+ */
+static void
+rcvproc(void* arg)
+{
+ Ctlr *ctlr;
+ Ether *ether;
+
+ ether = arg;
+ ctlr = ether->ctlr;
+ for(;;){
+ tsleep(&ctlr->rrendez, gotinput, ctlr, 10*1000);
+ ilock(ctlr);
+ getmibstats(ctlr);
+ if (ctlr->haveinput) {
+ ctlr->haveinput = 0;
+ iunlock(ctlr);
+ receive(ether);
+ } else
+ iunlock(ctlr);
+ }
+}
+
+static void
+interrupt(Ureg*, void *arg)
+{
+ ulong irq, irqe, handled;
+ Ether *ether = arg;
+ Ctlr *ctlr = ether->ctlr;
+ Gbereg *reg = ctlr->reg;
+
+ handled = 0;
+ irq = reg->irq;
+ irqe = reg->irqe;
+ reg->irqe = 0; /* extinguish intr causes */
+ reg->irq = 0; /* extinguish intr causes */
+ ethercheck(ether);
+
+ if(irq & (Irx | Irxbufferq(Qno))) {
+ /*
+ * letting a kproc process the input takes far less real time
+ * than doing it all at interrupt level.
+ */
+ ctlr->haveinput = 1;
+ wakeup(&ctlr->rrendez);
+ irq &= ~(Irx | Irxbufferq(Qno));
+ handled++;
+ } else
+ rxkick(ctlr);
+
+ if(irq & Itxendq(Qno)) { /* transmit ring empty? */
+ reg->irqmask &= ~Itxendq(Qno); /* prevent more interrupts */
+ reg->irqemask &= ~(IEtxerrq(Qno) | IEtxunderrun);
+ transmit(ether);
+ irq &= ~Itxendq(Qno);
+ handled++;
+ }
+
+ if(irqe & IEsum) {
+ /*
+ * IElinkchg appears to only be set when unplugging.
+ * autonegotiation is likely not done yet, so linkup not valid,
+ * thus we note the link change here, and check for
+ * that and autonegotiation done below.
+ */
+ if(irqe & IEphystschg) {
+ ether->link = (reg->ps0 & PS0linkup) != 0;
+ ether->linkchg = 1;
+ }
+ if(irqe & IEtxerrq(Qno))
+ ether->oerrs++;
+ if(irqe & IErxoverrun)
+ ether->overflows++;
+ if(irqe & IEtxunderrun)
+ ctlr->txunderrun++;
+ if(irqe & (IEphystschg | IEtxerrq(Qno) | IErxoverrun |
+ IEtxunderrun))
+ handled++;
+ }
+ if (irq & Isum) {
+ if (irq & Irxerr) { /* nil desc. ptr. or desc. owned by cpu */
+ ether->buffs++; /* approx. error */
+
+ /* if the input ring is full, drain it */
+ ctlr->haveinput = 1;
+ wakeup(&ctlr->rrendez);
+ }
+ if(irq & (Irxerr | Irxerrq(Qno)))
+ handled++;
+ irq &= ~(Irxerr | Irxerrq(Qno));
+ }
+
+ if(ether->linkchg && (reg->ps1 & PS1an_done)) {
+ handled++;
+ ether->link = (reg->ps0 & PS0linkup) != 0;
+ ether->linkchg = 0;
+ }
+ ctlr->newintrs++;
+
+ if (!handled) {
+ irq &= ~Isum;
+ irqe &= ~IEtxbufferq(Qno);
+ if (irq == 0 && irqe == 0) {
+ /* seems to be triggered by continuous output */
+ // iprint("ether1116: spurious interrupt\n");
+ } else
+ iprint("ether1116: interrupt cause unknown; "
+ "irq %#lux irqe %#lux\n", irq, irqe);
+ }
+ intrclear(Irqlo, ether->irq);
+}
+
+void
+promiscuous(void *arg, int on)
+{
+ Ether *ether = arg;
+ Ctlr *ctlr = ether->ctlr;
+ Gbereg *reg = ctlr->reg;
+
+ ilock(ctlr);
+ ether->prom = on;
+ if(on)
+ reg->portcfg |= PCFGupromisc;
+ else
+ reg->portcfg &= ~PCFGupromisc;
+ iunlock(ctlr);
+}
+
+void
+multicast(void *, uchar *, int)
+{
+ /* nothing to do; we always accept multicast */
+}
+
+static void quiesce(Gbereg *reg);
+
+static void
+shutdown(Ether *ether)
+{
+ int i;
+ Ctlr *ctlr = ether->ctlr;
+ Gbereg *reg = ctlr->reg;
+
+ ilock(ctlr);
+ quiesce(reg);
+ reg->euc |= Portreset;
+ coherence();
+ iunlock(ctlr);
+ delay(100);
+ ilock(ctlr);
+ reg->euc &= ~Portreset;
+ coherence();
+ delay(20);
+
+ reg->psc0 = 0; /* no PSC0porton */
+ reg->psc1 |= PSC1portreset;
+ coherence();
+ delay(50);
+ reg->psc1 &= ~PSC1portreset;
+ coherence();
+
+ for (i = 0; i < nelem(reg->tcqdp); i++)
+ reg->tcqdp[i] = 0;
+ for (i = 0; i < nelem(reg->crdp); i++)
+ reg->crdp[i].r = 0;
+ coherence();
+
+ iunlock(ctlr);
+}
+
+enum {
+ CMjumbo,
+};
+
+static Cmdtab ctlmsg[] = {
+ CMjumbo, "jumbo", 2,
+};
+
+long
+ctl(Ether *e, void *p, long n)
+{
+ Cmdbuf *cb;
+ Cmdtab *ct;
+ Ctlr *ctlr = e->ctlr;
+ Gbereg *reg = ctlr->reg;
+
+ cb = parsecmd(p, n);
+ if(waserror()) {
+ free(cb);
+ nexterror();
+ }
+
+ ct = lookupcmd(cb, ctlmsg, nelem(ctlmsg));
+ switch(ct->index) {
+ case CMjumbo:
+ if(strcmp(cb->f[1], "on") == 0) {
+ /* incoming packet queue doesn't expect jumbo frames */
+ error("jumbo disabled");
+ reg->psc0 = (reg->psc0 & ~PSC0mrumask) |
+ PSC0mru(PSC0mru9022);
+ e->maxmtu = 9022;
+ } else if(strcmp(cb->f[1], "off") == 0) {
+ reg->psc0 = (reg->psc0 & ~PSC0mrumask) |
+ PSC0mru(PSC0mru1522);
+ e->maxmtu = ETHERMAXTU;
+ } else
+ error(Ebadctl);
+ break;
+ default:
+ error(Ebadctl);
+ break;
+ }
+ free(cb);
+ poperror();
+ return n;
+}
+
+/*
+ * phy/mii goo
+ */
+
+static int
+smibusywait(Gbereg *reg, ulong waitbit)
+{
+ ulong timeout, smi_reg;
+
+ timeout = PhysmiTimeout;
+ /* wait till the SMI is not busy */
+ do {
+ /* read smi register */
+ smi_reg = reg->smi;
+ if (timeout-- == 0) {
+ MIIDBG("SMI busy timeout\n");
+ return -1;
+ }
+// delay(1);
+ } while (smi_reg & waitbit);
+ return 0;
+}
+
+static int
+miird(Mii *mii, int pa, int ra)
+{
+ ulong smi_reg, timeout;
+ Gbereg *reg;
+
+ reg = ((Ctlr*)mii->ctlr)->reg;
+
+ /* check params */
+ if ((pa<<Physmiaddroff) & ~Physmiaddrmask ||
+ (ra<<SmiRegaddroff) & ~SmiRegaddrmask)
+ return -1;
+
+ smibusywait(reg, PhysmiBusy);
+
+ /* fill the phy address and register offset and read opcode */
+ reg->smi = pa << Physmiaddroff | ra << SmiRegaddroff | PhysmiopRd;
+ coherence();
+
+ /* wait til read value is ready */
+ timeout = PhysmiTimeout;
+ do {
+ smi_reg = reg->smi;
+ if (timeout-- == 0) {
+ MIIDBG("SMI read-valid timeout\n");
+ return -1;
+ }
+ } while (!(smi_reg & PhysmiReadok));
+
+ /* Wait for the data to update in the SMI register */
+ for (timeout = 0; timeout < PhysmiTimeout; timeout++)
+ ;
+ return reg->smi & Physmidatamask;
+}
+
+static int
+miiwr(Mii *mii, int pa, int ra, int v)
+{
+ Gbereg *reg;
+ ulong smi_reg;
+
+ reg = ((Ctlr*)mii->ctlr)->reg;
+
+ /* check params */
+ if (((pa<<Physmiaddroff) & ~Physmiaddrmask) ||
+ ((ra<<SmiRegaddroff) & ~SmiRegaddrmask))
+ return -1;
+
+ smibusywait(reg, PhysmiBusy);
+
+ /* fill the phy address and register offset and read opcode */
+ smi_reg = v << Physmidataoff | pa << Physmiaddroff | ra << SmiRegaddroff;
+ reg->smi = smi_reg & ~PhysmiopRd;
+ coherence();
+ return 0;
+}
+
+#define MIIMODEL(idr2) (((idr2) >> 4) & MASK(6))
+
+enum {
+ Hacknone,
+ Hackdual,
+
+ Ouimarvell = 0x005043,
+
+ /* idr2 mii/phy model numbers */
+ Phy1000 = 0x00, /* 88E1000 Gb */
+ Phy1011 = 0x02, /* 88E1011 Gb */
+ Phy1000_3 = 0x03, /* 88E1000 Gb */
+ Phy1000s = 0x04, /* 88E1000S Gb */
+ Phy1000_5 = 0x05, /* 88E1000 Gb */
+ Phy1000_6 = 0x06, /* 88E1000 Gb */
+ Phy3082 = 0x08, /* 88E3082 10/100 */
+ Phy1112 = 0x09, /* 88E1112 Gb */
+ Phy1121r = 0x0b, /* says the 1121r manual */
+ Phy1149 = 0x0b, /* 88E1149 Gb */
+ Phy1111 = 0x0c, /* 88E1111 Gb */
+ Phy1116 = 0x21, /* 88E1116 Gb */
+ Phy1116r = 0x24, /* 88E1116R Gb */
+ Phy1118 = 0x22, /* 88E1118 Gb */
+ Phy3016 = 0x26, /* 88E3016 10/100 */
+};
+
+static int hackflavour;
+
+/*
+ * on openrd, ether0's phy has address 8, ether1's is ether0's 24.
+ * on guruplug, ether0's is phy 0 and ether1's is ether0's phy 1.
+ */
+int
+mymii(Mii* mii, int mask)
+{
+ Ctlr *ctlr;
+ MiiPhy *miiphy;
+ int bit, ctlrno, oui, model, phyno, r, rmask;
+ static int dualport, phyidx;
+ static int phynos[NMiiPhy];
+
+ ctlr = mii->ctlr;
+ ctlrno = ctlr->ether->ctlrno;
+
+ /* first pass: figure out what kind of phy(s) we have. */
+ dualport = 0;
+ if (ctlrno == 0) {
+ for(phyno = 0; phyno < NMiiPhy; phyno++){
+ bit = 1<<phyno;
+ if(!(mask & bit) || mii->mask & bit)
+ continue;
+ if(mii->mir(mii, phyno, Bmsr) == -1)
+ continue;
+ r = mii->mir(mii, phyno, Phyidr1);
+ oui = (r & 0x3FFF)<<6;
+ r = mii->mir(mii, phyno, Phyidr2);
+ oui |= r>>10;
+ model = MIIMODEL(r);
+ if (oui == 0xfffff && model == 0x3f)
+ continue;
+ MIIDBG("ctlrno %d phy %d oui %#ux model %#ux\n",
+ ctlrno, phyno, oui, model);
+ if (oui == Ouimarvell &&
+ (model == Phy1121r || model == Phy1116r))
+ ++dualport;
+ phynos[phyidx++] = phyno;
+ }
+ hackflavour = dualport == 2 && phyidx == 2? Hackdual: Hacknone;
+ MIIDBG("ether1116: %s-port phy\n",
+ hackflavour == Hackdual? "dual": "single");
+ }
+
+ /*
+ * Probe through mii for PHYs in mask;
+ * return the mask of those found in the current probe.
+ * If the PHY has not already been probed, update
+ * the Mii information.
+ */
+ rmask = 0;
+ if (hackflavour == Hackdual && ctlrno < phyidx) {
+ /*
+ * openrd, guruplug or the like: use ether0's phys.
+ * this is a nasty hack, but so is the hardware.
+ */
+ MIIDBG("ctlrno %d using ctlrno 0's phyno %d\n",
+ ctlrno, phynos[ctlrno]);
+ ctlr->mii = mii = ctlrs[0]->mii;
+ mask = 1 << phynos[ctlrno];
+ mii->mask = ~mask;
+ }
+ for(phyno = 0; phyno < NMiiPhy; phyno++){
+ bit = 1<<phyno;
+ if(!(mask & bit))
+ continue;
+ if(mii->mask & bit){
+ rmask |= bit;
+ continue;
+ }
+ if(mii->mir(mii, phyno, Bmsr) == -1)
+ continue;
+ r = mii->mir(mii, phyno, Phyidr1);
+ oui = (r & 0x3FFF)<<6;
+ r = mii->mir(mii, phyno, Phyidr2);
+ oui |= r>>10;
+ if(oui == 0xFFFFF || oui == 0)
+ continue;
+
+ if((miiphy = malloc(sizeof(MiiPhy))) == nil)
+ continue;
+ miiphy->mii = mii;
+ miiphy->oui = oui;
+ miiphy->phyno = phyno;
+
+ miiphy->anar = ~0;
+ miiphy->fc = ~0;
+ miiphy->mscr = ~0;
+
+ mii->phy[phyno] = miiphy;
+ if(ctlrno == 0 || hackflavour != Hackdual && mii->curphy == nil)
+ mii->curphy = miiphy;
+ mii->mask |= bit;
+ mii->nphy++;
+
+ rmask |= bit;
+ }
+ return rmask;
+}
+
+static int
+kirkwoodmii(Ether *ether)
+{
+ int i;
+ Ctlr *ctlr;
+ MiiPhy *phy;
+
+ MIIDBG("mii\n");
+ ctlr = ether->ctlr;
+ if((ctlr->mii = malloc(sizeof(Mii))) == nil)
+ return -1;
+ ctlr->mii->ctlr = ctlr;
+ ctlr->mii->mir = miird;
+ ctlr->mii->miw = miiwr;
+
+ if(mymii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){
+ print("#l%d: ether1116: init mii failure\n", ether->ctlrno);
+ free(ctlr->mii);
+ ctlr->mii = nil;
+ return -1;
+ }
+
+ /* oui 005043 is marvell */
+ MIIDBG("oui %#X phyno %d\n", phy->oui, phy->phyno);
+ // TODO: does this make sense? shouldn't each phy be initialised?
+ if((ctlr->ether->ctlrno == 0 || hackflavour != Hackdual) &&
+ miistatus(ctlr->mii) < 0){
+ miireset(ctlr->mii);
+ MIIDBG("miireset\n");
+ if(miiane(ctlr->mii, ~0, 0, ~0) < 0){
+ iprint("miiane failed\n");
+ return -1;
+ }
+ MIIDBG("miistatus\n");
+ miistatus(ctlr->mii);
+ if(miird(ctlr->mii, phy->phyno, Bmsr) & BmsrLs){
+ for(i = 0; ; i++){
+ if(i > 600){
+ iprint("ether1116: autonegotiation failed\n");
+ break;
+ }
+ if(miird(ctlr->mii, phy->phyno, Bmsr) & BmsrAnc)
+ break;
+ delay(10);
+ }
+ if(miistatus(ctlr->mii) < 0)
+ iprint("miistatus failed\n");
+ }else{
+ iprint("ether1116: no link\n");
+ phy->speed = 10; /* simple default */
+ }
+ }
+
+ ether->mbps = phy->speed;
+ MIIDBG("#l%d: kirkwoodmii: fd %d speed %d tfc %d rfc %d\n",
+ ctlr->port, phy->fd, phy->speed, phy->tfc, phy->rfc);
+ MIIDBG("mii done\n");
+ return 0;
+}
+
+enum { /* PHY register pages */
+ Pagcopper,
+ Pagfiber,
+ Pagrgmii,
+ Pagled,
+ Pagrsvd1,
+ Pagvct,
+ Pagtest,
+ Pagrsvd2,
+ Pagfactest,
+};
+
+static void
+miiregpage(Mii *mii, ulong dev, ulong page)
+{
+ miiwr(mii, dev, Eadr, page);
+}
+
+static int
+miiphyinit(Mii *mii)
+{
+ ulong dev;
+ Ctlr *ctlr;
+ Gbereg *reg;
+
+ ctlr = (Ctlr*)mii->ctlr;
+ reg = ctlr->reg;
+ dev = reg->phy;
+ MIIDBG("phy dev addr %lux\n", dev);
+
+ /* leds link & activity */
+ miiregpage(mii, dev, Pagled);
+ /* low 4 bits == 1: on - link, blink - activity, off - no link */
+ miiwr(mii, dev, Scr, (miird(mii, dev, Scr) & ~0xf) | 1);
+
+ miiregpage(mii, dev, Pagrgmii);
+ miiwr(mii, dev, Scr, miird(mii, dev, Scr) | Rgmiipwrup);
+ /* must now do a software reset, says the manual */
+ miireset(ctlr->mii);
+
+ /* enable RGMII delay on Tx and Rx for CPU port */
+ miiwr(mii, dev, Recr, miird(mii, dev, Recr) | Rxtiming | Rxtiming);
+ /* must now do a software reset, says the manual */
+ miireset(ctlr->mii);
+
+ miiregpage(mii, dev, Pagcopper);
+ miiwr(mii, dev, Scr,
+ (miird(mii, dev, Scr) & ~(Pwrdown|Endetect)) | Mdix);
+
+ return 0;
+}
+
+/*
+ * initialisation
+ */
+
+static void
+quiesce(Gbereg *reg)
+{
+ ulong v;
+
+ v = reg->tqc;
+ if (v & 0xFF)
+ reg->tqc = v << 8; /* stop active channels */
+ v = reg->rqc;
+ if (v & 0xFF)
+ reg->rqc = v << 8; /* stop active channels */
+ /* wait for all queues to stop */
+ while (reg->tqc & 0xFF || reg->rqc & 0xFF)
+ ;
+}
+
+static void
+p16(uchar *p, ulong v) /* convert big-endian short to bytes */
+{
+ *p++ = v>>8;
+ *p = v;
+}
+
+static void
+p32(uchar *p, ulong v) /* convert big-endian long to bytes */
+{
+ *p++ = v>>24;
+ *p++ = v>>16;
+ *p++ = v>>8;
+ *p = v;
+}
+
+/*
+ * set ether->ea from hw mac address,
+ * configure unicast filtering to accept it.
+ */
+void
+archetheraddr(Ether *ether, Gbereg *reg, int rxqno)
+{
+ uchar *ea;
+ ulong nibble, ucreg, tbloff, regoff;
+
+ ea = ether->ea;
+ p32(ea, reg->macah);
+ p16(ea+4, reg->macal);
+ if (memcmp(ea, zeroea, sizeof zeroea) == 0 && ether->ctlrno > 0) {
+ /* hack: use ctlr[0]'s + ctlrno */
+ memmove(ea, ctlrs[0]->ether->ea, Eaddrlen);
+ ea[Eaddrlen-1] += ether->ctlrno;
+ reg->macah = ea[0] << 24 | ea[1] << 16 | ea[2] << 8 | ea[3];
+ reg->macal = ea[4] << 8 | ea[5];
+ coherence();
+ }
+
+ /* accept frames on ea */
+ nibble = ea[5] & 0xf;
+ tbloff = nibble / 4;
+ regoff = nibble % 4;
+
+ regoff *= 8;
+ ucreg = reg->dfut[tbloff] & (0xff << regoff);
+ ucreg |= (rxqno << 1 | Pass) << regoff;
+ reg->dfut[tbloff] = ucreg;
+
+ /* accept all multicast too. set up special & other tables. */
+ memset(reg->dfsmt, Qno<<1 | Pass, sizeof reg->dfsmt);
+ memset(reg->dfomt, Qno<<1 | Pass, sizeof reg->dfomt);
+ coherence();
+}
+
+static void
+cfgdramacc(Gbereg *reg)
+{
+ memset(reg->harr, 0, sizeof reg->harr);
+ memset(reg->base, 0, sizeof reg->base);
+
+ reg->bare = MASK(6) - MASK(2); /* disable wins 2-5 */
+ /* this doesn't make any sense, but it's required */
+ reg->epap = 3 << 2 | 3; /* full access for wins 0 & 1 */
+// reg->epap = 0; /* no access on access violation for all wins */
+ coherence();
+
+ reg->base[0].base = PHYSDRAM | WINATTR(Attrcs0) | Targdram;
+ reg->base[0].size = WINSIZE(256*MB);
+ reg->base[1].base = (PHYSDRAM + 256*MB) | WINATTR(Attrcs1) | Targdram;
+ reg->base[1].size = WINSIZE(256*MB);
+ coherence();
+}
+
+static void
+ctlralloc(Ctlr *ctlr)
+{
+ int i;
+ Block *b;
+ Rx *r;
+ Tx *t;
+
+ ilock(&freeblocks);
+ for(i = 0; i < Nrxblks; i++) {
+ b = iallocb(Rxblklen+Bufalign-1);
+ if(b == nil) {
+ iprint("ether1116: no memory for rx buffers\n");
+ break;
+ }
+ assert(b->ref == 1);
+ b->wp = b->rp = (uchar*)
+ ((uintptr)(b->lim - Rxblklen) & ~(Bufalign - 1));
+ assert(((uintptr)b->rp & (Bufalign - 1)) == 0);
+ b->free = rxfreeb;
+ b->next = freeblocks.head;
+ freeblocks.head = b;
+ }
+ iunlock(&freeblocks);
+
+ /*
+ * allocate uncached rx ring descriptors because rings are shared
+ * with the ethernet controller and more than one fits in a cache line.
+ */
+ ctlr->rx = ucallocalign(Nrx * sizeof(Rx), Descralign, 0);
+ if(ctlr->rx == nil)
+ panic("ether1116: no memory for rx ring");
+ for(i = 0; i < Nrx; i++) {
+ r = &ctlr->rx[i];
+ assert(((uintptr)r & (Descralign - 1)) == 0);
+ r->cs = 0; /* owned by software until r->buf is non-nil */
+ r->buf = 0;
+ r->next = PADDR(&ctlr->rx[NEXT(i, Nrx)]);
+ ctlr->rxb[i] = nil;
+ }
+ ctlr->rxtail = ctlr->rxhead = 0;
+ rxreplenish(ctlr);
+
+ /* allocate uncached tx ring descriptors */
+ ctlr->tx = ucallocalign(Ntx * sizeof(Tx), Descralign, 0);
+ if(ctlr->tx == nil)
+ panic("ether1116: no memory for tx ring");
+ for(i = 0; i < Ntx; i++) {
+ t = &ctlr->tx[i];
+ assert(((uintptr)t & (Descralign - 1)) == 0);
+ t->cs = 0;
+ t->buf = 0;
+ t->next = PADDR(&ctlr->tx[NEXT(i, Ntx)]);
+ ctlr->txb[i] = nil;
+ }
+ ctlr->txtail = ctlr->txhead = 0;
+}
+
+static void
+ctlrinit(Ether *ether)
+{
+ int i;
+ Ctlr *ctlr = ether->ctlr;
+ Gbereg *reg = ctlr->reg;
+ static char name[KNAMELEN];
+ static Ctlr fakectlr; /* bigger than 4K; keep off the stack */
+
+ for (i = 0; i < nelem(reg->tcqdp); i++)
+ reg->tcqdp[i] = 0;
+ for (i = 0; i < nelem(reg->crdp); i++)
+ reg->crdp[i].r = 0;
+ coherence();
+
+ cfgdramacc(reg);
+ ctlralloc(ctlr);
+
+ reg->tcqdp[Qno] = PADDR(&ctlr->tx[ctlr->txhead]);
+ reg->crdp[Qno].r = PADDR(&ctlr->rx[ctlr->rxhead]);
+ coherence();
+
+// dumprxdescs(ctlr);
+
+ /* clear stats by reading them into fake ctlr */
+ getmibstats(&fakectlr);
+
+ reg->pxmfs = MFS40by; /* allow runts in */
+
+ /*
+ * ipg's (inter packet gaps) for interrupt coalescing,
+ * values in units of 64 clock cycles. A full-sized
+ * packet (1514 bytes) takes just over 12µs to transmit.
+ */
+ if (CLOCKFREQ/(Maxrxintrsec*64) >= (1<<16))
+ panic("rx coalescing value %d too big for short",
+ CLOCKFREQ/(Maxrxintrsec*64));
+ reg->sdc = SDCrifb | SDCrxburst(Burst16) | SDCtxburst(Burst16) |
+ SDCrxnobyteswap | SDCtxnobyteswap |
+ SDCipgintrx(CLOCKFREQ/(Maxrxintrsec*64));
+ reg->pxtfut = 0; /* TFUTipginttx(CLOCKFREQ/(Maxrxintrsec*64)) */
+
+ /* allow just these interrupts */
+ /* guruplug generates Irxerr interrupts continually */
+ reg->irqmask = Isum | Irx | Irxbufferq(Qno) | Irxerr | Itxendq(Qno);
+ reg->irqemask = IEsum | IEtxerrq(Qno) | IEphystschg | IErxoverrun |
+ IEtxunderrun;
+
+ reg->irqe = 0;
+ reg->euirqmask = 0;
+ coherence();
+ reg->irq = 0;
+ reg->euirq = 0;
+ /* send errors to end of memory */
+// reg->euda = PHYSDRAM + 512*MB - 8*1024;
+ reg->euda = 0;
+ reg->eudid = Attrcs1 << 4 | Targdram;
+
+// archetheraddr(ether, ctlr->reg, Qno); /* 2nd location */
+
+ reg->portcfg = Rxqdefault(Qno) | Rxqarp(Qno);
+ reg->portcfgx = 0;
+ coherence();
+
+ /*
+ * start the controller running.
+ * turn the port on, kick the receiver.
+ */
+
+ reg->psc1 = PSC1rgmii | PSC1encolonbp | PSC1coldomlim(0x23);
+ /* do this only when the controller is quiescent */
+ reg->psc0 = PSC0porton | PSC0an_flctloff |
+ PSC0an_pauseadv | PSC0nofrclinkdown | PSC0mru(PSC0mru1522);
+ coherence();
+ for (i = 0; i < 4000; i++) /* magic delay */
+ ;
+
+ ether->link = (reg->ps0 & PS0linkup) != 0;
+
+ /* set ethernet MTU for leaky bucket mechanism to 0 (disabled) */
+ reg->pmtu = 0;
+ etheractive(ether);
+
+ snprint(name, sizeof name, "#l%drproc", ether->ctlrno);
+ kproc(name, rcvproc, ether);
+
+ reg->rqc = Rxqon(Qno);
+ coherence();
+}
+
+static void
+attach(Ether* ether)
+{
+ Ctlr *ctlr = ether->ctlr;
+
+ lock(&ctlr->initlock);
+ if(ctlr->init == 0) {
+ ctlrinit(ether);
+ ctlr->init = 1;
+ }
+ unlock(&ctlr->initlock);
+}
+
+/*
+ * statistics goo.
+ * mib registers clear on read.
+ */
+
+static void
+getmibstats(Ctlr *ctlr)
+{
+ Gbereg *reg = ctlr->reg;
+
+ /*
+ * Marvell 88f6281 errata FE-ETH-120: high long of rxby and txby
+ * can't be read correctly, so read the low long frequently
+ * (every 30 seconds or less), thus avoiding overflow into high long.
+ */
+ ctlr->rxby += reg->rxbylo;
+ ctlr->txby += reg->txbylo;
+
+ ctlr->badrxby += reg->badrxby;
+ ctlr->mactxerr += reg->mactxerr;
+ ctlr->rxpkt += reg->rxpkt;
+ ctlr->badrxpkt += reg->badrxpkt;
+ ctlr->rxbcastpkt+= reg->rxbcastpkt;
+ ctlr->rxmcastpkt+= reg->rxmcastpkt;
+ ctlr->rx64 += reg->rx64;
+ ctlr->rx65_127 += reg->rx65_127;
+ ctlr->rx128_255 += reg->rx128_255;
+ ctlr->rx256_511 += reg->rx256_511;
+ ctlr->rx512_1023+= reg->rx512_1023;
+ ctlr->rx1024_max+= reg->rx1024_max;
+ ctlr->txpkt += reg->txpkt;
+ ctlr->txcollpktdrop+= reg->txcollpktdrop;
+ ctlr->txmcastpkt+= reg->txmcastpkt;
+ ctlr->txbcastpkt+= reg->txbcastpkt;
+ ctlr->badmacctlpkts+= reg->badmacctlpkts;
+ ctlr->txflctl += reg->txflctl;
+ ctlr->rxflctl += reg->rxflctl;
+ ctlr->badrxflctl+= reg->badrxflctl;
+ ctlr->rxundersized+= reg->rxundersized;
+ ctlr->rxfrags += reg->rxfrags;
+ ctlr->rxtoobig += reg->rxtoobig;
+ ctlr->rxjabber += reg->rxjabber;
+ ctlr->rxerr += reg->rxerr;
+ ctlr->crcerr += reg->crcerr;
+ ctlr->collisions+= reg->collisions;
+ ctlr->latecoll += reg->latecoll;
+}
+
+long
+ifstat(Ether *ether, void *a, long n, ulong off)
+{
+ Ctlr *ctlr = ether->ctlr;
+ Gbereg *reg = ctlr->reg;
+ char *buf, *p, *e;
+
+ buf = p = malloc(READSTR);
+ e = p + READSTR;
+
+ ilock(ctlr);
+ getmibstats(ctlr);
+
+ ctlr->intrs += ctlr->newintrs;
+ p = seprint(p, e, "interrupts: %lud\n", ctlr->intrs);
+ p = seprint(p, e, "new interrupts: %lud\n", ctlr->newintrs);
+ ctlr->newintrs = 0;
+ p = seprint(p, e, "tx underrun: %lud\n", ctlr->txunderrun);
+ p = seprint(p, e, "tx ring full: %lud\n", ctlr->txringfull);
+
+ ctlr->rxdiscard += reg->pxdfc;
+ ctlr->rxoverrun += reg->pxofc;
+ p = seprint(p, e, "rx discarded frames: %lud\n", ctlr->rxdiscard);
+ p = seprint(p, e, "rx overrun frames: %lud\n", ctlr->rxoverrun);
+ p = seprint(p, e, "no first+last flag: %lud\n", ctlr->nofirstlast);
+
+ p = seprint(p, e, "duplex: %s\n", (reg->ps0 & PS0fd)? "full": "half");
+ p = seprint(p, e, "flow control: %s\n", (reg->ps0 & PS0flctl)? "on": "off");
+ /* p = seprint(p, e, "speed: %d mbps\n", ); */
+
+ p = seprint(p, e, "received bytes: %llud\n", ctlr->rxby);
+ p = seprint(p, e, "bad received bytes: %lud\n", ctlr->badrxby);
+ p = seprint(p, e, "internal mac transmit errors: %lud\n", ctlr->mactxerr);
+ p = seprint(p, e, "total received frames: %lud\n", ctlr->rxpkt);
+ p = seprint(p, e, "received broadcast frames: %lud\n", ctlr->rxbcastpkt);
+ p = seprint(p, e, "received multicast frames: %lud\n", ctlr->rxmcastpkt);
+ p = seprint(p, e, "bad received frames: %lud\n", ctlr->badrxpkt);
+ p = seprint(p, e, "received frames 0-64: %lud\n", ctlr->rx64);
+ p = seprint(p, e, "received frames 65-127: %lud\n", ctlr->rx65_127);
+ p = seprint(p, e, "received frames 128-255: %lud\n", ctlr->rx128_255);
+ p = seprint(p, e, "received frames 256-511: %lud\n", ctlr->rx256_511);
+ p = seprint(p, e, "received frames 512-1023: %lud\n", ctlr->rx512_1023);
+ p = seprint(p, e, "received frames 1024-max: %lud\n", ctlr->rx1024_max);
+ p = seprint(p, e, "transmitted bytes: %llud\n", ctlr->txby);
+ p = seprint(p, e, "total transmitted frames: %lud\n", ctlr->txpkt);
+ p = seprint(p, e, "transmitted broadcast frames: %lud\n", ctlr->txbcastpkt);
+ p = seprint(p, e, "transmitted multicast frames: %lud\n", ctlr->txmcastpkt);
+ p = seprint(p, e, "transmit frames dropped by collision: %lud\n", ctlr->txcollpktdrop);
+ p = seprint(p, e, "misaligned buffers: %lud\n", ether->pktsmisaligned);
+
+ p = seprint(p, e, "bad mac control frames: %lud\n", ctlr->badmacctlpkts);
+ p = seprint(p, e, "transmitted flow control messages: %lud\n", ctlr->txflctl);
+ p = seprint(p, e, "received flow control messages: %lud\n", ctlr->rxflctl);
+ p = seprint(p, e, "bad received flow control messages: %lud\n", ctlr->badrxflctl);
+ p = seprint(p, e, "received undersized packets: %lud\n", ctlr->rxundersized);
+ p = seprint(p, e, "received fragments: %lud\n", ctlr->rxfrags);
+ p = seprint(p, e, "received oversized packets: %lud\n", ctlr->rxtoobig);
+ p = seprint(p, e, "received jabber packets: %lud\n", ctlr->rxjabber);
+ p = seprint(p, e, "mac receive errors: %lud\n", ctlr->rxerr);
+ p = seprint(p, e, "crc errors: %lud\n", ctlr->crcerr);
+ p = seprint(p, e, "collisions: %lud\n", ctlr->collisions);
+ p = seprint(p, e, "late collisions: %lud\n", ctlr->latecoll);
+ USED(p);
+ iunlock(ctlr);
+
+ n = readstr(off, a, n, buf);
+ free(buf);
+ return n;
+}
+
+
+static int
+reset(Ether *ether)
+{
+ Ctlr *ctlr;
+
+ ether->ctlr = ctlr = malloc(sizeof *ctlr);
+ switch(ether->ctlrno) {
+ case 0:
+ ether->irq = IRQ0gbe0sum;
+ break;
+ case 1:
+ ether->irq = IRQ0gbe1sum;
+ break;
+ default:
+ panic("ether1116: bad ether ctlr #%d", ether->ctlrno);
+ }
+ ctlr->reg = (Gbereg*)soc.ether[ether->ctlrno];
+
+ /* need this for guruplug, at least */
+ *(ulong *)soc.iocfg |= 1 << 7 | 1 << 15; /* io cfg 0: 1.8v gbe */
+ coherence();
+
+ ctlr->ether = ether;
+ ctlrs[ether->ctlrno] = ctlr;
+
+ shutdown(ether);
+ /* ensure that both interfaces are set to RGMII before calling mii */
+ ((Gbereg*)soc.ether[0])->psc1 |= PSC1rgmii;
+ ((Gbereg*)soc.ether[1])->psc1 |= PSC1rgmii;
+ coherence();
+
+ /* Set phy address of the port */
+ ctlr->port = ether->ctlrno;
+ ctlr->reg->phy = ether->ctlrno;
+ coherence();
+ ether->port = (uintptr)ctlr->reg;
+
+ if(kirkwoodmii(ether) < 0){
+ free(ctlr);
+ ether->ctlr = nil;
+ return -1;
+ }
+ miiphyinit(ctlr->mii);
+ archetheraddr(ether, ctlr->reg, Qno); /* original location */
+ if (memcmp(ether->ea, zeroea, sizeof zeroea) == 0){
+iprint("ether1116: reset: zero ether->ea\n");
+ free(ctlr);
+ ether->ctlr = nil;
+ return -1; /* no rj45 for this ether */
+ }
+
+ ether->attach = attach;
+ ether->transmit = transmit;
+ ether->interrupt = interrupt;
+ ether->ifstat = ifstat;
+ ether->shutdown = shutdown;
+ ether->ctl = ctl;
+
+ ether->arg = ether;
+ ether->promiscuous = promiscuous;
+ ether->multicast = multicast;
+ return 0;
+}
+
+void
+ether1116link(void)
+{
+ addethercard("88e1116", reset);
+}
diff --git a/sys/src/9/kw/etherif.h b/sys/src/9/kw/etherif.h
new file mode 100755
index 000000000..35d131fe3
--- /dev/null
+++ b/sys/src/9/kw/etherif.h
@@ -0,0 +1,55 @@
+enum
+{
+ MaxEther = 2,
+ Ntypes = 8,
+};
+
+typedef struct Ether Ether;
+struct Ether {
+ RWlock; /* TO DO */
+ ISAConf; /* hardware info */
+ int ctlrno;
+ int minmtu;
+ int maxmtu;
+ uchar ea[Eaddrlen];
+ void *address;
+ int tbusy;
+
+ void (*attach)(Ether*); /* filled in by reset routine */
+ void (*closed)(Ether*);
+ void (*detach)(Ether*);
+ void (*transmit)(Ether*);
+ void (*interrupt)(Ureg*, void*);
+ long (*ifstat)(Ether*, void*, long, ulong);
+ long (*ctl)(Ether*, void*, long); /* custom ctl messages */
+ void (*power)(Ether*, int); /* power on/off */
+ void (*shutdown)(Ether*); /* shutdown hardware before reboot */
+ void *ctlr;
+ int pcmslot; /* PCMCIA */
+ int fullduplex; /* non-zero if full duplex */
+ int linkchg; /* link status changed? */
+ uvlong starttime; /* last activity time */
+
+ Queue* oq;
+
+ /* statistics */
+ ulong interrupts;
+ ulong dmarxintr;
+ ulong dmatxintr;
+ ulong promisc;
+ ulong pktsdropped;
+ ulong pktsmisaligned;
+ ulong resets; /* after initialisation */
+ ulong bcasts; /* broadcast pkts rcv'd */
+ ulong mcasts; /* multicast pkts rcv'd */
+
+ Netif;
+};
+
+extern Block* etheriq(Ether*, Block*, int);
+extern void addethercard(char*, int(*)(Ether*));
+extern ulong ethercrc(uchar*, int);
+extern int parseether(uchar*, char*);
+
+#define NEXT(x, l) (((x)+1)%(l))
+#define PREV(x, l) (((x) == 0) ? (l)-1: (x)-1)
diff --git a/sys/src/9/kw/ethermii.c b/sys/src/9/kw/ethermii.c
new file mode 100755
index 000000000..90b219b3f
--- /dev/null
+++ b/sys/src/9/kw/ethermii.c
@@ -0,0 +1,235 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+#include "ethermii.h"
+
+int
+mii(Mii* mii, int mask)
+{
+ MiiPhy *miiphy;
+ int bit, oui, phyno, r, rmask;
+
+ /*
+ * Probe through mii for PHYs in mask;
+ * return the mask of those found in the current probe.
+ * If the PHY has not already been probed, update
+ * the Mii information.
+ */
+ rmask = 0;
+ for(phyno = 0; phyno < NMiiPhy; phyno++){
+ bit = 1<<phyno;
+ if(!(mask & bit))
+ continue;
+ if(mii->mask & bit){
+ rmask |= bit;
+ continue;
+ }
+ if(mii->mir(mii, phyno, Bmsr) == -1)
+ continue;
+ r = mii->mir(mii, phyno, Phyidr1);
+ oui = (r & 0x3FFF)<<6;
+ r = mii->mir(mii, phyno, Phyidr2);
+ oui |= r>>10;
+ if(oui == 0xFFFFF || oui == 0)
+ continue;
+
+ if((miiphy = malloc(sizeof(MiiPhy))) == nil)
+ continue;
+
+ miiphy->mii = mii;
+ miiphy->oui = oui;
+ miiphy->phyno = phyno;
+
+ miiphy->anar = ~0;
+ miiphy->fc = ~0;
+ miiphy->mscr = ~0;
+
+ mii->phy[phyno] = miiphy;
+ if(mii->curphy == nil)
+ mii->curphy = miiphy;
+ mii->mask |= bit;
+ mii->nphy++;
+
+ rmask |= bit;
+ }
+ return rmask;
+}
+
+int
+miimir(Mii* mii, int r)
+{
+ if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+ return -1;
+ return mii->mir(mii, mii->curphy->phyno, r);
+}
+
+int
+miimiw(Mii* mii, int r, int data)
+{
+ if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+ return -1;
+ return mii->miw(mii, mii->curphy->phyno, r, data);
+}
+
+int
+miireset(Mii* mii)
+{
+ int bmcr;
+
+ if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+ return -1;
+ bmcr = mii->mir(mii, mii->curphy->phyno, Bmcr);
+ bmcr |= BmcrR;
+ mii->miw(mii, mii->curphy->phyno, Bmcr, bmcr);
+ microdelay(1);
+
+ return 0;
+}
+
+int
+miiane(Mii* mii, int a, int p, int e)
+{
+ int anar, bmsr, mscr, r, phyno;
+
+ if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+ return -1;
+ phyno = mii->curphy->phyno;
+
+ bmsr = mii->mir(mii, phyno, Bmsr);
+ if(!(bmsr & BmsrAna))
+ return -1;
+
+ if(a != ~0)
+ anar = (AnaTXFD|AnaTXHD|Ana10FD|Ana10HD) & a;
+ else if(mii->curphy->anar != ~0)
+ anar = mii->curphy->anar;
+ else{
+ anar = mii->mir(mii, phyno, Anar);
+ anar &= ~(AnaAP|AnaP|AnaT4|AnaTXFD|AnaTXHD|Ana10FD|Ana10HD);
+ if(bmsr & Bmsr10THD)
+ anar |= Ana10HD;
+ if(bmsr & Bmsr10TFD)
+ anar |= Ana10FD;
+ if(bmsr & Bmsr100TXHD)
+ anar |= AnaTXHD;
+ if(bmsr & Bmsr100TXFD)
+ anar |= AnaTXFD;
+ }
+ mii->curphy->anar = anar;
+
+ if(p != ~0)
+ anar |= (AnaAP|AnaP) & p;
+ else if(mii->curphy->fc != ~0)
+ anar |= mii->curphy->fc;
+ mii->curphy->fc = (AnaAP|AnaP) & anar;
+
+ if(bmsr & BmsrEs){
+ mscr = mii->mir(mii, phyno, Mscr);
+ mscr &= ~(Mscr1000TFD|Mscr1000THD);
+ if(e != ~0)
+ mscr |= (Mscr1000TFD|Mscr1000THD) & e;
+ else if(mii->curphy->mscr != ~0)
+ mscr = mii->curphy->mscr;
+ else{
+ r = mii->mir(mii, phyno, Esr);
+ if(r & Esr1000THD)
+ mscr |= Mscr1000THD;
+ if(r & Esr1000TFD)
+ mscr |= Mscr1000TFD;
+ }
+ mii->curphy->mscr = mscr;
+ mii->miw(mii, phyno, Mscr, mscr);
+ }
+ mii->miw(mii, phyno, Anar, anar);
+
+ r = mii->mir(mii, phyno, Bmcr);
+ if(!(r & BmcrR)){
+ r |= BmcrAne|BmcrRan;
+ mii->miw(mii, phyno, Bmcr, r);
+ }
+
+ return 0;
+}
+
+int
+miistatus(Mii* mii)
+{
+ MiiPhy *phy;
+ int anlpar, bmsr, p, r, phyno;
+
+ if(mii == nil || mii->ctlr == nil || mii->curphy == nil)
+ return -1;
+ phy = mii->curphy;
+ phyno = phy->phyno;
+
+ /*
+ * Check Auto-Negotiation is complete and link is up.
+ * (Read status twice as the Ls bit is sticky).
+ */
+ bmsr = mii->mir(mii, phyno, Bmsr);
+ if(!(bmsr & (BmsrAnc|BmsrAna))) {
+ // print("miistatus: auto-neg incomplete\n");
+ return -1;
+ }
+
+ bmsr = mii->mir(mii, phyno, Bmsr);
+ if(!(bmsr & BmsrLs)){
+ // print("miistatus: link down\n");
+ phy->link = 0;
+ return -1;
+ }
+
+ phy->speed = phy->fd = phy->rfc = phy->tfc = 0;
+ if(phy->mscr){
+ r = mii->mir(mii, phyno, Mssr);
+ if((phy->mscr & Mscr1000TFD) && (r & Mssr1000TFD)){
+ phy->speed = 1000;
+ phy->fd = 1;
+ }
+ else if((phy->mscr & Mscr1000THD) && (r & Mssr1000THD))
+ phy->speed = 1000;
+ }
+
+ anlpar = mii->mir(mii, phyno, Anlpar);
+ if(phy->speed == 0){
+ r = phy->anar & anlpar;
+ if(r & AnaTXFD){
+ phy->speed = 100;
+ phy->fd = 1;
+ }
+ else if(r & AnaTXHD)
+ phy->speed = 100;
+ else if(r & Ana10FD){
+ phy->speed = 10;
+ phy->fd = 1;
+ }
+ else if(r & Ana10HD)
+ phy->speed = 10;
+ }
+ if(phy->speed == 0) {
+ // print("miistatus: phy speed 0\n");
+ return -1;
+ }
+
+ if(phy->fd){
+ p = phy->fc;
+ r = anlpar & (AnaAP|AnaP);
+ if(p == AnaAP && r == (AnaAP|AnaP))
+ phy->tfc = 1;
+ else if(p == (AnaAP|AnaP) && r == AnaAP)
+ phy->rfc = 1;
+ else if((p & AnaP) && (r & AnaP))
+ phy->rfc = phy->tfc = 1;
+ }
+
+ phy->link = 1;
+
+ return 0;
+}
diff --git a/sys/src/9/kw/ethermii.h b/sys/src/9/kw/ethermii.h
new file mode 100755
index 000000000..524a5743d
--- /dev/null
+++ b/sys/src/9/kw/ethermii.h
@@ -0,0 +1,143 @@
+typedef struct Mii Mii;
+typedef struct MiiPhy MiiPhy;
+
+enum { /* 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 Advertisement */
+ Anlpar = 5, /* AN Link Partner Ability */
+ Aner = 6, /* AN Expansion */
+ Annptr = 7, /* AN Next Page TX */
+ Annprr = 8, /* AN Next Page RX */
+ Mscr = 9, /* Gb Control */
+ Mssr = 10, /* Gb Status */
+ Esr = 15, /* Extended Status */
+
+ /* 88e1116-specific paged registers */
+ Scr = 16, /* special control register */
+ Ssr = 17, /* special status register */
+ Ier = 18, /* interrupt enable reg */
+ Isr = 19, /* interrupt status reg */
+ Escr = 20, /* extended special control reg */
+ Recr = 21, /* RX error counter reg */
+ Eadr = 22, /* extended address reg (page select) */
+ Globsts = 23, /* global status */
+ Impover = 24, /* RGMII output impedance override (page 2) */
+ Imptarg = 25, /* RGMII output impedance target (page 2) */
+ Scr2 = 26, /* special control register 2 */
+
+ NMiiPhyr = 32,
+ NMiiPhy = 32,
+};
+
+enum { /* Bmcr */
+ BmcrSs1 = 0x0040, /* Speed Select[1] */
+ BmcrCte = 0x0080, /* Collision Test Enable */
+ BmcrDm = 0x0100, /* Duplex Mode */
+ BmcrRan = 0x0200, /* Restart Auto-Negotiation */
+ BmcrI = 0x0400, /* Isolate */
+ BmcrPd = 0x0800, /* Power Down */
+ BmcrAne = 0x1000, /* Auto-Negotiation Enable */
+ BmcrSs0 = 0x2000, /* Speed Select[0] */
+ BmcrLe = 0x4000, /* Loopback Enable */
+ BmcrR = 0x8000, /* Reset */
+};
+
+enum { /* Bmsr */
+ BmsrEc = 0x0001, /* Extended Capability */
+ BmsrJd = 0x0002, /* Jabber Detect */
+ BmsrLs = 0x0004, /* Link Status */
+ BmsrAna = 0x0008, /* Auto-Negotiation Ability */
+ BmsrRf = 0x0010, /* Remote Fault */
+ BmsrAnc = 0x0020, /* Auto-Negotiation Complete */
+ BmsrPs = 0x0040, /* Preamble Suppression Capable */
+ BmsrEs = 0x0100, /* Extended Status */
+ Bmsr100T2HD = 0x0200, /* 100BASE-T2 HD Capable */
+ Bmsr100T2FD = 0x0400, /* 100BASE-T2 FD Capable */
+ Bmsr10THD = 0x0800, /* 10BASE-T HD Capable */
+ Bmsr10TFD = 0x1000, /* 10BASE-T FD Capable */
+ Bmsr100TXHD = 0x2000, /* 100BASE-TX HD Capable */
+ Bmsr100TXFD = 0x4000, /* 100BASE-TX FD Capable */
+ Bmsr100T4 = 0x8000, /* 100BASE-T4 Capable */
+};
+
+enum { /* Anar/Anlpar */
+ Ana10HD = 0x0020, /* Advertise 10BASE-T */
+ Ana10FD = 0x0040, /* Advertise 10BASE-T FD */
+ AnaTXHD = 0x0080, /* Advertise 100BASE-TX */
+ AnaTXFD = 0x0100, /* Advertise 100BASE-TX FD */
+ AnaT4 = 0x0200, /* Advertise 100BASE-T4 */
+ AnaP = 0x0400, /* Pause */
+ AnaAP = 0x0800, /* Asymmetrical Pause */
+ AnaRf = 0x2000, /* Remote Fault */
+ AnaAck = 0x4000, /* Acknowledge */
+ AnaNp = 0x8000, /* Next Page Indication */
+};
+
+enum { /* Mscr */
+ Mscr1000THD = 0x0100, /* Advertise 1000BASE-T HD */
+ Mscr1000TFD = 0x0200, /* Advertise 1000BASE-T FD */
+};
+
+enum { /* Mssr */
+ Mssr1000THD = 0x0400, /* Link Partner 1000BASE-T HD able */
+ Mssr1000TFD = 0x0800, /* Link Partner 1000BASE-T FD able */
+};
+
+enum { /* Esr */
+ Esr1000THD = 0x1000, /* 1000BASE-T HD Capable */
+ Esr1000TFD = 0x2000, /* 1000BASE-T FD Capable */
+ Esr1000XHD = 0x4000, /* 1000BASE-X HD Capable */
+ Esr1000XFD = 0x8000, /* 1000BASE-X FD Capable */
+};
+
+enum { /* Scr page 0 */
+ Pwrdown = 0x0004, /* power down */
+ Mdix = 0x0060, /* MDI crossover mode */
+ Endetect = 0x0300, /* energy detect */
+};
+enum { /* Scr page 2 */
+ Rgmiipwrup = 0x0008, /* RGMII power up: must sw reset after */
+};
+
+enum { /* Recr page 2 */
+ Txtiming = 1<<4,
+ Rxtiming = 1<<5,
+};
+
+typedef struct Mii {
+ Lock;
+ int nphy;
+ int mask;
+ MiiPhy* phy[NMiiPhy];
+ MiiPhy* curphy;
+
+ void* ctlr;
+ int (*mir)(Mii*, int, int);
+ int (*miw)(Mii*, int, int, int);
+} Mii;
+
+typedef struct MiiPhy {
+ Mii* mii;
+ int oui;
+ int phyno;
+
+ int anar;
+ int fc;
+ int mscr;
+
+ int link;
+ int speed;
+ int fd;
+ int rfc;
+ int tfc;
+};
+
+extern int mii(Mii*, int);
+extern int miiane(Mii*, int, int, int);
+extern int miimir(Mii*, int);
+extern int miimiw(Mii*, int, int);
+extern int miireset(Mii*);
+extern int miistatus(Mii*);
diff --git a/sys/src/9/kw/flashkw.c b/sys/src/9/kw/flashkw.c
new file mode 100755
index 000000000..e45bc29de
--- /dev/null
+++ b/sys/src/9/kw/flashkw.c
@@ -0,0 +1,671 @@
+/*
+ * sheevaplug nand flash driver
+ *
+ * for now separate from (inferno's) os/port/flashnand.c because the flash
+ * seems newer, and has different commands, but that is nand-chip specific,
+ * not sheevaplug-specific. they should be merged in future.
+ *
+ * the sheevaplug has a hynix 4gbit flash chip: hy27uf084g2m.
+ * 2048 byte pages, with 64 spare bytes each; erase block size is 128k.
+ *
+ * it has a "glueless" interface, at 0xf9000000. that's the address
+ * of the data register. the command and address registers are those
+ * or'ed with 1 and 2 respectively.
+ *
+ * linux uses this layout for the nand flash (from address 0 onwards):
+ * 1mb for u-boot
+ * 4mb for kernel
+ * 507mb for file system
+ *
+ * this is not so relevant here except for ecc. the first two areas
+ * (u-boot and kernel) are expected to have 4-bit ecc per 512 bytes
+ * (but calculated from last byte to first), bad erase blocks skipped.
+ * the file system area has 1-bit ecc per 256 bytes.
+ */
+
+#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/flashif.h"
+#include "../port/nandecc.h"
+
+enum {
+ Debug = 0,
+
+ Nopage = ~0ul, /* cache is empty */
+
+ /* vendors */
+ Hynix = 0xad,
+ Samsung = 0xec,
+
+ /* chips */
+ Hy27UF084G2M = 0xdc,
+
+ NandActCEBoot = 1<<1,
+};
+
+typedef struct Nandreg Nandreg;
+typedef struct Nandtab Nandtab;
+typedef struct Cache Cache;
+
+struct Nandreg { /* hw registers */
+ ulong rdparms;
+ ulong wrparms;
+ uchar _pad0[0x70 - 0x20];
+ ulong ctl;
+};
+
+struct Nandtab {
+ int vid;
+ int did;
+ vlong size;
+ char* name;
+};
+
+struct Cache {
+ Flash *flif;
+ ulong pageno;
+ ulong pgsize; /* r->pagesize */
+ char *page; /* of pgsize bytes */
+};
+
+enum {
+ /* commands */
+ Readstatus = 0x70,
+ Readid = 0x90, /* needs 1 0-address write */
+ Resetf = 0xff,
+
+ /*
+ * needs 5 address writes followed by Readstart,
+ * Readstartcache or Restartcopy.
+ */
+ Read = 0x00,
+ Readstart = 0x30,
+ Readstartcache = 0x31,
+ Readstartcopy = 0x35,
+ /* after Readstartcache, to stop reading next pages */
+ Readstopcache = 0x34,
+
+ /* needs 5 address writes, the data, and -start or -cache */
+ Program = 0x80,
+ Programstart = 0x10,
+ Programcache = 0x15,
+
+ Copyback = 0x85, /* followed by Programstart */
+
+ /* 3 address writes for block followed by Erasestart */
+ Erase = 0x60,
+ Erasestart = 0xd0,
+
+ Randomread = 0x85,
+ Randomwrite = 0x05,
+ Randomwritestart= 0xe0,
+
+ /* status bits */
+ SFail = 1<<0,
+ SCachefail = 1<<1,
+ SIdle = 1<<5, /* doesn't seem to come on ever */
+ SReady = 1<<6,
+ SNotprotected = 1<<7,
+
+ Srdymask = SReady, /* was SIdle|SReady */
+};
+
+Nandtab nandtab[] = {
+ {Hynix, Hy27UF084G2M, 512*MB, "Hy27UF084G2M"},
+ {Samsung, 0xdc, 512*MB, "Samsung 2Gb"},
+};
+
+static Cache cache;
+
+static void
+nandcmd(Flash *f, uchar b)
+{
+ uchar *p = (uchar *)((ulong)f->addr|1);
+
+ *p = b;
+ coherence();
+}
+
+static void
+nandaddr(Flash *f, uchar b)
+{
+ uchar *p = (uchar *)((ulong)f->addr|2);
+
+ *p = b;
+ coherence();
+}
+
+static uchar
+nandread(Flash *f)
+{
+ return *(uchar *)f->addr;
+}
+
+static void
+nandreadn(Flash *f, uchar *buf, long n)
+{
+ uchar *p = f->addr;
+
+ while(n-- > 0)
+ *buf++ = *p;
+}
+
+static void
+nandwrite(Flash *f, uchar b)
+{
+ *(uchar *)f->addr = b;
+ coherence();
+}
+
+static void
+nandwriten(Flash *f, uchar *buf, long n)
+{
+ uchar *p = f->addr;
+
+ while(n-- > 0)
+ *p = *buf++;
+ coherence();
+}
+
+static void
+nandclaim(Flash*)
+{
+ Nandreg *nand = (Nandreg*)soc.nand;
+
+ nand->ctl |= NandActCEBoot;
+ coherence();
+}
+
+static void
+nandunclaim(Flash*)
+{
+ Nandreg *nand = (Nandreg*)soc.nand;
+
+ nand->ctl &= ~NandActCEBoot;
+ coherence();
+}
+
+
+void mmuidmap(uintptr phys, int mbs);
+
+Nandtab *
+findflash(Flash *f, uintptr pa, uchar *id4p)
+{
+ int i;
+ ulong sts;
+ uchar maker, device, id3, id4;
+ Nandtab *chip;
+
+ mmuidmap(pa, 16);
+ f->addr = (void *)pa;
+
+ /* make sure controller is idle */
+ nandclaim(f);
+ nandcmd(f, Resetf);
+ nandunclaim(f);
+
+ nandclaim(f);
+ nandcmd(f, Readstatus);
+ sts = nandread(f);
+ nandunclaim(f);
+ for (i = 10; i > 0 && !(sts & SReady); i--) {
+ delay(50);
+ nandclaim(f);
+ nandcmd(f, Readstatus);
+ sts = nandread(f);
+ nandunclaim(f);
+ }
+ if(!(sts & SReady))
+ return nil;
+
+ nandclaim(f);
+ nandcmd(f, Readid);
+ nandaddr(f, 0);
+ maker = nandread(f);
+ device = nandread(f);
+ id3 = nandread(f);
+ USED(id3);
+ id4 = nandread(f);
+ nandunclaim(f);
+ if (id4p)
+ *id4p = id4;
+
+ for(i = 0; i < nelem(nandtab); i++) {
+ chip = &nandtab[i];
+ if(chip->vid == maker && chip->did == device)
+ return chip;
+ }
+ return nil;
+}
+
+int
+flashat(Flash *f, uintptr pa)
+{
+ return findflash(f, pa, nil) != nil;
+}
+
+static int
+idchip(Flash *f)
+{
+ uchar id4;
+ Flashregion *r;
+ Nandtab *chip;
+ static int blocksizes[4] = { 64*1024, 128*1024, 256*1024, 0 };
+ static int pagesizes[4] = { 1024, 2*1024, 0, 0 };
+ static int spares[2] = { 8, 16 }; /* per 512 bytes */
+
+ f->id = 0;
+ f->devid = 0;
+ f->width = 1;
+ chip = findflash(f, (uintptr)f->addr, &id4);
+ if (chip == nil)
+ return -1;
+ f->id = chip->vid;
+ f->devid = chip->did;
+ f->size = chip->size;
+ f->width = 1;
+ f->nr = 1;
+
+ r = &f->regions[0];
+ r->pagesize = pagesizes[id4 & MASK(2)];
+ r->erasesize = blocksizes[(id4 >> 4) & MASK(2)];
+ if (r->pagesize == 0 || r->erasesize == 0) {
+ iprint("flashkw: bogus flash sizes\n");
+ return -1;
+ }
+ r->n = f->size / r->erasesize;
+ r->start = 0;
+ r->end = f->size;
+ assert(ispow2(r->pagesize));
+ r->pageshift = log2(r->pagesize);
+ assert(ispow2(r->erasesize));
+ r->eraseshift = log2(r->erasesize);
+ assert(r->eraseshift >= r->pageshift);
+ if (cache.page == nil) {
+ cache.pgsize = r->pagesize;
+ cache.page = smalloc(r->pagesize);
+ }
+
+ r->spares = r->pagesize / 512 * spares[(id4 >> 2) & 1];
+ print("#F0: kwnand: %s %,lud bytes pagesize %lud erasesize %,lud"
+ " spares per page %lud\n", chip->name, f->size, r->pagesize,
+ r->erasesize, r->spares);
+ return 0;
+}
+
+static int
+ctlrwait(Flash *f)
+{
+ int sts, cnt;
+
+ nandclaim(f);
+ for (;;) {
+ nandcmd(f, Readstatus);
+ for(cnt = 100; cnt > 0 && (nandread(f) & Srdymask) != Srdymask;
+ cnt--)
+ microdelay(50);
+ nandcmd(f, Readstatus);
+ sts = nandread(f);
+ if((sts & Srdymask) == Srdymask)
+ break;
+ print("flashkw: flash ctlr busy, sts %#ux: resetting\n", sts);
+ nandcmd(f, Resetf);
+ }
+ nandunclaim(f);
+ return 0;
+}
+
+static int
+erasezone(Flash *f, Flashregion *r, ulong offset)
+{
+ int i;
+ ulong page, block;
+ uchar s;
+
+ if (Debug) {
+ print("flashkw: erasezone: offset %#lux, region nblocks %d,"
+ " start %#lux, end %#lux\n", offset, r->n, r->start,
+ r->end);
+ print(" erasesize %lud, pagesize %lud\n",
+ r->erasesize, r->pagesize);
+ }
+ assert(r->erasesize != 0);
+ if(offset & (r->erasesize - 1)) {
+ print("flashkw: erase offset %lud not block aligned\n", offset);
+ return -1;
+ }
+ page = offset >> r->pageshift;
+ block = page >> (r->eraseshift - r->pageshift);
+ if (Debug)
+ print("flashkw: erase: block %#lux\n", block);
+
+ /* make sure controller is idle */
+ if(ctlrwait(f) < 0) {
+ print("flashkw: erase: flash busy\n");
+ return -1;
+ }
+
+ /* start erasing */
+ nandclaim(f);
+ nandcmd(f, Erase);
+ nandaddr(f, page>>0);
+ nandaddr(f, page>>8);
+ nandaddr(f, page>>16);
+ nandcmd(f, Erasestart);
+
+ /* invalidate cache on any erasure (slight overkill) */
+ cache.pageno = Nopage;
+
+ /* have to wait until flash is done. typically ~2ms */
+ delay(1);
+ nandcmd(f, Readstatus);
+ for(i = 0; i < 100; i++) {
+ s = nandread(f);
+ if(s & SReady) {
+ nandunclaim(f);
+ if(s & SFail) {
+ print("flashkw: erase: failed, block %#lux\n",
+ block);
+ return -1;
+ }
+ return 0;
+ }
+ microdelay(50);
+ }
+ print("flashkw: erase timeout, block %#lux\n", block);
+ nandunclaim(f);
+ return -1;
+}
+
+static void
+flcachepage(Flash *f, ulong page, uchar *buf)
+{
+ Flashregion *r = &f->regions[0];
+
+ assert(cache.pgsize == r->pagesize);
+ cache.flif = f;
+ cache.pageno = page;
+ /* permit i/o directly to or from the cache */
+ if (buf != (uchar *)cache.page)
+ memmove(cache.page, buf, cache.pgsize);
+}
+
+static int
+write1page(Flash *f, ulong offset, void *buf)
+{
+ int i;
+ ulong page, v;
+ uchar s;
+ uchar *eccp, *p;
+ Flashregion *r = &f->regions[0];
+ static uchar *oob;
+
+ if (oob == nil)
+ oob = smalloc(r->spares);
+
+ page = offset >> r->pageshift;
+ if (Debug)
+ print("flashkw: write nand offset %#lux page %#lux\n",
+ offset, page);
+
+ if(offset & (r->pagesize - 1)) {
+ print("flashkw: write offset %lud not page aligned\n", offset);
+ return -1;
+ }
+
+ p = buf;
+ memset(oob, 0xff, r->spares);
+ assert(r->spares >= 24);
+ eccp = oob + r->spares - 24;
+ for(i = 0; i < r->pagesize / 256; i++) {
+ v = nandecc(p);
+ *eccp++ = v>>8;
+ *eccp++ = v>>0;
+ *eccp++ = v>>16;
+ p += 256;
+ }
+
+ if(ctlrwait(f) < 0) {
+ print("flashkw: write: nand not ready & idle\n");
+ return -1;
+ }
+
+ /* write, only whole pages for now, no sub-pages */
+ nandclaim(f);
+ nandcmd(f, Program);
+ nandaddr(f, 0);
+ nandaddr(f, 0);
+ nandaddr(f, page>>0);
+ nandaddr(f, page>>8);
+ nandaddr(f, page>>16);
+ nandwriten(f, buf, r->pagesize);
+ nandwriten(f, oob, r->spares);
+ nandcmd(f, Programstart);
+
+ microdelay(100);
+ nandcmd(f, Readstatus);
+ for(i = 0; i < 100; i++) {
+ s = nandread(f);
+ if(s & SReady) {
+ nandunclaim(f);
+ if(s & SFail) {
+ print("flashkw: write failed, page %#lux\n",
+ page);
+ return -1;
+ }
+ return 0;
+ }
+ microdelay(10);
+ }
+
+ nandunclaim(f);
+ flcachepage(f, page, buf);
+ print("flashkw: write timeout for page %#lux\n", page);
+ return -1;
+}
+
+static int
+read1page(Flash *f, ulong offset, void *buf)
+{
+ int i;
+ ulong addr, page, w;
+ uchar *eccp, *p;
+ Flashregion *r = &f->regions[0];
+ static uchar *oob;
+
+ if (oob == nil)
+ oob = smalloc(r->spares);
+
+ assert(r->pagesize != 0);
+ addr = offset & (r->pagesize - 1);
+ page = offset >> r->pageshift;
+ if(addr != 0) {
+ print("flashkw: read1page: read addr %#lux:"
+ " must read aligned page\n", addr);
+ return -1;
+ }
+
+ /* satisfy request from cache if possible */
+ if (f == cache.flif && page == cache.pageno &&
+ r->pagesize == cache.pgsize) {
+ memmove(buf, cache.page, r->pagesize);
+ return 0;
+ }
+
+ if (Debug)
+ print("flashkw: read offset %#lux addr %#lux page %#lux\n",
+ offset, addr, page);
+
+ nandclaim(f);
+ nandcmd(f, Read);
+ nandaddr(f, addr>>0);
+ nandaddr(f, addr>>8);
+ nandaddr(f, page>>0);
+ nandaddr(f, page>>8);
+ nandaddr(f, page>>16);
+ nandcmd(f, Readstart);
+
+ microdelay(50);
+
+ nandreadn(f, buf, r->pagesize);
+ nandreadn(f, oob, r->spares);
+
+ nandunclaim(f);
+
+ /* verify/correct data. last 8*3 bytes is ecc, per 256 bytes. */
+ p = buf;
+ assert(r->spares >= 24);
+ eccp = oob + r->spares - 24;
+ for(i = 0; i < r->pagesize / 256; i++) {
+ w = eccp[0] << 8 | eccp[1] << 0 | eccp[2] << 16;
+ eccp += 3;
+ switch(nandecccorrect(p, nandecc(p), &w, 1)) {
+ case NandEccErrorBad:
+ print("(page %d)\n", i);
+ return -1;
+ case NandEccErrorOneBit:
+ case NandEccErrorOneBitInEcc:
+ print("(page %d)\n", i);
+ /* fall through */
+ case NandEccErrorGood:
+ break;
+ }
+ p += 256;
+ }
+
+ flcachepage(f, page, buf);
+ return 0;
+}
+
+/*
+ * read a page at offset into cache, copy fragment from buf into it
+ * at pagoff, and rewrite that page.
+ */
+static int
+rewrite(Flash *f, ulong offset, ulong pagoff, void *buf, ulong size)
+{
+ if (read1page(f, offset, cache.page) < 0)
+ return -1;
+ memmove(&cache.page[pagoff], buf, size);
+ return write1page(f, offset, cache.page);
+}
+
+/* there are no alignment constraints on offset, buf, nor n */
+static int
+write(Flash *f, ulong offset, void *buf, long n)
+{
+ uint un, frag, pagoff;
+ ulong pgsize;
+ uchar *p;
+ Flashregion *r = &f->regions[0];
+
+ if(n <= 0)
+ panic("flashkw: write: non-positive count %ld", n);
+ un = n;
+ assert(r->pagesize != 0);
+ pgsize = r->pagesize;
+
+ /* if a partial first page exists, update the first page with it. */
+ p = buf;
+ pagoff = offset % pgsize;
+ if (pagoff != 0) {
+ frag = pgsize - pagoff;
+ if (frag > un) /* might not extend to end of page */
+ frag = un;
+ if (rewrite(f, offset - pagoff, pagoff, p, frag) < 0)
+ return -1;
+ offset += frag;
+ p += frag;
+ un -= frag;
+ }
+
+ /* copy whole pages */
+ while (un >= pgsize) {
+ if (write1page(f, offset, p) < 0)
+ return -1;
+ offset += pgsize;
+ p += pgsize;
+ un -= pgsize;
+ }
+
+ /* if a partial last page exists, update the last page with it. */
+ if (un > 0)
+ return rewrite(f, offset, 0, p, un);
+ return 0;
+}
+
+/* there are no alignment constraints on offset, buf, nor n */
+static int
+read(Flash *f, ulong offset, void *buf, long n)
+{
+ uint un, frag, pagoff;
+ ulong pgsize;
+ uchar *p;
+ Flashregion *r = &f->regions[0];
+
+ if(n <= 0)
+ panic("flashkw: read: non-positive count %ld", n);
+ un = n;
+ assert(r->pagesize != 0);
+ pgsize = r->pagesize;
+
+ /* if partial 1st page, read it into cache & copy fragment to buf */
+ p = buf;
+ pagoff = offset % pgsize;
+ if (pagoff != 0) {
+ frag = pgsize - pagoff;
+ if (frag > un) /* might not extend to end of page */
+ frag = un;
+ if (read1page(f, offset - pagoff, cache.page) < 0)
+ return -1;
+ offset += frag;
+ memmove(p, &cache.page[pagoff], frag);
+ p += frag;
+ un -= frag;
+ }
+
+ /* copy whole pages */
+ while (un >= pgsize) {
+ if (read1page(f, offset, p) < 0)
+ return -1;
+ offset += pgsize;
+ p += pgsize;
+ un -= pgsize;
+ }
+
+ /* if partial last page, read into cache & copy initial fragment to buf */
+ if (un > 0) {
+ if (read1page(f, offset, cache.page) < 0)
+ return -1;
+ memmove(p, cache.page, un);
+ }
+ return 0;
+}
+
+static int
+reset(Flash *f)
+{
+ if(f->data != nil)
+ return 1;
+ f->write = write;
+ f->read = read;
+ f->eraseall = nil;
+ f->erasezone = erasezone;
+ f->suspend = nil;
+ f->resume = nil;
+ f->sort = "nand";
+ cache.pageno = Nopage;
+ return idchip(f);
+}
+
+void
+flashkwlink(void)
+{
+ addflashcard("nand", reset);
+}
diff --git a/sys/src/9/kw/fns.h b/sys/src/9/kw/fns.h
new file mode 100755
index 000000000..fcaa647da
--- /dev/null
+++ b/sys/src/9/kw/fns.h
@@ -0,0 +1,203 @@
+#define checkmmu(a, b)
+#define countpagerefs(a, b)
+
+#include "../port/portfns.h"
+
+extern int led(int, int);
+extern void ledexit(int);
+extern void delay(int);
+extern void _uartputs(char*, int);
+extern int _uartprint(char*, ...);
+extern void uartkirkwoodconsole(void);
+extern void serialputs(char *, int);
+extern void serialputc(int c);
+
+#pragma varargck argpos _uartprint 1
+
+extern void archreboot(void);
+extern void archconfinit(void);
+extern void archreset(void);
+extern void barriers(void);
+extern void cachedinv(void);
+extern void cachedinvse(void*, int);
+extern void cachedwb(void);
+extern void cachedwbinv(void);
+extern void cachedwbinvse(void*, int);
+extern void cachedwbse(void*, int);
+extern void cacheiinv(void);
+extern void cacheuwbinv(void);
+extern uintptr cankaddr(uintptr pa);
+extern void clockshutdown(void);
+extern int clz(ulong);
+int cmpswap(long*, long, long);
+
+#define coherence barriers
+
+extern u32int controlget(void);
+extern u32int cpctget(void);
+extern u32int cpidget(void);
+extern char *cputype2name(char *, int);
+extern ulong cprd(int cp, int op1, int crn, int crm, int op2);
+extern ulong cprdsc(int op1, int crn, int crm, int op2);
+extern void cpuidprint(void);
+extern void cpwr(int cp, int op1, int crn, int crm, int op2, ulong val);
+extern void cpwrsc(int op1, int crn, int crm, int op2, ulong val);
+#define cycles(ip) *(ip) = lcycles()
+extern u32int dacget(void);
+extern void dacput(u32int);
+extern u32int farget(void);
+extern u32int fsrget(void);
+extern int ispow2(uvlong);
+extern void l1cachesoff(void);
+extern void l1cacheson(void);
+extern void l2cachecfgoff(void);
+extern void l2cachecfgon(void);
+extern void l2cacheon(void);
+extern void l2cacheuinv(void);
+extern void l2cacheuinvse(void*, int);
+extern void l2cacheuwb(void);
+extern void l2cacheuwbinv(void);
+extern void l2cacheuwbinvse(void*, int);
+extern void l2cacheuwbse(void*, int);
+extern void lastresortprint(char *buf, long bp);
+extern int log2(ulong);
+extern void mmuinvalidate(void); /* 'mmu' or 'tlb'? */
+extern void mmuinvalidateaddr(u32int); /* 'mmu' or 'tlb'? */
+extern u32int pidget(void);
+extern void pidput(u32int);
+void procrestore(Proc *);
+void procsave(Proc*);
+void procsetup(Proc*);
+extern void _reset(void);
+extern void setr13(int, u32int*);
+extern void syscallfmt(int syscallno, ulong pc, va_list list);
+extern void sysretfmt(int syscallno, va_list list, long ret, uvlong start, uvlong stop);
+extern int tas(void *);
+extern u32int ttbget(void);
+extern void ttbput(u32int);
+
+extern void intrclear(int sort, int v);
+extern void intrenable(int sort, int v, void (*f)(Ureg*, void*), void *a, char *name);
+extern void intrdisable(int sort, int v, void (*f)(Ureg*, void*), void* a, char *name);
+extern void vectors(void);
+extern void vtable(void);
+
+/*
+ * Things called in main.
+ */
+extern void clockinit(void);
+extern void i8250console(void);
+extern void links(void);
+extern void mmuinit(void);
+extern void touser(uintptr);
+extern void trapinit(void);
+
+extern int fpiarm(Ureg*);
+extern int fpudevprocio(Proc*, void*, long, uintptr, int);
+extern void fpuinit(void);
+extern void fpunoted(void);
+extern void fpunotify(Ureg*);
+extern void fpuprocrestore(Proc*);
+extern void fpuprocsave(Proc*);
+extern void fpusysprocsetup(Proc*);
+extern void fpusysrfork(Ureg*);
+extern void fpusysrforkchild(Proc*, Ureg *, Proc*);
+extern int fpuemu(Ureg*);
+
+/*
+ * Miscellaneous machine dependent stuff.
+ */
+extern char* getenv(char*, char*, int);
+char* getconf(char*);
+uintptr mmukmap(uintptr, uintptr, usize);
+uintptr mmukunmap(uintptr, uintptr, usize);
+extern void* mmuuncache(void*, usize);
+extern void* ucalloc(usize);
+extern void* ucallocalign(usize size, int align, int span);
+extern Block* ucallocb(int);
+extern void ucfree(void*);
+extern void ucfreeb(Block*);
+extern Block* uciallocb(int);
+
+/*
+ * Things called from port.
+ */
+extern void delay(int); /* only scheddump() */
+extern int islo(void);
+extern void microdelay(int); /* only edf.c */
+extern void evenaddr(uintptr);
+extern void idlehands(void);
+extern void setkernur(Ureg*, Proc*); /* only devproc.c */
+extern void spldone(void);
+extern int splfhi(void);
+extern int splflo(void);
+extern void sysprocsetup(Proc*);
+
+/*
+ * PCI
+ */
+ulong pcibarsize(Pcidev*, int);
+void pcibussize(Pcidev*, ulong*, ulong*);
+int pcicfgr8(Pcidev*, int);
+int pcicfgr16(Pcidev*, int);
+int pcicfgr32(Pcidev*, int);
+void pcicfgw8(Pcidev*, int, int);
+void pcicfgw16(Pcidev*, int, int);
+void pcicfgw32(Pcidev*, int, int);
+void pciclrbme(Pcidev*);
+void pciclrioe(Pcidev*);
+void pciclrmwi(Pcidev*);
+int pcigetpms(Pcidev*);
+void pcihinv(Pcidev*);
+uchar pciipin(Pcidev*, uchar);
+Pcidev* pcimatch(Pcidev*, int, int);
+Pcidev* pcimatchtbdf(int);
+void pcireset(void);
+int pciscan(int, Pcidev**);
+void pcisetbme(Pcidev*);
+void pcisetioe(Pcidev*);
+void pcisetmwi(Pcidev*);
+int pcisetpms(Pcidev*, int);
+int cas32(void*, u32int, u32int);
+int tas32(void*);
+
+#define CASU(p, e, n) cas32((p), (u32int)(e), (u32int)(n))
+#define CASV(p, e, n) cas32((p), (u32int)(e), (u32int)(n))
+#define CASW(addr, exp, new) cas32((addr), (exp), (new))
+#define TAS(addr) tas32(addr)
+
+extern void forkret(void);
+extern int userureg(Ureg*);
+void* vmap(uintptr, usize);
+void vunmap(void*, usize);
+
+extern void kexit(Ureg*);
+
+#define getpgcolor(a) 0
+#define kmapinval()
+
+#define PTR2UINT(p) ((uintptr)(p))
+#define UINT2PTR(i) ((void*)(i))
+
+#define waserror() (up->nerrlab++, setlabel(&up->errlab[up->nerrlab-1]))
+
+/*
+ * this low-level printing stuff is ugly,
+ * but there appears to be no other way to
+ * print until after #t is populated.
+ */
+#define wave(c) { \
+ coherence(); \
+ while ((*(ulong *)(PHYSCONS+4*5) & (1<<5)) == 0) /* (x->lsr&LSRthre)==0? */ \
+ ; \
+ *(ulong *)PHYSCONS = (c); \
+ coherence(); \
+}
+
+/*
+ * These are not good enough.
+ */
+#define KADDR(pa) UINT2PTR(KZERO|((uintptr)(pa)))
+#define PADDR(va) PTR2UINT(((uintptr)(va)) & ~KSEGM)
+
+#define MASK(v) ((1UL << (v)) - 1) /* mask `v' bits wide */
diff --git a/sys/src/9/kw/fpi.c b/sys/src/9/kw/fpi.c
new file mode 100755
index 000000000..f341f2e4a
--- /dev/null
+++ b/sys/src/9/kw/fpi.c
@@ -0,0 +1,300 @@
+/*
+ * Floating Point Interpreter.
+ * shamelessly stolen from an original by ark.
+ */
+#include "fpi.h"
+
+void
+fpiround(Internal *i)
+{
+ unsigned long guard;
+
+ guard = i->l & GuardMask;
+ i->l &= ~GuardMask;
+ if(guard > (LsBit>>1) || (guard == (LsBit>>1) && (i->l & LsBit))){
+ i->l += LsBit;
+ if(i->l & CarryBit){
+ i->l &= ~CarryBit;
+ i->h++;
+ if(i->h & CarryBit){
+ if (i->h & 0x01)
+ i->l |= CarryBit;
+ i->l >>= 1;
+ i->h >>= 1;
+ i->e++;
+ }
+ }
+ }
+}
+
+static void
+matchexponents(Internal *x, Internal *y)
+{
+ int count;
+
+ count = y->e - x->e;
+ x->e = y->e;
+ if(count >= 2*FractBits){
+ x->l = x->l || x->h;
+ x->h = 0;
+ return;
+ }
+ if(count >= FractBits){
+ count -= FractBits;
+ x->l = x->h|(x->l != 0);
+ x->h = 0;
+ }
+ while(count > 0){
+ count--;
+ if(x->h & 0x01)
+ x->l |= CarryBit;
+ if(x->l & 0x01)
+ x->l |= 2;
+ x->l >>= 1;
+ x->h >>= 1;
+ }
+}
+
+static void
+shift(Internal *i)
+{
+ i->e--;
+ i->h <<= 1;
+ i->l <<= 1;
+ if(i->l & CarryBit){
+ i->l &= ~CarryBit;
+ i->h |= 0x01;
+ }
+}
+
+static void
+normalise(Internal *i)
+{
+ while((i->h & HiddenBit) == 0)
+ shift(i);
+}
+
+static void
+renormalise(Internal *i)
+{
+ if(i->e < -2 * FractBits)
+ i->e = -2 * FractBits;
+ while(i->e < 1){
+ i->e++;
+ if(i->h & 0x01)
+ i->l |= CarryBit;
+ i->h >>= 1;
+ i->l = (i->l>>1)|(i->l & 0x01);
+ }
+ if(i->e >= ExpInfinity)
+ SetInfinity(i);
+}
+
+void
+fpinormalise(Internal *x)
+{
+ if(!IsWeird(x) && !IsZero(x))
+ normalise(x);
+}
+
+void
+fpiadd(Internal *x, Internal *y, Internal *i)
+{
+ Internal *t;
+
+ i->s = x->s;
+ if(IsWeird(x) || IsWeird(y)){
+ if(IsNaN(x) || IsNaN(y))
+ SetQNaN(i);
+ else
+ SetInfinity(i);
+ return;
+ }
+ if(x->e > y->e){
+ t = x;
+ x = y;
+ y = t;
+ }
+ matchexponents(x, y);
+ i->e = x->e;
+ i->h = x->h + y->h;
+ i->l = x->l + y->l;
+ if(i->l & CarryBit){
+ i->h++;
+ i->l &= ~CarryBit;
+ }
+ if(i->h & (HiddenBit<<1)){
+ if(i->h & 0x01)
+ i->l |= CarryBit;
+ i->l = (i->l>>1)|(i->l & 0x01);
+ i->h >>= 1;
+ i->e++;
+ }
+ if(IsWeird(i))
+ SetInfinity(i);
+}
+
+void
+fpisub(Internal *x, Internal *y, Internal *i)
+{
+ Internal *t;
+
+ if(y->e < x->e
+ || (y->e == x->e && (y->h < x->h || (y->h == x->h && y->l < x->l)))){
+ t = x;
+ x = y;
+ y = t;
+ }
+ i->s = y->s;
+ if(IsNaN(y)){
+ SetQNaN(i);
+ return;
+ }
+ if(IsInfinity(y)){
+ if(IsInfinity(x))
+ SetQNaN(i);
+ else
+ SetInfinity(i);
+ return;
+ }
+ matchexponents(x, y);
+ i->e = y->e;
+ i->h = y->h - x->h;
+ i->l = y->l - x->l;
+ if(i->l < 0){
+ i->l += CarryBit;
+ i->h--;
+ }
+ if(i->h == 0 && i->l == 0)
+ SetZero(i);
+ else while(i->e > 1 && (i->h & HiddenBit) == 0)
+ shift(i);
+}
+
+#define CHUNK (FractBits/2)
+#define CMASK ((1<<CHUNK)-1)
+#define HI(x) ((short)((x)>>CHUNK) & CMASK)
+#define LO(x) ((short)(x) & CMASK)
+#define SPILL(x) ((x)>>CHUNK)
+#define M(x, y) ((long)a[x]*(long)b[y])
+#define C(h, l) (((long)((h) & CMASK)<<CHUNK)|((l) & CMASK))
+
+void
+fpimul(Internal *x, Internal *y, Internal *i)
+{
+ long a[4], b[4], c[7], f[4];
+
+ i->s = x->s^y->s;
+ if(IsWeird(x) || IsWeird(y)){
+ if(IsNaN(x) || IsNaN(y) || IsZero(x) || IsZero(y))
+ SetQNaN(i);
+ else
+ SetInfinity(i);
+ return;
+ }
+ else if(IsZero(x) || IsZero(y)){
+ SetZero(i);
+ return;
+ }
+ normalise(x);
+ normalise(y);
+ i->e = x->e + y->e - (ExpBias - 1);
+
+ a[0] = HI(x->h); b[0] = HI(y->h);
+ a[1] = LO(x->h); b[1] = LO(y->h);
+ a[2] = HI(x->l); b[2] = HI(y->l);
+ a[3] = LO(x->l); b[3] = LO(y->l);
+
+ c[6] = M(3, 3);
+ c[5] = M(2, 3) + M(3, 2) + SPILL(c[6]);
+ c[4] = M(1, 3) + M(2, 2) + M(3, 1) + SPILL(c[5]);
+ c[3] = M(0, 3) + M(1, 2) + M(2, 1) + M(3, 0) + SPILL(c[4]);
+ c[2] = M(0, 2) + M(1, 1) + M(2, 0) + SPILL(c[3]);
+ c[1] = M(0, 1) + M(1, 0) + SPILL(c[2]);
+ c[0] = M(0, 0) + SPILL(c[1]);
+
+ f[0] = c[0];
+ f[1] = C(c[1], c[2]);
+ f[2] = C(c[3], c[4]);
+ f[3] = C(c[5], c[6]);
+
+ if((f[0] & HiddenBit) == 0){
+ f[0] <<= 1;
+ f[1] <<= 1;
+ f[2] <<= 1;
+ f[3] <<= 1;
+ if(f[1] & CarryBit){
+ f[0] |= 1;
+ f[1] &= ~CarryBit;
+ }
+ if(f[2] & CarryBit){
+ f[1] |= 1;
+ f[2] &= ~CarryBit;
+ }
+ if(f[3] & CarryBit){
+ f[2] |= 1;
+ f[3] &= ~CarryBit;
+ }
+ i->e--;
+ }
+ i->h = f[0];
+ i->l = f[1];
+ if(f[2] || f[3])
+ i->l |= 1;
+ renormalise(i);
+}
+
+void
+fpidiv(Internal *x, Internal *y, Internal *i)
+{
+ i->s = x->s^y->s;
+ if(IsNaN(x) || IsNaN(y)
+ || (IsInfinity(x) && IsInfinity(y)) || (IsZero(x) && IsZero(y))){
+ SetQNaN(i);
+ return;
+ }
+ else if(IsZero(x) || IsInfinity(y)){
+ SetInfinity(i);
+ return;
+ }
+ else if(IsInfinity(x) || IsZero(y)){
+ SetZero(i);
+ return;
+ }
+ normalise(x);
+ normalise(y);
+ i->h = 0;
+ i->l = 0;
+ i->e = y->e - x->e + (ExpBias + 2*FractBits - 1);
+ do{
+ if(y->h > x->h || (y->h == x->h && y->l >= x->l)){
+ i->l |= 0x01;
+ y->h -= x->h;
+ y->l -= x->l;
+ if(y->l < 0){
+ y->l += CarryBit;
+ y->h--;
+ }
+ }
+ shift(y);
+ shift(i);
+ }while ((i->h & HiddenBit) == 0);
+ if(y->h || y->l)
+ i->l |= 0x01;
+ renormalise(i);
+}
+
+int
+fpicmp(Internal *x, Internal *y)
+{
+ if(IsNaN(x) && IsNaN(y))
+ return 0;
+ if(IsInfinity(x) && IsInfinity(y))
+ return y->s - x->s;
+ if(x->e == y->e && x->h == y->h && x->l == y->l)
+ return y->s - x->s;
+ if(x->e < y->e
+ || (x->e == y->e && (x->h < y->h || (x->h == y->h && x->l < y->l))))
+ return y->s ? 1: -1;
+ return x->s ? -1: 1;
+}
diff --git a/sys/src/9/kw/fpi.h b/sys/src/9/kw/fpi.h
new file mode 100755
index 000000000..abaa7c120
--- /dev/null
+++ b/sys/src/9/kw/fpi.h
@@ -0,0 +1,61 @@
+typedef long Word;
+typedef unsigned long Single;
+typedef struct {
+ unsigned long l;
+ unsigned long h;
+} Double;
+
+enum {
+ FractBits = 28,
+ CarryBit = 0x10000000,
+ HiddenBit = 0x08000000,
+ MsBit = HiddenBit,
+ NGuardBits = 3,
+ GuardMask = 0x07,
+ LsBit = (1<<NGuardBits),
+
+ SingleExpBias = 127,
+ SingleExpMax = 255,
+ DoubleExpBias = 1023,
+ DoubleExpMax = 2047,
+
+ ExpBias = DoubleExpBias,
+ ExpInfinity = DoubleExpMax,
+};
+
+typedef struct {
+ unsigned char s;
+ short e;
+ long l; /* 0000FFFFFFFFFFFFFFFFFFFFFFFFFGGG */
+ long h; /* 0000HFFFFFFFFFFFFFFFFFFFFFFFFFFF */
+} Internal;
+
+#define IsWeird(n) ((n)->e >= ExpInfinity)
+#define IsInfinity(n) (IsWeird(n) && (n)->h == HiddenBit && (n)->l == 0)
+#define SetInfinity(n) ((n)->e = ExpInfinity, (n)->h = HiddenBit, (n)->l = 0)
+#define IsNaN(n) (IsWeird(n) && (((n)->h & ~HiddenBit) || (n)->l))
+#define SetQNaN(n) ((n)->s = 0, (n)->e = ExpInfinity, \
+ (n)->h = HiddenBit|(LsBit<<1), (n)->l = 0)
+#define IsZero(n) ((n)->e == 1 && (n)->h == 0 && (n)->l == 0)
+#define SetZero(n) ((n)->e = 1, (n)->h = 0, (n)->l = 0)
+
+/*
+ * fpi.c
+ */
+extern void fpiround(Internal *);
+extern void fpiadd(Internal *, Internal *, Internal *);
+extern void fpisub(Internal *, Internal *, Internal *);
+extern void fpimul(Internal *, Internal *, Internal *);
+extern void fpidiv(Internal *, Internal *, Internal *);
+extern int fpicmp(Internal *, Internal *);
+extern void fpinormalise(Internal*);
+
+/*
+ * fpimem.c
+ */
+extern void fpis2i(Internal *, void *);
+extern void fpid2i(Internal *, void *);
+extern void fpiw2i(Internal *, void *);
+extern void fpii2s(void *, Internal *);
+extern void fpii2d(void *, Internal *);
+extern void fpii2w(Word *, Internal *);
diff --git a/sys/src/9/kw/fpiarm.c b/sys/src/9/kw/fpiarm.c
new file mode 100755
index 000000000..063d10c52
--- /dev/null
+++ b/sys/src/9/kw/fpiarm.c
@@ -0,0 +1,576 @@
+/*
+ * this doesn't attempt to implement ARM floating-point properties
+ * that aren't visible in the Inferno environment.
+ * all arithmetic is done in double precision.
+ * the FP trap status isn't updated.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+#include "ureg.h"
+
+#include "arm.h"
+#include "fpi.h"
+
+/* undef this if correct kernel r13 isn't in Ureg;
+ * check calculation in fpiarm below
+ */
+
+
+#define REG(ur, x) (*(long*)(((char*)(ur))+roff[(x)]))
+#define FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&7])
+
+typedef struct FP2 FP2;
+typedef struct FP1 FP1;
+
+struct FP2 {
+ char* name;
+ void (*f)(Internal, Internal, Internal*);
+};
+
+struct FP1 {
+ char* name;
+ void (*f)(Internal*, Internal*);
+};
+
+enum {
+ N = 1<<31,
+ Z = 1<<30,
+ C = 1<<29,
+ V = 1<<28,
+ REGPC = 15,
+};
+
+enum {
+ fpemudebug = 0,
+};
+
+#undef OFR
+#define OFR(X) ((ulong)&((Ureg*)0)->X)
+
+static int roff[] = {
+ OFR(r0), OFR(r1), OFR(r2), OFR(r3),
+ OFR(r4), OFR(r5), OFR(r6), OFR(r7),
+ OFR(r8), OFR(r9), OFR(r10), OFR(r11),
+ OFR(r12), OFR(r13), OFR(r14), OFR(pc),
+};
+
+static Internal fpconst[8] = { /* indexed by op&7 */
+ /* s, e, l, h */
+ {0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */
+ {0, 0x3FF, 0x00000000, 0x08000000}, /* 1.0 */
+ {0, 0x400, 0x00000000, 0x08000000}, /* 2.0 */
+ {0, 0x400, 0x00000000, 0x0C000000}, /* 3.0 */
+ {0, 0x401, 0x00000000, 0x08000000}, /* 4.0 */
+ {0, 0x401, 0x00000000, 0x0A000000}, /* 5.0 */
+ {0, 0x3FE, 0x00000000, 0x08000000}, /* 0.5 */
+ {0, 0x402, 0x00000000, 0x0A000000}, /* 10.0 */
+};
+
+/*
+ * arm binary operations
+ */
+
+static void
+fadd(Internal m, Internal n, Internal *d)
+{
+ (m.s == n.s? fpiadd: fpisub)(&m, &n, d);
+}
+
+static void
+fsub(Internal m, Internal n, Internal *d)
+{
+ m.s ^= 1;
+ (m.s == n.s? fpiadd: fpisub)(&m, &n, d);
+}
+
+static void
+fsubr(Internal m, Internal n, Internal *d)
+{
+ n.s ^= 1;
+ (n.s == m.s? fpiadd: fpisub)(&n, &m, d);
+}
+
+static void
+fmul(Internal m, Internal n, Internal *d)
+{
+ fpimul(&m, &n, d);
+}
+
+static void
+fdiv(Internal m, Internal n, Internal *d)
+{
+ fpidiv(&m, &n, d);
+}
+
+static void
+fdivr(Internal m, Internal n, Internal *d)
+{
+ fpidiv(&n, &m, d);
+}
+
+/*
+ * arm unary operations
+ */
+
+static void
+fmov(Internal *m, Internal *d)
+{
+ *d = *m;
+}
+
+static void
+fmovn(Internal *m, Internal *d)
+{
+ *d = *m;
+ d->s ^= 1;
+}
+
+static void
+fabsf(Internal *m, Internal *d)
+{
+ *d = *m;
+ d->s = 0;
+}
+
+static void
+frnd(Internal *m, Internal *d)
+{
+ short e;
+
+ (m->s? fsub: fadd)(fpconst[6], *m, d);
+ if(IsWeird(d))
+ return;
+ fpiround(d);
+ e = (d->e - ExpBias) + 1;
+ if(e <= 0)
+ SetZero(d);
+ else if(e > FractBits){
+ if(e < 2*FractBits)
+ d->l &= ~((1<<(2*FractBits - e))-1);
+ }else{
+ d->l = 0;
+ if(e < FractBits)
+ d->h &= ~((1<<(FractBits-e))-1);
+ }
+}
+
+static FP1 optab1[16] = { /* Fd := OP Fm */
+[0] {"MOVF", fmov},
+[1] {"NEGF", fmovn},
+[2] {"ABSF", fabsf},
+[3] {"RNDF", frnd},
+[4] {"SQTF", /*fsqt*/0},
+/* LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN all `deprecated' */
+/* URD and NRM aren't implemented */
+};
+
+static FP2 optab2[16] = { /* Fd := Fn OP Fm */
+[0] {"ADDF", fadd},
+[1] {"MULF", fmul},
+[2] {"SUBF", fsub},
+[3] {"RSUBF", fsubr},
+[4] {"DIVF", fdiv},
+[5] {"RDIVF", fdivr},
+/* POW, RPW deprecated */
+[8] {"REMF", /*frem*/0},
+[9] {"FMF", fmul}, /* fast multiply */
+[10] {"FDV", fdiv}, /* fast divide */
+[11] {"FRD", fdivr}, /* fast reverse divide */
+/* POL deprecated */
+};
+
+static ulong
+fcmp(Internal *n, Internal *m)
+{
+ int i;
+ Internal rm, rn;
+
+ if(IsWeird(m) || IsWeird(n)){
+ /* BUG: should trap if not masked */
+ return V|C;
+ }
+ rn = *n;
+ rm = *m;
+ fpiround(&rn);
+ fpiround(&rm);
+ i = fpicmp(&rn, &rm);
+ if(i > 0)
+ return C;
+ else if(i == 0)
+ return C|Z;
+ else
+ return N;
+}
+
+static void
+fld(void (*f)(Internal*, void*), int d, ulong ea, int n, FPsave *ufp)
+{
+ void *mem;
+
+ mem = (void*)ea;
+ (*f)(&FR(ufp, d), mem);
+ if(fpemudebug)
+ print("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d);
+}
+
+static void
+fst(void (*f)(void*, Internal*), ulong ea, int s, int n, FPsave *ufp)
+{
+ Internal tmp;
+ void *mem;
+
+ mem = (void*)ea;
+ tmp = FR(ufp, s);
+ if(fpemudebug)
+ print("MOV%c F%d,#%lux\n", n==8? 'D': 'F', s, ea);
+ (*f)(mem, &tmp);
+}
+
+static int
+condok(int cc, int c)
+{
+ switch(c){
+ case 0: /* Z set */
+ return cc&Z;
+ case 1: /* Z clear */
+ return (cc&Z) == 0;
+ case 2: /* C set */
+ return cc&C;
+ case 3: /* C clear */
+ return (cc&C) == 0;
+ case 4: /* N set */
+ return cc&N;
+ case 5: /* N clear */
+ return (cc&N) == 0;
+ case 6: /* V set */
+ return cc&V;
+ case 7: /* V clear */
+ return (cc&V) == 0;
+ case 8: /* C set and Z clear */
+ return cc&C && (cc&Z) == 0;
+ case 9: /* C clear or Z set */
+ return (cc&C) == 0 || cc&Z;
+ case 10: /* N set and V set, or N clear and V clear */
+ return (~cc&(N|V))==0 || (cc&(N|V)) == 0;
+ case 11: /* N set and V clear, or N clear and V set */
+ return (cc&(N|V))==N || (cc&(N|V))==V;
+ case 12: /* Z clear, and either N set and V set or N clear and V clear */
+ return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0);
+ case 13: /* Z set, or N set and V clear or N clear and V set */
+ return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V;
+ case 14: /* always */
+ return 1;
+ case 15: /* never (reserved) */
+ return 0;
+ }
+ return 0; /* not reached */
+}
+
+static void
+unimp(ulong pc, ulong op)
+{
+ char buf[60];
+
+ snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", pc, op);
+ if(fpemudebug)
+ print("FPE: %s\n", buf);
+ error(buf);
+ /* no return */
+}
+
+static void
+fpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp)
+{
+ int rn, rd, tag, o;
+ long off;
+ ulong ea;
+ Internal tmp, *fm, *fn;
+
+ /* note: would update fault status here if we noted numeric exceptions */
+
+ /*
+ * LDF, STF; 10.1.1
+ */
+ if(((op>>25)&7) == 6){
+ if(op & (1<<22))
+ unimp(pc, op); /* packed or extended */
+ rn = (op>>16)&0xF;
+ off = (op&0xFF)<<2;
+ if((op & (1<<23)) == 0)
+ off = -off;
+ ea = REG(ur, rn);
+ if(rn == REGPC)
+ ea += 8;
+ if(op & (1<<24))
+ ea += off;
+ rd = (op>>12)&7;
+ if(op & (1<<20)){
+ if(op & (1<<15))
+ fld(fpid2i, rd, ea, 8, ufp);
+ else
+ fld(fpis2i, rd, ea, 4, ufp);
+ }else{
+ if(op & (1<<15))
+ fst(fpii2d, ea, rd, 8, ufp);
+ else
+ fst(fpii2s, ea, rd, 4, ufp);
+ }
+ if((op & (1<<24)) == 0)
+ ea += off;
+ if(op & (1<<21))
+ REG(ur, rn) = ea;
+ return;
+ }
+
+ /*
+ * CPRT/transfer, 10.3
+ */
+ if(op & (1<<4)){
+ rd = (op>>12) & 0xF;
+
+ /*
+ * compare, 10.3.1
+ */
+ if(rd == 15 && op & (1<<20)){
+ rn = (op>>16)&7;
+ fn = &FR(ufp, rn);
+ if(op & (1<<3)){
+ fm = &fpconst[op&7];
+ if(fpemudebug)
+ tag = 'C';
+ }else{
+ fm = &FR(ufp, op&7);
+ if(fpemudebug)
+ tag = 'F';
+ }
+ switch((op>>21)&7){
+ default:
+ unimp(pc, op);
+ case 4: /* CMF: Fn :: Fm */
+ case 6: /* CMFE: Fn :: Fm (with exception) */
+ ur->psr &= ~(N|C|Z|V);
+ ur->psr |= fcmp(fn, fm);
+ break;
+ case 5: /* CNF: Fn :: -Fm */
+ case 7: /* CNFE: Fn :: -Fm (with exception) */
+ tmp = *fm;
+ tmp.s ^= 1;
+ ur->psr &= ~(N|C|Z|V);
+ ur->psr |= fcmp(fn, &tmp);
+ break;
+ }
+ if(fpemudebug)
+ print("CMPF %c%d,F%ld =%#lux\n",
+ tag, rn, op&7, ur->psr>>28);
+ return;
+ }
+
+ /*
+ * other transfer, 10.3
+ */
+ switch((op>>20)&0xF){
+ default:
+ unimp(pc, op);
+ case 0: /* FLT */
+ rn = (op>>16) & 7;
+ fpiw2i(&FR(ufp, rn), &REG(ur, rd));
+ if(fpemudebug)
+ print("MOVW[FD] R%d, F%d\n", rd, rn);
+ break;
+ case 1: /* FIX */
+ if(op & (1<<3))
+ unimp(pc, op);
+ rn = op & 7;
+ tmp = FR(ufp, rn);
+ fpii2w(&REG(ur, rd), &tmp);
+ if(fpemudebug)
+ print("MOV[FD]W F%d, R%d =%ld\n", rn, rd, REG(ur, rd));
+ break;
+ case 2: /* FPSR := Rd */
+ ufp->status = REG(ur, rd);
+ if(fpemudebug)
+ print("MOVW R%d, FPSR\n", rd);
+ break;
+ case 3: /* Rd := FPSR */
+ REG(ur, rd) = ufp->status;
+ if(fpemudebug)
+ print("MOVW FPSR, R%d\n", rd);
+ break;
+ case 4: /* FPCR := Rd */
+ ufp->control = REG(ur, rd);
+ if(fpemudebug)
+ print("MOVW R%d, FPCR\n", rd);
+ break;
+ case 5: /* Rd := FPCR */
+ REG(ur, rd) = ufp->control;
+ if(fpemudebug)
+ print("MOVW FPCR, R%d\n", rd);
+ break;
+ }
+ return;
+ }
+
+ /*
+ * arithmetic
+ */
+
+ if(op & (1<<3)){ /* constant */
+ fm = &fpconst[op&7];
+ if(fpemudebug)
+ tag = 'C';
+ }else{
+ fm = &FR(ufp, op&7);
+ if(fpemudebug)
+ tag = 'F';
+ }
+ rd = (op>>12)&7;
+ o = (op>>20)&0xF;
+ if(op & (1<<15)){ /* monadic */
+ FP1 *fp;
+ fp = &optab1[o];
+ if(fp->f == nil)
+ unimp(pc, op);
+ if(fpemudebug)
+ print("%s %c%ld,F%d\n", fp->name, tag, op&7, rd);
+ (*fp->f)(fm, &FR(ufp, rd));
+ } else {
+ FP2 *fp;
+ fp = &optab2[o];
+ if(fp->f == nil)
+ unimp(pc, op);
+ rn = (op>>16)&7;
+ if(fpemudebug)
+ print("%s %c%ld,F%d,F%d\n", fp->name, tag, op&7, rn, rd);
+ (*fp->f)(*fm, FR(ufp, rn), &FR(ufp, rd));
+ }
+}
+
+void
+casemu(ulong pc, ulong op, Ureg *ur)
+{
+ ulong *rp, ro, rn, *rd;
+
+ USED(pc);
+
+ rp = (ulong*)ur;
+ ro = rp[op>>16 & 0x7];
+ rn = rp[op>>0 & 0x7];
+ rd = rp + (op>>12 & 0x7);
+ rp = (ulong*)*rd;
+ validaddr((ulong)rp, 4, 1);
+ splhi();
+ if(*rd = (*rp == ro))
+ *rp = rn;
+ spllo();
+}
+
+int ldrexvalid;
+
+void
+ldrex(ulong pc, ulong op, Ureg *ur)
+{
+ ulong *rp, *rd, *addr;
+
+ USED(pc);
+
+ rp = (ulong*)ur;
+ rd = rp + (op>>16 & 0x7);
+ addr = (ulong*)*rd;
+ validaddr((ulong)addr, 4, 0);
+ ldrexvalid = 1;
+ rp[op>>12 & 0x7] = *addr;
+ if(fpemudebug)
+ print("ldrex, r%ld = [r%ld]@0x%8.8p = 0x%8.8lux",
+ op>>12 & 0x7, op>>16 & 0x7, addr, rp[op>>12 & 0x7]);
+}
+
+void
+strex(ulong pc, ulong op, Ureg *ur)
+{
+ ulong *rp, rn, *rd, *addr;
+
+ USED(pc);
+
+ rp = (ulong*)ur;
+ rd = rp + (op>>16 & 0x7);
+ rn = rp[op>>0 & 0x7];
+ addr = (ulong*)*rd;
+ validaddr((ulong)addr, 4, 1);
+ splhi();
+ if(ldrexvalid){
+ if(fpemudebug)
+ print("strex valid, [r%ld]@0x%8.8p = r%ld = 0x%8.8lux",
+ op>>16 & 0x7, addr, op>>0 & 0x7, rn);
+ *addr = rn;
+ ldrexvalid = 0;
+ rp[op>>12 & 0x7] = 0;
+ }else{
+ if(fpemudebug)
+ print("strex invalid, r%ld = 1", op>>16 & 0x7);
+ rp[op>>12 & 0x7] = 1;
+ }
+ spllo();
+}
+
+struct {
+ ulong opc;
+ ulong mask;
+ void (*f)(ulong, ulong, Ureg*);
+} specialopc[] = {
+ { 0x01900f9f, 0x0ff00fff, ldrex },
+ { 0x01800f90, 0x0ff00ff0, strex },
+ { 0x0ed00100, 0x0ef08100, casemu },
+ { 0x00000000, 0x00000000, nil }
+};
+
+/*
+ * returns the number of FP instructions emulated
+ */
+int
+fpiarm(Ureg *ur)
+{
+ ulong op, o;
+ FPsave *ufp;
+ int i, n;
+
+ if(up == nil)
+ panic("fpiarm not in a process");
+ ufp = &up->fpsave;
+ /* because all the state is in the proc structure,
+ * it need not be saved/restored
+ */
+ if(up->fpstate != FPactive){
+// assert(sizeof(Internal) == sizeof(ufp->regs[0]));
+ up->fpstate = FPactive;
+ ufp->control = 0;
+ ufp->status = (0x01<<28)|(1<<12); /* software emulation, alternative C flag */
+ for(n = 0; n < 8; n++)
+ FR(ufp, n) = fpconst[0];
+ }
+ for(n=0; ;n++){
+ validaddr(ur->pc, 4, 0);
+ op = *(ulong*)(ur->pc);
+ if(fpemudebug)
+ print("%#lux: %#8.8lux ", ur->pc, op);
+ o = (op>>24) & 0xF;
+ if(condok(ur->psr, op>>28)){
+ for(i = 0; specialopc[i].f; i++)
+ if((op & specialopc[i].mask) == specialopc[i].opc)
+ break;
+ if(specialopc[i].f)
+ specialopc[i].f(ur->pc, op, ur);
+ else if((op & 0xF00) != 0x100 || o != 0xE && (o&~1) != 0xC)
+ break;
+ else
+ fpemu(ur->pc, op, ur, ufp);
+ }else if((op & 0xF00) != 0x100 || o != 0xE && (o&~1) != 0xC)
+ break;
+ ur->pc += 4;
+ }
+ if(fpemudebug) print("\n");
+ return n;
+}
diff --git a/sys/src/9/kw/fpimem.c b/sys/src/9/kw/fpimem.c
new file mode 100755
index 000000000..627ab6355
--- /dev/null
+++ b/sys/src/9/kw/fpimem.c
@@ -0,0 +1,136 @@
+#include "fpi.h"
+
+/*
+ * the following routines depend on memory format, not the machine
+ */
+
+void
+fpis2i(Internal *i, void *v)
+{
+ Single *s = v;
+
+ i->s = (*s & 0x80000000) ? 1: 0;
+ if((*s & ~0x80000000) == 0){
+ SetZero(i);
+ return;
+ }
+ i->e = ((*s>>23) & 0x00FF) - SingleExpBias + ExpBias;
+ i->h = (*s & 0x007FFFFF)<<(1+NGuardBits);
+ i->l = 0;
+ if(i->e)
+ i->h |= HiddenBit;
+ else
+ i->e++;
+}
+
+void
+fpid2i(Internal *i, void *v)
+{
+ Double *d = v;
+
+ i->s = (d->h & 0x80000000) ? 1: 0;
+ i->e = (d->h>>20) & 0x07FF;
+ i->h = ((d->h & 0x000FFFFF)<<(4+NGuardBits))|((d->l>>25) & 0x7F);
+ i->l = (d->l & 0x01FFFFFF)<<NGuardBits;
+ if(i->e)
+ i->h |= HiddenBit;
+ else
+ i->e++;
+}
+
+void
+fpiw2i(Internal *i, void *v)
+{
+ Word w, word = *(Word*)v;
+ short e;
+
+ if(word < 0){
+ i->s = 1;
+ word = -word;
+ }
+ else
+ i->s = 0;
+ if(word == 0){
+ SetZero(i);
+ return;
+ }
+ if(word > 0){
+ for (e = 0, w = word; w; w >>= 1, e++)
+ ;
+ } else
+ e = 32;
+ if(e > FractBits){
+ i->h = word>>(e - FractBits);
+ i->l = (word & ((1<<(e - FractBits)) - 1))<<(2*FractBits - e);
+ }
+ else {
+ i->h = word<<(FractBits - e);
+ i->l = 0;
+ }
+ i->e = (e - 1) + ExpBias;
+}
+
+void
+fpii2s(void *v, Internal *i)
+{
+ short e;
+ Single *s = (Single*)v;
+
+ fpiround(i);
+ if(i->h & HiddenBit)
+ i->h &= ~HiddenBit;
+ else
+ i->e--;
+ *s = i->s ? 0x80000000: 0;
+ e = i->e;
+ if(e < ExpBias){
+ if(e <= (ExpBias - SingleExpBias))
+ return;
+ e = SingleExpBias - (ExpBias - e);
+ }
+ else if(e >= (ExpBias + (SingleExpMax-SingleExpBias))){
+ *s |= SingleExpMax<<23;
+ return;
+ }
+ else
+ e = SingleExpBias + (e - ExpBias);
+ *s |= (e<<23)|(i->h>>(1+NGuardBits));
+}
+
+void
+fpii2d(void *v, Internal *i)
+{
+ Double *d = (Double*)v;
+
+ fpiround(i);
+ if(i->h & HiddenBit)
+ i->h &= ~HiddenBit;
+ else
+ i->e--;
+ i->l = ((i->h & GuardMask)<<25)|(i->l>>NGuardBits);
+ i->h >>= NGuardBits;
+ d->h = i->s ? 0x80000000: 0;
+ d->h |= (i->e<<20)|((i->h & 0x00FFFFFF)>>4);
+ d->l = (i->h<<28)|i->l;
+}
+
+void
+fpii2w(Word *word, Internal *i)
+{
+ Word w;
+ short e;
+
+ fpiround(i);
+ e = (i->e - ExpBias) + 1;
+ if(e <= 0)
+ w = 0;
+ else if(e > 31)
+ w = 0x7FFFFFFF;
+ else if(e > FractBits)
+ w = (i->h<<(e - FractBits))|(i->l>>(2*FractBits - e));
+ else
+ w = i->h>>(FractBits-e);
+ if(i->s)
+ w = -w;
+ *word = w;
+}
diff --git a/sys/src/9/kw/init9.s b/sys/src/9/kw/init9.s
new file mode 100755
index 000000000..1d7f2bec3
--- /dev/null
+++ b/sys/src/9/kw/init9.s
@@ -0,0 +1,25 @@
+/*
+ * This is the same as the C programme:
+ *
+ * void
+ * main(char* argv0)
+ * {
+ * startboot(argv0, &argv0);
+ * }
+ *
+ * It is in assembler because SB needs to be
+ * set and doing this in C drags in too many
+ * other routines.
+ */
+TEXT main(SB), 1, $8
+ MOVW $setR12(SB), R12 /* load the SB */
+ MOVW $boot(SB), R0
+
+ ADD $12, R13, R1 /* pointer to 0(FP) */
+
+ MOVW R0, 4(R13) /* pass argc, argv */
+ MOVW R1, 8(R13)
+
+ BL startboot(SB)
+_loop:
+ B _loop
diff --git a/sys/src/9/kw/io.h b/sys/src/9/kw/io.h
new file mode 100755
index 000000000..f1ad24689
--- /dev/null
+++ b/sys/src/9/kw/io.h
@@ -0,0 +1,425 @@
+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 */
+ BUSUNKNOWN = -1
+};
+
+#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 BUSBDF(tbdf) ((tbdf)&0x00FFFF00)
+
+/*
+ * PCI support code.
+ */
+enum { /* type 0 & 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 */
+};
+
+/* ccrb (base class code) values; controller types */
+enum {
+ Pcibcpci1 = 0, /* pci 1.0; no class codes defined */
+ Pcibcstore = 1, /* mass storage */
+ Pcibcnet = 2, /* network */
+ Pcibcdisp = 3, /* display */
+ Pcibcmmedia = 4, /* multimedia */
+ Pcibcmem = 5, /* memory */
+ Pcibcbridge = 6, /* bridge */
+ Pcibccomm = 7, /* simple comms (e.g., serial) */
+ Pcibcbasesys = 8, /* base system */
+ Pcibcinput = 9, /* input */
+ Pcibcdock = 0xa, /* docking stations */
+ Pcibcproc = 0xb, /* processors */
+ Pcibcserial = 0xc, /* serial bus (e.g., USB) */
+ Pcibcwireless = 0xd, /* wireless */
+ Pcibcintell = 0xe, /* intelligent i/o */
+ Pcibcsatcom = 0xf, /* satellite comms */
+ Pcibccrypto = 0x10, /* encryption/decryption */
+ Pcibcdacq = 0x11, /* data acquisition & signal proc. */
+};
+
+/* ccru (sub-class code) values; common cases only */
+enum {
+ /* mass storage */
+ Pciscscsi = 0, /* SCSI */
+ Pciscide = 1, /* IDE (ATA) */
+
+ /* network */
+ Pciscether = 0, /* Ethernet */
+
+ /* display */
+ Pciscvga = 0, /* VGA */
+ Pciscxga = 1, /* XGA */
+ Pcisc3d = 2, /* 3D */
+
+ /* bridges */
+ Pcischostpci = 0, /* host/pci */
+ Pciscpcicpci = 1, /* pci/pci */
+
+ /* simple comms */
+ Pciscserial = 0, /* 16450, etc. */
+ Pciscmultiser = 1, /* multiport serial */
+
+ /* serial bus */
+ Pciscusb = 3, /* USB */
+};
+
+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;
+struct Pcidev
+{
+ int tbdf; /* type+bus+device+function */
+ ushort vid; /* vendor ID */
+ ushort did; /* device ID */
+
+ ushort pcr;
+
+ uchar rid;
+ uchar ccrp;
+ uchar ccru;
+ uchar ccrb;
+ uchar cls;
+ uchar ltr;
+
+ struct {
+ ulong bar; /* base address */
+ int size;
+ } mem[6];
+
+ struct {
+ ulong bar;
+ int size;
+ } rom;
+ 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;
+
+ int pmrb; /* power management register block */
+};
+
+#define PCIWINDOW 0
+#define PCIWADDR(va) (PADDR(va)+PCIWINDOW)
+
+/*
+ * Kirkwood stuff
+ */
+
+enum {
+ AddrEfuse = PHYSIO+0x1008c,
+ Addrpci = PHYSIO+0x40000, /* for registers below */
+ Addrpcibase = PHYSIO+0x41800, /* for registers below */
+ AddrMpp = PHYSIO+0x10000,
+ AddrSdio = PHYSIO+0x90000,
+};
+
+enum {
+ Socrevz0,
+ Socreva0 = 2,
+ Socreva1,
+};
+
+enum {
+ /* registers; if we actually use these, change to soc.pci(base)->reg */
+ PciBAR0 = Addrpcibase + 4, /* base address */
+ PciBAR1 = Addrpcibase + 8,
+
+ PciCP = Addrpci + 0x64, /* capabilities pointer */
+
+ PciINTL = Addrpci + 0x3c, /* interrupt line */
+ PciINTP = PciINTL + 1, /* interrupt pin */
+};
+
+/*
+ * interrupt stuff
+ */
+
+enum {
+ Irqlo, Irqhi, Irqbridge,
+};
+
+enum {
+ /* main interrupt cause low register bit #s (LE) */
+ IRQ0hisum, /* summary of main intr high cause reg */
+ IRQ0bridge,
+ IRQ0h2cdoorbell,
+ IRQ0c2hdoorbell,
+ _IRQ0reserved0,
+ IRQ0xor0chan0,
+ IRQ0xor0chan1,
+ IRQ0xor1chan0,
+ IRQ0xor1chan1,
+ IRQ0pex0int, /* pex = pci-express */
+ _IRQ0reserved1,
+ IRQ0gbe0sum,
+ IRQ0gbe0rx,
+ IRQ0gbe0tx,
+ IRQ0gbe0misc,
+ IRQ0gbe1sum,
+ IRQ0gbe1rx,
+ IRQ0gbe1tx,
+ IRQ0gbe1misc,
+ IRQ0usb0,
+ _IRQ0reserved2,
+ IRQ0sata,
+ IRQ0crypto,
+ IRQ0spi,
+ IRQ0audio,
+ _IRQ0reserved3,
+ IRQ0ts0,
+ _IRQ0reserved4,
+ IRQ0sdio,
+ IRQ0twsi,
+ IRQ0avb,
+ IRQ0tdm,
+
+ /* main interrupt cause high register bit #s (LE) */
+ _IRQ1reserved0 = 0,
+ IRQ1uart0,
+ IRQ1uart1,
+ IRQ1gpiolo0,
+ IRQ1gpiolo1,
+ IRQ1gpiolo2,
+ IRQ1gpiolo3,
+ IRQ1gpiohi0,
+ IRQ1gpiohi1,
+ IRQ1gpiohi2,
+ IRQ1gpiohi3,
+ IRQ1xor0err,
+ IRQ1xor1err,
+ IRQ1pex0err,
+ _IRQ1reserved1,
+ IRQ1gbe0err,
+ IRQ1gbe1err,
+ IRQ1usberr,
+ IRQ1cryptoerr,
+ IRQ1audioerr,
+ _IRQ1reserved2,
+ _IRQ1reserved3,
+ IRQ1rtc,
+
+ /* bridged-interrupt causes */
+ IRQcpuself = 0,
+ IRQcputimer0,
+ IRQcputimer1,
+ IRQcputimerwd,
+ IRQaccesserr,
+};
+
+/*
+ * interrupt controller
+ */
+typedef struct IntrReg IntrReg;
+struct IntrReg
+{
+ struct {
+ ulong irq; /* main intr cause reg (ro) */
+ ulong irqmask;
+ ulong fiqmask;
+ ulong epmask;
+ } lo, hi;
+};
+
+/*
+ * CPU control & status (archkw.c and trap.c)
+ */
+typedef struct CpucsReg CpucsReg;
+struct CpucsReg
+{
+ ulong cpucfg;
+ ulong cpucsr;
+ ulong rstout;
+ ulong softreset;
+ ulong irq; /* mbus(-l) bridge interrupt cause */
+ ulong irqmask; /* ⋯ mask */
+ ulong mempm; /* memory power mgmt. control */
+ ulong clockgate; /* clock enable bits */
+ ulong biu;
+ ulong pad0;
+ ulong l2cfg; /* turn l2 cache on or off, set coherency */
+ ulong pad1[2];
+ ulong l2tm0;
+ ulong l2tm1;
+ ulong pad2[2];
+ ulong l2pm;
+ ulong ram0;
+ ulong ram1;
+ ulong ram2;
+ ulong ram3;
+};
+
+enum {
+ /* cpucfg bits */
+ Cfgvecinithi = 1<<1, /* boot at 0xffff0000, not 0; default 1 */
+ Cfgbigendreset = 3<<1, /* init. as big-endian at reset; default 0 */
+ Cfgiprefetch = 1<<16, /* instruction prefetch enable */
+ Cfgdprefetch = 1<<17, /* data prefetch enable */
+
+ /* cpucsr bits */
+ Reset = 1<<1, /* reset cpu core */
+
+ /* rstout bits */
+ RstoutPex = 1<<0, /* assert RSTOUTn at pci-e reset */
+ RstoutWatchdog = 1<<1, /* assert RSTOUTn at watchdog timeout */
+ RstoutSoft = 1<<2, /* assert RSTOUTn at sw reset */
+
+ /* softreset bits */
+ ResetSystem = 1<<0, /* assert RSTOUTn pin on SoftRstOutEn */
+
+ /* l2cfg bits */
+ L2ecc = 1<<2,
+ L2exists = 1<<3, /* l2 cache doesn't ignore cpu */
+ L2writethru = 1<<4, /* always WT, else see PTE C & B */
+};
+
+enum {
+ /* from 88f6281 func'l specs (MV-S104860-00), tables 2 & 3, chapter 2 */
+ Targdram = 0, /* ddr sdram */
+ Targflash = 1,
+ Targcesasram = 3, /* security accelerator sram */
+
+ /* attributes */
+ Attrcs0 = 0xe, /* chip select 0 (low dram) */
+ Attrcs1 = 0xd, /* chip select 1 (high dram) */
+ Attrbootrom = 0x1d,
+ Attrspi = 0x1e,
+ Attrnand = 0x2f,
+
+ Winenable = 1<<0,
+};
+
+typedef struct Pciex Pciex;
+struct Pciex {
+ ushort venid; /* 0x11ab means Marvell */
+ ushort devid; /* 0x6281 means 6281 */
+ ulong csr;
+ ulong revid;
+ ulong bistcache; /* bist hdr type & cache-line size */
+ ulong bar0;
+ ulong bar0hi;
+ ulong bar1;
+ ulong bar1hi;
+ ulong bar2;
+ ulong bar2hi;
+ ulong _pad0;
+ ushort ssvenid; /* 0x11ab means Marvell */
+ ushort ssdevid; /* 0x11ab means Marvell */
+ ulong rombar;
+ ulong caplist;
+ ulong _pad1;
+ ulong intrpinline; /* interrupt pin & line */
+ ulong pmcap; /* power mgmt. capability header */
+ ulong pmcsr; /* power mgmt. control & status */
+ ulong _pad2[2];
+ ulong msictl; /* msi message control */
+ ulong msiaddr;
+ ulong msiaddrhi;
+ ulong msidata;
+ ulong cap;
+ ulong devcap;
+ ulong devcsr;
+ ulong linkcap;
+ ulong linkcsr;
+
+ uchar _pad[0x40100-0x40074];
+ ulong errrep; /* advanced error report header */
+ ulong uncorrerr; /* uncorrectable error status */
+ ulong uncorrerrmask; /* uncorrectable error mask */
+ ulong uncorrerrsev; /* uncorrectable error severity */
+ ulong correrr; /* correctable error status */
+ ulong correrrmask; /* correctable error mask */
+ ulong errcap; /* advanced error capability & ctl. */
+ ulong hdrlog[4]; /* header log */
+ /* continues with more rubbish at 0x41a00. some day... */
+};
diff --git a/sys/src/9/kw/l.s b/sys/src/9/kw/l.s
new file mode 100755
index 000000000..f538b5d8d
--- /dev/null
+++ b/sys/src/9/kw/l.s
@@ -0,0 +1,774 @@
+/*
+ * sheevaplug machine assist
+ * arm926ej-s processor at 1.2GHz
+ *
+ * loader uses R11 as scratch.
+ * R9 and R10 are used for `extern register' variables.
+ *
+ * ARM v7 arch. ref. man. (I know, this is v5) §B1.3.3 that
+ * we don't need barriers around moves to CPSR. The ARM v6 manual
+ * seems to be silent on the subject.
+ */
+#include "arm.s"
+
+/*
+ * MCR and MRC are counter-intuitively named.
+ * MCR coproc, opcode1, Rd, CRn, CRm[, opcode2] # arm -> coproc
+ * MRC coproc, opcode1, Rd, CRn, CRm[, opcode2] # coproc -> arm
+ */
+
+/*
+ * Entered here from Das U-Boot with MMU disabled.
+ * Until the MMU is enabled it is OK to call functions provided
+ * they are within ±32MiB relative and do not require any
+ * local variables or more than one argument (i.e. there is
+ * no stack).
+ */
+TEXT _start(SB), 1, $-4
+ MOVW $setR12(SB), R12 /* load the SB */
+_main:
+ /* SVC mode, interrupts disabled */
+ MOVW $(PsrDirq|PsrDfiq|PsrMsvc), R1
+ MOVW R1, CPSR
+ BARRIERS
+
+ /*
+ * disable the MMU & caches,
+ * switch to system permission & 32-bit addresses.
+ */
+ MOVW $(CpCsystem|CpCd32|CpCi32), R1
+ MCR CpSC, 0, R1, C(CpCONTROL), C(0)
+ ISB
+
+ /*
+ * disable the Sheevaplug's L2 cache, invalidate all caches
+ */
+
+ /* flush caches. 926ejs manual says we have to do it iteratively. */
+_dwbinv0:
+ MRC CpSC, 0, PC, C(CpCACHE), C(CpCACHEwbi), CpCACHEtest
+ BNE _dwbinv0
+ /* drain L1 write buffer, also drains L2 eviction buffer on sheeva */
+ BARRIERS
+
+ /* make the l2 cache pay attention */
+ MOVW $(PHYSIO+0x20100), R1 /* CPUCSREG */
+ MOVW (4*10)(R1), R2
+ ORR $(1<<3), R2 /* cpu->l2cfg |= L2exists */
+ MOVW R2, (4*10)(R1)
+ ISB
+
+ /* invalidate l2 cache */
+ MCR CpSC, CpL2, R0, C(CpTESTCFG), C(CpTCl2inv), CpTCl2all
+ ISB
+
+ /* disable l2 cache. do this while l1 caches are off */
+ MRC CpSC, CpL2, R1, C(CpTESTCFG), C(CpTCl2cfg), CpTCl2conf
+ /* disabling write allocation is probably for cortex-a8 errata 460075 */
+ /* l2 off, no wr alloc, no streaming */
+ BIC $(CpTCl2ena | CpTCl2wralloc | CpTCldcstream), R1
+ MCR CpSC, CpL2, R1, C(CpTESTCFG), C(CpTCl2cfg), CpTCl2conf
+ BARRIERS
+
+ /* flush caches. 926ejs manual says we have to do it iteratively. */
+_dwbinv1:
+ MRC CpSC, 0, PC, C(CpCACHE), C(CpCACHEwbi), CpCACHEtest
+ BNE _dwbinv1
+ BARRIERS
+
+WAVE('\r')
+ /* clear Mach */
+ MOVW $PADDR(MACHADDR), R4 /* address of Mach */
+_machZ:
+ MOVW R0, (R4)
+ ADD $4, R4 /* bump PTE address */
+ CMP.S $PADDR(L1+L1X(0)), R4
+ BNE _machZ
+
+ /*
+ * set up the MMU page table
+ */
+
+ /* clear all PTEs first, to provide a default */
+WAVE('\n')
+ MOVW $PADDR(L1+L1X(0)), R4 /* address of PTE for 0 */
+_ptenv0:
+ ZEROPTE()
+ CMP.S $PADDR(L1+16*KiB), R4
+ BNE _ptenv0
+
+ /* double map of PHYSDRAM, KZERO to PHYSDRAM for first few MBs */
+ MOVW $PTEDRAM, R2 /* PTE bits */
+ MOVW $PHYSDRAM, R3 /* pa */
+ MOVW $PADDR(L1+L1X(PHYSDRAM)), R4 /* address of PTE for PHYSDRAM */
+ MOVW $16, R5
+_ptdbl:
+ FILLPTE()
+ SUB.S $1, R5
+ BNE _ptdbl
+
+ /*
+ * back up and fill in PTEs for memory at KZERO
+ * there is 1 bank of 512MB of SDRAM at PHYSDRAM
+ */
+ MOVW $PTEDRAM, R2 /* PTE bits */
+ MOVW $PHYSDRAM, R3
+ MOVW $PADDR(L1+L1X(KZERO)), R4 /* start with PTE for KZERO */
+ MOVW $512, R5 /* inner loop count */
+_ptekrw: /* set PTEs for 512MiB */
+ FILLPTE()
+ SUB.S $1, R5
+ BNE _ptekrw
+
+ /*
+ * back up and fill in PTE for MMIO
+ */
+ MOVW $PTEIO, R2 /* PTE bits */
+ MOVW $PHYSIO, R3
+ MOVW $PADDR(L1+L1X(VIRTIO)), R4 /* start with PTE for VIRTIO */
+ FILLPTE()
+
+ /* mmu.c sets up the vectors later */
+
+ /*
+ * set up a temporary stack; avoid data & bss segments
+ */
+ MOVW $(PHYSDRAM | (128*1024*1024)), R13
+
+WAVE('P')
+ /* set the domain access control */
+ MOVW $Client, R0
+ BL dacput(SB)
+
+ /* set the translation table base */
+ MOVW $PADDR(L1), R0
+ BL ttbput(SB)
+
+ MOVW $0, R0
+ BL pidput(SB) /* paranoia */
+
+ /* the little dance to turn the MMU & caches on */
+WAVE('l')
+ BL cacheuwbinv(SB)
+ BL mmuinvalidate(SB)
+ BL mmuenable(SB)
+
+WAVE('a')
+ /* warp the PC into the virtual map */
+ MOVW $KZERO, R0
+ BL _r15warp(SB)
+
+ /*
+ * now running at KZERO+something!
+ */
+
+ MOVW $setR12(SB), R12 /* reload the SB */
+
+ /*
+ * set up temporary stack again, in case we've just switched
+ * to a new register set.
+ */
+ MOVW $(KZERO|(128*1024*1024)), R13
+
+ /* can now execute arbitrary C code */
+
+ BL cacheuwbinv(SB)
+
+WAVE('n')
+ /* undo double map of 0, KZERO */
+ MOVW $PADDR(L1+L1X(0)), R4 /* address of PTE for 0 */
+ MOVW $0, R0
+ MOVW $16, R5
+_ptudbl:
+ MOVW R0, (R4)
+ ADD $4, R4 /* bump PTE address */
+ ADD $MiB, R0 /* bump pa */
+ SUB.S $1, R5
+ BNE _ptudbl
+ BARRIERS
+ MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvd), CpTLBinvse
+ MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv
+ BARRIERS
+
+WAVE(' ')
+ /* pass Mach to main and set up the stack */
+ MOVW $(MACHADDR), R0 /* Mach */
+ MOVW R0, R13
+ ADD $(MACHSIZE), R13 /* stack pointer */
+ SUB $4, R13 /* space for link register */
+
+ BL main(SB) /* void main(Mach*) */
+ /* fall through */
+
+
+/* not used */
+TEXT _reset(SB), 1, $-4
+ /* turn the caches off */
+ MOVW $(PsrDirq|PsrDfiq|PsrMsvc), R0
+ MOVW R0, CPSR
+ BARRIERS
+ BL cacheuwbinv(SB)
+ MRC CpSC, 0, R0, C(CpCONTROL), C(0)
+ BIC $(CpCwb|CpCicache|CpCdcache|CpCalign), R0
+ MCR CpSC, 0, R0, C(CpCONTROL), C(0)
+ BARRIERS
+WAVE('R')
+
+ /* redo double map of 0, KZERO */
+ MOVW $(L1+L1X(0)), R4 /* address of PTE for 0 */
+ MOVW $PTEDRAM, R2 /* PTE bits */
+ MOVW $0, R3
+ MOVW $16, R5
+_ptrdbl:
+ ORR R3, R2, R1 /* first identity-map 0 to 0, etc. */
+ MOVW R1, (R4)
+ ADD $4, R4 /* bump PTE address */
+ ADD $MiB, R3 /* bump pa */
+ SUB.S $1, R5
+ BNE _ptrdbl
+
+ BARRIERS
+WAVE('e')
+ MOVW $0, R0
+ MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvd), CpTLBinv
+ MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv
+ BARRIERS
+
+ /* back to 29- or 26-bit addressing, mainly for SB */
+ MRC CpSC, 0, R0, C(CpCONTROL), C(0)
+ BIC $(CpCd32|CpCi32), R0
+ MCR CpSC, 0, R0, C(CpCONTROL), C(0)
+ BARRIERS
+
+ /* turn the MMU off */
+ MOVW $PHYSDRAM, R0
+ BL _r15warp(SB)
+ BL mmuinvalidate(SB)
+ BL mmudisable(SB)
+
+WAVE('s')
+ /* set new reset vector */
+ MOVW $0, R2
+ MOVW $0xe59ff018, R3 /* MOVW 0x18(R15), R15 */
+ MOVW R3, (R2)
+WAVE('e')
+
+ MOVW $PHYSBOOTROM, R3
+ MOVW R3, 0x20(R2) /* where $0xe59ff018 jumps to */
+ BARRIERS
+WAVE('t')
+WAVE('\r')
+WAVE('\n')
+
+ /* ...and jump to it */
+ MOVW R2, R15 /* software reboot */
+_limbo: /* should not get here... */
+ B _limbo /* ... and can't get out */
+ BL _div(SB) /* hack to load _div, etc. */
+
+TEXT _r15warp(SB), 1, $-4
+ BIC $KSEGM, R14
+ ORR R0, R14
+ BIC $KSEGM, R13
+ ORR R0, R13
+ RET
+
+/* clobbers R1, R6 */
+TEXT myputc(SB), 1, $-4
+ MOVW $PHYSCONS, R6
+_busy:
+ MOVW 20(R6), R1
+ BIC.S $~(1<<5), R1 /* (x->lsr & LSRthre) == 0? */
+ BEQ _busy
+ MOVW R3, (R6) /* print */
+ ISB
+ RET
+
+/*
+ * l1 caches
+ */
+
+TEXT l1cacheson(SB), 1, $-4
+ MOVW CPSR, R5
+ ORR $(PsrDirq|PsrDfiq), R5, R4
+ MOVW R4, CPSR /* splhi */
+
+ MRC CpSC, 0, R0, C(CpCONTROL), C(0)
+ ORR $(CpCdcache|CpCicache|CpCwb), R0
+ MCR CpSC, 0, R0, C(CpCONTROL), C(0)
+ BARRIERS
+
+ MOVW R5, CPSR /* splx */
+ RET
+
+TEXT l1cachesoff(SB), 1, $-4
+ MOVM.DB.W [R14], (SP) /* save lr on stack */
+
+ MOVW CPSR, R5
+ ORR $(PsrDirq|PsrDfiq), R5, R4
+ MOVW R4, CPSR /* splhi */
+
+ BL cacheuwbinv(SB)
+
+ MRC CpSC, 0, R0, C(CpCONTROL), C(0)
+ BIC $(CpCdcache|CpCicache|CpCwb), R0
+ MCR CpSC, 0, R0, C(CpCONTROL), C(0)
+ BARRIERS
+
+ MOVW R5, CPSR /* splx */
+ MOVM.IA.W (SP), [R14] /* restore lr */
+ RET
+
+/*
+ * cache* functions affect only the L1 caches, which are VIVT.
+ */
+
+TEXT cachedwb(SB), 1, $-4 /* D writeback */
+ MOVW CPSR, R3 /* splhi */
+ ORR $(PsrDirq), R3, R1
+ MOVW R1, CPSR
+
+ BARRIERS /* force outstanding stores to cache */
+ /* keep writing back dirty cache lines until no more exist */
+_dwb:
+ MRC CpSC, 0, PC, C(CpCACHE), C(CpCACHEwb), CpCACHEtest
+ BNE _dwb
+ /* drain L1 write buffer, also drains L2 eviction buffer on sheeva */
+ BARRIERS
+
+ MOVW R3, CPSR /* splx */
+ RET
+
+TEXT cachedwbse(SB), 1, $-4 /* D writeback SE */
+ MOVW R0, R2 /* first arg: address */
+
+ MOVW CPSR, R3 /* splhi */
+ ORR $(PsrDirq), R3, R1
+ MOVW R1, CPSR
+
+ BARRIERS /* force outstanding stores to cache */
+ MOVW 4(FP), R1 /* second arg: size */
+
+// CMP.S $(4*1024), R1
+// BGT _dwb
+ ADD R2, R1
+ BIC $(CACHELINESZ-1), R2
+_dwbse:
+ MCR CpSC, 0, R2, C(CpCACHE), C(CpCACHEwb), CpCACHEse
+ ADD $CACHELINESZ, R2
+ CMP.S R2, R1
+ BGT _dwbse
+ /* drain L1 write buffer, also drains L2 eviction buffer on sheeva */
+ BARRIERS
+
+ MOVW R3, CPSR /* splx */
+ RET
+
+TEXT cachedwbinv(SB), 1, $-4 /* D writeback+invalidate */
+ MOVW CPSR, R3 /* splhi */
+ ORR $(PsrDirq), R3, R1
+ MOVW R1, CPSR
+
+ BARRIERS /* force outstanding stores to cache */
+ /* keep writing back dirty cache lines until no more exist */
+_dwbinv:
+ MRC CpSC, 0, PC, C(CpCACHE), C(CpCACHEwbi), CpCACHEtest
+ BNE _dwbinv
+ /* drain L1 write buffer, also drains L2 eviction buffer on sheeva */
+ BARRIERS
+
+ MOVW R3, CPSR /* splx */
+ RET
+
+TEXT cachedwbinvse(SB), 1, $-4 /* D writeback+invalidate SE */
+ MOVW R0, R2 /* first arg: address */
+
+ MOVW CPSR, R3 /* splhi */
+ ORR $(PsrDirq), R3, R1
+ MOVW R1, CPSR
+
+ BARRIERS /* force outstanding stores to cache */
+ MOVW 4(FP), R1 /* second arg: size */
+
+ DSB
+// CMP.S $(4*1024), R1
+// BGT _dwbinv
+ ADD R2, R1
+ BIC $(CACHELINESZ-1), R2
+_dwbinvse:
+ MCR CpSC, 0, R2, C(CpCACHE), C(CpCACHEwbi), CpCACHEse
+ ADD $CACHELINESZ, R2
+ CMP.S R2, R1
+ BGT _dwbinvse
+ /* drain L1 write buffer, also drains L2 eviction buffer on sheeva */
+ BARRIERS
+
+ MOVW R3, CPSR /* splx */
+ RET
+
+TEXT cachedinvse(SB), 1, $-4 /* D invalidate SE */
+ MOVW R0, R2 /* first arg: address */
+
+ MOVW CPSR, R3 /* splhi */
+ ORR $(PsrDirq), R3, R1
+ MOVW R1, CPSR
+
+ MOVW 4(FP), R1 /* second arg: size */
+
+ DSB
+// CMP.S $(4*1024), R1
+// BGT _dinv
+ ADD R2, R1
+ BIC $(CACHELINESZ-1), R2
+_dinvse:
+ MCR CpSC, 0, R2, C(CpCACHE), C(CpCACHEinvd), CpCACHEse
+ ADD $CACHELINESZ, R2
+ CMP.S R2, R1
+ BGT _dinvse
+ /* drain L1 write buffer, also drains L2 eviction buffer on sheeva */
+ BARRIERS
+
+ MOVW R3, CPSR /* splx */
+ RET
+
+TEXT cacheuwbinv(SB), 1, $-4 /* D+I writeback+invalidate */
+ MOVW CPSR, R3 /* splhi */
+ ORR $(PsrDirq), R3, R1
+ MOVW R1, CPSR
+
+ BARRIERS /* force outstanding stores to cache */
+ /* keep writing back dirty cache lines until no more exist */
+_uwbinv: /* D writeback+invalidate */
+ MRC CpSC, 0, PC, C(CpCACHE), C(CpCACHEwbi), CpCACHEtest
+ BNE _uwbinv
+ /* drain L1 write buffer, also drains L2 eviction buffer on sheeva */
+ BARRIERS
+
+ MOVW $0, R0 /* I invalidate */
+ MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEall
+ /* drain L1 write buffer, also drains L2 eviction buffer on sheeva */
+ BARRIERS
+
+ MOVW R3, CPSR /* splx */
+ RET
+
+TEXT cacheiinv(SB), 1, $-4 /* I invalidate */
+ BARRIERS
+ MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEall
+ /* drain L1 write buffer, also drains L2 eviction buffer on sheeva */
+ BARRIERS
+ RET
+
+TEXT cachedinv(SB), 1, $-4 /* D invalidate */
+_dinv:
+ BARRIERS
+ MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvd), CpCACHEall
+ /* drain L1 write buffer, also drains L2 eviction buffer on sheeva */
+ BARRIERS
+ RET
+
+/*
+ * l2 cache
+ *
+ * these functions assume that the necessary l1 cache operations have been
+ * or will be done explicitly by the caller.
+ */
+
+/* enable l2 cache in config coproc. reg. do this while l1 caches are off. */
+TEXT l2cachecfgon(SB), 1, $-4
+ BARRIERS
+ MCR CpSC, CpL2, R0, C(CpTESTCFG), C(CpTCl2inv), CpTCl2all
+ BARRIERS
+
+ MRC CpSC, CpL2, R1, C(CpTESTCFG), C(CpTCl2cfg), CpTCl2conf
+ ORR $(CpTCl2ena | CpTCl2prefdis), R1 /* l2 on, prefetch off */
+ MCR CpSC, CpL2, R1, C(CpTESTCFG), C(CpTCl2cfg), CpTCl2conf
+ BARRIERS
+ RET
+
+/* disable l2 cache in config coproc. reg. do this while l1 caches are off. */
+TEXT l2cachecfgoff(SB), 1, $-4
+ BARRIERS
+ MRC CpSC, CpL2, R1, C(CpTESTCFG), C(CpTCl2cfg), CpTCl2conf
+ BIC $CpTCl2ena, R1
+ MCR CpSC, CpL2, R1, C(CpTESTCFG), C(CpTCl2cfg), CpTCl2conf
+ BARRIERS
+
+ MCR CpSC, CpL2, R0, C(CpTESTCFG), C(CpTCl2inv), CpTCl2all
+ BARRIERS
+ RET
+
+TEXT l2cacheuwb(SB), 1, $-4 /* L2 unified writeback */
+ MCR CpSC, CpL2, R0, C(CpTESTCFG), C(CpTCl2flush), CpTCl2all
+ ISB
+ RET
+
+TEXT l2cacheuwbse(SB), 1, $-4 /* L2 unified writeback SE */
+ MOVW R0, R2 /* first arg: address */
+
+ MOVW CPSR, R3 /* splhi */
+ ORR $(PsrDirq), R3, R1
+ MOVW R1, CPSR
+
+ MOVW 4(FP), R1 /* second arg: size */
+
+ ADD R2, R1
+ BIC $(CACHELINESZ-1), R2
+_l2wbse:
+ MCR CpSC, CpL2, R2, C(CpTESTCFG), C(CpTCl2flush), CpTCl2seva
+ ADD $CACHELINESZ, R2
+ CMP.S R2, R1
+ BGT _l2wbse
+ ISB
+
+ MOVW R3, CPSR /* splx */
+ RET
+
+TEXT l2cacheuwbinv(SB), 1, $-4 /* L2 unified writeback+invalidate */
+ MOVW CPSR, R3 /* splhi */
+ ORR $(PsrDirq), R3, R1
+ MOVW R1, CPSR
+
+ MCR CpSC, CpL2, R0, C(CpTESTCFG), C(CpTCl2flush), CpTCl2all
+ ISB
+ MCR CpSC, CpL2, R0, C(CpTESTCFG), C(CpTCl2inv), CpTCl2all
+ ISB
+
+ MOVW R3, CPSR /* splx */
+ RET
+
+TEXT l2cacheuwbinvse(SB), 1, $-4 /* L2 unified writeback+invalidate SE */
+ MOVW R0, R2 /* first arg: address */
+
+ MOVW CPSR, R3 /* splhi */
+ ORR $(PsrDirq), R3, R1
+ MOVW R1, CPSR
+
+ MOVW 4(FP), R1 /* second arg: size */
+
+ ADD R2, R1
+ BIC $(CACHELINESZ-1), R2
+_l2wbinvse:
+ MCR CpSC, CpL2, R2, C(CpTESTCFG), C(CpTCl2flush), CpTCl2seva
+ ISB
+ MCR CpSC, CpL2, R2, C(CpTESTCFG), C(CpTCl2inv), CpTCl2seva
+ ADD $CACHELINESZ, R2
+ CMP.S R2, R1
+ BGT _l2wbinvse
+ ISB
+
+ MOVW R3, CPSR /* splx */
+ RET
+
+TEXT l2cacheuinv(SB), 1, $-4 /* L2 unified invalidate */
+ MCR CpSC, CpL2, R0, C(CpTESTCFG), C(CpTCl2inv), CpTCl2all
+ ISB
+ RET
+
+TEXT l2cacheuinvse(SB), 1, $-4 /* L2 unified invalidate SE */
+ MOVW R0, R2 /* first arg: address */
+
+ MOVW CPSR, R3 /* splhi */
+ ORR $(PsrDirq), R3, R1
+ MOVW R1, CPSR
+
+ MOVW 4(FP), R1 /* second arg: size */
+
+ ADD R2, R1
+ BIC $(CACHELINESZ-1), R2
+_l2invse:
+ MCR CpSC, CpL2, R2, C(CpTESTCFG), C(CpTCl2inv), CpTCl2seva
+ ADD $CACHELINESZ, R2
+ CMP.S R2, R1
+ BGT _l2invse
+ ISB
+
+ MOVW R3, CPSR /* splx */
+ RET
+
+/*
+ * enable mmu, i and d caches, and high vector
+ */
+TEXT mmuenable(SB), 1, $-4
+ MRC CpSC, 0, R0, C(CpCONTROL), C(0)
+ ORR $(CpChv|CpCmmu|CpCdcache|CpCicache|CpCwb|CpCsystem), R0
+ BIC $(CpCrom), R0
+ MCR CpSC, 0, R0, C(CpCONTROL), C(0)
+ BARRIERS
+ RET
+
+TEXT mmudisable(SB), 1, $-4
+ MRC CpSC, 0, R0, C(CpCONTROL), C(0)
+ BIC $(CpChv|CpCmmu|CpCdcache|CpCicache|CpCwb), R0
+ MCR CpSC, 0, R0, C(CpCONTROL), C(0)
+ BARRIERS
+ RET
+
+TEXT mmuinvalidate(SB), 1, $-4 /* invalidate all */
+ MOVW $0, R0
+ MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv
+ BARRIERS
+ RET
+
+TEXT mmuinvalidateaddr(SB), 1, $-4 /* invalidate single entry */
+ MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinvse
+ BARRIERS
+ RET
+
+TEXT cpidget(SB), 1, $-4 /* main ID */
+ MRC CpSC, 0, R0, C(CpID), C(0), CpIDid
+ RET
+
+TEXT cpctget(SB), 1, $-4 /* cache type */
+ MRC CpSC, 0, R0, C(CpID), C(0), CpIDct
+ RET
+
+TEXT controlget(SB), 1, $-4 /* control */
+ MRC CpSC, 0, R0, C(CpCONTROL), C(0)
+ RET
+
+TEXT ttbget(SB), 1, $-4 /* translation table base */
+ MRC CpSC, 0, R0, C(CpTTB), C(0)
+ RET
+
+TEXT ttbput(SB), 1, $-4 /* translation table base */
+ MCR CpSC, 0, R0, C(CpTTB), C(0)
+ ISB
+ RET
+
+TEXT dacget(SB), 1, $-4 /* domain access control */
+ MRC CpSC, 0, R0, C(CpDAC), C(0)
+ RET
+
+TEXT dacput(SB), 1, $-4 /* domain access control */
+ MCR CpSC, 0, R0, C(CpDAC), C(0)
+ ISB
+ RET
+
+TEXT fsrget(SB), 1, $-4 /* fault status */
+ MRC CpSC, 0, R0, C(CpFSR), C(0)
+ RET
+
+TEXT farget(SB), 1, $-4 /* fault address */
+ MRC CpSC, 0, R0, C(CpFAR), C(0x0)
+ RET
+
+TEXT pidget(SB), 1, $-4 /* address translation pid */
+ MRC CpSC, 0, R0, C(CpPID), C(0x0)
+ RET
+
+TEXT pidput(SB), 1, $-4 /* address translation pid */
+ MCR CpSC, 0, R0, C(CpPID), C(0x0)
+ ISB
+ RET
+
+TEXT splhi(SB), 1, $-4
+ MOVW $(MACHADDR+4), R2 /* save caller pc in Mach */
+ MOVW R14, 0(R2)
+
+ MOVW CPSR, R0 /* turn off interrupts */
+ ORR $(PsrDirq), R0, R1
+ MOVW R1, CPSR
+ RET
+
+TEXT spllo(SB), 1, $-4
+ MOVW CPSR, R0
+ BIC $(PsrDirq), R0, R1
+ MOVW R1, CPSR
+ RET
+
+TEXT splx(SB), 1, $-4
+ MOVW $(MACHADDR+0x04), R2 /* save caller pc in Mach */
+ MOVW R14, 0(R2)
+
+ MOVW R0, R1 /* reset interrupt level */
+ MOVW CPSR, R0
+ MOVW R1, CPSR
+ RET
+
+TEXT splxpc(SB), 1, $-4 /* for iunlock */
+ MOVW R0, R1
+ MOVW CPSR, R0
+ MOVW R1, CPSR
+ RET
+
+TEXT spldone(SB), 1, $0
+ RET
+
+TEXT islo(SB), 1, $-4
+ MOVW CPSR, R0
+ AND $(PsrDirq), R0
+ EOR $(PsrDirq), R0
+ RET
+
+TEXT splfhi(SB), $-4
+ MOVW CPSR, R0
+ ORR $(PsrDfiq|PsrDirq), R0, R1
+ MOVW R1, CPSR
+ RET
+
+//TEXT splflo(SB), $-4
+// MOVW CPSR, R0
+// BIC $(PsrDfiq), R0, R1
+// MOVW R1, CPSR
+// RET
+
+TEXT tas(SB), $-4
+TEXT _tas(SB), $-4
+ MOVW R0,R1
+ MOVW $1,R0
+ SWPW R0,(R1) /* fix: deprecated in armv7 */
+ RET
+
+//TEXT tas32(SB), 1, $-4
+// MOVW R0, R1
+// MOVW $0xDEADDEAD, R0
+// MOVW R0, R3
+// SWPW R0, (R1)
+// CMP.S R0, R3
+// BEQ _tasout
+// EOR R3, R3 /* R3 = 0 */
+// CMP.S R0, R3
+// BEQ _tasout
+// MOVW $1, R15 /* abort: lock != 0 && lock != $0xDEADDEAD */
+//_tasout:
+// RET
+
+TEXT clz(SB), 1, $-4
+ CLZ(0, 0) /* 0 is R0 */
+ RET
+
+TEXT setlabel(SB), 1, $-4
+ MOVW R13, 0(R0) /* sp */
+ MOVW R14, 4(R0) /* pc */
+ BARRIERS
+ MOVW $0, R0
+ RET
+
+TEXT gotolabel(SB), 1, $-4
+ MOVW 0(R0), R13 /* sp */
+ MOVW 4(R0), R14 /* pc */
+ BARRIERS
+ MOVW $1, R0
+ RET
+
+TEXT getcallerpc(SB), 1, $-4
+ MOVW 0(R13), R0
+ RET
+
+TEXT _idlehands(SB), 1, $-4
+ MOVW CPSR, R3
+// ORR $PsrDirq, R3, R1 /* splhi */
+ BIC $PsrDirq, R3, R1 /* spllo */
+ MOVW R1, CPSR
+
+ MOVW $0, R0 /* wait for interrupt */
+ MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEintr), CpCACHEwait
+ ISB
+
+ MOVW R3, CPSR /* splx */
+ RET
+
+TEXT barriers(SB), 1, $-4
+ BARRIERS
+ RET
diff --git a/sys/src/9/kw/lexception.s b/sys/src/9/kw/lexception.s
new file mode 100755
index 000000000..e3f330653
--- /dev/null
+++ b/sys/src/9/kw/lexception.s
@@ -0,0 +1,187 @@
+/*
+ * arm exception handlers
+ */
+#include "arm.s"
+
+#undef B /* B is for 'botch' */
+
+/*
+ * exception vectors, copied by trapinit() to somewhere useful
+ */
+TEXT vectors(SB), 1, $-4
+ MOVW 0x18(R15), R15 /* reset */
+ MOVW 0x18(R15), R15 /* undefined instr. */
+ MOVW 0x18(R15), R15 /* SWI & SMC */
+ MOVW 0x18(R15), R15 /* prefetch abort */
+ MOVW 0x18(R15), R15 /* data abort */
+ MOVW 0x18(R15), R15 /* reserved */
+ MOVW 0x18(R15), R15 /* IRQ */
+ MOVW 0x18(R15), R15 /* FIQ */
+
+TEXT vtable(SB), 1, $-4
+ WORD $_vsvc(SB) /* reset, in svc mode already */
+ WORD $_vund(SB) /* undefined, switch to svc mode */
+ WORD $_vsvc(SB) /* swi, in svc mode already */
+ WORD $_vpabt(SB) /* prefetch abort, switch to svc mode */
+ WORD $_vdabt(SB) /* data abort, switch to svc mode */
+ WORD $_vsvc(SB) /* reserved */
+ WORD $_virq(SB) /* IRQ, switch to svc mode */
+// WORD $_vfiq(SB) /* FIQ, switch to svc mode */
+ WORD $_virq(SB) /* FIQ, switch to svc mode */
+
+TEXT _vrst(SB), 1, $-4
+ BL _reset(SB)
+
+TEXT _vsvc(SB), 1, $-4 /* SWI */
+ MOVW.W R14, -4(R13) /* ureg->pc = interrupted PC */
+ MOVW SPSR, R14 /* ureg->psr = SPSR */
+ MOVW.W R14, -4(R13) /* ... */
+ MOVW $PsrMsvc, R14 /* ureg->type = PsrMsvc */
+ MOVW.W R14, -4(R13) /* ... */
+
+ /* avoid the ambiguity described in notes/movm.w. */
+// MOVM.DB.W.S [R0-R14], (R13) /* save user level registers, at end r13 points to ureg */
+ MOVM.DB.S [R0-R14], (R13) /* save user level registers */
+ SUB $(15*4), R13 /* r13 now points to ureg */
+
+ MOVW $setR12(SB), R12 /* Make sure we've got the kernel's SB loaded */
+
+// MOVW $(KSEG0+16*KiB-MACHSIZE), R10 /* m */
+ MOVW $(L1-MACHSIZE), R10 /* m */
+ MOVW 8(R10), R9 /* up */
+
+ MOVW R13, R0 /* first arg is pointer to ureg */
+ SUB $8, R13 /* space for argument+link */
+
+ BL syscall(SB)
+
+ ADD $(8+4*15), R13 /* make r13 point to ureg->type */
+ MOVW 8(R13), R14 /* restore link */
+ MOVW 4(R13), R0 /* restore SPSR */
+ MOVW R0, SPSR /* ... */
+ MOVM.DB.S (R13), [R0-R14] /* restore registers */
+ ADD $8, R13 /* pop past ureg->{type+psr} */
+ RFE /* MOVM.IA.S.W (R13), [R15] */
+
+TEXT _vund(SB), 1, $-4 /* undefined */
+ MOVM.IA [R0-R4], (R13) /* free some working space */
+ MOVW $PsrMund, R0
+ B _vswitch
+
+TEXT _vpabt(SB), 1, $-4 /* prefetch abort */
+ MOVM.IA [R0-R4], (R13) /* free some working space */
+ MOVW $PsrMabt, R0 /* r0 = type */
+ B _vswitch
+
+TEXT _vdabt(SB), 1, $-4 /* data abort */
+ MOVM.IA [R0-R4], (R13) /* free some working space */
+ MOVW $(PsrMabt+1), R0 /* r0 = type */
+ B _vswitch
+
+TEXT _virq(SB), 1, $-4 /* IRQ */
+ MOVM.IA [R0-R4], (R13) /* free some working space */
+ MOVW $PsrMirq, R0 /* r0 = type */
+ B _vswitch
+
+ /*
+ * come here with type in R0 and R13 pointing above saved [r0-r4].
+ * we'll switch to SVC mode and then call trap.
+ */
+_vswitch:
+ MOVW SPSR, R1 /* save SPSR for ureg */
+ MOVW R14, R2 /* save interrupted pc for ureg */
+ MOVW R13, R3 /* save pointer to where the original [R0-R4] are */
+
+ /*
+ * switch processor to svc mode. this switches the banked registers
+ * (r13 [sp] and r14 [link]) to those of svc mode.
+ */
+ MOVW CPSR, R14
+ BIC $PsrMask, R14
+ ORR $(PsrDirq|PsrDfiq|PsrMsvc), R14
+ MOVW R14, CPSR /* switch! */
+
+ AND.S $0xf, R1, R4 /* interrupted code kernel or user? */
+ BEQ _userexcep
+
+ /* here for trap from SVC mode */
+ MOVM.DB.W [R0-R2], (R13) /* set ureg->{type, psr, pc}; r13 points to ureg->type */
+ MOVM.IA (R3), [R0-R4] /* restore [R0-R4] from previous mode's stack */
+
+ /*
+ * avoid the ambiguity described in notes/movm.w.
+ * In order to get a predictable value in R13 after the stores,
+ * separate the store-multiple from the stack-pointer adjustment.
+ * We'll assume that the old value of R13 should be stored on the stack.
+ */
+ /* save kernel level registers, at end r13 points to ureg */
+// MOVM.DB.W [R0-R14], (R13)
+ MOVM.DB [R0-R14], (R13)
+ SUB $(15*4), R13 /* SP now points to saved R0 */
+
+ MOVW $setR12(SB), R12 /* Make sure we've got the kernel's SB loaded */
+
+ MOVW R13, R0 /* first arg is pointer to ureg */
+ SUB $(4*2), R13 /* space for argument+link (for debugger) */
+ MOVW $0xdeaddead, R11 /* marker */
+
+ BL trap(SB)
+
+ ADD $(4*2+4*15), R13 /* make r13 point to ureg->type */
+ MOVW 8(R13), R14 /* restore link */
+ MOVW 4(R13), R0 /* restore SPSR */
+ MOVW R0, SPSR /* ... */
+
+ MOVM.DB (R13), [R0-R14] /* restore registers */
+
+ ADD $(4*2), R13 /* pop past ureg->{type+psr} to pc */
+ RFE /* MOVM.IA.S.W (R13), [R15] */
+
+ /* here for trap from USER mode */
+_userexcep:
+ MOVM.DB.W [R0-R2], (R13) /* set ureg->{type, psr, pc}; r13 points to ureg->type */
+ MOVM.IA (R3), [R0-R4] /* restore [R0-R4] from previous mode's stack */
+
+ /* avoid the ambiguity described in notes/movm.w. */
+// MOVM.DB.W.S [R0-R14], (R13) /* save kernel level registers, at end r13 points to ureg */
+ MOVM.DB.S [R0-R14], (R13) /* save kernel level registers */
+ SUB $(15*4), R13 /* r13 now points to ureg */
+
+ MOVW $setR12(SB), R12 /* Make sure we've got the kernel's SB loaded */
+
+// MOVW $(KSEG0+16*KiB-MACHSIZE), R10 /* m */
+ MOVW $(L1-MACHSIZE), R10 /* m */
+ MOVW 8(R10), R9 /* up */
+
+ MOVW R13, R0 /* first arg is pointer to ureg */
+ SUB $(4*2), R13 /* space for argument+link (for debugger) */
+
+ BL trap(SB)
+
+ ADD $(4*2+4*15), R13 /* make r13 point to ureg->type */
+ MOVW 8(R13), R14 /* restore link */
+ MOVW 4(R13), R0 /* restore SPSR */
+ MOVW R0, SPSR /* ... */
+ MOVM.DB.S (R13), [R0-R14] /* restore registers */
+ ADD $(4*2), R13 /* pop past ureg->{type+psr} */
+ RFE /* MOVM.IA.S.W (R13), [R15] */
+
+TEXT _vfiq(SB), 1, $-4 /* FIQ */
+ RFE /* FIQ is special, ignore it for now */
+
+/*
+ * set the stack value for the mode passed in R0
+ */
+TEXT setr13(SB), 1, $-4
+ MOVW 4(FP), R1
+
+ MOVW CPSR, R2
+ BIC $PsrMask, R2, R3
+ ORR R0, R3
+ MOVW R3, CPSR /* switch to new mode */
+
+ MOVW R13, R0 /* return old sp */
+ MOVW R1, R13 /* install new one */
+
+ MOVW R2, CPSR /* switch back to old mode */
+ RET
diff --git a/sys/src/9/kw/lproc.s b/sys/src/9/kw/lproc.s
new file mode 100755
index 000000000..0b4eac238
--- /dev/null
+++ b/sys/src/9/kw/lproc.s
@@ -0,0 +1,47 @@
+#include "mem.h"
+#include "arm.h"
+
+/*
+ * This is the first jump from kernel to user mode.
+ * Fake a return from interrupt.
+ *
+ * Enter with R0 containing the user stack pointer.
+ * UTZERO + 0x20 is always the entry point.
+ *
+ */
+TEXT touser(SB), 1, $-4
+ /* store the user stack pointer into the USR_r13 */
+ MOVM.DB.W [R0], (R13)
+ /* avoid the ambiguity described in notes/movm.w. */
+// MOVM.S.IA.W (R13), [R13]
+ MOVM.S (R13), [R13]
+ ADD $4, R13
+
+ /* set up a PSR for user level */
+ MOVW $(PsrMusr), R0
+ MOVW R0, SPSR
+
+ /* save the PC on the stack */
+ MOVW $(UTZERO+0x20), R0
+ MOVM.DB.W [R0], (R13)
+
+ /*
+ * note that 5a's RFE is not the v6 arch. instruction (0xe89d0a00,
+ * I think), which loads CPSR from the word after the PC at (R13),
+ * but rather the pre-v6 simulation `MOVM.IA.S.W (R13), [R15]'
+ * (0xe8fd8000 since MOVM is LDM in this case), which loads CPSR
+ * not from memory but from SPSR due to `.S'.
+ */
+ RFE
+
+/*
+ * here to jump to a newly forked process
+ */
+TEXT forkret(SB), 1, $-4
+ ADD $(4*15), R13 /* make r13 point to ureg->type */
+ MOVW 8(R13), R14 /* restore link */
+ MOVW 4(R13), R0 /* restore SPSR */
+ MOVW R0, SPSR /* ... */
+ MOVM.DB.S (R13), [R0-R14] /* restore registers */
+ ADD $8, R13 /* pop past ureg->{type+psr} */
+ RFE /* MOVM.IA.S.W (R13), [R15] */
diff --git a/sys/src/9/kw/main.c b/sys/src/9/kw/main.c
new file mode 100755
index 000000000..2fa13917d
--- /dev/null
+++ b/sys/src/9/kw/main.c
@@ -0,0 +1,678 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+#include "init.h"
+#include "arm.h"
+#include <pool.h>
+
+#include "reboot.h"
+
+/*
+ * Where configuration info is left for the loaded programme.
+ * This will turn into a structure as more is done by the boot loader
+ * (e.g. why parse the .ini file twice?).
+ * There are 3584 bytes available at CONFADDR.
+ */
+#define BOOTARGS ((char*)CONFADDR)
+#define BOOTARGSLEN (16*KiB) /* limit in devenv.c */
+#define MAXCONF 64
+#define MAXCONFLINE 160
+
+#define isascii(c) ((uchar)(c) > 0 && (uchar)(c) < 0177)
+
+uintptr kseg0 = KZERO;
+Mach* machaddr[MAXMACH];
+
+/*
+ * Option arguments from the command line.
+ * oargv[0] is the boot file.
+ * Optionsinit() is called from multiboot()
+ * or some other machine-dependent place
+ * to set it all up.
+ */
+static int oargc;
+static char* oargv[20];
+static char oargb[128];
+static int oargblen;
+static char oenv[4096];
+
+static uintptr sp; /* XXX - must go - user stack of init proc */
+
+int vflag;
+char debug[256];
+
+/* store plan9.ini contents here at least until we stash them in #ec */
+static char confname[MAXCONF][KNAMELEN];
+static char confval[MAXCONF][MAXCONFLINE];
+static int nconf;
+
+#ifdef CRYPTOSANDBOX
+uchar sandbox[64*1024+BY2PG];
+#endif
+
+static int
+findconf(char *name)
+{
+ int i;
+
+ for(i = 0; i < nconf; i++)
+ if(cistrcmp(confname[i], name) == 0)
+ return i;
+ return -1;
+}
+
+char*
+getconf(char *name)
+{
+ int i;
+
+ i = findconf(name);
+ if(i >= 0)
+ return confval[i];
+ return nil;
+}
+
+void
+addconf(char *name, char *val)
+{
+ int i;
+
+ i = findconf(name);
+ if(i < 0){
+ if(val == nil || nconf >= MAXCONF)
+ return;
+ i = nconf++;
+ strecpy(confname[i], confname[i]+sizeof(confname[i]), name);
+ }
+// confval[i] = val;
+ strecpy(confval[i], confval[i]+sizeof(confval[i]), val);
+}
+
+static void
+writeconf(void)
+{
+ char *p, *q;
+ int n;
+
+ p = getconfenv();
+
+ if(waserror()) {
+ free(p);
+ nexterror();
+ }
+
+ /* convert to name=value\n format */
+ for(q=p; *q; q++) {
+ q += strlen(q);
+ *q = '=';
+ q += strlen(q);
+ *q = '\n';
+ }
+ n = q - p + 1;
+ if(n >= BOOTARGSLEN)
+ error("kernel configuration too large");
+ memmove(BOOTARGS, p, n);
+ poperror();
+ free(p);
+}
+
+/*
+ * assumes that we have loaded our /cfg/pxe/mac file at 0x1000 with
+ * tftp in u-boot. no longer uses malloc, so can be called early.
+ */
+static void
+plan9iniinit(void)
+{
+ char *k, *v, *next;
+
+ k = (char *)CONFADDR;
+ if(!isascii(*k))
+ return;
+
+ for(; k && *k != '\0'; k = next) {
+ if (!isascii(*k)) /* sanity check */
+ break;
+ next = strchr(k, '\n');
+ if (next)
+ *next++ = '\0';
+
+ if (*k == '\0' || *k == '\n' || *k == '#')
+ continue;
+ v = strchr(k, '=');
+ if(v == nil)
+ continue; /* mal-formed line */
+ *v++ = '\0';
+
+ addconf(k, v);
+ }
+}
+
+static void
+optionsinit(char* s)
+{
+ char *o;
+
+ o = strecpy(oargb, oargb+sizeof(oargb), s)+1;
+ if(getenv("bootargs", o, o - oargb) != nil)
+ *(o-1) = ' ';
+
+ oargblen = strlen(oargb);
+ oargc = tokenize(oargb, oargv, nelem(oargv)-1);
+ oargv[oargc] = nil;
+}
+
+char*
+getenv(char* name, char* buf, int n)
+{
+ char *e, *p, *q;
+
+ p = oenv;
+ while(*p != 0){
+ if((e = strchr(p, '=')) == nil)
+ break;
+ for(q = name; p < e; p++){
+ if(*p != *q)
+ break;
+ q++;
+ }
+ if(p == e && *q == 0){
+ strecpy(buf, buf+n, e+1);
+ return buf;
+ }
+ p += strlen(p)+1;
+ }
+
+ return nil;
+}
+
+#include "io.h"
+
+typedef struct Spiregs Spiregs;
+struct Spiregs {
+ ulong ictl; /* interface ctl */
+ ulong icfg; /* interface config */
+ ulong out; /* data out */
+ ulong in; /* data in */
+ ulong ic; /* interrupt cause */
+ ulong im; /* interrupt mask */
+ ulong _pad[2];
+ ulong dwrcfg; /* direct write config */
+ ulong dwrhdr; /* direct write header */
+};
+
+enum {
+ /* ictl bits */
+ Csnact = 1<<0, /* serial memory activated */
+
+ /* icfg bits */
+ Bytelen = 1<<5, /* 2^(this_bit) bytes per transfer */
+ Dirrdcmd= 1<<10, /* flag: fast read */
+};
+
+static void
+dumpbytes(uchar *bp, long max)
+{
+ iprint("%#p: ", bp);
+ for (; max > 0; max--)
+ iprint("%02.2ux ", *bp++);
+ iprint("...\n");
+}
+
+static void
+spiprobe(void)
+{
+ Spiregs *rp = (Spiregs *)soc.spi;
+
+ rp->ictl |= Csnact;
+ coherence();
+ rp->icfg |= Dirrdcmd | 3<<8; /* fast reads, 4-byte addresses */
+ rp->icfg &= ~Bytelen; /* one-byte reads */
+ coherence();
+
+// print("spi flash at %#ux: memory reads enabled\n", PHYSSPIFLASH);
+}
+
+void archconsole(void);
+
+/*
+ * entered from l.s with mmu enabled.
+ *
+ * we may have to realign the data segment; apparently 5l -H0 -R4096
+ * does not pad the text segment. on the other hand, we may have been
+ * loaded by another kernel.
+ *
+ * be careful not to touch the data segment until we know it's aligned.
+ */
+void
+main(Mach* mach)
+{
+ extern char bdata[], edata[], end[], etext[];
+ static ulong vfy = 0xcafebabe;
+
+ m = mach;
+ if (vfy != 0xcafebabe)
+ memmove(bdata, etext, edata - bdata);
+ if (vfy != 0xcafebabe) {
+ wave('?');
+ panic("misaligned data segment");
+ }
+ memset(edata, 0, end - edata); /* zero bss */
+ vfy = 0;
+
+wave('9');
+ machinit();
+ archreset();
+ mmuinit();
+
+ optionsinit("/boot/boot boot");
+ quotefmtinstall();
+ archconsole();
+wave(' ');
+
+ /* want plan9.ini to be able to affect memory sizing in confinit */
+ plan9iniinit(); /* before we step on plan9.ini in low memory */
+
+ confinit();
+ /* xinit would print if it could */
+ xinit();
+
+ /*
+ * Printinit will cause the first malloc call.
+ * (printinit->qopen->malloc) unless any of the
+ * above (like clockintr) do an irqenable, which
+ * will call malloc.
+ * If the system dies here it's probably due
+ * to malloc(->xalloc) not being initialised
+ * correctly, or the data segment is misaligned
+ * (it's amazing how far you can get with
+ * things like that completely broken).
+ *
+ * (Should be) boilerplate from here on.
+ */
+ trapinit();
+ clockinit();
+
+ printinit();
+ uartkirkwoodconsole();
+ /* only now can we print */
+ print("from Bell Labs\n\n");
+
+#ifdef CRYPTOSANDBOX
+ print("sandbox: 64K at physical %#lux, mapped to 0xf10b0000\n",
+ PADDR((uintptr)sandbox & ~(BY2PG-1)));
+#endif
+
+ archconfinit();
+ cpuidprint();
+ timersinit();
+
+ procinit0();
+ initseg();
+ links();
+ chandevreset(); /* most devices are discovered here */
+
+ pageinit();
+ swapinit();
+ userinit();
+ schedinit();
+ panic("schedinit returned");
+}
+
+void
+cpuidprint(void)
+{
+ char name[64];
+
+ cputype2name(name, sizeof name);
+ print("cpu%d: %lldMHz ARM %s\n", m->machno, m->cpuhz/1000000, name);
+}
+
+void
+machinit(void)
+{
+ memset(m, 0, sizeof(Mach));
+ m->machno = 0;
+ machaddr[m->machno] = m;
+
+ m->ticks = 1;
+ m->perf.period = 1;
+
+ conf.nmach = 1;
+
+ active.machs = 1;
+ active.exiting = 0;
+
+ up = nil;
+}
+
+static void
+shutdown(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)
+ iprint("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;
+ }
+ delay(1000);
+}
+
+/*
+ * exit kernel either on a panic or user request
+ */
+void
+exit(int code)
+{
+ shutdown(code);
+ splhi();
+ archreboot();
+}
+
+/*
+ * the new kernel is already loaded at address `code'
+ * of size `size' and entry point `entry'.
+ */
+void
+reboot(void *entry, void *code, ulong size)
+{
+ void (*f)(ulong, ulong, ulong);
+
+ iprint("starting reboot...");
+ writeconf();
+
+ shutdown(0);
+
+ /*
+ * should be the only processor running now
+ */
+
+ print("shutting down...\n");
+ delay(200);
+
+ /* turn off buffered serial console */
+ serialoq = nil;
+
+ /* shutdown devices */
+ chandevshutdown();
+
+ /* call off the dog */
+ clockshutdown();
+
+ splhi();
+
+ /* setup reboot trampoline function */
+ f = (void*)REBOOTADDR;
+ memmove(f, rebootcode, sizeof(rebootcode));
+ cacheuwbinv();
+ l2cacheuwb();
+
+ print("rebooting...");
+ iprint("entry %#lux code %#lux size %ld\n",
+ PADDR(entry), PADDR(code), size);
+ delay(100); /* wait for uart to quiesce */
+
+ /* off we go - never to return */
+ cacheuwbinv();
+ l2cacheuwb();
+ (*f)(PADDR(entry), PADDR(code), size);
+
+ iprint("loaded kernel returned!\n");
+ delay(1000);
+ archreboot();
+}
+
+/*
+ * starting place for first process
+ */
+void
+init0(void)
+{
+ int i;
+ char buf[2*KNAMELEN];
+
+ assert(up != nil);
+ up->nerrlab = 0;
+ coherence();
+ 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), "%s %s", "ARM", conffile);
+ ksetenv("terminal", buf, 0);
+ ksetenv("cputype", "arm", 0);
+ if(cpuserver)
+ ksetenv("service", "cpu", 0);
+ else
+ ksetenv("service", "terminal", 0);
+
+ /* convert plan9.ini variables to #e and #ec */
+ for(i = 0; i < nconf; i++) {
+ ksetenv(confname[i], confval[i], 0);
+ ksetenv(confname[i], confval[i], 1);
+ }
+ poperror();
+ }
+ kproc("alarm", alarmkproc, 0);
+
+ touser(sp);
+}
+
+static void
+bootargs(uintptr base)
+{
+ int i;
+ ulong ssize;
+ char **av, *p;
+
+ /*
+ * Push the boot args onto the stack.
+ * The initial value of the user stack must be such
+ * that the total used is larger than the maximum size
+ * of the argument list checked in syscall.
+ */
+ i = oargblen+1;
+ p = UINT2PTR(STACKALIGN(base + BY2PG - sizeof(up->s.args) - i));
+ memmove(p, oargb, i);
+
+ /*
+ * Now push argc and the argv pointers.
+ * This isn't strictly correct as the code jumped to by
+ * touser in init9.s calls startboot (port/initcode.c) which
+ * expects arguments
+ * startboot(char *argv0, char **argv)
+ * not the usual (int argc, char* argv[]), but argv0 is
+ * unused so it doesn't matter (at the moment...).
+ */
+ av = (char**)(p - (oargc+2)*sizeof(char*));
+ ssize = base + BY2PG - PTR2UINT(av);
+ *av++ = (char*)oargc;
+ for(i = 0; i < oargc; i++)
+ *av++ = (oargv[i] - oargb) + (p - base) + (USTKTOP - BY2PG);
+ *av = nil;
+
+ /*
+ * Leave space for the return PC of the
+ * caller of initcode.
+ */
+ sp = USTKTOP - ssize - sizeof(void*);
+}
+
+/*
+ * create the first process
+ */
+void
+userinit(void)
+{
+ Proc *p;
+ Segment *s;
+ KMap *k;
+ Page *pg;
+
+ /* no processes yet */
+ up = nil;
+
+ 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);
+
+ /*
+ * Kernel Stack
+ */
+ p->sched.pc = PTR2UINT(init0);
+ p->sched.sp = PTR2UINT(p->kstack+KSTACK-sizeof(up->s.args)-sizeof(uintptr));
+ p->sched.sp = STACKALIGN(p->sched.sp);
+
+ /*
+ * User Stack
+ *
+ * Technically, newpage can't be called here because it
+ * should only be called when in a user context as it may
+ * try to sleep if there are no pages available, but that
+ * shouldn't be the case here.
+ */
+ s = newseg(SG_STACK, USTKTOP-USTKSIZE, USTKSIZE/BY2PG);
+ p->seg[SSEG] = s;
+ pg = newpage(1, 0, USTKTOP-BY2PG);
+ segpage(s, pg);
+ k = kmap(pg);
+ bootargs(VA(k));
+ kunmap(k);
+
+ /*
+ * 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(UINT2PTR(VA(k)), initcode, sizeof initcode);
+ kunmap(k);
+
+ ready(p);
+}
+
+Conf conf; /* XXX - must go - gag */
+
+Confmem sheevamem[] = {
+ /*
+ * Memory available to Plan 9:
+ * the 8K is reserved for ethernet dma access violations to scribble on.
+ */
+ { .base = 0, .limit = 512*MB - 8*1024, },
+};
+
+void
+confinit(void)
+{
+ int i;
+ ulong kpages;
+ uintptr pa;
+
+ /*
+ * Copy the physical memory configuration to Conf.mem.
+ */
+ if(nelem(sheevamem) > nelem(conf.mem)){
+ iprint("memory configuration botch\n");
+ exit(1);
+ }
+ memmove(conf.mem, sheevamem, sizeof(sheevamem));
+
+ conf.npage = 0;
+ pa = PADDR(PGROUND(PTR2UINT(end)));
+
+ /*
+ * we assume that the kernel is at the beginning of one of the
+ * contiguous chunks of memory and fits therein.
+ */
+ for(i=0; i<nelem(conf.mem); i++){
+ /* take kernel out of allocatable space */
+ if(pa > conf.mem[i].base && pa < conf.mem[i].limit)
+ conf.mem[i].base = pa;
+
+ conf.mem[i].npage = (conf.mem[i].limit - conf.mem[i].base)/BY2PG;
+ conf.npage += conf.mem[i].npage;
+ }
+
+ conf.upages = (conf.npage*90)/100;
+ conf.ialloc = ((conf.npage-conf.upages)/2)*BY2PG;
+
+ /* only one processor */
+ conf.nmach = 1;
+
+ /* set up other configuration parameters */
+ conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
+ if(cpuserver)
+ conf.nproc *= 3;
+ if(conf.nproc > 2000)
+ conf.nproc = 2000;
+ conf.nswap = conf.npage*3;
+ conf.nswppo = 4096;
+ conf.nimage = 200;
+
+ conf.copymode = 0; /* copy on write */
+
+ /*
+ * Guess how much is taken by the large permanent
+ * datastructures. Mntcache and Mntrpc are not accounted for
+ * (probably ~300KB).
+ */
+ kpages = conf.npage - conf.upages;
+ 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;
+}
+
+int
+cmpswap(long *addr, long old, long new)
+{
+ return cas32(addr, old, new);
+}
diff --git a/sys/src/9/kw/mem.h b/sys/src/9/kw/mem.h
new file mode 100755
index 000000000..b35e4dba7
--- /dev/null
+++ b/sys/src/9/kw/mem.h
@@ -0,0 +1,142 @@
+/*
+ * Memory and machine-specific definitions. Used in C and assembler.
+ */
+#define KiB 1024u /* Kibi 0x0000000000000400 */
+#define MiB 1048576u /* Mebi 0x0000000000100000 */
+#define GiB 1073741824u /* Gibi 000000000040000000 */
+
+#define HOWMANY(x, y) (((x)+((y)-1))/(y))
+#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y)) /* ceiling */
+#define ROUNDDN(x, y) (((x)/(y))*(y)) /* floor */
+#define MIN(a, b) ((a) < (b)? (a): (b))
+#define MAX(a, b) ((a) > (b)? (a): (b))
+
+/*
+ * Not sure where these macros should go.
+ * This probably isn't right but will do for now.
+ * The macro names are problematic too.
+ */
+/*
+ * In B(o), 'o' is the bit offset in the register.
+ * For multi-bit fields use F(v, o, w) where 'v' is the value
+ * of the bit-field of width 'w' with LSb at bit offset 'o'.
+ */
+#define B(o) (1<<(o))
+#define F(v, o, w) (((v) & ((1<<(w))-1))<<(o))
+
+#define FCLR(d, o, w) ((d) & ~(((1<<(w))-1)<<(o)))
+#define FEXT(d, o, w) (((d)>>(o)) & ((1<<(w))-1))
+#define FINS(d, o, w, v) (FCLR((d), (o), (w))|F((v), (o), (w)))
+#define FSET(d, o, w) ((d)|(((1<<(w))-1)<<(o)))
+
+#define FMASK(o, w) (((1<<(w))-1)<<(o))
+
+/*
+ * Sizes
+ */
+#define BY2PG (4*KiB) /* bytes per page */
+#define PGSHIFT 12 /* log(BY2PG) */
+#define PGROUND(s) ROUNDUP(s, BY2PG)
+#define ROUND(s, sz) (((s)+(sz-1))&~(sz-1))
+
+#define MAXMACH 1 /* max # cpus system can run */
+#define MACHSIZE BY2PG
+
+#define KSTKSIZE (8*KiB)
+#define STACKALIGN(sp) ((sp) & ~3) /* bug: assure with alloc */
+
+/*
+ * Address spaces.
+ * KTZERO is used by kprof and dumpstack (if any).
+ *
+ * KZERO is mapped to physical 0.
+ * u-boot claims to take 0 - 8MB.
+ *
+ * vectors are at 0, plan9.ini is at KZERO+4K and is limited to 16K by
+ * devenv. L2 PTEs for trap vectors & i/o regs are stored from KZERO+56K
+ * to L1-MACHSIZE (KZERO+60K). cpu0's Mach struct is at L1 - MACHSIZE(4K)
+ * to L1 (KZERO+60K to KZERO+64K). L1 PTEs are stored from L1 to L1+32K
+ * (KZERO+64K to KZERO+96K). KTZERO may be anywhere after KZERO+96K.
+ */
+
+#define KSEG0 0x60000000 /* kernel segment */
+/* mask to check segment; good for 512MB dram */
+#define KSEGM 0xE0000000
+#define KZERO KSEG0 /* kernel address space */
+#define CONFADDR (KZERO+4*KiB) /* unparsed plan9.ini */
+#define L1 (KZERO+64*KiB) /* tt ptes: 16KiB aligned */
+#define KTZERO (KZERO+0x800000) /* kernel text start */
+
+#define UZERO 0 /* user segment */
+#define UTZERO (UZERO+BY2PG) /* user text start */
+#define USTKTOP KZERO /* user segment end +1 */
+#define USTKSIZE (8*1024*1024) /* user stack size */
+#define TSTKTOP (USTKTOP-USTKSIZE) /* sysexec temporary stack */
+#define TSTKSIZ 256
+
+/* address at which to copy and execute rebootcode */
+#define REBOOTADDR KADDR(0x100)
+
+/*
+ * Time.
+ * Does this need to be here? Used in assembler?
+ */
+#define HZ 100 /* clock frequency */
+#define MS2HZ (1000/HZ) /* millisec per clock tick */
+#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */
+
+/*
+ * More accurate time
+ */
+#define CLOCKFREQ (200*1000*1000) /* TCLK on sheeva: 200MHz */
+//#define MS2TMR(t) ((ulong)(((uvlong)(t)*CLOCKFREQ)/1000))
+//#define US2TMR(t) ((ulong)(((uvlong)(t)*CLOCKFREQ)/1000000))
+
+/*
+ * Legacy...
+ */
+#define BLOCKALIGN 32 /* only used in allocb.c */
+#define KSTACK KSTKSIZE
+
+/*
+ * Sizes
+ */
+#define BI2BY 8 /* bits per byte */
+#define BY2SE 4
+#define BY2WD 4
+#define BY2V 8 /* only used in xalloc.c */
+
+#define CACHELINESZ 32
+#define PTEMAPMEM (1024*1024)
+#define PTEPERTAB (PTEMAPMEM/BY2PG)
+#define SEGMAPSIZE 1984
+#define SSEGMAPSIZE 16
+#define PPN(x) ((x)&~(BY2PG-1))
+
+/*
+ * With a little work these move to port.
+ */
+#define PTEVALID (1<<0)
+#define PTERONLY 0
+#define PTEWRITE (1<<1)
+#define PTEUNCACHED (1<<2)
+#define PTEKERNEL (1<<3)
+
+/*
+ * Physical machine information from here on.
+ */
+#define PHYSDRAM 0
+
+/* from 0x80000000 up is uncached by L2 (see archkw.c) */
+#define PHYSCESASRAM 0xc8010000
+// #define PHYSSPIFLASH 0xe8000000 /* ignore spi flash */
+/* this address is configured by u-boot, and is 0xd0000000 at reset */
+#define PHYSIO 0xf1000000 /* internal registers */
+#define PHYSCONS (PHYSIO + 0x12000) /* uart */
+#define PHYSNAND1 0xf9000000 /* sheeva/openrd (remapped) */
+#define PHYSNAND2 0xd8000000 /* guru */
+#define PHYSBOOTROM 0xffff0000 /* boot rom */
+
+#define FLASHSIZE (512*MiB) /* but not addressed linearly */
+
+#define VIRTIO PHYSIO
diff --git a/sys/src/9/kw/mkfile b/sys/src/9/kw/mkfile
new file mode 100755
index 000000000..8f5402352
--- /dev/null
+++ b/sys/src/9/kw/mkfile
@@ -0,0 +1,156 @@
+CONF=plug
+CONFLIST=plug
+
+# allegedly u-boot uses the bottom 8MB (up to 0x800000)
+# so avoid that
+loadaddr=0x60800000
+
+objtype=arm
+</$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\
+ mul64fract.$O\
+ rebootcmd.$O\
+ page.$O\
+ parse.$O\
+ pgrp.$O\
+ portclock.$O\
+ print.$O\
+ proc.$O\
+ qio.$O\
+ qlock.$O\
+ segment.$O\
+ swap.$O\
+ syscallfmt.$O\
+ sysfile.$O\
+ sysproc.$O\
+ taslock.$O\
+ tod.$O\
+ xalloc.$O\
+
+OBJ=\
+ l.$O\
+ lexception.$O\
+ lproc.$O\
+ arch.$O\
+ cga.$O\
+ clock.$O\
+ fpi.$O\
+ fpiarm.$O\
+ fpimem.$O\
+ main.$O\
+ mmu.$O\
+ random.$O\
+ trap.$O\
+ $CONF.root.$O\
+ $CONF.rootc.$O\
+ $DEVS\
+ $PORT\
+
+# HFILES=
+
+LIB=\
+ /$objtype/lib/libmemlayer.a\
+ /$objtype/lib/libmemdraw.a\
+ /$objtype/lib/libdraw.a\
+ /$objtype/lib/libip.a\
+ /$objtype/lib/libsec.a\
+ /$objtype/lib/libc.a\
+
+9:V: $p$CONF s$p$CONF
+
+$p$CONF:DQ: $CONF.c $OBJ $LIB mkfile
+ $CC $CFLAGS '-DKERNDATE='`{date -n} $CONF.c
+ echo '# linking raw kernel'
+# sleep 1 # avoid relinking later
+ $LD -o $target -H0 -R4096 -T$loadaddr -l $OBJ $CONF.$O $LIB
+
+s$p$CONF:DQ: $CONF.$O $OBJ $LIB
+ echo '# linking kernel with symbols'
+# sleep 1 # avoid relinking later
+ $LD -o $target -R4096 -T$loadaddr -l $OBJ $CONF.$O $LIB
+ size $target
+
+$p$CONF.gz:D: $p$CONF
+ gzip -9 <$p$CONF >$target
+
+$OBJ: $HFILES
+
+install:V: /$objtype/$p$CONF
+
+install-in-flash:V: /$objtype/$p$CONF paqdisk
+ plug.flash.cfg
+ echo erase all >/dev/flash/kernelctl
+ cp /$objtype/$p$CONF /dev/flash/kernel
+ echo erase all >/dev/flash/plan9ctl
+ cp paqdisk /dev/flash/plan9
+
+/$objtype/$p$CONF:D: $p$CONF s$p$CONF
+ cp -x $p$CONF s$p$CONF /$objtype &
+ { 9fs lookout && cp -x $p$CONF s$p$CONF /n/lookout/$objtype } &
+# { 9fs piestand && cp -x $p$CONF s$p$CONF /n/piestand/$objtype } &
+ wait
+ touch $target
+
+paqdisk:
+ rm -fr armpaq
+ mkdir armpaq
+ cd armpaq
+ disk/mkfs -d . /sys/lib/sysconfig/proto/armpaqproto
+ mkpaqfs -o ../paqdisk
+ cd ..
+
+<../boot/bootmkfile
+<../port/portmkfile
+<|../port/mkbootrules $CONF
+
+# CFLAGS= -I. -I../port $CFLAGS # hack to compile private sysproc.c (e.g.)
+
+arch.$O clock.$O fpiarm.$O main.$O mmu.$O screen.$O sdscsi.$O syscall.$O \
+ trap.$O: /$objtype/include/ureg.h
+
+archkw.$O devether.$O ether1116.$O ethermii.$O: \
+ etherif.h ethermii.h ../port/netif.h
+archkw.$O devflash.$O flashkw.$O: ../port/flashif.h
+fpi.$O fpiarm.$O fpimem.$O: fpi.h
+l.$O lexception.$O lproc.$O mmu.$O: arm.s arm.h mem.h
+main.$O: errstr.h init.h reboot.h
+mouse.$O: screen.h
+devusb.$O: ../port/usb.h
+usbehci.$O usbohci.$O usbuhci.$O: ../port/usb.h usbehci.h uncached.h
+
+init.h:D: ../port/initcode.c init9.s
+ $CC ../port/initcode.c
+ $AS init9.s
+ $LD -l -R1 -s -o init.out init9.$O initcode.$O /$objtype/lib/libc.a
+ {echo 'uchar initcode[]={'
+ xd -1x <init.out |
+ sed -e 's/^[0-9a-f]+ //' -e 's/ ([0-9a-f][0-9a-f])/0x\1,/g'
+ echo '};'} > init.h
+
+reboot.h:D: rebootcode.s arm.s arm.h mem.h
+ $AS rebootcode.s
+ # -lc is only for memmove. -T arg is PADDR(REBOOTADDR)
+ $LD -l -a -s -T0x100 -R4 -o reboot.out rebootcode.$O -lc >reboot.list
+ {echo 'uchar rebootcode[]={'
+ xd -1x reboot.out |
+ sed -e '1,2d' -e 's/^[0-9a-f]+ //' -e 's/ ([0-9a-f][0-9a-f])/0x\1,/g'
+ echo '};'} > reboot.h
+errstr.h:D: ../port/mkerrstr ../port/error.h
+ rc ../port/mkerrstr > errstr.h
+
+plug.clean:
+ rm -rf $p$CONF s$p$CONF armpaq paqdisk $CONF.c boot$CONF.c ../boot/libboot.a5
diff --git a/sys/src/9/kw/mmu.c b/sys/src/9/kw/mmu.c
new file mode 100755
index 000000000..2557ff59b
--- /dev/null
+++ b/sys/src/9/kw/mmu.c
@@ -0,0 +1,522 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "arm.h"
+
+#define L1X(va) FEXT((va), 20, 12)
+#define L2X(va) FEXT((va), 12, 8)
+
+enum {
+ L1lo = UZERO/MiB, /* L1X(UZERO)? */
+ L1hi = (USTKTOP+MiB-1)/MiB, /* L1X(USTKTOP+MiB-1)? */
+};
+
+#define ISHOLE(pte) ((pte) == 0)
+
+/* dump level 1 page table at virtual addr l1 */
+void
+mmudump(PTE *l1)
+{
+ int i, type, rngtype;
+ uintptr pa, startva, startpa;
+ uvlong va, endva;
+ PTE pte;
+
+ iprint("\n");
+ endva = startva = startpa = 0;
+ rngtype = 0;
+ /* dump first level of ptes */
+ for (va = i = 0; i < 4096; i++) {
+ pte = l1[i];
+ pa = pte & ~(MB - 1);
+ type = pte & (Fine|Section|Coarse);
+ if (ISHOLE(pte)) {
+ if (endva != 0) { /* open range? close it */
+ iprint("l1 maps va (%#lux-%#llux) -> pa %#lux type %#ux\n",
+ startva, endva-1, startpa, rngtype);
+ endva = 0;
+ }
+ } else {
+ if (endva == 0) { /* no open range? start one */
+ startva = va;
+ startpa = pa;
+ rngtype = type;
+ }
+ endva = va + MB; /* continue the open range */
+ }
+ va += MB;
+ }
+ if (endva != 0) /* close an open range */
+ iprint("l1 maps va (%#lux-%#llux) -> pa %#lux type %#ux\n",
+ startva, endva-1, startpa, rngtype);
+}
+
+#ifdef CRYPTOSANDBOX
+extern uchar sandbox[64*1024+BY2PG];
+#endif
+
+/* identity map `mbs' megabytes from phys */
+void
+mmuidmap(uintptr phys, int mbs)
+{
+ PTE *l1;
+ uintptr pa, fpa;
+
+ pa = ttbget();
+ l1 = KADDR(pa);
+
+ for (fpa = phys; mbs-- > 0; fpa += MiB)
+ l1[L1X(fpa)] = fpa|Dom0|L1AP(Krw)|Section;
+ coherence();
+
+ mmuinvalidate();
+ cacheuwbinv();
+ l2cacheuwbinv();
+}
+
+void
+mmuinit(void)
+{
+ PTE *l1, *l2;
+ uintptr pa, i;
+
+ pa = ttbget();
+ l1 = KADDR(pa);
+
+ /*
+ * map high vectors to start of dram, but only 4K, not 1MB.
+ */
+ pa -= MACHSIZE+2*1024;
+ l2 = KADDR(pa);
+ memset(l2, 0, 1024);
+ /* vectors step on u-boot, but so do page tables */
+ l2[L2X(HVECTORS)] = PHYSDRAM|L2AP(Krw)|Small;
+ l1[L1X(HVECTORS)] = pa|Dom0|Coarse; /* vectors -> ttb-machsize-2k */
+
+ /* double map vectors at virtual 0 so reset will see them */
+ pa -= 1024;
+ l2 = KADDR(pa);
+ memset(l2, 0, 1024);
+ l2[L2X(0)] = PHYSDRAM|L2AP(Krw)|Small;
+ l1[L1X(0)] = pa|Dom0|Coarse;
+
+ /*
+ * set up L2 ptes for PHYSIO (i/o registers), with smaller pages to
+ * enable user-mode access to a few devices.
+ */
+ pa -= 1024;
+ l2 = KADDR(pa);
+ /* identity map by default */
+ for (i = 0; i < 1024/4; i++)
+ l2[L2X(VIRTIO + i*BY2PG)] = (PHYSIO + i*BY2PG)|L2AP(Krw)|Small;
+
+#ifdef CRYPTOSANDBOX
+ /*
+ * rest is to let rae experiment with the crypto hardware
+ */
+ /* access to cycle counter */
+ l2[L2X(soc.clock)] = soc.clock | L2AP(Urw)|Small;
+ /* cesa registers; also visible in user space */
+ for (i = 0; i < 16; i++)
+ l2[L2X(soc.cesa + i*BY2PG)] = (soc.cesa + i*BY2PG) |
+ L2AP(Urw)|Small;
+ /* crypto sram; remapped to unused space and visible in user space */
+ l2[L2X(PHYSIO + 0xa0000)] = PHYSCESASRAM | L2AP(Urw)|Small;
+ /* 64k of scratch dram */
+ for (i = 0; i < 16; i++)
+ l2[L2X(PHYSIO + 0xb0000 + i*BY2PG)] =
+ (PADDR((uintptr)sandbox & ~(BY2PG-1)) + i*BY2PG) |
+ L2AP(Urw) | Small;
+#endif
+
+ l1[L1X(VIRTIO)] = pa|Dom0|Coarse;
+ coherence();
+
+ mmuinvalidate();
+ cacheuwbinv();
+ l2cacheuwbinv();
+
+ m->mmul1 = l1;
+// mmudump(l1); /* DEBUG. too early to print */
+}
+
+static void
+mmul2empty(Proc* proc, int clear)
+{
+ PTE *l1;
+ Page **l2, *page;
+
+ l1 = m->mmul1;
+ l2 = &proc->mmul2;
+ for(page = *l2; page != nil; page = page->next){
+ if(clear)
+ memset(UINT2PTR(page->va), 0, BY2PG);
+ l1[page->daddr] = Fault;
+ l2 = &page->next;
+ }
+ *l2 = proc->mmul2cache;
+ proc->mmul2cache = proc->mmul2;
+ proc->mmul2 = nil;
+}
+
+static void
+mmul1empty(void)
+{
+#ifdef notdef /* there's a bug in here */
+ PTE *l1;
+
+ /* clean out any user mappings still in l1 */
+ if(m->mmul1lo > L1lo){
+ if(m->mmul1lo == 1)
+ m->mmul1[L1lo] = Fault;
+ else
+ memset(&m->mmul1[L1lo], 0, m->mmul1lo*sizeof(PTE));
+ m->mmul1lo = L1lo;
+ }
+ if(m->mmul1hi < L1hi){
+ l1 = &m->mmul1[m->mmul1hi];
+ if((L1hi - m->mmul1hi) == 1)
+ *l1 = Fault;
+ else
+ memset(l1, 0, (L1hi - m->mmul1hi)*sizeof(PTE));
+ m->mmul1hi = L1hi;
+ }
+#else
+ memset(&m->mmul1[L1lo], 0, (L1hi - L1lo)*sizeof(PTE));
+#endif /* notdef */
+}
+
+void
+mmuswitch(Proc* proc)
+{
+ int x;
+ PTE *l1;
+ Page *page;
+
+ /* do kprocs get here and if so, do they need to? */
+ if(m->mmupid == proc->pid && !proc->newtlb)
+ return;
+ m->mmupid = proc->pid;
+
+ /* write back dirty and invalidate l1 caches */
+ cacheuwbinv();
+
+ if(proc->newtlb){
+ mmul2empty(proc, 1);
+ proc->newtlb = 0;
+ }
+
+ mmul1empty();
+
+ /* move in new map */
+ l1 = m->mmul1;
+ for(page = proc->mmul2; page != nil; page = page->next){
+ x = page->daddr;
+ l1[x] = PPN(page->pa)|Dom0|Coarse;
+ /* know here that L1lo < x < L1hi */
+ if(x+1 - m->mmul1lo < m->mmul1hi - x)
+ m->mmul1lo = x+1;
+ else
+ m->mmul1hi = x;
+ }
+
+ /* make sure map is in memory */
+ /* could be smarter about how much? */
+ cachedwbse(&l1[L1X(UZERO)], (L1hi - L1lo)*sizeof(PTE));
+ l2cacheuwbse(&l1[L1X(UZERO)], (L1hi - L1lo)*sizeof(PTE));
+
+ /* lose any possible stale tlb entries */
+ mmuinvalidate();
+
+// mmudump(l1);
+ //print("mmuswitch l1lo %d l1hi %d %d\n",
+ // m->mmul1lo, m->mmul1hi, proc->kp);
+//print("\n");
+}
+
+void
+flushmmu(void)
+{
+ int s;
+
+ s = splhi();
+ up->newtlb = 1;
+ mmuswitch(up);
+ splx(s);
+}
+
+void
+mmurelease(Proc* proc)
+{
+ Page *page, *next;
+
+ /* write back dirty and invalidate l1 caches */
+ cacheuwbinv();
+
+ mmul2empty(proc, 0);
+ for(page = proc->mmul2cache; page != nil; page = next){
+ next = page->next;
+ if(--page->ref)
+ panic("mmurelease: page->ref %d", page->ref);
+ pagechainhead(page);
+ }
+ if(proc->mmul2cache && palloc.r.p)
+ wakeup(&palloc.r);
+ proc->mmul2cache = nil;
+
+ mmul1empty();
+
+ /* make sure map is in memory */
+ /* could be smarter about how much? */
+ cachedwbse(&m->mmul1[L1X(UZERO)], (L1hi - L1lo)*sizeof(PTE));
+ l2cacheuwbse(&m->mmul1[L1X(UZERO)], (L1hi - L1lo)*sizeof(PTE));
+
+ /* lose any possible stale tlb entries */
+ mmuinvalidate();
+}
+
+void
+putmmu(uintptr va, uintptr pa, Page* page)
+{
+ int x;
+ Page *pg;
+ PTE *l1, *pte;
+
+ x = L1X(va);
+ l1 = &m->mmul1[x];
+ //print("putmmu(%#p, %#p, %#p) ", va, pa, page->pa);
+ //print("mmul1 %#p l1 %#p *l1 %#ux x %d pid %d\n",
+ // m->mmul1, l1, *l1, x, up->pid);
+ if(*l1 == Fault){
+ /* wasteful - l2 pages only have 256 entries - fix */
+ if(up->mmul2cache == nil){
+ /* auxpg since we don't need much? memset if so */
+ pg = newpage(1, 0, 0);
+ pg->va = VA(kmap(pg));
+ }
+ else{
+ pg = up->mmul2cache;
+ up->mmul2cache = pg->next;
+ memset(UINT2PTR(pg->va), 0, BY2PG);
+ }
+ pg->daddr = x;
+ pg->next = up->mmul2;
+ up->mmul2 = pg;
+
+ /* force l2 page to memory */
+ cachedwbse((void *)pg->va, BY2PG);
+ l2cacheuwbse((void *)pg->va, BY2PG);
+
+ *l1 = PPN(pg->pa)|Dom0|Coarse;
+ cachedwbse(l1, sizeof *l1);
+ l2cacheuwbse(l1, sizeof *l1);
+ //print("l1 %#p *l1 %#ux x %d pid %d\n", l1, *l1, x, up->pid);
+
+ if(x >= m->mmul1lo && x < m->mmul1hi){
+ if(x+1 - m->mmul1lo < m->mmul1hi - x)
+ m->mmul1lo = x+1;
+ else
+ m->mmul1hi = x;
+ }
+ }
+ pte = UINT2PTR(KADDR(PPN(*l1)));
+ //print("pte %#p index %ld %#ux\n", pte, L2X(va), *(pte+L2X(va)));
+
+ /* protection bits are
+ * PTERONLY|PTEVALID;
+ * PTEWRITE|PTEVALID;
+ * PTEWRITE|PTEUNCACHED|PTEVALID;
+ */
+ x = Small;
+ if(!(pa & PTEUNCACHED))
+ x |= Cached|Buffered;
+ if(pa & PTEWRITE)
+ x |= L2AP(Urw);
+ else
+ x |= L2AP(Uro);
+ pte[L2X(va)] = PPN(pa)|x;
+ cachedwbse(&pte[L2X(va)], sizeof pte[0]);
+ l2cacheuwbse(&pte[L2X(va)], sizeof pte[0]);
+
+ /* clear out the current entry */
+ mmuinvalidateaddr(PPN(va));
+
+ /*
+ * write back dirty entries - we need this because pio() in
+ * fault.c is writing via a different virt addr and won't clean
+ * its changes out of the dcache. Page coloring doesn't work
+ * on this mmu because the l1 virtual cache is set associative
+ * rather than direct mapped.
+ */
+ cachedwbinv();
+ if(page->cachectl[0] == PG_TXTFLUSH){
+ /* pio() sets PG_TXTFLUSH whenever a text pg has been written */
+ cacheiinv();
+ page->cachectl[0] = PG_NOFLUSH;
+ }
+ //print("putmmu %#p %#p %#p\n", va, pa, PPN(pa)|x);
+}
+
+void*
+mmuuncache(void* v, usize size)
+{
+ int x;
+ PTE *pte;
+ uintptr va;
+
+ /*
+ * Simple helper for ucalloc().
+ * Uncache a Section, must already be
+ * valid in the MMU.
+ */
+ va = PTR2UINT(v);
+ assert(!(va & (1*MiB-1)) && size == 1*MiB);
+
+ x = L1X(va);
+ pte = &m->mmul1[x];
+ if((*pte & (Fine|Section|Coarse)) != Section)
+ return nil;
+ *pte &= ~(Cached|Buffered);
+ mmuinvalidateaddr(va);
+ cachedwbse(pte, 4);
+ l2cacheuwbse(pte, 4);
+
+ return v;
+}
+
+uintptr
+mmukmap(uintptr va, uintptr pa, usize size)
+{
+ int x;
+ PTE *pte;
+
+ /*
+ * Stub.
+ */
+ assert(!(va & (1*MiB-1)) && !(pa & (1*MiB-1)) && size == 1*MiB);
+
+ x = L1X(va);
+ pte = &m->mmul1[x];
+ if(*pte != Fault)
+ return 0;
+ *pte = pa|Dom0|L1AP(Krw)|Section;
+ mmuinvalidateaddr(va);
+ cachedwbse(pte, 4);
+ l2cacheuwbse(pte, 4);
+
+ return va;
+}
+
+uintptr
+mmukunmap(uintptr va, uintptr pa, usize size)
+{
+ int x;
+ PTE *pte;
+
+ /*
+ * Stub.
+ */
+ assert(!(va & (1*MiB-1)) && !(pa & (1*MiB-1)) && size == 1*MiB);
+
+ x = L1X(va);
+ pte = &m->mmul1[x];
+ if(*pte != (pa|Dom0|L1AP(Krw)|Section))
+ return 0;
+ *pte = Fault;
+ mmuinvalidateaddr(va);
+ cachedwbse(pte, 4);
+ l2cacheuwbse(pte, 4);
+
+ return va;
+}
+
+/*
+ * Return the number of bytes that can be accessed via KADDR(pa).
+ * If pa is not a valid argument to KADDR, return 0.
+ */
+uintptr
+cankaddr(uintptr pa)
+{
+ if(pa < PHYSDRAM + 512*MiB) /* assumes PHYSDRAM is 0 */
+ return PHYSDRAM + 512*MiB - pa;
+ return 0;
+}
+
+/* from 386 */
+void*
+vmap(uintptr pa, usize size)
+{
+ uintptr pae, va;
+ usize o, osize;
+
+ /*
+ * XXX - replace with new vm stuff.
+ * Crock after crock - the first 4MB is mapped with 2MB pages
+ * so catch that and return good values because the current mmukmap
+ * will fail.
+ */
+ if(pa+size < 4*MiB)
+ return UINT2PTR(kseg0|pa);
+
+ osize = size;
+ o = pa & (BY2PG-1);
+ pa -= o;
+ size += o;
+ size = ROUNDUP(size, BY2PG);
+
+ va = kseg0|pa;
+ pae = mmukmap(va, pa, size);
+ if(pae == 0 || pae-size != pa)
+ panic("vmap(%#p, %ld) called from %#p: mmukmap fails %#p",
+ pa+o, osize, getcallerpc(&pa), pae);
+
+ return UINT2PTR(va+o);
+}
+
+/* from 386 */
+void
+vunmap(void* v, usize size)
+{
+ /*
+ * XXX - replace with new vm stuff.
+ * Can't do this until do real vmap for all space that
+ * might be used, e.g. stuff below 1MB which is currently
+ * mapped automagically at boot but that isn't used (or
+ * at least shouldn't be used) by the kernel.
+ upafree(PADDR(v), size);
+ */
+ USED(v, size);
+}
+
+/*
+ * Notes.
+ * Everything is in domain 0;
+ * domain 0 access bits in the DAC register are set
+ * to Client, which means access is controlled by the
+ * permission values set in the PTE.
+ *
+ * L1 access control for the kernel is set to 1 (RW,
+ * no user mode access);
+ * L2 access control for the kernel is set to 1 (ditto)
+ * for all 4 AP sets;
+ * L1 user mode access is never set;
+ * L2 access control for user mode is set to either
+ * 2 (RO) or 3 (RW) depending on whether text or data,
+ * for all 4 AP sets.
+ * (To get kernel RO set AP to 0 and S bit in control
+ * register c1).
+ * Coarse L1 page-tables are used. They have 256 entries
+ * and so consume 1024 bytes per table.
+ * Small L2 page-tables are used. They have 1024 entries
+ * and so consume 4096 bytes per table.
+ *
+ * 4KiB. That's the size of 1) a page, 2) the
+ * size allocated for an L2 page-table page (note only 1KiB
+ * is needed per L2 page - to be dealt with later) and
+ * 3) the size of the area in L1 needed to hold the PTEs
+ * to map 1GiB of user space (0 -> 0x3fffffff, 1024 entries).
+ */
diff --git a/sys/src/9/kw/notes/movm.w b/sys/src/9/kw/notes/movm.w
new file mode 100755
index 000000000..a2b22f1e1
--- /dev/null
+++ b/sys/src/9/kw/notes/movm.w
@@ -0,0 +1,22 @@
+gorka writes:
+---
+I have userspace on the gumstix [xscale, not omap]. The problem that
+got me in trouble was that in lexception.s (or l.s),
+
+ MOVM.DB.W [R0-R14], (R13)
+
+works differently for this architecture (and probably for others, as
+it is unclear how it should behave by reading the arm specs). This
+happens only for kernel faults as the others (syscall, user faults)
+use MOVM.DB.W.S which uses the banked user registers.
+
+The problem is that in this arch the value of R13 saved is the value
+after R13 itself has been modified, whereas in the others (bitsy,
+pico...), it was the value before. Adding 4*15 to the stack before
+the RFE solves the problem.
+---
+
+In fact, the 2005 ARM arch. ref. man. (ARM DDI 0100I) says, under STM (1),
+that if Rn appears in the set of registers (and isn't the first one)
+and .W is specified, the stored value of Rn is unpredictable.
+The arm v7-ar arch. ref. man. says such usage is obsolete.
diff --git a/sys/src/9/kw/nvram b/sys/src/9/kw/nvram
new file mode 100755
index 000000000..a64a5a93f
--- /dev/null
+++ b/sys/src/9/kw/nvram
Binary files differ
diff --git a/sys/src/9/kw/openrd.words b/sys/src/9/kw/openrd.words
new file mode 100755
index 000000000..99f041231
--- /dev/null
+++ b/sys/src/9/kw/openrd.words
@@ -0,0 +1,21 @@
+global scale openrd client
+
+like the sheevaplug, and runs the same kernel, but with these additions:
+
+2 Gb ethers
+
+pci bus 0
+00.00.00 vid 11ab did 6281 memory controller sub-class 0x80
+00.01.00 vid 18ca did 0027 display controller sub-class 0
+ XGI Technology; Volari Z11 XGI Compatible SVGA controller (xg27 core)
+
+64MB vga memory, resolution up to 1280x1024 @ 60 Hz. the video
+controller is undocumented and even the Linux driver is only available
+as a binary, so so far this port is only a cpu kernel.
+
+run video as normal svga; get framebuffer addr from pci (implement pci).
+
+uart0 is the shared jtag/uart thing with the usb mini b connector.
+uart1 is the rs232/rs485 ports.
+
+has non-ahci sata without going through pci
diff --git a/sys/src/9/kw/plug b/sys/src/9/kw/plug
new file mode 100755
index 000000000..7fe107ba6
--- /dev/null
+++ b/sys/src/9/kw/plug
@@ -0,0 +1,80 @@
+# sheeva plug, openrd-client, guruplug and others
+# based on marvell's kirkwood soc
+dev
+ root
+ cons
+ env
+ pipe
+ proc
+ mnt
+ srv
+ dup
+ rtc
+ arch
+ ssl
+ tls
+ cap
+ kprof
+ aoe
+ sd
+ fs
+ flash
+ twsi
+# pnp pci
+
+ ether netif
+ ip arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum inferno
+
+## draw screen vga vgax
+## mouse mouse
+## vga
+# kbmap
+## kbin
+
+ uart
+ usb
+
+link
+ ether1116 ethermii
+ archkw
+ ethermedium
+# no flash yet for guruplug
+ flashkw ecc
+ loopbackmedium
+ netdevmedium
+ usbehci usbehcikw
+
+ip
+ tcp
+ udp
+ ipifc
+ icmp
+ icmp6
+ ipmux
+
+misc
+ rdb
+ coproc
+ sdaoe sdscsi
+ softfpu
+ syscall
+ uartkw
+ ucalloc
+## vgavesa
+
+port
+ int cpuserver = 1;
+ int i8250freq = 3686000;
+
+boot cpu
+ tcp
+# paq
+
+bootdir
+ boot$CONF.out boot
+ /arm/bin/ip/ipconfig
+ /arm/bin/auth/factotum
+# /arm/bin/paqfs
+ /arm/bin/usb/usbd
+# nvram not needed any longer, it's in flash
+ nvram
diff --git a/sys/src/9/kw/plug.words b/sys/src/9/kw/plug.words
new file mode 100755
index 000000000..385b459f4
--- /dev/null
+++ b/sys/src/9/kw/plug.words
@@ -0,0 +1,104 @@
+global scale sheevaplug & guruplug
+
+marvell 88f6281 (feroceon kirkwood) SoC
+arm926ej-s rev 1 [56251311] (armv5tejl) 1.2GHz cpu
+
+l1 I & D VIVT caches 16K each: 4-way, 128 sets, 32-byte lines
+ l1 D is write-through, l1 I is write-back
+unified l2 PIPT cache 256K: 4-way, 2048 sets, 32-byte lines
+ potentially 512K: 8-way
+
+apparently the mmu walks the page tables in dram and won't look in the
+l2 cache. there is no hardware cache coherence, thus the l1 caches
+need to be flushed or invalidated when mmu mappings change, but l2
+only needs to be flushed or invalidated around dma operations and page
+table changes, and only the affected dma buffers and descriptors or
+page table entries need to be flushed or invalidated in l2.
+
+we arrange that device registers are uncached.
+
+be aware that cache operations act on cache lines (of CACHELINESZ
+bytes) as atomic units, so if you invalidate one word of a cache line,
+you invalidate the entire cache line, whether it's been written back
+(is clean) or not (is dirty). mixed data structures with parts
+maintained by hardware and other parts by software are especially
+tricky. we try to pad the initial hardware parts so that the software
+parts start in a new cache line.
+
+there are no video controllers so far, so this port is a cpu
+kernel only.
+
+512MB of dram at physical address 0
+512MB of nand flash
+16550 uart for console
+see http://www.marvell.com/files/products/embedded_processors/kirkwood/\
+ FS_88F6180_9x_6281_OpenSource.pdf, stored locally as
+ /public/doc/marvell/88f61xx.kirkwood.pdf
+
+If you plan to use flash, it would be wise to avoid touching the first
+megabyte, which contains u-boot, right up to 0x100000. There's a
+linux kernel from there to 0x400000, if you care. You'll also likely
+want to use paqfs rather than fossil or kfs for file systems in flash
+since there is no wear-levelling.
+
+The code is fairly heavy-handed with the use of barrier instructions
+(BARRIERS in assembler, coherence in C), partly in reaction to bad
+experience doing Power PC ports, but also just as precautions against
+modern processors, which may feel free to execute instructions out of
+order or some time later, store to memory out of order or some time
+later, otherwise break the model of traditional sequential processors,
+or any combination of the above.
+
+this plan 9 port is based on the port of native inferno to the
+sheevaplug by Salva Peiró (saoret.one@gmail.com) and Mechiel Lukkien
+(mechiel@ueber.net).
+
+___
+
+# type this once at u-boot, replacing 00504301c49e with your plug's
+# mac address; thereafter the plug will pxe boot:
+setenv bootdelay 2
+setenv bootcmd 'bootp; bootp; tftp 0x1000 /cfg/pxe/00504301c49e; bootp; tftp 0x800000; go 0x800000'
+saveenv
+
+# see /cfg/pxe/example-kw
+
+
+ physical mem map
+hex addr size what
+----
+0 512MB sdram
+
+80000000 512MB pcie mem # default
+c8010000 2K cesa sram
+d0000000 1MB internal regs default address at reset
+d8000000 128MB nand flash # actually 512MB addressed through this
+e8000000 128MB spi serial flash
+f0000000 128MB boot rom # default
+f0000000 16MB pcie io # mapped from 0xc0000000 by u-boot
+
+f1000000 1MB internal regs as mapped by u-boot
+f1000000 64K dram regs
+f1010000 64K uart, flashes, rtc, gpio, etc.
+f1030000 64K crypto accelerator (cesa)
+f1040000 64K pci-e regs
+f1050000 64K usb otg regs (ehci-like)
+f1070000 64K gbe regs
+f1080000 64K non-ahci sata regs
+f1090000 64K sdio regs
+
+f8000000 128MB boot device # default, mapped to 0 by u-boot
+f8000000 16MB spi flash # mapped by u-boot
+f9000000 8MB nand flash # on sheeva/openrd, mapped by u-boot
+fb000000 64KB crypto engine
+ff000000 16MB boot rom # u-boot
+
+ virtual mem map
+hex addr size what
+----
+0 512MB user process address space
+
+60000000 kzero, mapped to 0
+90000000 256MB pcie mem # mapped by u-boot
+c0000000 64KB pcie i/o # mapped by u-boot
+... as per physical map
diff --git a/sys/src/9/kw/random.c b/sys/src/9/kw/random.c
new file mode 100755
index 000000000..1f7c0983d
--- /dev/null
+++ b/sys/src/9/kw/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[128];
+ 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 predictable 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/kw/rebootcode.s b/sys/src/9/kw/rebootcode.s
new file mode 100755
index 000000000..0f4a9f98c
--- /dev/null
+++ b/sys/src/9/kw/rebootcode.s
@@ -0,0 +1,172 @@
+/*
+ * sheevaplug reboot code
+ *
+ * R11 is used by the loader as a temporary, so avoid it.
+ */
+#include "arm.s"
+
+/*
+ * Turn off MMU, then copy the new kernel to its correct location
+ * in physical memory. Then jump to the start of the kernel.
+ */
+
+/* main(PADDR(entry), PADDR(code), size); */
+TEXT main(SB), 1, $-4
+ MOVW $setR12(SB), R12
+
+ MOVW R0, p1+0(FP) /* destination, passed in R0 */
+
+ /* copy in arguments from frame */
+ MOVW R0, R8 /* entry point */
+ MOVW p2+4(FP), R9 /* source */
+ MOVW n+8(FP), R10 /* byte count */
+
+WAVE('R')
+ BL cachesoff(SB)
+ /* now back in 29- or 26-bit addressing, mainly for SB */
+
+ /* turn the MMU off */
+WAVE('e')
+ MOVW $KSEGM, R7
+ MOVW $PHYSDRAM, R0
+ BL _r15warp(SB)
+
+ BIC R7, R12 /* SB */
+ BIC R7, R13 /* SP */
+ /* don't care about R14 */
+
+WAVE('b')
+ BL mmuinvalidate(SB)
+WAVE('o')
+ BL mmudisable(SB)
+
+WAVE('o')
+ MOVW R9, R4 /* restore regs across function calls */
+ MOVW R10, R5
+ MOVW R8, R6
+
+ /* set up a new stack for local vars and memmove args */
+ MOVW R6, SP /* tiny trampoline stack */
+ SUB $(0x20 + 4), SP /* back up before a.out header */
+
+ MOVW R14, -48(SP) /* store return addr */
+ SUB $48, SP /* allocate stack frame */
+
+ MOVW R6, 44(SP) /* save dest/entry */
+ MOVW R5, 40(SP) /* save count */
+
+WAVE('t')
+
+ MOVW R6, 0(SP)
+ MOVW R6, 4(SP) /* push dest */
+ MOVW R6, R0
+ MOVW R4, 8(SP) /* push src */
+ MOVW R5, 12(SP) /* push size */
+ BL memmove(SB)
+
+ MOVW 44(SP), R6 /* restore R6 (dest/entry) */
+ MOVW 40(SP), R5 /* restore R5 (count) */
+WAVE('-')
+ /*
+ * flush caches
+ */
+ BL cacheuwbinv(SB)
+
+WAVE('>')
+WAVE('\r');
+WAVE('\n');
+/*
+ * jump to kernel entry point. Note the true kernel entry point is
+ * the virtual address KZERO|R6, but this must wait until
+ * the MMU is enabled by the kernel in l.s
+ */
+ ORR R6, R6 /* NOP: avoid link bug */
+ B (R6)
+
+/*
+ * turn the caches off, double map 0 & KZERO, invalidate TLBs, revert to
+ * tiny addresses. upon return, it will be safe to turn off the mmu.
+ */
+TEXT cachesoff(SB), 1, $-4
+ MOVW $(PsrDirq|PsrDfiq|PsrMsvc), R0
+ MOVW R0, CPSR
+ MOVW $KADDR(0x100-4), R7 /* just before this code */
+ MOVW R14, (R7) /* save link */
+
+ BL cacheuwbinv(SB)
+
+ MRC CpSC, 0, R0, C(CpCONTROL), C(0)
+ BIC $(CpCwb|CpCicache|CpCdcache|CpCalign), R0
+ MCR CpSC, 0, R0, C(CpCONTROL), C(0)
+ BARRIERS
+
+ /* redo double map of 0, KZERO */
+ MOVW $(L1+L1X(PHYSDRAM)), R4 /* address of PTE for 0 */
+ MOVW $PTEDRAM, R2 /* PTE bits */
+// MOVW $PTEIO, R2 /* PTE bits */
+ MOVW $PHYSDRAM, R3
+ MOVW $512, R5
+_ptrdbl:
+ ORR R3, R2, R1 /* first identity-map 0 to 0, etc. */
+ MOVW R1, (R4)
+ ADD $4, R4 /* bump PTE address */
+ ADD $MiB, R3 /* bump pa */
+ SUB.S $1, R5
+ BNE _ptrdbl
+
+ BARRIERS
+ MOVW $0, R0
+ MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvd), CpTLBinv
+ MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv
+ BARRIERS
+
+ /* back to 29- or 26-bit addressing, mainly for SB */
+ MRC CpSC, 0, R0, C(CpCONTROL), C(0)
+ BIC $(CpCd32|CpCi32), R0
+ MCR CpSC, 0, R0, C(CpCONTROL), C(0)
+ BARRIERS
+
+ MOVW $KADDR(0x100-4), R7 /* just before this code */
+ MOVW (R7), R14 /* restore link */
+ RET
+
+TEXT _r15warp(SB), 1, $-4
+ BIC $KSEGM, R14
+ ORR R0, R14
+ RET
+
+TEXT mmudisable(SB), 1, $-4
+ MRC CpSC, 0, R0, C(CpCONTROL), C(0)
+ BIC $(CpChv|CpCmmu|CpCdcache|CpCicache|CpCwb), R0
+ MCR CpSC, 0, R0, C(CpCONTROL), C(0)
+ BARRIERS
+ RET
+
+TEXT mmuinvalidate(SB), 1, $-4 /* invalidate all */
+ MOVW $0, R0
+ MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv
+ BARRIERS
+ RET
+
+TEXT cacheuwbinv(SB), 1, $-4 /* D+I writeback+invalidate */
+ BARRIERS
+ MOVW CPSR, R3 /* splhi */
+ ORR $(PsrDirq), R3, R1
+ MOVW R1, CPSR
+
+_uwbinv: /* D writeback+invalidate */
+ MRC CpSC, 0, PC, C(CpCACHE), C(CpCACHEwbi), CpCACHEtest
+ BNE _uwbinv
+
+ MOVW $0, R0 /* I invalidate */
+ MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEall
+ /* drain L1 write buffer, also drains L2 eviction buffer on sheeva */
+ BARRIERS
+
+ MCR CpSC, CpL2, R0, C(CpTESTCFG), C(CpTCl2flush), CpTCl2all
+ BARRIERS
+ MCR CpSC, CpL2, R0, C(CpTESTCFG), C(CpTCl2inv), CpTCl2all
+ BARRIERS
+
+ MOVW R3, CPSR /* splx */
+ RET
diff --git a/sys/src/9/kw/softfpu.c b/sys/src/9/kw/softfpu.c
new file mode 100755
index 000000000..b412a6bf1
--- /dev/null
+++ b/sys/src/9/kw/softfpu.c
@@ -0,0 +1,119 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+int
+fpudevprocio(Proc* proc, void* a, long n, uintptr offset, int write)
+{
+ /*
+ * Called from procdevtab.read and procdevtab.write
+ * allow user process access to the FPU registers.
+ * This is the only FPU routine which is called directly
+ * from the port code; it would be nice to have dynamic
+ * creation of entries in the device file trees...
+ */
+ USED(proc, a, n, offset, write);
+
+ return 0;
+}
+
+void
+fpunotify(Ureg*)
+{
+ /*
+ * Called when a note is about to be delivered to a
+ * user process, usually at the end of a system call.
+ * Note handlers are not allowed to use the FPU so
+ * the state is marked (after saving if necessary) and
+ * checked in the Device Not Available handler.
+ */
+}
+
+void
+fpunoted(void)
+{
+ /*
+ * Called from sysnoted() via the machine-dependent
+ * noted() routine.
+ * Clear the flag set above in fpunotify().
+ */
+}
+
+void
+fpusysrfork(Ureg*)
+{
+ /*
+ * Called early in the non-interruptible path of
+ * sysrfork() via the machine-dependent syscall() routine.
+ * Save the state so that it can be easily copied
+ * to the child process later.
+ */
+}
+
+void
+fpusysrforkchild(Proc*, Ureg*, Proc*)
+{
+ /*
+ * Called later in sysrfork() via the machine-dependent
+ * sysrforkchild() routine.
+ * Copy the parent FPU state to the child.
+ */
+}
+
+void
+fpuprocsave(Proc*)
+{
+ /*
+ * Called from sched() and sleep() via the machine-dependent
+ * procsave() routine.
+ * About to go in to the scheduler.
+ * If the process wasn't using the FPU
+ * there's nothing to do.
+ */
+}
+
+void
+fpuprocrestore(Proc*)
+{
+ /*
+ * The process has been rescheduled and is about to run.
+ * Nothing to do here right now. If the process tries to use
+ * the FPU again it will cause a Device Not Available
+ * exception and the state will then be restored.
+ */
+}
+
+void
+fpusysprocsetup(Proc*)
+{
+ /*
+ * Disable the FPU.
+ * Called from sysexec() via sysprocsetup() to
+ * set the FPU for the new process.
+ */
+}
+
+void
+fpuinit(void)
+{
+}
+
+int
+fpuemu(Ureg* ureg)
+{
+ int nfp;
+
+ if(waserror()){
+ splhi();
+ postnote(up, 1, up->errstr, NDebug);
+ return 1;
+ }
+ spllo();
+ nfp = fpiarm(ureg);
+ splhi();
+ poperror();
+
+ return nfp;
+}
diff --git a/sys/src/9/kw/syscall.c b/sys/src/9/kw/syscall.c
new file mode 100755
index 000000000..d1fcd9fc5
--- /dev/null
+++ b/sys/src/9/kw/syscall.c
@@ -0,0 +1,354 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "../port/systab.h"
+
+#include <tos.h>
+#include "ureg.h"
+
+#include "arm.h"
+
+typedef struct {
+ uintptr ip;
+ Ureg* arg0;
+ char* arg1;
+ char msg[ERRMAX];
+ Ureg* old;
+ Ureg ureg;
+} NFrame;
+
+/*
+ * Return user to state before notify()
+ */
+static void
+noted(Ureg* cur, uintptr arg0)
+{
+ NFrame *nf;
+ Ureg *nur;
+
+ 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;
+ fpunoted();
+
+ nf = up->ureg;
+
+ /* sanity clause */
+ if(!okaddr(PTR2UINT(nf), sizeof(NFrame), 0)){
+ qunlock(&up->debug);
+ pprint("bad ureg in noted %#p\n", nf);
+ pexit("Suicide", 0);
+ }
+
+ /* don't let user change system flags */
+ nur = &nf->ureg;
+ nur->psr &= PsrMask|PsrDfiq|PsrDirq;
+ nur->psr |= (cur->psr & ~(PsrMask|PsrDfiq|PsrDirq));
+
+ memmove(cur, nur, sizeof(Ureg));
+
+ switch((int)arg0){
+ case NCONT:
+ case NRSTR:
+ if(!okaddr(nur->pc, BY2WD, 0) || !okaddr(nur->sp, BY2WD, 0)){
+ qunlock(&up->debug);
+ pprint("suicide: trap in noted\n");
+ pexit("Suicide", 0);
+ }
+ up->ureg = nf->old;
+ qunlock(&up->debug);
+ break;
+ case NSAVE:
+ if(!okaddr(nur->pc, BY2WD, 0) || !okaddr(nur->sp, BY2WD, 0)){
+ qunlock(&up->debug);
+ pprint("suicide: trap in noted\n");
+ pexit("Suicide", 0);
+ }
+ qunlock(&up->debug);
+
+ splhi();
+ nf->arg1 = nf->msg;
+ nf->arg0 = &nf->ureg;
+ nf->ip = 0;
+ cur->sp = PTR2UINT(nf);
+ break;
+ default:
+ pprint("unknown noted arg %#p\n", arg0);
+ up->lastnote.flag = NDebug;
+ /*FALLTHROUGH*/
+ case NDFLT:
+ if(up->lastnote.flag == NDebug){
+ qunlock(&up->debug);
+ pprint("suicide: %s\n", up->lastnote.msg);
+ }
+ else
+ qunlock(&up->debug);
+ pexit(up->lastnote.msg, up->lastnote.flag != NDebug);
+ }
+}
+
+/*
+ * Call user, if necessary, with note.
+ * Pass user the Ureg struct and the note on his stack.
+ */
+int
+notify(Ureg* ureg)
+{
+ int l;
+ Note *n;
+ u32int s;
+ uintptr sp;
+ NFrame *nf;
+
+ if(up->procctl)
+ procctl(up);
+ if(up->nnote == 0)
+ return 0;
+
+ fpunotify(ureg);
+
+ 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-23) /* " pc=0x0123456789abcdef\0" */
+ l = ERRMAX-23;
+ snprint(n->msg + l, sizeof n->msg - l, " pc=%#lux", ureg->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 == nil){
+ qunlock(&up->debug);
+ pexit(n->msg, n->flag != NDebug);
+ }
+ if(!okaddr(PTR2UINT(up->notify), 1, 0)){
+ pprint("suicide: notify function address %#p\n", up->notify);
+ qunlock(&up->debug);
+ pexit("Suicide", 0);
+ }
+
+ sp = ureg->sp - sizeof(NFrame);
+ if(!okaddr(sp, sizeof(NFrame), 1)){
+ qunlock(&up->debug);
+ pprint("suicide: notify stack address %#p\n", sp);
+ pexit("Suicide", 0);
+ }
+
+ nf = UINT2PTR(sp);
+ memmove(&nf->ureg, ureg, sizeof(Ureg));
+ nf->old = up->ureg;
+ up->ureg = nf;
+ memmove(nf->msg, up->note[0].msg, ERRMAX);
+ nf->arg1 = nf->msg;
+ nf->arg0 = &nf->ureg;
+ nf->ip = 0;
+
+ ureg->sp = sp;
+ ureg->pc = PTR2UINT(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;
+}
+
+void
+syscall(Ureg* ureg)
+{
+ char *e;
+ u32int s;
+ ulong sp;
+ long ret;
+ int i, scallnr;
+ vlong startns, stopns;
+
+ if(!userureg(ureg))
+ panic("syscall: from kernel: pc %#lux r14 %#lux psr %#lux",
+ ureg->pc, ureg->r14, ureg->psr);
+
+ cycles(&up->kentry);
+
+ m->syscall++;
+ up->insyscall = 1;
+ up->pc = ureg->pc;
+ up->dbgreg = ureg;
+
+ scallnr = ureg->r0;
+ up->scallnr = scallnr;
+ if(scallnr == RFORK)
+ fpusysrfork(ureg);
+ spllo();
+ sp = ureg->sp;
+
+ if(up->procctl == Proc_tracesyscall){
+ /*
+ * Redundant validaddr. Do we care?
+ * Tracing syscalls is not exactly a fast path...
+ * Beware, validaddr currently does a pexit rather
+ * than an error if there's a problem; that might
+ * change in the future.
+ */
+ if(sp < (USTKTOP-BY2PG) || sp > (USTKTOP-sizeof(Sargs)-BY2WD))
+ validaddr(sp, sizeof(Sargs)+BY2WD, 0);
+
+ syscallfmt(scallnr, ureg->pc, (va_list)(sp+BY2WD));
+ up->procctl = Proc_stopme;
+ procctl(up);
+ if (up->syscalltrace)
+ free(up->syscalltrace);
+ up->syscalltrace = nil;
+ }
+
+ up->nerrlab = 0;
+ ret = -1;
+ startns = todget(nil);
+ if(!waserror()){
+ if(scallnr >= nsyscall){
+ 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];
+
+ /* iprint("%s: syscall %s\n", up->text, sysctab[scallnr]?sysctab[scallnr]:"huh?"); */
+
+ 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 [%d]: %d extra\n", scallnr, up->nerrlab);
+ for(i = 0; i < NERR; i++)
+ print("sp=%#p pc=%#p\n",
+ up->errlab[i].sp, up->errlab[i].pc);
+ panic("error stack");
+ }
+
+ /*
+ * 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->r0 = ret;
+
+ if(up->procctl == Proc_tracesyscall){
+ stopns = todget(nil);
+ up->procctl = Proc_stopme;
+ sysretfmt(scallnr, (va_list)(sp+BY2WD), ret, startns, stopns);
+ s = splhi();
+ procctl(up);
+ splx(s);
+ if(up->syscalltrace)
+ free(up->syscalltrace);
+ up->syscalltrace = nil;
+ }
+
+ up->insyscall = 0;
+ up->psstate = 0;
+
+ if(scallnr == NOTED)
+ noted(ureg, *(ulong*)(sp+BY2WD));
+
+ splhi();
+ if(scallnr != RFORK && (up->procctl || up->nnote))
+ notify(ureg);
+
+ /* if we delayed sched because we held a lock, sched now */
+ if(up->delaysched){
+ sched();
+ splhi();
+ }
+ kexit(ureg);
+}
+
+long
+execregs(ulong entry, ulong ssize, ulong nargs)
+{
+ ulong *sp;
+ Ureg *ureg;
+
+ sp = (ulong*)(USTKTOP - ssize);
+ *--sp = nargs;
+
+ ureg = up->dbgreg;
+// memset(ureg, 0, 15*sizeof(ulong));
+ ureg->r13 = (ulong)sp;
+ ureg->pc = entry;
+//print("%lud: EXECREGS pc %#ux sp %#ux nargs %ld\n", up->pid, ureg->pc, ureg->r13, nargs);
+
+ /*
+ * return the address of kernel/user shared data
+ * (e.g. clock stuff)
+ */
+ return USTKTOP-sizeof(Tos);
+}
+
+void
+sysprocsetup(Proc* p)
+{
+ fpusysprocsetup(p);
+}
+
+/*
+ * Craft a return frame which will cause the child to pop out of
+ * the scheduler in user mode with the return register zero. Set
+ * pc to point to a l.s return function.
+ */
+void
+forkchild(Proc *p, Ureg *ureg)
+{
+ Ureg *cureg;
+
+//print("%lud setting up for forking child %lud\n", up->pid, p->pid);
+ p->sched.sp = (ulong)p->kstack+KSTACK-sizeof(Ureg);
+ p->sched.pc = (ulong)forkret;
+
+ cureg = (Ureg*)(p->sched.sp);
+ memmove(cureg, ureg, sizeof(Ureg));
+
+ /* syscall returns 0 for child */
+ cureg->r0 = 0;
+
+ /* Things from bottom of syscall which were never executed */
+ p->psstate = 0;
+ p->insyscall = 0;
+
+ fpusysrforkchild(p, cureg, up);
+}
diff --git a/sys/src/9/kw/trap.c b/sys/src/9/kw/trap.c
new file mode 100755
index 000000000..f4d2c7756
--- /dev/null
+++ b/sys/src/9/kw/trap.c
@@ -0,0 +1,671 @@
+/*
+ * sheevaplug traps, exceptions, interrupts, system calls.
+ */
+#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 "arm.h"
+
+enum {
+ Ntimevec = 20, /* # of time buckets for each intr */
+ Nvecs = 256,
+};
+
+extern int notify(Ureg*);
+
+extern int ldrexvalid;
+
+typedef struct Vctl Vctl;
+typedef struct Vctl {
+ Vctl* next; /* handlers on this vector */
+ char *name; /* of driver, xallocated */
+ void (*f)(Ureg*, void*); /* handler to call */
+ void* a; /* argument to call it with */
+} Vctl;
+
+static Lock vctllock;
+static Vctl* vctl[32];
+
+uvlong ninterrupt;
+uvlong ninterruptticks;
+ulong intrtimes[Nvecs][Ntimevec];
+
+typedef struct Handler Handler;
+struct Handler {
+ void (*r)(Ureg*, void*);
+ void *a;
+ char name[KNAMELEN];
+};
+
+static Handler irqlo[32];
+static Handler irqhi[32];
+static Handler irqbridge[32];
+static Lock irqlock;
+static int probing, trapped;
+
+typedef struct Irq Irq;
+struct Irq {
+ ulong *irq;
+ ulong *irqmask;
+ Handler *irqvec;
+ int nirqvec;
+ char *name;
+};
+/* irq and irqmask are filled in by trapinit */
+static Irq irqs[] = {
+[Irqlo] {nil, nil, irqlo, nelem(irqlo), "lo"},
+[Irqhi] {nil, nil, irqhi, nelem(irqhi), "hi"},
+[Irqbridge] {nil, nil, irqbridge, nelem(irqbridge), "bridge"},
+};
+
+/*
+ * keep histogram of interrupt service times
+ */
+void
+intrtime(Mach*, int vno)
+{
+ ulong diff, x;
+
+ if (m == nil)
+ return;
+ x = perfticks();
+ diff = x - m->perf.intrts;
+ m->perf.intrts = x;
+
+ m->perf.inintr += diff;
+ if(up == nil && m->perf.inidle > diff)
+ m->perf.inidle -= diff;
+
+ if (m->cpuhz == 0) /* not set yet? */
+ return;
+ diff /= (m->cpuhz/1000000)*100; /* quantum = 100µsec */
+ if(diff >= Ntimevec)
+ diff = Ntimevec-1;
+ assert(vno >= 0 && vno < Nvecs);
+ intrtimes[vno][diff]++;
+}
+
+void
+intrfmtcounts(char *s, char *se)
+{
+ USED(s, se);
+}
+
+static void
+dumpcounts(void)
+{
+}
+
+void
+intrclear(int sort, int v)
+{
+ *irqs[sort].irq = ~(1 << v);
+}
+
+void
+intrmask(int sort, int v)
+{
+ *irqs[sort].irqmask &= ~(1 << v);
+}
+
+void
+intrunmask(int sort, int v)
+{
+ *irqs[sort].irqmask |= 1 << v;
+}
+
+static void
+maskallints(void)
+{
+ CpucsReg *cpu = (CpucsReg *)soc.cpu;
+ IntrReg *intr;
+
+ /* no fiq or ep in use */
+ intr = (IntrReg *)soc.intr;
+ intr->lo.irqmask = 0;
+ intr->hi.irqmask = 0;
+ cpu->irqmask = 0;
+ coherence();
+}
+
+void
+intrset(Handler *h, void (*f)(Ureg*, void*), void *a, char *name)
+{
+ if(h->r != nil) {
+// iprint("duplicate irq: %s (%#p)\n", h->name, h->r);
+ return;
+ }
+ h->r = f;
+ h->a = a;
+ strncpy(h->name, name, KNAMELEN-1);
+ h->name[KNAMELEN-1] = 0;
+}
+
+void
+intrunset(Handler *h)
+{
+ h->r = nil;
+ h->a = nil;
+ h->name[0] = 0;
+}
+
+void
+intrdel(Handler *h, void (*f)(Ureg*, void*), void *a, char *name)
+{
+ if(h->r != f || h->a != a || strcmp(h->name, name) != 0)
+ return;
+ intrunset(h);
+}
+
+void
+intrenable(int sort, int v, void (*f)(Ureg*, void*), void *a, char *name)
+{
+//iprint("enabling intr %d vec %d for %s\n", sort, v, name);
+ ilock(&irqlock);
+ intrset(&irqs[sort].irqvec[v], f, a, name);
+ intrunmask(sort, v);
+ iunlock(&irqlock);
+}
+
+void
+intrdisable(int sort, int v, void (*f)(Ureg*, void*), void* a, char *name)
+{
+ ilock(&irqlock);
+ intrdel(&irqs[sort].irqvec[v], f, a, name);
+ intrmask(sort, v);
+ iunlock(&irqlock);
+}
+
+/*
+ * called by trap to handle interrupts
+ */
+static void
+intrs(Ureg *ur, int sort)
+{
+ int i, s;
+ ulong ibits;
+ Handler *h;
+ Irq irq;
+
+ assert(sort >= 0 && sort < nelem(irqs));
+ irq = irqs[sort];
+ ibits = *irq.irq;
+ ibits &= *irq.irqmask;
+
+ for(i = 0; i < irq.nirqvec && ibits; i++)
+ if(ibits & (1<<i)){
+ h = &irq.irqvec[i];
+ if(h->r != nil){
+ h->r(ur, h->a);
+ splhi();
+ intrtime(m, sort*32 + i);
+ if (sort == Irqbridge && i == IRQcputimer0)
+ m->inclockintr = 1;
+ ibits &= ~(1<<i);
+ }
+ }
+ if(ibits != 0) {
+ iprint("spurious irq%s interrupt: %8.8lux\n", irq.name, ibits);
+ s = splfhi();
+ *irq.irq &= ibits;
+ splx(s);
+ }
+}
+
+void
+intrhi(Ureg *ureg, void*)
+{
+ intrs(ureg, Irqhi);
+}
+
+void
+intrbridge(Ureg *ureg, void*)
+{
+ intrs(ureg, Irqbridge);
+ intrclear(Irqlo, IRQ0bridge);
+}
+
+void
+trapinit(void)
+{
+ int i;
+ CpucsReg *cpu;
+ IntrReg *intr;
+ Vectorpage *page0 = (Vectorpage*)HVECTORS;
+
+ intr = (IntrReg *)soc.intr;
+ cpu = (CpucsReg *)soc.cpu;
+ irqs[Irqlo].irq = &intr->lo.irq;
+ irqs[Irqlo].irqmask = &intr->lo.irqmask;
+ irqs[Irqhi].irq = &intr->hi.irq;
+ irqs[Irqhi].irqmask = &intr->hi.irqmask;
+ irqs[Irqbridge].irq = &cpu->irq;
+ irqs[Irqbridge].irqmask = &cpu->irqmask;
+ coherence();
+
+ setr13(PsrMfiq, m->fiqstack + nelem(m->fiqstack));
+ setr13(PsrMirq, m->irqstack + nelem(m->irqstack));
+ setr13(PsrMabt, m->abtstack + nelem(m->abtstack));
+ setr13(PsrMund, m->undstack + nelem(m->undstack));
+
+ memmove(page0->vectors, vectors, sizeof page0->vectors);
+ memmove(page0->vtable, vtable, sizeof page0->vtable);
+ cacheuwbinv();
+ l2cacheuwbinv();
+
+ cpu->cpucfg &= ~Cfgvecinithi;
+
+ for(i = 0; i < nelem(irqlo); i++)
+ intrunset(&irqlo[i]);
+ for(i = 0; i < nelem(irqhi); i++)
+ intrunset(&irqhi[i]);
+ for(i = 0; i < nelem(irqbridge); i++)
+ intrunset(&irqbridge[i]);
+
+ /* disable all interrupts */
+ intr->lo.fiqmask = intr->hi.fiqmask = 0;
+ intr->lo.irqmask = intr->hi.irqmask = 0;
+ intr->lo.epmask = intr->hi.epmask = 0;
+ cpu->irqmask = 0;
+ coherence();
+
+ /* clear interrupts */
+ intr->lo.irq = intr->hi.irq = ~0;
+ cpu->irq = ~0;
+ coherence();
+
+ intrenable(Irqlo, IRQ0hisum, intrhi, nil, "hi");
+ intrenable(Irqlo, IRQ0bridge, intrbridge, nil, "bridge");
+
+ /* enable watchdog & access-error interrupts */
+ cpu->irqmask |= 1 << IRQcputimerwd | 1 << IRQaccesserr;
+ coherence();
+}
+
+static char *trapnames[PsrMask+1] = {
+ [ PsrMusr ] "user mode",
+ [ PsrMfiq ] "fiq interrupt",
+ [ PsrMirq ] "irq interrupt",
+ [ PsrMsvc ] "svc/swi exception",
+ [ PsrMabt ] "prefetch abort/data abort",
+ [ PsrMabt+1 ] "data abort",
+ [ PsrMund ] "undefined instruction",
+ [ PsrMsys ] "sys trap",
+};
+
+static char *
+trapname(int psr)
+{
+ char *s;
+
+ s = trapnames[psr & PsrMask];
+ if(s == nil)
+ s = "unknown trap number in psr";
+ return s;
+}
+
+/*
+ * called by trap to handle access faults
+ */
+static void
+faultarm(Ureg *ureg, uintptr va, int user, int read)
+{
+ int n, insyscall;
+ char buf[ERRMAX];
+ static int cnt, lastpid;
+ static ulong lastva;
+
+ if(up == nil) {
+ dumpregs(ureg);
+ panic("fault: nil up in faultarm, accessing %#p", va);
+ }
+ insyscall = up->insyscall;
+ up->insyscall = 1;
+
+ /* this is quite helpful during mmu and cache debugging */
+ if(va == lastva && up->pid == lastpid) {
+ ++cnt;
+ if (cnt >= 2)
+ /* fault() isn't fixing the underlying cause */
+ panic("fault: %d consecutive faults for va %#lux",
+ cnt+1, va);
+ } else {
+ cnt = 0;
+ lastva = va;
+ lastpid = up->pid;
+ }
+
+ n = fault(va, read);
+ if(n < 0){
+ if(!user){
+ dumpregs(ureg);
+ panic("fault: kernel accessing %#p", va);
+ }
+ /* don't dump registers; programs suicide all the time */
+ snprint(buf, sizeof buf, "sys: trap: fault %s va=%#p",
+ read? "read": "write", va);
+ postnote(up, 1, buf, NDebug);
+ }
+ up->insyscall = insyscall;
+}
+
+/*
+ * returns 1 if the instruction writes memory, 0 otherwise
+ */
+int
+writetomem(ulong inst)
+{
+ /* swap always write memory */
+ if((inst & 0x0FC00000) == 0x01000000)
+ return 1;
+
+ /* loads and stores are distinguished by bit 20 */
+ if(inst & (1<<20))
+ return 0;
+
+ return 1;
+}
+
+void
+trap(Ureg *ureg)
+{
+ int user, x, rv, rem;
+ ulong inst;
+ u32int fsr;
+ uintptr va;
+ char buf[ERRMAX];
+
+ if(up != nil)
+ rem = (char*)ureg - up->kstack;
+ else
+ rem = (char*)ureg - ((char*)m + sizeof(Mach));
+ if(rem < 256) {
+ dumpstack();
+ panic("trap %d bytes remaining, up %#p ureg %#p at pc %#lux",
+ rem, up, ureg, ureg->pc);
+ }
+
+ user = (ureg->psr & PsrMask) == PsrMusr;
+ if(user){
+ up->dbgreg = ureg;
+ cycles(&up->kentry);
+ }
+
+ if(ureg->type == PsrMabt+1)
+ ureg->pc -= 8;
+ else
+ ureg->pc -= 4;
+
+ m->inclockintr = 0;
+ switch(ureg->type) {
+ default:
+ panic("unknown trap %ld", ureg->type);
+ break;
+ case PsrMirq:
+ ldrexvalid = 0;
+ // splflo(); /* allow fast interrupts */
+ intrs(ureg, Irqlo);
+ m->intr++;
+ break;
+ case PsrMabt: /* prefetch fault */
+ ldrexvalid = 0;
+ faultarm(ureg, ureg->pc, user, 1);
+ break;
+ case PsrMabt+1: /* data fault */
+ ldrexvalid = 0;
+ va = farget();
+ inst = *(ulong*)(ureg->pc);
+ fsr = fsrget() & 0xf;
+ if (probing && !user) {
+ if (trapped++ > 0)
+ panic("trap: recursive probe %#lux", va);
+ ureg->pc += 4; /* continue at next instruction */
+ break;
+ }
+ switch(fsr){
+ case 0x0:
+ panic("vector exception at %#lux", ureg->pc);
+ break;
+ case 0x1:
+ case 0x3:
+ if(user){
+ snprint(buf, sizeof buf,
+ "sys: alignment: pc %#lux va %#p\n",
+ ureg->pc, va);
+ postnote(up, 1, buf, NDebug);
+ } else
+ panic("kernel alignment: pc %#lux va %#p", ureg->pc, va);
+ break;
+ case 0x2:
+ panic("terminal exception at %#lux", ureg->pc);
+ break;
+ case 0x4:
+ case 0x6:
+ case 0x8:
+ case 0xa:
+ case 0xc:
+ case 0xe:
+ panic("external abort %#ux pc %#lux addr %#px",
+ fsr, ureg->pc, va);
+ break;
+ case 0x5: /* translation fault, no section entry */
+ case 0x7: /* translation fault, no page entry */
+ faultarm(ureg, va, user, !writetomem(inst));
+ break;
+ case 0x9:
+ case 0xb:
+ /* domain fault, accessing something we shouldn't */
+ if(user){
+ snprint(buf, sizeof buf,
+ "sys: access violation: pc %#lux va %#p\n",
+ ureg->pc, va);
+ postnote(up, 1, buf, NDebug);
+ } else
+ panic("kernel access violation: pc %#lux va %#p",
+ ureg->pc, va);
+ break;
+ case 0xd:
+ case 0xf:
+ /* permission error, copy on write or real permission error */
+ faultarm(ureg, va, user, !writetomem(inst));
+ break;
+ }
+ break;
+ case PsrMund: /* undefined instruction */
+ if(user){
+ /* look for floating point instructions to interpret */
+ x = spllo();
+ rv = fpiarm(ureg);
+ splx(x);
+ if(rv == 0){
+ ldrexvalid = 0;
+ snprint(buf, sizeof buf,
+ "undefined instruction: pc %#lux",
+ ureg->pc);
+ postnote(up, 1, buf, NDebug);
+ }
+ }else{
+ iprint("undefined instruction: pc %#lux inst %#ux\n",
+ ureg->pc, ((u32int*)ureg->pc)[-2]);
+ panic("undefined instruction");
+ }
+ break;
+ }
+ splhi();
+
+ /* delaysched set because we held a lock or because our quantum ended */
+ if(up && up->delaysched && m->inclockintr){
+ ldrexvalid = 0;
+ sched();
+ splhi();
+ }
+
+ if(user){
+ if(up->procctl || up->nnote)
+ notify(ureg);
+ kexit(ureg);
+ }
+}
+
+int
+isvalidaddr(void *v)
+{
+ return (uintptr)v >= KZERO;
+}
+
+void
+dumplongs(char *msg, ulong *v, int n)
+{
+ int i, l;
+
+ l = 0;
+ iprint("%s at %.8p: ", msg, v);
+ for(i=0; i<n; i++){
+ if(l >= 4){
+ iprint("\n %.8p: ", v);
+ l = 0;
+ }
+ if(isvalidaddr(v)){
+ iprint(" %.8lux", *v++);
+ l++;
+ }else{
+ iprint(" invalid");
+ break;
+ }
+ }
+ iprint("\n");
+}
+
+static void
+dumpstackwithureg(Ureg *ureg)
+{
+ uintptr l, i, v, estack;
+ u32int *p;
+
+ iprint("ktrace /kernel/path %#.8lux %#.8lux %#.8lux # pc, sp, link\n",
+ ureg->pc, ureg->sp, ureg->r14);
+ delay(2000);
+ i = 0;
+ if(up != nil && (uintptr)&l <= (uintptr)up->kstack+KSTACK)
+ estack = (uintptr)up->kstack+KSTACK;
+ else if((uintptr)&l >= (uintptr)m->stack
+ && (uintptr)&l <= (uintptr)m+MACHSIZE)
+ estack = (uintptr)m+MACHSIZE;
+ else{
+ if(up != nil)
+ iprint("&up->kstack %#p &l %#p\n", up->kstack, &l);
+ else
+ iprint("&m %#p &l %#p\n", m, &l);
+ return;
+ }
+ for(l = (uintptr)&l; l < estack; l += sizeof(uintptr)){
+ v = *(uintptr*)l;
+ if(KTZERO < v && v < (uintptr)etext && !(v & 3)){
+ v -= sizeof(u32int); /* back up an instr */
+ p = (u32int*)v;
+ if((*p & 0x0f000000) == 0x0b000000){ /* BL instr? */
+ iprint("%#8.8lux=%#8.8lux ", l, v);
+ i++;
+ }
+ }
+ if(i == 4){
+ i = 0;
+ iprint("\n");
+ }
+ }
+ if(i)
+ iprint("\n");
+}
+
+/*
+ * Fill in enough of Ureg to get a stack trace, and call a function.
+ * Used by debugging interface rdb.
+ */
+void
+callwithureg(void (*fn)(Ureg*))
+{
+ Ureg ureg;
+
+ ureg.pc = getcallerpc(&fn);
+ ureg.sp = PTR2UINT(&fn);
+ fn(&ureg);
+}
+
+void
+dumpstack(void)
+{
+ callwithureg(dumpstackwithureg);
+}
+
+void
+dumpregs(Ureg* ureg)
+{
+ int s;
+
+ if (ureg == nil) {
+ iprint("trap: no user process\n");
+ return;
+ }
+ s = splhi();
+ iprint("trap: %s", trapname(ureg->type));
+ if(ureg != nil && (ureg->psr & PsrMask) != PsrMsvc)
+ iprint(" in %s", trapname(ureg->psr));
+ iprint("\n");
+ iprint("psr %8.8lux type %2.2lux pc %8.8lux link %8.8lux\n",
+ ureg->psr, ureg->type, ureg->pc, ureg->link);
+ iprint("R14 %8.8lux R13 %8.8lux R12 %8.8lux R11 %8.8lux R10 %8.8lux\n",
+ ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10);
+ iprint("R9 %8.8lux R8 %8.8lux R7 %8.8lux R6 %8.8lux R5 %8.8lux\n",
+ ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5);
+ iprint("R4 %8.8lux R3 %8.8lux R2 %8.8lux R1 %8.8lux R0 %8.8lux\n",
+ ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0);
+ iprint("stack is at %#p\n", ureg);
+ iprint("pc %#lux link %#lux\n", ureg->pc, ureg->link);
+
+ if(up)
+ iprint("user stack: %#p-%#p\n", up->kstack, up->kstack+KSTACK-4);
+ else
+ iprint("kernel stack: %8.8lux-%8.8lux\n",
+ (ulong)(m+1), (ulong)m+BY2PG-4);
+ dumplongs("stack", (ulong *)(ureg + 1), 16);
+ delay(2000);
+ dumpstack();
+ splx(s);
+}
+
+void
+idlehands(void)
+{
+ extern void _idlehands(void);
+
+ _idlehands();
+}
+
+vlong
+probeaddr(uintptr addr)
+{
+ vlong v;
+ static Lock fltlck;
+
+ ilock(&fltlck);
+ trapped = 0;
+ probing = 1;
+ coherence();
+
+ v = *(ulong *)addr; /* this may cause a fault */
+ USED(probing);
+ coherence();
+
+ probing = 0;
+ coherence();
+ if (trapped)
+ v = -1;
+ iunlock(&fltlck);
+ return v;
+}
diff --git a/sys/src/9/kw/uartkw.c b/sys/src/9/kw/uartkw.c
new file mode 100755
index 000000000..0b7e048d0
--- /dev/null
+++ b/sys/src/9/kw/uartkw.c
@@ -0,0 +1,362 @@
+/*
+ * marvell kirkwood uart (supposed to be a 16550)
+ */
+#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/uart.h"
+
+enum {
+ UartFREQ = 0, // xxx
+
+ IERrx = 1<<0,
+ IERtx = 1<<1,
+
+ IRRintrmask = (1<<4)-1,
+ IRRnointr = 1,
+ IRRthrempty = 2,
+ IRRrxdata = 4,
+ IRRrxstatus = 6,
+ IRRtimeout = 12,
+
+ IRRfifomask = 3<<6,
+ IRRfifoenable = 3<<6,
+
+ FCRenable = 1<<0,
+ FCRrxreset = 1<<1,
+ FCRtxreset = 1<<2,
+ /* reserved */
+ FCRrxtriggermask = 3<<6,
+ FCRrxtrigger1 = 0<<6,
+ FCRrxtrigger4 = 1<<6,
+ FCRrxtrigger8 = 2<<6,
+ FCRrxtrigger14 = 3<<6,
+
+ LCRbpcmask = 3<<0,
+ LCRbpc5 = 0<<0,
+ LCRbpc6 = 1<<0,
+ LCRbpc7 = 2<<0,
+ LCRbpc8 = 3<<0,
+ LCRstop2b = 1<<2,
+ LCRparity = 1<<3,
+ LCRparityeven = 1<<4,
+ LCRbreak = 1<<6,
+ LCRdivlatch = 1<<7,
+
+ LSRrx = 1<<0,
+ LSRrunerr = 1<<1,
+ LSRparerr = 1<<2,
+ LSRframeerr = 1<<3,
+ LSRbi = 1<<4,
+ LSRthre = 1<<5,
+ LSRtxempty = 1<<6,
+ LSRfifoerr = 1<<7,
+};
+
+extern PhysUart kwphysuart;
+
+typedef struct UartReg UartReg;
+struct UartReg
+{
+ union {
+ ulong thr;
+ ulong dll;
+ ulong rbr;
+ };
+ union {
+ ulong ier;
+ ulong dlh;
+ };
+ union {
+ ulong iir;
+ ulong fcr;
+ };
+ ulong lcr;
+ ulong mcr;
+ ulong lsr;
+ ulong scr;
+};
+
+typedef struct Ctlr Ctlr;
+struct Ctlr {
+ UartReg*regs;
+ int irq;
+ Lock;
+};
+
+static Ctlr kirkwoodctlr[] = {
+{
+ .regs = nil, /* filled in below */
+ .irq = IRQ1uart0, },
+};
+
+static Uart kirkwooduart[] = {
+{
+ .regs = &kirkwoodctlr[0],
+ .name = "eia0",
+ .freq = UartFREQ,
+ .phys = &kwphysuart,
+ .special= 0,
+ .console= 1,
+ .next = nil, },
+};
+
+static void
+kw_read(Uart *uart)
+{
+ Ctlr *ctlr = uart->regs;
+ UartReg *regs = ctlr->regs;
+ ulong lsr;
+ char c;
+
+ while ((lsr = regs->lsr) & LSRrx) {
+ if(lsr&LSRrunerr)
+ uart->oerr++;
+ if(lsr&LSRparerr)
+ uart->perr++;
+ if(lsr&LSRframeerr)
+ uart->ferr++;
+ c = regs->rbr;
+ if((lsr & (LSRbi|LSRframeerr|LSRparerr)) == 0)
+ uartrecv(uart, c);
+ }
+}
+
+static void
+kw_intr(Ureg*, void *arg)
+{
+ Uart *uart = arg;
+ Ctlr *ctlr = uart->regs;
+ UartReg *regs = ctlr->regs;
+ ulong v;
+
+ if(regs == 0) {
+ kirkwoodctlr[0].regs = (UartReg *)soc.uart[0];
+ regs = (UartReg *)soc.uart[0]; /* caution */
+ coherence();
+ }
+ v = regs->iir;
+ if(v & IRRthrempty)
+ uartkick(uart);
+ if(v & IRRrxdata)
+ kw_read(uart);
+
+ intrclear(Irqhi, ctlr->irq);
+}
+
+static Uart*
+kw_pnp(void)
+{
+ kirkwoodctlr[0].regs = (UartReg *)soc.uart[0];
+ coherence();
+ return kirkwooduart;
+}
+
+static void
+kw_enable(Uart* uart, int ie)
+{
+ Ctlr *ctlr = uart->regs;
+ UartReg *regs = ctlr->regs;
+
+ if(regs == 0) {
+ kirkwoodctlr[0].regs = (UartReg *)soc.uart[0];
+ regs = (UartReg *)soc.uart[0]; /* caution */
+ coherence();
+ }
+ USED(ie);
+ regs->fcr = FCRenable|FCRrxtrigger4;
+ regs->ier = IERrx|IERtx;
+ intrenable(Irqhi, ctlr->irq, kw_intr, uart, uart->name);
+
+ (*uart->phys->dtr)(uart, 1);
+ (*uart->phys->rts)(uart, 1);
+}
+
+static void
+kw_disable(Uart* uart)
+{
+ Ctlr *ctlr = uart->regs;
+
+ (*uart->phys->dtr)(uart, 0);
+ (*uart->phys->rts)(uart, 0);
+ (*uart->phys->fifo)(uart, 0);
+
+ intrdisable(Irqhi, ctlr->irq, kw_intr, uart, uart->name);
+}
+
+static void
+kw_kick(Uart* uart)
+{
+ Ctlr *ctlr = uart->regs;
+ UartReg *regs = ctlr->regs;
+ int i;
+
+ if(uart->cts == 0 || uart->blocked)
+ return;
+
+ for(i = 0; i < 16; i++) {
+ if((regs->lsr & LSRthre) == 0 ||
+ uart->op >= uart->oe && uartstageoutput(uart) == 0)
+ break;
+ regs->thr = *uart->op++;
+ }
+}
+
+static void
+kw_break(Uart* uart, int ms)
+{
+ USED(uart, ms);
+}
+
+static int
+kw_baud(Uart* uart, int baud)
+{
+ USED(uart, baud);
+ return 0;
+}
+
+static int
+kw_bits(Uart* uart, int bits)
+{
+ USED(uart, bits);
+ return 0;
+}
+
+static int
+kw_stop(Uart* uart, int stop)
+{
+ USED(uart, stop);
+ return 0;
+}
+
+static int
+kw_parity(Uart* uart, int parity)
+{
+ USED(uart, parity);
+ return 0;
+}
+
+static void
+kw_modemctl(Uart* uart, int on)
+{
+ USED(uart, on);
+}
+
+static void
+kw_rts(Uart* uart, int on)
+{
+ USED(uart, on);
+}
+
+static void
+kw_dtr(Uart* uart, int on)
+{
+ USED(uart, on);
+}
+
+static long
+kw_status(Uart* uart, void* buf, long n, long offset)
+{
+ USED(uart, buf, n, offset);
+ return 0;
+}
+
+static void
+kw_fifo(Uart* uart, int level)
+{
+ USED(uart, level);
+}
+
+static int
+kw_getc(Uart *uart)
+{
+ Ctlr *ctlr = uart->regs;
+ UartReg *regs = ctlr->regs;
+
+ while((regs->lsr&LSRrx) == 0)
+ ;
+ return regs->rbr;
+}
+
+static void
+kw_putc(Uart *uart, int c)
+{
+ Ctlr *ctlr = uart->regs;
+ UartReg *regs = ctlr->regs;
+
+ /* can be called from iprint, among many other places */
+ if(regs == 0) {
+ kirkwoodctlr[0].regs = (UartReg *)soc.uart[0];
+ regs = (UartReg *)soc.uart[0]; /* caution */
+ coherence();
+ }
+ while((regs->lsr&LSRthre) == 0)
+ ;
+ regs->thr = c;
+ coherence();
+}
+
+PhysUart kwphysuart = {
+ .name = "kirkwood",
+ .pnp = kw_pnp,
+ .enable = kw_enable,
+ .disable = kw_disable,
+ .kick = kw_kick,
+ .dobreak = kw_break,
+ .baud = kw_baud,
+ .bits = kw_bits,
+ .stop = kw_stop,
+ .parity = kw_parity,
+ .modemctl = kw_modemctl,
+ .rts = kw_rts,
+ .dtr = kw_dtr,
+ .status = kw_status,
+ .fifo = kw_fifo,
+ .getc = kw_getc,
+ .putc = kw_putc,
+};
+
+void
+uartkirkwoodconsole(void)
+{
+ Uart *uart;
+
+ uart = &kirkwooduart[0];
+ (*uart->phys->enable)(uart, 0);
+ uartctl(uart, "b115200 l8 pn s1 i1");
+ uart->console = 1;
+ consuart = uart;
+//serialputs("uart0 kirkwood\n", strlen("uart0 kirkwood\n"));
+}
+
+void
+serialputc(int c)
+{
+ int cnt, s;
+ UartReg *regs = (UartReg *)soc.uart[0];
+
+ s = splhi();
+ cnt = m->cpuhz;
+ if (cnt <= 0) /* cpuhz not set yet? */
+ cnt = 1000000;
+ while((regs->lsr & LSRthre) == 0 && --cnt > 0)
+ ;
+ regs->thr = c;
+ coherence();
+ delay(1);
+ splx(s);
+}
+
+void
+serialputs(char *p, int len)
+{
+ while(--len >= 0) {
+ if(*p == '\n')
+ serialputc('\r');
+ serialputc(*p++);
+ }
+}
diff --git a/sys/src/9/kw/ucalloc.c b/sys/src/9/kw/ucalloc.c
new file mode 100755
index 000000000..37e8564c0
--- /dev/null
+++ b/sys/src/9/kw/ucalloc.c
@@ -0,0 +1,135 @@
+/*
+ * allocate uncached memory
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+#include <pool.h>
+
+typedef struct Private Private;
+struct Private {
+ Lock;
+ char msg[256];
+ char* cur;
+};
+
+static Private ucprivate;
+
+static void
+ucpoolpanic(Pool* p, char* fmt, ...)
+{
+ va_list v;
+ Private *pv;
+ char msg[sizeof pv->msg];
+
+ pv = p->private;
+ va_start(v, fmt);
+ vseprint(pv->cur, &pv->msg[sizeof pv->msg], fmt, v);
+ va_end(v);
+ memmove(msg, pv->msg, sizeof msg);
+ iunlock(pv);
+ panic("%s", msg);
+}
+
+static void
+ucpoolprint(Pool* p, char* fmt, ...)
+{
+ va_list v;
+ Private *pv;
+
+ pv = p->private;
+ va_start(v, fmt);
+ pv->cur = vseprint(pv->cur, &pv->msg[sizeof pv->msg], fmt, v);
+ va_end(v);
+}
+
+static void
+ucpoolunlock(Pool* p)
+{
+ Private *pv;
+ char msg[sizeof pv->msg];
+
+ pv = p->private;
+ if(pv->cur == pv->msg){
+ iunlock(pv);
+ return;
+ }
+
+ memmove(msg, pv->msg, sizeof msg);
+ pv->cur = pv->msg;
+ iunlock(pv);
+
+ iprint("%.*s", sizeof pv->msg, msg);
+}
+
+static void
+ucpoollock(Pool* p)
+{
+ Private *pv;
+
+ pv = p->private;
+ ilock(pv);
+ pv->pc = getcallerpc(&p);
+ pv->cur = pv->msg;
+}
+
+static void*
+ucarena(usize size)
+{
+ void *uv, *v;
+
+ assert(size == 1*MiB);
+
+ mainmem->maxsize += 1*MiB;
+ if((v = mallocalign(1*MiB, 1*MiB, 0, 0)) == nil ||
+ (uv = mmuuncache(v, 1*MiB)) == nil){
+ free(v);
+ mainmem->maxsize -= 1*MiB;
+ return nil;
+ }
+ return uv;
+}
+
+static Pool ucpool = {
+ .name = "Uncached",
+ .maxsize = 4*MiB,
+ .minarena = 1*MiB-32,
+ .quantum = 32,
+ .alloc = ucarena,
+ .merge = nil,
+ .flags = /*POOL_TOLERANCE|POOL_ANTAGONISM|POOL_PARANOIA|*/0,
+
+ .lock = ucpoollock,
+ .unlock = ucpoolunlock,
+ .print = ucpoolprint,
+ .panic = ucpoolpanic,
+
+ .private = &ucprivate,
+};
+
+void
+ucfree(void* v)
+{
+ if(v == nil)
+ return;
+ poolfree(&ucpool, v);
+}
+
+void*
+ucalloc(usize size)
+{
+ assert(size < ucpool.minarena-128);
+
+ return poolallocalign(&ucpool, size, 32, 0, 0);
+}
+
+void*
+ucallocalign(usize size, int align, int span)
+{
+ assert(size < ucpool.minarena-128);
+
+ return poolallocalign(&ucpool, size, align, 0, span);
+}
diff --git a/sys/src/9/kw/uncached.h b/sys/src/9/kw/uncached.h
new file mode 100755
index 000000000..9fffd31fc
--- /dev/null
+++ b/sys/src/9/kw/uncached.h
@@ -0,0 +1,36 @@
+/*
+ * running the l2 cache as write-back and using cached memory for
+ * usb data structures yields spurious errors such as
+ *
+ * qhintr: td 0x60ee3d80 csw 0x8824a error 0x48 transaction error
+ *
+ * from usbehci. so, at least for now, we will use uncached memory until
+ * we sort out the write-back problems.
+ */
+#define free ucfree
+#define malloc myucalloc
+#define mallocz ucallocz
+#define smalloc myucalloc
+#define xspanalloc ucallocalign
+
+#define allocb ucallocb
+#define iallocb uciallocb
+#define freeb ucfreeb
+
+static void *
+ucallocz(uint n, int)
+{
+ char *p = ucalloc(n);
+
+ if (p)
+ memset(p, 0, n);
+ else
+ panic("ucalloc: out of memory");
+ return p;
+}
+
+static void *
+myucalloc(uint n)
+{
+ return ucallocz(n, 1);
+}
diff --git a/sys/src/9/kw/usbehci.h b/sys/src/9/kw/usbehci.h
new file mode 100755
index 000000000..cd1015a8c
--- /dev/null
+++ b/sys/src/9/kw/usbehci.h
@@ -0,0 +1,242 @@
+/* override default macros from ../port/usb.h */
+#undef dprint
+#undef ddprint
+#undef deprint
+#undef ddeprint
+#define dprint if(ehcidebug)print
+#define ddprint if(ehcidebug>1)print
+#define deprint if(ehcidebug || ep->debug)print
+#define ddeprint if(ehcidebug>1 || ep->debug>1)print
+
+typedef struct Ctlr Ctlr;
+typedef struct Ecapio Ecapio;
+typedef struct Eopio Eopio;
+typedef struct Edbgio Edbgio;
+typedef struct Isoio Isoio;
+typedef struct Poll Poll;
+typedef struct Qh Qh;
+typedef struct Qtree Qtree;
+
+#pragma incomplete Ctlr;
+#pragma incomplete Ecapio;
+#pragma incomplete Eopio;
+#pragma incomplete Edbgio;
+#pragma incomplete Isoio;
+#pragma incomplete Poll;
+#pragma incomplete Qh;
+#pragma incomplete Qtree;
+
+/*
+ * EHCI interface registers and bits
+ */
+enum
+{
+ Cnports = 0xF, /* nport bits in Ecapio parms. */
+ Cdbgportshift = 20, /* debug port in Ecapio parms. */
+ Cdbgportmask = 0xF,
+ C64 = 1, /* 64-bits, in Ecapio capparms. */
+ Ceecpshift = 8, /* extended capabilities ptr. in */
+ Ceecpmask = 8, /* the Ecapio capparms reg. */
+ Clegacy = 1, /* legacy support cap. id */
+ CLbiossem = 2, /* legacy cap. bios sem. */
+ CLossem = 3, /* legacy cap. os sem */
+
+ /* typed links */
+ Lterm = 1,
+ Litd = 0<<1,
+ Lqh = 1<<1,
+ Lsitd = 2<<1,
+ Lfstn = 3<<1, /* we don't use these */
+
+ /* Cmd reg. */
+ Cstop = 0x00000, /* stop running */
+ Crun = 0x00001, /* start operation */
+ Chcreset = 0x00002, /* host controller reset */
+ Cflsmask = 0x0000C, /* frame list size bits */
+ Cfls1024 = 0x00000, /* frame list size 1024 */
+ Cfls512 = 0x00004, /* frame list size 512 frames */
+ Cfls256 = 0x00008, /* frame list size 256 frames */
+ Cpse = 0x00010, /* periodic sched. enable */
+ Case = 0x00020, /* async sched. enable */
+ Ciasync = 0x00040, /* interrupt on async advance doorbell */
+ Citc1 = 0x10000, /* interrupt threshold ctl. 1 µframe */
+ Citc4 = 0x40000, /* same. 2 µframes */
+ /* ... */
+ Citc8 = 0x80000, /* same. 8 µframes (can go up to 64) */
+
+ /* Sts reg. */
+ Sasyncss = 0x08000, /* aync schedule status */
+ Speriodss = 0x04000, /* periodic schedule status */
+ Srecl = 0x02000, /* reclamnation (empty async sched.) */
+ Shalted = 0x01000, /* h.c. is halted */
+ Sasync = 0x00020, /* interrupt on async advance */
+ Sherr = 0x00010, /* host system error */
+ Sfrroll = 0x00008, /* frame list roll over */
+ Sportchg = 0x00004, /* port change detect */
+ Serrintr = 0x00002, /* error interrupt */
+ Sintr = 0x00001, /* interrupt */
+ Sintrs = 0x0003F, /* interrupts status */
+
+ /* Intr reg. */
+ Iusb = 0x01, /* intr. on usb */
+ Ierr = 0x02, /* intr. on usb error */
+ Iportchg = 0x04, /* intr. on port change */
+ Ifrroll = 0x08, /* intr. on frlist roll over */
+ Ihcerr = 0x10, /* intr. on host error */
+ Iasync = 0x20, /* intr. on async advance enable */
+ Iall = 0x3F, /* all interrupts */
+
+ /* Config reg. */
+ Callmine = 1, /* route all ports to us */
+
+ /* Portsc reg. */
+ Pspresent = 0x00000001, /* device present */
+ Psstatuschg = 0x00000002, /* Pspresent changed */
+ Psenable = 0x00000004, /* device enabled */
+ Pschange = 0x00000008, /* Psenable changed */
+ Psresume = 0x00000040, /* resume detected */
+ Pssuspend = 0x00000080, /* port suspended */
+ Psreset = 0x00000100, /* port reset */
+ Pspower = 0x00001000, /* port power on */
+ Psowner = 0x00002000, /* port owned by companion */
+ Pslinemask = 0x00000C00, /* line status bits */
+ Pslow = 0x00000400, /* low speed device */
+
+ /* Debug port csw reg. */
+ Cowner = 0x40000000, /* port owned by ehci */
+ Cenable = 0x10000000, /* debug port enabled */
+ Cdone = 0x00010000, /* request is done */
+ Cbusy = 0x00000400, /* port in use by a driver */
+ Cerrmask= 0x00000380, /* error code bits */
+ Chwerr = 0x00000100, /* hardware error */
+ Cterr = 0x00000080, /* transaction error */
+ Cfailed = 0x00000040, /* transaction did fail */
+ Cgo = 0x00000020, /* execute the transaction */
+ Cwrite = 0x00000010, /* request is a write */
+ Clen = 0x0000000F, /* data len */
+
+ /* Debug port pid reg. */
+ Prpidshift = 16, /* received pid */
+ Prpidmask = 0xFF,
+ Pspidshift = 8, /* sent pid */
+ Pspidmask = 0xFF,
+ Ptokshift = 0, /* token pid */
+ Ptokmask = 0xFF,
+
+ Ptoggle = 0x00008800, /* to update toggles */
+ Ptogglemask = 0x0000FF00,
+
+ /* Debug port addr reg. */
+ Adevshift = 8, /* device address */
+ Adevmask = 0x7F,
+ Aepshift = 0, /* endpoint number */
+ Aepmask = 0xF,
+};
+
+/*
+ * Capability registers (hw)
+ */
+struct Ecapio
+{
+ ulong cap; /* 00 controller capability register */
+ ulong parms; /* 04 structural parameters register */
+ ulong capparms; /* 08 capability parameters */
+ ulong portroute; /* 0c not on the CS5536 */
+};
+
+/*
+ * Debug port registers (hw)
+ */
+struct Edbgio
+{
+ ulong csw; /* control and status */
+ ulong pid; /* USB pid */
+ uchar data[8]; /* data buffer */
+ ulong addr; /* device and endpoint addresses */
+};
+
+struct Poll
+{
+ Lock;
+ Rendez;
+ int must;
+ int does;
+};
+
+struct Ctlr
+{
+ Rendez; /* for waiting to async advance doorbell */
+ Lock; /* for ilock. qh lists and basic ctlr I/O */
+ QLock portlck; /* for port resets/enable... (and doorbell) */
+ int active; /* in use or not */
+ Ecapio* capio; /* Capability i/o regs */
+ Eopio* opio; /* Operational i/o regs */
+
+ int nframes; /* 1024, 512, or 256 frames in the list */
+ ulong* frames; /* periodic frame list (hw) */
+ Qh* qhs; /* async Qh circular list for bulk/ctl */
+ Qtree* tree; /* tree of Qhs for the periodic list */
+ int ntree; /* number of dummy qhs in tree */
+ Qh* intrqhs; /* list of (not dummy) qhs in tree */
+ Isoio* iso; /* list of active Iso I/O */
+ ulong load;
+ ulong isoload;
+ int nintr; /* number of interrupts attended */
+ int ntdintr; /* number of intrs. with something to do */
+ int nqhintr; /* number of async td intrs. */
+ int nisointr; /* number of periodic td intrs. */
+ int nreqs;
+ Poll poll;
+};
+
+/*
+ * Kirkwood-specific stuff
+ */
+
+/*
+ * Operational registers (hw)
+ */
+struct Eopio
+{
+ ulong cmd; /* 00 command */
+ ulong sts; /* 04 status */
+ ulong intr; /* 08 interrupt enable */
+ ulong frno; /* 0c frame index */
+ ulong seg; /* 10 bits 63:32 of EHCI datastructs (unused) */
+ ulong frbase; /* 14 frame list base addr, 4096-byte boundary */
+ ulong link; /* 18 link for async list */
+ uchar d2c[0x40-0x1c]; /* 1c dummy */
+ ulong config; /* 40 1: all ports default-routed to this HC */
+ ulong portsc[1]; /* 44 Port status and control, one per port */
+
+ /*
+ * these are present on the Kirkwood USB controller.
+ * are they standard now? Marvell doesn't document them publically.
+ */
+ uchar _pad0[0x64-0x48];
+ ulong otgsc;
+ ulong usbmode;
+ ulong epsetupsts;
+ ulong epprime;
+ ulong epflush;
+ ulong epsts;
+ ulong epcompl;
+ ulong epctl[6];
+
+ /* freescale registers */
+ uchar _pad1[0x2c0-0x98];
+ ulong snoop1;
+ ulong snoop2;
+ ulong agecntthresh;
+ ulong prictl; /* priority control */
+ ulong sictl; /* system interface control */
+
+ uchar _pad2[0x3c0-0x2d4];
+ ulong ctl;
+};
+
+extern int ehcidebug;
+
+void ehcilinkage(Hci *hp);
+void ehcimeminit(Ctlr *ctlr);
+void ehcirun(Ctlr *ctlr, int on);
diff --git a/sys/src/9/kw/usbehcikw.c b/sys/src/9/kw/usbehcikw.c
new file mode 100755
index 000000000..ca5b9ceb8
--- /dev/null
+++ b/sys/src/9/kw/usbehcikw.c
@@ -0,0 +1,350 @@
+/*
+ * Kirkwood-specific code for
+ * USB Enhanced Host Controller Interface (EHCI) driver
+ * High speed USB 2.0.
+ */
+
+#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/usb.h"
+#include "usbehci.h"
+//#include "uncached.h"
+
+#define WINTARG(ctl) (((ctl) >> 4) & 017)
+#define WINATTR(ctl) (((ctl) >> 8) & 0377)
+#define WIN64KSIZE(ctl) (((ctl) >> 16) + 1)
+
+#define SIZETO64KSIZE(size) ((size) / (64*1024) - 1)
+
+enum {
+ Debug = 0,
+};
+
+typedef struct Kwusb Kwusb;
+typedef struct Kwusbtt Kwusbtt;
+typedef struct Usbwin Usbwin;
+
+/* kirkwood usb transaction translator registers? (undocumented) */
+struct Kwusbtt { /* at soc.ehci */
+ ulong id;
+ ulong hwgeneral;
+ ulong hwhost;
+ ulong hwdevice;
+ ulong hwtxbuf;
+ ulong hwrxbuf;
+ ulong hwtttxbuf;
+ ulong hwttrxbuf;
+};
+
+/* kirkwood usb bridge & phy registers */
+struct Kwusb { /* at offset 0x300 from soc.ehci */
+ ulong bcs; /* bridge ctl & sts */
+ uchar _pad0[0x310-0x304];
+
+ ulong bic; /* bridge intr. cause */
+ ulong bim; /* bridge intr. mask */
+ ulong _pad1;
+ ulong bea; /* bridge error addr. */
+ struct Usbwin {
+ ulong ctl; /* see Winenable in io.h */
+ ulong base;
+ ulong _pad2[2];
+ } win[4];
+ ulong phycfg; /* phy config. */
+ uchar _pad3[0x400-0x364];
+
+ ulong pwrctl; /* power control */
+ uchar _pad4[0x410-0x404];
+ ulong phypll; /* phy pll control */
+ uchar _pad5[0x420-0x414];
+ ulong phytxctl; /* phy transmit control */
+ uchar _pad6[0x430-0x424];
+ ulong phyrxctl; /* phy receive control */
+ uchar _pad7[0x440-0x434];
+ ulong phyivref; /* phy ivref control */
+};
+
+static Ctlr* ctlrs[Nhcis];
+
+static void
+addrmapdump(void)
+{
+ int i;
+ ulong ctl, targ, attr, size64k;
+ Kwusb *map;
+ Usbwin *win;
+
+ if (!Debug)
+ return;
+ map = (Kwusb *)(soc.ehci + 0x300);
+ for (i = 0; i < nelem(map->win); i++) {
+ win = &map->win[i];
+ ctl = win->ctl;
+ if (ctl & Winenable) {
+ targ = WINTARG(ctl);
+ attr = WINATTR(ctl);
+ size64k = WIN64KSIZE(ctl);
+ print("usbehci: address map window %d: "
+ "targ %ld attr %#lux size %,ld addr %#lux\n",
+ i, targ, attr, size64k * 64*1024, win->base);
+ }
+ }
+}
+
+/* assumes ctlr is ilocked */
+static void
+ctlrreset(Ctlr *ctlr)
+{
+ int i;
+ Eopio *opio;
+
+ opio = ctlr->opio;
+ opio->cmd |= Chcreset;
+ coherence();
+ /* wait for it to come out of reset */
+ for(i = 0; i < 100 && opio->cmd & Chcreset; i++)
+ delay(1);
+ if(i >= 100)
+ print("ehci %#p controller reset timed out\n", ctlr->capio);
+ /*
+ * Marvell errata FE-USB-340 workaround: 1 << 4 magic:
+ * disable streaming. Magic 3 (usb host mode) from the linux driver
+ * makes it work. Ick.
+ */
+ opio->usbmode |= 1 << 4 | 3;
+ coherence();
+}
+
+/*
+ * configure window `win' as 256MB dram with attribute `attr' and
+ * base address
+ */
+static void
+setaddrwin(Kwusb *kw, int win, int attr, ulong base)
+{
+ kw->win[win].ctl = Winenable | Targdram << 4 | attr << 8 |
+ SIZETO64KSIZE(256*MB) << 16;
+ kw->win[win].base = base;
+}
+
+static void
+ehcireset(Ctlr *ctlr)
+{
+ int i, amp, txvdd;
+ ulong v;
+ Eopio *opio;
+ Kwusb *kw;
+
+ ilock(ctlr);
+ dprint("ehci %#p reset\n", ctlr->capio);
+ opio = ctlr->opio;
+
+ kw = (Kwusb *)(soc.ehci + 0x300);
+ kw->bic = 0;
+ kw->bim = (1<<4) - 1; /* enable all defined intrs */
+ ctlrreset(ctlr);
+
+ /*
+ * clear high 32 bits of address signals if it's 64 bits capable.
+ * This is probably not needed but it does not hurt and others do it.
+ */
+ if((ctlr->capio->capparms & C64) != 0){
+ dprint("ehci: 64 bits\n");
+ opio->seg = 0;
+ }
+
+ /* requesting more interrupts per µframe may miss interrupts */
+ opio->cmd |= Citc8; /* 1 intr. per ms */
+ switch(opio->cmd & Cflsmask){
+ case Cfls1024:
+ ctlr->nframes = 1024;
+ break;
+ case Cfls512:
+ ctlr->nframes = 512;
+ break;
+ case Cfls256:
+ ctlr->nframes = 256;
+ break;
+ default:
+ panic("ehci: unknown fls %ld", opio->cmd & Cflsmask);
+ }
+ dprint("ehci: %d frames\n", ctlr->nframes);
+
+ /*
+ * set up the USB address map (bridge address decoding)
+ */
+ for (i = 0; i < nelem(kw->win); i++)
+ kw->win[i].ctl = kw->win[i].base = 0;
+ coherence();
+
+ setaddrwin(kw, 0, Attrcs0, 0);
+ setaddrwin(kw, 1, Attrcs1, 256*MB);
+ coherence();
+
+ if (Debug)
+ if (kw->bcs & (1 << 4))
+ print("usbehci: not swapping bytes\n");
+ else
+ print("usbehci: swapping bytes\n");
+ addrmapdump(); /* verify sanity */
+
+ kw->pwrctl |= 1 << 0 | 1 << 1; /* Pu | PuPll */
+ coherence();
+
+ /*
+ * Marvell guideline GL-USB-160.
+ */
+ kw->phypll |= 1 << 21; /* VCOCAL_START: PLL calibration */
+ coherence();
+ microdelay(100);
+ kw->phypll &= ~(1 << 21);
+
+ v = kw->phytxctl & ~(017 << 27 | 7); /* REG_EXT_FS_RCALL & AMP_2_0 */
+ switch (m->socrev) {
+ default:
+ print("usbehci: bad 6281 soc rev %d\n", m->socrev);
+ /* fall through */
+ case Socreva0:
+ amp = 4;
+ txvdd = 1;
+ break;
+ case Socreva1:
+ amp = 3;
+ txvdd = 3;
+ break;
+ }
+ /* REG_EXT_FS_RCALL_EN | REG_RCAL_START | AMP_2_0 */
+ kw->phytxctl = v | 1 << 26 | 1 << 12 | amp;
+ coherence();
+ microdelay(100);
+ kw->phytxctl &= ~(1 << 12);
+
+ v = kw->phyrxctl & ~(3 << 2 | 017 << 4); /* LPF_COEF_1_0 & SQ_THRESH_3_0 */
+ kw->phyrxctl = v | 1 << 2 | 8 << 4;
+
+ v = kw->phyivref & ~(3 << 8); /* TXVDD12 */
+ kw->phyivref = v | txvdd << 8;
+ coherence();
+
+ ehcirun(ctlr, 0);
+ ctlrreset(ctlr);
+
+ iunlock(ctlr);
+}
+
+static void
+setdebug(Hci*, int d)
+{
+ ehcidebug = d;
+}
+
+static void
+shutdown(Hci *hp)
+{
+ Ctlr *ctlr;
+ Eopio *opio;
+
+ ctlr = hp->aux;
+ ilock(ctlr);
+ ctlrreset(ctlr);
+
+ delay(100);
+ ehcirun(ctlr, 0);
+
+ opio = ctlr->opio;
+ opio->frbase = 0;
+ coherence();
+ iunlock(ctlr);
+}
+
+static void
+findehcis(void) /* actually just use fixed addresses on sheeva */
+{
+ int i;
+ Ctlr *ctlr;
+ static int already = 0;
+
+ if(already)
+ return;
+ already = 1;
+
+ ctlr = smalloc(sizeof(Ctlr));
+ /* the sheeva's usb 2.0 otg uses a superset of the ehci registers */
+ ctlr->capio = (Ecapio *)(soc.ehci + 0x100);
+ ctlr->opio = (Eopio *) (soc.ehci + 0x140);
+ dprint("usbehci: port %#p\n", ctlr->capio);
+
+ for(i = 0; i < Nhcis; i++)
+ if(ctlrs[i] == nil){
+ ctlrs[i] = ctlr;
+ break;
+ }
+ if(i == Nhcis)
+ print("ehci: bug: more than %d controllers\n", Nhcis);
+}
+
+static int
+reset(Hci *hp)
+{
+ static Lock resetlck;
+ int i;
+ Ctlr *ctlr;
+ Ecapio *capio;
+
+ ilock(&resetlck);
+ findehcis();
+
+ /*
+ * Any adapter matches if no hp->port is supplied,
+ * otherwise the ports must match.
+ */
+ ctlr = nil;
+ for(i = 0; i < Nhcis && ctlrs[i] != nil; i++){
+ ctlr = ctlrs[i];
+ if(ctlr->active == 0)
+ if(hp->port == 0 || hp->port == (uintptr)ctlr->capio){
+ ctlr->active = 1;
+ break;
+ }
+ }
+ iunlock(&resetlck);
+ if(ctlrs[i] == nil || i == Nhcis)
+ return -1;
+
+ hp->aux = ctlr;
+ hp->port = (uintptr)ctlr->capio;
+ hp->irq = IRQ0usb0;
+ hp->tbdf = 0;
+
+ capio = ctlr->capio;
+ hp->nports = capio->parms & Cnports;
+
+ ddprint("echi: %s, ncc %lud npcc %lud\n",
+ capio->parms & 0x10000 ? "leds" : "no leds",
+ (capio->parms >> 12) & 0xf, (capio->parms >> 8) & 0xf);
+ ddprint("ehci: routing %s, %sport power ctl, %d ports\n",
+ capio->parms & 0x40 ? "explicit" : "automatic",
+ capio->parms & 0x10 ? "" : "no ", hp->nports);
+
+ ehcireset(ctlr);
+ ehcimeminit(ctlr);
+
+ /*
+ * Linkage to the generic HCI driver.
+ */
+ ehcilinkage(hp);
+ hp->shutdown = shutdown;
+ hp->debug = setdebug;
+ return 0;
+}
+
+void
+usbehcilink(void)
+{
+ addhcitype("ehci", reset);
+}