summaryrefslogtreecommitdiff
path: root/sys/src/9/omap
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/omap
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/omap')
-rwxr-xr-xsys/src/9/omap/arch.c223
-rwxr-xr-xsys/src/9/omap/archomap.c1389
-rwxr-xr-xsys/src/9/omap/arm.h246
-rwxr-xr-xsys/src/9/omap/arm.s95
-rwxr-xr-xsys/src/9/omap/beagle82
-rwxr-xr-xsys/src/9/omap/cache.v7.s208
-rwxr-xr-xsys/src/9/omap/clock.c489
-rwxr-xr-xsys/src/9/omap/coproc.c133
-rwxr-xr-xsys/src/9/omap/dat.h303
-rwxr-xr-xsys/src/9/omap/devarch.c200
-rwxr-xr-xsys/src/9/omap/devcons.c1354
-rwxr-xr-xsys/src/9/omap/devdss.c180
-rwxr-xr-xsys/src/9/omap/devether.c530
-rwxr-xr-xsys/src/9/omap/devuart.c789
-rwxr-xr-xsys/src/9/omap/devusb.c1461
-rwxr-xr-xsys/src/9/omap/dma.c263
-rwxr-xr-xsys/src/9/omap/ether9221.c961
-rwxr-xr-xsys/src/9/omap/etherif.h41
-rwxr-xr-xsys/src/9/omap/fns.h178
-rwxr-xr-xsys/src/9/omap/fpi.c300
-rwxr-xr-xsys/src/9/omap/fpi.h61
-rwxr-xr-xsys/src/9/omap/fpiarm.c576
-rwxr-xr-xsys/src/9/omap/fpimem.c136
-rwxr-xr-xsys/src/9/omap/init9.s25
-rwxr-xr-xsys/src/9/omap/io.h82
-rwxr-xr-xsys/src/9/omap/kbd.c410
-rwxr-xr-xsys/src/9/omap/l.s569
-rwxr-xr-xsys/src/9/omap/lexception.s187
-rwxr-xr-xsys/src/9/omap/lproc.s47
-rwxr-xr-xsys/src/9/omap/main.c698
-rwxr-xr-xsys/src/9/omap/mem.h212
-rwxr-xr-xsys/src/9/omap/mkfile136
-rwxr-xr-xsys/src/9/omap/mmu.c496
-rwxr-xr-xsys/src/9/omap/mouse.c255
-rwxr-xr-xsys/src/9/omap/notes/movm.w22
-rwxr-xr-xsys/src/9/omap/nvrambin0 -> 512 bytes
-rwxr-xr-xsys/src/9/omap/random.c138
-rwxr-xr-xsys/src/9/omap/rebootcode.s209
-rwxr-xr-xsys/src/9/omap/screen.c791
-rwxr-xr-xsys/src/9/omap/screen.h105
-rwxr-xr-xsys/src/9/omap/softfpu.c119
-rwxr-xr-xsys/src/9/omap/syscall.c333
-rwxr-xr-xsys/src/9/omap/trap.c769
-rwxr-xr-xsys/src/9/omap/uarti8250.c850
-rwxr-xr-xsys/src/9/omap/ucalloc.c135
-rwxr-xr-xsys/src/9/omap/ucallocb.c152
-rwxr-xr-xsys/src/9/omap/uncached.h36
-rwxr-xr-xsys/src/9/omap/usbehci.h243
-rwxr-xr-xsys/src/9/omap/usbehciomap.c224
-rwxr-xr-xsys/src/9/omap/words202
50 files changed, 17643 insertions, 0 deletions
diff --git a/sys/src/9/omap/arch.c b/sys/src/9/omap/arch.c
new file mode 100755
index 000000000..a9b9537a9
--- /dev/null
+++ b/sys/src/9/omap/arch.c
@@ -0,0 +1,223 @@
+#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));
+ cycles(&t);
+ tos->kcycles += t - up->kentry;
+ tos->pcycles = up->pcycles;
+ tos->cyclefreq = m->cpuhz;
+ 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;
+
+// TODO: save and restore VFPv3 FP state once 5[cal] know the new registers.
+ fpuprocsave(p);
+}
+
+void
+procrestore(Proc* p)
+{
+ uvlong t;
+
+ if(p->kp)
+ return;
+ cycles(&t);
+ 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/omap/archomap.c b/sys/src/9/omap/archomap.c
new file mode 100755
index 000000000..0f20d50a6
--- /dev/null
+++ b/sys/src/9/omap/archomap.c
@@ -0,0 +1,1389 @@
+/*
+ * omap3530 SoC (e.g. beagleboard) architecture-specific stuff
+ *
+ * errata: usb port 3 cannot operate in ulpi mode, only serial or
+ * ulpi tll mode
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+#include "arm.h"
+
+#include "../port/netif.h"
+#include "etherif.h"
+#include "../port/flashif.h"
+#include "../port/usb.h"
+#include "usbehci.h"
+
+#define FREQSEL(x) ((x) << 4)
+
+typedef struct Cm Cm;
+typedef struct Cntrl Cntrl;
+typedef struct Gen Gen;
+typedef struct Gpio Gpio;
+typedef struct L3agent L3agent;
+typedef struct L3protreg L3protreg;
+typedef struct L3regs L3regs;
+typedef struct Prm Prm;
+typedef struct Usbotg Usbotg;
+typedef struct Usbtll Usbtll;
+
+/* omap3 non-standard usb stuff */
+struct Usbotg {
+ uchar faddr;
+ uchar power;
+ ushort intrtx;
+ ushort intrrx;
+ ushort intrtxe;
+ ushort intrrxe;
+ uchar intrusb;
+ uchar intrusbe;
+ ushort frame;
+ uchar index;
+ uchar testmode;
+
+ /* indexed registers follow; ignore for now */
+ uchar _pad0[0x400 - 0x10];
+
+ ulong otgrev;
+ ulong otgsyscfg;
+ ulong otgsyssts;
+ ulong otgifcsel; /* interface selection */
+ uchar _pad1[0x414 - 0x410];
+ ulong otgforcestdby;
+};
+
+enum {
+ /* power bits */
+ Hsen = 1<<5, /* high-speed enable */
+
+ /* testmode bits */
+ Forcehost = 1<<7, /* force host (vs peripheral) mode */
+ Forcehs = 1<<4, /* force high-speed at reset */
+
+ /* otgsyscfg bits */
+ Midle = 1<<12, /* no standby mode */
+ Sidle = 1<<3, /* no idle mode */
+// Softreset = 1<<1,
+
+ /* otgsyssts bits, per sysstatus */
+};
+
+struct Usbtll {
+ ulong revision; /* ro */
+ uchar _pad0[0x10-0x4];
+ ulong sysconfig;
+ ulong sysstatus; /* ro */
+
+ ulong irqstatus;
+ ulong irqenable;
+};
+
+enum {
+ /* sysconfig bits */
+ Softreset = 1<<1,
+
+ /* sysstatus bits */
+ Resetdone = 1<<0,
+ /* only in uhh->sysstatus */
+ Ehci_resetdone = 1<<2,
+ Ohci_resetdone = 1<<1,
+};
+
+/*
+ * an array of these structs is preceded by error_log at 0x20, control,
+ * error_clear_single, error_clear_multi. first struct is at offset 0x48.
+ */
+struct L3protreg { /* hw: an L3 protection region */
+ uvlong req_info_perm;
+ uvlong read_perm;
+ uvlong write_perm;
+ uvlong addr_match; /* ro? write this one last, then flush */
+};
+
+// TODO: set these permission bits (e.g., for usb)?
+enum {
+ Permusbhost = 1<<9,
+ Permusbotg = 1<<4,
+ Permsysdma = 1<<3,
+ Permmpu = 1<<1,
+};
+
+struct L3agent { /* hw registers */
+ uchar _pad0[0x20];
+ uvlong ctl;
+ uvlong sts;
+ uchar _pad1[0x58 - 0x30];
+ uvlong errlog;
+ uvlong errlogaddr;
+};
+
+struct L3regs {
+ L3protreg *base; /* base of array */
+ int upper; /* index maximum */
+ char *name;
+};
+L3regs l3regs[] = {
+ (L3protreg *)(PHYSL3GPMCPM+0x48), 7, "gpmc", /* known to be first */
+ (L3protreg *)(PHYSL3PMRT+0x48), 1, "rt", /* l3 config */
+ (L3protreg *)(PHYSL3OCTRAM+0x48), 7, "ocm ram",
+ (L3protreg *)(PHYSL3OCTROM+0x48), 1, "ocm rom",
+ (L3protreg *)(PHYSL3MAD2D+0x48), 7, "mad2d", /* die-to-die */
+ (L3protreg *)(PHYSL3IVA+0x48), 3, "iva2.2", /* a/v */
+};
+
+/*
+ * PRM_CLKSEL (0x48306d40) low 3 bits are system clock speed, assuming
+ * units of MHz: 0 = 12, 1 = 13, 2 = 19.2, 3 = 26, 4 = 38.4, 5 = 16.8
+ */
+
+struct Cm { /* clock management */
+ ulong fclken; /* ``functional'' clock enable */
+ ulong fclken2;
+ ulong fclken3;
+ uchar _pad0[0x10 - 0xc];
+
+ ulong iclken; /* ``interface'' clock enable */
+ ulong iclken2;
+ ulong iclken3;
+ uchar _pad1[0x20 - 0x1c];
+
+ ulong idlest; /* idle status */
+ ulong idlest2;
+ ulong idlest3;
+ uchar _pad2[0x30 - 0x2c];
+
+ ulong autoidle;
+ ulong autoidle2;
+ ulong autoidle3;
+ uchar _pad3[0x40 - 0x3c];
+
+ union {
+ ulong clksel[5];
+ struct unused {
+ ulong sleepdep;
+ ulong clkstctrl;
+ ulong clkstst;
+ };
+ uchar _pad4[0x70 - 0x40];
+ };
+ ulong clkoutctrl;
+};
+
+struct Prm { /* power & reset management */
+ uchar _pad[0x50];
+ ulong rstctrl;
+};
+
+struct Gpio {
+ ulong _pad0[4];
+ ulong sysconfig;
+ ulong sysstatus;
+
+ ulong irqsts1; /* for mpu */
+ ulong irqen1;
+ ulong wkupen;
+ ulong _pad1;
+ ulong irqsts2; /* for iva */
+ ulong irqen2;
+
+ ulong ctrl;
+
+ ulong oe;
+ ulong datain;
+ ulong dataout;
+
+ ulong lvldet0;
+ ulong lvldet1;
+ ulong risingdet;
+ ulong fallingdet;
+
+ /* rest are uninteresting */
+ ulong deben; /* debouncing enable */
+ ulong debtime;
+ ulong _pad2[2];
+
+ ulong clrirqen1;
+ ulong setirqen1;
+ ulong _pad3[2];
+
+ ulong clrirqen2;
+ ulong setirqen2;
+ ulong _pad4[2];
+
+ ulong clrwkupen;
+ ulong setwkupen;
+ ulong _pad5[2];
+
+ ulong clrdataout;
+ ulong setdataout;
+};
+
+enum {
+ /* clock enable & idle status bits */
+ Wkusimocp = 1 << 9, /* SIM card: uses 120MHz clock */
+ Wkwdt2 = 1 << 5, /* wdt2 clock enable bit for wakeup */
+ Wkgpio1 = 1 << 3, /* gpio1 " */
+ Wkgpt1 = 1 << 0, /* gpt1 " */
+
+ Dssl3l4 = 1 << 0, /* dss l3, l4 i clks */
+ Dsstv = 1 << 2, /* dss tv f clock */
+ Dss2 = 1 << 1, /* dss clock 2 */
+ Dss1 = 1 << 0, /* dss clock 1 */
+
+ Pergpio6 = 1 << 17,
+ Pergpio5 = 1 << 16,
+ Pergpio4 = 1 << 15,
+ Pergpio3 = 1 << 14,
+ Pergpio2 = 1 << 13,
+ Perwdt3 = 1 << 12, /* wdt3 clock enable bit for periphs */
+ Peruart3 = 1 << 11, /* console uart */
+ Pergpt9 = 1 << 10,
+ Pergpt8 = 1 << 9,
+ Pergpt7 = 1 << 8,
+ Pergpt6 = 1 << 7,
+ Pergpt5 = 1 << 6,
+ Pergpt4 = 1 << 5,
+ Pergpt3 = 1 << 4,
+ Pergpt2 = 1 << 3, /* gpt2 clock enable bit for periphs */
+
+ Perenable = Pergpio6 | Pergpio5 | Perwdt3 | Pergpt2 | Peruart3,
+
+ Usbhost2 = 1 << 1, /* 120MHz clock enable */
+ Usbhost1 = 1 << 0, /* 48MHz clock enable */
+ Usbhost = Usbhost1, /* iclock enable */
+ Usbhostidle = 1 << 1,
+ Usbhoststdby = 1 << 0,
+
+ Coreusbhsotg = 1 << 4, /* usb hs otg enable bit */
+ Core3usbtll = 1 << 2, /* usb tll enable bit */
+
+ /* core->idlest bits */
+ Coreusbhsotgidle = 1 << 5,
+ Coreusbhsotgstdby= 1 << 4,
+
+ Dplllock = 7,
+
+ /* mpu->idlest2 bits */
+ Dplllocked = 1,
+ Dpllbypassed = 0,
+
+ /* wkup->idlest bits */
+ Gpio1idle = 1 << 3,
+
+ /* dss->idlest bits */
+ Dssidle = 1 << 1,
+
+ Gpio1vidmagic = 1<<24 | 1<<8 | 1<<5, /* gpio 1 pins for video */
+};
+enum {
+ Rstgs = 1 << 1, /* global sw. reset */
+
+ /* fp control regs. most are read-only */
+ Fpsid = 0,
+ Fpscr, /* rw */
+ Mvfr1 = 6,
+ Mvfr0,
+ Fpexc, /* rw */
+};
+
+/* see ether9221.c for explanation */
+enum {
+ Ethergpio = 176,
+ Etherchanbit = 1 << (Ethergpio % 32),
+};
+
+/*
+ * these shift values are for the Cortex-A8 L1 cache (A=2, L=6) and
+ * the Cortex-A8 L2 cache (A=3, L=6).
+ * A = log2(# of ways), L = log2(bytes per cache line).
+ * see armv7 arch ref p. 1403.
+ *
+ * #define L1WAYSH 30
+ * #define L1SETSH 6
+ * #define L2WAYSH 29
+ * #define L2SETSH 6
+ */
+enum {
+ /*
+ * cache capabilities. write-back vs write-through is controlled
+ * by the Buffered bit in PTEs.
+ */
+ Cawt = 1 << 31,
+ Cawb = 1 << 30,
+ Cara = 1 << 29,
+ Cawa = 1 << 28,
+};
+
+struct Gen {
+ ulong padconf_off;
+ ulong devconf0;
+ uchar _pad0[0x68 - 8];
+ ulong devconf1;
+};
+
+struct Cntrl {
+ ulong _pad0;
+ ulong id;
+ ulong _pad1;
+ ulong skuid;
+};
+
+
+static char *
+devidstr(ulong)
+{
+ return "ARM Cortex-A8";
+}
+
+void
+archomaplink(void)
+{
+}
+
+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 (n == 0 || !ispow2(n))
+ i++;
+ return i;
+}
+
+void
+archconfinit(void)
+{
+ char *p;
+ ulong mhz;
+
+ assert(m != nil);
+ m->cpuhz = 500 * Mhz; /* beagle speed */
+ p = getconf("*cpumhz");
+ if (p) {
+ mhz = atoi(p) * Mhz;
+ if (mhz >= 100*Mhz && mhz <= 3000UL*Mhz)
+ m->cpuhz = mhz;
+ }
+ m->delayloop = m->cpuhz/2000; /* initial estimate */
+}
+
+static void
+prperm(uvlong perm)
+{
+ if (perm == MASK(16))
+ print("all");
+ else
+ print("%#llux", perm);
+}
+
+static void
+prl3region(L3protreg *pr, int r)
+{
+ int level, size, addrspace;
+ uvlong am, base;
+
+ if (r == 0)
+ am = 0;
+ else
+ am = pr->addr_match;
+ size = (am >> 3) & MASK(5);
+ if (r > 0 && size == 0) /* disabled? */
+ return;
+
+ print(" %d: perms req ", r);
+ prperm(pr->req_info_perm);
+ if (pr->read_perm == pr->write_perm && pr->read_perm == MASK(16))
+ print(" rw all");
+ else {
+ print(" read ");
+ prperm(pr->read_perm);
+ print(" write ");
+ prperm(pr->write_perm);
+ }
+ if (r == 0)
+ print(", all addrs level 0");
+ else {
+ size = 1 << size; /* 2^size */
+ level = (am >> 9) & 1;
+ if (r == 1)
+ level = 3;
+ else
+ level++;
+ addrspace = am & 7;
+ base = am & ~MASK(10);
+ print(", base %#llux size %dKB level %d addrspace %d",
+ base, size, level, addrspace);
+ }
+ print("\n");
+ delay(100);
+}
+
+
+/*
+ * dump the l3 interconnect firewall settings by protection region.
+ * mpu, sys dma and both usbs (0x21a) should be set in all read & write
+ * permission registers.
+ */
+static void
+dumpl3pr(void)
+{
+ int r;
+ L3regs *reg;
+ L3protreg *pr;
+
+ for (reg = l3regs; reg < l3regs + nelem(l3regs); reg++) {
+ print("%#p (%s) enabled l3 regions:\n", reg->base, reg->name);
+ for (r = 0; r <= reg->upper; r++)
+ prl3region(reg->base + r, r);
+ }
+if (0) { // TODO
+ /* touch up gpmc perms */
+ reg = l3regs; /* first entry is gpmc */
+ for (r = 0; r <= reg->upper; r++) {
+ pr = reg->base + r;
+ // TODO
+ }
+ print("%#p (%s) modified l3 regions:\n", reg->base, reg->name);
+ for (r = 0; r <= reg->upper; r++)
+ prl3region(reg->base + r, r);
+}
+}
+
+static void
+p16(uchar *p, ulong v)
+{
+ *p++ = v>>8;
+ *p = v;
+}
+
+static void
+p32(uchar *p, ulong v)
+{
+ *p++ = v>>24;
+ *p++ = v>>16;
+ *p++ = v>>8;
+ *p = v;
+}
+
+int
+archether(unsigned ctlrno, Ether *ether)
+{
+ switch(ctlrno) {
+ case 0:
+ /* there's no built-in ether on the beagle but igepv2 has 1 */
+ ether->type = "9221";
+ ether->ctlrno = ctlrno;
+ ether->irq = 34;
+ ether->nopt = 0;
+ ether->mbps = 100;
+ return 1;
+ }
+ return -1;
+}
+
+/*
+ * turn on all the necessary clocks on the SoC.
+ *
+ * a ``functional'' clock drives a device; an ``interface'' clock drives
+ * its communication with the rest of the system. so the interface
+ * clock must be enabled to reach the device's registers.
+ *
+ * dplls: 1 mpu, 2 iva2, 3 core, 4 per, 5 per2.
+ */
+
+static void
+configmpu(void)
+{
+ ulong clk, mhz, nmhz, maxmhz;
+ Cm *mpu = (Cm *)PHYSSCMMPU;
+ Cntrl *id = (Cntrl *)PHYSCNTRL;
+
+ if ((id->skuid & MASK(4)) == 8)
+ maxmhz = 720;
+ else
+ maxmhz = 600;
+ iprint("cpu capable of %ldMHz operation", maxmhz);
+
+ clk = mpu->clksel[0];
+ mhz = (clk >> 8) & MASK(11); /* configured speed */
+// iprint("\tfclk src %ld; dpll1 mult %ld (MHz) div %ld",
+// (clk >> 19) & MASK(3), mhz, clk & MASK(7));
+ iprint("; at %ldMHz", mhz);
+ nmhz = m->cpuhz / Mhz; /* nominal speed */
+ if (mhz == nmhz) {
+ iprint("\n");
+ return;
+ }
+
+ mhz = nmhz;
+ if (mhz > maxmhz) {
+ mhz = maxmhz;
+ iprint("; limiting operation to %ldMHz", mhz);
+ }
+
+ /* disable dpll1 lock mode; put into low-power bypass mode */
+ mpu->fclken2 = mpu->fclken2 & ~MASK(3) | 5;
+ coherence();
+ while (mpu->idlest2 != Dpllbypassed)
+ ;
+
+ /*
+ * there's a dance to change processor speed,
+ * prescribed in spruf98d §4.7.6.9.
+ */
+
+ /* just change multiplier; leave divider alone at 12 (meaning 13?) */
+ mpu->clksel[0] = clk & ~(MASK(11) << 8) | mhz << 8;
+ coherence();
+
+ /* set output divider (M2) in clksel[1]: leave at 1 */
+
+ /*
+ * u-boot calls us with just freqsel 3 (~1MHz) & dpll1 lock mode.
+ */
+ /* set FREQSEL */
+ mpu->fclken2 = mpu->fclken2 & ~FREQSEL(MASK(4)) | FREQSEL(3);
+ coherence();
+
+ /* set ramp-up delay to `fast' */
+ mpu->fclken2 = mpu->fclken2 & ~(MASK(2) << 8) | 3 << 8;
+ coherence();
+
+ /* set auto-recalibration (off) */
+ mpu->fclken2 &= ~(1 << 3);
+ coherence();
+
+ /* disable auto-idle: ? */
+ /* unmask clock intr: later */
+
+ /* enable dpll lock mode */
+ mpu->fclken2 |= Dplllock;
+ coherence();
+ while (mpu->idlest2 != Dplllocked)
+ ;
+ delay(200); /* allow time for speed to ramp up */
+
+ if (((mpu->clksel[0] >> 8) & MASK(11)) != mhz)
+ panic("mpu clock speed change didn't stick");
+ iprint("; now at %ldMHz\n", mhz);
+}
+
+static void
+configpll(void)
+{
+ int i;
+ Cm *pll = (Cm *)PHYSSCMPLL;
+
+ pll->clkoutctrl |= 1 << 7; /* enable sys_clkout2 */
+ coherence();
+ delay(10);
+
+ /*
+ * u-boot calls us with just freqsel 3 (~1MHz) & lock mode
+ * for both dplls (3 & 4). ensure that.
+ */
+ if ((pll->idlest & 3) != 3) {
+ /* put dpll[34] into low-power bypass mode */
+ pll->fclken = pll->fclken & ~(MASK(3) << 16 | MASK(3)) |
+ 1 << 16 | 5;
+ coherence();
+ while (pll->idlest & 3) /* wait for both to bypass or stop */
+ ;
+
+ pll->fclken = (FREQSEL(3) | Dplllock) << 16 |
+ FREQSEL(3) | Dplllock;
+ coherence();
+ while ((pll->idlest & 3) != 3) /* wait for both to lock */
+ ;
+ }
+
+ /*
+ * u-boot calls us with just freqsel 1 (default but undefined)
+ * & stop mode for dpll5. try to lock it at 120MHz.
+ */
+ if (!(pll->idlest2 & Dplllocked)) {
+ /* force dpll5 into low-power bypass mode */
+ pll->fclken2 = 3 << 8 | FREQSEL(1) | 1;
+ coherence();
+ for (i = 0; pll->idlest2 & Dplllocked && i < 20; i++)
+ delay(50);
+ if (i >= 20)
+ iprint(" [dpll5 failed to stop]");
+
+ /*
+ * CORE_CLK is 26MHz.
+ */
+ pll->clksel[4-1] = 120 << 8 | 12; /* M=120, N=12+1 */
+ /* M2 divisor: 120MHz clock is exactly the DPLL5 clock */
+ pll->clksel[5-1] = 1;
+ coherence();
+
+ pll->fclken2 = 3 << 8 | FREQSEL(1) | Dplllock; /* def. freq */
+ coherence();
+
+ for (i = 0; !(pll->idlest2 & Dplllocked) && i < 20; i++)
+ delay(50);
+ if (i >= 20)
+ iprint(" [dpll5 failed to lock]");
+ }
+ if (!(pll->idlest2 & (1<<1)))
+ iprint(" [no 120MHz clock]");
+ if (!(pll->idlest2 & (1<<3)))
+ iprint(" [no dpll5 120MHz clock output]");
+}
+
+static void
+configper(void)
+{
+ Cm *per = (Cm *)PHYSSCMPER;
+
+ per->clksel[0] &= ~MASK(8); /* select 32kHz clock for GPTIMER2-9 */
+
+ per->iclken |= Perenable;
+ coherence();
+ per->fclken |= Perenable;
+ coherence();
+ while (per->idlest & Perenable)
+ ;
+
+ per->autoidle = 0;
+ coherence();
+}
+
+static void
+configwkup(void)
+{
+ Cm *wkup = (Cm *)PHYSSCMWKUP;
+
+ /* select 32kHz clock (not system clock) for GPTIMER1 */
+ wkup->clksel[0] &= ~1;
+
+ wkup->iclken |= Wkusimocp | Wkwdt2 | Wkgpt1;
+ coherence();
+ wkup->fclken |= Wkusimocp | Wkwdt2 | Wkgpt1;
+ coherence();
+ while (wkup->idlest & (Wkusimocp | Wkwdt2 | Wkgpt1))
+ ;
+}
+
+static void
+configusb(void)
+{
+ int i;
+ Cm *usb = (Cm *)PHYSSCMUSB;
+
+ /*
+ * make the usb registers accessible without address faults,
+ * notably uhh, ochi & ehci. tll seems to be separate & otg is okay.
+ */
+ usb->iclken |= Usbhost;
+ coherence();
+ usb->fclken |= Usbhost1 | Usbhost2; /* includes 120MHz clock */
+ coherence();
+ for (i = 0; usb->idlest & Usbhostidle && i < 20; i++)
+ delay(50);
+ if (i >= 20)
+ iprint(" [usb inaccessible]");
+}
+
+static void
+configcore(void)
+{
+ Cm *core = (Cm *)PHYSSCMCORE;
+
+ /*
+ * make the usb tll registers accessible.
+ */
+ core->iclken |= Coreusbhsotg;
+ core->iclken3 |= Core3usbtll;
+ coherence();
+ core->fclken3 |= Core3usbtll;
+ coherence();
+ delay(100);
+ while (core->idlest & Coreusbhsotgidle)
+ ;
+ if (core->idlest3 & Core3usbtll)
+ iprint(" [no usb tll]");
+}
+
+static void
+configclks(void)
+{
+ int s;
+ Gen *gen = (Gen *)PHYSSCMPCONF;
+
+ delay(20);
+ s = splhi();
+ configmpu(); /* sets cpu clock rate, turns on dplls 1 & 2 */
+
+ /*
+ * the main goal is to get enough clocks running, in the right order,
+ * so that usb has all the necessary clock signals.
+ */
+ iprint("clocks:");
+ iprint(" usb");
+ configusb(); /* starts usb clocks & 120MHz clock */
+ iprint(", pll");
+ configpll(); /* starts dplls 3, 4 & 5 & 120MHz clock */
+ iprint(", wakeup");
+ configwkup(); /* starts timer clocks and usim clock */
+ iprint(", per");
+ configper(); /* starts timer & gpio (ether) clocks */
+ iprint(", core");
+ configcore(); /* starts usb tll */
+ iprint("\n");
+
+ gen->devconf0 |= 1 << 1 | 1 << 0; /* dmareq[01] edge sensitive */
+ /* make dmareq[2-6] edge sensitive */
+ gen->devconf1 |= 1 << 23 | 1 << 22 | 1 << 21 | 1 << 8 | 1 << 7;
+ coherence();
+ splx(s);
+ delay(20);
+}
+
+static void
+resetwait(ulong *reg)
+{
+ long bound;
+
+ for (bound = 400*Mhz; !(*reg & Resetdone) && bound > 0; bound--)
+ ;
+ if (bound <= 0)
+ iprint("archomap: Resetdone didn't come ready\n");
+}
+
+/*
+ * gpio irq 1 goes to the mpu intr ctlr; irq 2 goes to the iva's.
+ * this stuff is magic and without it, we won't get irq 34 interrupts
+ * from the 9221 ethernet controller.
+ */
+static void
+configgpio(void)
+{
+ Gpio *gpio = (Gpio *)PHYSGPIO6;
+
+ gpio->sysconfig = Softreset;
+ coherence();
+ resetwait(&gpio->sysstatus);
+
+ gpio->ctrl = 1<<1 | 0; /* enable this gpio module, gating ratio 1 */
+ gpio->oe |= Etherchanbit; /* cfg ether pin as input */
+ coherence();
+
+ gpio->irqen1 = Etherchanbit; /* channel # == pin # */
+ gpio->irqen2 = 0;
+
+ gpio->lvldet0 = Etherchanbit; /* enable irq ass'n on low det'n */
+ gpio->lvldet1 = 0; /* disable irq ass'n on high det'n */
+ gpio->risingdet = 0; /* enable irq rising edge det'n */
+ gpio->fallingdet = 0; /* disable irq falling edge det'n */
+
+ gpio->wkupen = 0;
+
+ gpio->deben = 0; /* no de-bouncing */
+ gpio->debtime = 0;
+ coherence();
+
+ gpio->irqsts1 = ~0; /* dismiss all outstanding intrs */
+ gpio->irqsts2 = ~0;
+ coherence();
+}
+
+void
+configscreengpio(void)
+{
+ Cm *wkup = (Cm *)PHYSSCMWKUP;
+ Gpio *gpio = (Gpio *)PHYSGPIO1;
+
+ /* no clocksel needed */
+ wkup->iclken |= Wkgpio1;
+ coherence();
+ wkup->fclken |= Wkgpio1; /* turn gpio clock on */
+ coherence();
+ // wkup->autoidle |= Wkgpio1; /* set gpio clock on auto */
+ wkup->autoidle = 0;
+ coherence();
+ while (wkup->idlest & Gpio1idle)
+ ;
+
+ /*
+ * 0 bits in oe are output signals.
+ * enable output for gpio 1 (first gpio) video magic pins.
+ */
+ gpio->oe &= ~Gpio1vidmagic;
+ coherence();
+ gpio->dataout |= Gpio1vidmagic; /* set output pins to 1 */
+ coherence();
+ delay(50);
+}
+
+void
+screenclockson(void)
+{
+ Cm *dss = (Cm *)PHYSSCMDSS;
+
+ dss->iclken |= Dssl3l4;
+ coherence();
+ dss->fclken = Dsstv | Dss2 | Dss1;
+ coherence();
+ /* tv fclk is dpll4 clk; dpll4 m4 divide factor for dss1 fclk is 2 */
+ dss->clksel[0] = 1<<12 | 2;
+ coherence();
+ delay(50);
+ while (dss->idlest & Dssidle)
+ ;
+}
+
+void
+gpioirqclr(void)
+{
+ Gpio *gpio = (Gpio *)PHYSGPIO6;
+
+ gpio->irqsts1 = gpio->irqsts1;
+ coherence();
+}
+
+static char *
+l1iptype(uint type)
+{
+ static char *types[] = {
+ "reserved",
+ "asid-tagged VIVT",
+ "VIPT",
+ "PIPT",
+ };
+
+ if (type >= nelem(types) || types[type] == nil)
+ return "GOK";
+ return types[type];
+}
+
+void
+cacheinfo(int level, Memcache *cp)
+{
+ ulong setsways;
+
+ /* select cache level */
+ cpwrsc(CpIDcssel, CpID, CpIDid, 0, (level - 1) << 1);
+
+ setsways = cprdsc(CpIDcsize, CpID, CpIDid, 0);
+ cp->l1ip = cprdsc(0, CpID, CpIDidct, CpIDct);
+ cp->level = level;
+ cp->nways = ((setsways >> 3) & MASK(10)) + 1;
+ cp->nsets = ((setsways >> 13) & MASK(15)) + 1;
+ cp->log2linelen = (setsways & MASK(2)) + 2 + 2;
+ cp->linelen = 1 << cp->log2linelen;
+ cp->setsways = setsways;
+
+ cp->setsh = cp->log2linelen;
+ cp->waysh = 32 - log2(cp->nways);
+}
+
+static void
+prcachecfg(void)
+{
+ int cache;
+ Memcache mc;
+
+ for (cache = 1; cache <= 2; cache++) {
+ cacheinfo(cache, &mc);
+ iprint("l%d: %d ways %d sets %d bytes/line",
+ mc.level, mc.nways, mc.nsets, mc.linelen);
+ if (mc.linelen != CACHELINESZ)
+ iprint(" *should* be %d", CACHELINESZ);
+ if (mc.setsways & Cawt)
+ iprint("; can WT");
+ if (mc.setsways & Cawb)
+ iprint("; can WB");
+#ifdef COMPULSIVE /* both caches can do this */
+ if (mc.setsways & Cara)
+ iprint("; can read-allocate");
+#endif
+ if (mc.setsways & Cawa)
+ iprint("; can write-allocate");
+ if (cache == 1)
+ iprint("; l1 I policy %s",
+ l1iptype((mc.l1ip >> 14) & MASK(2)));
+ iprint("\n");
+ }
+}
+
+static char *
+subarch(int impl, uint sa)
+{
+ static char *armarchs[] = {
+ "VFPv1 (pre-armv7)",
+ "VFPv2 (pre-armv7)",
+ "VFPv3+ with common VFP subarch v2",
+ "VFPv3+ with null subarch",
+ "VFPv3+ with common VFP subarch v3",
+ };
+
+ if (impl != 'A' || sa >= nelem(armarchs))
+ return "GOK";
+ else
+ return armarchs[sa];
+}
+
+/*
+ * padconf bits in a short, 2 per long register
+ * 15 wakeupevent
+ * 14 wakeupenable
+ * 13 offpulltypeselect
+ * 12 offpulludenable
+ * 11 offoutvalue
+ * 10 offoutenable
+ * 9 offenable
+ * 8 inputenable
+ * 4 pulltypeselect
+ * 3 pulludenable
+ * 2-0 muxmode
+ *
+ * see table 7-5 in §7.4.4.3 of spruf98d
+ */
+
+enum {
+ /* pad config register bits */
+ Inena = 1 << 8, /* input enable */
+ Indis = 0 << 8, /* input disable */
+ Ptup = 1 << 4, /* pull type up */
+ Ptdown = 0 << 4, /* pull type down */
+ Ptena = 1 << 3, /* pull type selection is active */
+ Ptdis = 0 << 3, /* pull type selection is inactive */
+ Muxmode = MASK(3),
+
+ /* pad config registers relevant to flash */
+ GpmcA1 = 0x4800207A,
+ GpmcA2 = 0x4800207C,
+ GpmcA3 = 0x4800207E,
+ GpmcA4 = 0x48002080,
+ GpmcA5 = 0x48002082,
+ GpmcA6 = 0x48002084,
+ GpmcA7 = 0x48002086,
+ GpmcA8 = 0x48002088,
+ GpmcA9 = 0x4800208A,
+ GpmcA10 = 0x4800208C,
+ GpmcD0 = 0x4800208E,
+ GpmcD1 = 0x48002090,
+ GpmcD2 = 0x48002092,
+ GpmcD3 = 0x48002094,
+ GpmcD4 = 0x48002096,
+ GpmcD5 = 0x48002098,
+ GpmcD6 = 0x4800209A,
+ GpmcD7 = 0x4800209C,
+ GpmcD8 = 0x4800209E,
+ GpmcD9 = 0x480020A0,
+ GpmcD10 = 0x480020A2,
+ GpmcD11 = 0x480020A4,
+ GpmcD12 = 0x480020A6,
+ GpmcD13 = 0x480020A8,
+ GpmcD14 = 0x480020AA,
+ GpmcD15 = 0x480020AC,
+ GpmcNCS0 = 0x480020AE,
+ GpmcNCS1 = 0x480020B0,
+ GpmcNCS2 = 0x480020B2,
+ GpmcNCS3 = 0x480020B4,
+ GpmcNCS4 = 0x480020B6,
+ GpmcNCS5 = 0x480020B8,
+ GpmcNCS6 = 0x480020BA,
+ GpmcNCS7 = 0x480020BC,
+ GpmcCLK = 0x480020BE,
+ GpmcNADV_ALE = 0x480020C0,
+ GpmcNOE = 0x480020C2,
+ GpmcNWE = 0x480020C4,
+ GpmcNBE0_CLE = 0x480020C6,
+ GpmcNBE1 = 0x480020C8,
+ GpmcNWP = 0x480020CA,
+ GpmcWAIT0 = 0x480020CC,
+ GpmcWAIT1 = 0x480020CE,
+ GpmcWAIT2 = 0x480020D0,
+ GpmcWAIT3 = 0x480020D2,
+};
+
+/* set SCM pad config mux mode */
+void
+setmuxmode(ulong addr, int shorts, int mode)
+{
+ int omode;
+ ushort *ptr;
+
+ mode &= Muxmode;
+ for (ptr = (ushort *)addr; shorts-- > 0; ptr++) {
+ omode = *ptr & Muxmode;
+ if (omode != mode)
+ *ptr = *ptr & ~Muxmode | mode;
+ }
+ coherence();
+}
+
+static void
+setpadmodes(void)
+{
+ int off;
+
+ /* set scm pad modes for usb; hasn't made any difference yet */
+ setmuxmode(0x48002166, 7, 5); /* hsusb3_tll* in mode 5; is mode 4 */
+ setmuxmode(0x48002180, 1, 5); /* hsusb3_tll_clk; is mode 4 */
+ setmuxmode(0x48002184, 4, 5); /* hsusb3_tll_data?; is mode 1 */
+ setmuxmode(0x480021a2, 12, 0); /* hsusb0 (console) in mode 0 */
+ setmuxmode(0x480021d4, 6, 2); /* hsusb2_tll* (ehci port 2) in mode 2 */
+ /* mode 3 is hsusb2_data* */
+ setmuxmode(0x480025d8, 18, 6); /* hsusb[12]_tll*; mode 3 is */
+ /* hsusb1_data*, hsusb2* */
+
+ setmuxmode(0x480020e4, 2, 5); /* uart3_rx_* in mode 5 */
+ setmuxmode(0x4800219a, 4, 0); /* uart3_* in mode 0 */
+ /* uart3_* in mode 2; TODO: conflicts with hsusb0 */
+ setmuxmode(0x480021aa, 4, 2);
+ setmuxmode(0x48002240, 2, 3); /* uart3_* in mode 3 */
+
+ /*
+ * igep/gumstix only: mode 4 of 21d2 is gpio_176 (smsc9221 ether irq).
+ * see ether9221.c for more.
+ */
+ *(ushort *)0x480021d2 = Inena | Ptup | Ptena | 4;
+
+ /* magic from u-boot for flash */
+ *(ushort *)GpmcA1 = Indis | Ptup | Ptena | 0;
+ *(ushort *)GpmcA2 = Indis | Ptup | Ptena | 0;
+ *(ushort *)GpmcA3 = Indis | Ptup | Ptena | 0;
+ *(ushort *)GpmcA4 = Indis | Ptup | Ptena | 0;
+ *(ushort *)GpmcA5 = Indis | Ptup | Ptena | 0;
+ *(ushort *)GpmcA6 = Indis | Ptup | Ptena | 0;
+ *(ushort *)GpmcA7 = Indis | Ptup | Ptena | 0;
+ *(ushort *)GpmcA8 = Indis | Ptup | Ptena | 0;
+ *(ushort *)GpmcA9 = Indis | Ptup | Ptena | 0;
+ *(ushort *)GpmcA10 = Indis | Ptup | Ptena | 0;
+
+ *(ushort *)GpmcD0 = Inena | Ptup | Ptena | 0;
+ *(ushort *)GpmcD1 = Inena | Ptup | Ptena | 0;
+ *(ushort *)GpmcD2 = Inena | Ptup | Ptena | 0;
+ *(ushort *)GpmcD3 = Inena | Ptup | Ptena | 0;
+ *(ushort *)GpmcD4 = Inena | Ptup | Ptena | 0;
+ *(ushort *)GpmcD5 = Inena | Ptup | Ptena | 0;
+ *(ushort *)GpmcD6 = Inena | Ptup | Ptena | 0;
+ *(ushort *)GpmcD7 = Inena | Ptup | Ptena | 0;
+ *(ushort *)GpmcD8 = Inena | Ptup | Ptena | 0;
+ *(ushort *)GpmcD9 = Inena | Ptup | Ptena | 0;
+ *(ushort *)GpmcD10 = Inena | Ptup | Ptena | 0;
+ *(ushort *)GpmcD11 = Inena | Ptup | Ptena | 0;
+ *(ushort *)GpmcD12 = Inena | Ptup | Ptena | 0;
+ *(ushort *)GpmcD13 = Inena | Ptup | Ptena | 0;
+ *(ushort *)GpmcD14 = Inena | Ptup | Ptena | 0;
+ *(ushort *)GpmcD15 = Inena | Ptup | Ptena | 0;
+
+ *(ushort *)GpmcNCS0 = Indis | Ptup | Ptena | 0;
+ *(ushort *)GpmcNCS1 = Indis | Ptup | Ptena | 0;
+ *(ushort *)GpmcNCS2 = Indis | Ptup | Ptena | 0;
+ *(ushort *)GpmcNCS3 = Indis | Ptup | Ptena | 0;
+ *(ushort *)GpmcNCS4 = Indis | Ptup | Ptena | 0;
+ *(ushort *)GpmcNCS5 = Indis | Ptup | Ptena | 0;
+ *(ushort *)GpmcNCS6 = Indis | Ptup | Ptena | 0;
+
+ *(ushort *)GpmcNOE = Indis | Ptdown | Ptdis | 0;
+ *(ushort *)GpmcNWE = Indis | Ptdown | Ptdis | 0;
+
+ *(ushort *)GpmcWAIT2 = Inena | Ptup | Ptena | 4; /* GPIO_64 -ETH_NRESET */
+ *(ushort *)GpmcNCS7 = Inena | Ptup | Ptena | 1; /* SYS_nDMA_REQ3 */
+
+ *(ushort *)GpmcCLK = Indis | Ptdown | Ptdis | 0;
+
+ *(ushort *)GpmcNBE1 = Inena | Ptdown | Ptdis | 0;
+
+ *(ushort *)GpmcNADV_ALE = Indis | Ptdown | Ptdis | 0;
+ *(ushort *)GpmcNBE0_CLE = Indis | Ptdown | Ptdis | 0;
+
+ *(ushort *)GpmcNWP = Inena | Ptdown | Ptdis | 0;
+
+ *(ushort *)GpmcWAIT0 = Inena | Ptup | Ptena | 0;
+ *(ushort *)GpmcWAIT1 = Inena | Ptup | Ptena | 0;
+ *(ushort *)GpmcWAIT3 = Inena | Ptup | Ptena | 0;
+
+ /*
+ * magic from u-boot: set 0xe00 bits in gpmc_(nwe|noe|nadv_ale)
+ * to enable `off' mode for each.
+ */
+ for (off = 0xc0; off <= 0xc4; off += sizeof(short))
+ *((ushort *)(PHYSSCM + off)) |= 0xe00;
+ coherence();
+}
+
+static char *
+implement(uchar impl)
+{
+ if (impl == 'A')
+ return "arm";
+ else
+ return "unknown";
+}
+
+static void
+fpon(void)
+{
+ int gotfp, impl;
+ ulong acc, scr;
+
+ gotfp = 1 << CpFP | 1 << CpDFP;
+ cpwrsc(0, CpCONTROL, 0, CpCPaccess, MASK(28));
+ acc = cprdsc(0, CpCONTROL, 0, CpCPaccess);
+ if ((acc & (MASK(2) << (2*CpFP))) == 0) {
+ gotfp &= ~(1 << CpFP);
+ print("fpon: no single FP coprocessor\n");
+ }
+ if ((acc & (MASK(2) << (2*CpDFP))) == 0) {
+ gotfp &= ~(1 << CpDFP);
+ print("fpon: no double FP coprocessor\n");
+ }
+ if (!gotfp) {
+ print("fpon: no FP coprocessors\n");
+ return;
+ }
+
+ /* enable fp. must be first operation on the FPUs. */
+ fpwr(Fpexc, fprd(Fpexc) | 1 << 30);
+
+ scr = fprd(Fpsid);
+ impl = scr >> 24;
+ print("fp: %s arch %s", implement(impl),
+ subarch(impl, (scr >> 16) & MASK(7)));
+
+ scr = fprd(Fpscr);
+ // TODO configure Fpscr further
+ scr |= 1 << 9; /* div-by-0 exception */
+ scr &= ~(MASK(2) << 20 | MASK(3) << 16); /* all ops are scalar */
+ fpwr(Fpscr, scr);
+ print("\n");
+ /* we should now be able to execute VFP-style FP instr'ns natively */
+}
+
+static void
+resetusb(void)
+{
+ int bound;
+ Uhh *uhh;
+ Usbotg *otg;
+ Usbtll *tll;
+
+ iprint("resetting usb: otg...");
+ otg = (Usbotg *)PHYSUSBOTG;
+ otg->otgsyscfg = Softreset; /* see omap35x errata 3.1.1.144 */
+ coherence();
+ resetwait(&otg->otgsyssts);
+ otg->otgsyscfg |= Sidle | Midle;
+ coherence();
+
+ iprint("uhh...");
+ uhh = (Uhh *)PHYSUHH;
+ uhh->sysconfig |= Softreset;
+ coherence();
+ resetwait(&uhh->sysstatus);
+ for (bound = 400*Mhz; !(uhh->sysstatus & Resetdone) && bound > 0;
+ bound--)
+ ;
+ uhh->sysconfig |= Sidle | Midle;
+
+ /*
+ * using the TLL seems to be an optimisation when talking
+ * to another identical SoC, thus not very useful, so
+ * force PHY (ULPI) mode.
+ */
+ /* this bit is normally off when we get here */
+ uhh->hostconfig &= ~P1ulpi_bypass;
+ coherence();
+ if (uhh->hostconfig & P1ulpi_bypass)
+ iprint("utmi (tll) mode..."); /* via tll */
+ else
+ /* external transceiver (phy), no tll */
+ iprint("ulpi (phy) mode...");
+
+ tll = (Usbtll *)PHYSUSBTLL;
+ if (probeaddr(PHYSUSBTLL) >= 0) {
+ iprint("tll...");
+ tll->sysconfig |= Softreset;
+ coherence();
+ resetwait(&tll->sysstatus);
+ tll->sysconfig |= Sidle;
+ coherence();
+ } else
+ iprint("no tll...");
+ iprint("\n");
+}
+
+/*
+ * there are secure sdrc registers at 0x48002460
+ * sdrc regs at PHYSSDRC; see spruf98c §1.2.8.2.
+ * set or dump l4 prot regs at PHYSL4?
+ */
+void
+archreset(void)
+{
+ static int beenhere;
+
+ if (beenhere)
+ return;
+ beenhere = 1;
+
+ /* conservative temporary values until archconfinit runs */
+ m->cpuhz = 500 * Mhz; /* beagle speed */
+ m->delayloop = m->cpuhz/2000; /* initial estimate */
+
+// dumpl3pr();
+ prcachecfg();
+ /* fight omap35x errata 2.0.1.104 */
+ memset((void *)PHYSSWBOOTCFG, 0, 240);
+ coherence();
+
+ setpadmodes();
+ configclks(); /* may change cpu speed */
+ configgpio();
+
+ archconfinit();
+
+ resetusb();
+ fpon();
+}
+
+void
+archreboot(void)
+{
+ Prm *prm = (Prm *)PHYSPRMGLBL;
+
+ iprint("archreboot: reset!\n");
+ delay(20);
+
+ prm->rstctrl |= Rstgs;
+ coherence();
+ delay(500);
+
+ /* shouldn't get here */
+ splhi();
+ iprint("awaiting reset");
+ for(;;) {
+ delay(1000);
+ print(".");
+ }
+}
+
+void
+kbdinit(void)
+{
+}
+
+void
+lastresortprint(char *buf, long bp)
+{
+ iprint("%.*s", (int)bp, buf); /* nothing else seems to work */
+}
+
+static void
+scmdump(ulong addr, int shorts)
+{
+ ushort reg;
+ ushort *ptr;
+
+ ptr = (ushort *)addr;
+ print("scm regs:\n");
+ while (shorts-- > 0) {
+ reg = *ptr++;
+ print("%#p: %#ux\tinputenable %d pulltypeselect %d "
+ "pulludenable %d muxmode %d\n",
+ ptr, reg, (reg>>8) & 1, (reg>>4) & 1, (reg>>3) & 1,
+ reg & 7);
+ }
+}
+
+char *cputype2name(char *buf, int size);
+
+void
+cpuidprint(void)
+{
+ char name[64];
+
+ cputype2name(name, sizeof name);
+ delay(250); /* let uart catch up */
+ iprint("cpu%d: %lldMHz ARM %s\n", m->machno, m->cpuhz / Mhz, name);
+}
+
+static void
+missing(ulong addr, char *name)
+{
+ static int firstmiss = 1;
+
+ if (probeaddr(addr) >= 0)
+ return;
+ if (firstmiss) {
+ iprint("missing:");
+ firstmiss = 0;
+ } else
+ iprint(",\n\t");
+ iprint(" %s at %#lux", name, addr);
+}
+
+/* verify that all the necessary device registers are accessible */
+void
+chkmissing(void)
+{
+ delay(20);
+ missing(PHYSSCM, "scm");
+ missing(KZERO, "dram");
+ missing(PHYSL3, "l3 config");
+ missing(PHYSINTC, "intr ctlr");
+ missing(PHYSTIMER1, "timer1");
+ missing(PHYSCONS, "console uart2");
+ missing(PHYSUART0, "uart0");
+ missing(PHYSUART1, "uart1");
+ missing(PHYSETHER, "smc9221"); /* not on beagle */
+ missing(PHYSUSBOTG, "usb otg");
+ missing(PHYSUHH, "usb uhh");
+ missing(PHYSOHCI, "usb ohci");
+ missing(PHYSEHCI, "usb ehci");
+ missing(PHYSSDMA, "dma");
+ missing(PHYSWDOG, "watchdog timer");
+ missing(PHYSUSBTLL, "usb tll");
+ iprint("\n");
+ delay(20);
+}
+
+void
+archflashwp(Flash*, int)
+{
+}
+
+/*
+ * 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;
+ /*
+ * this is set up for the igepv2 board.
+ * if the beagleboard ever works, we'll have to sort this out.
+ */
+ f->type = "onenand";
+ f->addr = (void*)PHYSNAND; /* mapped here by archreset */
+ f->size = 0; /* done by probe */
+ f->width = 1;
+ f->interleave = 0;
+ return 0;
+}
diff --git a/sys/src/9/omap/arm.h b/sys/src/9/omap/arm.h
new file mode 100755
index 000000000..12443d849
--- /dev/null
+++ b/sys/src/9/omap/arm.h
@@ -0,0 +1,246 @@
+/*
+ * arm-specific definitions for cortex-a8
+ * these are used in C and assembler
+ *
+ * `cortex' refers specifically to the cortex-a8.
+ */
+
+/*
+ * Program Status Registers
+ */
+#define PsrMusr 0x00000010 /* mode */
+#define PsrMfiq 0x00000011
+#define PsrMirq 0x00000012
+#define PsrMsvc 0x00000013 /* `protected mode for OS' */
+#define PsrMmon 0x00000016 /* `secure monitor' (trustzone hyper) */
+#define PsrMabt 0x00000017
+#define PsrMund 0x0000001B
+#define PsrMsys 0x0000001F /* `privileged user mode for OS' (trustzone) */
+#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 */
+
+/*
+ * Primary (CRn) CpSC registers.
+ */
+#define CpID 0 /* ID and cache type */
+#define CpCONTROL 1 /* miscellaneous control */
+#define CpTTB 2 /* Translation Table Base(s) */
+#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 /* L2 Cache Lockdown, op1==1 */
+#define CpTLD 10 /* TLB Lockdown, with op2 */
+#define CpVECS 12 /* vector bases, op1==0, Crm==0, op2s (cortex) */
+#define CpPID 13 /* Process ID */
+#define CpDTLB 15 /* TLB, L1 cache stuff (cortex) */
+
+/*
+ * CpTTB op1==0, Crm==0 opcode2 values.
+ */
+#define CpTTB0 0
+#define CpTTB1 1 /* cortex */
+#define CpTTBctl 2 /* cortex */
+
+/*
+ * CpID Secondary (CRm) registers.
+ */
+#define CpIDidct 0
+
+/*
+ * CpID op1==0 opcode2 fields.
+ * the cortex has more op1 codes for cache size, etc.
+ */
+#define CpIDid 0 /* main ID */
+#define CpIDct 1 /* cache type */
+#define CpIDtlb 3 /* tlb type (cortex) */
+#define CpIDmpid 5 /* multiprocessor id (cortex) */
+
+/* CpIDid op1 values */
+#define CpIDcsize 1 /* cache size (cortex) */
+#define CpIDcssel 2 /* cache size select (cortex) */
+
+/*
+ * CpCONTROL op2 codes, op1==0, Crm==0.
+ */
+#define CpMainctl 0
+#define CpAuxctl 1
+#define CpCPaccess 2
+
+/*
+ * CpCONTROL: op1==0, CRm==0, op2==CpMainctl.
+ * main control register.
+ * cortex/armv7 has more ops and CRm values.
+ */
+#define CpCmmu 0x00000001 /* M: MMU enable */
+#define CpCalign 0x00000002 /* A: alignment fault enable */
+#define CpCdcache 0x00000004 /* C: data cache on */
+#define CpCsbo (3<<22|1<<18|1<<16|017<<3) /* must be 1 (armv7) */
+#define CpCsbz (CpCtre|1<<26|CpCve|1<<15|7<<7) /* must be 0 (armv7) */
+#define CpCsw (1<<10) /* SW: SWP(B) enable (deprecated in v7) */
+#define CpCpredict 0x00000800 /* Z: branch prediction (armv7) */
+#define CpCicache 0x00001000 /* I: instruction cache on */
+#define CpChv 0x00002000 /* V: high vectors */
+#define CpCrr (1<<14) /* RR: round robin vs random cache replacement */
+#define CpCha (1<<17) /* HA: hw access flag enable */
+#define CpCdz (1<<19) /* DZ: divide by zero fault enable */
+#define CpCfi (1<<21) /* FI: fast intrs */
+#define CpCve (1<<24) /* VE: intr vectors enable */
+#define CpCee (1<<25) /* EE: exception endianness */
+#define CpCnmfi (1<<27) /* NMFI: non-maskable fast intrs. */
+#define CpCtre (1<<28) /* TRE: TEX remap enable */
+#define CpCafe (1<<29) /* AFE: access flag (ttb) enable */
+
+/*
+ * CpCONTROL: op1==0, CRm==0, op2==CpAuxctl.
+ * Auxiliary control register on cortex at least.
+ */
+#define CpACcachenopipe (1<<20) /* don't pipeline cache maint. */
+#define CpACcp15serial (1<<18) /* serialise CP1[45] ops. */
+#define CpACcp15waitidle (1<<17) /* CP1[45] wait-on-idle */
+#define CpACcp15pipeflush (1<<16) /* CP1[45] flush pipeline */
+#define CpACneonissue1 (1<<12) /* neon single issue */
+#define CpACldstissue1 (1<<11) /* force single issue ld, st */
+#define CpACissue1 (1<<10) /* force single issue */
+#define CpACnobsm (1<<7) /* no branch size mispredicts */
+#define CpACibe (1<<6) /* cp15 invalidate & btb enable */
+#define CpACl1neon (1<<5) /* cache neon (FP) data in L1 cache */
+#define CpACasa (1<<4) /* enable speculative accesses */
+#define CpACl1pe (1<<3) /* l1 cache parity enable */
+#define CpACl2en (1<<1) /* l2 cache enable; default 1 */
+/*
+ * CpCONTROL Secondary (CRm) registers and opcode2 fields.
+ */
+#define CpCONTROLscr 1
+
+#define CpSCRscr 0
+
+/*
+ * CpCACHE Secondary (CRm) registers and opcode2 fields. op1==0.
+ * In ARM-speak, 'flush' means invalidate and 'clean' means writeback.
+ */
+#define CpCACHEintr 0 /* interrupt (op2==4) */
+#define CpCACHEisi 1 /* inner-sharable I cache (v7) */
+#define CpCACHEpaddr 4 /* 0: phys. addr (cortex) */
+#define CpCACHEinvi 5 /* instruction, branch table */
+#define CpCACHEinvd 6 /* data or unified */
+// #define CpCACHEinvu 7 /* unified (not on cortex) */
+#define CpCACHEva2pa 8 /* va -> pa translation (cortex) */
+#define CpCACHEwb 10 /* writeback */
+#define CpCACHEinvdse 11 /* data or unified by mva */
+#define CpCACHEwbi 14 /* writeback+invalidate */
+
+#define CpCACHEall 0 /* entire (not for invd nor wb(i) on cortex) */
+#define CpCACHEse 1 /* single entry */
+#define CpCACHEsi 2 /* set/index (set/way) */
+#define CpCACHEtest 3 /* test loop */
+#define CpCACHEwait 4 /* wait (prefetch flush on cortex) */
+#define CpCACHEdmbarr 5 /* wb only (cortex) */
+#define CpCACHEflushbtc 6 /* flush branch-target cache (cortex) */
+#define CpCACHEflushbtse 7 /* ⋯ or just one entry in it (cortex) */
+
+/*
+ * 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 */
+#define CpTBLasid 2 /* by ASID (cortex) */
+
+/*
+ * CpCLD Secondary (CRm) registers and opcode2 fields for op1==0. (cortex)
+ */
+#define CpCLDena 12 /* enables */
+#define CpCLDcyc 13 /* cycle counter */
+#define CpCLDuser 14 /* user enable */
+
+#define CpCLDenapmnc 0
+#define CpCLDenacyc 1
+
+/*
+ * CpCLD Secondary (CRm) registers and opcode2 fields for op1==1.
+ */
+#define CpCLDl2 0 /* l2 cache */
+
+#define CpCLDl2aux 2 /* auxiliary control */
+
+/*
+ * l2 cache aux. control
+ */
+#define CpCl2ecc (1<<28) /* use ecc, not parity */
+#define CpCl2noldforw (1<<27) /* no ld forwarding */
+#define CpCl2nowrcomb (1<<25) /* no write combining */
+#define CpCl2nowralldel (1<<24) /* no write allocate delay */
+#define CpCl2nowrallcomb (1<<23) /* no write allocate combine */
+#define CpCl2nowralloc (1<<22) /* no write allocate */
+#define CpCl2eccparity (1<<21) /* enable ecc or parity */
+#define CpCl2inner (1<<16) /* inner cacheability */
+/* other bits are tag ram & data ram latencies */
+
+/*
+ * CpTLD Secondary (CRm) registers and opcode2 fields.
+ */
+#define CpTLDlock 0 /* TLB lockdown registers */
+#define CpTLDpreload 1 /* TLB preload */
+
+#define CpTLDi 0 /* TLB instr. lockdown reg. */
+#define CpTLDd 1 /* " data " " */
+
+/*
+ * CpVECS Secondary (CRm) registers and opcode2 fields.
+ */
+#define CpVECSbase 0
+
+#define CpVECSnorm 0 /* (non-)secure base addr */
+#define CpVECSmon 1 /* secure monitor base addr */
+
+/*
+ * MMU page table entries.
+ * Mbz (0x10) bit is implementation-defined and must be 0 on the cortex.
+ */
+#define Mbz (0<<4)
+#define Fault 0x00000000 /* L[12] pte: unmapped */
+
+#define Coarse (Mbz|1) /* L1 */
+#define Section (Mbz|2) /* L1 1MB */
+#define Fine (Mbz|3) /* L1 */
+
+#define Large 0x00000001 /* L2 64KB */
+#define Small 0x00000002 /* L2 4KB */
+#define Tiny 0x00000003 /* L2 1KB: not in v7 */
+#define Buffered 0x00000004 /* L[12]: write-back not -thru */
+#define Cached 0x00000008 /* L[12] */
+#define Dom0 0
+
+#define Noaccess 0 /* AP, DAC */
+#define Krw 1 /* AP */
+/* armv7 deprecates AP[2] == 1 & AP[1:0] == 2 (Uro), prefers 3 (new in v7) */
+#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)))
+#define L2AP(ap) (AP(0, (ap))) /* armv7 */
+#define DAC(n, v) F((v), (n)*2, 2)
+
+#define HVECTORS 0xffff0000
diff --git a/sys/src/9/omap/arm.s b/sys/src/9/omap/arm.s
new file mode 100755
index 000000000..829e36234
--- /dev/null
+++ b/sys/src/9/omap/arm.s
@@ -0,0 +1,95 @@
+/*
+ * omap3530 machine assist, definitions
+ * cortex-a8 processor
+ *
+ * loader uses R11 as scratch.
+ */
+
+#include "mem.h"
+#include "arm.h"
+
+#undef B /* B is for 'botch' */
+
+#define KADDR(pa) (KZERO | ((pa) & ~KSEGM))
+#define PADDR(va) (PHYSDRAM | ((va) & ~KSEGM))
+
+#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)
+
+#define DOUBLEMAPMBS 256 /* megabytes of low dram to double-map */
+
+/* steps on R0 */
+#define DELAY(label, mloops) \
+ MOVW $((mloops)*1000000), R0; \
+label: \
+ SUB.S $1, R0; \
+ BNE label
+
+/* wave at the user; clobbers R0, R1 & R6; needs R12 (SB) set */
+#define WAVE(c) \
+ BARRIERS; \
+ MOVW $(c), R1; \
+ MOVW $PHYSCONS, R6; \
+ MOVW R1, (R6); \
+ BARRIERS
+
+/*
+ * new instructions
+ */
+
+#define SMC WORD $0xe1600070 /* low 4-bits are call # (trustzone) */
+/* flush branch-target cache; zeroes R0 (cortex) */
+#define FLBTC \
+ MOVW $0, R0; \
+ MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEflushbtc
+/* flush one entry of the branch-target cache, va in R0 (cortex) */
+#define FLBTSE \
+ MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEflushbtse
+
+/* arm v7 arch defines these */
+#define WFI WORD $0xe320f003 /* wait for interrupt */
+#define DMB WORD $0xf57ff05f /* data mem. barrier; last f = SY */
+#define DSB WORD $0xf57ff04f /* data synch. barrier; last f = SY */
+#define ISB WORD $0xf57ff06f /* instr. sync. barrier; last f = SY */
+#define NOOP WORD $0xe320f000
+#define CLZ(s, d) WORD $(0xe16f0f10 | (d) << 12 | (s)) /* count leading 0s */
+#define CPSIE WORD $0xf1080080 /* intr enable: zeroes I bit */
+#define CPSID WORD $0xf10c0080 /* intr disable: sets I bit */
+
+/* floating point */
+#define VMRS(fp, cpu) WORD $(0xeef00a10 | (fp)<<16 | (cpu)<<12) /* FP → arm */
+#define VMSR(cpu, fp) WORD $(0xeee00a10 | (fp)<<16 | (cpu)<<12) /* arm → FP */
+
+/*
+ * a popular code sequence used to write a pte for va is:
+ *
+ * MOVW R(n), TTB[LnX(va)]
+ * // clean the cache line
+ * DSB
+ * // invalidate tlb entry for va
+ * FLBTC
+ * DSB
+ * PFF (now ISB)
+ */
+/* zeroes R0 */
+#define BARRIERS FLBTC; DSB; ISB
+
+/*
+ * 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/omap/beagle b/sys/src/9/omap/beagle
new file mode 100755
index 000000000..887395443
--- /dev/null
+++ b/sys/src/9/omap/beagle
@@ -0,0 +1,82 @@
+# beagle, igepv2, gumstix overo omap35 boards
+dev
+ root
+ cons
+ env
+ pipe
+ proc
+ mnt
+ srv
+ dup
+ arch
+ ssl
+ tls
+ bridge log
+ sdp thwack unthwack
+ cap
+ kprof
+# aoe
+# sd
+ fs
+# flash
+
+ ether netif
+ ip arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum inferno
+
+ draw screen
+ dss
+ kbmap
+ kbin
+ mouse
+
+ uart
+# usb
+
+link
+ archomap
+ ethermedium
+# flashbeagle ecc
+# flashigep
+ loopbackmedium
+ netdevmedium
+
+ ether9221
+## avoid tickling errata 3.1.1.183
+## usbohci
+# usbehci usbehciomap
+
+ip
+ tcp
+ udp
+ ipifc
+ icmp
+ icmp6
+ ipmux
+ gre
+ esp
+
+misc
+ rdb
+ coproc
+ dma
+ mouse
+# sdaoe sdscsi
+ softfpu
+ syscall
+ uarti8250
+ ucalloc
+ ucallocb
+
+port
+ int cpuserver = 1;
+ int i8250freq = 3686000;
+
+boot cpu
+ tcp
+
+bootdir
+ boot$CONF.out boot
+ /arm/bin/ip/ipconfig ipconfig
+ /arm/bin/auth/factotum factotum
+ /arm/bin/usb/usbd
+ nvram
diff --git a/sys/src/9/omap/cache.v7.s b/sys/src/9/omap/cache.v7.s
new file mode 100755
index 000000000..d6a6eef6e
--- /dev/null
+++ b/sys/src/9/omap/cache.v7.s
@@ -0,0 +1,208 @@
+/*
+ * cortex arm arch v7 cache flushing and invalidation
+ * shared by l.s and rebootcode.s
+ */
+
+TEXT cacheiinv(SB), $-4 /* I invalidate */
+ MOVW $0, R0
+ MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEall /* ok on cortex */
+ ISB
+ RET
+
+/*
+ * set/way operators, passed a suitable set/way value in R0.
+ */
+TEXT cachedwb_sw(SB), $-4
+ MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEwb), CpCACHEsi
+ RET
+
+TEXT cachedwbinv_sw(SB), $-4
+ MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEwbi), CpCACHEsi
+ RET
+
+TEXT cachedinv_sw(SB), $-4
+ MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvd), CpCACHEsi
+ RET
+
+ /* set cache size select */
+TEXT setcachelvl(SB), $-4
+ MCR CpSC, CpIDcssel, R0, C(CpID), C(CpIDid), 0
+ ISB
+ RET
+
+ /* return cache sizes */
+TEXT getwayssets(SB), $-4
+ MRC CpSC, CpIDcsize, R0, C(CpID), C(CpIDid), 0
+ RET
+
+/*
+ * l1 cache operations.
+ * l1 and l2 ops are intended to be called from C, thus need save no
+ * caller's regs, only those we need to preserve across calls.
+ */
+
+TEXT cachedwb(SB), $-4
+ MOVW.W R14, -8(R13)
+ MOVW $cachedwb_sw(SB), R0
+ MOVW $1, R8
+ BL wholecache(SB)
+ MOVW.P 8(R13), R15
+
+TEXT cachedwbinv(SB), $-4
+ MOVW.W R14, -8(R13)
+ MOVW $cachedwbinv_sw(SB), R0
+ MOVW $1, R8
+ BL wholecache(SB)
+ MOVW.P 8(R13), R15
+
+TEXT cachedinv(SB), $-4
+ MOVW.W R14, -8(R13)
+ MOVW $cachedinv_sw(SB), R0
+ MOVW $1, R8
+ BL wholecache(SB)
+ MOVW.P 8(R13), R15
+
+TEXT cacheuwbinv(SB), $-4
+ MOVM.DB.W [R14], (R13) /* save lr on stack */
+ MOVW CPSR, R1
+ CPSID /* splhi */
+
+ MOVM.DB.W [R1], (R13) /* save R1 on stack */
+
+ BL cachedwbinv(SB)
+ BL cacheiinv(SB)
+
+ MOVM.IA.W (R13), [R1] /* restore R1 (saved CPSR) */
+ MOVW R1, CPSR
+ MOVM.IA.W (R13), [R14] /* restore lr */
+ RET
+
+/*
+ * l2 cache operations
+ */
+
+TEXT l2cacheuwb(SB), $-4
+ MOVW.W R14, -8(R13)
+ MOVW $cachedwb_sw(SB), R0
+ MOVW $2, R8
+ BL wholecache(SB)
+ MOVW.P 8(R13), R15
+
+TEXT l2cacheuwbinv(SB), $-4
+ MOVW.W R14, -8(R13)
+ MOVW CPSR, R1
+ CPSID /* splhi */
+
+ MOVM.DB.W [R1], (R13) /* save R1 on stack */
+
+ MOVW $cachedwbinv_sw(SB), R0
+ MOVW $2, R8
+ BL wholecache(SB)
+ BL l2cacheuinv(SB)
+
+ MOVM.IA.W (R13), [R1] /* restore R1 (saved CPSR) */
+ MOVW R1, CPSR
+ MOVW.P 8(R13), R15
+
+TEXT l2cacheuinv(SB), $-4
+ MOVW.W R14, -8(R13)
+ MOVW $cachedinv_sw(SB), R0
+ MOVW $2, R8
+ BL wholecache(SB)
+ MOVW.P 8(R13), R15
+
+/*
+ * these shift values are for the Cortex-A8 L1 cache (A=2, L=6) and
+ * the Cortex-A8 L2 cache (A=3, L=6).
+ * A = log2(# of ways), L = log2(bytes per cache line).
+ * see armv7 arch ref p. 1403.
+ */
+#define L1WAYSH 30
+#define L1SETSH 6
+#define L2WAYSH 29
+#define L2SETSH 6
+
+/*
+ * callers are assumed to be the above l1 and l2 ops.
+ * R0 is the function to call in the innermost loop.
+ * R8 is the cache level (one-origin: 1 or 2).
+ *
+ * initial translation by 5c, then massaged by hand.
+ */
+TEXT wholecache+0(SB), $-4
+ MOVW R0, R1 /* save argument for inner loop in R1 */
+ SUB $1, R8 /* convert cache level to zero origin */
+
+ /* we may not have the MMU on yet, so map R1 to PC's space */
+ BIC $KSEGM, R1 /* strip segment from address */
+ MOVW PC, R2 /* get PC's segment ... */
+ AND $KSEGM, R2
+ CMP $0, R2 /* PC segment should be non-zero on omap */
+ BEQ buggery
+ ORR R2, R1 /* combine them */
+
+ /* drain write buffers */
+ BARRIERS
+ MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEwb), CpCACHEwait
+ ISB
+
+ MOVW CPSR, R2
+ MOVM.DB.W [R2,R14], (SP) /* save regs on stack */
+ CPSID /* splhi to make entire op atomic */
+
+ /* get cache sizes */
+ SLL $1, R8, R0 /* R0 = (cache - 1) << 1 */
+ MCR CpSC, CpIDcssel, R0, C(CpID), C(CpIDid), 0 /* set cache size select */
+ ISB
+ MRC CpSC, CpIDcsize, R0, C(CpID), C(CpIDid), 0 /* get cache sizes */
+
+ /* compute # of ways and sets for this cache level */
+ SRA $3, R0, R5 /* R5 (ways) = R0 >> 3 */
+ AND $1023, R5 /* R5 = (R0 >> 3) & MASK(10) */
+ ADD $1, R5 /* R5 (ways) = ((R0 >> 3) & MASK(10)) + 1 */
+
+ SRA $13, R0, R2 /* R2 = R0 >> 13 */
+ AND $32767, R2 /* R2 = (R0 >> 13) & MASK(15) */
+ ADD $1, R2 /* R2 (sets) = ((R0 >> 13) & MASK(15)) + 1 */
+
+ /* precompute set/way shifts for inner loop */
+ CMP $0, R8 /* cache == 1? */
+ MOVW.EQ $L1WAYSH, R3 /* yes */
+ MOVW.EQ $L1SETSH, R4
+ MOVW.NE $L2WAYSH, R3 /* no */
+ MOVW.NE $L2SETSH, R4
+
+ /* iterate over ways */
+ MOVW $0, R7 /* R7: way */
+outer:
+ /* iterate over sets */
+ MOVW $0, R6 /* R6: set */
+inner:
+ /* compute set/way register contents */
+ SLL R3, R7, R0 /* R0 = way << R3 (L?WAYSH) */
+ ORR R8<<1, R0 /* R0 = way << L?WAYSH | (cache - 1) << 1 */
+ ORR R6<<R4, R0 /* R0 = way<<L?WAYSH | (cache-1)<<1 |set<<R4 */
+
+ BL (R1) /* call set/way operation with R0 */
+
+ ADD $1, R6 /* set++ */
+ CMP R2, R6 /* set >= sets? */
+ BLT inner /* no, do next set */
+
+ ADD $1, R7 /* way++ */
+ CMP R5, R7 /* way >= ways? */
+ BLT outer /* no, do next way */
+
+ MOVM.IA.W (SP), [R2,R14] /* restore regs */
+ MOVW R2, CPSR /* splx */
+
+ /* drain write buffers */
+ MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEwb), CpCACHEwait
+ ISB
+ RET
+
+buggery:
+WAVE('?')
+ MOVW PC, R0
+// B pczeroseg(SB)
+ RET
diff --git a/sys/src/9/omap/clock.c b/sys/src/9/omap/clock.c
new file mode 100755
index 000000000..eaab1e270
--- /dev/null
+++ b/sys/src/9/omap/clock.c
@@ -0,0 +1,489 @@
+/*
+ * omap3530 clocks
+ *
+ * timers count up to zero.
+ *
+ * the source clock signals for the timers are sometimes selectable. for
+ * WDTIMER[23] and GPTIMER12, it's always the 32kHz clock. for the
+ * others, it can be the 32kHz clock or the system clock. we use only
+ * WDTIMER2 and GPTIMER[12], and configure GPTIMER[12] in archomap.c to
+ * use the 32kHZ clock. WDTIMER1 is not accessible to us on GP
+ * (general-purpose) omaps.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "arm.h"
+
+enum {
+ Debug = 0,
+
+ Tn0 = PHYSTIMER1,
+ Tn1 = PHYSTIMER2,
+
+ /* irq 36 is watchdog timer module 3 overflow */
+ Tn0irq = 37, /* base IRQ for all timers */
+
+ Freebase = 1, /* base of free-running timer */
+
+ /*
+ * clock is 32K (32,768) Hz, so one tick is 30.517µs,
+ * so 327.68 ticks is 10ms, 32.768 ticks is 1ms.
+ */
+ Clockfreqbase = 32 * 1024, /* base rate in Hz */
+ Tcycles = Clockfreqbase / HZ, /* cycles per clock tick */
+
+ MinPeriod = (Tcycles / 100 < 2? 2: Tcycles / 100),
+ MaxPeriod = Tcycles,
+
+ Dogtimeout = 20 * Clockfreqbase, /* was 4 s.; must be ≤ 21 s. */
+};
+
+enum {
+ /* ticpcfg bits */
+ Noidle = 1<<3,
+ Softreset = 1<<1,
+
+ /* tistat bits */
+ Resetdone = 1<<0,
+
+ /* tisr/tier bits */
+ Ovf_it = 1<<1, /* gp: overflow intr */
+ Mat_it = 1<<0, /* gp: match intr */
+ Wdovf_it = 1<<0, /* wdog: overflow intr */
+
+ /* tclr bits */
+ Ar = 1<<1, /* gp only: autoreload mode overflow */
+ St = 1<<0, /* gp only: start the timer */
+};
+
+/* omap35x timer registers */
+typedef struct Timerregs Timerregs;
+struct Timerregs {
+ /* common to all timers, gp and watchdog */
+ uchar pad0[0x10];
+ ulong ticpcfg;
+ ulong tistat; /* ro: low bit: reset done */
+ ulong tisr;
+ ulong tier;
+ ulong twer;
+ ulong tclr;
+ ulong tcrr; /* counter: cycles to zero */
+ ulong tldr;
+ ulong ttgr; /* trigger */
+ ulong twps; /* ro: write posted pending */
+
+ /* gp timers only, unused by us */
+ ulong tmar; /* value to compare with counter */
+ ulong tcar1; /* ro */
+ ulong tsicr;
+ ulong tcar2; /* ro */
+ union {
+ ulong tpir; /* gp: 1 ms tick generation: +ve */
+ ulong wspr; /* wdog: start/stop control */
+ };
+ ulong tnir; /* 1 ms tick generation: -ve */
+ ulong tcvr; /* 1 ms tick generation: next counter value */
+ ulong tocr; /* intr mask for n ticks */
+ ulong towr;
+};
+
+static int ticks; /* for sanity checking; m->ticks doesn't always get called */
+static Lock clklck;
+
+static ulong rdcycles(void), rdbaseticks(void);
+
+/* write a watchdog timer's start/stop register */
+static void
+wdogwrss(Timerregs *tn, ulong val)
+{
+ while (tn->twps & (1 << 4)) /* pending write to start/stop reg? */
+ ;
+ tn->wspr = val;
+ coherence();
+ while (tn->twps & (1 << 4)) /* pending write to start/stop reg? */
+ ;
+}
+
+static void
+resetwait(Timerregs *tn)
+{
+ long bound;
+
+ for (bound = 400*Mhz; !(tn->tistat & Resetdone) && bound > 0; bound--)
+ ;
+ if (bound <= 0)
+ iprint("clock reset didn't complete\n");
+}
+
+
+static void
+wdogoff(Timerregs *tn)
+{
+ resetwait(tn);
+
+ wdogwrss(tn, 0xaaaa); /* magic off sequence */
+ wdogwrss(tn, 0x5555);
+
+ tn->tldr = 1;
+ coherence();
+ tn->tcrr = 1; /* paranoia */
+ coherence();
+}
+
+static void wdogassure(void);
+
+static void
+wdogon(Timerregs *tn)
+{
+ static int beenhere;
+
+ resetwait(tn);
+ tn->tldr = -Dogtimeout;
+ tn->tcrr = -Dogtimeout;
+ coherence();
+ wdogwrss(tn, 0xbbbb); /* magic on sequence */
+ wdogwrss(tn, 0x4444); /* magic on sequence */
+
+ if (!beenhere) {
+ beenhere = 1;
+ /* touching the dog is not quick, so do it infrequently */
+ addclock0link(wdogassure, HZ);
+ }
+}
+
+static void
+wdogassure(void) /* reset the watch dog's counter */
+{
+ Timerregs *tn;
+
+ tn = (Timerregs *)PHYSWDOG;
+ wdogoff(tn);
+
+ tn->tcrr = -Dogtimeout;
+ coherence();
+
+ wdogon(tn);
+}
+
+static void
+clockintr(Ureg* ureg, void *arg)
+{
+ Timerregs *tn;
+ static int nesting;
+
+ ticks++;
+ coherence();
+
+ if (nesting == 0) { /* if the clock interrupted itself, bail out */
+ ++nesting;
+ timerintr(ureg, 0);
+ --nesting;
+ }
+
+ tn = arg;
+ tn->tisr = Ovf_it; /* dismiss the interrupt */
+ coherence();
+}
+
+static void
+clockreset(Timerregs *tn)
+{
+ if (probeaddr((uintptr)&tn->ticpcfg) < 0)
+ panic("no clock at %#p", tn);
+ tn->ticpcfg = Softreset | Noidle;
+ coherence();
+ resetwait(tn);
+ tn->tier = tn->tclr = 0;
+ coherence();
+}
+
+/* stop clock interrupts and disable the watchdog timer */
+void
+clockshutdown(void)
+{
+ clockreset((Timerregs *)PHYSWDT2);
+ wdogoff((Timerregs *)PHYSWDT2);
+ clockreset((Timerregs *)PHYSWDT3);
+ wdogoff((Timerregs *)PHYSWDT3);
+
+ clockreset((Timerregs *)Tn0);
+ clockreset((Timerregs *)Tn1);
+}
+
+enum {
+ Instrs = 10*Mhz,
+};
+
+static long
+issue1loop(void)
+{
+ register int i;
+ long st;
+
+ st = rdbaseticks();
+ i = Instrs;
+ do {
+ --i; --i; --i; --i; --i;
+ --i; --i; --i; --i;
+ } while(--i >= 0);
+ return rdbaseticks() - st;
+}
+
+static long
+issue2loop(void)
+{
+ register int i, j;
+ long st;
+
+ st = rdbaseticks();
+ i = Instrs / 2;
+ j = 0;
+ do {
+ --i; --j; --i; --j;
+ --i; --j; --i; --j;
+ --j;
+ } while(--i >= 0);
+ return rdbaseticks() - st;
+}
+
+/* estimate instructions/s. using 32kHz clock */
+static void
+guessmips(long (*loop)(void), char *lab)
+{
+ int s;
+ long tcks;
+
+ do {
+ s = splhi();
+ tcks = loop();
+ splx(s);
+ if (tcks < 0)
+ iprint("again...");
+ } while (tcks < 0);
+ /*
+ * Instrs instructions took tcks ticks @ Clockfreqbase Hz.
+ */
+ s = ((vlong)Clockfreqbase * Instrs) / tcks / 1000000;
+ if (Debug)
+ iprint("%ud mips (%s-issue)", s, lab);
+ USED(s);
+}
+
+void
+clockinit(void)
+{
+ int i, s;
+ Timerregs *tn;
+
+ clockshutdown();
+
+ /* turn cycle counter on */
+ cpwrsc(0, CpCLD, CpCLDena, CpCLDenacyc, 1<<31);
+
+ /* turn all counters on and clear the cycle counter */
+ cpwrsc(0, CpCLD, CpCLDena, CpCLDenapmnc, 1<<2 | 1);
+
+ /* let users read the cycle counter directly */
+ cpwrsc(0, CpCLD, CpCLDena, CpCLDenapmnc, 1);
+
+ ilock(&clklck);
+ m->fastclock = 1;
+ m->ticks = ticks = 0;
+
+ /*
+ * T0 is a freerunning timer (cycle counter); it wraps,
+ * automatically reloads, and does not dispatch interrupts.
+ */
+ tn = (Timerregs *)Tn0;
+ tn->tcrr = Freebase; /* count up to 0 */
+ tn->tldr = Freebase;
+ coherence();
+ tn->tclr = Ar | St;
+ iunlock(&clklck);
+
+ /*
+ * T1 is the interrupting timer and does not participate
+ * in measuring time. It is initially set to HZ.
+ */
+ tn = (Timerregs *)Tn1;
+ irqenable(Tn0irq+1, clockintr, tn, "clock");
+ ilock(&clklck);
+ tn->tcrr = -Tcycles; /* approx.; count up to 0 */
+ tn->tldr = -Tcycles;
+ coherence();
+ tn->tclr = Ar | St;
+ coherence();
+ tn->tier = Ovf_it;
+ coherence();
+ iunlock(&clklck);
+
+ /*
+ * verify sanity of timer1
+ */
+ s = spllo(); /* risky */
+ for (i = 0; i < 5 && ticks == 0; i++) {
+ delay(10);
+ cachedwbinvse(&ticks, sizeof ticks);
+ }
+ splx(s);
+ if (ticks == 0) {
+ if (tn->tcrr == 0)
+ panic("clock not interrupting");
+ else if (tn->tcrr == tn->tldr)
+ panic("clock not ticking at all");
+#ifdef PARANOID
+ else
+ panic("clock running very slowly");
+#endif
+ }
+
+ guessmips(issue1loop, "single");
+ if (Debug)
+ iprint(", ");
+ guessmips(issue2loop, "dual");
+ if (Debug)
+ iprint("\n");
+
+ /*
+ * m->delayloop should be the number of delay loop iterations
+ * needed to consume 1 ms. 2 is min. instructions in the delay loop.
+ */
+ m->delayloop = m->cpuhz / (1000 * 2);
+// iprint("m->delayloop = %lud\n", m->delayloop);
+
+ /*
+ * desynchronize the processor clocks so that they all don't
+ * try to resched at the same time.
+ */
+ delay(m->machno*2);
+}
+
+void
+watchdoginit(void)
+{
+ wdogassure();
+}
+
+ulong
+µs(void)
+{
+ return fastticks2us(fastticks(nil));
+}
+
+void
+timerset(Tval next)
+{
+ long offset;
+ Timerregs *tn = (Timerregs *)Tn1;
+ static Lock setlck;
+
+ ilock(&setlck);
+ offset = next - fastticks(nil);
+ if(offset < MinPeriod)
+ offset = MinPeriod;
+ else if(offset > MaxPeriod)
+ offset = MaxPeriod;
+ tn->tcrr = -offset;
+ coherence();
+ iunlock(&setlck);
+}
+
+static ulong
+rdcycles(void)
+{
+ ulong v;
+
+ /* reads 32-bit cycle counter (counting up) */
+ v = cprdsc(0, CpCLD, CpCLDcyc, 0);
+ /* keep it positive; prevent m->fastclock ever going to 0 */
+ return v == 0? 1: v;
+}
+
+static ulong
+rdbaseticks(void)
+{
+ ulong v;
+
+ v = ((Timerregs *)Tn0)->tcrr; /* tcrr should be counting up */
+ /* keep it positive; prevent m->fastclock ever going to 0 */
+ return v == 0? 1: v;
+}
+
+ulong
+perfticks(void)
+{
+ return rdcycles();
+}
+
+long
+lcycles(void)
+{
+ return perfticks();
+}
+
+/*
+ * until 5[cal] inline vlong ops, avoid them where possible,
+ * they are currently slow function calls.
+ */
+typedef union Counter Counter;
+union Counter {
+ uvlong uvl;
+ struct { /* little-endian */
+ ulong low;
+ ulong high;
+ };
+};
+
+enum {
+ Fastvlongops = 0,
+};
+
+uvlong
+fastticks(uvlong *hz)
+{
+ Counter now, sclnow;
+
+ if(hz)
+ *hz = m->cpuhz;
+ ilock(&clklck);
+ if (m->ticks > HZ/10 && m->fastclock == 0)
+ panic("fastticks: zero m->fastclock; ticks %lud fastclock %#llux",
+ m->ticks, m->fastclock);
+
+ now.uvl = m->fastclock;
+ now.low = rdcycles();
+ if(now.uvl < m->fastclock) /* low bits must have wrapped */
+ now.high++;
+ m->fastclock = now.uvl;
+ coherence();
+
+ sclnow.uvl = now.uvl;
+ iunlock(&clklck);
+ return sclnow.uvl;
+}
+
+void
+microdelay(int l)
+{
+ int i;
+
+ l = l * (vlong)m->delayloop / 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/omap/coproc.c b/sys/src/9/omap/coproc.c
new file mode 100755
index 000000000..411f0fef7
--- /dev/null
+++ b/sys/src/9/omap/coproc.c
@@ -0,0 +1,133 @@
+/*
+ * 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, /* MOV R14, R15 */
+};
+
+void
+cpwr(int cp, int op1, int crn, int crm, int op2, ulong val)
+{
+ volatile ulong instr[2];
+ void *pcaddr;
+ void (*fp)(ulong);
+
+ 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;
+
+ pcaddr = (void *)MAP2PCSPACE(instr, getcallerpc(&cp));
+ cachedwbse(pcaddr, sizeof instr);
+ cacheiinv();
+
+ fp = (void (*)(ulong))pcaddr;
+ (*fp)(val);
+ coherence();
+}
+
+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)
+{
+ volatile ulong instr[2];
+ void *pcaddr;
+ ulong (*fp)(void);
+
+ 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;
+
+ pcaddr = (void *)MAP2PCSPACE(instr, getcallerpc(&cp));
+ cachedwbse(pcaddr, sizeof instr);
+ cacheiinv();
+
+ fp = (ulong (*)(void))pcaddr;
+ return (*fp)();
+}
+
+ulong
+cprdsc(int op1, int crn, int crm, int op2)
+{
+ return cprd(CpSC, op1, crn, crm, op2);
+}
+
+/* floating point */
+
+ulong
+fprd(int fpreg)
+{
+ volatile ulong instr[2];
+ void *pcaddr;
+ ulong (*fp)(void);
+
+ fpreg &= 017;
+ /*
+ * VMRS. return value will be in R0, which is convenient.
+ * Rt will be R0.
+ */
+ instr[0] = 0xeef00a10 | fpreg << 16 | 0 << 12;
+ instr[1] = Retinst;
+ coherence();
+
+ pcaddr = (void *)MAP2PCSPACE(instr, getcallerpc(&fpreg));
+ cachedwbse(pcaddr, sizeof instr);
+ cacheiinv();
+
+ fp = (ulong (*)(void))pcaddr;
+ return (*fp)();
+}
+
+void
+fpwr(int fpreg, ulong val)
+{
+ volatile ulong instr[2];
+ void *pcaddr;
+ void (*fp)(ulong);
+
+ fpreg &= 017;
+ /* VMSR. Rt will be R0. */
+ instr[0] = 0xeee00a10 | fpreg << 16 | 0 << 12;
+ instr[1] = Retinst;
+ coherence();
+
+ pcaddr = (void *)MAP2PCSPACE(instr, getcallerpc(&fpreg));
+ cachedwbse(pcaddr, sizeof instr);
+ cacheiinv();
+
+ fp = (void (*)(ulong))pcaddr;
+ (*fp)(val);
+ coherence();
+}
diff --git a/sys/src/9/omap/dat.h b/sys/src/9/omap/dat.h
new file mode 100755
index 000000000..37114691d
--- /dev/null
+++ b/sys/src/9/omap/dat.h
@@ -0,0 +1,303 @@
+/*
+ * Time.
+ *
+ * HZ should divide 1000 evenly, ideally.
+ * 100, 125, 200, 250 and 333 are okay.
+ */
+#define HZ 100 /* clock frequency */
+#define MS2HZ (1000/HZ) /* millisec per clock tick */
+#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */
+
+enum {
+ Mhz = 1000 * 1000,
+};
+
+/*
+ * More accurate time
+ */
+#define MS2TMR(t) ((ulong)(((uvlong)(t) * m->cpuhz)/1000))
+#define US2TMR(t) ((ulong)(((uvlong)(t) * m->cpuhz)/1000000))
+
+/*
+ * we ignore the first 2 uarts on the omap3530 (see uarti8250.c) and use the
+ * third one but call it 0.
+ */
+#define CONSOLE 0
+
+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 u32int Mreg; /* Msr - bloody UART */
+typedef struct Notsave Notsave;
+typedef struct Page Page;
+typedef struct PhysUart PhysUart;
+typedef struct PMMU PMMU;
+typedef struct Proc Proc;
+typedef u32int PTE;
+typedef struct Uart Uart;
+typedef struct Ureg Ureg;
+typedef uvlong Tval;
+
+#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 */
+ 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;
+ int monitor; /* flag */
+};
+
+/*
+ * 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;
+ ulong delayloop;
+
+ /* stats */
+ int tlbfault;
+ int tlbpurge;
+ int pfault;
+ int cs;
+ int syscall;
+ int load;
+ int intr;
+ uvlong 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, hold R0-R4 */
+ u32int sfiq[5];
+ u32int sirq[5];
+ u32int sund[5];
+ u32int sabt[5];
+ u32int smon[5]; /* probably not needed */
+ u32int ssys[5];
+
+ 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;
+
+extern register Mach* m; /* R10 */
+extern register Proc* up; /* R9 */
+extern uintptr kseg0;
+extern Mach* machaddr[MAXMACH];
+extern ulong memsize;
+extern int normalprint;
+
+/*
+ * 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 l1ip; /* l1 I policy */
+
+ uint nways; /* associativity */
+ uint nsets;
+ uint linelen; /* bytes per cache line */
+ uint setsways;
+
+ uint log2linelen;
+ uint waysh; /* shifts for set/way register */
+ uint setsh;
+};
+
+enum Dmamode {
+ Const,
+ Postincr,
+ Index,
+ Index2,
+};
diff --git a/sys/src/9/omap/devarch.c b/sys/src/9/omap/devarch.c
new file mode 100755
index 000000000..06b18d829
--- /dev/null
+++ b/sys/src/9/omap/devarch.c
@@ -0,0 +1,200 @@
+#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)
+{
+ seprint(buf, buf + size, "Cortex-A8");
+ 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 / Mhz);
+ return readstr(offset, a, n, str);
+}
+
+static long
+tbread(Chan*, void *a, long n, vlong offset)
+{
+ char str[16];
+ uvlong tb;
+
+ cycles(&tb);
+
+ snprint(str, sizeof(str), "%16.16llux", tb);
+ return readstr(offset, a, n, str);
+}
+
+static long
+nsread(Chan*, void *a, long n, vlong offset)
+{
+ char str[16];
+ uvlong tb;
+
+ cycles(&tb);
+
+ snprint(str, sizeof(str), "%16.16llux", (tb/700)* 1000);
+ return readstr(offset, a, n, str);
+}
+
+void
+archinit(void)
+{
+ addarchfile("cputype", 0444, cputyperead, nil);
+ addarchfile("timebase",0444, tbread, nil);
+// addarchfile("nsec", 0444, nsread, nil);
+}
diff --git a/sys/src/9/omap/devcons.c b/sys/src/9/omap/devcons.c
new file mode 100755
index 000000000..f94f12d53
--- /dev/null
+++ b/sys/src/9/omap/devcons.c
@@ -0,0 +1,1354 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "pool.h"
+#include <authsrv.h>
+
+void (*consdebug)(void) = nil;
+void (*screenputs)(char*, int) = nil;
+
+Queue* kbdq; /* unprocessed console input */
+Queue* lineq; /* processed console input */
+Queue* serialoq; /* serial console output */
+Queue* kprintoq; /* console output, for /dev/kprint */
+ulong kprintinuse; /* test and set whether /dev/kprint is open */
+int iprintscreenputs = 1;
+
+int panicking;
+
+static struct
+{
+ QLock;
+
+ int raw; /* true if we shouldn't process input */
+ Ref ctl; /* number of opens to the control file */
+ int x; /* index into line */
+ char line[1024]; /* current input line */
+
+ int count;
+ int ctlpoff;
+
+ /* a place to save up characters at interrupt time before dumping them in the queue */
+ Lock lockputc;
+ char istage[1024];
+ char *iw;
+ char *ir;
+ char *ie;
+} kbd = {
+ .iw = kbd.istage,
+ .ir = kbd.istage,
+ .ie = kbd.istage + sizeof(kbd.istage),
+};
+
+char *sysname;
+vlong fasthz;
+
+static void seedrand(void);
+static int readtime(ulong, char*, int);
+static int readbintime(char*, int);
+static int writetime(char*, int);
+static int writebintime(char*, int);
+
+enum
+{
+ CMhalt,
+ CMreboot,
+ CMpanic,
+};
+
+Cmdtab rebootmsg[] =
+{
+ CMhalt, "halt", 1,
+ CMreboot, "reboot", 0,
+ CMpanic, "panic", 0,
+};
+
+void
+printinit(void)
+{
+ lineq = qopen(2*1024, 0, nil, nil);
+ if(lineq == nil)
+ panic("printinit");
+ qnoblock(lineq, 1);
+}
+
+int
+consactive(void)
+{
+ if(serialoq)
+ return qlen(serialoq) > 0;
+ return 0;
+}
+
+void
+prflush(void)
+{
+ ulong now;
+
+ now = m->ticks;
+ while(consactive())
+ if(m->ticks - now >= HZ)
+ break;
+}
+
+/*
+ * Log console output so it can be retrieved via /dev/kmesg.
+ * This is good for catching boot-time messages after the fact.
+ */
+struct {
+ Lock lk;
+// char buf[16384]; /* normal */
+ char buf[256*1024]; /* for acpi debugging */
+ uint n;
+} kmesg;
+
+static void
+kmesgputs(char *str, int n)
+{
+ uint nn, d;
+
+ ilock(&kmesg.lk);
+ /* take the tail of huge writes */
+ if(n > sizeof kmesg.buf){
+ d = n - sizeof kmesg.buf;
+ str += d;
+ n -= d;
+ }
+
+ /* slide the buffer down to make room */
+ nn = kmesg.n;
+ if(nn + n >= sizeof kmesg.buf){
+ d = nn + n - sizeof kmesg.buf;
+ if(d)
+ memmove(kmesg.buf, kmesg.buf+d, sizeof kmesg.buf-d);
+ nn -= d;
+ }
+
+ /* copy the data in */
+ memmove(kmesg.buf+nn, str, n);
+ nn += n;
+ kmesg.n = nn;
+ iunlock(&kmesg.lk);
+}
+
+/*
+ * Print a string on the console. Convert \n to \r\n for serial
+ * line consoles. Locking of the queues is left up to the screen
+ * or uart code. Multi-line messages to serial consoles may get
+ * interspersed with other messages.
+ */
+static void
+putstrn0(char *str, int n, int usewrite)
+{
+ int m;
+ char *t;
+
+ if(!islo())
+ usewrite = 0;
+
+ /*
+ * how many different output devices do we need?
+ */
+ kmesgputs(str, n);
+
+ /*
+ * if someone is reading /dev/kprint,
+ * put the message there.
+ * if not and there's an attached bit mapped display,
+ * put the message there.
+ *
+ * if there's a serial line being used as a console,
+ * put the message there.
+ */
+ if(kprintoq != nil && !qisclosed(kprintoq)){
+ if(usewrite)
+ qwrite(kprintoq, str, n);
+ else
+ qiwrite(kprintoq, str, n);
+ }else if(screenputs != nil)
+ screenputs(str, n);
+
+ if(serialoq == nil){
+ uartputs(str, n);
+ return;
+ }
+
+ while(n > 0) {
+ t = memchr(str, '\n', n);
+ if(t && !kbd.raw) {
+ m = t-str;
+ if(usewrite){
+ qwrite(serialoq, str, m);
+ qwrite(serialoq, "\r\n", 2);
+ } else {
+ qiwrite(serialoq, str, m);
+ qiwrite(serialoq, "\r\n", 2);
+ }
+ n -= m+1;
+ str = t+1;
+ } else {
+ if(usewrite)
+ qwrite(serialoq, str, n);
+ else
+ qiwrite(serialoq, str, n);
+ break;
+ }
+ }
+}
+
+void
+putstrn(char *str, int n)
+{
+ putstrn0(str, n, 0);
+}
+
+int noprint;
+
+int
+print(char *fmt, ...)
+{
+ int n;
+ va_list arg;
+ char buf[PRINTSIZE];
+
+ if(noprint)
+ return -1;
+
+ va_start(arg, fmt);
+ n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
+ va_end(arg);
+
+ if(!normalprint) {
+ if(0) iprint("\nprint called too early from %#lux\n",
+ getcallerpc(&fmt));
+ iprint("%.*s", n, buf);
+ } else
+ putstrn(buf, n);
+
+ return n;
+}
+
+/*
+ * Want to interlock iprints to avoid interlaced output on
+ * multiprocessor, but don't want to deadlock if one processor
+ * dies during print and another has something important to say.
+ * Make a good faith effort.
+ */
+static Lock iprintlock;
+static int
+iprintcanlock(Lock *l)
+{
+ int i;
+
+ for(i=0; i<1000; i++){
+ if(canlock(l))
+ return 1;
+ if(l->m == MACHP(m->machno))
+ return 0;
+ microdelay(100);
+ }
+ return 0;
+}
+
+int
+iprint(char *fmt, ...)
+{
+ int n, s, locked;
+ va_list arg;
+ char buf[PRINTSIZE];
+
+ s = splhi();
+ va_start(arg, fmt);
+ n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
+ va_end(arg);
+ locked = iprintcanlock(&iprintlock);
+ if(screenputs != nil && iprintscreenputs)
+ screenputs(buf, n);
+ if(consuart == nil || consuart->phys == nil ||
+ consuart->phys->putc == nil)
+ _uartputs(buf, n);
+ else
+ uartputs(buf, n);
+ if(locked)
+ unlock(&iprintlock);
+ splx(s);
+
+ return n;
+}
+
+void
+panic(char *fmt, ...)
+{
+ int n, s;
+ va_list arg;
+ char buf[PRINTSIZE];
+
+ kprintoq = nil; /* don't try to write to /dev/kprint */
+
+ if(panicking)
+ for(;;);
+ panicking = 1;
+
+ delay(20);
+ s = splhi();
+ strcpy(buf, "panic: ");
+ va_start(arg, fmt);
+ n = vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg) - buf;
+ va_end(arg);
+ iprint("%s\n", buf);
+ if(consdebug)
+ (*consdebug)();
+ splx(s);
+ prflush();
+ buf[n] = '\n';
+// putstrn(buf, n+1);
+// dumpstack();
+
+ exit(1);
+}
+
+/* libmp at least contains a few calls to sysfatal; simulate with panic */
+void
+sysfatal(char *fmt, ...)
+{
+ char err[256];
+ va_list arg;
+
+ va_start(arg, fmt);
+ vseprint(err, err + sizeof err, fmt, arg);
+ va_end(arg);
+ panic("sysfatal: %s", err);
+}
+
+void
+_assert(char *fmt)
+{
+ panic("assert failed at %#p: %s", getcallerpc(&fmt), fmt);
+}
+
+int
+pprint(char *fmt, ...)
+{
+ int n;
+ Chan *c;
+ va_list arg;
+ char buf[2*PRINTSIZE];
+
+ if(up == nil || up->fgrp == nil)
+ return 0;
+
+ c = up->fgrp->fd[2];
+ if(c==0 || (c->mode!=OWRITE && c->mode!=ORDWR))
+ return 0;
+ n = snprint(buf, sizeof buf, "%s %lud: ", up->text, up->pid);
+ va_start(arg, fmt);
+ n = vseprint(buf+n, buf+sizeof(buf), fmt, arg) - buf;
+ va_end(arg);
+
+ if(waserror())
+ return 0;
+ devtab[c->type]->write(c, buf, n, c->offset);
+ poperror();
+
+ lock(c);
+ c->offset += n;
+ unlock(c);
+
+ return n;
+}
+
+static void
+echoscreen(char *buf, int n)
+{
+ char *e, *p;
+ char ebuf[128];
+ int x;
+
+ p = ebuf;
+ e = ebuf + sizeof(ebuf) - 4;
+ while(n-- > 0){
+ if(p >= e){
+ screenputs(ebuf, p - ebuf);
+ p = ebuf;
+ }
+ x = *buf++;
+ if(x == 0x15){
+ *p++ = '^';
+ *p++ = 'U';
+ *p++ = '\n';
+ } else
+ *p++ = x;
+ }
+ if(p != ebuf)
+ screenputs(ebuf, p - ebuf);
+}
+
+static void
+echoserialoq(char *buf, int n)
+{
+ int x;
+ char *e, *p;
+ char ebuf[128];
+
+ p = ebuf;
+ e = ebuf + sizeof(ebuf) - 4;
+ while(n-- > 0){
+ if(p >= e){
+ qiwrite(serialoq, ebuf, p - ebuf);
+ p = ebuf;
+ }
+ x = *buf++;
+ if(x == '\n'){
+ *p++ = '\r';
+ *p++ = '\n';
+ } else if(x == 0x15){
+ *p++ = '^';
+ *p++ = 'U';
+ *p++ = '\n';
+ } else
+ *p++ = x;
+ }
+ if(p != ebuf)
+ qiwrite(serialoq, ebuf, p - ebuf);
+}
+
+static void
+echo(char *buf, int n)
+{
+ static int ctrlt, pid;
+ int x;
+ char *e, *p;
+
+ if(n == 0)
+ return;
+
+ e = buf+n;
+ for(p = buf; p < e; p++){
+ switch(*p){
+ case 0x10: /* ^P */
+ if(cpuserver && !kbd.ctlpoff){
+ active.exiting = 1;
+ return;
+ }
+ break;
+ case 0x14: /* ^T */
+ ctrlt++;
+ if(ctrlt > 2)
+ ctrlt = 2;
+ continue;
+ }
+
+ if(ctrlt != 2)
+ continue;
+
+ /* ^T escapes */
+ ctrlt = 0;
+ switch(*p){
+ case 'S':
+ x = splhi();
+ dumpstack();
+ procdump();
+ splx(x);
+ return;
+ case 's':
+ dumpstack();
+ return;
+ case 'x':
+ xsummary();
+ ixsummary();
+ mallocsummary();
+ // memorysummary();
+ pagersummary();
+ return;
+ case 'd':
+ if(consdebug == nil)
+ consdebug = rdb;
+ else
+ consdebug = nil;
+ print("consdebug now %#p\n", consdebug);
+ return;
+ case 'D':
+ if(consdebug == nil)
+ consdebug = rdb;
+ consdebug();
+ return;
+ case 'p':
+ x = spllo();
+ procdump();
+ splx(x);
+ return;
+ case 'q':
+ scheddump();
+ return;
+ case 'k':
+ killbig("^t ^t k");
+ return;
+ case 'r':
+ exit(0);
+ return;
+ }
+ }
+
+ qproduce(kbdq, buf, n);
+ if(kbd.raw)
+ return;
+ kmesgputs(buf, n);
+ if(screenputs != nil)
+ echoscreen(buf, n);
+ if(serialoq)
+ echoserialoq(buf, n);
+}
+
+/*
+ * Called by a uart interrupt for console input.
+ *
+ * turn '\r' into '\n' before putting it into the queue.
+ */
+int
+kbdcr2nl(Queue*, int ch)
+{
+ char *next;
+
+ ilock(&kbd.lockputc); /* just a mutex */
+ if(ch == '\r' && !kbd.raw)
+ ch = '\n';
+ next = kbd.iw+1;
+ if(next >= kbd.ie)
+ next = kbd.istage;
+ if(next != kbd.ir){
+ *kbd.iw = ch;
+ kbd.iw = next;
+ }
+ iunlock(&kbd.lockputc);
+ return 0;
+}
+
+/*
+ * Put character, possibly a rune, into read queue at interrupt time.
+ * Called at interrupt time to process a character.
+ */
+int
+kbdputc(Queue*, int ch)
+{
+ int i, n;
+ char buf[3];
+ Rune r;
+ char *next;
+
+ if(kbd.ir == nil)
+ return 0; /* in case we're not inited yet */
+
+ ilock(&kbd.lockputc); /* just a mutex */
+ r = ch;
+ n = runetochar(buf, &r);
+ for(i = 0; i < n; i++){
+ next = kbd.iw+1;
+ if(next >= kbd.ie)
+ next = kbd.istage;
+ if(next == kbd.ir)
+ break;
+ *kbd.iw = buf[i];
+ kbd.iw = next;
+ }
+ iunlock(&kbd.lockputc);
+ return 0;
+}
+
+/*
+ * we save up input characters till clock time to reduce
+ * per character interrupt overhead.
+ */
+static void
+kbdputcclock(void)
+{
+ char *iw;
+
+ /* this amortizes cost of qproduce */
+ if(kbd.iw != kbd.ir){
+ iw = kbd.iw;
+ if(iw < kbd.ir){
+ echo(kbd.ir, kbd.ie-kbd.ir);
+ kbd.ir = kbd.istage;
+ }
+ if(kbd.ir != iw){
+ echo(kbd.ir, iw-kbd.ir);
+ kbd.ir = iw;
+ }
+ }
+}
+
+enum{
+ Qdir,
+ Qbintime,
+ Qcons,
+ Qconsctl,
+ Qcputime,
+ Qdrivers,
+ Qkmesg,
+ Qkprint,
+ Qhostdomain,
+ Qhostowner,
+ Qnull,
+ Qosversion,
+ Qpgrpid,
+ Qpid,
+ Qppid,
+ Qrandom,
+ Qreboot,
+ Qswap,
+ Qsysname,
+ Qsysstat,
+ Qtime,
+ Quser,
+ Qzero,
+};
+
+enum
+{
+ VLNUMSIZE= 22,
+};
+
+static Dirtab consdir[]={
+ ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
+ "bintime", {Qbintime}, 24, 0664,
+ "cons", {Qcons}, 0, 0660,
+ "consctl", {Qconsctl}, 0, 0220,
+ "cputime", {Qcputime}, 6*NUMSIZE, 0444,
+ "drivers", {Qdrivers}, 0, 0444,
+ "hostdomain", {Qhostdomain}, DOMLEN, 0664,
+ "hostowner", {Qhostowner}, 0, 0664,
+ "kmesg", {Qkmesg}, 0, 0440,
+ "kprint", {Qkprint, 0, QTEXCL}, 0, DMEXCL|0440,
+ "null", {Qnull}, 0, 0666,
+ "osversion", {Qosversion}, 0, 0444,
+ "pgrpid", {Qpgrpid}, NUMSIZE, 0444,
+ "pid", {Qpid}, NUMSIZE, 0444,
+ "ppid", {Qppid}, NUMSIZE, 0444,
+ "random", {Qrandom}, 0, 0444,
+ "reboot", {Qreboot}, 0, 0664,
+ "swap", {Qswap}, 0, 0664,
+ "sysname", {Qsysname}, 0, 0664,
+ "sysstat", {Qsysstat}, 0, 0666,
+ "time", {Qtime}, NUMSIZE+3*VLNUMSIZE, 0664,
+ "user", {Quser}, 0, 0666,
+ "zero", {Qzero}, 0, 0444,
+};
+
+int
+readnum(ulong off, char *buf, ulong n, ulong val, int size)
+{
+ char tmp[64];
+
+ snprint(tmp, sizeof(tmp), "%*lud", size-1, val);
+ tmp[size-1] = ' ';
+ if(off >= size)
+ return 0;
+ if(off+n > size)
+ n = size-off;
+ memmove(buf, tmp+off, n);
+ return n;
+}
+
+int
+readstr(ulong off, char *buf, ulong n, char *str)
+{
+ int size;
+
+ size = strlen(str);
+ if(off >= size)
+ return 0;
+ if(off+n > size)
+ n = size-off;
+ memmove(buf, str+off, n);
+ return n;
+}
+
+static void
+consinit(void)
+{
+ todinit();
+ randominit();
+ /*
+ * at 115200 baud, the 1024 char buffer takes 56 ms to process,
+ * processing it every 22 ms should be fine
+ */
+ addclock0link(kbdputcclock, 22);
+}
+
+static Chan*
+consattach(char *spec)
+{
+ return devattach('c', spec);
+}
+
+static Walkqid*
+conswalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name,nname, consdir, nelem(consdir), devgen);
+}
+
+static int
+consstat(Chan *c, uchar *dp, int n)
+{
+ return devstat(c, dp, n, consdir, nelem(consdir), devgen);
+}
+
+static Chan*
+consopen(Chan *c, int omode)
+{
+ c->aux = nil;
+ c = devopen(c, omode, consdir, nelem(consdir), devgen);
+ switch((ulong)c->qid.path){
+ case Qconsctl:
+ incref(&kbd.ctl);
+ break;
+
+ case Qkprint:
+ if(tas(&kprintinuse) != 0){
+ c->flag &= ~COPEN;
+ error(Einuse);
+ }
+ if(kprintoq == nil){
+ kprintoq = qopen(8*1024, Qcoalesce, 0, 0);
+ if(kprintoq == nil){
+ c->flag &= ~COPEN;
+ error(Enomem);
+ }
+ qnoblock(kprintoq, 1);
+ }else
+ qreopen(kprintoq);
+ c->iounit = qiomaxatomic;
+ break;
+ }
+ return c;
+}
+
+static void
+consclose(Chan *c)
+{
+ switch((ulong)c->qid.path){
+ /* last close of control file turns off raw */
+ case Qconsctl:
+ if(c->flag&COPEN){
+ if(decref(&kbd.ctl) == 0)
+ kbd.raw = 0;
+ }
+ break;
+
+ /* close of kprint allows other opens */
+ case Qkprint:
+ if(c->flag & COPEN){
+ kprintinuse = 0;
+ qhangup(kprintoq, nil);
+ }
+ break;
+ }
+}
+
+static long
+consread(Chan *c, void *buf, long n, vlong off)
+{
+ ulong l;
+ Mach *mp;
+ char *b, *bp, ch;
+ char tmp[256]; /* must be >= 18*NUMSIZE (Qswap) */
+ int i, k, id, send;
+ vlong offset = off;
+
+ if(n <= 0)
+ return n;
+
+ switch((ulong)c->qid.path){
+ case Qdir:
+ return devdirread(c, buf, n, consdir, nelem(consdir), devgen);
+
+ case Qcons:
+ qlock(&kbd);
+ if(waserror()) {
+ qunlock(&kbd);
+ nexterror();
+ }
+ while(!qcanread(lineq)){
+ if(qread(kbdq, &ch, 1) == 0)
+ continue;
+ send = 0;
+ if(ch == 0){
+ /* flush output on rawoff -> rawon */
+ if(kbd.x > 0)
+ send = !qcanread(kbdq);
+ }else if(kbd.raw){
+ kbd.line[kbd.x++] = ch;
+ send = !qcanread(kbdq);
+ }else{
+ switch(ch){
+ case '\b':
+ if(kbd.x > 0)
+ kbd.x--;
+ break;
+ case 0x15: /* ^U */
+ kbd.x = 0;
+ break;
+ case '\n':
+ case 0x04: /* ^D */
+ send = 1;
+ default:
+ if(ch != 0x04)
+ kbd.line[kbd.x++] = ch;
+ break;
+ }
+ }
+ if(send || kbd.x == sizeof kbd.line){
+ qwrite(lineq, kbd.line, kbd.x);
+ kbd.x = 0;
+ }
+ }
+ n = qread(lineq, buf, n);
+ qunlock(&kbd);
+ poperror();
+ return n;
+
+ case Qcputime:
+ k = offset;
+ if(k >= 6*NUMSIZE)
+ return 0;
+ if(k+n > 6*NUMSIZE)
+ n = 6*NUMSIZE - k;
+ /* easiest to format in a separate buffer and copy out */
+ for(i=0; i<6 && NUMSIZE*i<k+n; i++){
+ l = up->time[i];
+ if(i == TReal)
+ l = MACHP(0)->ticks - l;
+ l = TK2MS(l);
+ readnum(0, tmp+NUMSIZE*i, NUMSIZE, l, NUMSIZE);
+ }
+ memmove(buf, tmp+k, n);
+ return n;
+
+ case Qkmesg:
+ /*
+ * This is unlocked to avoid tying up a process
+ * that's writing to the buffer. kmesg.n never
+ * gets smaller, so worst case the reader will
+ * see a slurred buffer.
+ */
+ if(off >= kmesg.n)
+ n = 0;
+ else{
+ if(off+n > kmesg.n)
+ n = kmesg.n - off;
+ memmove(buf, kmesg.buf+off, n);
+ }
+ return n;
+
+ case Qkprint:
+ return qread(kprintoq, buf, n);
+
+ case Qpgrpid:
+ return readnum((ulong)offset, buf, n, up->pgrp->pgrpid, NUMSIZE);
+
+ case Qpid:
+ return readnum((ulong)offset, buf, n, up->pid, NUMSIZE);
+
+ case Qppid:
+ return readnum((ulong)offset, buf, n, up->parentpid, NUMSIZE);
+
+ case Qtime:
+ return readtime((ulong)offset, buf, n);
+
+ case Qbintime:
+ return readbintime(buf, n);
+
+ case Qhostowner:
+ return readstr((ulong)offset, buf, n, eve);
+
+ case Qhostdomain:
+ return readstr((ulong)offset, buf, n, hostdomain);
+
+ case Quser:
+ return readstr((ulong)offset, buf, n, up->user);
+
+ case Qnull:
+ return 0;
+
+ case Qsysstat:
+ b = smalloc(conf.nmach*(NUMSIZE*11+1) + 1); /* +1 for NUL */
+ bp = b;
+ for(id = 0; id < 32; id++) {
+ if(active.machs & (1<<id)) {
+ mp = MACHP(id);
+ readnum(0, bp, NUMSIZE, id, NUMSIZE);
+ bp += NUMSIZE;
+ readnum(0, bp, NUMSIZE, mp->cs, NUMSIZE);
+ bp += NUMSIZE;
+ readnum(0, bp, NUMSIZE, mp->intr, NUMSIZE);
+ bp += NUMSIZE;
+ readnum(0, bp, NUMSIZE, mp->syscall, NUMSIZE);
+ bp += NUMSIZE;
+ readnum(0, bp, NUMSIZE, mp->pfault, NUMSIZE);
+ bp += NUMSIZE;
+ readnum(0, bp, NUMSIZE, mp->tlbfault, NUMSIZE);
+ bp += NUMSIZE;
+ readnum(0, bp, NUMSIZE, mp->tlbpurge, NUMSIZE);
+ bp += NUMSIZE;
+ readnum(0, bp, NUMSIZE, mp->load, NUMSIZE);
+ bp += NUMSIZE;
+ readnum(0, bp, NUMSIZE,
+ (mp->perf.avg_inidle*100)/mp->perf.period,
+ NUMSIZE);
+ bp += NUMSIZE;
+ readnum(0, bp, NUMSIZE,
+ (mp->perf.avg_inintr*100)/mp->perf.period,
+ NUMSIZE);
+ bp += NUMSIZE;
+ *bp++ = '\n';
+ }
+ }
+ if(waserror()){
+ free(b);
+ nexterror();
+ }
+ n = readstr((ulong)offset, buf, n, b);
+ free(b);
+ poperror();
+ return n;
+
+ case Qswap:
+ snprint(tmp, sizeof tmp,
+ "%lud memory\n"
+ "%d pagesize\n"
+ "%lud kernel\n"
+ "%lud/%lud user\n"
+ "%lud/%lud swap\n"
+ "%lud/%lud kernel malloc\n"
+ "%lud/%lud kernel draw\n",
+ conf.npage*BY2PG,
+ BY2PG,
+ conf.npage-conf.upages,
+ palloc.user-palloc.freecount, palloc.user,
+ conf.nswap-swapalloc.free, conf.nswap,
+ mainmem->cursize, mainmem->maxsize,
+ imagmem->cursize, imagmem->maxsize);
+
+ return readstr((ulong)offset, buf, n, tmp);
+
+ case Qsysname:
+ if(sysname == nil)
+ return 0;
+ return readstr((ulong)offset, buf, n, sysname);
+
+ case Qrandom:
+ return randomread(buf, n);
+
+ case Qdrivers:
+ b = malloc(READSTR);
+ if(b == nil)
+ error(Enomem);
+ k = 0;
+ for(i = 0; devtab[i] != nil; i++)
+ k += snprint(b+k, READSTR-k, "#%C %s\n",
+ devtab[i]->dc, devtab[i]->name);
+ if(waserror()){
+ free(b);
+ nexterror();
+ }
+ n = readstr((ulong)offset, buf, n, b);
+ free(b);
+ poperror();
+ return n;
+
+ case Qzero:
+ memset(buf, 0, n);
+ return n;
+
+ case Qosversion:
+ snprint(tmp, sizeof tmp, "2000");
+ n = readstr((ulong)offset, buf, n, tmp);
+ return n;
+
+ default:
+ print("consread %#llux\n", c->qid.path);
+ error(Egreg);
+ }
+ return -1; /* never reached */
+}
+
+static long
+conswrite(Chan *c, void *va, long n, vlong off)
+{
+ char buf[256], ch;
+ long l, bp;
+ char *a;
+ Mach *mp;
+ int id, fd;
+ Chan *swc;
+ ulong offset;
+ Cmdbuf *cb;
+ Cmdtab *ct;
+
+ a = va;
+ offset = off;
+
+ switch((ulong)c->qid.path){
+ case Qcons:
+ /*
+ * Can't page fault in putstrn, so copy the data locally.
+ */
+ l = n;
+ while(l > 0){
+ bp = l;
+ if(bp > sizeof buf)
+ bp = sizeof buf;
+ memmove(buf, a, bp);
+ putstrn0(buf, bp, 1);
+ a += bp;
+ l -= bp;
+ }
+ break;
+
+ case Qconsctl:
+ if(n >= sizeof(buf))
+ n = sizeof(buf)-1;
+ strncpy(buf, a, n);
+ buf[n] = 0;
+ for(a = buf; a;){
+ if(strncmp(a, "rawon", 5) == 0){
+ kbd.raw = 1;
+ /* clumsy hack - wake up reader */
+ ch = 0;
+ qwrite(kbdq, &ch, 1);
+ } else if(strncmp(a, "rawoff", 6) == 0){
+ kbd.raw = 0;
+ } else if(strncmp(a, "ctlpon", 6) == 0){
+ kbd.ctlpoff = 0;
+ } else if(strncmp(a, "ctlpoff", 7) == 0){
+ kbd.ctlpoff = 1;
+ }
+ if(a = strchr(a, ' '))
+ a++;
+ }
+ break;
+
+ case Qtime:
+ if(!iseve())
+ error(Eperm);
+ return writetime(a, n);
+
+ case Qbintime:
+ if(!iseve())
+ error(Eperm);
+ return writebintime(a, n);
+
+ case Qhostowner:
+ return hostownerwrite(a, n);
+
+ case Qhostdomain:
+ return hostdomainwrite(a, n);
+
+ case Quser:
+ return userwrite(a, n);
+
+ case Qnull:
+ break;
+
+ case Qreboot:
+ if(!iseve())
+ error(Eperm);
+ cb = parsecmd(a, n);
+
+ if(waserror()) {
+ free(cb);
+ nexterror();
+ }
+ ct = lookupcmd(cb, rebootmsg, nelem(rebootmsg));
+ switch(ct->index) {
+ case CMhalt:
+ reboot(nil, 0, 0);
+ break;
+ case CMreboot:
+ rebootcmd(cb->nf-1, cb->f+1);
+ break;
+ case CMpanic:
+ *(ulong*)0=0;
+ panic("/dev/reboot");
+ }
+ poperror();
+ free(cb);
+ break;
+
+ case Qsysstat:
+ for(id = 0; id < 32; id++) {
+ if(active.machs & (1<<id)) {
+ mp = MACHP(id);
+ mp->cs = 0;
+ mp->intr = 0;
+ mp->syscall = 0;
+ mp->pfault = 0;
+ mp->tlbfault = 0;
+ mp->tlbpurge = 0;
+ }
+ }
+ break;
+
+ case Qswap:
+ if(n >= sizeof buf)
+ error(Egreg);
+ memmove(buf, va, n); /* so we can NUL-terminate */
+ buf[n] = 0;
+ /* start a pager if not already started */
+ if(strncmp(buf, "start", 5) == 0){
+ kickpager();
+ break;
+ }
+ if(!iseve())
+ error(Eperm);
+ if(buf[0]<'0' || '9'<buf[0])
+ error(Ebadarg);
+ fd = strtoul(buf, 0, 0);
+ swc = fdtochan(fd, -1, 1, 1);
+ setswapchan(swc);
+ break;
+
+ case Qsysname:
+ if(offset != 0)
+ error(Ebadarg);
+ if(n <= 0 || n >= sizeof buf)
+ error(Ebadarg);
+ strncpy(buf, a, n);
+ buf[n] = 0;
+ if(buf[n-1] == '\n')
+ buf[n-1] = 0;
+ kstrdup(&sysname, buf);
+ break;
+
+ default:
+ print("conswrite: %#llux\n", c->qid.path);
+ error(Egreg);
+ }
+ return n;
+}
+
+Dev consdevtab = {
+ 'c',
+ "cons",
+
+ devreset,
+ consinit,
+ devshutdown,
+ consattach,
+ conswalk,
+ consstat,
+ consopen,
+ devcreate,
+ consclose,
+ consread,
+ devbread,
+ conswrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
+
+static ulong randn;
+
+static void
+seedrand(void)
+{
+ if(!waserror()){
+ randomread((void*)&randn, sizeof(randn));
+ poperror();
+ }
+}
+
+int
+nrand(int n)
+{
+ if(randn == 0)
+ seedrand();
+ randn = randn*1103515245 + 12345 + MACHP(0)->ticks;
+ return (randn>>16) % n;
+}
+
+int
+rand(void)
+{
+ nrand(1);
+ return randn;
+}
+
+static uvlong uvorder = 0x0001020304050607ULL;
+
+static uchar*
+le2vlong(vlong *to, uchar *f)
+{
+ uchar *t, *o;
+ int i;
+
+ t = (uchar*)to;
+ o = (uchar*)&uvorder;
+ for(i = 0; i < sizeof(vlong); i++)
+ t[o[i]] = f[i];
+ return f+sizeof(vlong);
+}
+
+static uchar*
+vlong2le(uchar *t, vlong from)
+{
+ uchar *f, *o;
+ int i;
+
+ f = (uchar*)&from;
+ o = (uchar*)&uvorder;
+ for(i = 0; i < sizeof(vlong); i++)
+ t[i] = f[o[i]];
+ return t+sizeof(vlong);
+}
+
+static long order = 0x00010203;
+
+static uchar*
+le2long(long *to, uchar *f)
+{
+ uchar *t, *o;
+ int i;
+
+ t = (uchar*)to;
+ o = (uchar*)&order;
+ for(i = 0; i < sizeof(long); i++)
+ t[o[i]] = f[i];
+ return f+sizeof(long);
+}
+
+static uchar*
+long2le(uchar *t, long from)
+{
+ uchar *f, *o;
+ int i;
+
+ f = (uchar*)&from;
+ o = (uchar*)&order;
+ for(i = 0; i < sizeof(long); i++)
+ t[i] = f[o[i]];
+ return t+sizeof(long);
+}
+
+char *Ebadtimectl = "bad time control";
+
+/*
+ * like the old #c/time but with added info. Return
+ *
+ * secs nanosecs fastticks fasthz
+ */
+static int
+readtime(ulong off, char *buf, int n)
+{
+ vlong nsec, ticks;
+ long sec;
+ char str[7*NUMSIZE];
+
+ nsec = todget(&ticks);
+ if(fasthz == 0LL)
+ fastticks((uvlong*)&fasthz);
+ sec = nsec/1000000000ULL;
+ snprint(str, sizeof(str), "%*lud %*llud %*llud %*llud ",
+ NUMSIZE-1, sec,
+ VLNUMSIZE-1, nsec,
+ VLNUMSIZE-1, ticks,
+ VLNUMSIZE-1, fasthz);
+ return readstr(off, buf, n, str);
+}
+
+/*
+ * set the time in seconds
+ */
+static int
+writetime(char *buf, int n)
+{
+ char b[13];
+ long i;
+ vlong now;
+
+ if(n >= sizeof(b))
+ error(Ebadtimectl);
+ strncpy(b, buf, n);
+ b[n] = 0;
+ i = strtol(b, 0, 0);
+ if(i <= 0)
+ error(Ebadtimectl);
+ now = i*1000000000LL;
+ todset(now, 0, 0);
+ return n;
+}
+
+/*
+ * read binary time info. all numbers are little endian.
+ * ticks and nsec are syncronized.
+ */
+static int
+readbintime(char *buf, int n)
+{
+ int i;
+ vlong nsec, ticks;
+ uchar *b = (uchar*)buf;
+
+ i = 0;
+ if(fasthz == 0LL)
+ fastticks((uvlong*)&fasthz);
+ nsec = todget(&ticks);
+ if(n >= 3*sizeof(uvlong)){
+ vlong2le(b+2*sizeof(uvlong), fasthz);
+ i += sizeof(uvlong);
+ }
+ if(n >= 2*sizeof(uvlong)){
+ vlong2le(b+sizeof(uvlong), ticks);
+ i += sizeof(uvlong);
+ }
+ if(n >= 8){
+ vlong2le(b, nsec);
+ i += sizeof(vlong);
+ }
+ return i;
+}
+
+/*
+ * set any of the following
+ * - time in nsec
+ * - nsec trim applied over some seconds
+ * - clock frequency
+ */
+static int
+writebintime(char *buf, int n)
+{
+ uchar *p;
+ vlong delta;
+ long period;
+
+ n--;
+ p = (uchar*)buf + 1;
+ switch(*buf){
+ case 'n':
+ if(n < sizeof(vlong))
+ error(Ebadtimectl);
+ le2vlong(&delta, p);
+ todset(delta, 0, 0);
+ break;
+ case 'd':
+ if(n < sizeof(vlong)+sizeof(long))
+ error(Ebadtimectl);
+ p = le2vlong(&delta, p);
+ le2long(&period, p);
+ todset(-1, delta, period);
+ break;
+ case 'f':
+ if(n < sizeof(uvlong))
+ error(Ebadtimectl);
+ le2vlong(&fasthz, p);
+ if(fasthz <= 0)
+ error(Ebadtimectl);
+ todsetfreq(fasthz);
+ break;
+ }
+ return n;
+}
diff --git a/sys/src/9/omap/devdss.c b/sys/src/9/omap/devdss.c
new file mode 100755
index 000000000..17ad06abd
--- /dev/null
+++ b/sys/src/9/omap/devdss.c
@@ -0,0 +1,180 @@
+/*
+ * omap35 display subsystem (dss) device interface to screen.c.
+ * implements #v/vgactl
+ */
+#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"
+
+#define Image IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+// #include "gamma.h"
+
+enum {
+ Qdir,
+ Qdss,
+};
+
+extern OScreen oscreen;
+extern Settings settings[];
+extern Omap3fb *framebuf;
+
+static QLock dsslck;
+static Dirtab dsstab[] = {
+ ".", {Qdir, 0, QTDIR}, 0, 0555|DMDIR,
+ "vgactl", {Qdss, 0}, 0, 0666,
+};
+
+static Chan*
+screenattach(char *spec)
+{
+ return devattach('v', spec);
+}
+
+static Walkqid*
+screenwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, dsstab, nelem(dsstab), devgen);
+}
+
+static int
+screenstat(Chan *c, uchar *dp, int n)
+{
+ return devstat(c, dp, n, dsstab, nelem(dsstab), devgen);
+}
+
+static Chan*
+screenopen(Chan *c, int omode)
+{
+ if ((ulong)c->qid.path == Qdss) {
+ qlock(&dsslck);
+ oscreen.open = 1;
+ c->mode = openmode(omode);
+ c->flag |= COPEN;
+ c->offset = 0;
+ }
+ return c;
+}
+
+static void
+screenclose(Chan *c)
+{
+ if ((c->qid.type & QTDIR) == 0 && c->flag & COPEN)
+ if (c->qid.path == Qdss) {
+ oscreen.open = 0;
+ qunlock(&dsslck);
+ }
+}
+
+static ulong
+getchans(char *p)
+{
+ if (strncmp("x24" , p, 3) == 0)
+ return RGB24; /* can't work yet, pixels are shorts */
+ else if (strncmp("x16", p, 3) == 0)
+ return RGB16;
+ else
+ return RGB16;
+}
+
+static long
+settingswrite(OScreen *scr, char *p)
+{
+ if (strncmp("800x600", p, 7) == 0) {
+ p += 7;
+ scr->settings = &settings[Res800x600];
+ } else if (strncmp("1024x768", p, 8) == 0) {
+ p += 8;
+ scr->settings = &settings[Res1024x768];
+ } else if (strncmp("1280x1024", p, 9) == 0) {
+ p += 9;
+ scr->settings = &settings[Res1280x1024];
+ } else
+ return -1;
+ scr->settings->chan = getchans(p);
+ return 1;
+}
+
+static long
+screenread(Chan *c, void *a, long n, vlong off)
+{
+ int len, depth;
+ char *p;
+ Settings *set;
+
+ switch ((ulong)c->qid.path) {
+ case Qdir:
+ return devdirread(c, a, n, dsstab, nelem(dsstab), devgen);
+ case Qdss:
+ set = oscreen.settings;
+ p = malloc(READSTR);
+ if(waserror()){
+ free(p);
+ nexterror();
+ }
+ if (set->chan == RGB16)
+ depth = 16;
+ else if (set->chan == RGB24)
+ depth = 24;
+ else
+ depth = 0;
+ len = snprint(p, READSTR, "size %dx%dx%d @ %d Hz\n"
+ "addr %#p size %ud\n", set->wid, set->ht, depth,
+ set->freq, framebuf, sizeof *framebuf);
+ USED(len);
+ n = readstr(off, a, n, p);
+ poperror();
+ free(p);
+ return n;
+ default:
+ error(Egreg);
+ }
+ return 0;
+}
+
+static long
+screenwrite(Chan *c, void *a, long n, vlong off)
+{
+ switch ((ulong)c->qid.path) {
+ case Qdss:
+ if(off)
+ error(Ebadarg);
+ n = settingswrite(&oscreen, a);
+ if (n < 0)
+ error(Ebadctl);
+ screeninit();
+ return n;
+ default:
+ error(Egreg);
+ }
+ return 0;
+}
+
+Dev dssdevtab = {
+ L'v',
+ "dss",
+
+ devreset,
+ devinit,
+ devshutdown, // TODO add a shutdown to stop dma to monitor
+ screenattach,
+ screenwalk,
+ screenstat,
+ screenopen,
+ devcreate,
+ screenclose,
+ screenread,
+ devbread,
+ screenwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
diff --git a/sys/src/9/omap/devether.c b/sys/src/9/omap/devether.c
new file mode 100755
index 000000000..f6c32b9f0
--- /dev/null
+++ b/sys/src/9/omap/devether.c
@@ -0,0 +1,530 @@
+#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) != nil && (f->type == type || f->type < 0) &&
+ (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(cb->f[0] && strcmp(cb->f[0], "nonblocking") == 0){
+ if(cb->nf <= 1)
+ onoff = 1;
+ else
+ onoff = atoi(cb->f[1]);
+ qnoblock(ether->oq, onoff);
+ free(cb);
+ return n;
+ }
+ free(cb);
+ if(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;
+
+ if(isaconfig("ether", ctlrno, ether) == 0){
+ free(ether);
+// return nil;
+ 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],
+ "100BASE-TXFD") == 0)
+ ether->mbps = 100;
+ if(cards[n].reset(ether))
+ break;
+ snprint(name, sizeof(name), "ether%d", ctlrno);
+
+ if(ether->interrupt != nil && ether->irq >= 0)
+ intrenable(ether->irq, ether->interrupt,
+ ether, 0, 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");
+ iprint("%s", buf); /* it may be too early for print */
+
+ 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/omap/devuart.c b/sys/src/9/omap/devuart.c
new file mode 100755
index 000000000..af4fce768
--- /dev/null
+++ b/sys/src/9/omap/devuart.c
@@ -0,0 +1,789 @@
+#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"
+
+enum
+{
+ /* soft flow control chars */
+ CTLS= 023,
+ CTLQ= 021,
+};
+
+extern Dev uartdevtab;
+extern PhysUart* physuart[];
+
+static Uart* uartlist;
+static Uart** uart;
+static int uartnuart;
+static Dirtab *uartdir;
+static int uartndir;
+static Timer *uarttimer;
+
+struct Uartalloc {
+ Lock;
+ Uart *elist; /* list of enabled interfaces */
+} uartalloc;
+
+static void uartclock(void);
+static void uartflow(void*);
+
+/*
+ * enable/disable uart and add/remove to list of enabled uarts
+ */
+//static
+Uart*
+uartenable(Uart *p)
+{
+ Uart **l;
+
+ if (up == nil)
+ return p; /* too soon; try again later */
+// return nil;
+
+ if(p->iq == nil){
+ if((p->iq = qopen(8*1024, 0, uartflow, p)) == nil)
+ return nil;
+ }
+ else
+ qreopen(p->iq);
+ if(p->oq == nil){
+ if((p->oq = qopen(8*1024, 0, uartkick, p)) == nil){
+ qfree(p->iq);
+ p->iq = nil;
+ return nil;
+ }
+ }
+ else
+ qreopen(p->oq);
+
+ p->ir = p->istage;
+ p->iw = p->istage;
+ p->ie = &p->istage[Stagesize];
+ p->op = p->ostage;
+ p->oe = p->ostage;
+
+ p->hup_dsr = p->hup_dcd = 0;
+ p->dsr = p->dcd = 0;
+
+ /* assume we can send */
+ p->cts = 1;
+ p->ctsbackoff = 0;
+
+if (up) {
+ if(p->bits == 0)
+ uartctl(p, "l8");
+ if(p->stop == 0)
+ uartctl(p, "s1");
+ if(p->parity == 0)
+ uartctl(p, "pn");
+ if(p->baud == 0)
+ uartctl(p, "b9600");
+ (*p->phys->enable)(p, 1);
+}
+
+ /*
+ * use ilock because uartclock can otherwise interrupt here
+ * and would hang on an attempt to lock uartalloc.
+ */
+ ilock(&uartalloc);
+ for(l = &uartalloc.elist; *l; l = &(*l)->elist){
+ if(*l == p)
+ break;
+ }
+ if(*l == 0){
+ p->elist = uartalloc.elist;
+ uartalloc.elist = p;
+ }
+ p->enabled = 1;
+ iunlock(&uartalloc);
+
+ return p;
+}
+
+static void
+uartdisable(Uart *p)
+{
+ Uart **l;
+
+ (*p->phys->disable)(p);
+
+ ilock(&uartalloc);
+ for(l = &uartalloc.elist; *l; l = &(*l)->elist){
+ if(*l == p){
+ *l = p->elist;
+ break;
+ }
+ }
+ p->enabled = 0;
+ iunlock(&uartalloc);
+}
+
+void
+uartmouse(Uart* p, int (*putc)(Queue*, int), int setb1200)
+{
+ qlock(p);
+ if(p->opens++ == 0 && uartenable(p) == nil){
+ qunlock(p);
+ error(Enodev);
+ }
+ if(setb1200)
+ uartctl(p, "b1200");
+ p->putc = putc;
+ p->special = 1;
+ qunlock(p);
+}
+
+void
+uartsetmouseputc(Uart* p, int (*putc)(Queue*, int))
+{
+ qlock(p);
+ if(p->opens == 0 || p->special == 0){
+ qunlock(p);
+ error(Enodev);
+ }
+ p->putc = putc;
+ qunlock(p);
+}
+
+static void
+setlength(int i)
+{
+ Uart *p;
+
+ if(i > 0){
+ p = uart[i];
+ if(p && p->opens && p->iq)
+ uartdir[1+3*i].length = qlen(p->iq);
+ } else for(i = 0; i < uartnuart; i++){
+ p = uart[i];
+ if(p && p->opens && p->iq)
+ uartdir[1+3*i].length = qlen(p->iq);
+ }
+}
+
+/*
+ * set up the '#t' directory
+ */
+static void
+uartreset(void)
+{
+ int i;
+ Dirtab *dp;
+ Uart *p, *tail;
+
+ tail = nil;
+ for(i = 0; physuart[i] != nil; i++){
+ if(physuart[i]->pnp == nil)
+ continue;
+ if((p = physuart[i]->pnp()) == nil)
+ continue;
+ if(uartlist != nil)
+ tail->next = p;
+ else
+ uartlist = p;
+ for(tail = p; tail->next != nil; tail = tail->next)
+ uartnuart++;
+ uartnuart++;
+ }
+
+ if(uartnuart)
+ uart = xalloc(uartnuart*sizeof(Uart*));
+
+ uartndir = 1 + 3*uartnuart;
+ uartdir = xalloc(uartndir * sizeof(Dirtab));
+ if (uart == nil || uartdir == nil)
+ panic("uartreset: no memory");
+ dp = uartdir;
+ strcpy(dp->name, ".");
+ mkqid(&dp->qid, 0, 0, QTDIR);
+ dp->length = 0;
+ dp->perm = DMDIR|0555;
+ dp++;
+ p = uartlist;
+ for(i = 0; i < uartnuart; i++){
+ /* 3 directory entries per port */
+ snprint(dp->name, sizeof dp->name, "eia%d", i);
+ dp->qid.path = NETQID(i, Ndataqid);
+ dp->perm = 0660;
+ dp++;
+ snprint(dp->name, sizeof dp->name, "eia%dctl", i);
+ dp->qid.path = NETQID(i, Nctlqid);
+ dp->perm = 0660;
+ dp++;
+ snprint(dp->name, sizeof dp->name, "eia%dstatus", i);
+ dp->qid.path = NETQID(i, Nstatqid);
+ dp->perm = 0444;
+ dp++;
+
+ uart[i] = p;
+ p->dev = i;
+ if(p->console || p->special){
+ if(uartenable(p) != nil){
+ if(p->console && up){
+ kbdq = p->iq;
+ serialoq = p->oq;
+ p->putc = kbdcr2nl;
+ }
+ p->opens++;
+ }
+ }
+ p = p->next;
+ }
+
+ if(uartnuart){
+ /*
+ * at 115200 baud, the 1024 char buffer takes 56 ms to process,
+ * processing it every 22 ms should be fine.
+ */
+ uarttimer = addclock0link(uartclock, 22);
+ }
+}
+
+
+static Chan*
+uartattach(char *spec)
+{
+ return devattach('t', spec);
+}
+
+static Walkqid*
+uartwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, uartdir, uartndir, devgen);
+}
+
+static int
+uartstat(Chan *c, uchar *dp, int n)
+{
+ if(NETTYPE(c->qid.path) == Ndataqid)
+ setlength(NETID(c->qid.path));
+ return devstat(c, dp, n, uartdir, uartndir, devgen);
+}
+
+static Chan*
+uartopen(Chan *c, int omode)
+{
+ Uart *p;
+
+ c = devopen(c, omode, uartdir, uartndir, devgen);
+
+ switch(NETTYPE(c->qid.path)){
+ case Nctlqid:
+ case Ndataqid:
+ p = uart[NETID(c->qid.path)];
+ qlock(p);
+ if(p->opens++ == 0 && uartenable(p) == nil){
+ qunlock(p);
+ c->flag &= ~COPEN;
+ error(Enodev);
+ }
+ qunlock(p);
+ break;
+ }
+
+ c->iounit = qiomaxatomic;
+ return c;
+}
+
+static int
+uartdrained(void* arg)
+{
+ Uart *p;
+
+ p = arg;
+ return qlen(p->oq) == 0 && p->op == p->oe;
+}
+
+static void
+uartdrainoutput(Uart *p)
+{
+ if(!p->enabled || up == nil)
+ return;
+
+ p->drain = 1;
+ if(waserror()){
+ p->drain = 0;
+ nexterror();
+ }
+ sleep(&p->r, uartdrained, p);
+ poperror();
+}
+
+static void
+uartclose(Chan *c)
+{
+ Uart *p;
+
+ if(c->qid.type & QTDIR)
+ return;
+ if((c->flag & COPEN) == 0)
+ return;
+ switch(NETTYPE(c->qid.path)){
+ case Ndataqid:
+ case Nctlqid:
+ p = uart[NETID(c->qid.path)];
+ qlock(p);
+ if(--(p->opens) == 0){
+ qclose(p->iq);
+ ilock(&p->rlock);
+ p->ir = p->iw = p->istage;
+ iunlock(&p->rlock);
+
+ /*
+ */
+ qhangup(p->oq, nil);
+ if(!waserror()){
+ uartdrainoutput(p);
+ poperror();
+ }
+ qclose(p->oq);
+ uartdisable(p);
+ p->dcd = p->dsr = p->dohup = 0;
+ }
+ qunlock(p);
+ break;
+ }
+}
+
+static long
+uartread(Chan *c, void *buf, long n, vlong off)
+{
+ Uart *p;
+ ulong offset = off;
+
+ if(c->qid.type & QTDIR){
+ setlength(-1);
+ return devdirread(c, buf, n, uartdir, uartndir, devgen);
+ }
+
+ p = uart[NETID(c->qid.path)];
+ switch(NETTYPE(c->qid.path)){
+ case Ndataqid:
+ return qread(p->iq, buf, n);
+ case Nctlqid:
+ return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE);
+ case Nstatqid:
+ return (*p->phys->status)(p, buf, n, offset);
+ }
+
+ return 0;
+}
+
+int
+uartctl(Uart *p, char *cmd)
+{
+ char *f[16];
+ int i, n, nf;
+
+ nf = tokenize(cmd, f, nelem(f));
+ for(i = 0; i < nf; i++){
+ if(strncmp(f[i], "break", 5) == 0){
+ (*p->phys->dobreak)(p, 0);
+ continue;
+ }
+
+ n = atoi(f[i]+1);
+ switch(*f[i]){
+ case 'B':
+ case 'b':
+ uartdrainoutput(p);
+ if((*p->phys->baud)(p, n) < 0)
+ return -1;
+ break;
+ case 'C':
+ case 'c':
+ p->hup_dcd = n;
+ break;
+ case 'D':
+ case 'd':
+ uartdrainoutput(p);
+ (*p->phys->dtr)(p, n);
+ break;
+ case 'E':
+ case 'e':
+ p->hup_dsr = n;
+ break;
+ case 'f':
+ case 'F':
+ if(p->oq != nil)
+ qflush(p->oq);
+ break;
+ case 'H':
+ case 'h':
+ if(p->iq != nil)
+ qhangup(p->iq, 0);
+ if(p->oq != nil)
+ qhangup(p->oq, 0);
+ break;
+ case 'i':
+ case 'I':
+ uartdrainoutput(p);
+ (*p->phys->fifo)(p, n);
+ break;
+ case 'K':
+ case 'k':
+ uartdrainoutput(p);
+ (*p->phys->dobreak)(p, n);
+ break;
+ case 'L':
+ case 'l':
+ uartdrainoutput(p);
+ if((*p->phys->bits)(p, n) < 0)
+ return -1;
+ break;
+ case 'm':
+ case 'M':
+ uartdrainoutput(p);
+ (*p->phys->modemctl)(p, n);
+ break;
+ case 'n':
+ case 'N':
+ if(p->oq != nil)
+ qnoblock(p->oq, n);
+ break;
+ case 'P':
+ case 'p':
+ uartdrainoutput(p);
+ if((*p->phys->parity)(p, *(f[i]+1)) < 0)
+ return -1;
+ break;
+ case 'Q':
+ case 'q':
+ if(p->iq != nil)
+ qsetlimit(p->iq, n);
+ if(p->oq != nil)
+ qsetlimit(p->oq, n);
+ break;
+ case 'R':
+ case 'r':
+ uartdrainoutput(p);
+ (*p->phys->rts)(p, n);
+ break;
+ case 'S':
+ case 's':
+ uartdrainoutput(p);
+ if((*p->phys->stop)(p, n) < 0)
+ return -1;
+ break;
+ case 'W':
+ case 'w':
+ if(uarttimer == nil || n < 1)
+ return -1;
+ uarttimer->tns = (vlong)n * 100000LL;
+ break;
+ case 'X':
+ case 'x':
+ if(p->enabled){
+ ilock(&p->tlock);
+ p->xonoff = n;
+ iunlock(&p->tlock);
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+static long
+uartwrite(Chan *c, void *buf, long n, vlong)
+{
+ Uart *p;
+ char *cmd;
+
+ if(c->qid.type & QTDIR)
+ error(Eperm);
+
+ p = uart[NETID(c->qid.path)];
+
+ switch(NETTYPE(c->qid.path)){
+ case Ndataqid:
+ qlock(p);
+ if(waserror()){
+ qunlock(p);
+ nexterror();
+ }
+
+ n = qwrite(p->oq, buf, n);
+
+ qunlock(p);
+ poperror();
+ break;
+ case Nctlqid:
+ cmd = malloc(n+1);
+ memmove(cmd, buf, n);
+ cmd[n] = 0;
+ qlock(p);
+ if(waserror()){
+ qunlock(p);
+ free(cmd);
+ nexterror();
+ }
+
+ /* let output drain */
+ if(uartctl(p, cmd) < 0)
+ error(Ebadarg);
+
+ qunlock(p);
+ poperror();
+ free(cmd);
+ break;
+ }
+
+ return n;
+}
+
+static int
+uartwstat(Chan *c, uchar *dp, int n)
+{
+ Dir d;
+ Dirtab *dt;
+
+ if(!iseve())
+ error(Eperm);
+ if(QTDIR & c->qid.type)
+ error(Eperm);
+ if(NETTYPE(c->qid.path) == Nstatqid)
+ error(Eperm);
+
+ dt = &uartdir[1 + 3 * NETID(c->qid.path)];
+ n = convM2D(dp, n, &d, nil);
+ if(n == 0)
+ error(Eshortstat);
+ if(d.mode != ~0UL)
+ dt[0].perm = dt[1].perm = d.mode;
+ return n;
+}
+
+void
+uartpower(int on)
+{
+ Uart *p;
+
+ for(p = uartlist; p != nil; p = p->next) {
+ if(p->phys->power)
+ (*p->phys->power)(p, on);
+ }
+}
+
+Dev uartdevtab = {
+ 't',
+ "uart",
+
+ uartreset,
+ devinit,
+ devshutdown,
+ uartattach,
+ uartwalk,
+ uartstat,
+ uartopen,
+ devcreate,
+ uartclose,
+ uartread,
+ devbread,
+ uartwrite,
+ devbwrite,
+ devremove,
+ uartwstat,
+ uartpower,
+};
+
+/*
+ * restart input if it's off
+ */
+static void
+uartflow(void *v)
+{
+ Uart *p;
+
+ p = v;
+ if(p->modem)
+ (*p->phys->rts)(p, 1);
+}
+
+/*
+ * put some bytes into the local queue to avoid calling
+ * qconsume for every character
+ */
+int
+uartstageoutput(Uart *p)
+{
+ int n;
+
+ n = qconsume(p->oq, p->ostage, Stagesize);
+ if(n <= 0)
+// n = 0; /* experiment */
+ return 0;
+ p->op = p->ostage;
+ p->oe = p->ostage + n;
+ return n;
+}
+
+/*
+ * restart output
+ */
+void
+uartkick(void *v)
+{
+ Uart *p = v;
+
+ if(p->blocked)
+ return;
+
+ ilock(&p->tlock);
+ (*p->phys->kick)(p);
+ iunlock(&p->tlock);
+
+ if(p->drain && uartdrained(p)){
+ p->drain = 0;
+ wakeup(&p->r);
+ }
+}
+
+/*
+ * Move data from the interrupt staging area to
+ * the input Queue.
+ */
+static void
+uartstageinput(Uart *p)
+{
+ int n;
+ uchar *ir, *iw;
+
+ while(p->ir != p->iw){
+ ir = p->ir;
+ if(p->ir > p->iw){
+ iw = p->ie;
+ p->ir = p->istage;
+ }
+ else{
+ iw = p->iw;
+ p->ir = p->iw;
+ }
+ if((n = qproduce(p->iq, ir, iw - ir)) < 0){
+ p->serr++;
+ (*p->phys->rts)(p, 0);
+ }
+ else if(n == 0)
+ p->berr++;
+ }
+}
+
+/*
+ * receive a character at interrupt time
+ */
+void
+uartrecv(Uart *p, char ch)
+{
+ uchar *next;
+
+ /* software flow control */
+ if(p->xonoff){
+ if(ch == CTLS){
+ p->blocked = 1;
+ }else if(ch == CTLQ){
+ p->blocked = 0;
+ p->ctsbackoff = 2; /* clock gets output going again */
+ }
+ }
+
+ /* receive the character */
+ if(p->putc)
+ p->putc(p->iq, ch);
+ else if (p->iw) { /* maybe the line isn't enabled yet */
+ ilock(&p->rlock);
+ next = p->iw + 1;
+ if(next == p->ie)
+ next = p->istage;
+ if(next == p->ir)
+ uartstageinput(p);
+ if(next != p->ir){
+ *p->iw = ch;
+ p->iw = next;
+ }
+ iunlock(&p->rlock);
+ }
+}
+
+/*
+ * we save up input characters till clock time to reduce
+ * per character interrupt overhead.
+ */
+static void
+uartclock(void)
+{
+ Uart *p;
+
+ ilock(&uartalloc);
+ for(p = uartalloc.elist; p; p = p->elist){
+
+ /* this hopefully amortizes cost of qproduce to many chars */
+ if(p->iw != p->ir){
+ ilock(&p->rlock);
+ uartstageinput(p);
+ iunlock(&p->rlock);
+ }
+
+ /* hang up if requested */
+ if(p->dohup){
+ qhangup(p->iq, 0);
+ qhangup(p->oq, 0);
+ p->dohup = 0;
+ }
+
+ /* this adds hysteresis to hardware/software flow control */
+ if(p->ctsbackoff){
+ ilock(&p->tlock);
+ if(p->ctsbackoff){
+ if(--(p->ctsbackoff) == 0)
+ (*p->phys->kick)(p);
+ }
+ iunlock(&p->tlock);
+ }
+ uartkick(p); /* keep it moving */
+ }
+ iunlock(&uartalloc);
+}
+
+/*
+ * polling console input, output
+ */
+
+Uart* consuart;
+
+int
+uartgetc(void)
+{
+ if(consuart == nil || consuart->phys->getc == nil)
+ return -1;
+ return consuart->phys->getc(consuart);
+}
+
+void
+uartputc(int c)
+{
+ if(consuart == nil || consuart->phys->putc == nil)
+ return;
+ consuart->phys->putc(consuart, c);
+}
+
+void
+uartputs(char *s, int n)
+{
+ char *e;
+
+ if(consuart == nil || consuart->phys->putc == nil)
+ return;
+
+ e = s+n;
+ for(; s<e; s++){
+ if(*s == '\n')
+ consuart->phys->putc(consuart, '\r');
+ consuart->phys->putc(consuart, *s);
+ }
+}
diff --git a/sys/src/9/omap/devusb.c b/sys/src/9/omap/devusb.c
new file mode 100755
index 000000000..efe93a931
--- /dev/null
+++ b/sys/src/9/omap/devusb.c
@@ -0,0 +1,1461 @@
+/*
+ * 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;
+
+ 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(hp->irq, hp->interrupt, hp, UNKNOWN, 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/omap/dma.c b/sys/src/9/omap/dma.c
new file mode 100755
index 000000000..385fcd45d
--- /dev/null
+++ b/sys/src/9/omap/dma.c
@@ -0,0 +1,263 @@
+/*
+ * omap3530 system dma controller
+ *
+ * terminology: a block consist of frame(s), a frame consist of elements
+ * (uchar, ushort, or ulong sized).
+ */
+#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"
+
+enum {
+ Nirq = 4,
+ Baseirq = 12,
+
+ Nchan = 32,
+};
+
+/*
+ * has a sw reset bit
+ * dma req lines 1, 2, 6, 63 are available for `system expansion'
+ */
+
+typedef struct Regs Regs;
+typedef struct Dchan Dchan;
+struct Regs {
+ uchar _pad0[8];
+ /* bitfield of intrs pending, by Dchan; write 1s to clear */
+ ulong irqsts[Nirq];
+ ulong irqen[Nirq]; /* bitfield of intrs enabled, by Dchan */
+ ulong syssts; /* 1<<0 is Resetdone */
+ ulong syscfg; /* 1<<1 is Softreset */
+ uchar _pad1[0x64 - 0x30];
+
+ ulong caps[5]; /* caps[1] not defined */
+ ulong gcr; /* knobs */
+ ulong _pad2;
+
+ struct Dchan {
+ ulong ccr; /* chan ctrl: incr, etc. */
+ ulong clnkctrl; /* link ctrl */
+ ulong cicr; /* intr ctrl */
+ ulong csr; /* status */
+ ulong csdp; /* src & dest params */
+ ulong cen; /* element # */
+ ulong cfn; /* frame # */
+ ulong cssa; /* src start addr */
+ ulong cdsa; /* dest start addr */
+ ulong csei; /* src element index */
+ ulong csfi; /* src frame index | pkt size */
+ ulong cdei; /* dest element index */
+ ulong cdfi; /* dest frame index | pkt size */
+ ulong csac; /* src addr value (read-only?) */
+ ulong cdac; /* dest addr value */
+ ulong ccen; /* curr transferred element # (in frame) */
+ ulong ccfn; /* curr transferred frame # (in xfer) */
+ ulong color;
+ uchar _pad3[24];
+ } chan[Nchan];
+};
+
+enum {
+ /* cicr/csr bits */
+ Blocki = 1 << 5,
+
+ /* ccr bits */
+ Enable = 1 << 7,
+};
+
+typedef struct Xfer Xfer;
+static struct Xfer {
+ Rendez *rend;
+ int *done; /* flag to set on intr */
+} xfer[Nirq];
+
+int
+isdmadone(int irq)
+{
+ Dchan *cp;
+ Regs *regs = (Regs *)PHYSSDMA;
+
+ cp = regs->chan + irq;
+ return cp->csr & Blocki;
+}
+
+static void
+dmaintr(Ureg *, void *a)
+{
+ int i = (int)a; /* dma request & chan # */
+ Dchan *cp;
+ Regs *regs = (Regs *)PHYSSDMA;
+
+ assert(i >= 0 && i < Nirq);
+
+ *xfer[i].done = 1;
+ assert(xfer[i].rend != nil);
+ wakeup(xfer[i].rend);
+
+ cp = regs->chan + i;
+ if(!(cp->csr & Blocki))
+ iprint("dmaintr: req %d: Blocki not set; csr %#lux\n",
+ i, cp->csr);
+ cp->csr |= cp->csr; /* extinguish intr source */
+ coherence();
+ regs->irqsts[i] = regs->irqsts[i]; /* extinguish intr source */
+ coherence();
+ regs->irqen[i] &= ~(1 << i);
+ coherence();
+
+ xfer[i].rend = nil;
+ coherence();
+}
+
+void
+zerowds(ulong *wdp, int cnt)
+{
+ while (cnt-- > 0)
+ *wdp++ = 0;
+}
+
+static int
+istestdmadone(void *arg)
+{
+ return *(int *)arg;
+}
+
+void
+dmainit(void)
+{
+ int n;
+ char name[16];
+ Dchan *cp;
+ Regs *regs = (Regs *)PHYSSDMA;
+
+ if (probeaddr((uintptr)&regs->syssts) < 0)
+ panic("dmainit: no syssts reg");
+ regs->syssts = 0;
+ coherence();
+ regs->syscfg |= 1<<1; /* Softreset */
+ coherence();
+ while(!(regs->syssts & (1<<0))) /* Resetdone? */
+ ;
+
+ for (n = 0; n < Nchan; n++) {
+ cp = regs->chan + n;
+ cp->ccr = 0;
+ cp->clnkctrl = 0;
+ cp->cicr = 0;
+ cp->csr = 0;
+ cp->csdp = 0;
+ cp->cen = cp->cfn = 0;
+ cp->cssa = cp->cdsa = 0;
+ cp->csei = cp->csfi = 0;
+ cp->cdei = cp->cdfi = 0;
+// cp->csac = cp->cdac = 0; // ro
+ cp->ccen = cp->ccfn = 0;
+ cp->color = 0;
+ }
+ zerowds((void *)regs->irqsts, sizeof regs->irqsts / sizeof(ulong));
+ zerowds((void *)regs->irqen, sizeof regs->irqen / sizeof(ulong));
+ coherence();
+
+ regs->gcr = 65; /* burst size + 1 */
+ coherence();
+
+ for (n = 0; n < Nirq; n++) {
+ snprint(name, sizeof name, "dma%d", n);
+ intrenable(Baseirq + n, dmaintr, (void *)n, nil, name);
+ }
+}
+
+enum {
+ Testbyte = 0252,
+ Testsize = 256,
+ Scratch = MB,
+};
+
+/*
+ * try to confirm sane operation
+ */
+void
+dmatest(void)
+{
+ int n, done;
+ uchar *bp;
+ static ulong pat = 0x87654321;
+ static Rendez trendez;
+
+ if (up == nil)
+ panic("dmatest: up not set yet");
+ bp = (uchar *)KADDR(PHYSDRAM + 128*MB);
+ memset(bp, Testbyte, Scratch);
+ done = 0;
+ dmastart((void *)PADDR(bp), Postincr, (void *)PADDR(&pat), Const,
+ Testsize, &trendez, &done);
+ sleep(&trendez, istestdmadone, &done);
+ cachedinvse(bp, Scratch);
+
+ if (((ulong *)bp)[0] != pat)
+ panic("dmainit: copied incorrect data %#lux != %#lux",
+ ((ulong *)bp)[0], pat);
+ for (n = Testsize; n < Scratch && bp[n] != Testbyte; n++)
+ ;
+ if (n >= Scratch)
+ panic("dmainit: ran wild over memory, clobbered ≥%,d bytes", n);
+ if (bp[n] == Testbyte && n != Testsize)
+ iprint("dma: %d-byte dma stopped after %d bytes!\n",
+ Testsize, n);
+}
+
+/* addresses are physical */
+int
+dmastart(void *to, int tmode, void *from, int fmode, uint len, Rendez *rend,
+ int *done)
+{
+ int irq, chan;
+ uint ruplen;
+ Dchan *cp;
+ Regs *regs = (Regs *)PHYSSDMA;
+ static Lock alloclck;
+
+ /* allocate free irq (and chan) */
+ ilock(&alloclck);
+ for (irq = 0; irq < Nirq && xfer[irq].rend != nil; irq++)
+ ;
+ if (irq >= Nirq)
+ panic("dmastart: no available irqs; too many concurrent dmas");
+ chan = irq;
+ xfer[irq].rend = rend; /* for wakeup at intr time */
+ xfer[irq].done = done;
+ *done = 0;
+ iunlock(&alloclck);
+
+ ruplen = ROUNDUP(len, sizeof(ulong));
+ assert(to != from);
+
+ cp = regs->chan + chan;
+ cp->ccr &= ~Enable; /* paranoia */
+ cp->cicr = 0;
+ regs->irqen[irq] &= ~(1 << chan);
+ coherence();
+
+ cp->csdp = 2; /* 2 = log2(sizeof(ulong)) */
+ cp->cssa = (uintptr)from;
+ cp->cdsa = (uintptr)to;
+ cp->ccr = tmode << 14 | fmode << 12;
+ cp->csei = cp->csfi = cp->cdei = cp->cdfi = 1;
+ cp->cen = ruplen / sizeof(ulong); /* ulongs / frame */
+ cp->cfn = 1; /* 1 frame / xfer */
+ cp->cicr = Blocki; /* intr at end of block */
+
+ regs->irqen[irq] |= 1 << chan;
+ coherence();
+
+ cp->ccr |= Enable; /* fire! */
+ coherence();
+
+ return irq;
+}
diff --git a/sys/src/9/omap/ether9221.c b/sys/src/9/omap/ether9221.c
new file mode 100755
index 000000000..9f7a571aa
--- /dev/null
+++ b/sys/src/9/omap/ether9221.c
@@ -0,0 +1,961 @@
+/*
+ * SMSC 9221 Ethernet driver
+ * specifically for the ISEE IGEPv2 board,
+ * where it is assigned to Chip Select 5,
+ * its registers are at 0x2c000000 (inherited from u-boot),
+ * and irq is 34 from gpio pin 176, thus gpio module 6.
+ *
+ * it's slow due to the use of fifos instead of buffer rings.
+ * the slow system dma just makes it worse.
+ *
+ * igepv2 u-boot uses pin 64 on gpio 3 as an output pin to reset the 9221.
+ */
+#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"
+
+/* currently using kprocs is a lot slower than not (87 s. to boot vs 60) */
+#undef USE_KPROCS
+
+enum {
+ Vid9221 = 0x9221,
+ Slop = 4, /* beyond ETHERMAXTU */
+};
+
+typedef struct Regs Regs;
+struct Regs {
+ /* fifo ports */
+ ulong rxdata;
+ uchar _pad0[0x20 - 4];
+ ulong txdata;
+ uchar _pad1[0x40 - 0x24];
+ ulong rxsts;
+ ulong rxstspeek;
+ ulong txsts;
+ ulong txstspeek;
+
+ /* control & status */
+ ushort rev; /* chip revision */
+ ushort id; /* chip id, 0x9221 */
+ ulong irqcfg;
+ ulong intsts;
+ ulong inten;
+ ulong _pad2;
+ ulong bytetest;
+ ulong fifoint; /* fifo level interrupts */
+ ulong rxcfg;
+ ulong txcfg;
+ ulong hwcfg;
+ ulong rxdpctl; /* rx data path control */
+ ulong rxfifoinf;
+ ulong txfifoinf;
+ ulong pmtctl; /* power mgmt. control */
+ ulong gpiocfg;
+ ulong gptcfg; /* timer */
+ ulong gptcnt;
+ ulong _pad3;
+ ulong wordswap;
+ ulong freerun; /* counters */
+ ulong rxdrop;
+
+ /*
+ * mac registers are accessed indirectly via the mac csr registers.
+ * phy registers are doubly indirect, via the mac csr mii_acc &
+ * mii_data mac csr registers.
+ */
+ ulong maccsrcmd; /* mac csr synchronizer */
+ ulong maccsrdata;
+ ulong afccfg; /* automatic flow control cfg. */
+ ulong eepcmd; /* eeprom */
+ ulong eepdata;
+ /* 0xb8 */
+};
+
+enum {
+ Nstatistics = 128,
+};
+
+enum {
+ /* txcmda bits */
+ Intcompl = 1<<31,
+ Bufendalign = 3<<24, /* mask */
+ Datastoff = 037<<16, /* mask */
+ Firstseg = 1<<13,
+ Lastseg = 1<<12,
+ Bufsize = MASK(11),
+
+ /* txcmdb bits */
+ Pkttag = MASK(16) << 16,
+ Txcksumen = 1<<14,
+ Addcrcdis = 1<<13,
+ Framepaddis = 1<<12,
+ Pktlen = (1<<1) - 1, /* mask */
+
+ /* txcfg bits */
+ Txsdump = 1<<15, /* flush tx status fifo */
+ Txddump = 1<<14, /* flush tx data fifo */
+ Txon = 1<<1,
+ Stoptx = 1<<0,
+
+ /* hwcfg bits */
+ Mbo = 1<<20, /* must be one */
+ Srstto = 1<<1, /* soft reset time-out */
+ Srst = 1<<0,
+
+ /* rxcfg bits */
+ Rxdmacntshift = 16, /* ulong count, 12 bits wide */
+ Rxdmacntmask = MASK(12) << Rxdmacntshift,
+ Rxdump = 1<<15, /* flush rx fifos */
+
+ /* rxsts bits */
+ Rxpktlenshift = 16, /* byte count */
+ Rxpktlenmask = MASK(14) << Rxpktlenshift,
+ Rxerr = 1<<15,
+
+ /* rxfifoinf bits */
+ Rxstsusedshift = 16, /* ulong count */
+ Rxstsusedmask = MASK(8) << Rxstsusedshift,
+ Rxdatausedmask = MASK(16), /* byte count */
+
+ /* txfifoinf bits */
+ Txstsusedshift = 16, /* ulong count */
+ Txstsusedmask = MASK(8) << Txstsusedshift,
+ Txdatafreemask = MASK(16), /* byte count */
+
+ /* pmtctl bits */
+ Dready = 1<<0,
+
+ /* maccsrcmd bits */
+ Csrbusy = 1<<31,
+ Csrread = 1<<30, /* not write */
+ Csraddrshift = 0,
+ Csraddrmask = MASK(8) - 1,
+
+ /* mac registers' indices */
+ Maccr = 1,
+ Macaddrh,
+ Macaddrl,
+ Machashh,
+ Machashl,
+ Macmiiacc, /* for doubly-indirect phy access */
+ Macmiidata,
+ Macflow,
+ Macvlan1,
+ Macvlan2,
+ Macwuff,
+ Macwucsr,
+ Maccoe,
+
+ /* Maccr bits */
+ Rxall = 1<<31,
+ Rcvown = 1<<23, /* don't receive own transmissions */
+ Fdpx = 1<<20, /* full duplex */
+ Mcpas = 1<<19, /* pass all multicast */
+ Prms = 1<<18, /* promiscuous */
+ Ho = 1<<15, /* hash-only filtering */
+ Hpfilt = 1<<13, /* hash/perfect filtering */
+ Padstr = 1<<8, /* strip padding & fcs (crc) */
+ Txen = 1<<3,
+ Rxen = 1<<2,
+
+ /* irqcfg bits */
+ Irqdeasclr = 1<<14, /* deassertion intv'l clear */
+ Irqdeassts = 1<<13, /* deassertion intv'l status */
+ Irqint = 1<<12, /* intr being asserted? (ro) */
+ Irqen = 1<<8,
+ Irqpol = 1<<4, /* irq output is active high */
+ Irqpushpull = 1<<0, /* irq output is push/pull driver */
+
+ /* intsts/inten bits */
+ Swint = 1<<31, /* generate an interrupt */
+ Txstop = 1<<25,
+ Rxstop = 1<<24,
+ Txioc = 1<<21,
+ Rxdma = 1<<20,
+ Gptimer = 1<<19,
+ Phy = 1<<18,
+ Rxe = 1<<14, /* errors */
+ Txe = 1<<13,
+ Tdfo = 1<<10, /* tx data fifo overrun */
+ Tdfa = 1<<9, /* tx data fifo available */
+ Tsff = 1<<8, /* tx status fifo full */
+ Tsfl = 1<<7, /* tx status fifo level */
+ Rsff = 1<<4, /* rx status fifo full */
+ Rsfl = 1<<3, /* rx status fifo level */
+
+ /* eepcmd bits */
+ Epcbusy = 1<<31,
+ Epccmdshift = 28, /* interesting one is Reload (7) */
+ Epctimeout = 1<<9,
+ Epcmacloaded = 1<<8,
+ Epcaddrshift = 0,
+};
+
+enum {
+ Rxintrs = Rsff | Rsfl | Rxe,
+ Txintrs = Tsff | Tsfl | Txe | Txioc,
+};
+
+/* wake-up frame filter */
+struct Wakeup {
+ ulong bytemask[4]; /* index is filter # */
+ uchar filt0cmd; /* filter 0 command */
+ uchar _pad0;
+ uchar filt1cmd;
+ uchar _pad1;
+ uchar filt2cmd;
+ uchar _pad2;
+ uchar filt3cmd;
+ uchar _pad3;
+ uchar offset[4]; /* index is filter # */
+ ushort crc16[4]; /* " */
+};
+
+typedef struct Ctlr Ctlr;
+struct Ctlr {
+ int port;
+ Ctlr* next;
+ Ether* edev;
+ Regs* regs;
+ int active;
+ int started;
+ int inited;
+ int id;
+ int cls;
+ ushort eeprom[0x40];
+
+ QLock alock; /* attach */
+ int nrb; /* how many this Ctlr has in the pool */
+
+ int* nic;
+ Lock imlock;
+ int im; /* interrupt mask */
+
+// Mii* mii;
+// Rendez lrendez;
+ int lim;
+
+ int link;
+
+ QLock slock;
+ uint statistics[Nstatistics];
+ uint lsleep;
+ uint lintr;
+ uint rsleep;
+ uint rintr;
+ int tsleep;
+ uint tintr;
+
+ uchar ra[Eaddrlen]; /* receive address */
+ ulong mta[128]; /* multicast table array */
+
+ Rendez rrendez;
+ int gotinput;
+ int rdcpydone;
+
+ Rendez trendez;
+ int gotoutput;
+ int wrcpydone;
+
+ Lock tlock;
+};
+
+#define csr32r(c, r) (*((c)->nic+((r)/4)))
+#define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v))
+
+static Ctlr *smcctlrhead, *smcctlrtail;
+
+static char* statistics[Nstatistics] = { "dummy", };
+
+static uchar mymac[] = { 0xb0, 0x0f, 0xba, 0xbe, 0x00, 0x00, };
+
+static void etherclock(void);
+static void smcreceive(Ether *edev);
+static void smcinterrupt(Ureg*, void* arg);
+
+static Ether *thisether;
+static int attached;
+
+static void
+smconce(Ether *edev)
+{
+ static int beenhere;
+ static Lock l;
+
+ ilock(&l);
+ if (!beenhere && edev != nil) {
+ beenhere = 1;
+ /* simulate interrupts if we don't know the irq */
+ if (edev->irq < 0) { /* poll as backup */
+ thisether = edev;
+ addclock0link(etherclock, 1000/HZ);
+ iprint(" polling");
+ }
+ }
+ iunlock(&l);
+}
+
+/*
+ * indirect (mac) register access
+ */
+
+static void
+macwait(Regs *regs)
+{
+ long bound;
+
+ for (bound = 400*Mhz; regs->maccsrcmd & Csrbusy && bound > 0; bound--)
+ ;
+ if (bound <= 0)
+ iprint("smc: mac registers didn't come ready\n");
+}
+
+static ulong
+macrd(Regs *regs, uchar index)
+{
+ macwait(regs);
+ regs->maccsrcmd = Csrbusy | Csrread | index;
+ coherence(); /* back-to-back write/read delay per §6.2.1 */
+ macwait(regs);
+ return regs->maccsrdata;
+}
+
+static void
+macwr(Regs *regs, uchar index, ulong val)
+{
+ macwait(regs);
+ regs->maccsrdata = val;
+ regs->maccsrcmd = Csrbusy | index; /* fire */
+ macwait(regs);
+}
+
+
+static long
+smcifstat(Ether* edev, void* a, long n, ulong offset)
+{
+ Ctlr *ctlr;
+ char *p, *s;
+ int i, l, r;
+
+ ctlr = edev->ctlr;
+ qlock(&ctlr->slock);
+ p = malloc(READSTR);
+ l = 0;
+ for(i = 0; i < Nstatistics; i++){
+ // read regs->rxdrop TODO
+ r = 0;
+ if((s = statistics[i]) == nil)
+ continue;
+ switch(i){
+ default:
+ ctlr->statistics[i] += r;
+ if(ctlr->statistics[i] == 0)
+ continue;
+ l += snprint(p+l, READSTR-l, "%s: %ud %ud\n",
+ s, ctlr->statistics[i], r);
+ break;
+ }
+ }
+
+ l += snprint(p+l, READSTR-l, "lintr: %ud %ud\n",
+ ctlr->lintr, ctlr->lsleep);
+ l += snprint(p+l, READSTR-l, "rintr: %ud %ud\n",
+ ctlr->rintr, ctlr->rsleep);
+ l += snprint(p+l, READSTR-l, "tintr: %ud %ud\n",
+ ctlr->tintr, ctlr->tsleep);
+
+ l += snprint(p+l, READSTR-l, "eeprom:");
+ for(i = 0; i < 0x40; i++){
+ if(i && ((i & 0x07) == 0))
+ l += snprint(p+l, READSTR-l, "\n ");
+ l += snprint(p+l, READSTR-l, " %4.4uX", ctlr->eeprom[i]);
+ }
+ l += snprint(p+l, READSTR-l, "\n");
+ USED(l);
+
+ n = readstr(offset, a, n, p);
+ free(p);
+ qunlock(&ctlr->slock);
+
+ return n;
+}
+
+static void
+smcpromiscuous(void* arg, int on)
+{
+ int rctl;
+ Ctlr *ctlr;
+ Ether *edev;
+ Regs *regs;
+
+ edev = arg;
+ ctlr = edev->ctlr;
+ regs = ctlr->regs;
+ rctl = macrd(regs, Maccr);
+ if(on)
+ rctl |= Prms;
+ else
+ rctl &= ~Prms;
+ macwr(regs, Maccr, rctl);
+}
+
+static void
+smcmulticast(void*, uchar*, int)
+{
+ /* nothing to do, we allow all multicast packets in */
+}
+
+static int
+iswrcpydone(void *arg)
+{
+ return ((Ctlr *)arg)->wrcpydone;
+}
+
+static int
+smctxstart(Ctlr *ctlr, uchar *ubuf, uint len)
+{
+ uint wds, ruplen;
+ ulong *wdp, *txdp;
+ Regs *regs;
+ static ulong buf[ROUNDUP(ETHERMAXTU, sizeof(ulong)) / sizeof(ulong)];
+
+ if (!ctlr->inited) {
+ iprint("smctxstart: too soon to send\n");
+ return -1; /* toss it */
+ }
+ regs = ctlr->regs;
+
+ /* is there room for a packet in the tx data fifo? */
+ if (len < ETHERMINTU)
+ iprint("sending too-short (%d) pkt\n", len);
+ else if (len > ETHERMAXTU)
+ iprint("sending jumbo (%d) pkt\n", len);
+
+ ruplen = ROUNDUP(len, sizeof(ulong));
+ coherence(); /* back-to-back read/read delay per §6.2.2 */
+ if ((regs->txfifoinf & Txdatafreemask) < ruplen + 2*sizeof(ulong))
+ return -1; /* not enough room for data + command words */
+
+ if ((uintptr)ubuf & MASK(2)) { /* ensure word alignment */
+ memmove(buf, ubuf, len);
+ ubuf = (uchar *)buf;
+ }
+
+ /* tx cmd a: length is bytes in this buffer */
+ txdp = &regs->txdata;
+ *txdp = Intcompl | Firstseg | Lastseg | len;
+ /* tx cmd b: length is bytes in this packet (could be multiple buf.s) */
+ *txdp = len;
+
+ /* shovel pkt into tx fifo, which triggers transmission due to Txon */
+ wdp = (ulong *)ubuf;
+ for (wds = ruplen / sizeof(ulong) + 1; --wds > 0; )
+ *txdp = *wdp++;
+
+ regs->intsts = Txintrs; /* dismiss intr */
+ coherence();
+ regs->inten |= Txintrs;
+ coherence(); /* back-to-back write/read delay per §6.2.1 */
+ return 0;
+}
+
+static void
+smctransmit(Ether* edev)
+{
+ Block *bp;
+ Ctlr *ctlr;
+
+ ctlr = edev->ctlr;
+ if (ctlr == nil)
+ panic("smctransmit: nil ctlr");
+ ilock(&ctlr->tlock);
+ /*
+ * Try to fill the chip's buffers back up, via the tx fifo.
+ */
+ while ((bp = qget(edev->oq)) != nil)
+ if (smctxstart(ctlr, bp->rp, BLEN(bp)) < 0) {
+ qputback(edev->oq, bp); /* retry the block later */
+ iprint("smctransmit: tx data fifo full\n");
+ break;
+ } else
+ freeb(bp);
+ iunlock(&ctlr->tlock);
+}
+
+static void
+smctransmitcall(Ether *edev) /* called from devether.c */
+{
+ Ctlr *ctlr;
+
+ ctlr = edev->ctlr;
+ ctlr->gotoutput = 1;
+#ifdef USE_KPROCS
+ wakeup(&ctlr->trendez);
+#else
+ smctransmit(edev);
+#endif
+}
+
+static int
+smcrim(void* ctlr)
+{
+ return ((Ctlr*)ctlr)->gotinput;
+}
+
+static void
+smcrproc(void* arg)
+{
+ Ctlr *ctlr;
+ Ether *edev;
+
+ edev = arg;
+ ctlr = edev->ctlr;
+ for(;;){
+ ctlr->rsleep++;
+ sleep(&ctlr->rrendez, smcrim, ctlr);
+
+ /* process any newly-arrived packets and pass to etheriq */
+ ctlr->gotinput = 0;
+ smcreceive(edev);
+ }
+}
+
+static int
+smcgotout(void* ctlr)
+{
+ return ((Ctlr*)ctlr)->gotoutput;
+}
+
+static void
+smctproc(void* arg)
+{
+ Ctlr *ctlr;
+ Ether *edev;
+
+ edev = arg;
+ ctlr = edev->ctlr;
+ for(;;){
+ ctlr->tsleep++;
+ sleep(&ctlr->trendez, smcgotout, ctlr);
+
+ /* process any newly-arrived packets and pass to etheriq */
+ ctlr->gotoutput = 0;
+ smctransmit(edev);
+ }
+}
+
+void gpioirqclr(void);
+
+static void
+smcattach(Ether* edev)
+{
+#ifdef USE_KPROCS
+ char name[KNAMELEN];
+#endif
+ Ctlr *ctlr;
+
+ ctlr = edev->ctlr;
+ qlock(&ctlr->alock);
+ if(waserror()){
+ qunlock(&ctlr->alock);
+ nexterror();
+ }
+ if (!ctlr->inited) {
+ ctlr->inited = 1;
+#ifdef USE_KPROCS
+ snprint(name, KNAMELEN, "#l%drproc", edev->ctlrno);
+ kproc(name, smcrproc, edev);
+
+ snprint(name, KNAMELEN, "#l%dtproc", edev->ctlrno);
+ kproc(name, smctproc, edev);
+#endif
+
+iprint("smcattach:");
+#ifdef USE_KPROCS
+iprint(" with kprocs");
+#else
+iprint(" no kprocs");
+#endif
+iprint(", no dma");
+ /* can now accept real or simulated interrupts */
+
+ smconce(edev);
+ attached = 1;
+iprint("\n");
+ }
+ qunlock(&ctlr->alock);
+ poperror();
+}
+
+static int
+isrdcpydone(void *arg)
+{
+ return ((Ctlr *)arg)->rdcpydone;
+}
+
+static void
+smcreceive(Ether *edev)
+{
+ uint wds, len, sts;
+ ulong *wdp, *rxdp;
+ Block *bp;
+ Ctlr *ctlr;
+ Regs *regs;
+
+ ctlr = edev->ctlr;
+ regs = ctlr->regs;
+ coherence(); /* back-to-back read/read delay per §6.2.2 */
+ /*
+ * is there a full packet in the rx data fifo?
+ */
+ while (((regs->rxfifoinf & Rxstsusedmask) >> Rxstsusedshift) != 0) {
+ coherence();
+ sts = regs->rxsts; /* pop rx status */
+ if(sts & Rxerr)
+ iprint("smcreceive: rx error\n");
+ len = (sts & Rxpktlenmask) >> Rxpktlenshift;
+ if (len > ETHERMAXTU + Slop)
+ iprint("smcreceive: oversized rx pkt (%d)\n", len);
+ else if (len < ETHERMINTU)
+ iprint("smcreceive: too-short (%d) pkt\n", len);
+ wds = ROUNDUP(len, sizeof(ulong)) / sizeof(ulong);
+ if (wds > 0) {
+ /* copy aligned words from rx fifo into a Block */
+ bp = iallocb(len + sizeof(ulong) /* - 1 */);
+ if (bp == nil)
+ panic("smcreceive: nil Block*");
+
+ /* bp->rp should be 32-byte aligned, more than we need */
+ assert(((uintptr)bp->rp & (sizeof(ulong) - 1)) == 0);
+ wdp = (ulong *)bp->rp;
+ rxdp = &regs->rxdata;
+ wds = ROUNDUP(len, sizeof(ulong)) / sizeof(ulong) + 1;
+ while (--wds > 0)
+ *wdp++ = *rxdp;
+ bp->wp = bp->rp + len;
+
+ /* and push the Block upstream */
+ if (ctlr->inited)
+ etheriq(edev, bp, 1);
+ else
+ freeb(bp);
+
+ regs->intsts = Rxintrs; /* dismiss intr */
+ coherence();
+ regs->inten |= Rxintrs;
+ }
+ coherence();
+ }
+ regs->inten |= Rxintrs;
+ coherence();
+}
+
+/*
+ * disable the stsclr bits in inten and write them to intsts to ack and dismiss
+ * the interrupt source.
+ */
+void
+ackintr(Regs *regs, ulong stsclr)
+{
+ if (stsclr == 0)
+ return;
+
+ regs->inten &= ~stsclr;
+ coherence();
+
+// regs->intsts = stsclr; /* acknowledge & clear intr(s) */
+// coherence();
+}
+
+static void
+smcinterrupt(Ureg*, void* arg)
+{
+ int junk;
+ unsigned intsts, intr;
+ Ctlr *ctlr;
+ Ether *edev;
+ Regs *regs;
+
+ edev = arg;
+ ctlr = edev->ctlr;
+ ilock(&ctlr->imlock);
+ regs = ctlr->regs;
+
+ gpioirqclr();
+
+ coherence(); /* back-to-back read/read delay per §6.2.2 */
+ intsts = regs->intsts;
+ coherence();
+
+ intsts &= ~MASK(3); /* ignore gpio bits */
+ if (0 && intsts == 0) {
+ coherence();
+ iprint("smc: interrupt without a cause; insts %#ux (vs inten %#lux)\n",
+ intsts, regs->inten);
+ }
+
+ intr = intsts & Rxintrs;
+ if(intr) {
+ /* disable interrupt sources; kproc/smcreceive will reenable */
+ ackintr(regs, intr);
+
+ ctlr->rintr++;
+ ctlr->gotinput = 1;
+#ifdef USE_KPROCS
+ wakeup(&ctlr->rrendez);
+#else
+ smcreceive(edev);
+#endif
+ }
+
+ while(((regs->txfifoinf & Txstsusedmask) >> Txstsusedshift) != 0) {
+ /* probably indicates tx completion, just toss it */
+ junk = regs->txsts; /* pop tx sts */
+ USED(junk);
+ coherence();
+ }
+
+ intr = intsts & Txintrs;
+ if (ctlr->gotoutput || intr) {
+ /* disable interrupt sources; kproc/smctransmit will reenable */
+ ackintr(regs, intr);
+
+ ctlr->tintr++;
+ ctlr->gotoutput = 1;
+#ifdef USE_KPROCS
+ wakeup(&ctlr->trendez);
+#else
+ smctransmit(edev);
+#endif
+ }
+
+ iunlock(&ctlr->imlock);
+}
+
+static void
+etherclock(void)
+{
+ smcinterrupt(nil, thisether);
+}
+
+static int
+smcmii(Ctlr *)
+{
+ return 0;
+}
+
+static int
+smcdetach(Ctlr* ctlr)
+{
+ Regs *regs;
+
+ if (ctlr == nil || ctlr->regs == nil)
+ return -1;
+ regs = ctlr->regs;
+ /* verify that it's real by reading a few registers */
+ switch (regs->id) {
+ case Vid9221:
+ break;
+ default:
+ print("smc: unknown chip id %#ux\n", regs->id);
+ return -1;
+ }
+ regs->inten = 0; /* no interrupts */
+ regs->intsts = ~0; /* clear any pending */
+ regs->gptcfg = 0;
+ coherence();
+ regs->rxcfg = Rxdump;
+ regs->txcfg = Txsdump | Txddump;
+ regs->irqcfg &= ~Irqen;
+ coherence();
+ return 0;
+}
+
+static void
+smcshutdown(Ether* ether)
+{
+ smcdetach(ether->ctlr);
+}
+
+static void
+powerwait(Regs *regs)
+{
+ long bound;
+
+ regs->bytetest = 0; /* bring power on */
+ for (bound = 400*Mhz; !(regs->pmtctl & Dready) && bound > 0; bound--)
+ ;
+ if (bound <= 0)
+ iprint("smc: pmtctl didn't come ready\n");
+}
+
+static int
+smcreset(Ctlr* ctlr)
+{
+ int r;
+ Regs *regs;
+ static char zea[Eaddrlen];
+
+ regs = ctlr->regs;
+ powerwait(regs);
+
+ if(smcdetach(ctlr))
+ return -1;
+
+ /* verify that it's real by reading a few registers */
+ switch (regs->id) {
+ case Vid9221:
+ break;
+ default:
+ print("smc: unknown chip id %#ux\n", regs->id);
+ return -1;
+ }
+ if (regs->bytetest != 0x87654321) {
+ print("smc: bytetest reg %#p (%#lux) != 0x87654321\n",
+ &regs->bytetest, regs->bytetest);
+ return -1;
+ }
+
+#ifdef TODO /* read MAC from EEPROM */
+// int ctrl, i, pause, swdpio, txcw;
+ /*
+ * Snarf and set up the receive addresses.
+ * There are 16 addresses. The first should be the MAC address.
+ * The others are cleared and not marked valid (MS bit of Rah).
+ */
+ for(i = Ea; i < Eaddrlen/2; i++){
+ ctlr->ra[2*i] = ctlr->eeprom[i];
+ ctlr->ra[2*i+1] = ctlr->eeprom[i]>>8;
+ }
+
+ /*
+ * Clear the Multicast Table Array.
+ * It's a 4096 bit vector accessed as 128 32-bit registers.
+ */
+ memset(ctlr->mta, 0, sizeof(ctlr->mta));
+ for(i = 0; i < 128; i++)
+ csr32w(ctlr, Mta+i*4, 0);
+#endif
+ regs->hwcfg |= Mbo;
+
+ /* don't overwrite existing ea */
+// if (memcmp(edev->ea, zea, Eaddrlen) == 0)
+// memmove(edev->ea, ctlr->ra, Eaddrlen);
+
+ r = ctlr->ra[3]<<24 | ctlr->ra[2]<<16 | ctlr->ra[1]<<8 | ctlr->ra[0];
+ macwr(regs, Macaddrl, r);
+ macwr(regs, Macaddrh, ctlr->ra[5]<<8 | ctlr->ra[4]);
+
+ /* turn on the controller */
+ macwr(regs, Maccoe, 0);
+ regs->inten = 0; /* no interrupts yet */
+ regs->intsts = ~0; /* clear any pending */
+ regs->gptcfg = 0;
+ coherence();
+ regs->rxcfg = Rxdump;
+ regs->txcfg = Txsdump | Txddump | Txon;
+ regs->fifoint = 72<<24; /* default values */
+ macwr(regs, Maccr, Rxall | Rcvown | Fdpx | Mcpas | Txen | Rxen);
+ coherence(); /* back-to-back write/read delay per §6.2.1 */
+ regs->irqcfg = 1<<24 | Irqen | Irqpushpull; /* deas for 10µs (linux) */
+ coherence(); /* back-to-back write/read delay per §6.2.1 */
+ regs->inten = Rxintrs | Txintrs;
+ coherence();
+
+ if(smcmii(ctlr) < 0)
+ return -1;
+ return 0;
+}
+
+static void
+smcpci(void)
+{
+ Ctlr *ctlr;
+ static int beenhere;
+
+ if (beenhere)
+ return;
+ beenhere = 1;
+
+ if (probeaddr(PHYSETHER) < 0)
+ return;
+ ctlr = malloc(sizeof(Ctlr));
+ ctlr->id = Vid9221<<16 | 0x0424; /* smsc 9221 */
+ ctlr->port = PHYSETHER;
+ ctlr->nic = (int *)PHYSETHER;
+ ctlr->regs = (Regs *)PHYSETHER;
+
+ if(smcreset(ctlr)){
+ free(ctlr);
+ return;
+ }
+ if(smcctlrhead != nil)
+ smcctlrtail->next = ctlr;
+ else
+ smcctlrhead = ctlr;
+ smcctlrtail = ctlr;
+}
+
+static int
+smcpnp(Ether* edev)
+{
+ Ctlr *ctlr;
+ static char zea[Eaddrlen];
+
+ if(smcctlrhead == nil)
+ smcpci();
+
+ /*
+ * Any adapter matches if no edev->port is supplied,
+ * otherwise the ports must match.
+ */
+ for(ctlr = smcctlrhead; ctlr != nil; ctlr = ctlr->next){
+ if(ctlr->active)
+ continue;
+ if(edev->port == 0 || edev->port == ctlr->port){
+ ctlr->active = 1;
+ break;
+ }
+ }
+ if(ctlr == nil)
+ return -1;
+
+ edev->ctlr = ctlr;
+ ctlr->edev = edev; /* point back to Ether* */
+ edev->port = ctlr->port;
+ edev->irq = 34;
+// TODO: verify speed (100Mb/s) and duplicity (full-duplex)
+ edev->mbps = 100;
+
+ /* don't overwrite existing ea */
+ if (memcmp(edev->ea, zea, Eaddrlen) == 0)
+ memmove(edev->ea, ctlr->ra, Eaddrlen);
+
+ /*
+ * Linkage to the generic ethernet driver.
+ */
+ edev->attach = smcattach;
+ edev->transmit = smctransmitcall;
+ edev->interrupt = smcinterrupt;
+ edev->ifstat = smcifstat;
+/* edev->ctl = smcctl; /* no ctl msgs supported */
+
+ edev->arg = edev;
+ edev->promiscuous = smcpromiscuous;
+ edev->multicast = smcmulticast;
+ edev->shutdown = smcshutdown;
+ return 0;
+}
+
+void
+ether9221link(void)
+{
+ addethercard("9221", smcpnp);
+}
diff --git a/sys/src/9/omap/etherif.h b/sys/src/9/omap/etherif.h
new file mode 100755
index 000000000..dbc1721bf
--- /dev/null
+++ b/sys/src/9/omap/etherif.h
@@ -0,0 +1,41 @@
+enum
+{
+ MaxEther = 4,
+ Ntypes = 8,
+};
+
+typedef struct Ether Ether;
+struct Ether {
+ RWlock;
+ ISAConf; /* hardware info */
+
+ int ctlrno;
+ int minmtu;
+ int maxmtu;
+
+ Netif;
+
+ void (*attach)(Ether*); /* filled in by reset routine */
+ 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;
+ uchar ea[Eaddrlen];
+ void* address;
+ int irq;
+
+ Queue* oq;
+};
+
+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/omap/fns.h b/sys/src/9/omap/fns.h
new file mode 100755
index 000000000..6fe173108
--- /dev/null
+++ b/sys/src/9/omap/fns.h
@@ -0,0 +1,178 @@
+#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*, ...);
+
+#pragma varargck argpos _uartprint 1
+
+extern void archreboot(void);
+extern void archreset(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 cacheinfo(int level, Memcache *cp);
+extern void cacheuwbinv(void);
+extern uintptr cankaddr(uintptr pa);
+extern void chkmissing(void);
+extern void clockshutdown(void);
+extern int clz(ulong);
+extern int cmpswap(long*, long, long);
+extern void coherence(void);
+extern void configscreengpio(void);
+extern u32int controlget(void);
+extern u32int cpctget(void);
+extern u32int cpidget(void);
+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 void dmainit(void);
+extern int dmastart(void *, int, void *, int, uint, Rendez *, int *);
+extern void dmatest(void);
+extern u32int farget(void);
+extern ulong fprd(int fpreg);
+extern void fpwr(int fpreg, ulong val);
+extern u32int fsrget(void);
+extern u32int getscr(void);
+extern u32int getpsr(void);
+extern ulong getwayssets(void);
+extern void intrsoff(void);
+extern int isaconfig(char*, int, ISAConf*);
+extern int isdmadone(int);
+extern int ispow2(uvlong);
+extern void kbdenable(void);
+extern void l2cacheuinv(void);
+extern void l2cacheuwb(void);
+extern void l2cacheuwbinv(void);
+extern void lastresortprint(char *buf, long bp);
+extern int log2(ulong);
+extern void machinit(void);
+extern void mmuidmap(uintptr phys, int mbs);
+extern void mmuinvalidate(void); /* 'mmu' or 'tlb'? */
+extern void mmuinvalidateaddr(u32int); /* 'mmu' or 'tlb'? */
+extern void mousectl(Cmdbuf *cb);
+extern u32int pidget(void);
+extern void pidput(u32int);
+extern vlong probeaddr(uintptr);
+extern void procrestore(Proc *);
+extern void procsave(Proc*);
+extern void procsetup(Proc*);
+extern void _reset(void);
+extern void screenclockson(void);
+extern void screeninit(void);
+extern void serialputs(char* s, int n);
+extern void setcachelvl(int);
+extern void setr13(int, u32int*);
+extern int tas(void *);
+extern u32int ttbget(void);
+extern void ttbput(u32int);
+extern void watchdoginit(void);
+
+extern int irqenable(int, void (*)(Ureg*, void*), void*, char*);
+extern int irqdisable(int, void (*)(Ureg*, void*), void*, char*);
+#define intrenable(i, f, a, b, n) irqenable((i), (f), (a), (n))
+#define intrdisable(i, f, a, b, n) irqdisable((i), (f), (a), (n))
+extern void vectors(void);
+extern void vtable(void);
+
+/* dregs, going away */
+extern int inb(int);
+extern void outb(int, int);
+
+/*
+ * Things called in main.
+ */
+extern void archconfinit(void);
+extern void clockinit(void);
+extern int 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 Block* ucallocb(int);
+extern void* ucallocalign(usize size, int align, int span);
+extern void ucfree(void*);
+extern void ucfreeb(Block*);
+
+/*
+ * 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* sysexecregs(uintptr, ulong, int);
+extern void sysprocsetup(Proc*);
+
+/*
+ * PCI stuff.
+ */
+
+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]))
+
+#define KADDR(pa) UINT2PTR(KZERO | ((uintptr)(pa) & ~KSEGM))
+#define PADDR(va) PTR2UINT(PHYSDRAM | ((uintptr)(va) & ~KSEGM))
+
+#define wave(c) *(ulong *)PHYSCONS = (c)
+
+#define MASK(v) ((1UL << (v)) - 1) /* mask `v' bits wide */
diff --git a/sys/src/9/omap/fpi.c b/sys/src/9/omap/fpi.c
new file mode 100755
index 000000000..f341f2e4a
--- /dev/null
+++ b/sys/src/9/omap/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/omap/fpi.h b/sys/src/9/omap/fpi.h
new file mode 100755
index 000000000..abaa7c120
--- /dev/null
+++ b/sys/src/9/omap/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/omap/fpiarm.c b/sys/src/9/omap/fpiarm.c
new file mode 100755
index 000000000..063d10c52
--- /dev/null
+++ b/sys/src/9/omap/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/omap/fpimem.c b/sys/src/9/omap/fpimem.c
new file mode 100755
index 000000000..627ab6355
--- /dev/null
+++ b/sys/src/9/omap/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/omap/init9.s b/sys/src/9/omap/init9.s
new file mode 100755
index 000000000..1d7f2bec3
--- /dev/null
+++ b/sys/src/9/omap/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/omap/io.h b/sys/src/9/omap/io.h
new file mode 100755
index 000000000..19afa7670
--- /dev/null
+++ b/sys/src/9/omap/io.h
@@ -0,0 +1,82 @@
+/*
+ * the ``general-purpose'' memory controller.
+ * only works with flash memory.
+ */
+
+enum {
+ /* syscfg bits */
+ Idlemask = MASK(2) << 3,
+ Noidle = 1 << 3,
+
+ /* config bits */
+ Postnandwrites = 1<<0, /* force nand reg. writes to be posted */
+
+ /* indices of cscfg[].cfg[] */
+ Csctl = 1 - 1, /* chip-select signal ctl */
+ Csmap = 7 - 1, /* chip-select addr map cfg */
+
+ /* Csctl bits */
+ Muxadddata = 1 << 9,
+ Devtypemask = MASK(2) << 10,
+ Devtypenor = 0 << 10,
+ Devtypenand = 2 << 10,
+ Devsizemask = 1 << 12,
+ Devsize8 = 0 << 12,
+ Devsize16 = 1 << 12,
+ Writesync = 1 << 27,
+ Readsync = 1 << 29,
+
+ /* Csmap bits */
+ Csvalid = 1 << 6,
+ MB16 = 017 << 8, /* 16MB size */
+ MB128 = 010 << 8, /* 128MB size */
+};
+
+typedef struct Gpmc Gpmc;
+typedef struct Gpmccs Gpmccs;
+
+/*
+ * configuration for non-dram (e.g., flash) memory
+ */
+struct Gpmc { /* hw registers */
+ uchar _pad0[0x10];
+ ulong syscfg;
+ ulong syssts;
+ ulong irqsts;
+ ulong irqenable;
+ uchar _pad1[0x40 - 0x20];
+ ulong tmout_ctl;
+ ulong erraddr;
+ ulong errtype;
+ ulong _pad7;
+ ulong config;
+ ulong sts;
+ uchar _pad2[0x60 - 0x58];
+
+ /* chip-select config */
+ struct Gpmccs {
+ ulong cfg[7];
+ ulong nandcmd;
+ ulong nandaddr;
+ ulong nanddata;
+ ulong _pad6[2];
+ } cscfg[8];
+
+ /* prefetch */
+ ulong prefcfg[2];
+ ulong _pad8;
+ ulong prefctl;
+ ulong prefsts;
+
+ /* ecc */
+ ulong ecccfg;
+ ulong eccctl;
+ ulong eccsize;
+ ulong eccres[9];
+ uchar _pad3[0x240 - 0x224];
+
+ /* bch */
+ ulong bchres[8][4];
+ uchar _pad4[0x2d0 - 0x2c0];
+ ulong bchswdata;
+};
diff --git a/sys/src/9/omap/kbd.c b/sys/src/9/omap/kbd.c
new file mode 100755
index 000000000..e81b352d2
--- /dev/null
+++ b/sys/src/9/omap/kbd.c
@@ -0,0 +1,410 @@
+/*
+ * simulated keyboard input for omap35 with no keyboard (except via uart or usb)
+ *
+ * gutted version of ps2 version from ../pc
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+enum {
+ Spec= 0xF800, /* Unicode private space */
+ PF= Spec|0x20, /* num pad function key */
+ View= Spec|0x00, /* view (shift window up) */
+ KF= 0xF000, /* function key (begin Unicode private space) */
+ Shift= Spec|0x60,
+ Break= Spec|0x61,
+ Ctrl= Spec|0x62,
+ Latin= Spec|0x63,
+ Caps= Spec|0x64,
+ Num= Spec|0x65,
+ Middle= Spec|0x66,
+ Altgr= Spec|0x67,
+ Kmouse= Spec|0x100,
+ No= 0x00, /* peter */
+
+ Home= KF|13,
+ Up= KF|14,
+ Pgup= KF|15,
+ Print= KF|16,
+ Left= KF|17,
+ Right= KF|18,
+ End= KF|24,
+ Down= View,
+ Pgdown= KF|19,
+ Ins= KF|20,
+ Del= 0x7F,
+ Scroll= KF|21,
+
+ Nscan= 128,
+
+ Int= 0, /* kbscans indices */
+ Ext,
+ Nscans,
+};
+
+/*
+ * The codes at 0x79 and 0x7b are produced by the PFU Happy Hacking keyboard.
+ * A 'standard' keyboard doesn't produce anything above 0x58.
+ */
+Rune kbtab[Nscan] =
+{
+[0x00] No, 0x1b, '1', '2', '3', '4', '5', '6',
+[0x08] '7', '8', '9', '0', '-', '=', '\b', '\t',
+[0x10] 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
+[0x18] 'o', 'p', '[', ']', '\n', Ctrl, 'a', 's',
+[0x20] 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
+[0x28] '\'', '`', Shift, '\\', 'z', 'x', 'c', 'v',
+[0x30] 'b', 'n', 'm', ',', '.', '/', Shift, '*',
+[0x38] Latin, ' ', Ctrl, KF|1, KF|2, KF|3, KF|4, KF|5,
+[0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, Scroll, '7',
+[0x48] '8', '9', '-', '4', '5', '6', '+', '1',
+[0x50] '2', '3', '0', '.', No, No, No, KF|11,
+[0x58] KF|12, No, No, No, No, No, No, No,
+[0x60] No, No, No, No, No, No, No, No,
+[0x68] No, No, No, No, No, No, No, No,
+[0x70] No, No, No, No, No, No, No, No,
+[0x78] No, View, No, Up, No, No, No, No,
+};
+
+Rune kbtabshift[Nscan] =
+{
+[0x00] No, 0x1b, '!', '@', '#', '$', '%', '^',
+[0x08] '&', '*', '(', ')', '_', '+', '\b', '\t',
+[0x10] 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I',
+[0x18] 'O', 'P', '{', '}', '\n', Ctrl, 'A', 'S',
+[0x20] 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':',
+[0x28] '"', '~', Shift, '|', 'Z', 'X', 'C', 'V',
+[0x30] 'B', 'N', 'M', '<', '>', '?', Shift, '*',
+[0x38] Latin, ' ', Ctrl, KF|1, KF|2, KF|3, KF|4, KF|5,
+[0x40] KF|6, KF|7, KF|8, KF|9, KF|10, Num, Scroll, '7',
+[0x48] '8', '9', '-', '4', '5', '6', '+', '1',
+[0x50] '2', '3', '0', '.', No, No, No, KF|11,
+[0x58] KF|12, No, No, No, No, No, No, No,
+[0x60] No, No, No, No, No, No, No, No,
+[0x68] No, No, No, No, No, No, No, No,
+[0x70] No, No, No, No, No, No, No, No,
+[0x78] No, Up, No, Up, No, No, No, No,
+};
+
+Rune kbtabesc1[Nscan] =
+{
+[0x00] No, No, No, No, No, No, No, No,
+[0x08] No, No, No, No, No, No, No, No,
+[0x10] No, No, No, No, No, No, No, No,
+[0x18] No, No, No, No, '\n', Ctrl, No, No,
+[0x20] No, No, No, No, No, No, No, No,
+[0x28] No, No, Shift, No, No, No, No, No,
+[0x30] No, No, No, No, No, '/', No, Print,
+[0x38] Altgr, No, No, No, No, No, No, No,
+[0x40] No, No, No, No, No, No, Break, Home,
+[0x48] Up, Pgup, No, Left, No, Right, No, End,
+[0x50] Down, Pgdown, Ins, Del, No, No, No, No,
+[0x58] No, No, No, No, No, No, No, No,
+[0x60] No, No, No, No, No, No, No, No,
+[0x68] No, No, No, No, No, No, No, No,
+[0x70] No, No, No, No, No, No, No, No,
+[0x78] No, Up, No, No, No, No, No, No,
+};
+
+Rune kbtabaltgr[Nscan] =
+{
+[0x00] No, No, No, No, No, No, No, No,
+[0x08] No, No, No, No, No, No, No, No,
+[0x10] No, No, No, No, No, No, No, No,
+[0x18] No, No, No, No, '\n', Ctrl, No, No,
+[0x20] No, No, No, No, No, No, No, No,
+[0x28] No, No, Shift, No, No, No, No, No,
+[0x30] No, No, No, No, No, '/', No, Print,
+[0x38] Altgr, No, No, No, No, No, No, No,
+[0x40] No, No, No, No, No, No, Break, Home,
+[0x48] Up, Pgup, No, Left, No, Right, No, End,
+[0x50] Down, Pgdown, Ins, Del, No, No, No, No,
+[0x58] No, No, No, No, No, No, No, No,
+[0x60] No, No, No, No, No, No, No, No,
+[0x68] No, No, No, No, No, No, No, No,
+[0x70] No, No, No, No, No, No, No, No,
+[0x78] No, Up, No, No, No, No, No, No,
+};
+
+Rune kbtabctrl[Nscan] =
+{
+[0x00] No, '', '', '', '', '', '', '',
+[0x08] '', '', '', '', ' ', '', '\b', '\t',
+[0x10] '', '', '', '', '', '', '', '\t',
+[0x18] '', '', '', '', '\n', Ctrl, '', '',
+[0x20] '', '', '', '\b', '\n', ' ', ' ', '',
+[0x28] '', No, Shift, '', '', '', '', '',
+[0x30] '', '', ' ', ' ', '', '', Shift, '\n',
+[0x38] Latin, No, Ctrl, '', '', '', '', '',
+[0x40] '', '', ' ', ' ', '', '', '', '',
+[0x48] '', '', ' ', '', '', '', ' ', '',
+[0x50] '', '', '', '', No, No, No, '',
+[0x58] ' ', No, No, No, No, No, No, No,
+[0x60] No, No, No, No, No, No, No, No,
+[0x68] No, No, No, No, No, No, No, No,
+[0x70] No, No, No, No, No, No, No, No,
+[0x78] No, '', No, '\b', No, No, No, No,
+};
+
+int mouseshifted;
+void (*kbdmouse)(int);
+
+static int kdebug;
+
+typedef struct Kbscan Kbscan;
+struct Kbscan {
+ int esc1;
+ int esc2;
+ int alt;
+ int altgr;
+ int caps;
+ int ctl;
+ int num;
+ int shift;
+ int collecting;
+ int nk;
+ Rune kc[5];
+ int buttons;
+};
+
+Kbscan kbscans[Nscans]; /* kernel and external scan code state */
+
+/*
+ * Scan code processing
+ */
+void
+kbdputsc(int c, int external)
+{
+ int i, keyup;
+ Kbscan *kbscan;
+
+ if(external)
+ kbscan = &kbscans[Ext];
+ else
+ kbscan = &kbscans[Int];
+
+ if(kdebug)
+ print("sc %x ms %d\n", c, mouseshifted);
+ /*
+ * e0's is the first of a 2 character sequence, e1 the first
+ * of a 3 character sequence (on the safari)
+ */
+ if(c == 0xe0){
+ kbscan->esc1 = 1;
+ return;
+ } else if(c == 0xe1){
+ kbscan->esc2 = 2;
+ return;
+ }
+
+ keyup = c & 0x80;
+ c &= 0x7f;
+ if(c > sizeof kbtab){
+ c |= keyup;
+ if(c != 0xFF) /* these come fairly often: CAPSLOCK U Y */
+ print("unknown key %ux\n", c);
+ return;
+ }
+
+ if(kbscan->esc1){
+ c = kbtabesc1[c];
+ kbscan->esc1 = 0;
+ } else if(kbscan->esc2){
+ kbscan->esc2--;
+ return;
+ } else if(kbscan->shift)
+ c = kbtabshift[c];
+ else if(kbscan->altgr)
+ c = kbtabaltgr[c];
+ else if(kbscan->ctl)
+ c = kbtabctrl[c];
+ else
+ c = kbtab[c];
+
+ if(kbscan->caps && c<='z' && c>='a')
+ c += 'A' - 'a';
+
+ /*
+ * keyup only important for shifts
+ */
+ if(keyup){
+ switch(c){
+ case Latin:
+ kbscan->alt = 0;
+ break;
+ case Shift:
+ kbscan->shift = 0;
+ mouseshifted = 0;
+ if(kdebug)
+ print("shiftclr\n");
+ break;
+ case Ctrl:
+ kbscan->ctl = 0;
+ break;
+ case Altgr:
+ kbscan->altgr = 0;
+ break;
+ case Kmouse|1:
+ case Kmouse|2:
+ case Kmouse|3:
+ case Kmouse|4:
+ case Kmouse|5:
+ kbscan->buttons &= ~(1<<(c-Kmouse-1));
+ if(kbdmouse)
+ kbdmouse(kbscan->buttons);
+ break;
+ }
+ return;
+ }
+
+ /*
+ * normal character
+ */
+ if(!(c & (Spec|KF))){
+ if(kbscan->ctl)
+ if(kbscan->alt && c == Del)
+ exit(0);
+ if(!kbscan->collecting){
+ kbdputc(kbdq, c);
+ return;
+ }
+ kbscan->kc[kbscan->nk++] = c;
+ c = latin1(kbscan->kc, kbscan->nk);
+ if(c < -1) /* need more keystrokes */
+ return;
+ if(c != -1) /* valid sequence */
+ kbdputc(kbdq, c);
+ else /* dump characters */
+ for(i=0; i<kbscan->nk; i++)
+ kbdputc(kbdq, kbscan->kc[i]);
+ kbscan->nk = 0;
+ kbscan->collecting = 0;
+ return;
+ } else {
+ switch(c){
+ case Caps:
+ kbscan->caps ^= 1;
+ return;
+ case Num:
+ kbscan->num ^= 1;
+ return;
+ case Shift:
+ kbscan->shift = 1;
+ if(kdebug)
+ print("shift\n");
+ mouseshifted = 1;
+ return;
+ case Latin:
+ kbscan->alt = 1;
+ /*
+ * VMware and Qemu use Ctl-Alt as the key combination
+ * to make the VM give up keyboard and mouse focus.
+ * This has the unfortunate side effect that when you
+ * come back into focus, Plan 9 thinks you want to type
+ * a compose sequence (you just typed alt).
+ *
+ * As a clumsy hack around this, we look for ctl-alt
+ * and don't treat it as the start of a compose sequence.
+ */
+ if(!kbscan->ctl){
+ kbscan->collecting = 1;
+ kbscan->nk = 0;
+ }
+ return;
+ case Ctrl:
+ kbscan->ctl = 1;
+ return;
+ case Altgr:
+ kbscan->altgr = 1;
+ return;
+ case Kmouse|1:
+ case Kmouse|2:
+ case Kmouse|3:
+ case Kmouse|4:
+ case Kmouse|5:
+ kbscan->buttons |= 1<<(c-Kmouse-1);
+ if(kbdmouse)
+ kbdmouse(kbscan->buttons);
+ return;
+ case KF|11:
+ print("kbd debug on, F12 turns it off\n");
+ kdebug = 1;
+ break;
+ case KF|12:
+ kdebug = 0;
+ break;
+ }
+ }
+ kbdputc(kbdq, c);
+}
+
+void
+kbdenable(void)
+{
+#ifdef notdef
+ kbdq = qopen(4*1024, 0, 0, 0);
+ if(kbdq == nil)
+ panic("kbdinit");
+ qnoblock(kbdq, 1);
+#endif
+ kbscans[Int].num = 0;
+}
+
+void
+kbdputmap(ushort m, ushort scanc, Rune r)
+{
+ if(scanc >= Nscan)
+ error(Ebadarg);
+ switch(m) {
+ default:
+ error(Ebadarg);
+ case 0:
+ kbtab[scanc] = r;
+ break;
+ case 1:
+ kbtabshift[scanc] = r;
+ break;
+ case 2:
+ kbtabesc1[scanc] = r;
+ break;
+ case 3:
+ kbtabaltgr[scanc] = r;
+ break;
+ case 4:
+ kbtabctrl[scanc] = r;
+ break;
+ }
+}
+
+int
+kbdgetmap(uint offset, int *t, int *sc, Rune *r)
+{
+ if ((int)offset < 0)
+ error(Ebadarg);
+ *t = offset/Nscan;
+ *sc = offset%Nscan;
+ switch(*t) {
+ default:
+ return 0;
+ case 0:
+ *r = kbtab[*sc];
+ return 1;
+ case 1:
+ *r = kbtabshift[*sc];
+ return 1;
+ case 2:
+ *r = kbtabesc1[*sc];
+ return 1;
+ case 3:
+ *r = kbtabaltgr[*sc];
+ return 1;
+ case 4:
+ *r = kbtabctrl[*sc];
+ return 1;
+ }
+}
diff --git a/sys/src/9/omap/l.s b/sys/src/9/omap/l.s
new file mode 100755
index 000000000..9950cd2ad
--- /dev/null
+++ b/sys/src/9/omap/l.s
@@ -0,0 +1,569 @@
+/*
+ * ti omap3530 SoC machine assist
+ * arm cortex-a8 processor
+ *
+ * loader uses R11 as scratch.
+ * R9 and R10 are used for `extern register' variables.
+ *
+ * ARM v7 arch. ref. man. §B1.3.3 that we don't need barriers
+ * around moves to CPSR.
+ */
+
+#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 or another Plan 9 kernel 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 */
+ SUB $KZERO, R12
+ ADD $PHYSDRAM, R12
+
+ /* SVC mode, interrupts disabled */
+ MOVW $(PsrDirq|PsrDfiq|PsrMsvc), R1
+ MOVW R1, CPSR
+ BARRIERS
+
+ DELAY(printloopret, 1)
+WAVE('\r')
+ DELAY(printloopnl, 1)
+WAVE('\n')
+ /*
+ * work around errata
+ */
+ MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl
+ ORR $(CpACissue1|CpACldstissue1), R1 /* fight omap35x errata 3.1.1.9 */
+ ORR $CpACibe, R1 /* enable cp15 invalidate */
+ ORR $CpACl1pe, R1 /* enable l1 parity checking */
+ ORR $CpCalign, R1 /* catch alignment errors */
+ BIC $CpACasa, R1 /* no speculative accesses */
+ /* go faster with fewer restrictions */
+ BIC $(CpACcachenopipe|CpACcp15serial|CpACcp15waitidle|CpACcp15pipeflush), R1
+ MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl
+ ISB
+
+ MRC CpSC, 1, R1, C(CpCLD), C(CpCLDl2), CpCLDl2aux
+ ORR $CpCl2nowralloc, R1 /* fight cortex errata 460075 */
+ ORR $(CpCl2ecc|CpCl2eccparity), R1
+#ifdef TEDIUM
+ /*
+ * I don't know why this clobbers the system, but I'm tired
+ * of arguing with this fussy processor. To hell with it.
+ */
+ MCR CpSC, 1, R1, C(CpCLD), C(CpCLDl2), CpCLDl2aux
+ ISB
+#endif
+ DELAY(printloops, 1)
+WAVE('P')
+ /*
+ * disable the MMU & caches
+ */
+ MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl
+ BIC $(CpCdcache|CpCicache|CpCmmu), R1
+ ORR $CpCsbo, R1
+ BIC $CpCsbz, R1
+ MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl
+ ISB
+
+ MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl
+ BIC $CpACl2en, R1 /* turn l2 cache off */
+ MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl
+ ISB
+
+WAVE('l')
+ DELAY(printloop3, 1)
+
+WAVE('a')
+ /* clear Mach */
+ MOVW $PADDR(MACHADDR), R4 /* address of Mach */
+ MOVW $0, R0
+_machZ:
+ MOVW R0, (R4)
+ ADD $4, R4
+ CMP.S $PADDR(L1+L1X(0)), R4 /* end at top-level page table */
+ BNE _machZ
+
+ /*
+ * set up the MMU page table
+ */
+
+WAVE('n')
+ /* clear all PTEs first, to provide a default */
+// MOVW $PADDR(L1+L1X(0)), R4 /* address of PTE for 0 */
+_ptenv0:
+ ZEROPTE()
+ CMP.S $PADDR(L1+16*KiB), R4
+ BNE _ptenv0
+
+ DELAY(printloop4, 2)
+WAVE(' ')
+ /*
+ * set up double map of PHYSDRAM, KZERO to PHYSDRAM for first few MBs,
+ * but only if KZERO and PHYSDRAM differ.
+ */
+ MOVW $PTEDRAM, R2 /* PTE bits */
+ MOVW $PHYSDRAM, R3 /* pa */
+ CMP $KZERO, R3
+ BEQ no2map
+ MOVW $PADDR(L1+L1X(PHYSDRAM)), R4 /* address of PTE for PHYSDRAM */
+ MOVW $DOUBLEMAPMBS, R5
+_ptdbl:
+ FILLPTE()
+ SUB.S $1, R5
+ BNE _ptdbl
+no2map:
+
+ /*
+ * back up and fill in PTEs for memory at KZERO.
+ * beagle has 1 bank of 256MB of SDRAM at PHYSDRAM;
+ * igepv2 has 1 bank of 512MB at PHYSDRAM.
+ * Map the maximum (512MB).
+ */
+WAVE('9')
+ 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 (MBs) */
+_ptekrw: /* set PTEs */
+ FILLPTE()
+ SUB.S $1, R5 /* decrement inner loop count */
+ BNE _ptekrw
+
+ /*
+ * back up and fill in PTEs for MMIO
+ * stop somewhere after uarts
+ */
+WAVE(' ')
+ MOVW $PTEIO, R2 /* PTE bits */
+ MOVW $PHYSIO, R3
+ MOVW $PADDR(L1+L1X(VIRTIO)), R4 /* start with PTE for VIRTIO */
+_ptenv2:
+ FILLPTE()
+ CMP.S $PADDR(L1+L1X(PHYSIOEND)), R4
+ BNE _ptenv2
+
+ /* mmu.c sets up the trap vectors later */
+
+ /*
+ * set up a temporary stack; avoid data & bss segments
+ */
+ MOVW $(PHYSDRAM | (128*1024*1024)), R13
+
+ /* invalidate caches */
+ BL cachedinv(SB)
+ MOVW $KZERO, R0
+ MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvi), CpCACHEall
+ ISB
+ MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEwb), CpCACHEwait
+ BARRIERS
+
+WAVE('f')
+ /*
+ * turn caches on
+ */
+ MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl
+ ORR $CpACl2en, R1 /* turn l2 cache on */
+ MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl
+ BARRIERS
+
+ MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl
+ ORR $(CpCdcache|CpCicache), R1
+ MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpMainctl
+ BARRIERS
+
+WAVE('r')
+ /* set the domain access control */
+ MOVW $Client, R0
+ BL dacput(SB)
+
+ DELAY(printloop5, 2)
+WAVE('o')
+ /* set the translation table base */
+ MOVW $PADDR(L1), R0
+ BL ttbput(SB)
+
+ MOVW $0, R0
+ BL pidput(SB) /* paranoia */
+
+WAVE('m')
+ /*
+ * the little dance to turn the MMU on
+ */
+ BL cacheuwbinv(SB)
+ BL mmuinvalidate(SB)
+ BL mmuenable(SB)
+
+WAVE(' ')
+ /* 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('B')
+ MOVW $PHYSDRAM, R3 /* pa */
+ CMP $KZERO, R3
+ BEQ no2unmap
+ /* undo double map of PHYSDRAM, KZERO & first few MBs */
+ MOVW $(L1+L1X(PHYSDRAM)), R4 /* addr. of PTE for PHYSDRAM */
+ MOVW $0, R0
+ MOVW $DOUBLEMAPMBS, R5
+_ptudbl:
+ ZEROPTE()
+ SUB.S $1, R5
+ BNE _ptudbl
+no2unmap:
+ BARRIERS
+ MOVW $KZERO, R0
+ MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv
+ BARRIERS
+
+#ifdef HIGH_SECURITY /* i.e., not GP omap */
+ /* hack: set `secure monitor' vector base addr for cortex */
+// MOVW $HVECTORS, R0
+ MOVW $PADDR(L1), R0
+ SUB $(MACHSIZE+(2*1024)), R0
+ MCR CpSC, 0, R0, C(CpVECS), C(CpVECSbase), CpVECSmon
+ ISB
+#endif
+
+ /*
+ * call main in C
+ * pass Mach to main and set up the stack in it
+ */
+ MOVW $(MACHADDR), R0 /* Mach */
+ MOVW R0, R13
+ ADD $(MACHSIZE), R13 /* stack pointer */
+ SUB $4, R13 /* space for link register */
+ MOVW R0, R10 /* m = MACHADDR */
+WAVE('e')
+ BL main(SB) /* void main(Mach*) */
+ /*FALLTHROUGH*/
+
+/*
+ * reset the system
+ */
+
+TEXT _reset(SB), 1, $-4
+ MOVW $(PsrDirq|PsrDfiq|PsrMsvc), R0
+ MOVW R0, CPSR
+ BARRIERS
+
+ DELAY(printloopr, 2)
+WAVE('!')
+WAVE('r')
+WAVE('e')
+WAVE('s')
+WAVE('e')
+WAVE('t')
+WAVE('!')
+WAVE('\r')
+WAVE('\n')
+
+ /* turn the caches off */
+ BL cacheuwbinv(SB)
+
+ MRC CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl
+ BIC $(CpCicache|CpCdcache|CpCalign), R0
+ ORR $CpCsw, R0 /* enable SWP */
+ MCR CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl
+ BARRIERS
+
+ /* redo double map of PHYSDRAM, KZERO & first few MBs */
+ MOVW $PTEDRAM, R2 /* PTE bits */
+ MOVW $PHYSDRAM, R3 /* pa */
+ MOVW $(L1+L1X(PHYSDRAM)), R4 /* address of PHYSDRAM's PTE */
+ MOVW $DOUBLEMAPMBS, R5
+_ptrdbl:
+ FILLPTE()
+ SUB.S $1, R5
+ BNE _ptrdbl
+
+ MOVW $PHYSDRAM, R0
+ MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv
+ BARRIERS
+
+ /* turn the MMU off */
+ MOVW $PHYSDRAM, R0
+ BL _r15warp(SB)
+ BL mmuinvalidate(SB)
+ BL mmudisable(SB)
+
+ /* set new reset vector */
+ MOVW $HVECTORS, R2
+ MOVW $0xe59ff018, R3 /* MOVW 0x18(R15), R15 */
+ MOVW R3, (R2)
+ BARRIERS
+
+// MOVW $PHYSFLASH, R3 /* TODO */
+// MOVW R3, 0x20(R2) /* where $0xe59ff018 jumps to */
+
+ /* ...and jump to it */
+// MOVW R2, R15 /* software reboot */
+_limbo: /* should not get here... */
+ BL idlehands(SB)
+ B _limbo /* ... and can't get out */
+ BL _div(SB) /* hack to load _div, etc. */
+
+TEXT _r15warp(SB), 1, $-4
+ BIC $KSEGM, R14 /* link reg, will become PC */
+ ORR R0, R14
+ BIC $KSEGM, R13 /* SP too */
+ ORR R0, R13
+ RET
+
+/*
+ * `single-element' cache operations.
+ * in arm arch v7, they operate on all cache levels, so separate
+ * l2 functions are unnecessary.
+ */
+
+TEXT cachedwbse(SB), $-4 /* D writeback SE */
+ MOVW R0, R2
+
+ MOVW CPSR, R3
+ CPSID /* splhi */
+
+ BARRIERS /* force outstanding stores to cache */
+ MOVW R2, R0
+ MOVW 4(FP), R1
+ ADD R0, R1 /* R1 is end address */
+ BIC $(CACHELINESZ-1), R0 /* cache line start */
+_dwbse:
+ MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEwb), CpCACHEse
+ /* can't have a BARRIER here since it zeroes R0 */
+ ADD $CACHELINESZ, R0
+ CMP.S R0, R1
+ BGT _dwbse
+ B _wait
+
+TEXT cachedwbinvse(SB), $-4 /* D writeback+invalidate SE */
+ MOVW R0, R2
+
+ MOVW CPSR, R3
+ CPSID /* splhi */
+
+ BARRIERS /* force outstanding stores to cache */
+ MOVW R2, R0
+ MOVW 4(FP), R1
+ ADD R0, R1 /* R1 is end address */
+ BIC $(CACHELINESZ-1), R0 /* cache line start */
+_dwbinvse:
+ MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEwbi), CpCACHEse
+ /* can't have a BARRIER here since it zeroes R0 */
+ ADD $CACHELINESZ, R0
+ CMP.S R0, R1
+ BGT _dwbinvse
+_wait: /* drain write buffer */
+ BARRIERS
+ /* drain L1 write buffer, also drains L2 eviction buffer on sheeva */
+ MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEwb), CpCACHEwait
+ ISB
+
+ MOVW R3, CPSR /* splx */
+ RET
+
+TEXT cachedinvse(SB), $-4 /* D invalidate SE */
+ MOVW R0, R2
+
+ MOVW CPSR, R3
+ CPSID /* splhi */
+
+ BARRIERS /* force outstanding stores to cache */
+ MOVW R2, R0
+ MOVW 4(FP), R1
+ ADD R0, R1 /* R1 is end address */
+ BIC $(CACHELINESZ-1), R0 /* cache line start */
+_dinvse:
+ MCR CpSC, 0, R0, C(CpCACHE), C(CpCACHEinvd), CpCACHEse
+ /* can't have a BARRIER here since it zeroes R0 */
+ ADD $CACHELINESZ, R0
+ CMP.S R0, R1
+ BGT _dinvse
+ B _wait
+
+/*
+ * enable mmu and high vectors
+ */
+TEXT mmuenable(SB), 1, $-4
+ MRC CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl
+ ORR $(CpChv|CpCmmu), R0
+ MCR CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl
+ BARRIERS
+ RET
+
+TEXT mmudisable(SB), 1, $-4
+ MRC CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl
+ BIC $(CpChv|CpCmmu), R0
+ MCR CpSC, 0, R0, C(CpCONTROL), C(0), CpMainctl
+ BARRIERS
+ RET
+
+/*
+ * If one of these MCR instructions crashes or hangs the machine,
+ * check your Level 1 page table (at TTB) closely.
+ */
+TEXT mmuinvalidate(SB), $-4 /* invalidate all */
+ MOVW CPSR, R2
+ CPSID /* interrupts off */
+
+ BARRIERS
+ MOVW PC, R0 /* some valid virtual address */
+ MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv
+ BARRIERS
+ MOVW R2, CPSR /* interrupts restored */
+ RET
+
+TEXT mmuinvalidateaddr(SB), $-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), CpMainctl
+ RET
+
+TEXT ttbget(SB), 1, $-4 /* translation table base */
+ MRC CpSC, 0, R0, C(CpTTB), C(0), CpTTB0
+ RET
+
+TEXT ttbput(SB), 1, $-4 /* translation table base */
+ MCR CpSC, 0, R0, C(CpTTB), C(0), CpTTB0
+ MCR CpSC, 0, R0, C(CpTTB), C(0), CpTTB1 /* cortex has two */
+ 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 getpsr(SB), 1, $-4
+ MOVW CPSR, R0
+ RET
+
+TEXT getscr(SB), 1, $-4
+ MRC CpSC, 0, R0, C(CpCONTROL), C(CpCONTROLscr), CpSCRscr
+ 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 CPSR, R0
+ CPSID /* turn off interrupts */
+
+ MOVW $(MACHADDR+4), R2 /* save caller pc in Mach */
+ MOVW R14, 0(R2)
+ RET
+
+TEXT spllo(SB), 1, $-4 /* start marker for devkprof.c */
+ MOVW CPSR, R0
+ CPSIE
+ RET
+
+TEXT splx(SB), 1, $-4
+ MOVW $(MACHADDR+0x04), R2 /* save caller pc in Mach */
+ MOVW R14, 0(R2)
+
+ MOVW CPSR, R3
+ MOVW R0, CPSR /* reset interrupt level */
+ MOVW R3, R0 /* must return old CPSR */
+ RET
+
+TEXT spldone(SB), 1, $0 /* end marker for devkprof.c */
+ RET
+
+TEXT islo(SB), 1, $-4
+ MOVW CPSR, R0
+ AND $(PsrDirq), R0
+ EOR $(PsrDirq), R0
+ RET
+
+TEXT tas(SB), $-4
+TEXT _tas(SB), $-4
+ MOVW R0,R1
+ MOVW $1,R0
+ SWPW R0,(R1) /* fix: deprecated in armv7 */
+ RET
+
+TEXT clz(SB), $-4
+ CLZ(0, 0) /* 0 is R0 */
+ RET
+
+TEXT setlabel(SB), 1, $-4
+ MOVW R13, 0(R0) /* sp */
+ MOVW R14, 4(R0) /* pc */
+ MOVW $0, R0
+ RET
+
+TEXT gotolabel(SB), 1, $-4
+ MOVW 0(R0), R13 /* sp */
+ MOVW 4(R0), R14 /* pc */
+ MOVW $1, R0
+ RET
+
+TEXT getcallerpc(SB), 1, $-4
+ MOVW 0(R13), R0
+ RET
+
+TEXT idlehands(SB), $-4
+ BARRIERS
+ WFI
+ RET
+
+TEXT coherence(SB), $-4
+ BARRIERS
+ RET
+
+#include "cache.v7.s"
diff --git a/sys/src/9/omap/lexception.s b/sys/src/9/omap/lexception.s
new file mode 100755
index 000000000..e3f330653
--- /dev/null
+++ b/sys/src/9/omap/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/omap/lproc.s b/sys/src/9/omap/lproc.s
new file mode 100755
index 000000000..0b4eac238
--- /dev/null
+++ b/sys/src/9/omap/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/omap/main.c b/sys/src/9/omap/main.c
new file mode 100755
index 000000000..6f04f765a
--- /dev/null
+++ b/sys/src/9/omap/main.c
@@ -0,0 +1,698 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "init.h"
+#include <pool.h>
+
+#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
+
+enum {
+ Minmem = 256*MB, /* conservative default */
+};
+
+#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;
+int normalprint;
+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;
+
+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);
+ memset(BOOTARGS + n, '\n', BOOTARGSLEN - 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;
+
+ strcpy(oenv, "");
+ 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;
+}
+
+void
+main(void)
+{
+// int i;
+ extern char bdata[], edata[], end[], etext[];
+ static ulong vfy = 0xcafebabe;
+
+ /* l.s has already printed "Plan 9 from Be" */
+// m = mach; /* now done in l.s */
+
+ /* realign data seg; apparently -H0 -R4096 does not pad the text seg */
+ if (vfy != 0xcafebabe) {
+// wave('<'); wave('-');
+ memmove(bdata, etext, edata - bdata);
+ }
+ /*
+ * once data segment is in place, always zero bss since we may
+ * have been loaded by another Plan 9 kernel.
+ */
+ memset(edata, 0, end - edata); /* zero BSS */
+ cacheuwbinv();
+ l2cacheuwbinv();
+
+ if (vfy != 0xcafebabe)
+ panic("data segment misaligned");
+ vfy = 0;
+
+wave('l');
+ machinit();
+ mmuinit();
+
+ optionsinit("/boot/boot boot");
+ quotefmtinstall();
+
+ /* want plan9.ini to be able to affect memory sizing in confinit */
+ plan9iniinit(); /* before we step on plan9.ini in low memory */
+
+ trapinit(); /* so confinit can probe memory to size it */
+ confinit(); /* figures out amount of memory */
+ /* xinit prints (if it can), so finish up the banner here. */
+ delay(500);
+ iprint("l Labs\n\n");
+ delay(500);
+ xinit();
+
+ mainmem->flags |= POOL_ANTAGONISM /* | POOL_PARANOIA */ ;
+
+ /*
+ * Printinit will cause the first malloc call.
+ * (printinit->qopen->malloc) unless any of the
+ * above (like clockinit) 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.
+ */
+
+ archreset(); /* configure clock signals */
+ clockinit(); /* start clocks */
+ timersinit();
+ watchdoginit();
+
+ delay(250); /* let uart catch up */
+ printinit();
+ kbdenable();
+
+ cpuidprint();
+// chkmissing();
+
+ procinit0();
+ initseg();
+
+ dmainit();
+ links();
+ conf.monitor = 1;
+ screeninit();
+ chandevreset(); /* most devices are discovered here */
+
+// i8250console(); /* too early; see init0 */
+
+ pageinit();
+ swapinit();
+ userinit();
+ schedinit();
+}
+
+void
+machinit(void)
+{
+ if (m == 0)
+ wave('?');
+// memset(m, 0, sizeof(Mach)); /* done by l.s, now contains stack */
+ 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();
+}
+
+int
+isaconfig(char *class, int ctlrno, ISAConf *isa)
+{
+ char cc[32], *p;
+ int i;
+
+ snprint(cc, sizeof cc, "%s%d", class, ctlrno);
+ p = getconf(cc);
+ if(p == nil)
+ return 0;
+
+ isa->type = "";
+ isa->nopt = tokenize(p, isa->opt, NISAOPT);
+ for(i = 0; i < isa->nopt; i++){
+ p = isa->opt[i];
+ if(cistrncmp(p, "type=", 5) == 0)
+ isa->type = p + 5;
+ else if(cistrncmp(p, "port=", 5) == 0)
+ isa->port = strtoul(p+5, &p, 0);
+ else if(cistrncmp(p, "irq=", 4) == 0)
+ isa->irq = strtoul(p+4, &p, 0);
+ else if(cistrncmp(p, "dma=", 4) == 0)
+ isa->dma = strtoul(p+4, &p, 0);
+ else if(cistrncmp(p, "mem=", 4) == 0)
+ isa->mem = strtoul(p+4, &p, 0);
+ else if(cistrncmp(p, "size=", 5) == 0)
+ isa->size = strtoul(p+5, &p, 0);
+ else if(cistrncmp(p, "freq=", 5) == 0)
+ isa->freq = strtoul(p+5, &p, 0);
+ }
+ return 1;
+}
+
+/*
+ * 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);
+
+ print("starting reboot...");
+ writeconf();
+ shutdown(0);
+
+ /*
+ * should be the only processor running now
+ */
+
+ print("reboot entry %#lux code %#lux size %ld\n",
+ PADDR(entry), PADDR(code), size);
+ delay(100);
+
+ /* turn off buffered serial console */
+ serialoq = nil;
+ kprintoq = nil;
+ screenputs = nil;
+
+ /* shutdown devices */
+ chandevshutdown();
+
+ /* call off the dog */
+ clockshutdown();
+
+ splhi();
+ intrsoff();
+
+ /* setup reboot trampoline function */
+ f = (void*)REBOOTADDR;
+ memmove(f, rebootcode, sizeof(rebootcode));
+ cacheuwbinv();
+ l2cacheuwbinv();
+
+ /* off we go - never to return */
+ (*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];
+
+ 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);
+
+ dmatest(); /* needs `up' set, so can't do it earlier */
+ chandevinit();
+ i8250console(); /* might be redundant, but harmless */
+ if(kbdq == nil)
+ panic("init0: nil kbdq");
+ if(serialoq == nil)
+ panic("init0: nil serialoq");
+ normalprint = 1;
+
+ 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);
+ s->flushme++;
+ 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);
+ 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 omapmem[nelem(conf.mem)] = {
+ /*
+ * Memory available to Plan 9:
+ */
+ { .base = PHYSDRAM, .limit = PHYSDRAM + Minmem, },
+};
+ulong memsize = Minmem;
+
+static int
+gotmem(uintptr sz)
+{
+ uintptr addr;
+
+ addr = PHYSDRAM + sz - BY2WD;
+ mmuidmap(addr, 1);
+ if (probeaddr(addr) >= 0) {
+ memsize = sz;
+ return 0;
+ }
+ return -1;
+}
+
+void
+confinit(void)
+{
+ int i;
+ ulong kpages;
+ uintptr pa;
+ char *p;
+
+ /*
+ * Copy the physical memory configuration to Conf.mem.
+ */
+ if(nelem(omapmem) > nelem(conf.mem)){
+ iprint("memory configuration botch\n");
+ exit(1);
+ }
+ if((p = getconf("*maxmem")) != nil) {
+ memsize = strtoul(p, 0, 0) - PHYSDRAM;
+ if (memsize < 16*MB) /* sanity */
+ memsize = 16*MB;
+ }
+
+ /*
+ * see if all that memory exists; if not, find out how much does.
+ * trapinit must have been called first.
+ */
+ if (gotmem(memsize) < 0 && gotmem(256*MB) < 0 && gotmem(128*MB) < 0) {
+ iprint("can't find any memory, assuming %dMB\n", Minmem / MB);
+ memsize = Minmem;
+ }
+
+ omapmem[0].limit = PHYSDRAM + memsize;
+ memmove(conf.mem, omapmem, sizeof(omapmem));
+
+ 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*80)/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;
+
+// archconfinit();
+}
+
+int
+cmpswap(long *addr, long old, long new)
+{
+ return cas32(addr, old, new);
+}
diff --git a/sys/src/9/omap/mem.h b/sys/src/9/omap/mem.h
new file mode 100755
index 000000000..1e5c570c9
--- /dev/null
+++ b/sys/src/9/omap/mem.h
@@ -0,0 +1,212 @@
+/*
+ * 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 (16*KiB) /* was 8K */
+#define STACKALIGN(sp) ((sp) & ~3) /* bug: assure with alloc */
+
+/*
+ * Address spaces.
+ * KTZERO is used by kprof and dumpstack (if any).
+ *
+ * KZERO (0xc0000000) is mapped to physical 0x80000000 (start of dram).
+ * u-boot claims to occupy the first 3 MB of dram, but we're willing to
+ * step on it once we're loaded. Expect plan9.ini in the first 64K past 3MB.
+ *
+ * L2 PTEs are stored in 1K before Mach (11K to 12K above KZERO).
+ * cpu0's Mach struct is at L1 - MACHSIZE(4K) to L1 (12K to 16K above KZERO).
+ * L1 PTEs are stored from L1 to L1+32K (16K to 48K above KZERO).
+ * KTZERO may be anywhere after that (but probably shouldn't collide with
+ * u-boot).
+ * This should leave over 8K from KZERO to L2 PTEs.
+ */
+#define KSEG0 0xC0000000 /* kernel segment */
+/* mask to check segment; good for 512MB dram */
+#define KSEGM 0xE0000000
+#define KZERO KSEG0 /* kernel address space */
+#define L1 (KZERO+16*KiB) /* tt ptes: 16KiB aligned */
+#define CONFADDR (KZERO+0x300000) /* unparsed plan9.ini */
+/* KTZERO must match loadaddr in mkfile */
+#define KTZERO (KZERO+0x310000) /* kernel text start */
+
+#define UZERO 0 /* user segment */
+#define UTZERO (UZERO+BY2PG) /* user text start */
+#define UTROUND(t) ROUNDUP((t), BY2PG)
+/* moved USTKTOP down to 512MB to keep MMIO space out of user space. */
+#define USTKTOP 0x20000000 /* 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)
+
+/*
+ * 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 64 /* bytes per cache line */
+#define PTEMAPMEM (1024*1024)
+#define PTEPERTAB (PTEMAPMEM/BY2PG)
+#define SEGMAPSIZE 1984 /* magic 16*124 */
+#define SSEGMAPSIZE 16 /* magic */
+#define PPN(x) ((x)&~(BY2PG-1)) /* pure page number? */
+
+/*
+ * 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.
+ */
+
+/* gpmc-controlled address space 0—1G */
+#define PHYSNAND 1 /* cs0 is onenand flash */
+#define PHYSETHER 0x2c000000
+
+#define PHYSIO 0x48000000 /* L4 ctl */
+
+#define PHYSSCM 0x48002000 /* system control module */
+
+/* core control pad cfg 0x48002030—0x480021e4, */
+/* core control d2d pad cfg 0x480021e4—0x48002264 */
+#define PHYSSCMPCONF 0x48002270 /* general device config */
+#define PHYSOMAPSTS 0x4800244c /* standalone short: has l2 size */
+/* core control pad cfg (2) 0x480025d8—0x480025fc */
+#define PHYSSWBOOTCFG 0x48002910 /* sw booting config */
+/* wakeup control pad cfg 0x48002a00—0x48002a54 */
+
+#define PHYSSCMMPU 0x48004900 /* actually CPU */
+#define PHYSSCMCORE 0x48004a00
+#define PHYSSCMWKUP 0x48004c00
+#define PHYSSCMPLL 0x48004d00 /* clock ctl for dpll[3-5] */
+#define PHYSSCMDSS 0x48004e00
+#define PHYSSCMPER 0x48005000
+#define PHYSSCMUSB 0x48005400
+
+#define PHYSL4CORE 0x48040100 /* l4 ap */
+#define PHYSDSS 0x48050000 /* start of dss registers */
+#define PHYSDISPC 0x48050400
+#define PHYSGFX 0x48050480 /* part of dispc */
+
+#define PHYSSDMA 0x48056000 /* system dma */
+#define PHYSDMA 0x48060000
+
+#define PHYSUSBTLL 0x48062000 /* usb: transceiver-less link */
+#define PHYSUHH 0x48064000 /* usb: `high-speed usb host' ctlr or subsys */
+#define PHYSOHCI 0x48064400 /* usb 1.0: slow */
+#define PHYSEHCI 0x48064800 /* usb 2.0: medium */
+#define PHYSUART0 0x4806a000
+#define PHYSUART1 0x4806c000
+#define PHYSMMCHS1 0x4809c000 /* mmc/sdio */
+#define PHYSUSBOTG 0x480ab000 /* on-the-go usb */
+#define PHYSMMCHS3 0x480ad000
+#define PHYSMMCHS2 0x480b4000
+
+#define PHYSINTC 0x48200000 /* interrupt controller */
+
+#define PHYSPRMIVA2 0x48206000 /* prm iva2 regs */
+/* 48306d40 sys_clkin_sel */
+#define PHYSPRMGLBL 0x48307200 /* prm global regs */
+#define PHYSPRMWKUSB 0x48307400
+
+#define PHYSCNTRL 0x4830a200 /* SoC id, etc. */
+#define PHYSWDT1 0x4830c000 /* wdt1, not on GP omaps */
+
+#define PHYSGPIO1 0x48310000 /* contains dss gpio */
+
+#define PHYSWDOG 0x48314000 /* watchdog timer, wdt2 */
+#define PHYSWDT2 0x48314000 /* watchdog timer, wdt2 */
+#define PHYSTIMER1 0x48318000
+
+#define PHYSL4WKUP 0x48328100 /* l4 wkup */
+#define PHYSL4PER 0x49000100 /* l4 per */
+
+#define PHYSCONS 0x49020000 /* uart console (third one) */
+
+#define PHYSWDT3 0x49030000 /* wdt3 */
+#define PHYSTIMER2 0x49032000
+#define PHYSTIMER3 0x49034000
+#define PHYSGPIO5 0x49056000
+#define PHYSGPIO6 0x49058000 /* contains igep ether gpio */
+
+#define PHYSIOEND 0x49100000 /* end of PHYSIO identity map */
+
+#define PHYSL4EMU 0x54006100 /* l4 emu */
+#define PHYSL4PROT 0x54728000 /* l4 protection regs */
+
+#define PHYSL3 0x68000000 /* l3 interconnect control */
+#define PHYSL3GPMCCFG 0x68002000 /* l3 gpmc target port agent cfg */
+#define PHYSL3USB 0x68004000 /* l3 regs for usb */
+#define PHYSL3USBOTG 0x68004400 /* l3 regs for usb otg */
+/* (target port) protection registers */
+#define PHYSL3PMRT 0x68010000 /* l3 PM register target prot. */
+#define PHYSL3GPMCPM 0x68012400 /* l3 gpmc target port protection */
+#define PHYSL3OCTRAM 0x68012800 /* l3 ocm ram */
+#define PHYSL3OCTROM 0x68012c00 /* l3 ocm rom */
+#define PHYSL3MAD2D 0x68013000 /* l3 die-to-die */
+#define PHYSL3IVA 0x68014000 /* l3 die-to-die */
+
+#define PHYSSMS 0x6c000000 /* cfg regs: sms addr space 2 */
+#define PHYSDRC 0x6d000000 /* sdram ctlr, addr space 3 */
+#define PHYSGPMC 0x6e000000 /* flash, non-dram memory ctlr */
+
+#define PHYSDRAM 0x80000000
+
+#define VIRTNAND 0x20000000 /* fixed by u-boot */
+#define VIRTIO PHYSIO
diff --git a/sys/src/9/omap/mkfile b/sys/src/9/omap/mkfile
new file mode 100755
index 000000000..de353a776
--- /dev/null
+++ b/sys/src/9/omap/mkfile
@@ -0,0 +1,136 @@
+CONF=beagle
+CONFLIST=beagle
+
+# allegedly u-boot uses the bottom 3MB (up to 0x300000) so avoid that,
+# and leave 64K for plan9.ini. loadaddr must match KTZERO in mem.h.
+# 0xc0310000 has worked, 0x80310000 should work but doesn't yet.
+loadaddr=0xc0310000
+
+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\
+ sysfile.$O\
+ sysproc.$O\
+ taslock.$O\
+ tod.$O\
+ xalloc.$O\
+
+OBJ=\
+ l.$O\
+ lexception.$O\
+ lproc.$O\
+ arch.$O\
+ clock.$O\
+ fpi.$O\
+ fpiarm.$O\
+ fpimem.$O\
+ kbd.$O\
+ main.$O\
+ mmu.$O\
+ random.$O\
+ trap.$O\
+ $CONF.root.$O\
+ $CONF.rootc.$O\
+ $DEVS\
+ $PORT\
+
+LIB=\
+ /$objtype/lib/libmemlayer.a\
+ /$objtype/lib/libmemdraw.a\
+ /$objtype/lib/libdraw.a\
+ /$objtype/lib/libip.a\
+ /$objtype/lib/libsec.a\
+ /$objtype/lib/libmp.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'
+ $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'
+# $LD -o $target -R4096 -T$loadaddr -l -a $OBJ $CONF.$O $LIB >$target.list
+ $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
+
+/$objtype/$p$CONF:D: $p$CONF s$p$CONF
+ { cp -x $p$CONF s$p$CONF /$objtype } &
+ wait
+ touch $target
+
+<../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
+
+archomap.$O devether.$0 ether9221.$O: etherif.h ../port/netif.h
+archomap.$O devflash.$O flashbeagle.$O flashigep.$O: ../port/flashif.h
+ecc.$O flashbeagle.$O flashigep.$O: ../port/nandecc.h io.h
+fpi.$O fpiarm.$O fpimem.$O: fpi.h
+l.$O lexception.$O lproc.$O mmu.$O: arm.s arm.h mem.h
+l.$O rebootcode.$O: cache.v7.s
+main.$O: errstr.h init.h reboot.h
+devdss.$O devmouse.$O mouse.$O screen.$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 cache.v7.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
+ $LD -l -s -T0x100 -R4 -o reboot.out rebootcode.$O -lc
+ {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
diff --git a/sys/src/9/omap/mmu.c b/sys/src/9/omap/mmu.c
new file mode 100755
index 000000000..05b4dc38c
--- /dev/null
+++ b/sys/src/9/omap/mmu.c
@@ -0,0 +1,496 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.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;
+
+// pa -= MACHSIZE+1024; /* put level 2 entries below level 1 */
+// l2 = KADDR(pa);
+
+ print("\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 */
+ print("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 */
+// if (type == Coarse) {
+// // could dump the l2 table for this l1 entry
+// }
+ }
+ va += MB;
+ }
+ if (endva != 0) /* close an open range */
+ print("l1 maps va (%#lux-%#llux) -> pa %#lux type %#ux\n",
+ startva, endva-1, startpa, rngtype);
+}
+
+/* identity map the megabyte containing va, uncached */
+static void
+idmap(PTE *l1, ulong va)
+{
+ va &= ~(MB-1);
+ l1[L1X(va)] = va | Dom0 | L1AP(Krw) | Section;
+}
+
+/* map `mbs' megabytes from virt to phys */
+void
+mmumap(uintptr virt, uintptr phys, int mbs)
+{
+ uint off;
+ PTE *l1;
+
+ phys &= ~(MB-1);
+ virt &= ~(MB-1);
+ l1 = KADDR(ttbget());
+ for (off = 0; mbs-- > 0; off += MB)
+ l1[L1X(virt + off)] = (phys + off) | Dom0 | L1AP(Krw) | Section;
+ cacheuwbinv();
+ l2cacheuwbinv();
+ mmuinvalidate();
+}
+
+/* identity map `mbs' megabytes from phys */
+void
+mmuidmap(uintptr phys, int mbs)
+{
+ mmumap(phys, phys, mbs);
+}
+
+void
+mmuinit(void)
+{
+ uintptr pa;
+ PTE *l1, *l2;
+
+ pa = ttbget();
+ l1 = KADDR(pa);
+
+ /* redundant with l.s; only covers first MB of 17MB */
+ l1[L1X(VIRTIO)] = PHYSIO|Dom0|L1AP(Krw)|Section;
+
+ idmap(l1, PHYSETHER); /* igep 9221 ethernet regs */
+ idmap(l1, PHYSL4PROT);
+ idmap(l1, PHYSL3);
+ idmap(l1, PHYSSMS);
+ idmap(l1, PHYSDRC);
+ idmap(l1, PHYSGPMC);
+
+ /* 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 */
+ coherence();
+
+ cacheuwbinv();
+ l2cacheuwbinv();
+ mmuinvalidate();
+
+ m->mmul1 = l1;
+// mmudump(l1); /* DEBUG */
+}
+
+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));
+
+ /* lose any possible stale tlb entries */
+ mmuinvalidate();
+
+ //print("mmuswitch l1lo %d l1hi %d %d\n",
+ // m->mmul1lo, m->mmul1hi, proc->kp);
+}
+
+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));
+
+ /* 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);
+
+ *l1 = PPN(pg->pa)|Dom0|Coarse;
+ cachedwbse(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 was %#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]);
+
+ /* clear out the current entry */
+ mmuinvalidateaddr(PPN(va));
+
+ /* write back dirty entries - we need this because the 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 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);
+ cachedwbinvse(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);
+ cachedwbinvse(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);
+ cachedwbinvse(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 && pa < PHYSDRAM+memsize)
+ return PHYSDRAM+memsize - 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/omap/mouse.c b/sys/src/9/omap/mouse.c
new file mode 100755
index 000000000..f0f62c8a1
--- /dev/null
+++ b/sys/src/9/omap/mouse.c
@@ -0,0 +1,255 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+#define Image IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+
+/*
+ * mouse types
+ */
+enum
+{
+ Mouseother= 0,
+ Mouseserial= 1,
+ MousePS2= 2,
+};
+
+extern int mouseshifted;
+
+static QLock mousectlqlock;
+static int mousetype;
+static int intellimouse;
+static int packetsize;
+static int resolution;
+static int accelerated;
+static int mousehwaccel;
+static char mouseport[5];
+
+enum
+{
+ CMaccelerated,
+ CMhwaccel,
+ CMintellimouse,
+ CMlinear,
+ CMps2,
+ CMps2intellimouse,
+ CMres,
+ CMreset,
+ CMserial,
+};
+
+static Cmdtab mousectlmsg[] =
+{
+ CMaccelerated, "accelerated", 0,
+ CMhwaccel, "hwaccel", 2,
+ CMintellimouse, "intellimouse", 1,
+ CMlinear, "linear", 1,
+ CMps2, "ps2", 1,
+ CMps2intellimouse, "ps2intellimouse", 1,
+ CMres, "res", 0,
+ CMreset, "reset", 1,
+ CMserial, "serial", 0,
+};
+
+/*
+ * ps/2 mouse message is three bytes
+ *
+ * byte 0 - 0 0 SDY SDX 1 M R L
+ * byte 1 - DX
+ * byte 2 - DY
+ *
+ * shift & right button is the same as middle button
+ *
+ * Intellimouse and AccuPoint with extra buttons deliver
+ * byte 3 - 00 or 01 or FF according to extra button state.
+ * extra buttons are mapped in this code to buttons 4 and 5.
+ * AccuPoint generates repeated events for these buttons;
+* it and Intellimouse generate 'down' events only, so
+ * user-level code is required to generate button 'up' events
+ * if they are needed by the application.
+ * Also on laptops with AccuPoint AND external mouse, the
+ * controller may deliver 3 or 4 bytes according to the type
+ * of the external mouse; code must adapt.
+ *
+ * On the NEC Versa series (and perhaps others?) we seem to
+ * lose a byte from the packet every once in a while, which
+ * means we lose where we are in the instruction stream.
+ * To resynchronize, if we get a byte more than two seconds
+ * after the previous byte, we assume it's the first in a packet.
+ */
+static void
+ps2mouseputc(int c, int shift)
+{
+ static short msg[4];
+ static int nb;
+ static uchar b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 3, 2, 3, 6, 7 };
+ static ulong lasttick;
+ ulong m;
+ int buttons, dx, dy;
+
+ shift |= mouseshifted;
+ m = MACHP(0)->ticks;
+ if(TK2SEC(m - lasttick) > 2)
+ nb = 0;
+ lasttick = m;
+ if(nb==0 && (c&0xc8)!=0x08)
+ if(intellimouse && (c==0x00 || c==0x01 || c==0xFF)){
+ packetsize = 4;
+ return;
+ }
+
+ msg[nb] = c;
+ if(++nb == packetsize){
+ nb = 0;
+ if(msg[0] & 0x10)
+ msg[1] |= 0xFF00;
+ if(msg[0] & 0x20)
+ msg[2] |= 0xFF00;
+
+ buttons = b[(msg[0]&7) | (shift ? 8 : 0)];
+ if(intellimouse && packetsize==4){
+ if((msg[3]&0xc8) == 0x08){
+ packetsize = 3;
+ msg[0] = msg[3];
+ nb = 1;
+ }else{
+ if((msg[3] >> 3) & 1)
+ buttons |= 1<<3;
+ else if(msg[3] & 0x7)
+ buttons |= 1<<4;
+ }
+ }
+ dx = msg[1];
+ dy = -msg[2];
+ mousetrack(dx, dy, buttons, TK2MS(MACHP(0)->ticks));
+ }
+}
+
+/*
+ * set up a ps2 mouse
+ */
+static void
+ps2mouse(void)
+{
+ if(mousetype == MousePS2)
+ return;
+
+// i8042auxenable(ps2mouseputc);
+// i8042auxcmd(0xEA); // TODO
+// i8042auxcmd(0xF4);
+
+ mousetype = MousePS2;
+ packetsize = 3;
+ mousehwaccel = 1;
+}
+
+/*
+ * The PS/2 Trackpoint multiplexor on the IBM Thinkpad T23 ignores
+ * acceleration commands. It is supposed to pass them on
+ * to the attached device, but my Logitech mouse is simply
+ * not behaving any differently. For such devices, we allow
+ * the user to use "hwaccel off" to tell us to back off to
+ * software acceleration even if we're using the PS/2 port.
+ * (Serial mice are always software accelerated.)
+ * For more information on the Thinkpad multiplexor, see
+ * http://wwwcssrv.almaden.ibm.com/trackpoint/
+ */
+static void
+setaccelerated(int x)
+{
+ accelerated = x;
+ mouseaccelerate(x);
+}
+
+static void
+setlinear(void)
+{
+ accelerated = 0;
+ mouseaccelerate(0);
+}
+
+static void
+setres(int n)
+{
+ resolution = n;
+}
+
+static void
+setintellimouse(void)
+{
+ intellimouse = 1;
+ packetsize = 4;
+}
+
+static void
+resetmouse(void)
+{
+ packetsize = 3;
+}
+
+void
+mousectl(Cmdbuf *cb)
+{
+ Cmdtab *ct;
+
+ qlock(&mousectlqlock);
+ if(waserror()){
+ qunlock(&mousectlqlock);
+ nexterror();
+ }
+
+ ct = lookupcmd(cb, mousectlmsg, nelem(mousectlmsg));
+ switch(ct->index){
+ case CMaccelerated:
+ setaccelerated(cb->nf == 1? 1: atoi(cb->f[1]));
+ break;
+ case CMintellimouse:
+ setintellimouse();
+ break;
+ case CMlinear:
+ setlinear();
+ break;
+ case CMps2:
+ intellimouse = 0;
+ break;
+ case CMps2intellimouse:
+ setintellimouse();
+ break;
+ case CMres:
+ if(cb->nf >= 2)
+ setres(atoi(cb->f[1]));
+ else
+ setres(1);
+ break;
+ case CMreset:
+ resetmouse();
+ if(accelerated)
+ setaccelerated(accelerated);
+ if(resolution)
+ setres(resolution);
+ if(intellimouse)
+ setintellimouse();
+ break;
+ case CMserial:
+ error("serial mice not supported");
+ break;
+ case CMhwaccel:
+ if(strcmp(cb->f[1], "on")==0)
+ mousehwaccel = 1;
+ else if(strcmp(cb->f[1], "off")==0)
+ mousehwaccel = 0;
+ else
+ cmderror(cb, "bad mouse control message");
+ }
+
+ qunlock(&mousectlqlock);
+ poperror();
+}
diff --git a/sys/src/9/omap/notes/movm.w b/sys/src/9/omap/notes/movm.w
new file mode 100755
index 000000000..a2b22f1e1
--- /dev/null
+++ b/sys/src/9/omap/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/omap/nvram b/sys/src/9/omap/nvram
new file mode 100755
index 000000000..a64a5a93f
--- /dev/null
+++ b/sys/src/9/omap/nvram
Binary files differ
diff --git a/sys/src/9/omap/random.c b/sys/src/9/omap/random.c
new file mode 100755
index 000000000..1f7c0983d
--- /dev/null
+++ b/sys/src/9/omap/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/omap/rebootcode.s b/sys/src/9/omap/rebootcode.s
new file mode 100755
index 000000000..f07146f81
--- /dev/null
+++ b/sys/src/9/omap/rebootcode.s
@@ -0,0 +1,209 @@
+/*
+ * omap3530 reboot code
+ *
+ * must fit in 11K to avoid stepping on PTEs; see mem.h.
+ *
+ * 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 */
+
+ MOVW CPSR, R0
+ ORR $(PsrDirq|PsrDfiq), R0
+ MOVW R0, CPSR /* splhi */
+ BARRIERS
+
+WAVE('R')
+ MRC CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl
+ BIC $CpACasa, R1 /* no speculative I access forwarding to mem */
+ /* slow down */
+ ORR $(CpACcachenopipe|CpACcp15serial|CpACcp15waitidle|CpACcp15pipeflush), R1
+ MCR CpSC, 0, R1, C(CpCONTROL), C(0), CpAuxctl
+ BARRIERS
+
+ BL cachesoff(SB)
+ /* now back in 29- or 26-bit addressing, mainly for SB */
+ /* double mapping of PHYSDRAM & KZERO now in effect */
+
+ /*
+ * turn the MMU off
+ */
+
+WAVE('e')
+ /* first switch to PHYSDRAM-based addresses */
+ DMB
+
+ MOVW $KSEGM, R7 /* clear segment bits */
+ MOVW $PHYSDRAM, R0 /* set dram base bits */
+ BIC R7, R12 /* adjust SB */
+ ORR R0, R12
+
+ BL _r15warp(SB)
+ /* don't care about saving R14; we're not returning */
+
+ /*
+ * now running in PHYSDRAM segment, not KZERO.
+ */
+
+WAVE('b')
+ SUB $12, SP /* paranoia */
+ BL cacheuwbinv(SB)
+ ADD $12, SP /* paranoia */
+
+ /* invalidate mmu mappings */
+ MOVW $KZERO, R0 /* some valid virtual address */
+ MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv
+ BARRIERS
+
+WAVE('o')
+ MRC CpSC, 0, R0, C(CpCONTROL), C(0)
+ BIC $(CpCmmu|CpCdcache|CpCicache), R0
+ MCR CpSC, 0, R0, C(CpCONTROL), C(0) /* mmu off */
+ BARRIERS
+
+WAVE('o')
+ /* copy in arguments from stack frame before moving stack */
+ MOVW p2+4(FP), R4 /* phys source */
+ MOVW n+8(FP), R5 /* byte count */
+ MOVW p1+0(FP), R6 /* phys destination */
+
+ /* 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 R5, 40(SP) /* save count */
+ MOVW R6, 44(SP) /* save dest/entry */
+
+ DELAY(printloop2, 2)
+WAVE('t')
+
+ MOVW 40(SP), R5 /* restore count */
+ MOVW 44(SP), R6 /* restore dest/entry */
+ MOVW R6, 0(SP) /* normally saved LR goes here */
+ MOVW R6, 4(SP) /* push dest */
+ MOVW R6, R0
+ MOVW R4, 8(SP) /* push src */
+ MOVW R5, 12(SP) /* push size */
+ BL memmove(SB)
+
+WAVE('-')
+ /*
+ * flush caches
+ */
+ BL cacheuwbinv(SB)
+
+WAVE('>')
+ DELAY(printloopret, 1)
+WAVE('\r')
+ DELAY(printloopnl, 1)
+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
+ */
+ MOVW 44(SP), R6 /* restore R6 (dest/entry) */
+ ORR R6, R6 /* NOP: avoid link bug */
+ B (R6)
+WAVE('?')
+ B 0(PC)
+
+/*
+ * turn the caches off, double map PHYSDRAM & KZERO, invalidate TLBs, revert
+ * to tiny addresses. upon return, it will be safe to turn off the mmu.
+ */
+TEXT cachesoff(SB), 1, $-4
+ MOVM.DB.W [R14,R1-R10], (R13) /* save regs on stack */
+ MOVW CPSR, R0
+ ORR $(PsrDirq|PsrDfiq), R0
+ MOVW R0, CPSR
+ BARRIERS
+
+ SUB $12, SP /* paranoia */
+ BL cacheuwbinv(SB)
+ ADD $12, SP /* paranoia */
+
+ MRC CpSC, 0, R0, C(CpCONTROL), C(0)
+ BIC $(CpCicache|CpCdcache), R0
+ MCR CpSC, 0, R0, C(CpCONTROL), C(0) /* caches off */
+ BARRIERS
+
+ /*
+ * caches are off
+ */
+
+ /* invalidate stale TLBs before changing them */
+ MOVW $KZERO, R0 /* some valid virtual address */
+ MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv
+ BARRIERS
+
+ /* redo double map of PHYSDRAM, KZERO */
+ MOVW $PHYSDRAM, R3
+ CMP $KZERO, R3
+ BEQ noun2map
+ MOVW $(L1+L1X(PHYSDRAM)), R4 /* address of PHYSDRAM's PTE */
+ MOVW $PTEDRAM, R2 /* PTE bits */
+ MOVW $DOUBLEMAPMBS, 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
+noun2map:
+
+ /*
+ * flush stale TLB entries
+ */
+
+ BARRIERS
+ MOVW $KZERO, R0 /* some valid virtual address */
+ MCR CpSC, 0, R0, C(CpTLB), C(CpTLBinvu), CpTLBinv
+ BARRIERS
+
+ /* switch back to PHYSDRAM addressing, mainly for SB */
+ MOVW $KSEGM, R7 /* clear segment bits */
+ MOVW $PHYSDRAM, R0 /* set dram base bits */
+ BIC R7, R12 /* adjust SB */
+ ORR R0, R12
+ BIC R7, SP
+ ORR R0, SP
+
+ MOVM.IA.W (R13), [R14,R1-R10] /* restore regs from stack */
+
+ MOVW $KSEGM, R0 /* clear segment bits */
+ BIC R0, R14 /* adjust link */
+ MOVW $PHYSDRAM, R0 /* set dram base bits */
+ ORR R0, R14
+
+ RET
+
+TEXT _r15warp(SB), 1, $-4
+ BIC R7, R14 /* link */
+ ORR R0, R14
+
+ BIC R7, R13 /* SP */
+ ORR R0, R13
+ RET
+
+TEXT panic(SB), 1, $-4 /* stub */
+WAVE('?')
+ RET
+TEXT pczeroseg(SB), 1, $-4 /* stub */
+ RET
+
+#include "cache.v7.s"
diff --git a/sys/src/9/omap/screen.c b/sys/src/9/omap/screen.c
new file mode 100755
index 000000000..e47fd2821
--- /dev/null
+++ b/sys/src/9/omap/screen.c
@@ -0,0 +1,791 @@
+/*
+ * ti omap35 display subsystem (dss)
+ *
+ * can handle 2ⁿ bits per pixel for 0 < n ≤ 4, and 12 and 24 bits.
+ * can handle 1024×768 at 60 Hz with pixel clock of 63.5 MHz
+ * 1280×800 at 59.91 Hz with pixel clock of 71 MHz
+ * 1400×1050 lcd at 50 MHz with pixel clock of 75 MHz
+ * has 256 24-bit entries in RGB palette
+ */
+#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"
+
+#define Image IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+#include "screen.h"
+// #include "gamma.h"
+
+enum {
+ Tabstop = 4, /* should be 8 */
+ Scroll = 8, /* lines to scroll at one time */
+ /*
+ * screen settings for Wid and Ht, should a bit more dynamic?
+ * http://www.epanorama.net/faq/vga2rgb/calc.html
+ * used to calculate settings.
+ */
+
+// Hbp = (248-1) << 20,
+// Hfp = (48-1) << 8,
+// Hsw = 112-1,
+
+// Vbp = 38 << 20,
+// Vfp = 1 << 8,
+// Vsw = 3,
+
+ Tft = 0x60,
+
+ Loadmode = 2 << 1,
+ Fifosize = 0x400,
+
+ /* dispc sysconfig */
+ Midlemode = 2 << 12,
+ Sidlemode = 2 << 3,
+ EnableWakeup = 1 << 2,
+ Autoidle = 1 << 0,
+
+ /* dispc pool_freq */
+ Ipc = 1 << 14,
+ Ihs = 1 << 13,
+ Ivs = 1 << 12,
+ Acb = 0x28,
+
+ /* gfx attribs */
+ Burstsize = 2 << 6,
+ Format = 6 << 1,
+ Gfxenable = 1 << 0,
+
+ /* dispc control */
+ Gpout1 = 1 << 16,
+ Gpout0 = 1 << 15,
+ Tftdata = 3 << 8,
+ Digital = 1 << 6,
+ Lcd = 1 << 5,
+ Stntft = 1 << 3,
+ Digitalen = 1 << 1,
+// Lcden = 1 << 0, /* unused */
+};
+
+typedef struct Dispcregs Dispc;
+typedef struct Dssregs Dss;
+typedef struct Ioregs Ioregs;
+
+struct Ioregs { /* common registers, 68 (0x44) bytes */
+ ulong rev;
+ uchar _pad0[0x10-0x4];
+ ulong sysconf;
+ ulong sysstat;
+ ulong irqstat1;
+
+ /* Dispc only regs */
+ ulong irqen1;
+ ulong wkupen;
+ ulong _pad1;
+ ulong irqsts2;
+ ulong irqen2;
+ ulong _pad2[4];
+
+ ulong ctrl;
+};
+
+struct Dssregs { /* display subsys at 0x48050000 */
+ Ioregs;
+ ulong sdicrtl;
+ ulong pllcrtl;
+ uchar _pad3[0x5c-0x4c];
+ ulong sdistat;
+};
+
+struct Dispcregs { /* display ctlr at 0x48050400 */
+ Ioregs;
+ ulong config;
+ ulong _pad3;
+ ulong defaultcolor[2];
+ ulong transcolor[2];
+ ulong linestat;
+ ulong linenum;
+ ulong timing_h;
+ ulong timing_v;
+ ulong pol_req;
+ ulong divisor;
+ ulong alpha;
+ ulong digsize;
+ ulong lcdsize;
+
+ ulong base[2]; /* should allocate both to avoid dithering */
+ ulong pos;
+ ulong size;
+ ulong _pad4[4];
+ ulong attrib;
+ ulong fifothr;
+ ulong fifosize;
+ ulong rowinc;
+ ulong pixelinc;
+ ulong winskip;
+ ulong palette; /* gfx_table_ba */
+ uchar _pad5[0x5d4 - 0x4bc];
+
+ ulong datacycle[3];
+ uchar _pad5[0x620 - 0x5e0];
+
+ ulong cprcoefr;
+ ulong cprcoefg;
+ ulong cprcoefb;
+ ulong preload;
+};
+
+int drawdebug;
+Point ZP = {0, 0};
+Cursor arrow = {
+ { -1, -1 },
+ { 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C,
+ 0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04,
+ 0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04,
+ 0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40,
+ },
+ { 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0,
+ 0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8,
+ 0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8,
+ 0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00,
+ },
+};
+
+OScreen oscreen;
+Settings settings[] = {
+[Res800x600] { 800, 600, 60, RGB16, 40000, 88, 40, 128, 23, 1, 5, },
+[Res1024x768] { 1024, 768, 60, RGB16, 65000, 160, 24, 136, 29, 3, 7, },
+[Res1280x1024] { 1280, 1024, 60, RGB16, 108000, 248, 48, 112, 38, 1, 4, },
+[Res1400x1050] { 1400, 1050, 50, RGB16, 108000, 248, 48, 112, 38, 1, 4, }, // TODO
+};
+Omap3fb *framebuf;
+Memimage *gscreen;
+
+static Memdata xgdata;
+
+static Memimage xgscreen =
+{
+ { 0, 0, Wid, Ht }, /* r */
+ { 0, 0, Wid, Ht }, /* clipr */
+ Depth, /* depth */
+ 3, /* nchan */
+ RGB16, /* chan */
+ nil, /* cmap */
+ &xgdata, /* data */
+ 0, /* zero */
+ Wid*(Depth/BI2BY)/BY2WD, /* width in words of a single scan line */
+ 0, /* layer */
+ 0, /* flags */
+};
+
+static Memimage *conscol;
+static Memimage *back;
+
+static Memsubfont *memdefont;
+
+static Lock screenlock;
+
+static Point curpos;
+static int h, w;
+static int landscape = 0; /* screen orientation, default is 0: portrait */
+static ushort *vscreen; /* virtual screen */
+static Rectangle window;
+
+static Dispc *dispc = (Dispc *)PHYSDISPC;
+static Dss *dss = (Dss *)PHYSDSS;
+
+static void omapscreenputs(char *s, int n);
+static ulong rep(ulong, int);
+static void screenputc(char *buf);
+static void screenwin(void);
+
+/*
+ * Software cursor.
+ */
+int swvisible; /* is the cursor visible? */
+int swenabled; /* is the cursor supposed to be on the screen? */
+Memimage* swback; /* screen under cursor */
+Memimage* swimg; /* cursor image */
+Memimage* swmask; /* cursor mask */
+Memimage* swimg1;
+Memimage* swmask1;
+
+Point swoffset;
+Rectangle swrect; /* screen rectangle in swback */
+Point swpt; /* desired cursor location */
+Point swvispt; /* actual cursor location */
+int swvers; /* incremented each time cursor image changes */
+int swvisvers; /* the version on the screen */
+
+static void
+lcdoff(void)
+{
+ dispc->ctrl &= ~1; /* disable the lcd */
+ coherence();
+
+ dispc->irqstat1 |= 1; /* set framedone */
+ coherence();
+
+ /* the lcd never comes ready, so don't bother with this */
+#ifdef notdef
+ /* spin until the frame is complete, but not forever */
+ for(cnt = 50; !(dispc->irqstat1 & 1) && cnt-- > 0; )
+ delay(10);
+#endif
+ delay(20); /* worst case for 1 frame, 50Hz */
+}
+
+static void
+dssstart(void)
+{
+ /* should reset the dss system */
+ dss->sysconf |= 1;
+ coherence();
+}
+
+/* see spruf98i §15.6.7.4.2 */
+static void
+configdispc(void)
+{
+ Settings *sp;
+
+ sp = oscreen.settings;
+ dss->ctrl &= 0x78; /* choose dss clock */
+ dispc->sysconf = Midlemode | Sidlemode | EnableWakeup | Autoidle;
+ dispc->config = Loadmode;
+ coherence();
+
+ /* pll */
+ dispc->defaultcolor[0] = 0; /* set background color to black? */
+ dispc->defaultcolor[1] = 0;
+ dispc->transcolor[0] = 0; /* set transparency to full */
+ dispc->transcolor[1] = 0;
+
+ dispc->timing_h = (sp->hbp-1) << 20 | (sp->hfp-1) << 8 |
+ (sp->hsw-1);
+ dispc->timing_v = sp->vbp << 20 | sp->vfp << 8 |
+ (sp->vsw-1);
+
+ dispc->pol_req = Ipc | Ihs | Ivs | Acb;
+ dispc->divisor = 1 << 16 | HOWMANY(432000, sp->pixelclock);
+
+ dispc->lcdsize = (sp->ht - 1) << 16 | (sp->wid - 1);
+ coherence();
+
+ dispc->base[0] = PADDR(framebuf->pixel);
+ dispc->base[1] = PADDR(framebuf->pixel);
+
+ dispc->pos = 0; /* place screen in the left corner */
+ /* use the whole screen */
+ dispc->size = (sp->ht - 1) << 16 | (sp->wid - 1);
+
+ /* what mode does plan 9 use for fb? */
+ dispc->attrib = Burstsize | Format | Gfxenable;
+
+ dispc->preload = Tft;
+ dispc->fifosize = Fifosize;
+ /* 1008 is max for our Burstsize */
+ dispc->fifothr = (Fifosize - 1) << 16 | (1008 - 1);
+
+ /* 1 byte is one pixel (not true, we use 2 bytes per pixel) */
+ dispc->rowinc = 1;
+ dispc->pixelinc = 1;
+ dispc->winskip = 0; /* don't skip anything */
+ coherence();
+
+ // dispc->palette = PADDR(framebuf->palette);
+}
+
+static void
+lcdon(int enable)
+{
+ dispc->ctrl = Gpout1 | Gpout0 | Tftdata | Digital | Lcd | Stntft |
+ Digitalen | enable;
+ coherence();
+ delay(10);
+}
+
+static void
+lcdstop(void)
+{
+ configscreengpio();
+ screenclockson();
+
+ lcdoff();
+}
+
+static void
+lcdinit(void)
+{
+ lcdstop();
+
+ dssstart();
+ configdispc();
+}
+
+/* Paint the image data with blue pixels */
+void
+screentest(void)
+{
+ int i;
+
+ for (i = nelem(framebuf->pixel) - 1; i >= 0; i--)
+ framebuf->pixel[i] = 0x1f; /* blue */
+// memset(framebuf->pixel, ~0, sizeof framebuf->pixel); /* white */
+}
+
+void
+screenpower(int on)
+{
+ blankscreen(on == 0);
+}
+
+/*
+ * called with drawlock locked for us, most of the time.
+ * kernel prints at inopportune times might mean we don't
+ * hold the lock, but memimagedraw is now reentrant so
+ * that should be okay: worst case we get cursor droppings.
+ */
+void
+swcursorhide(void)
+{
+ if(swvisible == 0)
+ return;
+ if(swback == nil)
+ return;
+ swvisible = 0;
+ memimagedraw(gscreen, swrect, swback, ZP, memopaque, ZP, S);
+ flushmemscreen(swrect);
+}
+
+void
+swcursoravoid(Rectangle r)
+{
+ if(swvisible && rectXrect(r, swrect))
+ swcursorhide();
+}
+
+void
+swcursordraw(void)
+{
+ if(swvisible)
+ return;
+ if(swenabled == 0)
+ return;
+ if(swback == nil || swimg1 == nil || swmask1 == nil)
+ return;
+// assert(!canqlock(&drawlock)); // assertion fails on omap
+ swvispt = swpt;
+ swvisvers = swvers;
+ swrect = rectaddpt(Rect(0,0,16,16), swvispt);
+ memimagedraw(swback, swback->r, gscreen, swpt, memopaque, ZP, S);
+ memimagedraw(gscreen, swrect, swimg1, ZP, swmask1, ZP, SoverD);
+ flushmemscreen(swrect);
+ swvisible = 1;
+}
+
+int
+cursoron(int dolock)
+{
+ if (dolock)
+ lock(&oscreen);
+ cursoroff(0);
+ swcursordraw();
+ if (dolock)
+ unlock(&oscreen);
+ return 0;
+}
+
+void
+cursoroff(int dolock)
+{
+ if (dolock)
+ lock(&oscreen);
+ swcursorhide();
+ if (dolock)
+ unlock(&oscreen);
+}
+
+void
+swload(Cursor *curs)
+{
+ uchar *ip, *mp;
+ int i, j, set, clr;
+
+ if(!swimg || !swmask || !swimg1 || !swmask1)
+ return;
+ /*
+ * Build cursor image and mask.
+ * Image is just the usual cursor image
+ * but mask is a transparent alpha mask.
+ *
+ * The 16x16x8 memimages do not have
+ * padding at the end of their scan lines.
+ */
+ ip = byteaddr(swimg, ZP);
+ mp = byteaddr(swmask, ZP);
+ for(i=0; i<32; i++){
+ set = curs->set[i];
+ clr = curs->clr[i];
+ for(j=0x80; j; j>>=1){
+ *ip++ = set&j ? 0x00 : 0xFF;
+ *mp++ = (clr|set)&j ? 0xFF : 0x00;
+ }
+ }
+ swoffset = curs->offset;
+ swvers++;
+ memimagedraw(swimg1, swimg1->r, swimg, ZP, memopaque, ZP, S);
+ memimagedraw(swmask1, swmask1->r, swmask, ZP, memopaque, ZP, S);
+}
+
+/* called from devmouse */
+void
+setcursor(Cursor* curs)
+{
+ cursoroff(1);
+ oscreen.Cursor = *curs;
+ swload(curs);
+ cursoron(1);
+}
+
+int
+swmove(Point p)
+{
+ swpt = addpt(p, swoffset);
+ return 0;
+}
+
+void
+swcursorclock(void)
+{
+ int x;
+
+ if(!swenabled)
+ return;
+ swmove(mousexy());
+ if(swvisible && eqpt(swpt, swvispt) && swvers==swvisvers)
+ return;
+
+ x = splhi();
+ if(swenabled)
+ if(!swvisible || !eqpt(swpt, swvispt) || swvers!=swvisvers)
+ if(canqlock(&drawlock)){
+ swcursorhide();
+ swcursordraw();
+ qunlock(&drawlock);
+ }
+ splx(x);
+}
+
+void
+swcursorinit(void)
+{
+ static int init;
+
+ if(!init){
+ init = 1;
+ addclock0link(swcursorclock, 10);
+ }
+ if(swback){
+ freememimage(swback);
+ freememimage(swmask);
+ freememimage(swmask1);
+ freememimage(swimg);
+ freememimage(swimg1);
+ }
+
+ swback = allocmemimage(Rect(0,0,32,32), gscreen->chan);
+ swmask = allocmemimage(Rect(0,0,16,16), GREY8);
+ swmask1 = allocmemimage(Rect(0,0,16,16), GREY1);
+ swimg = allocmemimage(Rect(0,0,16,16), GREY8);
+ swimg1 = allocmemimage(Rect(0,0,16,16), GREY1);
+ if(swback==nil || swmask==nil || swmask1==nil || swimg==nil || swimg1 == nil){
+ print("software cursor: allocmemimage fails\n");
+ return;
+ }
+
+ memfillcolor(swmask, DOpaque);
+ memfillcolor(swmask1, DOpaque);
+ memfillcolor(swimg, DBlack);
+ memfillcolor(swimg1, DBlack);
+}
+
+/* called from main and possibly later from devdss to change resolution */
+void
+screeninit(void)
+{
+ static int first = 1;
+
+ if (first) {
+ iprint("screeninit...");
+ oscreen.settings = &settings[Res1280x1024];
+
+ lcdstop();
+ if (framebuf)
+ free(framebuf);
+ /* mode is 16*32 = 512 */
+ framebuf = xspanalloc(sizeof *framebuf, 16*32, 0);
+ }
+
+ lcdinit();
+ lcdon(1);
+ if (first) {
+ memimageinit();
+ memdefont = getmemdefont();
+ screentest();
+ }
+
+ xgdata.ref = 1;
+ xgdata.bdata = (uchar *)framebuf->pixel;
+
+ gscreen = &xgscreen;
+ gscreen->r = Rect(0, 0, Wid, Ht);
+ gscreen->clipr = gscreen->r;
+ /* width, in words, of a single scan line */
+ gscreen->width = Wid * (Depth / BI2BY) / BY2WD;
+ flushmemscreen(gscreen->r);
+
+ blanktime = 3; /* minutes */
+
+ if (first) {
+ iprint("on: blue for 3 seconds...");
+ delay(3*1000);
+ iprint("\n");
+
+ screenwin(); /* draw border & top orange bar */
+ screenputs = omapscreenputs;
+ iprint("screen: frame buffer at %#p for %dx%d\n",
+ framebuf, oscreen.settings->wid, oscreen.settings->ht);
+
+ swenabled = 1;
+ swcursorinit(); /* needs gscreen set */
+ setcursor(&arrow);
+
+ first = 0;
+ }
+}
+
+/* flushmemscreen should change buffer? */
+void
+flushmemscreen(Rectangle r)
+{
+ ulong start, end;
+
+ if (r.min.x < 0)
+ r.min.x = 0;
+ if (r.max.x > Wid)
+ r.max.x = Wid;
+ if (r.min.y < 0)
+ r.min.y = 0;
+ if (r.max.y > Ht)
+ r.max.y = Ht;
+ if (rectclip(&r, gscreen->r) == 0)
+ return;
+ start = (ulong)&framebuf->pixel[r.min.y*Wid + r.min.x];
+ end = (ulong)&framebuf->pixel[(r.max.y - 1)*Wid + r.max.x -1];
+ cachedwbse((ulong *)start, end - start);
+}
+
+/*
+ * export screen to devdraw
+ */
+uchar*
+attachscreen(Rectangle *r, ulong *chan, int *d, int *width, int *softscreen)
+{
+ *r = gscreen->r;
+ *d = gscreen->depth;
+ *chan = gscreen->chan;
+ *width = gscreen->width;
+ *softscreen = (landscape == 0);
+ return (uchar *)gscreen->data->bdata;
+}
+
+void
+getcolor(ulong p, ulong *pr, ulong *pg, ulong *pb)
+{
+ USED(p, pr, pg, pb);
+}
+
+int
+setcolor(ulong p, ulong r, ulong g, ulong b)
+{
+ USED(p, r, g, b);
+ return 0;
+}
+
+void
+blankscreen(int blank)
+{
+ if (blank)
+ lcdon(0);
+ else {
+ lcdinit();
+ lcdon(1);
+ }
+}
+
+static void
+omapscreenputs(char *s, int n)
+{
+ int i;
+ Rune r;
+ char buf[4];
+
+ if (!islo()) {
+ /* don't deadlock trying to print in interrupt */
+ if (!canlock(&screenlock))
+ return; /* discard s */
+ } else
+ lock(&screenlock);
+
+ while (n > 0) {
+ i = chartorune(&r, s);
+ if (i == 0) {
+ s++;
+ --n;
+ continue;
+ }
+ memmove(buf, s, i);
+ buf[i] = 0;
+ n -= i;
+ s += i;
+ screenputc(buf);
+ }
+ unlock(&screenlock);
+}
+
+static void
+screenwin(void)
+{
+ char *greet;
+ Memimage *orange;
+ Point p, q;
+ Rectangle r;
+
+ memsetchan(gscreen, RGB16);
+
+ back = memwhite;
+ conscol = memblack;
+
+ orange = allocmemimage(Rect(0, 0, 1, 1), RGB16);
+ orange->flags |= Frepl;
+ orange->clipr = gscreen->r;
+ orange->data->bdata[0] = 0x40; /* magic: colour? */
+ orange->data->bdata[1] = 0xfd; /* magic: colour? */
+
+ w = memdefont->info[' '].width;
+ h = memdefont->height;
+
+ r = insetrect(gscreen->r, 4);
+
+ memimagedraw(gscreen, r, memblack, ZP, memopaque, ZP, S);
+ window = insetrect(r, 4);
+ memimagedraw(gscreen, window, memwhite, ZP, memopaque, ZP, S);
+
+ memimagedraw(gscreen, Rect(window.min.x, window.min.y,
+ window.max.x, window.min.y + h + 5 + 6), orange, ZP, nil, ZP, S);
+ freememimage(orange);
+ window = insetrect(window, 5);
+
+ greet = " Plan 9 Console ";
+ p = addpt(window.min, Pt(10, 0));
+ q = memsubfontwidth(memdefont, greet);
+ memimagestring(gscreen, p, conscol, ZP, memdefont, greet);
+ flushmemscreen(r);
+ window.min.y += h + 6;
+ curpos = window.min;
+ window.max.y = window.min.y + ((window.max.y - window.min.y) / h) * h;
+}
+
+static void
+scroll(void)
+{
+ int o;
+ Point p;
+ Rectangle r;
+
+ /* move window contents up Scroll text lines */
+ o = Scroll * h;
+ r = Rpt(window.min, Pt(window.max.x, window.max.y - o));
+ p = Pt(window.min.x, window.min.y + o);
+ memimagedraw(gscreen, r, gscreen, p, nil, p, S);
+ flushmemscreen(r);
+
+ /* clear the bottom Scroll text lines */
+ r = Rpt(Pt(window.min.x, window.max.y - o), window.max);
+ memimagedraw(gscreen, r, back, ZP, nil, ZP, S);
+ flushmemscreen(r);
+
+ curpos.y -= o;
+}
+
+static void
+screenputc(char *buf)
+{
+ int w;
+ uint pos;
+ Point p;
+ Rectangle r;
+ static int *xp;
+ static int xbuf[256];
+
+ if (xp < xbuf || xp >= &xbuf[sizeof(xbuf)])
+ xp = xbuf;
+
+ switch (buf[0]) {
+ case '\n':
+ if (curpos.y + h >= window.max.y)
+ scroll();
+ curpos.y += h;
+ screenputc("\r");
+ break;
+ case '\r':
+ xp = xbuf;
+ curpos.x = window.min.x;
+ break;
+ case '\t':
+ p = memsubfontwidth(memdefont, " ");
+ w = p.x;
+ if (curpos.x >= window.max.x - Tabstop * w)
+ screenputc("\n");
+
+ pos = (curpos.x - window.min.x) / w;
+ pos = Tabstop - pos % Tabstop;
+ *xp++ = curpos.x;
+ r = Rect(curpos.x, curpos.y, curpos.x + pos * w, curpos.y + h);
+ memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S);
+ flushmemscreen(r);
+ curpos.x += pos * w;
+ break;
+ case '\b':
+ if (xp <= xbuf)
+ break;
+ xp--;
+ r = Rect(*xp, curpos.y, curpos.x, curpos.y + h);
+ memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S);
+ flushmemscreen(r);
+ curpos.x = *xp;
+ break;
+ case '\0':
+ break;
+ default:
+ p = memsubfontwidth(memdefont, buf);
+ w = p.x;
+
+ if (curpos.x >= window.max.x - w)
+ screenputc("\n");
+
+ *xp++ = curpos.x;
+ r = Rect(curpos.x, curpos.y, curpos.x + w, curpos.y + h);
+ memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S);
+ memimagestring(gscreen, curpos, conscol, ZP, memdefont, buf);
+ flushmemscreen(r);
+ curpos.x += w;
+ }
+}
diff --git a/sys/src/9/omap/screen.h b/sys/src/9/omap/screen.h
new file mode 100755
index 000000000..ba9b36bd3
--- /dev/null
+++ b/sys/src/9/omap/screen.h
@@ -0,0 +1,105 @@
+typedef struct Cursor Cursor;
+typedef struct Cursorinfo Cursorinfo;
+typedef struct OScreen OScreen;
+typedef struct Omap3fb Omap3fb;
+typedef struct Settings Settings;
+
+struct Cursorinfo
+{
+ Cursor;
+ Lock;
+};
+
+extern Cursor arrow;
+extern Cursorinfo cursor;
+
+/* devmouse.c */
+extern void mousetrack(int, int, int, int);
+extern Point mousexy(void);
+
+extern void mouseaccelerate(int);
+extern void mouseresize(void);
+
+/* screen.c */
+extern uchar* attachscreen(Rectangle*, ulong*, int*, int*, int*);
+extern void flushmemscreen(Rectangle);
+extern int cursoron(int);
+extern void cursoroff(int);
+extern void setcursor(Cursor*);
+extern int screensize(int, int, int, ulong);
+extern int screenaperture(int, int);
+extern Rectangle physgscreenr; /* actual monitor size */
+extern void blankscreen(int);
+
+extern void swcursorinit(void);
+extern void swcursorhide(void);
+extern void swcursoravoid(Rectangle);
+extern void swcursorunhide(void);
+
+/* devdraw.c */
+extern void deletescreenimage(void);
+extern void resetscreenimage(void);
+extern int drawhasclients(void);
+extern ulong blanktime;
+extern void setscreenimageclipr(Rectangle);
+extern void drawflush(void);
+extern int drawidletime(void);
+extern QLock drawlock;
+
+#define ishwimage(i) 0 /* for ../port/devdraw.c */
+
+/* for communication between devdss.c and screen.c */
+
+enum {
+ /* maxima */
+ Wid = 1280,
+ Ht = 1024,
+ Depth = 16, /* bits per pixel */
+
+ Pcolours = 256, /* Palette */
+ Pred = 0,
+ Pgreen = 1,
+ Pblue = 2,
+
+ Pblack = 0x00,
+ Pwhite = 0xFF,
+
+ /* settings indices */
+ Res800x600 = 0,
+ Res1024x768,
+ Res1280x1024,
+ Res1400x1050,
+};
+
+struct Settings {
+ uint wid; /* width in pixels */
+ uint ht; /* height in pixels */
+ uint freq; /* refresh frequency; only printed */
+ uint chan; /* draw chan */
+
+ /* shouldn't be needed? */
+ uint pixelclock;
+
+ /* horizontal timing */
+ uint hbp; /* back porch: pixel clocks before scan line */
+ uint hfp; /* front porch: pixel clocks after scan line */
+ uint hsw; /* sync pulse width: more hfp */
+
+ /* vertical timing */
+ uint vbp; /* back porch: line clocks before frame */
+ uint vfp; /* front porch: line clocks after frame */
+ uint vsw; /* sync pulse width: more vfp */
+};
+
+struct OScreen {
+ Lock;
+ Cursor;
+ Settings *settings;
+ int open;
+};
+
+struct Omap3fb { /* frame buffer for 24-bit active color */
+// short palette[256];
+ /* pixel data, even; base type's width must match Depth */
+ ushort pixel[Wid*Ht];
+};
diff --git a/sys/src/9/omap/softfpu.c b/sys/src/9/omap/softfpu.c
new file mode 100755
index 000000000..7f01446c0
--- /dev/null
+++ b/sys/src/9/omap/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/omap/syscall.c b/sys/src/9/omap/syscall.c
new file mode 100755
index 000000000..f9f390e2b
--- /dev/null
+++ b/sys/src/9/omap/syscall.c
@@ -0,0 +1,333 @@
+#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;
+
+ 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;
+
+ if(up->procctl == Proc_tracesyscall){
+ up->procctl = Proc_stopme;
+ procctl(up);
+ }
+
+ scallnr = ureg->r0;
+ up->scallnr = scallnr;
+ if(scallnr == RFORK)
+ fpusysrfork(ureg);
+ spllo();
+
+ sp = ureg->sp;
+ up->nerrlab = 0;
+ ret = -1;
+ 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){
+ up->procctl = Proc_stopme;
+ s = splhi();
+ procctl(up);
+ splx(s);
+ }
+
+ 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 /* void* */
+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/omap/trap.c b/sys/src/9/omap/trap.c
new file mode 100755
index 000000000..6b0911e1b
--- /dev/null
+++ b/sys/src/9/omap/trap.c
@@ -0,0 +1,769 @@
+/*
+ * omap3530 traps, exceptions, interrupts, system calls.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include "ureg.h"
+#include "arm.h"
+
+enum {
+ Nirqs = 96,
+ Nvec = 8, /* # of vectors at start of lexception.s */
+ Bi2long = BI2BY * sizeof(long),
+};
+
+extern int notify(Ureg*);
+
+extern int ldrexvalid;
+
+/* omap35x intc (aka mpu_intc) */
+typedef struct Intrregs Intrregs;
+struct Intrregs {
+ /*
+ * the manual inserts "INTCPS_" before each register name;
+ * we'll just assume the prefix.
+ */
+ uchar _pad0[4*4];
+ ulong sysconfig;
+ ulong sysstatus; /* ro */
+ uchar _pad1[0x40 - 0x18];
+ ulong sir_irq; /* ro */
+ ulong sir_fiq; /* ro */
+ ulong control;
+ ulong protection;
+ ulong idle;
+ uchar _pad2[0x60 - 0x54];
+ ulong irq_priority;
+ ulong fiq_priority;
+ ulong threshold;
+ uchar _pad3[0x80 - 0x6c];
+ struct Bits { /* bitmaps */
+ ulong itr; /* ro: pending intrs (no mask) */
+ ulong mir; /* interrupt mask: 1 means masked */
+ ulong mir_clear; /* wo: 1 sets the bit */
+ ulong mir_set; /* wo: 1 clears the bit */
+ ulong isr_set; /* software interrupts */
+ ulong isr_clear; /* wo */
+ ulong pending_irq; /* ro */
+ ulong pending_fiq; /* ro */
+ } bits[3]; /* 3*32 = 96 (Nirqs) */
+ ulong ilr[Nirqs];
+};
+
+enum {
+ /* sysconfig bits */
+ Softreset = 1<<1,
+
+ /* sysstatus bits */
+ Resetdone = 1<<0,
+
+ /* sir_irq/fiq bits */
+ Activeirq = MASK(7),
+
+ /* control bits */
+ Newirqagr = 1<<0,
+
+ /* protection bits */
+ Protection = 1<<0,
+
+ /* irq/fiq_priority bits */
+ Irqpriority = MASK(6),
+
+ /* threshold bits */
+ Prioritythreshold = MASK(8),
+
+ /* ilr bits */
+ Priority = MASK(8) - MASK(2),
+};
+
+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[Nirqs];
+
+/*
+ * Layout at virtual address 0.
+ */
+typedef struct Vpage0 {
+ void (*vectors[Nvec])(void);
+ u32int vtable[Nvec];
+} Vpage0;
+static Vpage0 *vpage0;
+
+uvlong ninterrupt;
+uvlong ninterruptticks;
+int irqtooearly = 1;
+
+static volatile int probing, trapped;
+
+static int
+irqinuse(uint irq)
+{
+ Intrregs *ip = (Intrregs *)PHYSINTC;
+
+ /*
+ * mir registers are odd: a 0 bit means intr unmasked (i.e.,
+ * we've unmasked it because it's in use).
+ */
+ return (ip->bits[irq / Bi2long].mir & (1 << (irq % Bi2long))) == 0;
+}
+
+static void
+intcmask(uint irq)
+{
+ Intrregs *ip = (Intrregs *)PHYSINTC;
+
+ ip->bits[irq / Bi2long].mir_set = 1 << (irq % Bi2long);
+ coherence();
+}
+
+static void
+intcunmask(uint irq)
+{
+ Intrregs *ip = (Intrregs *)PHYSINTC;
+
+ ip->bits[irq / Bi2long].mir_clear = 1 << (irq % Bi2long);
+ coherence();
+}
+
+static void
+intcmaskall(void)
+{
+ int i;
+ Intrregs *ip = (Intrregs *)PHYSINTC;
+
+ for (i = 0; i < 3; i++)
+ ip->bits[i].mir_set = ~0;
+ coherence();
+}
+
+static void
+intcunmaskall(void)
+{
+ int i;
+ Intrregs *ip = (Intrregs *)PHYSINTC;
+
+ for (i = 0; i < 3; i++)
+ ip->bits[i].mir_clear = ~0;
+ coherence();
+}
+
+static void
+intcinvertall(void)
+{
+ int i, s;
+ ulong bits;
+ Intrregs *ip = (Intrregs *)PHYSINTC;
+
+ s = splhi();
+ for (i = 0; i < 3; i++) {
+ bits = ip->bits[i].mir;
+ ip->bits[i].mir_set = ~0; /* mask all */
+ coherence();
+ /* clearing enables only those intrs. that were disabled */
+ ip->bits[i].mir_clear = bits;
+ }
+ coherence();
+ splx(s);
+}
+
+static void
+intrsave(ulong buf[3])
+{
+ int i;
+ Intrregs *ip = (Intrregs *)PHYSINTC;
+
+ for (i = 0; i < nelem(buf); i++)
+ buf[i] = ip->bits[i].mir;
+ coherence();
+}
+
+static void
+intrrestore(ulong buf[3])
+{
+ int i, s;
+ Intrregs *ip = (Intrregs *)PHYSINTC;
+
+ s = splhi();
+ for (i = 0; i < nelem(buf); i++) {
+ ip->bits[i].mir_clear = ~0; /* unmask all */
+ coherence();
+ ip->bits[i].mir_set = buf[i]; /* mask previously disabled */
+ }
+ coherence();
+ splx(s);
+}
+
+/*
+ * set up for exceptions
+ */
+void
+trapinit(void)
+{
+ int i;
+ Intrregs *ip = (Intrregs *)PHYSINTC;
+
+ /* set up the exception vectors */
+ vpage0 = (Vpage0*)HVECTORS;
+ memmove(vpage0->vectors, vectors, sizeof(vpage0->vectors));
+ memmove(vpage0->vtable, vtable, sizeof(vpage0->vtable));
+ cacheuwbinv();
+ l2cacheuwbinv();
+
+ /* set up the stacks for the interrupt modes */
+ setr13(PsrMfiq, m->sfiq);
+ setr13(PsrMirq, m->sirq);
+ setr13(PsrMabt, m->sabt);
+ setr13(PsrMund, m->sund);
+#ifdef HIGH_SECURITY
+ setr13(PsrMmon, m->smon);
+#endif
+ setr13(PsrMsys, m->ssys);
+
+ intcmaskall();
+ ip->control = 0;
+ ip->threshold = Prioritythreshold; /* disable threshold */
+ for (i = 0; i < Nirqs; i++)
+ ip->ilr[i] = 0<<2 | 0; /* all intrs pri 0 & to irq, not fiq */
+ irqtooearly = 0;
+ coherence();
+}
+
+void
+intrsoff(void)
+{
+ Intrregs *ip = (Intrregs *)PHYSINTC;
+
+ intcmaskall();
+ ip->control = Newirqagr; /* dismiss interrupt */
+ coherence();
+}
+
+/*
+ * enable an irq interrupt
+ */
+int
+irqenable(int irq, void (*f)(Ureg*, void*), void* a, char *name)
+{
+ Vctl *v;
+
+ if(irq >= nelem(vctl) || irq < 0)
+ panic("irqenable irq %d", irq);
+
+ if (irqtooearly) {
+ iprint("irqenable for %d %s called too early\n", irq, name);
+ return -1;
+ }
+ if(irqinuse(irq))
+ print("irqenable: %s: irq %d already in use, chaining\n",
+ name, irq);
+ v = malloc(sizeof(Vctl));
+ if (v == nil)
+ panic("irqenable: malloc Vctl");
+ v->f = f;
+ v->a = a;
+ v->name = malloc(strlen(name)+1);
+ if (v->name == nil)
+ panic("irqenable: malloc name");
+ strcpy(v->name, name);
+
+ lock(&vctllock);
+ v->next = vctl[irq];
+ vctl[irq] = v;
+
+ intcunmask(irq);
+ unlock(&vctllock);
+ return 0;
+}
+
+/*
+ * disable an irq interrupt
+ */
+int
+irqdisable(int irq, void (*f)(Ureg*, void*), void* a, char *name)
+{
+ Vctl **vp, *v;
+
+ if(irq >= nelem(vctl) || irq < 0)
+ panic("irqdisable irq %d", irq);
+
+ lock(&vctllock);
+ for(vp = &vctl[irq]; v = *vp; vp = &v->next)
+ if (v->f == f && v->a == a && strcmp(v->name, name) == 0){
+ print("irqdisable: remove %s\n", name);
+ *vp = v->next;
+ free(v);
+ break;
+ }
+
+ if(v == nil)
+ print("irqdisable: irq %d, name %s not enabled\n", irq, name);
+ if(vctl[irq] == nil){
+ print("irqdisable: clear icmr bit %d\n", irq);
+ intcmask(irq);
+ }
+ unlock(&vctllock);
+
+ return 0;
+}
+
+/*
+ * called by trap to handle access faults
+ */
+static void
+faultarm(Ureg *ureg, uintptr va, int user, int read)
+{
+ int n, insyscall;
+ char buf[ERRMAX];
+
+ if(up == nil) {
+ dumpregs(ureg);
+ panic("fault: nil up in faultarm, accessing %#p", va);
+ }
+ insyscall = up->insyscall;
+ up->insyscall = 1;
+ 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;
+}
+
+/*
+ * called by trap to handle interrupts.
+ * returns true iff a clock interrupt, thus maybe reschedule.
+ */
+static int
+irq(Ureg* ureg)
+{
+ int clockintr;
+ uint irqno, handled, t, ticks = perfticks();
+ Intrregs *ip = (Intrregs *)PHYSINTC;
+ Vctl *v;
+ static int nesting, lastirq = -1;
+
+ handled = 0;
+ irqno = ip->sir_irq & Activeirq;
+
+ if (irqno >= 37 && irqno <= 47) /* this is a clock intr? */
+ m->inclockintr++; /* yes, count nesting */
+ lastirq = irqno;
+
+ if (irqno >= nelem(vctl)) {
+ iprint("trap: irq %d >= # vectors (%d)\n", irqno, nelem(vctl));
+ ip->control = Newirqagr; /* dismiss interrupt */
+ return 0;
+ }
+
+ ++nesting;
+ for(v = vctl[irqno]; v != nil; v = v->next)
+ if (v->f) {
+ if (islo())
+ panic("trap: pl0 before trap handler for %s",
+ v->name);
+ v->f(ureg, v->a);
+ if (islo())
+ panic("trap: %s lowered pl", v->name);
+// splhi(); /* in case v->f lowered pl */
+ handled++;
+ }
+ if(!handled) {
+ iprint("unexpected interrupt: irq %d", irqno);
+ switch (irqno) {
+ case 56:
+ case 57:
+ iprint(" (I⁲C)");
+ break;
+ case 83:
+ case 86:
+ case 94:
+ iprint(" (MMC)");
+ break;
+ }
+
+ if(irqno < nelem(vctl)) {
+ intcmask(irqno);
+ iprint(", now masked");
+ }
+ iprint("\n");
+ }
+ t = perfticks();
+ ninterrupt++;
+ if(t < ticks)
+ ninterruptticks += ticks-t;
+ else
+ ninterruptticks += t-ticks;
+ ip->control = Newirqagr; /* dismiss interrupt */
+ coherence();
+
+ --nesting;
+ clockintr = m->inclockintr == 1;
+ if (irqno >= 37 && irqno <= 47)
+ m->inclockintr--;
+ return clockintr;
+}
+
+/*
+ * 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 prgpmcerrs(void);
+
+/*
+ * here on all exceptions other than syscall (SWI)
+ */
+void
+trap(Ureg *ureg)
+{
+ int clockintr, user, x, rv, rem;
+ ulong inst, fsr;
+ uintptr va;
+ char buf[ERRMAX];
+
+ splhi(); /* paranoia */
+ if(up != nil)
+ rem = ((char*)ureg)-up->kstack;
+ else
+ rem = ((char*)ureg)-((char*)m+sizeof(Mach));
+ if(rem < 1024) {
+ iprint("trap: %d stack bytes left, up %#p ureg %#p at pc %#lux\n",
+ rem, up, ureg, ureg->pc);
+ delay(1000);
+ dumpstack();
+ panic("trap: %d stack bytes left, 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);
+ }
+
+ /*
+ * All interrupts/exceptions should be resumed at ureg->pc-4,
+ * except for Data Abort which resumes at ureg->pc-8.
+ */
+ if(ureg->type == (PsrMabt+1))
+ ureg->pc -= 8;
+ else
+ ureg->pc -= 4;
+
+ clockintr = 0; /* if set, may call sched() before return */
+ switch(ureg->type){
+ default:
+ panic("unknown trap; type %#lux, psr mode %#lux", ureg->type,
+ ureg->psr & PsrMask);
+ break;
+ case PsrMirq:
+ ldrexvalid = 0;
+ clockintr = irq(ureg);
+ 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);
+ /* bits 12 and 10 have to be concatenated with status */
+ x = fsrget();
+ fsr = (x>>7) & 0x20 | (x>>6) & 0x10 | x & 0xf;
+ if (probing && !user) {
+ if (trapped++ > 0)
+ panic("trap: recursive probe %#lux", va);
+ ureg->pc += 4; /* continue at next instruction */
+ break;
+ }
+ switch(fsr){
+ default:
+ case 0xa: /* ? was under external abort */
+ panic("unknown data fault, 6b fsr %#lux", fsr);
+ break;
+ case 0x0:
+ panic("vector exception at %#lux", ureg->pc);
+ break;
+ case 0x1: /* alignment fault */
+ case 0x3: /* access flag fault (section) */
+ 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: /* icache maint fault */
+ case 0x6: /* access flag fault (page) */
+ case 0x8: /* precise external abort, non-xlat'n */
+ case 0x28:
+ case 0xc: /* l1 translation, precise ext. abort */
+ case 0x2c:
+ case 0xe: /* l2 translation, precise ext. abort */
+ case 0x2e:
+ case 0x16: /* imprecise ext. abort, non-xlt'n */
+ case 0x36:
+ panic("external abort %#lux pc %#lux addr %#p",
+ fsr, ureg->pc, va);
+ break;
+ case 0x1c: /* l1 translation, precise parity err */
+ case 0x1e: /* l2 translation, precise parity err */
+ case 0x18: /* imprecise parity or ecc err */
+ panic("translation parity error %#lux pc %#lux addr %#p",
+ 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\n",
+ ureg->pc);
+ postnote(up, 1, buf, NDebug);
+ }
+ }else{
+ if (ureg->pc & 3) {
+ iprint("rounding fault pc %#lux down to word\n",
+ ureg->pc);
+ ureg->pc &= ~3;
+ }
+ 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 && clockintr){
+ ldrexvalid = 0;
+ sched(); /* can cause more traps */
+ splhi();
+ }
+
+ if(user){
+ if(up->procctl || up->nnote)
+ notify(ureg);
+ kexit(ureg);
+ }
+}
+
+/*
+ * 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);
+}
+
+static void
+dumpstackwithureg(Ureg *ureg)
+{
+ int x;
+ uintptr l, v, i, estack;
+ char *s;
+
+ dumpregs(ureg);
+ if((s = getconf("*nodumpstack")) != nil && strcmp(s, "0") != 0){
+ iprint("dumpstack disabled\n");
+ return;
+ }
+ iprint("dumpstack\n");
+
+ x = 0;
+ x += iprint("ktrace /kernel/path %#.8lux %#.8lux %#.8lux # pc, sp, link\n",
+ ureg->pc, ureg->sp, ureg->r14);
+ delay(20);
+ i = 0;
+ if(up
+ && (uintptr)&l >= (uintptr)up->kstack
+ && (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
+ return;
+ x += iprint("estackx %p\n", estack);
+
+ for(l = (uintptr)&l; l < estack; l += sizeof(uintptr)){
+ v = *(uintptr*)l;
+ if((KTZERO < v && v < (uintptr)etext) || estack-l < 32){
+ x += iprint("%.8p ", v);
+ delay(20);
+ i++;
+ }
+ if(i == 8){
+ i = 0;
+ x += iprint("\n");
+ delay(20);
+ }
+ }
+ if(i)
+ iprint("\n");
+}
+
+void
+dumpstack(void)
+{
+ callwithureg(dumpstackwithureg);
+}
+
+/*
+ * dump system control coprocessor registers
+ */
+static void
+dumpscr(void)
+{
+ iprint("0:\t%#8.8ux id\n", cpidget());
+ iprint("\t%8.8#ux ct\n", cpctget());
+ iprint("1:\t%#8.8ux control\n", controlget());
+ iprint("2:\t%#8.8ux ttb\n", ttbget());
+ iprint("3:\t%#8.8ux dac\n", dacget());
+ iprint("4:\t(reserved)\n");
+ iprint("5:\t%#8.8ux fsr\n", fsrget());
+ iprint("6:\t%#8.8ux far\n", farget());
+ iprint("7:\twrite-only cache\n");
+ iprint("8:\twrite-only tlb\n");
+ iprint("13:\t%#8.8ux pid\n", pidget());
+ delay(10);
+}
+
+/*
+ * dump general registers
+ */
+static void
+dumpgpr(Ureg* ureg)
+{
+ if(up != nil)
+ iprint("cpu%d: registers for %s %lud\n",
+ m->machno, up->text, up->pid);
+ else
+ iprint("cpu%d: registers for kernel\n", m->machno);
+
+ delay(20);
+ iprint("%#8.8lux\tr0\n", ureg->r0);
+ iprint("%#8.8lux\tr1\n", ureg->r1);
+ iprint("%#8.8lux\tr2\n", ureg->r2);
+ delay(20);
+ iprint("%#8.8lux\tr3\n", ureg->r3);
+ iprint("%#8.8lux\tr4\n", ureg->r4);
+ iprint("%#8.8lux\tr5\n", ureg->r5);
+ delay(20);
+ iprint("%#8.8lux\tr6\n", ureg->r6);
+ iprint("%#8.8lux\tr7\n", ureg->r7);
+ iprint("%#8.8lux\tr8\n", ureg->r8);
+ delay(20);
+ iprint("%#8.8lux\tr9 (up)\n", ureg->r9);
+ iprint("%#8.8lux\tr10 (m)\n", ureg->r10);
+ iprint("%#8.8lux\tr11 (loader temporary)\n", ureg->r11);
+ iprint("%#8.8lux\tr12 (SB)\n", ureg->r12);
+ delay(20);
+ iprint("%#8.8lux\tr13 (sp)\n", ureg->r13);
+ iprint("%#8.8lux\tr14 (link)\n", ureg->r14);
+ iprint("%#8.8lux\tr15 (pc)\n", ureg->pc);
+ delay(20);
+ iprint("%10.10lud\ttype\n", ureg->type);
+ iprint("%#8.8lux\tpsr\n", ureg->psr);
+ delay(20);
+}
+
+void
+dumpregs(Ureg* ureg)
+{
+ dumpgpr(ureg);
+ dumpscr();
+}
+
+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/omap/uarti8250.c b/sys/src/9/omap/uarti8250.c
new file mode 100755
index 000000000..7642a2685
--- /dev/null
+++ b/sys/src/9/omap/uarti8250.c
@@ -0,0 +1,850 @@
+/*
+ * omap35 8250-like UART
+ *
+ * we ignore the first 2 uarts on the omap35 (see below) and use the
+ * third one but call it 0.
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+enum { /* registers */
+ Rbr = 0, /* Receiver Buffer (RO) */
+ Thr = 0, /* Transmitter Holding (WO) */
+ Ier = 1, /* Interrupt Enable */
+ Iir = 2, /* Interrupt Identification (RO) */
+ Fcr = 2, /* FIFO Control (WO) */
+ Lcr = 3, /* Line Control */
+ Mcr = 4, /* Modem Control */
+ Lsr = 5, /* Line Status */
+ Msr = 6, /* Modem Status */
+ Scr = 7, /* Scratch Pad */
+ Mdr = 8, /* Mode Def'n (omap rw) */
+// Usr = 31, /* Uart Status Register; missing in omap? */
+ Dll = 0, /* Divisor Latch LSB */
+ Dlm = 1, /* Divisor Latch MSB */
+};
+
+enum { /* Usr */
+ Busy = 0x01,
+};
+
+enum { /* Ier */
+ Erda = 0x01, /* Enable Received Data Available */
+ Ethre = 0x02, /* Enable Thr Empty */
+ Erls = 0x04, /* Enable Receiver Line Status */
+ Ems = 0x08, /* Enable Modem Status */
+};
+
+enum { /* Iir */
+ Ims = 0x00, /* Ms interrupt */
+ Ip = 0x01, /* Interrupt Pending (not) */
+ Ithre = 0x02, /* Thr Empty */
+ Irda = 0x04, /* Received Data Available */
+ Irls = 0x06, /* Receiver Line Status */
+ Ictoi = 0x0C, /* Character Time-out Indication */
+ IirMASK = 0x3F,
+ Ifena = 0xC0, /* FIFOs enabled */
+};
+
+enum { /* Fcr */
+ FIFOena = 0x01, /* FIFO enable */
+ FIFOrclr = 0x02, /* clear Rx FIFO */
+ FIFOtclr = 0x04, /* clear Tx FIFO */
+// FIFOdma = 0x08,
+ FIFO1 = 0x00, /* Rx FIFO trigger level 1 byte */
+ FIFO4 = 0x40, /* 4 bytes */
+ FIFO8 = 0x80, /* 8 bytes */
+ FIFO14 = 0xC0, /* 14 bytes */
+};
+
+enum { /* Lcr */
+ Wls5 = 0x00, /* Word Length Select 5 bits/byte */
+ Wls6 = 0x01, /* 6 bits/byte */
+ Wls7 = 0x02, /* 7 bits/byte */
+ Wls8 = 0x03, /* 8 bits/byte */
+ WlsMASK = 0x03,
+ Stb = 0x04, /* 2 stop bits */
+ Pen = 0x08, /* Parity Enable */
+ Eps = 0x10, /* Even Parity Select */
+ Stp = 0x20, /* Stick Parity */
+ Brk = 0x40, /* Break */
+ Dlab = 0x80, /* Divisor Latch Access Bit */
+};
+
+enum { /* Mcr */
+ Dtr = 0x01, /* Data Terminal Ready */
+ Rts = 0x02, /* Ready To Send */
+ Out1 = 0x04, /* no longer in use */
+// Ie = 0x08, /* IRQ Enable (cd_sts_ch on omap) */
+ Dm = 0x10, /* Diagnostic Mode loopback */
+};
+
+enum { /* Lsr */
+ Dr = 0x01, /* Data Ready */
+ Oe = 0x02, /* Overrun Error */
+ Pe = 0x04, /* Parity Error */
+ Fe = 0x08, /* Framing Error */
+ Bi = 0x10, /* Break Interrupt */
+ Thre = 0x20, /* Thr Empty */
+ Temt = 0x40, /* Transmitter Empty */
+ FIFOerr = 0x80, /* error in receiver FIFO */
+};
+
+enum { /* Msr */
+ Dcts = 0x01, /* Delta Cts */
+ Ddsr = 0x02, /* Delta Dsr */
+ Teri = 0x04, /* Trailing Edge of Ri */
+ Ddcd = 0x08, /* Delta Dcd */
+ Cts = 0x10, /* Clear To Send */
+ Dsr = 0x20, /* Data Set Ready */
+ Ri = 0x40, /* Ring Indicator */
+ Dcd = 0x80, /* Carrier Detect */
+};
+
+enum { /* Mdr */
+ Modemask = 7,
+ Modeuart = 0,
+};
+
+
+typedef struct Ctlr {
+ u32int* io;
+ int irq;
+ int tbdf;
+ int iena;
+ int poll;
+
+ uchar sticky[Scr+1];
+
+ Lock;
+ int hasfifo;
+ int checkfifo;
+ int fena;
+} Ctlr;
+
+extern PhysUart i8250physuart;
+
+static Ctlr i8250ctlr[] = {
+{ .io = (u32int*)PHYSCONS, /* UART3 in TI terminology */
+ .irq = 74,
+ .tbdf = -1,
+ .poll = 0, },
+
+/* these exist, but I don't think they're connected to anything external */
+//{ .io = (u32int*)PHYSUART0,
+// .irq = 72,
+// .tbdf = -1,
+// .poll = 0, },
+//
+//{ .io = (u32int*)PHYSUART1,
+// .irq = 73,
+// .tbdf = -1,
+// .poll = 0, },
+};
+
+static Uart i8250uart[] = {
+{ .regs = &i8250ctlr[0], /* not [2] */
+ .name = "COM3",
+ .freq = 3686000, /* Not used, we use the global i8250freq */
+ .phys = &i8250physuart,
+ .console= 1,
+ .next = nil, },
+
+/* these exist, but I don't think they're connected to anything external */
+//{ .regs = &i8250ctlr[0],
+// .name = "COM1",
+// .freq = 3686000, /* Not used, we use the global i8250freq */
+// .phys = &i8250physuart,
+// .special= 0,
+// .next = &i8250uart[1], },
+//
+//{ .regs = &i8250ctlr[1],
+// .name = "COM2",
+// .freq = 3686000, /* Not used, we use the global i8250freq */
+// .phys = &i8250physuart,
+// .special= 0,
+// .next = &i8250uart[2], },
+};
+
+#define csr8r(c, r) ((c)->io[r])
+#define csr8w(c, r, v) ((c)->io[r] = (c)->sticky[r] | (v), coherence())
+#define csr8o(c, r, v) ((c)->io[r] = (v), coherence())
+
+static long
+i8250status(Uart* uart, void* buf, long n, long offset)
+{
+ char *p;
+ Ctlr *ctlr;
+ uchar ier, lcr, mcr, msr;
+
+ ctlr = uart->regs;
+ p = malloc(READSTR);
+ mcr = ctlr->sticky[Mcr];
+ msr = csr8r(ctlr, Msr);
+ ier = ctlr->sticky[Ier];
+ lcr = ctlr->sticky[Lcr];
+ snprint(p, READSTR,
+ "b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d\n"
+ "dev(%d) type(%d) framing(%d) overruns(%d) "
+ "berr(%d) serr(%d)%s%s%s%s\n",
+
+ uart->baud,
+ uart->hup_dcd,
+ (msr & Dsr) != 0,
+ uart->hup_dsr,
+ (lcr & WlsMASK) + 5,
+ (ier & Ems) != 0,
+ (lcr & Pen) ? ((lcr & Eps) ? 'e': 'o'): 'n',
+ (mcr & Rts) != 0,
+ (lcr & Stb) ? 2: 1,
+ ctlr->fena,
+
+ uart->dev,
+ uart->type,
+ uart->ferr,
+ uart->oerr,
+ uart->berr,
+ uart->serr,
+ (msr & Cts) ? " cts": "",
+ (msr & Dsr) ? " dsr": "",
+ (msr & Dcd) ? " dcd": "",
+ (msr & Ri) ? " ring": ""
+ );
+ n = readstr(offset, buf, n, p);
+ free(p);
+
+ return n;
+}
+
+static void
+i8250fifo(Uart* uart, int level)
+{
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ if(ctlr->hasfifo == 0)
+ return;
+
+ /*
+ * Changing the FIFOena bit in Fcr flushes data
+ * from both receive and transmit FIFOs; there's
+ * no easy way to guarantee not losing data on
+ * the receive side, but it's possible to wait until
+ * the transmitter is really empty.
+ */
+ ilock(ctlr);
+ while(!(csr8r(ctlr, Lsr) & Temt))
+ ;
+
+ /*
+ * Set the trigger level, default is the max.
+ * value.
+ * Some UARTs require FIFOena to be set before
+ * other bits can take effect, so set it twice.
+ */
+ ctlr->fena = level;
+ switch(level){
+ case 0:
+ break;
+ case 1:
+ level = FIFO1|FIFOena;
+ break;
+ case 4:
+ level = FIFO4|FIFOena;
+ break;
+ case 8:
+ level = FIFO8|FIFOena;
+ break;
+ default:
+ level = FIFO14|FIFOena;
+ break;
+ }
+ csr8w(ctlr, Fcr, level);
+ csr8w(ctlr, Fcr, level);
+ iunlock(ctlr);
+}
+
+static void
+i8250dtr(Uart* uart, int on)
+{
+ Ctlr *ctlr;
+
+ /*
+ * Toggle DTR.
+ */
+ ctlr = uart->regs;
+ if(on)
+ ctlr->sticky[Mcr] |= Dtr;
+ else
+ ctlr->sticky[Mcr] &= ~Dtr;
+ csr8w(ctlr, Mcr, 0);
+}
+
+static void
+i8250rts(Uart* uart, int on)
+{
+ Ctlr *ctlr;
+
+ /*
+ * Toggle RTS.
+ */
+ ctlr = uart->regs;
+ if(on)
+ ctlr->sticky[Mcr] |= Rts;
+ else
+ ctlr->sticky[Mcr] &= ~Rts;
+ csr8w(ctlr, Mcr, 0);
+}
+
+static void
+i8250modemctl(Uart* uart, int on)
+{
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ ilock(&uart->tlock);
+ if(on){
+ ctlr->sticky[Ier] |= Ems;
+ csr8w(ctlr, Ier, 0);
+ uart->modem = 1;
+ uart->cts = csr8r(ctlr, Msr) & Cts;
+ }
+ else{
+ ctlr->sticky[Ier] &= ~Ems;
+ csr8w(ctlr, Ier, 0);
+ uart->modem = 0;
+ uart->cts = 1;
+ }
+ iunlock(&uart->tlock);
+
+ /* modem needs fifo */
+ (*uart->phys->fifo)(uart, on);
+}
+
+static int
+i8250parity(Uart* uart, int parity)
+{
+ int lcr;
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ lcr = ctlr->sticky[Lcr] & ~(Eps|Pen);
+
+ switch(parity){
+ case 'e':
+ lcr |= Eps|Pen;
+ break;
+ case 'o':
+ lcr |= Pen;
+ break;
+ case 'n':
+ break;
+ default:
+ return -1;
+ }
+ ctlr->sticky[Lcr] = lcr;
+ csr8w(ctlr, Lcr, 0);
+
+ uart->parity = parity;
+
+ return 0;
+}
+
+static int
+i8250stop(Uart* uart, int stop)
+{
+ int lcr;
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ lcr = ctlr->sticky[Lcr] & ~Stb;
+
+ switch(stop){
+ case 1:
+ break;
+ case 2:
+ lcr |= Stb;
+ break;
+ default:
+ return -1;
+ }
+ ctlr->sticky[Lcr] = lcr;
+ csr8w(ctlr, Lcr, 0);
+
+ uart->stop = stop;
+
+ return 0;
+}
+
+static int
+i8250bits(Uart* uart, int bits)
+{
+ int lcr;
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ lcr = ctlr->sticky[Lcr] & ~WlsMASK;
+
+ switch(bits){
+ case 5:
+ lcr |= Wls5;
+ break;
+ case 6:
+ lcr |= Wls6;
+ break;
+ case 7:
+ lcr |= Wls7;
+ break;
+ case 8:
+ lcr |= Wls8;
+ break;
+ default:
+ return -1;
+ }
+ ctlr->sticky[Lcr] = lcr;
+ csr8w(ctlr, Lcr, 0);
+
+ uart->bits = bits;
+
+ return 0;
+}
+
+static int
+i8250baud(Uart* uart, int baud)
+{
+#ifdef notdef /* don't change the speed */
+ ulong bgc;
+ Ctlr *ctlr;
+ extern int i8250freq; /* In the config file */
+
+ /*
+ * Set the Baud rate by calculating and setting the Baud rate
+ * Generator Constant. This will work with fairly non-standard
+ * Baud rates.
+ */
+ if(i8250freq == 0 || baud <= 0)
+ return -1;
+ bgc = (i8250freq+8*baud-1)/(16*baud);
+
+ ctlr = uart->regs;
+ while(csr8r(ctlr, Usr) & Busy)
+ delay(1);
+ csr8w(ctlr, Lcr, Dlab); /* begin kludge */
+ csr8o(ctlr, Dlm, bgc>>8);
+ csr8o(ctlr, Dll, bgc);
+ csr8w(ctlr, Lcr, 0);
+#endif
+ uart->baud = baud;
+ return 0;
+}
+
+static void
+i8250break(Uart* uart, int ms)
+{
+ Ctlr *ctlr;
+
+ if (up == nil)
+ panic("i8250break: nil up");
+ /*
+ * Send a break.
+ */
+ if(ms <= 0)
+ ms = 200;
+
+ ctlr = uart->regs;
+ csr8w(ctlr, Lcr, Brk);
+ tsleep(&up->sleep, return0, 0, ms);
+ csr8w(ctlr, Lcr, 0);
+}
+
+static void
+emptyoutstage(Uart *uart, int n)
+{
+ _uartputs((char *)uart->op, n);
+ uart->op = uart->oe = uart->ostage;
+}
+
+static void
+i8250kick(Uart* uart)
+{
+ int i;
+ Ctlr *ctlr;
+
+ if(/* uart->cts == 0 || */ uart->blocked)
+ return;
+
+ if(!normalprint) { /* early */
+ if (uart->op < uart->oe)
+ emptyoutstage(uart, uart->oe - uart->op);
+ while ((i = uartstageoutput(uart)) > 0)
+ emptyoutstage(uart, i);
+ return;
+ }
+
+ /* nothing more to send? then disable xmit intr */
+ ctlr = uart->regs;
+ if (uart->op >= uart->oe && qlen(uart->oq) == 0 &&
+ csr8r(ctlr, Lsr) & Temt) {
+ ctlr->sticky[Ier] &= ~Ethre;
+ csr8w(ctlr, Ier, 0);
+ return;
+ }
+
+ /*
+ * 128 here is an arbitrary limit to make sure
+ * we don't stay in this loop too long. If the
+ * chip's output queue is longer than 128, too
+ * bad -- presotto
+ */
+ for(i = 0; i < 128; i++){
+ if(!(csr8r(ctlr, Lsr) & Thre))
+ break;
+ if(uart->op >= uart->oe && uartstageoutput(uart) == 0)
+ break;
+ csr8o(ctlr, Thr, *uart->op++); /* start tx */
+ ctlr->sticky[Ier] |= Ethre;
+ csr8w(ctlr, Ier, 0); /* intr when done */
+ }
+}
+
+void
+serialkick(void)
+{
+ uartkick(&i8250uart[CONSOLE]);
+}
+
+static void
+i8250interrupt(Ureg*, void* arg)
+{
+ Ctlr *ctlr;
+ Uart *uart;
+ int iir, lsr, old, r;
+
+ uart = arg;
+ ctlr = uart->regs;
+ for(iir = csr8r(ctlr, Iir); !(iir & Ip); iir = csr8r(ctlr, Iir)){
+ switch(iir & IirMASK){
+ case Ims: /* Ms interrupt */
+ r = csr8r(ctlr, Msr);
+ if(r & Dcts){
+ ilock(&uart->tlock);
+ old = uart->cts;
+ uart->cts = r & Cts;
+ if(old == 0 && uart->cts)
+ uart->ctsbackoff = 2;
+ iunlock(&uart->tlock);
+ }
+ if(r & Ddsr){
+ old = r & Dsr;
+ if(uart->hup_dsr && uart->dsr && !old)
+ uart->dohup = 1;
+ uart->dsr = old;
+ }
+ if(r & Ddcd){
+ old = r & Dcd;
+ if(uart->hup_dcd && uart->dcd && !old)
+ uart->dohup = 1;
+ uart->dcd = old;
+ }
+ break;
+ case Ithre: /* Thr Empty */
+ uartkick(uart);
+ break;
+ case Irda: /* Received Data Available */
+ case Irls: /* Receiver Line Status */
+ case Ictoi: /* Character Time-out Indication */
+ /*
+ * Consume any received data.
+ * If the received byte came in with a break,
+ * parity or framing error, throw it away;
+ * overrun is an indication that something has
+ * already been tossed.
+ */
+ while((lsr = csr8r(ctlr, Lsr)) & Dr){
+ if(lsr & (FIFOerr|Oe))
+ uart->oerr++;
+ if(lsr & Pe)
+ uart->perr++;
+ if(lsr & Fe)
+ uart->ferr++;
+ r = csr8r(ctlr, Rbr);
+ if(!(lsr & (Bi|Fe|Pe)))
+ uartrecv(uart, r);
+ }
+ break;
+
+ default:
+ iprint("weird uart interrupt type %#2.2uX\n", iir);
+ break;
+ }
+ }
+}
+
+static void
+i8250disable(Uart* uart)
+{
+ Ctlr *ctlr;
+
+ /*
+ * Turn off DTR and RTS, disable interrupts and fifos.
+ */
+ (*uart->phys->dtr)(uart, 0);
+ (*uart->phys->rts)(uart, 0);
+ (*uart->phys->fifo)(uart, 0);
+
+ ctlr = uart->regs;
+ ctlr->sticky[Ier] = 0;
+ csr8w(ctlr, Ier, 0);
+
+ if(ctlr->iena != 0){
+ if(irqdisable(ctlr->irq, i8250interrupt, uart, uart->name) == 0)
+ ctlr->iena = 0;
+ }
+}
+
+static void
+i8250enable(Uart* uart, int ie)
+{
+ int mode;
+ Ctlr *ctlr;
+
+ if (up == nil)
+ return; /* too soon */
+
+ ctlr = uart->regs;
+
+ /* omap only: set uart/irda/cir mode to uart */
+ mode = csr8r(ctlr, Mdr);
+ csr8o(ctlr, Mdr, (mode & ~Modemask) | Modeuart);
+
+ ctlr->sticky[Lcr] = Wls8; /* no parity */
+ csr8w(ctlr, Lcr, 0);
+
+ /*
+ * Check if there is a FIFO.
+ * Changing the FIFOena bit in Fcr flushes data
+ * from both receive and transmit FIFOs; there's
+ * no easy way to guarantee not losing data on
+ * the receive side, but it's possible to wait until
+ * the transmitter is really empty.
+ * Also, reading the Iir outwith i8250interrupt()
+ * can be dangerous, but this should only happen
+ * once, before interrupts are enabled.
+ */
+ ilock(ctlr);
+ if(!ctlr->checkfifo){
+ /*
+ * Wait until the transmitter is really empty.
+ */
+ while(!(csr8r(ctlr, Lsr) & Temt))
+ ;
+ csr8w(ctlr, Fcr, FIFOena);
+ if(csr8r(ctlr, Iir) & Ifena)
+ ctlr->hasfifo = 1;
+ csr8w(ctlr, Fcr, 0);
+ ctlr->checkfifo = 1;
+ }
+ iunlock(ctlr);
+
+ /*
+ * Enable interrupts and turn on DTR and RTS.
+ * Be careful if this is called to set up a polled serial line
+ * early on not to try to enable interrupts as interrupt-
+ * -enabling mechanisms might not be set up yet.
+ */
+ if(ie){
+ if(ctlr->iena == 0 && !ctlr->poll){
+ irqenable(ctlr->irq, i8250interrupt, uart, uart->name);
+ ctlr->iena = 1;
+ }
+ ctlr->sticky[Ier] = Erda;
+// ctlr->sticky[Mcr] |= Ie; /* not on omap */
+ ctlr->sticky[Mcr] = 0;
+ }
+ else{
+ ctlr->sticky[Ier] = 0;
+ ctlr->sticky[Mcr] = 0;
+ }
+ csr8w(ctlr, Ier, 0);
+ csr8w(ctlr, Mcr, 0);
+
+ (*uart->phys->dtr)(uart, 1);
+ (*uart->phys->rts)(uart, 1);
+
+ /*
+ * During startup, the i8259 interrupt controller is reset.
+ * This may result in a lost interrupt from the i8250 uart.
+ * The i8250 thinks the interrupt is still outstanding and does not
+ * generate any further interrupts. The workaround is to call the
+ * interrupt handler to clear any pending interrupt events.
+ * Note: this must be done after setting Ier.
+ */
+ if(ie)
+ i8250interrupt(nil, uart);
+}
+
+static Uart*
+i8250pnp(void)
+{
+ return i8250uart;
+}
+
+static int
+i8250getc(Uart* uart)
+{
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ while(!(csr8r(ctlr, Lsr) & Dr))
+ delay(1);
+ return csr8r(ctlr, Rbr);
+}
+
+static void
+i8250putc(Uart* uart, int c)
+{
+ int i;
+ Ctlr *ctlr;
+
+ if (!normalprint) { /* too early; use brute force */
+ int s = splhi();
+
+ while (!(((ulong *)PHYSCONS)[Lsr] & Thre))
+ ;
+ ((ulong *)PHYSCONS)[Thr] = c;
+ coherence();
+ splx(s);
+ return;
+ }
+
+ ctlr = uart->regs;
+ for(i = 0; !(csr8r(ctlr, Lsr) & Thre) && i < 128; i++)
+ delay(1);
+ csr8o(ctlr, Thr, (uchar)c);
+ for(i = 0; !(csr8r(ctlr, Lsr) & Thre) && i < 128; i++)
+ delay(1);
+}
+
+void
+serialputc(int c)
+{
+ i8250putc(&i8250uart[CONSOLE], c);
+}
+
+void
+serialputs(char* s, int n)
+{
+ _uartputs(s, n);
+}
+
+#ifdef notdef
+static void
+i8250poll(Uart* uart)
+{
+ Ctlr *ctlr;
+
+ /*
+ * If PhysUart has a non-nil .poll member, this
+ * routine will be called from the uartclock timer.
+ * If the Ctlr .poll member is non-zero, when the
+ * Uart is enabled interrupts will not be enabled
+ * and the result is polled input and output.
+ * Not very useful here, but ports to new hardware
+ * or simulators can use this to get serial I/O
+ * without setting up the interrupt mechanism.
+ */
+ ctlr = uart->regs;
+ if(ctlr->iena || !ctlr->poll)
+ return;
+ i8250interrupt(nil, uart);
+}
+#endif
+
+PhysUart i8250physuart = {
+ .name = "i8250",
+ .pnp = i8250pnp,
+ .enable = i8250enable,
+ .disable = i8250disable,
+ .kick = i8250kick,
+ .dobreak = i8250break,
+ .baud = i8250baud,
+ .bits = i8250bits,
+ .stop = i8250stop,
+ .parity = i8250parity,
+ .modemctl = i8250modemctl,
+ .rts = i8250rts,
+ .dtr = i8250dtr,
+ .status = i8250status,
+ .fifo = i8250fifo,
+ .getc = i8250getc,
+ .putc = i8250putc,
+// .poll = i8250poll, /* only in 9k, not 9 */
+};
+
+static void
+i8250dumpregs(Ctlr* ctlr)
+{
+ int dlm, dll;
+ int _uartprint(char*, ...);
+
+ csr8w(ctlr, Lcr, Dlab);
+ dlm = csr8r(ctlr, Dlm);
+ dll = csr8r(ctlr, Dll);
+ csr8w(ctlr, Lcr, 0);
+
+ _uartprint("dlm %#ux dll %#ux\n", dlm, dll);
+}
+
+Uart* uartenable(Uart *p);
+
+/* must call this from a process's context */
+int
+i8250console(void)
+{
+ Uart *uart = &i8250uart[CONSOLE];
+
+ if (up == nil)
+ return -1; /* too early */
+
+ if(uartenable(uart) != nil /* && uart->console */){
+ // iprint("i8250console: enabling console uart\n");
+ kbdq = uart->iq;
+ serialoq = uart->oq;
+ uart->putc = kbdcr2nl;
+ uart->opens++;
+ consuart = uart;
+ }
+ uartctl(uart, "b115200 l8 pn r1 s1 i1");
+ return 0;
+}
+
+void
+_uartputs(char* s, int n)
+{
+ char *e;
+
+ for(e = s+n; s < e; s++){
+ if(*s == '\n')
+ i8250putc(&i8250uart[CONSOLE], '\r');
+ i8250putc(&i8250uart[CONSOLE], *s);
+ }
+}
+
+int
+_uartprint(char* fmt, ...)
+{
+ int n;
+ va_list arg;
+ char buf[PRINTSIZE];
+
+ va_start(arg, fmt);
+ n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
+ va_end(arg);
+ _uartputs(buf, n);
+
+ return n;
+}
diff --git a/sys/src/9/omap/ucalloc.c b/sys/src/9/omap/ucalloc.c
new file mode 100755
index 000000000..37e8564c0
--- /dev/null
+++ b/sys/src/9/omap/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/omap/ucallocb.c b/sys/src/9/omap/ucallocb.c
new file mode 100755
index 000000000..954c5d96a
--- /dev/null
+++ b/sys/src/9/omap/ucallocb.c
@@ -0,0 +1,152 @@
+/*
+ * allocate Blocks from uncached memory
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+
+enum
+{
+ Hdrspc = 64, /* leave room for high-level headers */
+ Bdead = 0x51494F42, /* "QIOB" */
+};
+
+struct
+{
+ Lock;
+ ulong bytes;
+} ucialloc;
+
+static Block*
+_ucallocb(int size)
+{
+ Block *b;
+ ulong addr;
+
+ if((b = ucalloc(sizeof(Block)+size+Hdrspc)) == nil)
+ return nil;
+
+ b->next = nil;
+ b->list = nil;
+ b->free = 0;
+ b->flag = 0;
+ b->ref = 0;
+ _xinc(&b->ref);
+
+ /* align start of data portion by rounding up */
+ addr = (ulong)b;
+ addr = ROUND(addr + sizeof(Block), BLOCKALIGN);
+ b->base = (uchar*)addr;
+
+ /* align end of data portion by rounding down */
+ b->lim = ((uchar*)b) + msize(b);
+ addr = (ulong)(b->lim);
+ addr = addr & ~(BLOCKALIGN-1);
+ b->lim = (uchar*)addr;
+
+ /* leave sluff at beginning for added headers */
+ b->rp = b->lim - ROUND(size, BLOCKALIGN);
+ if(b->rp < b->base)
+ panic("_ucallocb");
+ b->wp = b->rp;
+
+ return b;
+}
+
+Block*
+ucallocb(int size)
+{
+ Block *b;
+
+ /*
+ * Check in a process and wait until successful.
+ * Can still error out of here, though.
+ */
+ if(up == nil)
+ panic("ucallocb without up: %#p", getcallerpc(&size));
+ if((b = _ucallocb(size)) == nil)
+ panic("ucallocb: no memory for %d bytes", size);
+ setmalloctag(b, getcallerpc(&size));
+
+ return b;
+}
+
+Block*
+uciallocb(int size)
+{
+ Block *b;
+ static int m1, m2, mp;
+
+ if(0 && ucialloc.bytes > conf.ialloc){
+ if((m1++%10000)==0){
+ if(mp++ > 1000){
+ active.exiting = 1;
+ exit(0);
+ }
+ iprint("uciallocb: limited %lud/%lud\n",
+ ucialloc.bytes, conf.ialloc);
+ }
+ return nil;
+ }
+
+ if((b = _ucallocb(size)) == nil){
+ if(0 && (m2++%10000)==0){
+ if(mp++ > 1000){
+ active.exiting = 1;
+ exit(0);
+ }
+ iprint("uciallocb: no memory %lud/%lud\n",
+ ucialloc.bytes, conf.ialloc);
+ }
+ return nil;
+ }
+ setmalloctag(b, getcallerpc(&size));
+ b->flag = BINTR;
+
+ ilock(&ucialloc);
+ ucialloc.bytes += b->lim - b->base;
+ iunlock(&ucialloc);
+
+ return b;
+}
+
+void
+ucfreeb(Block *b)
+{
+ void *dead = (void*)Bdead;
+ long ref;
+
+ if(b == nil || (ref = _xdec(&b->ref)) > 0)
+ return;
+
+ if(ref < 0){
+ dumpstack();
+ panic("ucfreeb: ref %ld; caller pc %#p", ref, getcallerpc(&b));
+ }
+
+ /*
+ * drivers which perform non cache coherent DMA manage their own buffer
+ * pool of uncached buffers and provide their own free routine.
+ */
+ if(b->free) {
+ b->free(b);
+ return;
+ }
+ if(b->flag & BINTR) {
+ ilock(&ucialloc);
+ ucialloc.bytes -= b->lim - b->base;
+ iunlock(&ucialloc);
+ }
+
+ /* poison the block in case someone is still holding onto it */
+ b->next = dead;
+ b->rp = dead;
+ b->wp = dead;
+ b->lim = dead;
+ b->base = dead;
+
+ ucfree(b);
+}
diff --git a/sys/src/9/omap/uncached.h b/sys/src/9/omap/uncached.h
new file mode 100755
index 000000000..9fffd31fc
--- /dev/null
+++ b/sys/src/9/omap/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/omap/usbehci.h b/sys/src/9/omap/usbehci.h
new file mode 100755
index 000000000..279b30a09
--- /dev/null
+++ b/sys/src/9/omap/usbehci.h
@@ -0,0 +1,243 @@
+/* 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 */
+ CLcontrol = 4, /* legacy support control & status */
+
+ /* 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;
+};
+
+/*
+ * OMAP3-specific stuff
+ */
+
+enum {
+ /* hostconfig bits */
+ P1ulpi_bypass = 1<<0, /* utmi if set; else ulpi */
+};
+
+/*
+ * 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[3]; /* 44 Port status and control, one per port */
+
+ /* defined for omap35 ehci at least */
+ uchar _pad0[0x80 - 0x50];
+ ulong insn[6]; /* implementation-specific */
+};
+
+typedef struct Uhh Uhh;
+struct Uhh {
+ ulong revision; /* ro */
+ uchar _pad0[0x10-0x4];
+ ulong sysconfig;
+ ulong sysstatus; /* ro */
+
+ uchar _pad1[0x40-0x18];
+ ulong hostconfig;
+ ulong debug_csr;
+};
+
+extern Ecapio *ehcidebugcapio;
+extern int ehcidebugport;
+
+extern int ehcidebug;
+
+void ehcilinkage(Hci *hp);
+void ehcimeminit(Ctlr *ctlr);
+void ehcirun(Ctlr *ctlr, int on);
diff --git a/sys/src/9/omap/usbehciomap.c b/sys/src/9/omap/usbehciomap.c
new file mode 100755
index 000000000..a1592bb54
--- /dev/null
+++ b/sys/src/9/omap/usbehciomap.c
@@ -0,0 +1,224 @@
+/*
+ * OMAP3-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"
+
+static Ctlr* ctlrs[Nhcis];
+
+static void
+ehcireset(Ctlr *ctlr)
+{
+ Eopio *opio;
+ int i;
+
+ ilock(ctlr);
+ dprint("ehci %#p reset\n", ctlr->capio);
+ opio = ctlr->opio;
+
+ /*
+ * Turn off legacy mode. Some controllers won't
+ * interrupt us as expected otherwise.
+ */
+ ehcirun(ctlr, 0);
+
+ /* 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;
+ }
+
+ if(ehcidebugcapio != ctlr->capio){
+ opio->cmd |= Chcreset; /* controller reset */
+ coherence();
+ for(i = 0; i < 100; i++){
+ if((opio->cmd & Chcreset) == 0)
+ break;
+ delay(1);
+ }
+ if(i == 100)
+ print("ehci %#p controller reset timed out\n", ctlr->capio);
+ }
+
+ /* requesting more interrupts per µframe may miss interrupts */
+ opio->cmd |= Citc8; /* 1 intr. per ms */
+ coherence();
+ 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);
+ }
+ coherence();
+ dprint("ehci: %d frames\n", ctlr->nframes);
+ iunlock(ctlr);
+}
+
+static void
+setdebug(Hci*, int d)
+{
+ ehcidebug = d;
+}
+
+static void
+shutdown(Hci *hp)
+{
+ int i;
+ Ctlr *ctlr;
+ Eopio *opio;
+
+ ctlr = hp->aux;
+ ilock(ctlr);
+ opio = ctlr->opio;
+ opio->cmd |= Chcreset; /* controller reset */
+ coherence();
+ for(i = 0; i < 100; i++){
+ if((opio->cmd & Chcreset) == 0)
+ break;
+ delay(1);
+ }
+ if(i >= 100)
+ print("ehci %#p controller reset timed out\n", ctlr->capio);
+ delay(100);
+ ehcirun(ctlr, 0);
+ opio->frbase = 0;
+ coherence();
+ iunlock(ctlr);
+}
+
+/*
+ * omap3-specific ehci code
+ */
+
+enum {
+ /* opio->insn[5] bits */
+ Control = 1<<31, /* set to start access, cleared when done */
+ Write = 2<<22,
+ Read = 3<<22,
+ Portsh = 24,
+ Regaddrsh = 16, /* 0x2f means use extended reg addr */
+ Eregaddrsh = 8,
+
+ /* phy reg addresses */
+ Funcctlreg = 4,
+ Ifcctlreg = 7,
+
+ Phystppullupoff = 0x90, /* on is 0x10 */
+
+ Phyrstport2 = 147, /* gpio # */
+
+};
+
+static void
+wrulpi(Eopio *opio, int port, int reg, uchar data)
+{
+ opio->insn[5] = Control | port << Portsh | Write | reg << Regaddrsh |
+ data;
+ coherence();
+ /*
+ * this seems contrary to the skimpy documentation in the manual
+ * but inverting the test hangs forever.
+ */
+ while (!(opio->insn[5] & Control))
+ ;
+}
+
+static int
+reset(Hci *hp)
+{
+ Ctlr *ctlr;
+ Ecapio *capio;
+ Eopio *opio;
+ Uhh *uhh;
+ static int beenhere;
+
+ if (beenhere)
+ return -1;
+ beenhere = 1;
+
+ if(getconf("*nousbehci") != nil || probeaddr(PHYSEHCI) < 0)
+ return -1;
+
+ ctlr = smalloc(sizeof(Ctlr));
+ /*
+ * don't bother with vmap; i/o space is all mapped anyway,
+ * and a size less than 1MB will blow an assertion in mmukmap.
+ */
+ ctlr->capio = capio = (Ecapio *)PHYSEHCI;
+ ctlr->opio = opio = (Eopio*)((uintptr)capio + (capio->cap & 0xff));
+
+ hp->aux = ctlr;
+ hp->port = (uintptr)ctlr->capio;
+ hp->irq = 77;
+ 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);
+
+ /* omap35-specific set up */
+ /* bit 5 `must be set to 1 for proper behavior', spruf98d §23.2.6.7.17 */
+ opio->insn[4] |= 1<<5;
+ coherence();
+
+ /* insn[5] is for both utmi and ulpi, depending on hostconfig mode */
+ uhh = (Uhh *)PHYSUHH;
+ if (uhh->hostconfig & P1ulpi_bypass) { /* utmi port 1 active */
+ /* not doing this */
+ iprint("usbehci: bypassing ulpi on port 1!\n");
+ opio->insn[5] &= ~(MASK(4) << 13);
+ opio->insn[5] |= 1 << 13; /* select port 1 */
+ coherence();
+ } else { /* ulpi port 1 active */
+ /* TODO may need to reset gpio port2 here */
+
+ /* disable integrated stp pull-up resistor */
+ wrulpi(opio, 1, Ifcctlreg, Phystppullupoff);
+
+ /* force phy to `high-speed' */
+ wrulpi(opio, 1, Funcctlreg, 0x40);
+ }
+
+ /*
+ * Linkage to the generic HCI driver.
+ */
+ ehcilinkage(hp);
+ hp->shutdown = shutdown;
+ hp->debug = setdebug;
+
+ intrenable(78, hp->interrupt, hp, UNKNOWN, "usbtll");
+ intrenable(92, hp->interrupt, hp, UNKNOWN, "usb otg");
+ intrenable(93, hp->interrupt, hp, UNKNOWN, "usb otg dma");
+ return 0;
+}
+
+void
+usbehcilink(void)
+{
+ addhcitype("ehci", reset);
+}
diff --git a/sys/src/9/omap/words b/sys/src/9/omap/words
new file mode 100755
index 000000000..d55b335da
--- /dev/null
+++ b/sys/src/9/omap/words
@@ -0,0 +1,202 @@
+beagleboard rev c3:
+cortex-a8 cpu: arm v7-a arch. rev 3, 500MHz, dual-issue
+OMAP3530-GP rev 2, CPU-OPP2 L3-165MHz
+OMAP3 Beagle board + LPDDR/NAND
+DRAM: 256 MB
+NAND: 256 MiB
+Board revision C
+Serial #784200230000000004013f790401d018
+
+igepv2 board:
+cortex-a8 cpu: arm v7-a arch. rev 3, 720MHz, dual-issue
+OMAP3530-GP ES3.1, CPU-OPP2 L3-165MHz
+IGEP v2.x rev. B + LPDDR/ONENAND
+DRAM: 512 MB
+Muxed OneNAND(DDP) 512MB 1.8V 16-bit (0x58)
+OneNAND version = 0x0031
+Chip support all block unlock
+Chip has 2 plane
+Scanning device for bad blocks
+Bad eraseblock 3134 at 0x187c0000
+Bad eraseblock 3135 at 0x187e0000
+OneNAND: 512 MB
+
+omap3530 SoC
+CORE_CLK runs at 26MHz
+see spruf98d from ti.com (/public/doc/ti/omap35x.ref.spruf98d.pdf)
+
+separate i & d tlbs, each 32 entries
+ can invalidate i, d or both tlbs by { all, mva, or asid match }
+
+i & d L1 caches, 16K each, 4 ways, 64 sets, 64-byte lines
+ i is VIPT, d is PIPT
+ no `test and clean D & U all' operations
+ no prefetching, no cache maintenance
+ can invalidate i, d or both cache but not D & U all
+ can invalidate entire i-cache only
+ can clean or invalidate by set and way data/unified cache
+unified L2 PIPT cache, 256K, 8 ways, 512 sets, 64-byte lines
+no hardware cache coherence
+
+l3 interconnect firewalls are all off at boot time, except for a bit of
+ secure ram
+sram at 0x40200000 size 1MB
+l4 interconnect firewalls seem to be sane at boot time
+
+___
+The state of the Beagleboard/IGEPv2 (TI OMAP35 SoC, Cortex-A8) port.
+
+Plan 9 runs on the IGEPv2 and Gumstix Overo boards.
+
+On the Beagleboard, Plan 9 is not yet usable but it gets as far as
+trying to access the USB ethernet (since the Beagleboard has no
+built-in ethernet and must use USB ethernet).
+
+IGEP & Gumstix Ethernet
+
+The smsc9221 ethernet consumes a lot of system time. The design
+decision to use fifos rather than buffer rings and to not incorporate
+dma into the ethernet controller is probably responsible. With only a
+single core, running the 9221 consumes a lot of the available CPU
+time. It's probably worth trying to use the system dma controller again.
+
+USB
+
+The ohci and ehci controllers are seen, but no devices yet.
+
+There are four USB errata that need to be looked into for the igepv2
+(silicon 3.1) at least. From the omap3530 errata (rev e):
+
+- 3.1.1.130 only one usb dma channel (rx or tx) can be active
+ at one time: use interrupt mode instead
+- 3.1.1.144 otg soft reset doesn't work right
+- 3.1.1.183 ohci and ehci controllers cannot work concurrently
+- §3.1.3 usb limitations: all ports must be configured to identical speeds
+ (high vs full/low)
+
+Flash
+
+access to nand flash would be handy for nvram and paqfs or sacfs file
+systems.
+
+In the flash, x-loader occupies up to 0x20000, then u-boot from
+0x80000 to 0x1e0000, and there's a linux kernel after that (if you
+care). The beagle's flash chip is a micron pop 2Gb nand
+mt29f2g16abdhc-et (physical marking jw256), and the igep's is a
+samsung onenand.
+
+VFPv3 Floating Point
+
+The Cortex-A8 has VFPv3 floating point, which uses different opcodes
+than 5c/5l currently generate. New 5c or 5l is in the works.
+
+Video
+
+The display subsystem for omap3 (dss) is divided into 3 parts, called lcd,
+video and dsi (ignoring the various accelerators). The system only
+supports the lcd via dvi interface so far because it's the only one we
+have been able to test. 1280x1024x16 is the default resolution, this
+might be changed. Writing to /dev/dssctl (e.g., echo 1024x768x16
+>/dev/dssctl) changes the resolution. Currently the system does not
+use the rfbi since it seems like an unnecessary optimisation at this
+point. Per Odlund wrote the first draft of the video driver for a
+Google Summer of Code project.
+
+Stray Interrupts
+
+IRQs 56 and 57 are I2C. 83, 86 and 94 are MMC.
+
+___
+
+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.
+___
+
+There are a few rough edges:
+
+- the clock.c scheduling rate (HZ) is quite approximate. The OMAP
+timers are complex, but one could eventually do better (or just let
+timesync compensate).
+
+- User processes are limited to 512MB virtual (mainly by the IGEPv2 Ethernet
+being at 0x2c000000), which isn't a problem since Beagleboards only
+have 256MB of dram and IGEPv2s have 512MB, and we don't want to swap.
+
+- might use ucalloc.c to allocate uncached scratch space for generated code
+in coproc.c.
+
+- the C implementation of cache primitives failed with mmu off; still true?
+
+- unlock, setup: protect module register target APE (PM_RT) per spruf98c §1.6.7
+
+- setup mpp (multi-purpose pins)?
+
+___
+ memory map (mostly from omap35x ref)
+hex addr size what
+----
+0 16MB physical address of flash registers, buffers
+20000000 16MB virtual address of flash registers, buffers
+2c000000 ? smc 9221 ethernet
+38000000 16MB 256MB (beagle) or 512MB (igep) nand flash mapped here
+
+40000000 112K boot rom, top of user space
+40200000 64K sram
+
+48000000 16MB L4 core
+48002000 8K system control (scm)
+48004000 16K clock manager
+48040000 8K L4-core config
+48050000 4K graphics
+48062000 4K usb tll
+48064000 1K usb uhh_config
+48064400 1K ohci
+48064800 1K ehci
+4806a000 8K 8250 uart0
+4806c000 8K 8250 uart1
+48086000 4K gptimer10
+48088000 4K gptimer11
+4809c000 8K mmc/sd goo
+480ab000 8K hs usb otg
+480ad000 8K mmc/sd goo
+480b4000 8K mmc/sd goo
+480c7000 device intr controller
+48200000 2K intr ctlr (intc)
+
+48300000 256K L4-wakeup
+48304000 4K gptimer12
+48318000 8K gptimer1
+
+49000000 1MB L4 peripherals
+49020000 8K 8250 uart2 (with exposed connector for console)
+49032000 4K gptimer2
+49034000 4K gptimer3
+⋯
+49040000 4K gptimer9
+49050000 8K gpio2
+⋯
+49058000 8K gpio6
+
+50000000 64K graphics accelerator
+
+68000000 1K L3 config (rt)
+68004000 1K L3 hs usb host
+68004400 1K L3 hs usb otg
+68005400 1K L3 graphics
+68006800 1K L4-core config
+68010000 L3 protection mechanism
+
+6e000000 ? gpmc
+
+80000000 256MB dram on beagle
+ 512MB dram on igep
+
+c0000000 1GB kernel virtual space, mapped to 80000000
+
+apparently the vector address (0 or 0xffff0000) is virtual,
+so we're expected to map it to ram.