ref: d0fb274a9820f51d4fe0eb8cd13c6f0c53c0a3df
author: mia soweli <inbox@tachibana-labs.org>
date: Sun May 28 09:01:01 EDT 2023
*
--- /dev/null
+++ b/dat.h
@@ -1,0 +1,147 @@
+typedef struct Conf Conf;
+typedef struct Confmem Confmem;
+typedef struct FPsave FPsave;
+typedef struct Label Label;
+typedef struct Lock Lock;
+typedef struct Mach Mach;
+typedef struct MMMU MMMU;
+typedef struct Page Page;
+typedef struct Proc Proc;
+typedef struct PFPU PFPU;
+typedef struct PMMU PMMU;
+typedef struct Ureg Ureg;
+
+typedef u32int PTE;
+typedef uvlong Tval;
+
+#pragma incomplete Ureg
+
+#define MAXSYSARG 5
+#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;
+};
+
+struct Confmem {
+ uintptr base;
+ uintptr limit;
+ uintptr kbase;
+ uintptr klimit;
+ ulong npage;
+};
+
+struct Conf {
+ ulong nmach;
+ ulong nproc;
+ Confmem mem[1];
+ ulong npage;
+ ulong upages;
+ ulong copymode;
+ ulong ialloc;
+ ulong pipeqsize;
+ ulong nimage;
+ ulong nswap;
+ int nswppo;
+ int monitor;
+};
+
+struct FPsave {
+ ulong exc;
+ ulong scr;
+ uchar regs[256];
+};
+
+struct PFPU {
+ enum {
+ FPinit,
+ FPactive,
+ FPinactive,
+
+ FPillegal = 0x100,
+ } fpstate;
+ FPsave fpsave[1];
+};
+
+#define NCOLOR 1
+struct PMMU {
+ Page *mmul2;
+ Page *mmul2cache;
+};
+
+struct MMMU {
+ PTE *mmul1;
+ uint mmupid;
+};
+
+#include "../port/portdat.h"
+
+struct Mach {
+ int machno;
+ uintptr splpc;
+ Proc *proc;
+ MMMU;
+
+ PMach;
+
+ u32int save[5];
+ uintptr stack[1];
+};
+
+typedef struct ISAConf ISAConf;
+typedef struct Devport Devport;
+typedef struct DevConf DevConf;
+
+#define BUSUNKNOWN 0
+#define BUSMODEM 1
+
+#define NISAOPT 8
+struct ISAConf {
+ char *type;
+ uintptr port;
+ int irq;
+ ulong dma;
+ ulong mem;
+ ulong size;
+ ulong freq;
+
+ int nopt;
+ char *opt[NISAOPT];
+};
+
+struct Devport {
+ ulong port;
+ int size;
+};
+
+struct DevConf {
+ ulong intnum;
+ char *type;
+ int nports;
+ Devport *ports;
+};
+
+typedef void KMap;
+#define VA(p) ((uintptr)(p))
+#define kmap(p) (KMap*)((p)->pa|KZERO)
+#define kunmap(p)
+#define kmapinval()
+#define getpgcolor(p) 0
+
+struct {
+ char machs[MAXMACH];
+ int exiting;
+} active;
+
+extern register Mach *m;
+extern register Proc *up;
--- /dev/null
+++ b/devkbd.c
@@ -1,0 +1,243 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "../port/error.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/i2c.h"
+
+enum {
+ Rctrl = 0xd2,
+ Csoftreset = 1<<0,
+ Csoftmode = 1<<1,
+ Cenable = 1<<6,
+ Rcode = 0xdb,
+ Risr = 0xe3,
+ Rimr = 0xe4,
+ Ikp = 1<<0,
+ Ilk = 1<<1,
+ Ito = 1<<2,
+ Rsir = 0xe7,
+ Redr = 0xe8,
+ Ekpfalling = 1<<0,
+ Ekprising = 1<<1,
+ Elkfalling = 1<<2,
+ Elkrising = 1<<3,
+ Etofalling = 1<<4,
+ Etorising = 1<<5,
+ Emisfalling = 1<<6,
+ Emisrising = 1<<7,
+ Rsih = 0xe9,
+ Scor = 1<<2,
+};
+
+enum {
+ Qdir,
+ Qscan,
+};
+
+typedef struct Ctlr Ctlr;
+struct Ctlr {
+ Ref;
+ Lock;
+
+ I2Cdev *dev;
+ Queue *q;
+
+ uchar cur[8];
+ uchar prev[8];
+};
+
+static Ctlr ctlr;
+static Dirtab kbdtab[] = {
+ ".", {Qdir, 0, QTDIR}, 0, 0555,
+ "scancode", {Qscan, 0, QTFILE}, 0, 0440,
+};
+
+static u8int
+csr8r(Ctlr *ctlr, u8int r)
+{
+ uchar buf;
+
+ i2crecv(ctlr->dev, &buf, sizeof(buf), r);
+ return buf;
+}
+
+static u8int
+csr8w(Ctlr *ctlr, u8int r, u8int w)
+{
+ i2csend(ctlr->dev, &w, sizeof(w), r);
+ return w;
+}
+
+static void
+kbdinterrupt(Ureg *, void*)
+{
+ int i, j, c, k;
+
+ ilock(&ctlr);
+ if(!(csr8r(&ctlr, Risr) & Ikp)) {
+ iunlock(&ctlr);
+ return;
+ }
+
+ /* scan key columns */
+ for(i = 0; i < 8; i++) {
+ ctlr.prev[i] = ctlr.cur[i];
+ ctlr.cur[i] = csr8r(&ctlr, Rcode + i);
+
+ /* changed? */
+ c = ctlr.cur[i] ^ ctlr.prev[i];
+ if(!c)
+ continue;
+
+ /* scan key rows */
+ for(j = 0; j < 8; j++) {
+ if(!(c & (1 << j)))
+ continue;
+
+ /* pressed or released? */
+ k = i << 3 | j;
+ if(ctlr.prev[i] & (1 << j))
+ k |= 0x80;
+
+ qproduce(ctlr.q, &k, 1);
+ }
+ }
+
+ iunlock(&ctlr);
+}
+
+static void
+kbdreset(void)
+{
+ ilock(&ctlr);
+ ctlr.q = qopen(1024, Qcoalesce, 0, 0);
+ if(!ctlr.q) {
+ iunlock(&ctlr);
+ return;
+ }
+
+ ctlr.dev = i2cdev(i2cbus("i2c1"), 0x4a);
+ if(!ctlr.dev) {
+ iunlock(&ctlr);
+ return;
+ }
+
+ ctlr.dev->subaddr = 1;
+ ctlr.dev->size = 0x100;
+
+ csr8w(&ctlr, Rctrl, Csoftreset | Csoftmode | Cenable);
+ csr8w(&ctlr, Rsih, Scor);
+ csr8w(&ctlr, Rimr, ~Ikp);
+
+ intrenable(IRQTWL, kbdinterrupt, nil, BUSUNKNOWN, "kbd");
+ iunlock(&ctlr);
+}
+
+static void
+kbdshutdown(void)
+{
+}
+
+static Chan *
+kbdattach(char *spec)
+{
+ if(!ctlr.dev)
+ error(Enonexist);
+
+ return devattach(L'b', spec);
+}
+
+static Walkqid *
+kbdwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, kbdtab, nelem(kbdtab), devgen);
+}
+
+static int
+kbdstat(Chan *c, uchar *dp, int n)
+{
+ return devstat(c, dp, n, kbdtab, nelem(kbdtab), devgen);
+}
+
+static Chan *
+kbdopen(Chan *c, int mode)
+{
+ if(!iseve)
+ error(Eperm);
+
+ if(c->qid.path == Qscan) {
+ if(waserror()) {
+ decref(&ctlr);
+ nexterror();
+ }
+
+ if(incref(&ctlr) != 1)
+ error(Einuse);
+
+ c = devopen(c, mode, kbdtab, nelem(kbdtab), devgen);
+ poperror();
+ return c;
+ }
+
+ return devopen(c, mode, kbdtab, nelem(kbdtab), devgen);
+}
+
+static void
+kbdclose(Chan *c)
+{
+ if((c->flag & COPEN) && c->qid.path == Qscan)
+ decref(&ctlr);
+}
+
+static Block*
+kbdbread(Chan *c, long n, ulong off)
+{
+ if(c->qid.path == Qscan)
+ return qbread(ctlr.q, n);
+
+ return devbread(c, n, off);
+}
+
+static long
+kbdread(Chan *c, void *a, long n, vlong)
+{
+ if(c->qid.path == Qscan)
+ return qread(ctlr.q, a, n);
+
+ if(c->qid.path == Qdir)
+ return devdirread(c, a, n, kbdtab, nelem(kbdtab), devgen);
+
+ error(Egreg);
+ return 0;
+}
+
+static long
+kbdwrite(Chan *, void *, long, vlong)
+{
+ error(Egreg);
+ return 0;
+}
+
+Dev kbddevtab = {
+ L'b',
+ "kbd",
+
+ kbdreset,
+ devinit,
+ kbdshutdown,
+ kbdattach,
+ kbdwalk,
+ kbdstat,
+ kbdopen,
+ devcreate,
+ kbdclose,
+ kbdread,
+ kbdbread,
+ kbdwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
--- /dev/null
+++ b/devrtc.c
@@ -1,0 +1,202 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "../port/error.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/i2c.h"
+
+enum {
+ Rsec = 0x1c,
+ Rmin = 0x1d,
+ Rhour = 0x1e,
+ Rday = 0x1f,
+ Rmonth = 0x20,
+ Ryear = 0x21,
+ Rweeks = 0x22,
+ Rctrl = 0x29,
+ Cget = 1<<6,
+
+ Qdir = 0,
+ Qrtc,
+
+ SecMin = 60,
+ SecHour = 60*SecMin,
+ SecDay = 24*SecHour,
+};
+
+typedef struct Ctlr Ctlr;
+struct Ctlr {
+ I2Cdev *dev;
+
+ int sec;
+ int min;
+ int hour;
+ int day;
+ int month;
+ int year;
+};
+
+static Ctlr ctlr;
+static int dmsize[] = { 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+static int ldmsize[] = { 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+static Dirtab rtctab[] = {
+ ".", {Qdir, 0, QTDIR}, 0, 0555,
+ "rtc", {Qrtc, 0, QTFILE}, 0, 0440,
+};
+
+#define bcddec(x) (((x) & 0xf) + ((x) >> 4) * 10)
+#define bcdenc(x) (((x / 10) << 4) + (x) % 10)
+#define leap(x) (((x) % 4) == 0 && ((x % 100) != 0 || (x % 400) == 0))
+
+static u8int
+csr8r(Ctlr *ctlr, u8int r)
+{
+ uchar buf;
+
+ i2crecv(ctlr->dev, &buf, sizeof(buf), r);
+ return buf;
+}
+
+static u8int
+csr8w(Ctlr *ctlr, u8int r, u8int w)
+{
+ i2csend(ctlr->dev, &w, sizeof(w), r);
+ return w;
+}
+
+static vlong
+rtcsnarf(void)
+{
+ vlong s;
+ int i;
+
+ /* latch and snarf */
+ csr8w(&ctlr, Rctrl, csr8r(&ctlr, Rctrl) | Cget);
+ ctlr.sec = bcddec(csr8r(&ctlr, Rsec)) % 60;
+ ctlr.min = bcddec(csr8r(&ctlr, Rmin)) % 60;
+ ctlr.hour = bcddec(csr8r(&ctlr, Rhour)) % 24;
+ ctlr.day = bcddec(csr8r(&ctlr, Rday));
+ ctlr.month = bcddec(csr8r(&ctlr, Rmonth));
+ ctlr.year = bcddec(csr8r(&ctlr, Ryear)) % 100;
+ ctlr.year += 2000;
+
+ /* seconds per year */
+ s = 0;
+ for(i = 1970; i < ctlr.year; i++) {
+ if(leap(i))
+ s += ldmsize[0] * SecDay;
+ else
+ s += dmsize[0] * SecDay;
+ }
+
+ /* seconds per month */
+ for(i = 1; i < ctlr.month; i++) {
+ if(leap(ctlr.year))
+ s += ldmsize[i] * SecDay;
+ else
+ s += dmsize[i] * SecDay;
+ }
+
+ /* days, hours, minutes, seconds */
+ s += (ctlr.day - 1) * SecDay;
+ s += ctlr.hour * SecHour;
+ s += ctlr.min * SecMin;
+ s += ctlr.sec;
+ return s;
+}
+
+static void
+rtcreset(void)
+{
+ ctlr.dev = i2cdev(i2cbus("i2c1"), 0x4b);
+ if(!ctlr.dev)
+ return;
+
+ ctlr.dev->subaddr = 1;
+ ctlr.dev->size = 0x100;
+}
+
+static void
+rtcshutdown(void)
+{
+}
+
+static Chan *
+rtcattach(char *spec)
+{
+ if(!ctlr.dev)
+ error(Enonexist);
+
+ return devattach('r', spec);
+}
+
+static Walkqid *
+rtcwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, rtctab, nelem(rtctab), devgen);
+}
+
+static int
+rtcstat(Chan *c, uchar *dp, int n)
+{
+ return devstat(c, dp, n, rtctab, nelem(rtctab), devgen);
+}
+
+static Chan *
+rtcopen(Chan *c, int mode)
+{
+ mode = openmode(mode);
+ if(c->qid.path == Qrtc) {
+ if(!iseve() && mode != OREAD)
+ error(Eperm);
+ }
+
+ return devopen(c, mode, rtctab, nelem(rtctab), devgen);
+}
+
+static void
+rtcclose(Chan *)
+{
+}
+
+static long
+rtcread(Chan *c, void *a, long n, vlong off)
+{
+ if(c->qid.path == Qdir)
+ return devdirread(c, a, n, rtctab, nelem(rtctab), devgen);
+ if(c->qid.path == Qrtc)
+ return readnum(off, a, n, rtcsnarf(), 12);
+
+ error(Egreg);
+ return 0;
+}
+
+static long
+rtcwrite(Chan *, void *, long, vlong)
+{
+ error(Egreg);
+ return 0;
+}
+
+Dev rtcdevtab = {
+ 'r',
+ "rtc",
+
+ rtcreset,
+ devinit,
+ rtcshutdown,
+ rtcattach,
+ rtcwalk,
+ rtcstat,
+ rtcopen,
+ devcreate,
+ rtcclose,
+ rtcread,
+ devbread,
+ rtcwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
--- /dev/null
+++ b/fns.h
@@ -1,0 +1,77 @@
+#include "../port/portfns.h"
+
+#define KADDR(a) ((void*)(a))
+#define PADDR(a) ((uintptr)(a))
+
+#define userureg(ur) (((ur)->psr & PsrMask) == PsrMusr)
+
+void* ucallocalign(usize, int, usize);
+void* ucalloc(usize);
+
+int cmpswap(long *, long, long);
+int cas(long *, long, long);
+int tas(void *);
+
+void evenaddr(uintptr va);
+void procrestore(Proc *);
+void procsave(Proc *);
+void procsetup(Proc *);
+void procfork(Proc *);
+
+void coherence(void);
+void idlehands(void);
+void touser(void*);
+void setR13(uint, u32int*);
+ulong getdfsr(void);
+ulong getifsr(void);
+uintptr getdfar(void);
+uintptr getifar(void);
+void setvectors(uintptr);
+void breakpt(void);
+
+char* getconf(char*);
+int isaconfig(char *, int, ISAConf *);
+
+void mmuinvalidate(void);
+void* mmuuncache(void*, usize);
+uintptr cankaddr(uintptr);
+
+ulong µs(void);
+void delay(int);
+void microdelay(int);
+void cycles(uvlong*);
+
+void dumpureg(Ureg*);
+void dumpstackureg(Ureg*);
+
+void intrenable(int, void (*f)(Ureg *, void*), void *, int, char*);
+void intrdisable(int, void (*f)(Ureg *, void*), void *, int, char*);
+void intr(Ureg *);
+
+void uartinit(void);
+void mmuinit(void);
+void trapinit(void);
+void intrinit(void);
+void timerinit(void);
+void screeninit(void);
+
+void links(void);
+
+void l1icacheinv(void);
+void l1dcachewb(void);
+void l1dcacheinv(void);
+void l1dcachewbinv(void);
+void l1ucachewbinv(void);
+
+void l2idcacheinv(void);
+void l2idcachewb(void);
+void l2idcachewbinv(void);
+void l2ucachewbinv(void);
+
+
+void fpinit(void);
+void fpoff(void);
+void fpclear(void);
+
+void fpsave(FPsave *);
+void fprestore(FPsave *);
--- /dev/null
+++ b/i2cn900.c
@@ -1,0 +1,224 @@
+#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/i2c.h"
+
+enum {
+ Rrev = 0x00,
+ Rie = 0x04,
+ Ris = 0x08,
+ Ial = 1 << 0, /* arbitration lost */
+ Inack = 1 << 1, /* no acknowledgement */
+ Iardy = 1 << 2, /* address ready */
+ Irrdy = 1 << 3, /* receive ready */
+ Ixrdy = 1 << 4, /* transmit ready */
+ Ibb = 1 << 12, /* bus busy */
+ Iall = 0xffff,
+
+ Rwe = 0x0c,
+ Rsyss = 0x10,
+ SSreset = 1 << 0, /* reset status */
+
+ Rbuf = 0x14,
+ Rcnt = 0x18,
+ Rdata = 0x1c,
+ Rsysc = 0x20,
+ SCreset = 1 << 1, /* software reset */
+
+ Rcon = 0x24,
+ Cstt = 1 << 0, /* start condition */
+ Cstp = 1 << 1, /* stop condiction */
+ Cxoa3 = 1 << 4, /* expand address */
+ Cxoa2 = 1 << 5,
+ Cxoa1 = 1 << 6,
+ Cxoa0 = 1 << 7,
+ Cxa = 1 << 8,
+ Ctrx = 1 << 9, /* transmit mode */
+ Cmst = 1 << 10, /* master mode */
+ Cstb = 1 << 11, /* start byte */
+ Cen = 1 << 15, /* enable */
+
+ Raddr = 0x2c,
+};
+
+#define csr32r(c, r) ((c)->io[(r)/4])
+#define csr32w(c, r, w) ((c)->io[(r)/4] = (w))
+
+typedef struct Ctlr Ctlr;
+struct Ctlr {
+ u32int *io;
+ ulong irq;
+
+ Rendez;
+};
+
+static Ctlr ctlr[] = {
+ { .io = (u32int*) PHYSI2C1, .irq = IRQI2C1 },
+ { .io = (u32int*) PHYSI2C2, .irq = IRQI2C2 },
+ { .io = (u32int*) PHYSI2C3, .irq = IRQI2C3 },
+};
+
+static void
+n900i2cwaitbus(Ctlr *ctlr)
+{
+ /* FIXME: timeout here? */
+ while(csr32r(ctlr, Ris) & Ibb)
+ ;
+}
+
+static int
+n900i2cwaitirq(void *arg)
+{
+ Ctlr *ctlr = arg; return csr32r(ctlr, Ris);
+}
+
+static uint
+n900i2cwait(Ctlr *ctlr)
+{
+ uint s;
+
+ /* FIXME: timeout here? */
+ while(!(s = csr32r(ctlr, Ris))) {
+ if(!up || !islo())
+ continue;
+
+ tsleep(ctlr, n900i2cwaitirq, ctlr, 5);
+ }
+
+ return s;
+}
+
+static void
+n900i2cflush(Ctlr *ctlr)
+{
+ while(csr32r(ctlr, Ris) & Irrdy) {
+ USED(csr32r(ctlr, Rdata));
+ csr32w(ctlr, Ris, Irrdy);
+ }
+}
+
+static void
+n900i2cintr(Ureg *, void *arg)
+{
+ Ctlr *ctlr;
+
+ ctlr = arg;
+ wakeup(ctlr);
+}
+
+static int
+n900i2cinit(I2Cbus *bus)
+{
+ Ctlr *ctlr;
+
+ /* reset the ctlr */
+ ctlr = bus->ctlr;
+ csr32w(ctlr, Rsysc, SCreset);
+ csr32w(ctlr, Rcon, Cen);
+
+ /* FIXME: timeout here? */
+ while(!(csr32r(ctlr, Rsyss) & SSreset))
+ ;
+
+ intrenable(ctlr->irq, n900i2cintr, ctlr, 0, bus->name);
+ return 0;
+}
+
+static int
+n900i2cio(I2Cbus *bus, uchar *pkt, int olen, int ilen)
+{
+ Ctlr *ctlr;
+ uint con, addr, stat;
+ uint o;
+
+ ctlr = bus->ctlr;
+ if(olen <= 0 || pkt == nil)
+ return -1;
+
+ o = 0;
+ con = Cen | Cmst | Ctrx | Cstp | Cstt;
+ if((pkt[o] & 0xf8) == 0xf0) {
+ /* 10-bit address: qemu has bugs, nothing on the n900 needs them.
+ * con |= Cxa;
+ * addr = ((pkt[o++] & 6) << 7) | pkt[o++];
+ */
+ return -1;
+ } else {
+ /* 7-bit address */
+ addr = pkt[o++] >> 1;
+ }
+
+ /* wait for bus */
+ n900i2cwaitbus(ctlr);
+
+ /* first attempt to probe, will get nack here if no dev */
+ csr32w(ctlr, Rcnt, olen);
+ csr32w(ctlr, Raddr, addr);
+ csr32w(ctlr, Rcon, con);
+ stat = n900i2cwait(ctlr);
+ if(stat & Inack || stat & Ial) {
+ o = -1; goto err;
+ }
+
+ /* transmit */
+ while(o < olen) {
+ stat = n900i2cwait(ctlr);
+ if(stat == 0 || stat & Inack || stat & Ial) {
+ o = -1; goto err;
+ }
+
+ if(stat & Iardy) {
+ csr32w(ctlr, Ris, Iardy);
+ break;
+ }
+
+ if(stat & Ixrdy) {
+ csr32w(ctlr, Rdata, pkt[o++]);
+ csr32w(ctlr, Ris, Ixrdy);
+ }
+ }
+
+ /* receive */
+ csr32w(ctlr, Rcnt, ilen);
+ csr32w(ctlr, Raddr, addr);
+ csr32w(ctlr, Rcon, Cen | Cmst | Cstp | Cstt);
+ while(o < olen + ilen) {
+ stat = n900i2cwait(ctlr);
+ if(stat == 0 || stat & Inack || stat & Ial) {
+ o = -1; goto err;
+ }
+
+ if(stat & Iardy) {
+ csr32w(ctlr, Ris, Iardy);
+ break;
+ }
+
+ if(stat & Irrdy) {
+ pkt[o++] = csr32r(ctlr, Rdata);
+ csr32w(ctlr, Ris, Irrdy);
+ }
+ }
+
+err:
+ n900i2cflush(ctlr);
+ csr32w(ctlr, Ris, Iall);
+ return o;
+}
+
+void
+i2cn900link(void)
+{
+ int i;
+ static I2Cbus bus[] = {
+ { "i2c1", 4000000, &ctlr[0], n900i2cinit, n900i2cio },
+ { "i2c2", 4000000, &ctlr[1], n900i2cinit, n900i2cio },
+ { "i2c3", 4000000, &ctlr[2], n900i2cinit, n900i2cio },
+ };
+
+ for(i = 0; i < nelem(bus); i++)
+ addi2cbus(&bus[i]);
+}
--- /dev/null
+++ b/init9.s
@@ -1,0 +1,12 @@
+TEXT main(SB), 1, $8
+ MOVW $setR12(SB), R12
+ MOVW $boot(SB), R0
+
+ ADD $12, R13, R1
+
+ MOVW R0, 4(R13)
+ MOVW R1, 8(R13)
+
+ BL startboot(SB)
+_limbo:
+ B _limbo
--- /dev/null
+++ b/intr.c
@@ -1,0 +1,199 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+enum {
+ Rrev = 0x00,
+ Rsysconf = 0x10,
+ Rsysstat = 0x14,
+ Rirq = 0x40,
+ Rfiq = 0x44,
+ Rcontrol = 0x48,
+ Cnewirqgen = 1<<0,
+ Rprot = 0x4c,
+ Ridle = 0x50,
+ Rirqprio = 0x60,
+ Rfiqprio = 0x64,
+ Rthreshold = 0x68,
+
+ Ritr = 0x80,
+ Rmir = 0x84,
+ Rmirclear = 0x88,
+ Rmirset = 0x8c,
+ Risrset = 0x90,
+ Risrclear = 0x94,
+ Rirqpend = 0x98,
+ Rfiqpend = 0x9c,
+
+ Rilr = 0x100,
+};
+
+enum {
+ Nmir = 3,
+ Nitr = 3,
+ Nintrs = 96,
+};
+
+#define Ritrn(n) (Ritrn + 32*(n))
+#define Rmirn(n) (Rmirn + 32*(n))
+#define Rmirclearn(n) (Rmirclear + 32*(n))
+#define Rmirsetn(n) (Rmirset + 32*(n))
+#define Rirqpendn(n) (Rirqpend + 32*(n))
+#define Rfiqpendn(n) (Rfiqpend + 32*(n))
+
+#define Rilrn(n) (Rilr + 4*(n))
+
+#define csr32r(c, r) ((c)->io[(r)/4])
+#define csr32w(c, r, w) ((c)->io[(r)/4] = (w))
+
+typedef struct Intr Intr;
+typedef struct Ctlr Ctlr;
+
+struct Intr {
+ void (*f)(Ureg *, void *);
+ void *arg;
+ char *name;
+
+ Intr *next;
+};
+
+struct Ctlr {
+ Lock;
+
+ u32int *io;
+
+ Intr *intrs[Nintrs];
+};
+
+static Ctlr ctlrmpu = { .io = (u32int*) PHYSINTRMPU };
+
+void
+intrinit(void)
+{
+ Ctlr *ctlr = &ctlrmpu;
+ int i;
+
+ /* mask all interrupts */
+ for (i = 0; i < Nmir; i++)
+ csr32w(ctlr, Rmirsetn(i), ~0);
+
+ /* protection off, threshold off, set all intrs priority 0, mapped to irq */
+ csr32w(ctlr, Rcontrol, 0);
+ csr32w(ctlr, Rthreshold, 0xff);
+ for (i = 0; i < Nintrs; i++)
+ csr32w(ctlr, Rilrn(i), 0);
+
+ coherence();
+}
+
+void
+intrenable(int n, void (*f)(Ureg *, void *), void *arg, int, char *name)
+{
+ Ctlr *ctlr = &ctlrmpu;
+ Intr *intr;
+
+ if (n >= nelem(ctlr->intrs) || n < 0)
+ panic("intrenable %d", n);
+
+ intr = malloc(sizeof(*intr));
+ if(!intr)
+ panic("intrenable: no memory for interrupt");
+
+ intr->f = f;
+ intr->arg = arg;
+ intr->name = name;
+
+ lock(ctlr);
+
+ /* chain this interrupt */
+ intr->next = ctlr->intrs[n];
+ ctlr->intrs[n] = intr;
+
+ /* new handler assigned, unmask this interrupt */
+ csr32w(ctlr, Rmirclearn(n >> 5), 1 << (n & 31));
+
+ unlock(ctlr);
+ coherence();
+}
+
+void
+intrdisable(int n, void (*f)(Ureg *, void *), void *arg, int, char *name)
+{
+ Ctlr *ctlr = &ctlrmpu;
+ Intr *intr, **ip;
+
+ if (n >= nelem(ctlr->intrs) || n < 0)
+ panic("intrdisable %d", n);
+
+ lock(ctlr);
+ for(ip = &ctlr->intrs[n]; intr = *ip; ip = &intr->next) {
+ if(intr->f == f && intr->arg == arg && strcmp(intr->name, name) == 0) {
+ *ip = intr->next;
+ free(intr);
+ break;
+ }
+ }
+
+ /* no more handlers assigned, mask this interrupt */
+ if(ctlr->intrs[n] == nil)
+ csr32w(ctlr, Rmirsetn(n >> 5), 1 << (n & 31));
+
+ unlock(ctlr);
+ coherence();
+}
+
+void
+intr(Ureg *ureg)
+{
+ Ctlr *ctlr = &ctlrmpu;
+ Intr *intr;
+ int n, h, s;
+
+ h = 0;
+ n = csr32r(ctlr, Rirq) & 0x7f;
+ s = csr32r(ctlr, Rirq) & ~0x7f;
+ if(s) {
+ /* interrupt controller reports spurious interrupt flag. */
+ iprint("cpu%d: spurious interrupt\n", m->machno);
+ csr32w(ctlr, Rcontrol, Cnewirqgen);
+ return;
+ }
+
+ if(n >= nelem(ctlr->intrs)) {
+ iprint("cpu%d: invalid interrupt %d\n", m->machno, n);
+ csr32w(ctlr, Rcontrol, Cnewirqgen);
+ return;
+ }
+
+ /* call all handlers for this interrupt number */
+ for (intr = ctlr->intrs[n]; intr; intr = intr->next) {
+ if(intr->f) {
+ if(islo())
+ panic("trap: islo() in interrupt handler\n");
+
+ intr->f(ureg, intr->arg);
+ if(islo())
+ panic("trap: islo() after interrupt handler\n");
+ }
+
+ h++;
+ }
+
+ csr32w(ctlr, Rcontrol, Cnewirqgen);
+ coherence();
+
+ if(!h) iprint("cpu%d: spurious interrupt %d\n", m->machno, n);
+ if(up) {
+ if(n >= IRQTIMER1 && n <= IRQTIMER11) {
+ if(up->delaysched) {
+ splhi();
+ sched();
+ }
+ } else {
+ preempted();
+ }
+ }
+}
--- /dev/null
+++ b/io.h
@@ -1,0 +1,63 @@
+#define PHYSL4 0x40000000
+#define PHYSL4END 0x50000000
+
+#define PHYSTIMER1 0x48318000
+#define PHYSTIMER2 0x49032000
+#define PHYSTIMER3 0x49034000
+#define PHYSTIMER4 0x49036000
+#define PHYSTIMER5 0x49038000
+#define PHYSTIMER6 0x4903a000
+#define PHYSTIMER7 0x4903c000
+#define PHYSTIMER8 0x4903e000
+#define PHYSTIMER9 0x49040000
+#define PHYSTIMER10 0x48086000
+#define PHYSTIMER11 0x48088000
+
+#define PHYSUART1 0x4806a000
+#define PHYSUART2 0x4806c000
+#define PHYSUART3 0x49020000
+
+#define PHYSI2C1 0x48070000
+#define PHYSI2C2 0x48072000
+#define PHYSI2C3 0x48060000
+
+#define PHYSMMC1 0x4809c000
+#define PHYSMMC2 0x480b4000
+#define PHYSMMC3 0x480ad000
+
+#define PHYSDSS 0x48050000
+
+#define PHYSINTRMODEM 0x480c7000
+#define PHYSINTRMPU 0x48200000
+
+#define PHYSL3 0x68000000
+#define PHYSL3END 0x70000000
+
+#define PHYSMEM 0x80000000
+#define PHYSMEMEND 0x90000000
+
+#define IRQTIMER1 37
+#define IRQTIMER2 38
+#define IRQTIMER3 39
+#define IRQTIMER4 40
+#define IRQTIMER5 41
+#define IRQTIMER6 42
+#define IRQTIMER7 43
+#define IRQTIMER8 44
+#define IRQTIMER9 45
+#define IRQTIMER10 46
+#define IRQTIMER11 47
+
+#define IRQUART1 72
+#define IRQUART2 73
+#define IRQUART3 74
+
+#define IRQI2C1 56
+#define IRQI2C2 57
+#define IRQI2C3 61
+
+#define IRQMMC1 83
+#define IRQMMC2 86
+#define IRQMMC3 94
+
+#define IRQTWL 7
--- /dev/null
+++ b/l.s
@@ -1,0 +1,354 @@
+#include "mem.h"
+#include "io.h"
+
+TEXT _start(SB), $-4
+ /* load static base */
+ MOVW $setR12(SB), R12
+
+ /* fiqs and irqs off, svc mode (cortex a8 trm figure 2.12) */
+ MOVW $(PsrDfiq|PsrDirq|PsrMsvc), R0
+ MOVW R0, CPSR
+
+ /* mmu and l1 caches off (cortex a8 trm table 3.46) */
+ MRC 15, 0, R0, C1, C0, 0
+ BIC $(1<<12), R0 /* level 1 instruction cache */
+ BIC $(1<<1), R0 /* level 1 data cache */
+ BIC $(1<<0), R0 /* mmu */
+ MCR 15, 0, R0, C1, C0, 0
+ ISB
+
+ /* l2 caches off (cortex a8 trm table 3.49) */
+ MCR 15, 0, R0, C1, C0, 1
+ BIC $(1<<1), R0 /* level 2 cache */
+ MCR 15, 0, R0, C1, C0, 1
+ ISB
+
+ /* fill mach with 0 */
+ MOVW $0, R0
+ MOVW $MACH(0), R1
+ MOVW $MACH(MAXMACH), R2
+zeromach:
+ MOVW R0, (R1)
+ ADD $4, R1
+ CMP R1, R2
+ BNE zeromach
+
+ /* fill page tables with 0 */
+ MOVW R0, R0
+ MOVW $MACHL1(0), R1 /* bottom of l1 tables */
+ MOVW $MACHL1(MAXMACH), R2 /* top of l1 tables */
+zeropte:
+ MOVW R0, (R1)
+ ADD $4, R1
+ CMP R1, R2
+ BNE zeropte
+
+ /* fill bss with 0 */
+ MOVW $0, R0
+ MOVW $edata(SB), R1
+ MOVW $end(SB), R2
+zerobss:
+ MOVW R0, (R1)
+ ADD $4, R1
+ CMP R1, R2
+ BNE zerobss
+
+ /* fill page tables for memory:
+ * 1mb section, cached, buffered, kernel read-write */
+ MOVW $((1<<1)|(1<<2)|(1<<3)|(1<<10)), R1
+ MOVW $PHYSMEM, R2
+ MOVW $(MACHL1(0)+L1X(PHYSMEM)), R3
+ MOVW $(MACHL1(0)+L1X(PHYSMEMEND)), R4
+ptemem:
+ ORR R2, R1, R0
+ MOVW R0, (R3)
+ ADD $(MiB), R2
+ ADD $4, R3
+ CMP R3, R4
+ BNE ptemem
+
+ /* fill page tables for l4 interconnect:
+ * 1mb section, kernel read-write */
+ MOVW $((1<<1)|(1<<4)|(1<<10)), R1
+ MOVW $PHYSL4, R2
+ MOVW $(MACHL1(0)+L1X(PHYSL4)), R3
+ MOVW $(MACHL1(0)+L1X(PHYSL4END)), R4
+ptel4:
+ ORR R2, R1, R0
+ MOVW R0, (R3)
+ ADD $(MiB), R2
+ ADD $4, R3
+ CMP R3, R4
+ BNE ptel4
+
+ /* fill page tables for l3 interconnect:
+ * 1mb section, kernel read-write */
+ MOVW $((1<<1)|(1<<4)|(1<<10)), R1
+ MOVW $PHYSL3, R2
+ MOVW $(MACHL1(0)+L1X(PHYSL3)), R3
+ MOVW $(MACHL1(0)+L1X(PHYSL3END)), R4
+ptel3:
+ ORR R2, R1, R0
+ MOVW R0, (R3)
+ ADD $(MiB), R2
+ ADD $4, R3
+ CMP R3, R4
+ BNE ptel3
+
+ /* fpu on (set bits 20-23 in CPACR) but disabled */
+ MRC 15, 0, R0, C1, C0, 2
+ ORR $(0xf<<20), R0
+ MCR 15, 0, R0, C1, C0, 2
+
+ VMRS(FPEXC, 0)
+ BIC $(FPEXCEX|FPEXCEN), R0
+ VMSR(0, FPEXC)
+
+ /* invalidate caches */
+ BL l1dcacheinv(SB)
+ BL l1icacheinv(SB)
+
+ /* l2 caches back on */
+ MRC 15, 0, R0, C1, C0, 1
+ ORR $(1<<1), R0
+ MCR 15, 0, R0, C1, C0, 1
+
+ /* l1 caches back on */
+ MRC 15, 0, R0, C1, C0, 0
+ ORR $(1<<12), R0
+ ORR $(1<<1), R0
+ MCR 15, 0, R0, C1, C0, 0
+
+ /* set domain access control to client. */
+ MOVW $1, R0
+ BL putdac(SB)
+
+ /* set translation table base */
+ MOVW $MACHL1(0), R0
+ BL putttb(SB)
+
+ /* mmu on, time to get virtual */
+ MOVW $virt(SB), R2
+ BL mmuinvalidate(SB)
+ BL mmuenable(SB)
+ MOVW R2, R15
+
+TEXT virt(SB), $-4
+ /* setup register variables */
+ MOVW $setR12(SB), R12
+ MOVW $(MACH(0)), R(Rmach)
+ MOVW $0, R(Rup)
+
+ /* setup stack in mach */
+ MOVW $(MACH(0)), R13
+ ADD $(MACHSZ), R13
+ SUB $4, R13
+
+ BL main(SB)
+
+_limbo:
+ BL idlehands(SB)
+ B _limbo
+
+ /* hack to load div */
+ BL _div(SB)
+
+
+TEXT mmuenable(SB), 1, $-4
+ MRC 15, 0, R0, C1, C0, 0
+ ORR $(1<<0), R0
+ MCR 15, 0, R0, C1, C0, 0
+ MCR 15, 0, R0, C7, C5, 6
+ DMB; DSB; ISB
+ RET
+
+TEXT mmudisable(SB), 1, $-4
+ MRC 15, 0, R0, C1, C0, 0
+ BIC $(1<<0), R0
+ MCR 15, 0, R0, C1, C0, 0
+ MCR 15, 0, R0, C7, C5, 6
+ DMB; DSB; ISB
+ RET
+
+TEXT mmuinvalidate(SB), 1, $-4
+ MOVW CPSR, R1
+ CPSID
+
+ MOVW R15, R0
+ MCR 15, 0, R0, C8, C7, 0
+ MCR 15, 0, R0, C7, C5, 6
+ DMB; DSB; ISB
+
+ MOVW R1, CPSR
+ RET
+
+/* get and put domain access control */
+TEXT getdac(SB), 1, $-4; MCR 15, 0, R0, C3, C0; RET
+TEXT putdac(SB), 1, $-4
+ MRC 15, 0, R0, C3, C0
+ ISB
+ RET
+
+
+/* get and put translation table base */
+TEXT getttb(SB), 1, $-4; MRC 15, 0, R0, C2, C0, 0; RET
+TEXT putttb(SB), 1, $-4
+ MCR 15, 0, R0, C2, C0, 0
+ MCR 15, 0, R0, C2, C0, 1
+ ISB
+ RET
+
+TEXT getdfsr(SB), $-4; MRC 15, 0, R0, C5, C0, 0; RET
+TEXT getifsr(SB), $-4; MRC 15, 0, R0, C5, C0, 1; RET
+TEXT getdfar(SB), $-4; MRC 15, 0, R0, C6, C0, 0; RET
+TEXT getifar(SB), $-4; MRC 15, 0, R0, C6, C0, 2; RET
+
+TEXT setvectors(SB), $-4;
+ MCR 15, 0, R0, C12, C0, 0
+ RET
+
+TEXT setlabel(SB), $-4
+ MOVW R13, 0(R0)
+ MOVW R14, 4(R0)
+ MOVW $0, R0
+ RET
+
+TEXT gotolabel(SB), $-4
+ MOVW 0(R0), R13
+ MOVW 4(R0), R14
+ MOVW $1, R0
+ RET
+
+TEXT cas(SB), $0
+TEXT cmpswap(SB), $0
+ MOVW ov+4(FP), R1
+ MOVW nv+8(FP), R2
+casspin:
+ LDREX (R0), R3
+ CMP R3, R1
+ BNE casfail
+ STREX R2, (R0), R4
+ CMP $0, R4
+ BNE casspin
+ MOVW $1, R0
+ DMB
+ RET
+casfail:
+ CLREX
+ MOVW $0, R0
+ RET
+
+TEXT tas(SB), $0
+TEXT _tas(SB), $0
+ MOVW $0xdeaddead, R2
+tasspin:
+ LDREX (R0), R1
+ STREX R2, (R0), R3
+ CMP $0, R3
+ BNE tasspin
+ MOVW R1, R0
+ DMB
+ RET
+
+TEXT idlehands(SB), $-4
+ DMB; DSB; ISB
+ WFI
+ RET
+
+TEXT coherence(SB), $-4
+ DMB; DSB; ISB
+ RET
+
+TEXT splhi(SB), $-4
+ MOVW R14, 4(R(Rmach))
+ MOVW CPSR, R0
+ CPSID
+ RET
+
+TEXT spllo(SB), $-4
+ MOVW CPSR, R0
+ CPSIE
+ RET
+
+TEXT splx(SB), $-4
+ MOVW R14, 4(R(Rmach))
+ MOVW R0, R1
+ MOVW CPSR, R0
+ MOVW R1, CPSR
+ RET
+
+TEXT spldone(SB), $-4
+ RET
+
+TEXT islo(SB), $0
+ MOVW CPSR, R0
+ AND $(PsrDirq), R0
+ EOR $(PsrDirq), R0
+ RET
+
+TEXT perfticks(SB), $0
+ MCR 15, 0, R0, C9, C13, 0
+ RET
+
+TEXT touser(SB), $-4
+ MOVM.DB.W [R0], (R13)
+ MOVM.S (R13), [R13]
+ ADD $4, R13
+
+ MOVW CPSR, R0
+ BIC $(PsrMask|PsrDirq|PsrDfiq), R0
+ ORR $PsrMusr, R0
+ MOVW R0, SPSR
+
+ MOVW $(UTZERO+0x20), R0
+ MOVM.DB.W [R0], (R13)
+
+ MOVM.IA.S.W (R13), [R15]
+
+TEXT forkret(SB), $-4
+ ADD $(15*4), R13
+ MOVW 8(R13), R14
+ MOVW 4(R13), R0
+ MOVW R0, SPSR
+ MOVM.DB.S (R13), [R0-R14]
+ ADD $8, R13
+ MOVM.IA.S.W (R13), [R15]
+
+TEXT peek(SB), $0
+ MOVW src+0(FP), R0
+ MOVW dst+4(FP), R1
+ MOVW cnt+8(FP), R2
+TEXT _peekinst(SB), $0
+_peekloop:
+ MOVB (R0), R3
+ MOVB R3, (R1)
+ SUB.S $1, R0
+ BNE _peekloop
+ RET
+
+TEXT fpinit(SB), $0
+ MOVW $FPEXCEN, R0
+ VMSR(0, FPEXC)
+ MOVW $0, R0
+ VMSR(0, FPSCR)
+ RET
+
+TEXT fpoff(SB), $0
+TEXT fpclear(SB), $0
+ MOVW $0, R1
+ VMSR(1, FPEXC)
+ RET
+
+TEXT fpsave(SB), $0
+ VMRS(FPEXC, 1)
+ VMRS(FPSCR, 2)
+ MOVM.IA.W [R1-R2], (R0)
+ VSTMIA
+ RET
+
+TEXT fprestore(SB), $0
+ MOVM.IA.W (R0), [R1-R2]
+ VMSR(1, FPEXC)
+ VMSR(2, FPSCR)
+ VLDMIA
+ RET
--- /dev/null
+++ b/lcache.s
@@ -1,0 +1,143 @@
+#include "mem.h"
+#include "io.h"
+
+/* l1 instruction cache invalidate */
+TEXT l1icacheinv(SB), $-4
+ MOVW $0, R0
+ MCR 15, 0, R0, C7, C5, 0
+ ISB
+ RET
+
+/* l1 data cache writeback */
+TEXT l1dcachewb(SB), $-4
+ MOVW $cacheopwb(SB), R0
+ MOVW $0, R1
+ B cacheop(SB)
+
+/* l1 data cache invalidate */
+TEXT l1dcacheinv(SB), $-4
+ MOVW $cacheopinv(SB), R0
+ MOVW $0, R1
+ B cacheop(SB)
+
+/* l1 data cache writeback + invalidate */
+TEXT l1dcachewbinv(SB), $-4
+ MOVW $cacheopwbinv(SB), R0
+ MOVW $0, R1
+ B cacheop(SB)
+
+/* l1 unified instruction + data cache writeback + invalidate */
+TEXT l1ucachewbinv(SB), $-4
+ /* stash and splhi */
+ MOVW CPSR, R0
+ MOVM.DB.W [R0], (SP)
+ MOVM.DB.W [R14], (SP)
+ CPSID
+
+ /* go */
+ BL l1dcachewbinv(SB)
+ BL l1icacheinv(SB)
+
+ /* restore and splx */
+ MOVM.IA.W (SP), [R14]
+ MOVM.IA.W (SP), [R0]
+ MOVW R0, CPSR
+ RET
+
+/* l2 instruction + data cache writeback */
+TEXT l2idcachewb(SB), $-4
+ MOVW $cacheopwb(SB), R0
+ MOVW $1, R1
+ B cacheop(SB)
+
+/* l2 instruction + data cache invalidate */
+TEXT l2idcacheinv(SB), $-4
+ MOVW $cacheopinv(SB), R0
+ MOVW $1, R1
+ B cacheop(SB)
+
+/* l2 instruction + data cache writeback + invalidate */
+TEXT l2idcachewbinv(SB), $-4
+ MOVW $cacheopwbinv(SB), R0
+ MOVW $1, R1
+ B cacheop(SB)
+
+/* l1 unified instruction + data cache writeback + invalidate */
+TEXT l2ucachewbinv(SB), $-4
+ /* stash and splhi */
+ MOVW CPSR, R0
+ MOVM.DB.W [R0], (SP)
+ MOVM.DB.W [R14], (SP)
+ CPSID
+
+ BL l2idcachewbinv(SB)
+ BL l2idcacheinv(SB)
+
+ /* restore and splx */
+ MOVM.IA.W (SP), [R14]
+ MOVM.IA.W (SP), [R0]
+ MOVW R0, CPSR
+ RET
+
+/* set/way operations for cacheop */
+TEXT cacheopwb(SB), $-4; MCR 15, 0, R0, C7, C10, 2; RET
+TEXT cacheopinv(SB), $-4; MCR 15, 0, R0, C7, C6, 2; RET
+TEXT cacheopwbinv(SB), $-4; MCR 15, 0, R0, C7, C14, 2; RET
+
+#define Rop R2
+#define Rcache R3
+#define Rways R4
+#define Rwayshift R5
+#define Rsets R6
+#define Rsetshift R7
+#define Rset R8
+
+/* apply a cache operation to the whole cache */
+TEXT cacheop(SB), $-4
+ /* stash and splhi */
+ MOVW CPSR, R2
+ MOVM.DB.W [R2,R14], (SP)
+ MOVW R0, Rop
+ MOVW R1, Rcache
+ CPSID
+
+ /* get cache geometry */
+ MCR 15, 2, Rcache, C0, C0, 0; ISB
+ MRC 15, 1, R0, C0, C0, 0
+
+ /* compute ways = ((R0 >> 3) & 0x3ff) + 1) */
+ SRA $3, R0, Rways
+ AND $0x3ff, Rways
+ ADD $1, Rways
+
+ /* compute wayshift = log₂(ways) */
+ CLZ(4, 5) /* Rways, Rwayshift */
+ ADD $1, Rwayshift
+
+ /* compute sets = ((R0 >> 13) & 0x7fff) + 1) */
+ SRA $13, R0, Rsets
+ AND $0x7fff, Rsets
+ ADD $1, Rsets
+
+ /* compute setshift = log₂(cache line size) */
+ AND $0x7, R0, Rsetshift
+ ADD $4, Rsetshift
+
+cacheopways:
+ MOVW Rsets, Rset
+cacheopsets:
+ /* compute set / way register contents */
+ SLL Rwayshift, Rways, R0
+ SLL Rsetshift, Rset, R1
+ ORR R1, R0
+ SLL $1, Rcache, R1
+ ORR R1, R0
+
+ BL (Rop)
+ SUB $1, Rset; CMP $0, Rset; BEQ cacheopsets /* loop sets */
+ SUB $1, Rways; CMP $0, Rways; BEQ cacheopways /* loop ways */
+
+ /* restore regs and splx */
+ MOVM.IA.W (SP), [R2,R14]
+ MOVW R2, CPSR
+ RET
--- /dev/null
+++ b/ltrap.s
@@ -1,0 +1,200 @@
+#include "mem.h"
+#include "io.h"
+
+TEXT vectors(SB), $-4
+ MOVW 24(R15), R15
+ MOVW 24(R15), R15
+ MOVW 24(R15), R15
+ MOVW 24(R15), R15
+ MOVW 24(R15), R15
+ MOVW 24(R15), R15
+ MOVW 24(R15), R15
+ MOVW 24(R15), R15
+
+TEXT vtable(SB), $-4
+ WORD $_vund(SB)
+ WORD $_vund(SB)
+ WORD $_vsvc(SB)
+ WORD $_viabt(SB)
+ WORD $_vdabt(SB)
+ WORD $_vund(SB)
+ WORD $_virq(SB)
+ WORD $_vfiq(SB)
+
+TEXT _vsvc(SB), $-4
+ CLREX
+ DSB
+
+ /* stash in ureg */
+ MOVM.DB.W [R14], (R13) /* ureg->pc */
+ MOVW SPSR, R14
+ MOVM.DB.W [R14], (R13) /* ureg->psr */
+ MOVW $PsrMsvc, R14
+ MOVM.DB.W [R14], (R13) /* ureg->type */
+
+ /* save user regs.
+ * not MOVM.DB.W.S because the saved value of R13 is undefined.
+ * (arm v7 manual §A8.8.199) */
+ MOVM.DB.S [R0-R14], (R13)
+ SUB $(15*4), R13
+
+ /* get our sb, mach, up */
+ MOVW $setR12(SB), R12
+ MOVW $(MACH(0)), R(Rmach)
+ MOVW 8(R(Rmach)), R(Rup)
+
+ /* make space for debugger and go to syscall passing ureg */
+ MOVW R13, R0
+ SUB $8, R13
+ BL syscall(SB)
+ ADD $8, R13
+
+ /* restore link, spsr */
+ ADD $(15*4), R13
+ MOVW 8(R13), R14
+ MOVW 4(R13), R0
+ MOVW R0, SPSR
+
+ /* restore user regs */
+ MOVM.DB.S (R13), [R0-R14]
+
+ /* pop past ureg->type, ureg->psr and restore ureg->pc.
+ * omap and others have RFE here but 5a has no idea about newer instructions
+ * and simulates it with the MOVM below */
+ ADD $8, R13
+ MOVM.IA.S.W (R13), [R15]
+
+TEXT _viabt(SB), $-4
+ CLREX
+ DSB
+ MOVM.IA [R0-R4], (R13)
+ MOVW $PsrMiabt, R0
+ B _vswitch
+
+TEXT _vdabt(SB), $-4
+ CLREX
+ DSB
+ MOVM.IA [R0-R4], (R13)
+ MOVW $PsrMdabt, R0
+ B _vswitch
+
+TEXT _virq(SB), $-4
+ DSB
+ MOVM.IA [R0-R4], (R13)
+ MOVW $PsrMirq, R0
+ B _vswitch
+
+TEXT _vfiq(SB), $-4
+ CLREX
+ DSB
+ MOVM.IA [R0-R4], (R13)
+ MOVW $PsrMfiq, R0
+ B _vswitch
+
+TEXT _vund(SB), $-4
+ CLREX
+ DSB
+ MOVM.IA [R0-R4], (R13)
+ MOVW $PsrMund, R0
+ B _vswitch
+
+_vswitch:
+ /* stash pointer to previous R0-R4, stash SPSR and R14 for ureg */
+ MOVW SPSR, R1
+ MOVW R14, R2
+ MOVW R13, R3
+
+ /* back to svc mode */
+ MOVW CPSR, R14
+ BIC $PsrMask, R14
+ ORR $(PsrDirq|PsrDfiq|PsrMsvc), R14
+ MOVW R14, CPSR
+
+ /* from user or kernel mode? */
+ AND.S $0xf, R1, R4
+ BEQ _vuser
+
+ /* from kernel mode */
+ /* set ureg->type, ureg->psr, ureg->pc */
+ MOVM.DB.W [R0-R2], (R13)
+ MOVM.IA (R3), [R0-R4]
+
+ /* save kernel regs
+ * not MOVM.DB.W.S because the saved value of R13 is undefined.
+ * (arm v7 manual §A8.8.199) */
+ MOVM.DB [R0-R14], (R13)
+ SUB $(15*4), R13
+
+ /* get our sb, mach, up */
+ MOVW $setR12(SB), R12
+
+ /* make space for debugger and go to trap passing ureg */
+ MOVW R13, R0
+ SUB $8, R13
+ BL trap(SB)
+ ADD $8, R13
+
+ /* restore link, spsr */
+ ADD $(15*4), R13
+ MOVW 8(R13), R14
+ MOVW 4(R13), R0
+ MOVW R0, SPSR
+
+ /* restore kernel regs */
+ MOVM.DB (R13), [R0-R14]
+
+ /* pop past ureg->type, ureg->psr, and restore ureg->pc. */
+ ADD $8, R13
+ MOVM.IA.S.W (R13), [R15]
+
+_vuser:
+ /* from user mode */
+ /* set ureg->type, ureg->psr, ureg->pc */
+ MOVM.DB.W [R0-R2], (R13)
+ MOVM.IA (R3), [R0-R4]
+
+ /* save kernel regs
+ * not MOVM.DB.W.S because the saved value of R13 is undefined.
+ * (arm v7 manual §A8.8.199) */
+ MOVM.DB.S [R0-R14], (R13)
+ SUB $(15*4), R13
+
+ /* get our sb, mach, up */
+ MOVW $setR12(SB), R12
+ MOVW $(MACH(0)), R(Rmach)
+ MOVW 8(R(Rmach)), R(Rup)
+
+ /* make space for debugger and go to trap passing ureg */
+ MOVW R13, R0
+ SUB $8, R13
+ BL trap(SB)
+ ADD $8, R13
+
+ /* restore link, spsr */
+ ADD $(15*4), R13
+ MOVW 8(R13), R14
+ MOVW 4(R13), R0
+ MOVW R0, SPSR
+
+ /* restore kernel regs */
+ MOVM.DB.S (R13), [R0-R14]
+
+ /* pop past ureg->type, ureg->psr, and restore ureg->pc */
+ ADD $8, R13
+ MOVM.IA.S.W (R13), [R15]
+
+TEXT setR13(SB), $-4
+ MOVW 4(FP), R1
+
+ /* switch to new mode */
+ MOVW CPSR, R2
+ BIC $PsrMask, R2, R3
+ ORR R0, R3
+ MOVW R3, CPSR
+
+ /* set r13 */
+ MOVW R1, R13
+
+ /* back to old mode */
+ MOVW R2, CPSR
+ RET
--- /dev/null
+++ b/main.c
@@ -1,0 +1,222 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#include "tos.h"
+#include "ureg.h"
+#include "pool.h"
+
+#define MAXCONF 64
+
+static char *confname[MAXCONF];
+static char *confval[MAXCONF];
+static int nconf;
+
+Conf conf;
+
+char*
+getconf(char *name)
+{
+ int i;
+
+ for(i = 0; i < nconf; i++)
+ if(cistrcmp(confname[i], name) == 0)
+ return confval[i];
+
+ return nil;
+}
+
+int
+isaconfig(char *, int, ISAConf *)
+{
+ return 0;
+}
+
+void
+cpuidprint(void)
+{
+ /* FIXME: how fast are we really? */
+ print("cpu%d: %dMHz ARM Cortex-A8\n", m->machno, 600);
+}
+
+void
+plan9iniinit(void)
+{
+ char *c, *p, *q;
+ char *v[MAXCONF];
+ int i, n;
+
+ c = (char*) CONFADDR;
+ for(p = q = c; *q; q++) {
+ if(*q == '\r')
+ continue;
+ if(*q == '\t')
+ *q = ' ';
+ *p++ = *q;
+ }
+
+ *p = 0;
+ n = getfields(c, v, MAXCONF, 1, "\n");
+ for(i = 0; i < n; i++) {
+ if(v[i][0] == '#')
+ continue;
+
+ c = strchr(v[i], '=');
+ if(!c)
+ continue;
+
+ confname[nconf] = v[i];
+ confval[nconf] = c;
+ nconf++;
+ }
+}
+
+void
+confinit(void)
+{
+ int i;
+ uintptr pa;
+ ulong kp;
+
+ conf.nmach = 1;
+ conf.npage = 0;
+ conf.mem[0].base = PHYSMEM;
+ conf.mem[0].limit = PHYSMEMEND;
+
+ pa = PADDR(PGROUND((uintptr)end));
+ for(i = 0; i < nelem(conf.mem); i++) {
+ 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;
+
+ conf.nproc = 100 + ((conf.npage*BY2PG)/MB)*5;
+ if(cpuserver)
+ conf.nproc *= 3;
+ if(conf.nproc > 4000)
+ conf.nproc = 4000;
+
+ conf.nswap = conf.npage*3;
+ conf.nswppo = 4096;
+ conf.nimage = 200;
+ conf.copymode = 0;
+
+ kp = conf.npage - conf.upages;
+ kp *= BY2PG;
+ kp -= conf.upages * sizeof(Page)
+ + conf.nproc * sizeof(Proc*)
+ + conf.nimage * sizeof(Image)
+ + conf.nswap
+ + conf.nswppo * sizeof(Page*);
+
+ mainmem->maxsize = kp;
+ if(!cpuserver)
+ imagmem->maxsize = kp;
+}
+
+void
+machinit(void)
+{
+ m->machno = 0;
+
+ active.machs[0] = 1;
+ active.exiting = 0;
+
+ up = nil;
+}
+
+void
+init0(void)
+{
+ char **sp, buf[KNAMELEN];
+ int i;
+
+ chandevinit();
+ if(!waserror()) {
+ ksetenv("cputype", "arm", 0);
+ if(cpuserver)
+ ksetenv("service", "cpu", 0);
+ else
+ ksetenv("service", "terminal", 0);
+
+ snprint(buf, sizeof(buf), "nokia %s", conffile);
+ ksetenv("terminal", buf, 0);
+ ksetenv("console", "2", 0);
+ ksetenv("kbmap", "n900", 0);
+ for(i = 0; i < nconf; i++) {
+ if(*confname[i] != '*')
+ ksetenv(confname[i], confval[i], 0);
+ ksetenv(confname[i], confval[i], 1);
+ }
+
+ poperror();
+ }
+
+ kproc("alarm", alarmkproc, 0);
+
+ /* prepare stack for boot */
+ sp = (char**)(USTKTOP - sizeof(Tos) - 8 - sizeof(sp[0])*4);
+ sp[3] = sp[2] = sp[1] = nil;
+ strcpy(sp[0] = (char*)&sp[4], "boot");
+ touser(sp);
+}
+
+void
+main(void)
+{
+ uartinit();
+ machinit();
+ mmuinit();
+ plan9iniinit();
+ confinit();
+ printinit();
+ quotefmtinstall();
+ fmtinstall(L'H', encodefmt);
+ print("\nPlan 9\n");
+
+ xinit();
+ trapinit();
+ intrinit();
+ timerinit();
+ cpuidprint();
+ procinit0();
+ initseg();
+ links();
+
+ screeninit();
+ chandevreset();
+
+ pageinit();
+ userinit();
+ schedinit();
+
+ panic("schedinit returned");
+}
+
+void
+exit(int)
+{
+ for(;;)
+ idlehands();
+}
+
+void
+reboot(void *, void *, ulong)
+{
+}
+
+void
+setupwatchpts(Proc *, Watchpt *, int n)
+{
+ if(n > 0)
+ error("no watchpoints");
+}
--- /dev/null
+++ b/mem.h
@@ -1,0 +1,105 @@
+#define KiB (1024u)
+#define MiB (1024*1024u)
+#define GiB (1024*1024*1024u)
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+#define Rmach 10
+#define Rup 9
+
+#define HZ (100)
+#define MS2HZ (1000/HZ)
+#define TK2SEC(t) ((t)/HZ)
+
+#define CONFADDR 0x80010000
+
+#define KZERO 0x80000000
+#define KTZERO 0x80020000
+#define KSTACK (8*KiB)
+
+#define UZERO 0
+#define UTZERO BY2PG
+#define USTKTOP 0x20000000
+#define USTKSIZE (16*MiB)
+
+#define MAXMACH 1
+#define MACHSZ (16*KiB)
+#define L1SZ (4*4096)
+#define L2SZ (4*256)
+
+#define L1X(va) ((((va)>>20) & 0xfff) << 2)
+
+#define MACH(n) (KZERO+(n)*MACHSZ)
+#define MACHP(n) ((Mach*)MACH(n))
+#define MACHL1(n) (MACH(MAXMACH)+(n)*L1SZ)
+#define MACHVEC(n) (MACHL1(MAXMACH)+(n)*64*4)
+
+#define BY2PG (4*KiB)
+#define BY2SE 4
+#define BY2WD 4
+#define BY2V 8
+
+#define CACHELINEZ 64
+#define BLOCKALIGN 32
+
+#define ROUND(s, sz) (((s)+((sz)-1))&~((sz)-1))
+#define PGROUND(s) ROUND(s, BY2PG)
+#define PGSHIFT 12
+
+#define PTEMAPMEM MiB
+#define PTEPERTAB (PTEMAPMEM/BY2PG)
+#define SEGMAPSIZE 1984
+#define SSEGMAPSIZE 16
+
+#define PPN(p) ((p)&~(BY2PG-1))
+
+#define PTEVALID (1<<0)
+#define PTERONLY (0<<1)
+#define PTEWRITE (1<<1)
+#define PTECACHED (0<<2)
+#define PTEUNCACHED (1<<2)
+#define PTENOEXEC (1<<3)
+
+#define PsrDfiq 0x40
+#define PsrDirq 0x80
+
+#define PsrMusr 0x10
+#define PsrMfiq 0x11
+#define PsrMirq 0x12
+#define PsrMsvc 0x13
+#define PsrMmon 0x16
+#define PsrMiabt 0x17
+#define PsrMdabt 0x18
+#define PsrMund 0x1b
+#define PsrMsys 0x1f
+
+#define PsrMask 0x1f
+
+#define WFI WORD $0xe320f003
+#define DSB WORD $0xf57ff04f
+#define DMB WORD $0xf57ff05f
+#define ISB WORD $0xf57ff06f
+
+#define CPSIE WORD $0xf1080080
+#define CPSID WORD $0xf10c0080
+
+#define CLZ(s, d) WORD $(0xe16f0f10 | (d) << 12 | (s))
+#define VMSR(cpu, fp) WORD $(0xeee00a10|(fp)<<16|(cpu)<<12)
+#define VMRS(fp, cpu) WORD $(0xeef00a10|(fp)<<16|(cpu)<<12)
+
+#define FPSID 0x0
+#define FPSCR 0x1
+#define MVFR1 0x6
+#define MVFR0 0x7
+#define FPEXC 0x8
+#define FPEXCEX (1<<31)
+#define FPEXCEN (1<<30)
+
+/* vlmdia r0!, {d0-d15}
+ * vldmia r0!, {d16-d31} */
+#define VLDMIA WORD $0xecb00b20; WORD $0xecf00b20;
+
+/* vstmia r0!, {d0-d15}
+ * vstmia r0!, {d16-d31} */
+#define VSTMIA WORD $0xeca00b20; WORD $0xece00b20;
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,82 @@
+CONF=n900
+CONFLIST=n900
+
+p=9
+objtype=arm
+ktzero=0x80020000
+
+</$objtype/mkfile
+
+DEVS=`{rc ../port/mkdevlist $CONF}
+PORT=\
+ alarm.$O\
+ alloc.$O\
+ allocb.$O\
+ auth.$O\
+ cache.$O\
+ chan.$O\
+ dev.$O\
+ edf.$O\
+ fault.$O\
+ mul64fract.$O\
+ rebootcmd.$O\
+ page.$O\
+ parse.$O\
+ pgrp.$O\
+ portclock.$O\
+ print.$O\
+ proc.$O\
+ qio.$O\
+ qlock.$O\
+ segment.$O\
+ sysfile.$O\
+ sysproc.$O\
+ taslock.$O\
+ tod.$O\
+ xalloc.$O\
+ random.$O\
+ rdb.$O\
+ syscallfmt.$O\
+ userinit.$O\
+ ucalloc.$O\
+
+OBJ=\
+ l.$O\
+ lcache.$O\
+ ltrap.$O\
+ main.$O\
+ mmu.$O\
+ timer.$O\
+ trap.$O\
+ intr.$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\
+ /$objtype/lib/libdtracy.a\
+
+$p$CONF.u:D: $p$CONF
+ aux/aout2uimage -o $target -Z0 $prereq
+
+$p$CONF:D: $OBJ $CONF.$O $LIB
+ $LD -o $target -T$ktzero -l $prereq
+
+<../boot/bootmkfile
+<../port/portmkfile
+<|../port/mkbootrules $CONF
+
+initcode.out: init9.$O initcode.$O /$objtype/lib/libc.a
+ $LD -l -R1 -s -o $target $prereq
+
+install:V: $p$CONF.u
+ cp $p$CONF /$objtype/
+ cp $p$CONF.u /$objtype/
--- /dev/null
+++ b/mmcn900.c
@@ -1,0 +1,285 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "../port/error.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/pci.h"
+#include "../port/sd.h"
+
+enum {
+ Rsysc = 0x10,
+ SCreset = 1 << 1,
+ Rsyss = 0x14,
+ SSreset = 1 << 0,
+ Rcsre = 0x24,
+ Rcon = 0x28,
+ Rpwcnt = 0x2c,
+ Rblk = 0x104,
+ Rarg = 0x108,
+ Rcmd = 0x10c,
+ CRnone = 0 << 16,
+ CR136 = 1 << 16,
+ CR48 = 2 << 16,
+ CR48busy = 3 << 16,
+ CRmask = 3 << 16,
+
+ CFcheckidx = 1 << 19,
+ CFcheckcrc = 1 << 20,
+ CFdata = 1 << 21,
+ CFdataread = 1 << 4,
+ CFdatamulti = 1 << 5,
+
+ CTnone = 0 << 22,
+ CTbus = 1 << 22,
+ CTfunc = 2 << 22,
+ CTio = 3 << 22,
+ Rrsp10 = 0x110,
+ Rrsp32 = 0x114,
+ Rrsp54 = 0x118,
+ Rrsp76 = 0x11c,
+ Rdata = 0x120,
+ Rpstate = 0x124,
+ Rhctl = 0x128,
+ Rsysctl = 0x12c,
+ Rstatus = 0x130,
+ STcmd = 1 << 0,
+ STtransfer = 1 << 1,
+ STbufwrite = 1 << 4,
+ STbufread = 1 << 5,
+
+ STmaskok = 0xffff << 0,
+ STmaskerr = 0xffff << 16,
+ Rie = 0x134,
+ Rise = 0x138,
+ Rac12 = 0x13c,
+ Rcapa = 0x140,
+ Rcapacur = 0x148,
+ Rrev = 0x1fc,
+};
+
+#define csr32r(c, r) ((c)->io[(r)/4])
+#define csr32w(c, r, w) ((c)->io[(r)/4] = (w))
+
+typedef struct Ctlr Ctlr;
+struct Ctlr {
+ u32int *io;
+ ulong irq;
+
+ struct {
+ uint bcount;
+ uint bsize;
+ } cmd;
+
+ Lock;
+ Rendez;
+};
+
+static int
+n900mmcinit(SDio *io)
+{
+ Ctlr *ctlr;
+
+ ctlr = io->aux;
+ csr32w(ctlr, Rsysc, SCreset);
+ while(!(csr32r(ctlr, Rsyss) & SSreset))
+ ;
+
+ return 0;
+}
+
+static void
+n900mmcenable(SDio *)
+{
+}
+
+static int
+n900mmcinquiry(SDio *, char *inquiry, int len)
+{
+ return snprint(inquiry, len, "MMC Host Controller");
+}
+
+static int
+n900mmccmd(SDio *io, SDiocmd *iocmd, u32int arg, u32int *resp)
+{
+ Ctlr *ctlr;
+ u32int cmd;
+
+ /* prepare flags for this command */
+ ctlr = io->aux;
+ cmd = iocmd->index << 24;
+ switch(iocmd->resp) {
+ case 0: cmd |= CRnone; break;
+ case 2: cmd |= CR136 | CFcheckcrc; break;
+ case 3: cmd |= CR48; break;
+ case 1:
+ if(iocmd->busy) {
+ cmd |= CR48busy | CFcheckidx | CFcheckcrc;
+ break;
+ }
+
+ default:
+ cmd |= CR48 | CFcheckidx | CFcheckcrc;
+ break;
+ }
+
+ /* if there is data, set the data, read, and multi flags */
+ if(iocmd->data) {
+ cmd |= CFdata;
+ if(iocmd->data & 1)
+ cmd |= CFdataread;
+ if(iocmd->data > 2)
+ cmd |= CFdatamulti;
+ }
+
+ /* off it goes, wait for a response */
+ csr32w(ctlr, Rstatus, ~0);
+ csr32w(ctlr, Rarg, arg);
+ csr32w(ctlr, Rcmd, cmd);
+
+ /* FIXME: interrupts */
+ while(!(csr32r(ctlr, Rstatus) & STcmd)) {
+ if(csr32r(ctlr, Rstatus) & STmaskerr)
+ error(Eio);
+
+ delay(10);
+ }
+
+ /* unpack the response */
+ switch(cmd & CRmask) {
+ case CRnone:
+ resp[0] = 0;
+ break;
+
+ case CR136:
+ resp[0] = csr32r(ctlr, Rrsp10);
+ resp[1] = csr32r(ctlr, Rrsp32);
+ resp[2] = csr32r(ctlr, Rrsp54);
+ resp[3] = csr32r(ctlr, Rrsp76);
+ break;
+
+ case CR48:
+ case CR48busy:
+ resp[0] = csr32r(ctlr, Rrsp10);
+ break;
+ }
+
+ return 0;
+}
+
+static void
+n900mmciosetup(SDio *io, int, void *, int bsize, int bcount)
+{
+ Ctlr *ctlr;
+
+ ctlr = io->aux;
+ if(bsize == 0 || (bsize & 3) != 0)
+ error(Egreg);
+
+ ctlr->cmd.bsize = bsize;
+ ctlr->cmd.bcount = bcount;
+ csr32w(ctlr, Rblk, (bsize & 0x3ff) | (bcount << 16));
+}
+
+static void
+n900mmcbufread(Ctlr *ctlr, uchar *buf, int len)
+{
+ for(len >>= 2; len > 0; len--) {
+ *((u32int*)buf) = csr32r(ctlr, Rdata);
+ buf += 4;
+ }
+}
+
+static void
+n900mmcbufwrite(Ctlr *ctlr, uchar *buf, int len)
+{
+ for(len >>= 2; len > 0; len--) {
+ csr32w(ctlr, Rdata, *((u32int*)buf));
+ buf += 4;
+ }
+}
+
+static void
+n900mmcio(SDio *io, int write, uchar *buf, int len)
+{
+ Ctlr *ctlr;
+ u32int stat, n;
+
+ ctlr = io->aux;
+ if(len != ctlr->cmd.bsize * ctlr->cmd.bcount)
+ error(Egreg);
+
+ while(len > 0) {
+ stat = csr32r(ctlr, Rstatus);
+ if(stat & STmaskerr) {
+ csr32w(ctlr, Rstatus, STmaskerr);
+ error(Eio);
+ }
+
+ if(stat & STbufwrite) {
+ csr32w(ctlr, Rstatus, STbufwrite);
+ if(!write)
+ error(Eio);
+
+ n = len;
+ if(n > ctlr->cmd.bsize)
+ n = ctlr->cmd.bsize;
+
+ n900mmcbufwrite(ctlr, buf, n);
+ len -= n;
+ buf += n;
+ }
+
+ if(stat & STbufread) {
+ csr32w(ctlr, Rstatus, STbufread);
+ if(write)
+ error(Eio);
+
+ n = len;
+ if(n > ctlr->cmd.bsize)
+ n = ctlr->cmd.bsize;
+
+ n900mmcbufread(ctlr, buf, n);
+ len -= n;
+ buf += n;
+ }
+
+ if(stat & STtransfer) {
+ csr32w(ctlr, Rstatus, STtransfer);
+ if(len != 0)
+ error(Eio);
+ }
+ }
+}
+
+static void
+n900mmcbus(SDio *, int, int)
+{
+ /* FIXME: change bus width */
+}
+
+void
+mmcn900link(void)
+{
+ int i;
+ static Ctlr ctlr[2] = {
+ { .io = (u32int*) PHYSMMC1, .irq = IRQMMC1, },
+ { .io = (u32int*) PHYSMMC2, .irq = IRQMMC2, },
+ };
+
+ static SDio io[nelem(ctlr)];
+ for(i = 0; i < nelem(io); i++) {
+ io[i].name = "mmc",
+ io[i].init = n900mmcinit,
+ io[i].enable = n900mmcenable,
+ io[i].inquiry = n900mmcinquiry,
+ io[i].cmd = n900mmccmd,
+ io[i].iosetup = n900mmciosetup,
+ io[i].io = n900mmcio,
+ io[i].bus = n900mmcbus,
+ io[i].aux = &ctlr[i];
+
+ addmmcio(&io[i]);
+ }
+}
--- /dev/null
+++ b/mmu.c
@@ -1,0 +1,308 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+static int debug = 0;
+
+#define L1(va) (((va)>>20) & 0xfff)
+#define L2(va) (((va)>>12) & 0xff)
+#define L1AP(ap) (((ap) << 10) & 0xff) /* dont care about ap[2] */
+#define L2AP(ap) (((ap) << 4) & 0xff)
+
+enum {
+ /* l1 descriptor type (arm v7 manual fig. 12.4) */
+ L1coarse = 1,
+ L1section = 2,
+ L1fine = 3,
+
+ /* l2 descriptor type (arm v7 manual fig. 12.5) */
+ L2large = 1,
+ L2small = 2,
+ L2tiny = 3,
+
+ Fnoexec = 1<<0,
+ Fbuffered = 1<<2,
+ Fcached = 1<<3,
+
+ APkrw = 1, /* kernel read write */
+ APuro = 2, /* user read only */
+ APurw = 3, /* user read write */
+};
+
+void
+mmudebugl2(PTE *l2, uintptr va)
+{
+ uintptr startpa, pa;
+ uintptr startva, endva;
+ int i, t;
+
+ t = 0;
+ startpa = 0;
+ startva = endva = 0;
+ for(i = 0; i < 256; i++) {
+ pa = l2[i] & ~((4*KiB)-1);
+ if(l2[i] == 0) {
+ if(endva) {
+ iprint("mmudebug: l2 type %#ux %#lux %#lux -> %#lux\n", t, startva, endva, startpa);
+ endva = 0;
+ }
+ } else {
+ if(!endva) {
+ startva = va;
+ startpa = pa;
+ t = l2[i] & (L2large | L2small | L2tiny);
+ }
+
+ endva = va + (4*KiB);
+ }
+
+ va += (4*KiB);
+ }
+
+ if(endva)
+ iprint("mmudebug: l2 type %#ux %#lux %#lux -> %#lux\n", t, startva, endva, startpa);
+}
+
+void
+mmudebug(char *where)
+{
+ PTE *l1;
+ uintptr pa, startpa;
+ uintptr va, startva, endva;
+ int i, t;
+
+ if(!debug)
+ return;
+
+ iprint("mmudebug: %s pid %d\n", where, m->mmupid);
+
+ t = 0;
+ l1 = m->mmul1;
+ startpa = 0;
+ startva = endva = 0;
+ for(va = i = 0; i < 4096; i++) {
+ pa = l1[i] & ~(MiB-1);
+ if(l1[i] == 0) {
+ if(endva) {
+ iprint("mmudebug: l1 type %#ux %#lux %#lux -> %#lux\n", t, startva, endva, startpa);
+ endva = 0;
+ }
+ } else {
+ if(!endva) {
+ startva = va;
+ startpa = pa;
+ t = l1[i] & (L1coarse|L1section|L1fine);
+ }
+
+ if(t == L1coarse) {
+ mmudebugl2((PTE*) (l1[i] & ~(KiB-1)), startva);
+ endva = 0;
+ } else {
+ endva = va + MB;
+ }
+ }
+
+ va += MB;
+ }
+
+ if(endva)
+ iprint("mmudebug: l1 type %#ux %#lux %#lux -> %#lux\n", t, startva, endva, startpa);
+}
+
+void
+mmuinit(void)
+{
+ m->mmul1 = (PTE*)MACHL1(m->machno);
+}
+
+static void
+mmul1empty(void)
+{
+ memset(m->mmul1, 0, (ROUND(USTKTOP, MiB)/MiB)*sizeof(PTE));
+}
+
+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((void*)page->va, 0, BY2PG);
+
+ l1[page->daddr] = 0;
+ l2 = &page->next;
+ }
+
+ *l2 = proc->mmul2cache;
+ proc->mmul2cache = proc->mmul2;
+ proc->mmul2 = nil;
+}
+
+void
+mmuswitch(Proc *proc)
+{
+ PTE *l1;
+ Page *page;
+ int l1x;
+
+ if(m->mmupid == proc->pid && !proc->newtlb)
+ return;
+ m->mmupid = proc->pid;
+
+ /* write back and invalidate caches */
+ l1ucachewbinv();
+ l2ucachewbinv();
+
+ if(proc->newtlb) {
+ mmul2empty(proc, 1);
+ proc->newtlb = 0;
+ }
+
+ mmul1empty();
+
+ /* switch to new map */
+ l1 = m->mmul1;
+ for(page = proc->mmul2; page != nil; page = page->next) {
+ l1x = page->daddr;
+ l1[l1x] = PPN(page->pa)|L1coarse;
+ }
+
+ /* FIXME: excessive invalidation */
+ l1ucachewbinv();
+ l2ucachewbinv();
+ mmuinvalidate();
+ mmudebug("mmuswitch");
+}
+
+void
+mmurelease(Proc *proc)
+{
+ l1ucachewbinv();
+ l2ucachewbinv();
+
+ mmul2empty(proc, 0);
+
+ freepages(proc->mmul2cache, nil, 0);
+ proc->mmul2cache = nil;
+
+ mmul1empty();
+
+ /* FIXME: excessive invalidation */
+ l1ucachewbinv();
+ l2ucachewbinv();
+ mmuinvalidate();
+ mmudebug("mmurelease");
+}
+
+void*
+mmuuncache(void *v, usize s)
+{
+ PTE *l1;
+ uintptr va;
+
+ assert(!((uintptr)v & (MiB-1)) && s == MiB);
+
+ va = (uintptr)v;
+ l1 = &m->mmul1[L1(va)];
+ if((*l1 & (L1fine|L1section|L1coarse)) != L1section)
+ return nil;
+
+ *l1 &= ~(Fbuffered|Fcached);
+
+ /* FIXME: excessive invalidation */
+ l1ucachewbinv();
+ l2ucachewbinv();
+ mmuinvalidate();
+ mmudebug("mmuuncache");
+
+ return v;
+}
+
+void
+putmmu(uintptr va, uintptr pa, Page *page)
+{
+ PTE *l1, *l2;
+ Page *pg;
+ int l1x;
+ int x;
+
+ l1x = L1(va);
+ l1 = &m->mmul1[l1x];
+
+ /* put l1 for l2 table if needed */
+ if(*l1 == 0) {
+ if(up->mmul2cache == nil) {
+ pg = newpage(1, 0, 0);
+ pg->va = VA(kmap(pg));
+ } else {
+ pg = up->mmul2cache;
+ up->mmul2cache = pg->next;
+ memset((void*)pg->va, 0, BY2PG);
+ }
+
+ pg->daddr = l1x;
+ pg->next = up->mmul2;
+ up->mmul2 = pg;
+
+ /* FIXME: excessive invalidation */
+ *l1 = PPN(pg->pa)|L1coarse;
+ l1ucachewbinv();
+ l2ucachewbinv();
+ }
+
+ /* put l2 entry */
+ x = L2small;
+ if(!(pa & PTEUNCACHED))
+ x |= Fbuffered|Fcached;
+ if(pa & PTENOEXEC)
+ x |= Fnoexec;
+ if(pa & PTEWRITE)
+ x |= L2AP(APurw);
+ else
+ x |= L2AP(APuro);
+
+ l2 = KADDR(PPN(*l1)); l2[L2(va)] = PPN(pa)|x;
+
+ /* FIXME: excessive invalidation */
+ l1ucachewbinv();
+ l2ucachewbinv();
+ if(needtxtflush(page)) {
+ l1icacheinv();
+ donetxtflush(page);
+ }
+
+ mmuinvalidate();
+ mmudebug("putmmu");
+}
+
+void
+checkmmu(uintptr, uintptr)
+{
+ /* this page is intentionally left blank */
+}
+
+void
+flushmmu(void)
+{
+ uint s;
+
+ s = splhi();
+ up->newtlb = 1; mmuswitch(up);
+ splx(s);
+}
+
+uintptr
+cankaddr(uintptr pa)
+{
+ if(pa >= PHYSMEM && pa < PHYSMEMEND)
+ return PHYSMEMEND-pa;
+
+ return 0;
+}
--- /dev/null
+++ b/n900
@@ -1,0 +1,49 @@
+dev
+ root
+ cons
+ swap
+ env
+ pipe
+ proc
+ mnt
+ srv
+ shr
+ dup
+ tls
+ cap
+ sd
+ fs
+ dtracy
+
+ draw screen
+
+ kbd devi2c
+ rtc devi2c
+
+ uart
+ usb
+ i2c
+
+misc
+ uartn900
+
+ sdmmc mmcn900
+ sdloop
+ sdram
+
+ dtracysys
+ dtracytimer
+ dtracydev
+
+link
+ i2cn900 devi2c
+ mmcn900
+
+port
+ int cpuserver = 0;
+
+bootdir
+ /$objtype/bin/paqfs
+ /$objtype/bin/auth/factotum
+ boot
+ bootfs.paq
--- /dev/null
+++ b/screen.c
@@ -1,0 +1,373 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#define Image IMAGE
+#include <draw.h>
+#include <memdraw.h>
+#include "screen.h"
+
+enum {
+ /* display controller registers */
+ RDrev = 0x400,
+ RDsysconf = 0x410,
+ DSCidle = 1<<0,
+ DSCreset = 1<<1,
+ DSCidlesmart = 2<<3,
+ RDsysstat = 0x414,
+ DSSreset = 1<<0,
+ RDirqstat = 0x418,
+ RDirqen = 0x41c,
+ RDcontrol = 0x440,
+ DClcdon = 1<<0,
+ DClcdactive = 1<<3,
+ DClcdclockrun = 1<<27,
+ DClcdenable = 1<<28,
+ DClcdenablehi = 1<<29,
+ DClcdbits24 = 3<<8,
+ RDconfig = 0x444,
+ DCNgamma = 1<<3,
+ RDdefcolor = 0x44c,
+ RDtranscolor = 0x454,
+ RDlinestat = 0x45c,
+ RDlineintr = 0x460,
+ RDtimeh = 0x464,
+ RDtimev = 0x468,
+ RDsize = 0x47c,
+
+ /* display controller graphics layer registers */
+ RDgfxba = 0x480,
+ RDgfxpos = 0x488,
+ RDgfxsize = 0x48c,
+ RDgfxattr = 0x4a0,
+ DGAenable = 1<<0,
+ DGAfmt = 0x6<<1,
+ DGAburst = 0x2<<6,
+ RDgfxrowinc = 0x4ac,
+ RDgfxpixelinc = 0x4b0,
+};
+
+enum {
+ Tab = 4,
+ Scroll = 8,
+};
+
+#define RDdefcolorn(n) (RDdefcolor + (n)*4)
+#define RDtranscolorn(n) (RDtranscolor + (n)*4)
+
+#define RDgfxban(n) (RDgfxba + (n)*4)
+
+#define TIME(s, f, b) ((s & 0xff) | (f & 0xfff) << 8 | (b & 0xfff) << 20)
+#define POS(x, y) ((x)<<16 | (y))
+#define SIZE(w, h) ((h-1)<<16 | (w-1))
+
+typedef struct Ctlr Ctlr;
+struct Ctlr {
+ Lock;
+
+ u32int *io;
+
+ Memdata scrdata;
+ Memimage *scrimg;
+ Memsubfont *scrfont;
+
+ Rectangle win;
+ Point font;
+ Point pos;
+};
+
+static Ctlr ctlr = {
+ .io = (u32int*) PHYSDSS,
+};
+
+#define csr32r(c, r) ((c)->io[(r)/4])
+#define csr32w(c, r, w) ((c)->io[(r)/4] = (w))
+
+static void myscreenputs(char *s, int n);
+
+static int
+screendatainit(Ctlr *ctlr)
+{
+ Rectangle r;
+
+ if(memimageinit() < 0)
+ return -1;
+
+ r = Rect(0, 0, 800, 480);
+ ctlr->scrdata.ref = 1;
+ ctlr->scrdata.bdata = ucalloc(r.max.x * r.max.y * 2);
+ if(!ctlr->scrdata.bdata)
+ return -1;
+
+ ctlr->scrimg = allocmemimaged(r, RGB16, &ctlr->scrdata);
+ if(!ctlr->scrimg)
+ return -1;
+
+ ctlr->scrfont = getmemdefont();
+ return 0;
+}
+
+static Memimage*
+screenmkcolor(Memimage *scr, ulong color)
+{
+ Memimage *i;
+
+ i = allocmemimage(Rect(0, 0, 1, 1), scr->chan);
+ if(i) {
+ i->flags |= Frepl;
+ i->clipr = scr->r;
+ memfillcolor(i, color);
+ }
+
+ return i;
+}
+
+static void
+screenwin(Ctlr *ctlr)
+{
+ Rectangle r;
+ Memimage *i;
+ Point p;
+ int h;
+
+ ctlr->font.y = h = ctlr->scrfont->height;
+ ctlr->font.x = ctlr->scrfont->info[' '].width;
+
+ r = ctlr->scrimg->r;
+ if(i = screenmkcolor(ctlr->scrimg, 0x0D686BFF)) {
+ memimagedraw(ctlr->scrimg, r, i, ZP, memopaque, ZP, S);
+ freememimage(i);
+ }
+
+ r = insetrect(r, 16); memimagedraw(ctlr->scrimg, r, memblack, ZP, memopaque, ZP, S);
+ r = insetrect(r, 4); memimagedraw(ctlr->scrimg, r, memwhite, ZP, memopaque, ZP, S);
+ if(i = screenmkcolor(ctlr->scrimg, 0xaaaaaaff)) {
+ memimagedraw(ctlr->scrimg, Rpt(r.min, Pt(r.max.x, r.min.y+h+12)), i, ZP, memopaque, ZP, S);
+ freememimage(i);
+
+ r = insetrect(r, 6);
+ p = r.min;
+ memimagestring(ctlr->scrimg, p, memblack, ZP, getmemdefont(), " Plan 9 Console ");
+ }
+
+ ctlr->win = Rpt(addpt(r.min, Pt(0, h + 6)), subpt(r.max, Pt(6, 6)));
+ ctlr->pos = ctlr->win.min;
+ screenputs = myscreenputs;
+}
+
+void
+screeninit(void)
+{
+ Ctlr *c;
+
+ c = &ctlr;
+ if(screendatainit(c) < 0)
+ return;
+
+ screenwin(c);
+ screenputs(kmesg.buf, kmesg.n);
+
+ /* reset the display controller */
+ csr32w(c, RDsysconf, DSCreset);
+ while(!(csr32r(c, RDsysstat) & DSSreset))
+ ;
+
+ /* configure the display controller */
+ csr32w(c, RDsysconf, DSCidle | DSCidlesmart);
+ csr32w(c, RDcontrol, DClcdbits24);
+ csr32w(c, RDconfig, DCNgamma);
+
+ /* configure display size and timings */
+ csr32w(c, RDtimeh, TIME(3, 15, 11));
+ csr32w(c, RDtimev, TIME(2, 3, 3));
+ csr32w(c, RDsize, SIZE(800, 480));
+
+ /* enable the lcd interface */
+ csr32w(c, RDcontrol, csr32r(c, RDcontrol) | DClcdactive | DClcdclockrun | DClcdenablehi);
+ csr32w(c, RDcontrol, csr32r(c, RDcontrol) | DClcdenable);
+
+ /* configure the graphics layer */
+ csr32w(c, RDgfxban(0), (uintptr) c->scrdata.bdata);
+ csr32w(c, RDgfxpos, POS(c->scrimg->r.min.x, c->scrimg->r.min.y));
+ csr32w(c, RDgfxsize, SIZE(c->scrimg->r.max.x, c->scrimg->r.max.y));
+ csr32w(c, RDgfxattr, DGAfmt | DGAburst);
+ csr32w(c, RDgfxrowinc, 1);
+ csr32w(c, RDgfxpixelinc, 1);
+
+ /* enable gfx pipeline and turn lcd on */
+ csr32w(c, RDgfxattr, csr32r(c, RDgfxattr) | DGAenable);
+ csr32w(c, RDcontrol, csr32r(c, RDcontrol) | DClcdon);
+
+ conf.monitor = 1;
+}
+
+static void
+screenscroll(Ctlr *ctlr)
+{
+ int o, h;
+ Point p;
+ Rectangle r;
+
+ h = ctlr->font.y;
+ o = Scroll * h;
+ r = Rpt(ctlr->win.min, Pt(ctlr->win.max.x, ctlr->win.max.y - o));
+ p = Pt(ctlr->win.min.x, ctlr->win.min.y + o);
+ memimagedraw(ctlr->scrimg, r, ctlr->scrimg, p, nil, p, S);
+ flushmemscreen(r);
+
+ r = Rpt(Pt(ctlr->win.min.x, ctlr->win.max.y - o), ctlr->win.max);
+ memimagedraw(ctlr->scrimg, r, memwhite, ZP, nil, ZP, S);
+ flushmemscreen(r);
+
+ ctlr->pos.y -= o;
+
+}
+
+static void
+screenputc(Ctlr *ctlr, char *buf)
+{
+ Point p;
+ Rectangle r;
+ uint chr;
+ int w, h;
+ static int *xp;
+ static int xbuf[256];
+
+ w = ctlr->font.x;
+ h = ctlr->font.y;
+ if(xp < xbuf || xp >= &xbuf[nelem(xbuf)])
+ xp = xbuf;
+
+ switch(buf[0]) {
+ case '\n':
+ if(ctlr->pos.y + h >= ctlr->win.max.y)
+ screenscroll(ctlr);
+
+ ctlr->pos.y += h;
+ screenputc(ctlr, "\r");
+ break;
+
+ case '\r':
+ xp = xbuf;
+ ctlr->pos.x = ctlr->win.min.x;
+ break;
+
+ case '\t':
+ if(ctlr->pos.x >= ctlr->win.max.x - 4 * w)
+ screenputc(ctlr, "\n");
+
+ chr = (ctlr->pos.x - ctlr->win.min.x) / w;
+ chr = Tab - chr % Tab;
+ *xp++ = ctlr->pos.x;
+
+ r = Rect(ctlr->pos.x, ctlr->pos.y, ctlr->pos.x + chr * w, ctlr->pos.y + h);
+ memimagedraw(ctlr->scrimg, r, memwhite, ZP, memopaque, ZP, S);
+ flushmemscreen(r);
+ ctlr->pos.x += chr * w;
+ break;
+
+ case '\b':
+ if(xp <= xbuf)
+ break;
+
+ xp--;
+ r = Rect(*xp, ctlr->pos.y, ctlr->pos.x, ctlr->pos.y + h);
+ memimagedraw(ctlr->scrimg, r, memwhite, ZP, memopaque, ZP, S);
+ ctlr->pos.x = *xp;
+ break;
+
+ case '\0':
+ break;
+
+ default:
+ p = memsubfontwidth(getmemdefont(), buf); w = p.x;
+ if(ctlr->pos.x >= ctlr->win.max.x - w)
+ screenputc(ctlr, "\n");
+
+ *xp++ = ctlr->pos.x;
+ r = Rect(ctlr->pos.x, ctlr->pos.y, ctlr->pos.x + w, ctlr->pos.y + h);
+ memimagedraw(ctlr->scrimg, r, memwhite, ZP, memopaque, ZP, S);
+ memimagestring(ctlr->scrimg, ctlr->pos, memblack, ZP, getmemdefont(), buf);
+ ctlr->pos.x += w;
+ break;
+ }
+}
+
+static void
+myscreenputs(char *s, int n)
+{
+ Ctlr *c;
+ Rune r;
+ int i;
+ char buf[UTFmax];
+
+ c = &ctlr;
+ if(!c->scrimg)
+ return;
+
+ if(!islo()) {
+ /* don't deadlock trying to print in an interrupt */
+ if(!canlock(c))
+ return;
+ } else {
+ lock(c);
+ }
+
+ while(n > 0) {
+ i = chartorune(&r, s);
+ if(i == 0) {
+ s++; n--;
+ continue;
+ }
+
+ memmove(buf, s, i);
+ buf[i] = 0;
+ s += i; n -= i;
+ screenputc(c, buf);
+ }
+
+ unlock(c);
+}
+
+Memdata*
+attachscreen(Rectangle *r, ulong *chan, int *d, int *width, int *softscreen)
+{
+ Ctlr *c;
+
+ c = &ctlr;
+ if(!c->scrimg)
+ return nil;
+
+ *r = c->scrimg->r;
+ *d = c->scrimg->depth;
+ *chan = c->scrimg->chan;
+ *width = c->scrimg->width;
+ *softscreen = 1;
+
+ c->scrdata.ref++;
+ return &c->scrdata;
+}
+
+void
+getcolor(ulong, ulong *, ulong *, ulong *)
+{
+}
+
+int
+setcolor(ulong, ulong, ulong, ulong)
+{
+ return 0;
+}
+
+void
+flushmemscreen(Rectangle)
+{
+}
+
+void
+mouseresize(void)
+{
+}
--- /dev/null
+++ b/screen.h
@@ -1,0 +1,9 @@
+void screeninit(void);
+void flushmemscreen(Rectangle);
+Memdata *attachscreen(Rectangle *, ulong *, int *, int *, int *);
+
+#define ishwimage(i) 1
+
+void mousectl(Cmdbuf*);
+void mouseresize(void);
+void mouseredraw(void);
--- /dev/null
+++ b/timer.c
@@ -1,0 +1,172 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+enum {
+ Rrev = 0x00,
+ Rsyscfg = 0x10,
+ Cidle = 1<<0,
+ Creset = 1<<1,
+ Rsysstat = 0x14,
+ Sreset = 1<<0,
+ Risr = 0x18,
+ Rier = 0x1c,
+ Imatch = 1<<0,
+ Ioverflow = 1<<1,
+ Icapture = 1<<2,
+ Rclr = 0x24,
+ CLst = 1<<0,
+ CLar = 1<<1,
+ CLtgovf = 1<<10,
+ CLtgmatch = 1<<11,
+ Rcrr = 0x28,
+ Rldr = 0x2c,
+};
+
+typedef union Counter Counter;
+typedef struct Ctlr Ctlr;
+
+union Counter {
+ uvlong cnt;
+ struct {
+ ulong cntlo;
+ ulong cnthi;
+ };
+};
+
+struct Ctlr {
+ Lock;
+ Counter;
+
+ u32int *io;
+};
+
+static Ctlr timers[] = {
+ { .io = (u32int*) PHYSTIMER1 }, /* for cycles */
+ { .io = (u32int*) PHYSTIMER2 }, /* for interrupts */
+};
+
+#define csr32r(c, r) ((c)->io[(r)/4])
+#define csr32w(c, r, w) ((c)->io[(r)/4] = (w))
+
+static void
+timerreset(Ctlr *ctlr)
+{
+ int i;
+ int cfg;
+
+ cfg = csr32r(ctlr, Rsyscfg);
+ cfg |= Creset;
+ cfg &= ~Cidle;
+
+ ilock(ctlr);
+ csr32w(ctlr, Rsyscfg, cfg);
+ for(i = 40000; i > 0; i++) {
+ if(csr32r(ctlr, Rsysstat) & Sreset)
+ break;
+ }
+
+ if(i == 0)
+ panic("clock reset failed");
+
+ iunlock(ctlr);
+}
+
+static void
+timerstartcycles(Ctlr *ctlr)
+{
+ timerreset(ctlr);
+
+ /* configure this timer for measuring cycles */
+ ilock(ctlr);
+ csr32w(ctlr, Rldr, 0);
+ csr32w(ctlr, Rclr, CLst | CLar);
+ iunlock(ctlr);
+}
+
+static void
+timerstartintr(Ctlr *ctlr, ulong t)
+{
+ timerreset(ctlr);
+
+ /* configure this timer for periodic interrupts */
+ ilock(ctlr);
+ csr32w(ctlr, Rier, Ioverflow);
+ csr32w(ctlr, Rldr, -t);
+ csr32w(ctlr, Rcrr, -t);
+ csr32w(ctlr, Rclr, CLst | CLar);
+ iunlock(ctlr);
+}
+
+static void
+timerinterrupt(Ureg *u, void *arg)
+{
+ Ctlr *ctlr = arg;
+ csr32w(ctlr, Risr, Ioverflow);
+
+ timerintr(u, 0);
+}
+
+void
+timerinit(void)
+{
+ intrenable(IRQTIMER2, timerinterrupt, &timers[1], BUSUNKNOWN, "timer");
+
+ timerstartcycles(&timers[0]);
+ timerstartintr(&timers[1], 32);
+}
+
+uvlong
+fastticks(uvlong *hz)
+{
+ Counter c;
+ Ctlr *ctlr;
+
+ /* FIXME: this has poor precision, but qemu has no cycle counter */
+ ctlr = &timers[0];
+ if(hz)
+ *hz = 32*1024;
+
+ ilock(ctlr);
+ c.cnt = ctlr->cnt;
+ c.cntlo = csr32r(ctlr, Rcrr);
+ if(c.cnt < ctlr->cnt)
+ c.cnthi++;
+
+ ctlr->cnt = c.cnt;
+ iunlock(ctlr);
+
+ return ctlr->cnt;
+}
+
+ulong
+µs(void)
+{
+ return fastticks2us(fastticks(nil));
+}
+
+void
+microdelay(int n)
+{
+ ulong now;
+
+ now = µs();
+ while(µs() - now < n)
+ ;
+}
+
+void
+delay(int n)
+{
+ while(--n >= 0)
+ microdelay(1000);
+}
+
+void
+timerset(Tval)
+{
+ /* FIXME: ? */
+}
--- /dev/null
+++ b/trap.c
@@ -1,0 +1,570 @@
+#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"
+
+void
+callwithureg(void (*f) (Ureg *))
+{
+ Ureg u;
+
+ u.pc = getcallerpc(&f);
+ u.sp = (uintptr) &f - 4;
+ f(&u);
+}
+
+void
+dumpstackureg(Ureg *ureg)
+{
+ uintptr l, v, i, estack;
+ int x;
+
+ x = 0;
+ x += iprint("ktrace /arm/9n900 %#.8lux %#.8lux %#.8lux <<EOF\n",
+ ureg->pc, ureg->sp, ureg->r14);
+
+ i = 0;
+ if(up)
+ estack = (uintptr)up;
+ else
+ estack = (uintptr)m+MACHSZ;
+
+ 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=%.8p ", l, v);
+ i++;
+ }
+
+ if(i == 4) {
+ i = 0;
+ x += iprint("\n");
+ }
+ }
+
+ if(i)
+ iprint("\n");
+ iprint("EOF\n");
+}
+
+void
+dumpstack(void)
+{
+ callwithureg(dumpstackureg);
+}
+
+void
+dumpureg(Ureg *ureg)
+{
+ if(up)
+ iprint("cpu%d: registers for %s %lud\n", m->machno, up->text, up->pid);
+ else
+ iprint("cpu%d: registers for kernel\n", m->machno);
+
+ iprint("r0 %#.8lux\tr1 %#.8lux\tr2 %#.8lux\tr3 %#.8lux\n", ureg->r0, ureg->r1, ureg->r2, ureg->r3);
+ iprint("r4 %#.8lux\tr5 %#.8lux\tr6 %#.8lux\tr7 %#.8lux\n", ureg->r4, ureg->r5, ureg->r6, ureg->r7);
+ iprint("r8 %#.8lux\tr9 %#.8lux\tr10 %#.8lux\tr11 %#.8lux\n", ureg->r8, ureg->r9, ureg->r10, ureg->r11);
+ iprint("r12 %#.8lux\tr13 %#.8lux\tr14 %#.8lux\tr15 %#.8lux\n", ureg->r12, ureg->r13, ureg->r14, ureg->pc);
+}
+
+
+uintptr
+userpc(void)
+{
+ return ((Ureg*)up->dbgreg)->pc;
+}
+
+uintptr
+dbgpc(Proc *)
+{
+ if(up->dbgreg)
+ return userpc();
+
+ return 0;
+}
+
+void
+procsetup(Proc *p)
+{
+ p->fpstate = FPinit;
+ fpoff();
+}
+
+void
+procsave(Proc *p)
+{
+ if(p->fpstate == FPactive) {
+ if(p->state == Moribund)
+ fpclear();
+ else
+ fpsave(p->fpsave);
+ p->fpstate = FPinactive;
+ }
+}
+
+void
+procrestore(Proc *)
+{
+}
+
+void
+procfork(Proc *p)
+{
+ ulong s;
+
+ s = splhi();
+ switch(up->fpstate & ~FPillegal) {
+ case FPactive:
+ fpsave(up->fpsave);
+ up->fpstate = FPinactive;
+ case FPinactive:
+ memmove(p->fpsave, up->fpsave, sizeof(FPsave));
+ p->fpstate = FPinactive;
+ }
+
+ splx(s);
+}
+
+void
+kprocchild(Proc *p, void (*entry)(void))
+{
+ p->sched.pc = (uintptr) entry;
+ p->sched.sp = (uintptr) p;
+}
+
+void
+forkchild(Proc *p, Ureg *ureg)
+{
+ Ureg *cureg;
+
+ p->sched.sp = (uintptr) p - sizeof(Ureg);
+ p->sched.pc = (uintptr) forkret;
+
+ cureg = (Ureg*) p->sched.sp;
+ memmove(cureg, ureg, sizeof(Ureg));
+ cureg->r0 = 0;
+}
+
+uintptr
+execregs(uintptr entry, ulong ssize, ulong nargs)
+{
+ ulong *sp;
+ Ureg *ureg;
+
+ sp = (ulong*)(USTKTOP - ssize); *--sp = nargs;
+ ureg = up->dbgreg;
+ ureg->sp = (uintptr) sp;
+ ureg->pc = entry;
+ ureg->r14 = 0;
+
+ return USTKTOP-sizeof(Tos);
+}
+
+void
+setkernur(Ureg *ureg, Proc *p)
+{
+ ureg->pc = p->sched.pc;
+ ureg->sp = p->sched.sp+4;
+ ureg->r14 = (uintptr) sched;
+}
+
+void
+setregisters(Ureg *ureg, char *pureg, char *uva, int n)
+{
+ uvlong v;
+
+ v = ureg->psr;
+ memmove(pureg, uva, n);
+ ureg->psr &= ~(PsrMask|PsrDfiq|PsrDirq);
+ ureg->psr |= v & (PsrMask|PsrDfiq|PsrDirq);
+}
+
+void
+evenaddr(uintptr addr)
+{
+ if(addr & 3) {
+ postnote(up, 1, "sys: odd address", NDebug);
+ error(Ebadarg);
+ }
+}
+
+int
+notify(Ureg *ureg)
+{
+ ulong s, sp;
+ char *msg;
+
+ if(up->procctl)
+ procctl();
+ if(up->nnote == 0)
+ return 0;
+
+ s = spllo();
+ qlock(&up->debug);
+ msg = popnote(ureg);
+ if(msg == nil) {
+ qunlock(&up->debug);
+ splhi();
+ return 0;
+ }
+
+ sp = ureg->sp;
+ sp -= 256;
+ sp -= sizeof(Ureg);
+
+ if(!okaddr((uintptr)up->notify, 1, 0)
+ || !okaddr(sp-ERRMAX-4*BY2WD, sizeof(Ureg)+ERRMAX+4*BY2WD, 1)
+ || ((uintptr) up->notify & 3) != 0
+ || (sp & 3) != 0) {
+ qunlock(&up->debug);
+ pprint("suicide: bad address in notify\n");
+ pexit("Suicide", 0);
+ }
+
+ memmove((Ureg*)sp, ureg, sizeof(Ureg));
+ *(Ureg**)(sp-BY2WD) = up->ureg;
+ up->ureg = (void*)sp;
+ sp -= BY2WD+ERRMAX;
+ memmove((char*)sp, msg, ERRMAX);
+ sp -= 3*BY2WD;
+ *(uintptr*)(sp+2*BY2WD) = sp+3*BY2WD;
+ *(uintptr*)(sp+1*BY2WD) = (uintptr)up->ureg;
+ ureg->r0 = (uintptr) up->ureg;
+ ureg->sp = sp;
+ ureg->pc = (uintptr) up->notify;
+ ureg->r14 = 0;
+
+ qunlock(&up->debug);
+ splx(s);
+ return 1;
+}
+
+void
+noted(Ureg *ureg, ulong arg0)
+{
+ Ureg *nureg;
+ ulong oureg, sp;
+
+ qlock(&up->debug);
+ if(arg0 != NRSTR && !up->notified) {
+ qunlock(&up->debug);
+ iprint("called to noted when not notified\n");
+ pexit("Suicide", 0);
+ }
+
+ up->notified = 0;
+ nureg = up->ureg;
+ oureg = (ulong) nureg;
+ if(!okaddr(oureg - BY2WD, BY2WD + sizeof(Ureg), 0) || (oureg & 3) != 0) {
+ qunlock(&up->debug);
+ pprint("bad ureg in noted or call to noted when not notifed\n");
+ pexit("Suicide", 0);
+ }
+
+ nureg->psr &= PsrMask|PsrDfiq|PsrDirq;
+ nureg->psr |= (ureg->psr & ~(PsrMask|PsrDfiq|PsrDirq));
+
+ memmove(ureg, nureg, sizeof(Ureg));
+ switch(arg0) {
+ case NCONT:
+ case NRSTR:
+ if(!okaddr(nureg->pc, BY2WD, 0) || (nureg->pc & 3) != 0
+ || !okaddr(nureg->sp, BY2WD, 0) || (nureg->sp & 3) != 0) {
+ qunlock(&up->debug);
+ pprint("suicide: trap in noted\n");
+ pexit("Suicide", 0);
+ }
+
+ up->ureg = (Ureg *) (*(ulong*) (oureg - BY2WD));
+ qunlock(&up->debug);
+ break;
+
+ case NSAVE:
+ if(!okaddr(nureg->pc, BY2WD, 0) || (nureg->pc & 3) != 0
+ || !okaddr(nureg->sp, BY2WD, 0) || (nureg->sp & 3) != 0) {
+ qunlock(&up->debug);
+ pprint("suicide: trap in noted\n");
+ pexit("Suicide", 0);
+ }
+
+ qunlock(&up->debug);
+ sp = oureg - 4 * BY2WD - ERRMAX;
+ splhi();
+ ureg->sp = sp;
+ ureg->r0 = (uintptr) oureg;
+ ((ulong*) sp)[1] = oureg;
+ ((ulong*) sp)[0] = 0;
+ break;
+
+ default:
+ up->lastnote->flag = NDebug;
+ /* wet floor */
+
+ case NDFLT:
+ qunlock(&up->debug);
+ if(up->lastnote->flag == NDebug)
+ pprint("suicide: %s\n", up->lastnote->msg);
+
+ pexit(up->lastnote->msg, up->lastnote->flag != NDebug);
+ break;
+ }
+}
+
+void
+trapinit(void)
+{
+ extern ulong vectors[];
+
+ /* install stack pointer for other exception modes */
+ setR13(PsrMfiq, m->save);
+ setR13(PsrMirq, m->save);
+ setR13(PsrMiabt, m->save);
+ setR13(PsrMund, m->save);
+ setR13(PsrMsys, m->save);
+
+ /* install vectors and vtable to MACHVEC because vectors must be
+ * aligned on a 128 byte boundary */
+ memmove((ulong*)MACHVEC(m->machno), vectors, 64 * 4);
+
+ /* set vectors base address */
+ setvectors(MACHVEC(m->machno));
+}
+
+static void
+trapfpu(void)
+{
+ int s;
+
+ if((up->fpstate & FPillegal) != 0) {
+ postnote(up, 1, "sys: floating point in note handler", NDebug);
+ return;
+ }
+
+ switch(up->fpstate) {
+ case FPinit:
+ s = splhi();
+ fpinit(); up->fpstate = FPactive;
+ splx(s);
+ break;
+
+ case FPinactive:
+ s = splhi();
+ fprestore(up->fpsave); up->fpstate = FPactive;
+ splx(s);
+ break;
+
+ case FPactive:
+ postnote(up, 1, "sys: floating point error", NDebug);
+ break;
+ }
+}
+
+static void
+traparm(Ureg *ureg, ulong fsr, uintptr far)
+{
+ int user;
+ int read;
+ int syscall;
+
+ static char buf[ERRMAX];
+
+ read = (fsr & (1<<11)) == 0;
+ user = userureg(ureg);
+ if(!user) {
+ if(far >= USTKTOP)
+ panic("kernel fault: bad address pc=%#.8lux far=%#.8lux fsr=%#.8lux",
+ ureg->pc, far, fsr);
+ if(up == nil)
+ panic("kernel fault: no user process pc=%#.8lux far=%#.8lux fsr=%#.8lux",
+ ureg->pc, far, fsr);
+ }
+
+ if(up == nil) {
+ panic("user fault: up=nil pc=%#.8lux far=%#.8lux fsr=%#.8lux",
+ ureg->pc, far, fsr);
+ }
+
+ syscall = up->insyscall; up->insyscall = 1;
+ switch(fsr & 0x1f) {
+ case 0x03: /* l1 access flag fault */
+ case 0x05: /* l1 translation fault */
+ case 0x06: /* l2 access flag fault */
+ case 0x07: /* l2 translation fault */
+ case 0x09: /* l1 domain fault */
+ case 0x0b: /* l2 domain fault */
+ case 0x0d: /* l1 permission fault */
+ case 0x0f: /* l2 permission fault */
+ if(fault(far, ureg->pc, read) == 0)
+ break;
+
+ default:
+ if(!user)
+ panic("kernel fault: pc=%#.8lux far=%#.8lux fsr=%#.8lux",
+ ureg->pc, far, fsr);
+
+ dumpureg(ureg);
+ dumpstackureg(ureg);
+ snprint(buf, sizeof(buf), "sys: trap: fault %s far=%#.8lux fsr=%#.8lux",
+ read ? "read" : "write", far, fsr);
+ postnote(up, 1, buf, NDebug);
+ }
+
+ up->insyscall = syscall;
+}
+
+void
+trap(Ureg *ureg)
+{
+ int user;
+ u32int op, cp;
+
+ user = kenter(ureg);
+ switch(ureg->type) {
+ case PsrMfiq:
+ case PsrMirq:
+ ureg->pc -= 4;
+ intr(ureg);
+ break;
+
+ case PsrMiabt:
+ ureg->pc -= 4;
+ traparm(ureg, getifsr(), getifar());
+ break;
+
+ case PsrMdabt:
+ ureg->pc -= 8;
+ traparm(ureg, getdfsr(), getdfar());
+ break;
+
+ case PsrMund:
+ ureg->pc -= 4;
+ if(user) {
+ spllo();
+ if(okaddr(ureg->pc, 4, 0)) {
+ op = *(u32int*)ureg->pc;
+ if((op & 0x0f000000) == 0x0e000000 || (op & 0x0e000000) == 0x0c000000) {
+ cp = op >> 8 & 15;
+ if(cp == 10 || cp == 11) {
+ trapfpu();
+ break;
+ }
+ }
+ }
+
+ postnote(up, 1, "sys: trap: invalid opcode", NDebug);
+ break;
+ }
+
+ panic("invalid opcode at pc=%#.8lux lr=%#.8lux", ureg->pc, ureg->r14);
+ break;
+
+ default:
+ panic("unknown trap at pc=%#.8lux lr=%#.8lux", ureg->pc, ureg->r14);
+ break;
+ }
+
+ splhi();
+ if(user) {
+ if(up->procctl || up->nnote)
+ notify(ureg);
+
+ kexit(ureg);
+ }
+}
+
+void
+syscall(Ureg *ureg)
+{
+ char *e;
+ uintptr sp;
+ long ret;
+ int i, s;
+ ulong scallnr;
+ vlong startns, stopns;
+
+ if(!kenter(ureg))
+ panic("syscall: from kernel: pc=%#.8lux", ureg->pc);
+
+ m->syscall++;
+ up->insyscall = 1;
+ up->pc = ureg->pc;
+
+ scallnr = up->scallnr = ureg->r0;
+ sp = ureg->sp;
+
+ spllo();
+
+ up->nerrlab = 0;
+ ret = -1;
+ if(!waserror()) {
+ if(scallnr >= nsyscall) {
+ pprint("bad sys call number %lux pc %#lux", 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);
+ evenaddr(sp);
+ }
+
+ up->s = *((Sargs*)(sp + BY2WD));
+ up->psstate = sysctab[scallnr];
+ if (up->procctl == Proc_tracesyscall) {
+ syscallfmt(scallnr, ureg->pc, (va_list)up->s.args);
+ s = splhi();
+ up->procctl = Proc_stopme;
+ procctl();
+ splx(s);
+ startns = todget(nil);
+ }
+
+ ret = systab[scallnr]((va_list)up->s.args);
+ poperror();
+ } else {
+ e = up->syserrstr;
+ up->syserrstr = up->errstr;
+ up->errstr = e;
+ }
+
+ if(up->nerrlab) {
+ print("bad errstack [%lud]: %d extra\n", scallnr, up->nerrlab);
+ for (i = 0; i < NERR; i++)
+ print("sp=%lux pc=%lux\n", up->errlab[i].sp, up->errlab[i].pc);
+
+ panic("error stack");
+ }
+
+ ureg->r0 = ret;
+ if(up->procctl == Proc_tracesyscall) {
+ stopns = todget(nil);
+ sysretfmt(scallnr, (va_list)up->s.args, ret, startns, stopns);
+ s = splhi();
+ up->procctl = Proc_stopme;
+ procctl();
+ splx(s);
+ }
+
+ up->insyscall = 0;
+ up->psstate = 0;
+ if(scallnr == NOTED)
+ noted(ureg, *((ulong *)up->s.args));
+
+ if(scallnr != RFORK && (up->procctl || up->nnote)) {
+ splhi();
+ notify(ureg);
+ }
+
+ if(up->delaysched)
+ sched();
+
+ kexit(ureg);
+ splhi();
+}
--- /dev/null
+++ b/uartn900.c
@@ -1,0 +1,268 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+enum {
+ Rdll = 0x00,
+ Rrhr = 0x00,
+ Rthr = 0x00,
+ Rdlh = 0x04,
+ Rier = 0x04,
+ IErhr = 1 << 0,
+ IEthr = 1 << 1,
+ IEls = 1 << 2,
+ IEms = 1 << 3,
+ Riir = 0x08,
+ Ipending = 1 << 0,
+ Imodem = 0x00,
+ Ithr = 0x01,
+ Irhr = 0x02,
+ Ils = 0x03,
+ Irxt = 0x06,
+ Ixoff = 0x08,
+ Icts = 0x10,
+ Imask = 0x1f,
+ Rfcr = 0x08,
+ FCRen = 1 << 0,
+ Refr = 0x08,
+ Rlcr = 0x0c,
+ Rmcr = 0x10,
+ Rxon1 = 0x10,
+ Rlsr = 0x14,
+ LSRrxempty = 1 << 0,
+ LSRrxover = 1 << 1,
+ LSRrxparity = 1 << 2,
+ LSRrxframe = 1 << 3,
+ LSRrxbreak = 1 << 4,
+ LSRtxempty = 1 << 5,
+ LSRtxshift = 1 << 6,
+ LSRrxstat = 1 << 7,
+ Rxon2 = 0x14,
+ Rmsr = 0x18,
+ Rtcr = 0x18,
+ Rxoff1 = 0x18,
+ Rspr = 0x1c,
+ Rtlr = 0x1c,
+ Rxoff2 = 0x1c,
+ Rmdr1 = 0x20,
+ Rmdr2 = 0x24,
+ Rsysc = 0x54,
+ SCreset = 1 << 1,
+ Rsyss = 0x58,
+ SSreset = 1 << 0,
+};
+
+#define csr32r(c, r) ((c)->io[(r)/4])
+#define csr32w(c, r, w) ((c)->io[(r)/4] = (w))
+
+typedef struct Ctlr Ctlr;
+struct Ctlr {
+ Lock;
+
+ u32int *io;
+ ulong irq;
+
+ int ie;
+};
+
+extern PhysUart n900physuart;
+
+static Ctlr ctlr[] = {
+ { .io = (u32int*) PHYSUART1, .irq = IRQUART1, },
+ { .io = (u32int*) PHYSUART2, .irq = IRQUART2, },
+ { .io = (u32int*) PHYSUART3, .irq = IRQUART3, },
+};
+
+static Uart n900uart[] = {
+ {
+ .regs = &ctlr[0],
+ .name = "uart1",
+ .freq = 48000000,
+ .phys = &n900physuart,
+ .next = &n900uart[1],
+ },
+ {
+ .regs = &ctlr[1],
+ .name = "uart2",
+ .freq = 48000000,
+ .phys = &n900physuart,
+ .next = &n900uart[2],
+ },
+ {
+ .regs = &ctlr[2],
+ .name = "uart3",
+ .freq = 48000000,
+ .phys = &n900physuart,
+ .next = nil,
+ },
+};
+
+static Uart *
+n900uartpnp(void)
+{
+ return n900uart;
+}
+
+static long
+n900uartstatus(Uart *, void *, long, long)
+{
+ return 0;
+}
+
+static void
+n900uartintr(Ureg *, void *arg)
+{
+ Uart *uart;
+ Ctlr *ctlr;
+ int lsr;
+ char c;
+
+ uart = arg;
+ ctlr = uart->regs;
+
+ ilock(ctlr);
+ switch((csr32r(ctlr, Riir) >> 1) & Imask) {
+ case Ithr:
+ uartkick(uart);
+ break;
+
+ case Irhr:
+ while((lsr = csr32r(ctlr, Rlsr)) & LSRrxempty) {
+ c = csr32r(ctlr, Rrhr);
+
+ if(lsr & LSRrxover) { uart->oerr++; break; }
+ if(lsr & LSRrxparity) { uart->perr++; break; }
+ if(lsr & LSRrxframe) { uart->ferr++; break; }
+
+ uartrecv(uart, c);
+ }
+
+ break;
+ }
+
+ iunlock(ctlr);
+}
+
+static void
+n900uartenable(Uart *uart, int ie)
+{
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ ilock(ctlr);
+
+ csr32w(ctlr, Rsysc, SCreset);
+ while(!(csr32r(ctlr, Rsyss) & SSreset))
+ ;
+
+ csr32w(ctlr, Rfcr, FCRen);
+ if(ie) {
+ if(!ctlr->ie) {
+ intrenable(ctlr->irq, n900uartintr, uart, 0, uart->name);
+ ctlr->ie = 1;
+ }
+
+ csr32w(ctlr, Rier, IErhr);
+ }
+
+ iunlock(ctlr);
+}
+
+static void
+n900uartdisable(Uart *uart)
+{
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+
+ ilock(ctlr);
+ csr32w(ctlr, Rier, 0);
+ if(ctlr->ie) {
+ intrdisable(ctlr->irq, n900uartintr, uart, 0, uart->name);
+ ctlr->ie = 0;
+ }
+
+ iunlock(ctlr);
+}
+
+static void
+n900uartkick(Uart *uart)
+{
+ Ctlr *ctlr;
+ int i;
+
+ ctlr = uart->regs;
+ if(uart->blocked)
+ return;
+
+ for(i = 0; i < 128; i++) {
+ if(csr32r(ctlr, Rlsr) & LSRtxempty) {
+ if(uart->op >= uart->oe && uartstageoutput(uart) == 0)
+ break;
+
+ csr32w(ctlr, Rthr, *uart->op++);
+ }
+ }
+}
+
+static int
+n900uartgetc(Uart *uart)
+{
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ while(!(csr32r(ctlr, Rlsr) & LSRrxempty))
+ ;
+
+ return csr32r(ctlr, Rrhr);
+}
+
+static void
+n900uartputc(Uart *uart, int c)
+{
+ Ctlr *ctlr;
+
+ ctlr = uart->regs;
+ while(!(csr32r(ctlr, Rlsr) & LSRtxempty))
+ ;
+
+ csr32w(ctlr, Rthr, c);
+}
+
+static void n900uartnop(Uart *, int) {}
+static int n900uartnope(Uart *, int) { return -1; }
+
+PhysUart n900physuart = {
+ .name = "n900",
+
+ .pnp = n900uartpnp,
+ .enable = n900uartenable,
+ .disable = n900uartdisable,
+ .kick = n900uartkick,
+ .status = n900uartstatus,
+ .getc = n900uartgetc,
+ .putc = n900uartputc,
+
+ .dobreak = n900uartnop,
+ .baud = n900uartnope,
+ .bits = n900uartnope,
+ .stop = n900uartnope,
+ .parity = n900uartnope,
+ .modemctl = n900uartnop,
+ .rts = n900uartnop,
+ .dtr = n900uartnop,
+ .fifo = n900uartnop,
+ .power = n900uartnop,
+};
+
+void
+uartinit(void)
+{
+ consuart = &n900uart[2];
+ consuart->console = 1;
+ uartputs(kmesg.buf, kmesg.n);
+}