summaryrefslogtreecommitdiff
path: root/os/sa1110
diff options
context:
space:
mode:
Diffstat (limited to 'os/sa1110')
-rw-r--r--os/sa1110/clock.c317
-rw-r--r--os/sa1110/devether.c617
-rw-r--r--os/sa1110/devgpio.c165
-rw-r--r--os/sa1110/devpcmcia.c761
-rw-r--r--os/sa1110/devpower.c377
-rw-r--r--os/sa1110/devrtc.c169
-rw-r--r--os/sa1110/devuart.c781
-rw-r--r--os/sa1110/dma.c233
-rw-r--r--os/sa1110/etherif.h41
-rw-r--r--os/sa1110/fpi.h61
-rw-r--r--os/sa1110/fpiarm.c483
-rw-r--r--os/sa1110/gscreen.c587
-rw-r--r--os/sa1110/gscreen.h87
-rw-r--r--os/sa1110/i2c.h16
-rw-r--r--os/sa1110/i2cgpio.c274
-rw-r--r--os/sa1110/l.s530
-rw-r--r--os/sa1110/l3gpio.c246
-rw-r--r--os/sa1110/mmu.c140
-rw-r--r--os/sa1110/sa1110break.c360
-rw-r--r--os/sa1110/sa1110io.h500
-rw-r--r--os/sa1110/softcursor.c365
-rw-r--r--os/sa1110/suspend.c161
-rw-r--r--os/sa1110/trap.c601
23 files changed, 7872 insertions, 0 deletions
diff --git a/os/sa1110/clock.c b/os/sa1110/clock.c
new file mode 100644
index 00000000..784844e9
--- /dev/null
+++ b/os/sa1110/clock.c
@@ -0,0 +1,317 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+#include "ureg.h"
+
+static ulong timer_incr[4] = { 0, 0, 0, -1 };
+
+typedef struct Clock0link Clock0link;
+typedef struct Clock0link {
+ void (*clock)(void);
+ Clock0link* link;
+} Clock0link;
+
+static Clock0link *clock0link;
+static Lock clock0lock;
+static void (*prof_fcn)(Ureg *, int);
+
+Timer*
+addclock0link(void (*clock)(void), int)
+{
+ Clock0link *lp;
+
+ if((lp = malloc(sizeof(Clock0link))) == 0){
+ print("addclock0link: too many links\n");
+ return nil;
+ }
+ ilock(&clock0lock);
+ lp->clock = clock;
+ lp->link = clock0link;
+ clock0link = lp;
+ iunlock(&clock0lock);
+ return nil;
+}
+
+static void
+profintr(Ureg *ur, void*)
+{
+ OstmrReg *ost = OSTMRREG;
+ int t;
+
+ if((ost->osmr[3] - ost->oscr) < 2*CLOCKFREQ) {
+ /* less than 2 seconds before reset, say something */
+ setpanic();
+ clockpoll();
+ dumpregs(ur);
+ panic("Watchdog timer will expire");
+ }
+
+ /* advance the profile clock tick */
+ ost->osmr[2] += timer_incr[2];
+ ost->ossr = (1 << 2); /* Clear the SR */
+ t = 1;
+ while((ost->osmr[2] - ost->oscr) > 0x80000000) {
+ ost->osmr[2] += timer_incr[2];
+ t++;
+ }
+ if(prof_fcn)
+ prof_fcn(ur, t);
+}
+
+static void
+clockintr(Ureg*, void*)
+{
+ Clock0link *lp;
+ int losttick = 0;
+ OstmrReg *ost = OSTMRREG;
+
+ m->ticks++;
+ ost->osmr[3] = ost->oscr + timer_incr[3]; /* restart the watchdog */
+ ost->osmr[0] += timer_incr[0]; /* advance the clock tick */
+ ost->ossr = (1<<0); /* Clear the SR */
+
+ while((ost->osmr[0] - ost->oscr) >= 0x80000000) {
+ ost->osmr[0] += timer_incr[0];
+ losttick++;
+ m->ticks++;
+ }
+
+ checkalarms();
+
+ if(canlock(&clock0lock)){
+ for(lp = clock0link; lp; lp = lp->link)
+ if(lp->clock)
+ lp->clock();
+ unlock(&clock0lock);
+ }
+
+ /* round robin time slice is done by trap.c and proc.c */
+}
+
+void
+timerenable( int timer, int Hz, void (*f)(Ureg *, void*), void* a)
+{
+ OstmrReg *ost = OSTMRREG;
+ char name[KNAMELEN];
+
+ if(timer < 0 || timer > 3)
+ return;
+ timer_incr[timer] = CLOCKFREQ/Hz; /* set up freq */
+ ost->osmr[timer] = ost->oscr+timer_incr[timer];
+ snprint(name, sizeof(name), "timer%d", timer);
+ intrenable(OSTimerbit(timer), f, a, BusCPU, name);
+ ost->ossr = (1 << timer); /* clear any pending interrupt */
+ ost->oier |= (1 << timer); /* enable interrupt */
+}
+
+void
+timerdisable( int timer )
+{
+ OstmrReg *ost = OSTMRREG;
+
+ if(timer < 0 || timer > 3)
+ return;
+ ost->osmr[timer] = 0; /* clear freq */
+ ost->oier &= ~(1 << timer); /* disable interrupt */
+}
+
+void
+installprof(void (*pf)(Ureg *, int))
+{
+ int s;
+
+ s = splfhi();
+ prof_fcn = pf;
+ timerenable( 2, HZ+1, profintr, 0);
+ timer_incr[2] = timer_incr[0]+63; /* fine tuning */
+ splx(s);
+}
+
+void
+clockinit(void)
+{
+ OstmrReg *ost = OSTMRREG;
+ m->ticks = 0;
+ /* Set up timer registers */
+ ost->ossr = 0xf; /* clear all four OSSR trigger bits */
+ ost->oier = 0;
+ ost->osmr[0] = 0;
+ ost->osmr[1] = 0;
+ ost->osmr[2] = 0;
+ // ost->osmr[3] = 0;
+ timerenable( 0, HZ, clockintr, 0);
+ timer_incr[3] = CLOCKFREQ*10; /* 10 second watchdog */
+ timer_setwatchdog(timer_incr[3]);
+ timerenable( 2, 1, profintr, 0); /* watch the watchdog */
+}
+
+void
+clockpoll(void)
+{
+ OstmrReg *ost = OSTMRREG;
+ ost->osmr[3] = ost->oscr + timer_incr[3]; /* restart the watchdog */
+}
+
+void
+clockcheck(void)
+{
+ OstmrReg *ost = OSTMRREG;
+
+ if((ost->osmr[3] - ost->oscr) < CLOCKFREQ) {
+ setpanic();
+ clockpoll();
+ dumpstack();
+ panic("Watchdog timer will expire");
+ }
+}
+
+// macros for fixed-point math
+
+ulong _mularsv(ulong m0, ulong m1, ulong a, ulong s);
+
+/* truncated: */
+#define FXDPTDIV(a,b,n) ((ulong)(((uvlong)(a) << (n)) / (b)))
+#define MAXMUL(a,n) ((ulong)((((uvlong)1<<(n))-1)/(a)))
+#define MULDIV(x,a,b,n) (((x)*FXDPTDIV(a,b,n)) >> (n))
+#define MULDIV64(x,a,b,n) ((ulong)_mularsv(x, FXDPTDIV(a,b,n), 0, (n)))
+
+/* rounded: */
+#define FXDPTDIVR(a,b,n) ((ulong)((((uvlong)(a) << (n))+((b)/2)) / (b)))
+#define MAXMULR(a,n) ((ulong)((((uvlong)1<<(n))-1)/(a)))
+#define MULDIVR(x,a,b,n) (((x)*FXDPTDIVR(a,b,n)+(1<<((n)-1))) >> (n))
+#define MULDIVR64(x,a,b,n) ((ulong)_mularsv(x, FXDPTDIVR(a,b,n), 1<<((n)-1), (n)))
+
+
+// these routines are all limited to a maximum of 1165 seconds,
+// due to the wrap-around of the OSTIMER
+
+ulong
+timer_start(void)
+{
+ return OSTMRREG->oscr;
+}
+
+ulong
+timer_ticks(ulong t0)
+{
+ return OSTMRREG->oscr - t0;
+}
+
+int
+timer_devwait(ulong *adr, ulong mask, ulong val, int ost)
+{
+ int i;
+ ulong t0 = timer_start();
+ while((*adr & mask) != val)
+ if(timer_ticks(t0) > ost)
+ return ((*adr & mask) == val) ? 0 : -1;
+ else
+ for(i = 0; i < 10; i++); /* don't pound OSCR too hard! (why not?) */
+ return 0;
+}
+
+void
+timer_setwatchdog(int t)
+{
+ OstmrReg *ost = OSTMRREG;
+ ost->osmr[3] = ost->oscr + t;
+ if(t) {
+ ost->ossr = (1<<3);
+ ost->oier |= (1<<3);
+ ost->ower = 1;
+ } else
+ ost->oier &= ~(1<<3);
+}
+
+void
+timer_delay(int t)
+{
+ ulong t0 = timer_start();
+ while(timer_ticks(t0) < t)
+ ;
+}
+
+
+ulong
+us2tmr(int us)
+{
+ return MULDIV64(us, CLOCKFREQ, 1000000, 24);
+}
+
+int
+tmr2us(ulong t)
+{
+ return MULDIV64(t, 1000000, CLOCKFREQ, 24);
+}
+
+void
+microdelay(int us)
+{
+ ulong t0 = timer_start();
+ ulong t = us2tmr(us);
+ while(timer_ticks(t0) <= t)
+ ;
+}
+
+ulong
+ms2tmr(int ms)
+{
+ return MULDIV64(ms, CLOCKFREQ, 1000, 20);
+}
+
+int
+tmr2ms(ulong t)
+{
+ return MULDIV64(t, 1000, CLOCKFREQ, 32);
+}
+
+void
+delay(int ms)
+{
+ ulong t0 = timer_start();
+ ulong t = ms2tmr(ms);
+ while(timer_ticks(t0) <= t)
+ clockpoll();
+}
+
+/*
+ * for devbench.c
+ */
+vlong
+archrdtsc(void)
+{
+ return OSTMRREG->oscr;
+}
+
+ulong
+archrdtsc32(void)
+{
+ return OSTMRREG->oscr;
+}
+
+/*
+ * for devkprof.c
+ */
+long
+archkprofmicrosecondspertick(void)
+{
+ return MS2HZ*1000;
+}
+
+void
+archkprofenable(int)
+{
+ /* TO DO */
+}
+
+uvlong
+fastticks(uvlong *hz)
+{
+ if(hz)
+ *hz = HZ;
+ return m->ticks;
+}
diff --git a/os/sa1110/devether.c b/os/sa1110/devether.c
new file mode 100644
index 00000000..f4f0c456
--- /dev/null
+++ b/os/sa1110/devether.c
@@ -0,0 +1,617 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+static Ether *etherxx[MaxEther];
+
+Chan*
+etherattach(char* spec)
+{
+ ulong ctlrno;
+ char *p;
+ Chan *chan;
+ Ether *ether;
+
+ ctlrno = 0;
+ if(spec && *spec){
+ ctlrno = strtoul(spec, &p, 0);
+ if((ctlrno == 0 && p == spec) || *p || (ctlrno >= MaxEther))
+ error(Ebadarg);
+ }
+ if((ether = etherxx[ctlrno]) == 0)
+ error(Enodev);
+ rlock(ether);
+ if(waserror()){
+ runlock(ether);
+ nexterror();
+ }
+ chan = devattach('l', spec);
+ chan->dev = ctlrno;
+ if(ether->attach)
+ ether->attach(etherxx[ctlrno]);
+ poperror();
+ runlock(ether);
+ return chan;
+}
+
+static void
+ethershutdown(void)
+{
+ Ether *ether;
+ int i;
+
+ for(i=0; i<MaxEther; i++){
+ ether = etherxx[i];
+ if(ether != nil && ether->detach != nil)
+ ether->detach(ether);
+ }
+}
+
+static Walkqid*
+etherwalk(Chan* chan, Chan *nchan, char **name, int nname)
+{
+ Walkqid *wq;
+ Ether *ether;
+
+ ether = etherxx[chan->dev];
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ wq = netifwalk(etherxx[chan->dev], chan, nchan, name, nname);
+ poperror();
+ runlock(ether);
+ return wq;
+}
+
+static int
+etherstat(Chan* chan, uchar* dp, int n)
+{
+ int s;
+ Ether *ether;
+
+ ether = etherxx[chan->dev];
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ s = netifstat(ether, chan, dp, n);
+ poperror();
+ runlock(ether);
+ return s;
+}
+
+static Chan*
+etheropen(Chan* chan, int omode)
+{
+ Chan *c;
+ Ether *ether;
+
+ ether = etherxx[chan->dev];
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ c = netifopen(ether, chan, omode);
+ poperror();
+ runlock(ether);
+ return c;
+}
+
+static void
+ethercreate(Chan*, char*, int, ulong)
+{
+}
+
+static void
+etherclose(Chan* chan)
+{
+ Ether *ether;
+
+ ether = etherxx[chan->dev];
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ netifclose(ether, chan);
+ poperror();
+ runlock(ether);
+}
+
+static long
+etherread(Chan* chan, void* buf, long n, vlong off)
+{
+ Ether *ether;
+ ulong offset = off;
+ long r;
+
+ ether = etherxx[chan->dev];
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ 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){
+ r = ether->ifstat(ether, buf, n, offset);
+ goto out;
+ }
+ if(NETTYPE(chan->qid.path) == Nstatqid)
+ ether->ifstat(ether, buf, 0, offset);
+ }
+ r = netifread(ether, chan, buf, n, offset);
+out:
+ poperror();
+ runlock(ether);
+ return r;
+}
+
+static Block*
+etherbread(Chan* chan, long n, ulong offset)
+{
+ Block *b;
+ Ether *ether;
+
+ ether = etherxx[chan->dev];
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ b = netifbread(ether, chan, n, offset);
+ poperror();
+ runlock(ether);
+ return b;
+}
+
+static int
+etherwstat(Chan* chan, uchar* dp, int n)
+{
+ Ether *ether;
+ int r;
+
+ ether = etherxx[chan->dev];
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ r = netifwstat(ether, chan, dp, n);
+ poperror();
+ runlock(ether);
+ return r;
+}
+
+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 multcast 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) && (f->type == type || f->type < 0))
+ if(tome || multi || f->prom){
+ /* Don't want to hear bridged packets */
+ if(f->bridge && !fromwire && !fromme)
+ continue;
+ if(!f->headersonly){
+ if(fromwire && fx == 0)
+ fx = f;
+ else if(xbp = iallocb(len)){
+ memmove(xbp->wp, pkt, len);
+ xbp->wp += len;
+ if(qpass(f->in, xbp) < 0)
+ ether->soverflows++;
+ }
+ else
+ ether->soverflows++;
+ }
+ else
+ etherrtrace(f, pkt, len);
+ }
+ }
+
+ if(fx){
+ if(qpass(fx->in, bp) < 0)
+ ether->soverflows++;
+ return 0;
+ }
+ if(fromwire){
+ freeb(bp);
+ return 0;
+ }
+
+ return bp;
+}
+
+static int
+etheroq(Ether* ether, Block* bp)
+{
+ int len, loopback, s;
+ Etherpkt *pkt;
+
+ ether->outpackets++;
+
+ /*
+ * Check if the packet has to be placed back onto the input queue,
+ * i.e. if it's a loopback or broadcast packet or the interface is
+ * in promiscuous mode.
+ * If it's a loopback packet indicate to etheriq that the data isn't
+ * needed and return, etheriq will pass-on or free the block.
+ * To enable bridging to work, only packets that were originated
+ * by this interface are fed back.
+ */
+ pkt = (Etherpkt*)bp->rp;
+ len = BLEN(bp);
+ loopback = memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
+ if(loopback || memcmp(pkt->d, ether->bcast, sizeof(pkt->d)) == 0 || ether->prom){
+ s = splhi();
+ etheriq(ether, bp, 0);
+ splx(s);
+ }
+
+ if(!loopback){
+ qbwrite(ether->oq, bp);
+ if(ether->transmit != nil)
+ ether->transmit(ether);
+ }else
+ freeb(bp);
+
+ return len;
+}
+
+static long
+etherwrite(Chan* chan, void* buf, long n, vlong)
+{
+ Ether *ether;
+ Block *bp;
+ int onoff;
+ Cmdbuf *cb;
+ long l;
+
+ ether = etherxx[chan->dev];
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ if(NETTYPE(chan->qid.path) != Ndataqid) {
+ l = netifwrite(ether, chan, buf, n);
+ if(l >= 0)
+ goto out;
+ cb = parsecmd(buf, n);
+ if(strcmp(cb->f[0], "nonblocking") == 0){
+ if(cb->nf <= 1)
+ onoff = 1;
+ else
+ onoff = atoi(cb->f[1]);
+ qnoblock(ether->oq, onoff);
+ free(cb);
+ goto out;
+ }
+ free(cb);
+ if(ether->ctl!=nil){
+ l = ether->ctl(ether,buf,n);
+ goto out;
+ }
+ 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);
+ bp->wp += n;
+ poperror();
+
+ l = etheroq(ether, bp);
+out:
+ poperror();
+ runlock(ether);
+ return l;
+}
+
+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];
+ rlock(ether);
+ if(waserror()) {
+ runlock(ether);
+ nexterror();
+ }
+ if(n > ether->maxmtu){
+ freeb(bp);
+ error(Etoobig);
+ }
+ if(n < ether->minmtu){
+ freeb(bp);
+ error(Etoosmall);
+ }
+ n = etheroq(ether, bp);
+ poperror();
+ runlock(ether);
+ return n;
+}
+
+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;
+ ether->itype = -1;
+
+ if(archether(ctlrno, ether) <= 0)
+ continue;
+
+ for(n = 0; cards[n].type; n++){
+ if(cistrcmp(cards[n].type, ether->type))
+ continue;
+ for(i = 0; i < ether->nopt; i++){
+ if(cistrncmp(ether->opt[i], "ea=", 3) == 0){
+ if(parseether(ether->ea, &ether->opt[i][3]) == -1)
+ memset(ether->ea, 0, Eaddrlen);
+ }else if(cistrcmp(ether->opt[i], "fullduplex") == 0 ||
+ cistrcmp(ether->opt[i], "10BASE-TFD") == 0)
+ ether->fullduplex = 1;
+ else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0)
+ ether->mbps = 100;
+ }
+ if(cards[n].reset(ether))
+ break;
+ snprint(name, sizeof(name), "ether%d", ctlrno);
+
+ if(ether->interrupt != nil)
+ intrenable(ether->irq, ether->interrupt, ether, ether->itype, name);
+
+ i = sprint(buf, "#l%d: %s: %dMbps port 0x%luX irq %lud",
+ ctlrno, ether->type, ether->mbps, ether->port, ether->irq);
+ if(ether->mem)
+ i += sprint(buf+i, " addr 0x%luX", PADDR(ether->mem));
+ if(ether->size)
+ i += sprint(buf+i, " size 0x%luX", ether->size);
+ i += sprint(buf+i, ": %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX",
+ ether->ea[0], ether->ea[1], ether->ea[2],
+ ether->ea[3], ether->ea[4], ether->ea[5]);
+ sprint(buf+i, "\n");
+ iprint(buf);
+
+ if(ether->mbps == 100){
+ netifinit(ether, name, Ntypes, 256*1024);
+ if(ether->oq == 0)
+ ether->oq = qopen(256*1024, Qmsg, 0, 0);
+ }
+ else{
+ netifinit(ether, name, Ntypes, 64*1024);
+ if(ether->oq == 0)
+ ether->oq = qopen(64*1024, Qmsg, 0, 0);
+ }
+ if(ether->oq == 0)
+ panic("etherreset %s", name);
+ ether->alen = Eaddrlen;
+ memmove(ether->addr, ether->ea, Eaddrlen);
+ memset(ether->bcast, 0xFF, Eaddrlen);
+
+ etherxx[ctlrno] = ether;
+ ether = 0;
+ break;
+ }
+ }
+ if(ether)
+ free(ether);
+}
+
+static void
+etherpower(int on)
+{
+ int i;
+ Ether *ether;
+
+ for(i = 0; i < MaxEther; i++){
+ if((ether = etherxx[i]) == nil || ether->power == nil)
+ continue;
+ if(on){
+ if(canrlock(ether))
+ continue;
+ if(ether->power != nil)
+ ether->power(ether, on);
+ wunlock(ether);
+ }else{
+ if(!canrlock(ether))
+ continue;
+ wlock(ether);
+ if(ether->power != nil)
+ ether->power(ether, on);
+ /* Keep locked until power goes back on */
+ }
+ }
+}
+
+#define POLY 0xedb88320
+
+/* really slow 32 bit crc for ethers */
+ulong
+ethercrc(uchar *p, int len)
+{
+ int i, j;
+ ulong crc, b;
+
+ crc = 0xffffffff;
+ for(i = 0; i < len; i++){
+ b = *p++;
+ for(j = 0; j < 8; j++){
+ crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0);
+ b >>= 1;
+ }
+ }
+ return crc;
+}
+
+Dev etherdevtab = {
+ 'l',
+ "ether",
+
+ etherreset,
+ devinit,
+ ethershutdown,
+ etherattach,
+ etherwalk,
+ etherstat,
+ etheropen,
+ ethercreate,
+ etherclose,
+ etherread,
+ etherbread,
+ etherwrite,
+ etherbwrite,
+ devremove,
+ etherwstat,
+ etherpower,
+};
diff --git a/os/sa1110/devgpio.c b/os/sa1110/devgpio.c
new file mode 100644
index 00000000..f12d5951
--- /dev/null
+++ b/os/sa1110/devgpio.c
@@ -0,0 +1,165 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+enum{
+ Qdir,
+ Qgpioset,
+ Qgpioclear,
+ Qgpioedge,
+ Qgpioctl,
+ Qgpiostatus,
+};
+
+Dirtab gpiodir[]={
+ ".", {Qdir,0}, 0, 0555,
+ "gpioset", {Qgpioset, 0}, 0, 0664,
+ "gpioclear", {Qgpioclear, 0}, 0, 0664,
+ "gpioedge", {Qgpioedge, 0}, 0, 0664,
+ "gpioctl", {Qgpioctl,0}, 0, 0664,
+ "gpiostatus", {Qgpiostatus,0}, 0, 0444,
+};
+
+static Chan*
+gpioattach(char* spec)
+{
+ return devattach('G', spec);
+}
+
+static Walkqid*
+gpiowalk(Chan* c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, gpiodir, nelem(gpiodir), devgen);
+}
+
+static int
+gpiostat(Chan* c, uchar *dp, int n)
+{
+ return devstat(c, dp, n, gpiodir, nelem(gpiodir), devgen);
+}
+
+static Chan*
+gpioopen(Chan* c, int omode)
+{
+ return devopen(c, omode, gpiodir, nelem(gpiodir), devgen);
+}
+
+static void
+gpioclose(Chan*)
+{
+}
+
+static long
+gpioread(Chan* c, void *buf, long n, vlong offset)
+{
+ char str[128];
+ GpioReg *g;
+
+ if(c->qid.type & QTDIR)
+ return devdirread(c, buf, n, gpiodir, nelem(gpiodir), devgen);
+
+ g = GPIOREG;
+ switch((ulong)c->qid.path){
+ case Qgpioset:
+ case Qgpioclear:
+ sprint(str, "%8.8lux", g->gplr);
+ break;
+ case Qgpioedge:
+ sprint(str, "%8.8lux", g->gedr);
+ break;
+ case Qgpioctl:
+ /* return 0; */
+ case Qgpiostatus:
+ snprint(str, sizeof(str), "GPDR:%8.8lux\nGRER:%8.8lux\nGFER:%8.8lux\nGAFR:%8.8lux\nGPLR:%8.8lux\n", g->gpdr, g->grer, g->gfer, g->gafr, g->gplr);
+ break;
+ default:
+ error(Ebadarg);
+ return 0;
+ }
+ return readstr(offset, buf, n, str);
+}
+
+static long
+gpiowrite(Chan *c, void *a, long n, vlong)
+{
+ char buf[128], *field[3];
+ int pin, set;
+ ulong *r;
+ GpioReg *g;
+
+ if(n >= sizeof(buf))
+ n = sizeof(buf)-1;
+ memmove(buf, a, n);
+ buf[n] = 0;
+ g = GPIOREG;
+ switch((ulong)c->qid.path){
+ case Qgpioset:
+ g->gpsr = strtol(buf, 0, 16);
+ break;
+ case Qgpioclear:
+ g->gpcr = strtol(buf, 0, 16);
+ break;
+ case Qgpioedge:
+ g->gedr = strtol(buf, 0, 16);
+ break;
+ case Qgpioctl:
+ if(getfields(buf, field, 3, 1, " \n\t") == 3) {
+ pin = strtol(field[1], 0, 0);
+ if(pin < 0 || pin >= 32)
+ error(Ebadarg);
+ set = strtol(field[2], 0, 0);
+ switch(*field[0]) {
+ case 'd':
+ r = &g->gpdr;
+ break;
+ case 'r':
+ r = &g->grer;
+ break;
+ case 'f':
+ r = &g->gfer;
+ break;
+ case 'a':
+ r = &g->gafr;
+ break;
+ default:
+ error(Ebadarg);
+ return 0;
+ }
+ if(set)
+ *r |= 1 << pin;
+ else
+ *r &= ~(1 << pin);
+ } else
+ error(Ebadarg);
+ break;
+ default:
+ error(Ebadusefd);
+ return 0;
+ }
+ return n;
+}
+
+Dev gpiodevtab = {
+ 'G',
+ "gpio",
+
+ devreset,
+ devinit,
+ devshutdown,
+ gpioattach,
+ gpiowalk,
+ gpiostat,
+ gpioopen,
+ devcreate,
+ gpioclose,
+ gpioread,
+ devbread,
+ gpiowrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
diff --git a/os/sa1110/devpcmcia.c b/os/sa1110/devpcmcia.c
new file mode 100644
index 00000000..288295b4
--- /dev/null
+++ b/os/sa1110/devpcmcia.c
@@ -0,0 +1,761 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+int pcmdebug=0;
+#define DPRINT if(pcmdebug)iprint
+#define DPRINT1 if(pcmdebug > 1)iprint
+#define DPRINT2 if(pcmdebug > 2)iprint
+#define PCMERR(x) pce(x);
+
+enum
+{
+ Qdir,
+ Qmem,
+ Qattr,
+ Qctl,
+};
+
+#define SLOTNO(c) (((ulong)c->qid.path>>8)&0xff)
+#define TYPE(c) ((ulong)c->qid.path&0xff)
+#define QID(s,t) (((s)<<8)|(t))
+
+/*
+ * Support for 2 card slots usng StrongArm pcmcia support.
+ *
+ */
+enum
+{
+ /*
+ * configuration registers - they start at an offset in attribute
+ * memory found in the CIS.
+ */
+ Rconfig= 0,
+ Creset= (1<<7), /* reset device */
+ Clevel= (1<<6), /* level sensitive interrupt line */
+
+};
+
+
+enum {
+ Maxctab= 8, /* maximum configuration table entries */
+ Maxslot= 2
+};
+
+static struct {
+ Ref;
+} pcmcia;
+
+static PCMslot slot[Maxslot];
+static PCMslot *lastslot ;
+static int nslot = Maxslot;
+
+static void slotdis(PCMslot *);
+static void pcmciaintr(Ureg*, void*);
+static void pcmciareset(void);
+static int pcmio(int, ISAConf*);
+static long pcmread(int, int, void*, long, ulong);
+static long pcmwrite(int, int, void*, long, ulong);
+static void slottiming(int, int, int, int, int);
+static void slotmap(int, ulong, ulong, ulong);
+
+static void pcmciadump(PCMslot*);
+
+static ulong GPIOrdy[2];
+static ulong GPIOeject[2];
+static ulong GPIOall[2];
+
+/*
+ * get info about card
+ */
+static void
+slotinfo(PCMslot *pp)
+{
+ ulong gplr;
+ int was;
+
+ gplr = GPIOREG->gplr;
+ was = pp->occupied;
+ pp->occupied = (gplr & GPIOeject[pp->slotno]) ? 0 : 1;
+ pp->busy = (gplr & GPIOrdy[pp->slotno]) ? 0 : 1;
+ pp->powered = pcmpowered(pp->slotno);
+ pp->battery = 0;
+ pp->wrprot = 0;
+ if (!was & pp->occupied)
+ print("PCMCIA card %d inserted\n", pp->slotno);
+ if (was & !pp->occupied)
+ print("PCMCIA card %d removed!\n", pp->slotno);
+}
+
+/*
+ * enable the slot card
+ */
+static void
+slotena(PCMslot *pp)
+{
+ if(pp->enabled)
+ return;
+ DPRINT("Enable slot# %d\n", pp->slotno);
+ DPRINT("pcmcia ready %8.8lux\n", GPIOREG->gplr & GPIOrdy[pp->slotno]);
+
+ /* get configuration */
+ slotinfo(pp);
+ if(pp->occupied){
+ if(pp->cisread == 0){
+ pcmcisread(pp);
+ pp->cisread = 1;
+ }
+ pp->enabled = 1;
+ } else
+ slotdis(pp);
+}
+
+/*
+ * disable the slot card
+ */
+static void
+slotdis(PCMslot *pp)
+{
+ if (pp->enabled)
+ DPRINT("Disable slot# %d\n", pp->slotno);
+ pp->enabled = 0;
+ pp->cisread = 0;
+}
+
+/*
+ * status change interrupt
+ */
+static void
+pcmciaintr(Ureg*, void*)
+{
+ uchar was;
+ PCMslot *pp;
+
+ if(slot == 0)
+ return;
+ for(pp = slot; pp < lastslot; pp++){
+ was = pp->occupied;
+ slotinfo(pp);
+ if(0 && !pp->occupied){
+ if(was != pp->occupied){
+ slotdis(pp);
+// if (pp->special && pp->notify.f)
+// (*pp->notify.f)(ur, pp->notify.a, 1);
+ }
+ }
+ }
+}
+
+static void
+increfp(PCMslot *pp)
+{
+ if(up){
+ wlock(pp);
+ if(waserror()){
+ wunlock(pp);
+ nexterror();
+ }
+ }
+ if(incref(&pcmcia) == 1){
+ pcmpower(pp->slotno, 1);
+ pcmreset(pp->slotno);
+ delay(500);
+ }
+
+ if(incref(&pp->ref) == 1)
+ slotena(pp);
+ if(up){
+ poperror();
+ wunlock(pp);
+ }
+}
+
+static void
+decrefp(PCMslot *pp)
+{
+ if(decref(&pp->ref) == 0)
+ slotdis(pp);
+ if(decref(&pcmcia) == 0)
+ pcmpower(pp->slotno, 0);
+}
+
+/*
+ * look for a card whose version contains 'idstr'
+ */
+int
+pcmspecial(char *idstr, ISAConf *isa)
+{
+ PCMslot *pp;
+
+ pcmciareset();
+ for(pp = slot; pp < lastslot; pp++){
+ if(pp->special)
+ continue; /* already taken */
+ increfp(pp);
+
+ if(pp->occupied)
+ if(strstr(pp->verstr, idstr)){
+ DPRINT("PCMslot #%d: Found %s - ",pp->slotno, idstr);
+ if(isa == 0 || pcmio(pp->slotno, isa) == 0){
+ DPRINT("ok.\n");
+ pp->special = 1;
+ return pp->slotno;
+ }
+ print("error with isa io for %s\n", idstr);
+ }
+ decrefp(pp);
+ }
+ return -1;
+}
+
+void
+pcmspecialclose(int slotno)
+{
+ PCMslot *pp;
+ int s;
+
+ if(slotno < 0 || slotno >= nslot)
+ panic("pcmspecialclose");
+ pp = slot + slotno;
+ pp->special = 0; /* Is this OK ? */
+ s = splhi();
+ GPIOREG->gfer &= ~GPIOrdy[pp->slotno]; /* TO DO: intrdisable */
+ GPIOREG->grer &= ~GPIOrdy[pp->slotno];
+ splx(s);
+ decrefp(pp);
+}
+
+static int
+pcmgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp)
+{
+ int slotno;
+ Qid qid;
+ long len;
+ PCMslot *pp;
+
+ if(i == DEVDOTDOT){
+ mkqid(&qid, Qdir, 0, QTDIR);
+ devdir(c, qid, "#y", 0, eve, 0555, dp);
+ return 1;
+ }
+
+ if(i>=3*nslot)
+ return -1;
+ slotno = i/3;
+ pp = slot + slotno;
+ len = 0;
+ switch(i%3){
+ case 0:
+ qid.path = QID(slotno, Qmem);
+ sprint(up->genbuf, "pcm%dmem", slotno);
+ len = pp->memlen;
+ break;
+ case 1:
+ qid.path = QID(slotno, Qattr);
+ sprint(up->genbuf, "pcm%dattr", slotno);
+ len = pp->memlen;
+ break;
+ case 2:
+ qid.path = QID(slotno, Qctl);
+ sprint(up->genbuf, "pcm%dctl", slotno);
+ break;
+ }
+ qid.vers = 0;
+ qid.type = QTFILE;
+ devdir(c, qid, up->genbuf, len, eve, 0660, dp);
+ return 1;
+}
+
+static void
+pcmciadump(PCMslot *pp)
+{
+ USED(pp);
+}
+
+/*
+ * set up for slot cards
+ */
+static void
+pcmciareset(void)
+{
+ static int already;
+ int slotno, v, rdypin;
+ PCMslot *pp;
+
+ if(already)
+ return;
+ already = 1;
+ DPRINT("pcmcia reset\n");
+
+ lastslot = slot;
+
+ nslot = 0;
+ for(slotno = 0; slotno < Maxslot; slotno++){
+ rdypin = pcmpin(slotno, PCMready);
+ if(rdypin < 0)
+ break;
+ nslot = slotno+1;
+ slotmap(slotno, PCMCIAIO(slotno), PCMCIAAttr(slotno), PCMCIAMem(slotno));
+ slottiming(slotno, 300, 300, 300, 0); /* set timing to the default, 300 */
+ pp = lastslot++;
+ GPIOeject[slotno] = (1<<pcmpin(slotno, PCMeject));
+ GPIOrdy[slotno] = (1<<rdypin);
+ GPIOall[slotno] = GPIOeject[slotno] | GPIOrdy[slotno];
+ GPIOREG->gafr &= ~GPIOall[slotno];
+ slotdis(pp);
+ intrenable(pcmpin(slotno, PCMeject), pcmciaintr, 0, BusGPIOrising, "pcmcia eject");
+ if((v = pcmpin(slotno, PCMstschng)) >= 0) /* status change interrupt */
+ intrenable(v, pcmciaintr, 0, BusGPIOrising, "pcmcia status");
+ }
+}
+
+static Chan*
+pcmciaattach(char *spec)
+{
+ return devattach('y', spec);
+}
+
+static Walkqid*
+pcmciawalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, 0, 0, pcmgen);
+}
+
+static int
+pcmciastat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, 0, 0, pcmgen);
+}
+
+static Chan*
+pcmciaopen(Chan *c, int omode)
+{
+ if(c->qid.type & QTDIR){
+ if(omode != OREAD)
+ error(Eperm);
+ } else
+ increfp(slot + SLOTNO(c));
+ c->mode = openmode(omode);
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+}
+
+static void
+pcmciaclose(Chan *c)
+{
+ if(c->flag & COPEN)
+ if((c->qid.type & QTDIR) == 0)
+ decrefp(slot+SLOTNO(c));
+}
+
+/* a memmove using only bytes */
+static void
+memmoveb(uchar *to, uchar *from, int n)
+{
+ while(n-- > 0)
+ *to++ = *from++;
+}
+
+static long
+pcmread(int slotno, int attr, void *a, long n, ulong offset)
+{
+ PCMslot *pp;
+ long i;
+ uchar *b, *p;
+
+ pp = slot + slotno;
+ rlock(pp);
+ if(waserror()){
+ runlock(pp);
+ nexterror();
+ }
+ if(!pp->occupied)
+ error(Eio);
+ if(pp->memlen < offset){
+ runlock(pp);
+ poperror();
+ return 0;
+ }
+ if(pp->memlen < offset + n)
+ n = pp->memlen - offset;
+ if (attr){
+ b = a;
+ p = (uchar*)PCMCIAAttr(slotno) + offset;
+ for(i=0; i<n; i++){
+ if(!pp->occupied)
+ error(Eio);
+ b[0] = *p;
+ i++;
+ if(i<n)
+ b[1] = 0;
+ b += 2;
+ p += 2;
+ }
+ }else
+ memmoveb(a, (uchar *)PCMCIAMem(slotno) + offset, n);
+ poperror();
+ runlock(pp);
+ return n;
+}
+
+static long
+pcmciaread(Chan *c, void *a, long n, vlong offset)
+{
+ char *cp, *buf;
+ ulong p;
+ PCMslot *pp;
+ int i;
+
+ p = TYPE(c);
+ switch(p){
+ case Qdir:
+ return devdirread(c, a, n, 0, 0, pcmgen);
+ case Qmem:
+ case Qattr:
+ return pcmread(SLOTNO(c), p==Qattr, a, n, offset);
+ case Qctl:
+ buf = malloc(2048);
+ if(buf == nil)
+ error(Eio);
+ if(waserror()){
+ free(buf);
+ nexterror();
+ }
+ cp = buf;
+ pp = slot + SLOTNO(c);
+ if(pp->occupied)
+ cp += sprint(cp, "occupied\n");
+ if(pp->enabled)
+ cp += sprint(cp, "enabled\n");
+ if(pp->powered)
+ cp += sprint(cp, "powered\n");
+ if(pp->configed)
+ cp += sprint(cp, "configed\n");
+ if(pp->busy)
+ cp += sprint(cp, "busy\n");
+ if(pp->enabled && (i = strlen(pp->verstr)) > 0)
+ cp += sprint(cp, "verstr %d\n%s\n", i, pp->verstr);
+ cp += sprint(cp, "battery lvl %d\n", pp->battery);
+ /* DUMP registers here */
+ cp += sprint(cp, "mecr 0x%lux\n",
+ (SLOTNO(c) ? MEMCFGREG->mecr >> 16 : MEMCFGREG->mecr) & 0x7fff);
+ *cp = 0;
+ n = readstr(offset, a, n, buf);
+ poperror();
+ free(buf);
+ break;
+ default:
+ n=0;
+ break;
+ }
+ return n;
+}
+
+static long
+pcmwrite(int slotno, int attr, void *a, long n, ulong offset)
+{
+ PCMslot *pp;
+
+ pp = slot + slotno;
+ rlock(pp);
+ if(waserror()){
+ runlock(pp);
+ nexterror();
+ }
+ if(pp->memlen < offset)
+ error(Eio);
+ if(pp->memlen < offset + n)
+ error(Eio);
+ memmoveb((uchar *)(attr ? PCMCIAAttr(slotno) : PCMCIAMem(slotno)) + offset, a, n);
+ poperror();
+ runlock(pp);
+ return n;
+}
+
+/*
+ * the regions are staticly mapped
+ */
+static void
+slotmap(int slotno, ulong regs, ulong attr, ulong mem)
+{
+ PCMslot *sp;
+
+ if(slotno >= Maxslot)
+ return;
+
+ sp = &slot[slotno];
+ sp->slotno = slotno;
+ sp->memlen = 64*MB;
+ sp->verstr[0] = 0;
+
+ sp->mem = (void*)mem;
+ sp->memmap.ca = 0;
+ sp->memmap.cea = 64*MB;
+ sp->memmap.isa = (ulong)mem;
+ sp->memmap.len = 64*MB;
+ sp->memmap.attr = 0;
+
+ sp->attr = (void*)attr;
+ sp->attrmap.ca = 0;
+ sp->attrmap.cea = MB;
+ sp->attrmap.isa = (ulong)attr;
+ sp->attrmap.len = MB;
+ sp->attrmap.attr = 1;
+
+ sp->regs = (void*)regs;
+}
+
+PCMmap*
+pcmmap(int slotno, ulong, int, int attr)
+{
+ if(slotno >= nslot)
+ panic("pcmmap");
+ if(attr)
+ return &slot[slotno].attrmap;
+ else
+ return &slot[slotno].memmap;
+}
+void
+pcmunmap(int, PCMmap*)
+{
+}
+
+/*
+ * setup card timings
+ * times are in ns
+ * count = ceiling[access-time/(2*3*T)] - 1, where T is a processor cycle
+ *
+ */
+static int
+ns2count(int ns)
+{
+ ulong y;
+
+ /* get 100 times cycle time */
+ y = 100000000/(m->cpuhz/1000);
+
+ /* get 10 times ns/(cycle*6) */
+ y = (1000*ns)/(6*y);
+
+ /* round up */
+ y += 9;
+ y /= 10;
+
+ /* subtract 1 */
+ y = y-1;
+ if(y < 0)
+ y = 0;
+ if(y > 0x1F)
+ y = 0x1F;
+
+ return y & 0x1F;
+}
+static void
+slottiming(int slotno, int tio, int tattr, int tmem, int fast)
+{
+ ulong x;
+ MemcfgReg *memconfregs = MEMCFGREG;
+
+ x = ns2count(tio) << 0;
+ x |= ns2count(tattr) << 5;
+ x |= ns2count(tmem) << 10;
+ if(fast)
+ x |= 1<<15;
+ if(slotno == 0){
+ x |= memconfregs->mecr & 0xffff0000;
+ } else {
+ x <<= 16;
+ x |= memconfregs->mecr & 0xffff;
+ }
+ memconfregs->mecr = x;
+}
+
+static long
+pcmciawrite(Chan *c, void *a, long n, vlong offset)
+{
+ ulong p;
+ PCMslot *pp;
+ char buf[32];
+
+ p = TYPE(c);
+ switch(p){
+ case Qctl:
+ if(n >= sizeof(buf))
+ n = sizeof(buf) - 1;
+ strncpy(buf, a, n);
+ buf[n] = 0;
+ pp = slot + SLOTNO(c);
+ if(!pp->occupied)
+ error(Eio);
+
+ if(strncmp(buf, "vpp", 3) == 0)
+ pcmsetvpp(pp->slotno, atoi(buf+3));
+ break;
+ case Qmem:
+ case Qattr:
+ pp = slot + SLOTNO(c);
+ if(pp->occupied == 0 || pp->enabled == 0)
+ error(Eio);
+ n = pcmwrite(SLOTNO(c), p == Qattr, a, n, offset);
+ if(n < 0)
+ error(Eio);
+ break;
+ default:
+ error(Ebadusefd);
+ }
+ return n;
+}
+
+Dev pcmciadevtab = {
+ 'y',
+ "pcmcia",
+
+ pcmciareset,
+ devinit,
+ devshutdown,
+ pcmciaattach,
+ pcmciawalk,
+ pcmciastat,
+ pcmciaopen,
+ devcreate,
+ pcmciaclose,
+ pcmciaread,
+ devbread,
+ pcmciawrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
+
+/*
+ * configure the PCMslot for IO. We assume very heavily that we can read
+ * configuration info from the CIS. If not, we won't set up correctly.
+ */
+
+static int
+pce(char *s)
+{
+ USED(s);
+ DPRINT("pcmio failed: %s\n", s);
+ return -1;
+}
+
+static int
+pcmio(int slotno, ISAConf *isa)
+{
+ uchar *p;
+ PCMslot *pp;
+ int i, index;
+ char *cp;
+
+ if(slotno >= nslot)
+ return PCMERR("bad slot#");
+ pp = slot + slotno;
+
+ if(!pp->occupied)
+ return PCMERR("empty slot");
+
+ index = 0;
+ if(pp->def)
+ index = pp->def->index;
+ for(i = 0; i < isa->nopt; i++){
+ if(strncmp(isa->opt[i], "index=", 6))
+ continue;
+ index = strtol(&isa->opt[i][6], &cp, 0);
+ if(cp == &isa->opt[i][6] || index < 0 || index >= pp->nctab)
+ return PCMERR("bad index");
+ break;
+ }
+ /* only touch Rconfig if it is present */
+ if(pp->cfg[0].cpresent & (1<<Rconfig)){
+ p = (uchar*)(PCMCIAAttr(slotno) + pp->cfg[0].caddr + Rconfig);
+ *p = index;
+ delay(5);
+ }
+ isa->port = (ulong)pp->regs;
+ isa->mem = (ulong)pp->mem;
+ isa->irq = pcmpin(pp->slotno, PCMready);
+ isa->itype = BusGPIOfalling;
+ return 0;
+}
+
+int
+inb(ulong p)
+{
+ return *(uchar*)p;
+}
+
+int
+ins(ulong p)
+{
+ return *(ushort*)p;
+}
+
+ulong
+inl(ulong p)
+{
+ return *(ulong*)p;
+}
+
+void
+outb(ulong p, int v)
+{
+ *(uchar*)p = v;
+}
+
+void
+outs(ulong p, int v)
+{
+ *(ushort*)p = v;
+}
+
+void
+outl(ulong p, ulong v)
+{
+ *(ulong*)p = v;
+}
+
+void
+inss(ulong p, void* buf, int ns)
+{
+ ushort *addr;
+
+ addr = (ushort*)buf;
+ for(;ns > 0; ns--)
+ *addr++ = *(ushort*)p;
+}
+
+void
+outss(ulong p, void* buf, int ns)
+{
+ ushort *addr;
+
+ addr = (ushort*)buf;
+ for(;ns > 0; ns--)
+ *(ushort*)p = *addr++;
+}
+
+void
+insb(ulong p, void* buf, int ns)
+{
+ uchar *addr;
+
+ addr = (uchar*)buf;
+ for(;ns > 0; ns--)
+ *addr++ = *(uchar*)p;
+}
+
+void
+outsb(ulong p, void* buf, int ns)
+{
+ uchar *addr;
+
+ addr = (uchar*)buf;
+ for(;ns > 0; ns--)
+ *(uchar*)p = *addr++;
+}
diff --git a/os/sa1110/devpower.c b/os/sa1110/devpower.c
new file mode 100644
index 00000000..c7cd5095
--- /dev/null
+++ b/os/sa1110/devpower.c
@@ -0,0 +1,377 @@
+/*
+ * power management
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+typedef struct Power Power;
+typedef struct Puser Puser;
+
+enum{
+ Qdir,
+ Qctl,
+ Qdata
+};
+
+static
+Dirtab powertab[]={
+ ".", {Qdir, 0, QTDIR}, 0, 0500,
+ "powerctl", {Qctl, 0}, 0, 0600,
+ "powerdata", {Qdata, 0}, 0, 0666,
+};
+
+struct Puser {
+ Ref;
+ ulong alarm; /* real time clock alarm time, if non-zero */
+ QLock rl; /* mutual exclusion to protect r */
+ Rendez r; /* wait for event of interest */
+ int state; /* shutdown state of this process */
+ Puser* next;
+};
+
+enum{
+ Pwridle,
+ Pwroff,
+ Pwrack
+};
+
+static struct {
+ QLock;
+ Puser *list;
+ Lock l; /* protect shutdown, nwaiting */
+ int shutdown; /* non-zero if currently shutting down */
+ int nwaiting; /* waiting for this many processes */
+ Rendez ackr; /* wait here for all acks */
+} pwrusers;
+
+
+static Chan*
+powerattach(char* spec)
+{
+ return devattach(L'↓', spec);
+}
+
+static int
+powerwalk(Chan* c, char* name)
+{
+ return devwalk(c, name, powertab, nelem(powertab), devgen);
+}
+
+static void
+powerstat(Chan* c, char* db)
+{
+ devstat(c, db, powertab, nelem(powertab), devgen);
+}
+
+static Chan*
+poweropen(Chan* c, int omode)
+{
+ Puser *p;
+
+ if(c->qid.type & QTDIR)
+ return devopen(c, omode, powertab, nelem(powertab), devgen);
+ switch(c->qid.path){
+ case Qdata:
+ p = mallocz(sizeof(Puser), 1);
+ if(p == nil)
+ error(Enovmem);
+ p->state = Pwridle;
+ p->ref = 1;
+ if(waserror()){
+ free(p);
+ nexterror();
+ }
+ c = devopen(c, omode, powertab, nelem(powertab), devgen);
+ c->aux = p;
+ qlock(&pwrusers);
+ p->next = pwrusers.list;
+ pwrusers.list = p; /* note: must place on front of list for correct shutdown ordering */
+ qunlock(&pwrusers);
+ poperror();
+ break;
+ case Qctl:
+ c = devopen(c, omode, powertab, nelem(powertab), devgen);
+ break;
+ }
+ return c;
+}
+
+static Chan *
+powerclone(Chan *c, Chan *nc)
+{
+ Puser *p;
+
+ nc = devclone(c, nc);
+ if((p = nc->aux) != nil)
+ incref(p);
+ return nc;
+}
+
+static void
+powerclose(Chan* c)
+{
+ Puser *p, **l;
+
+ if(c->qid.type & QTDIR || (c->flag & COPEN) == 0)
+ return;
+ p = c->aux;
+ if(p != nil && decref(p) == 0){
+ /* TO DO: cancel alarm */
+ qlock(&pwrusers);
+ for(l = &pwrusers.list; *l != nil; l = &(*l)->next)
+ if(*l == p){
+ *l = p->next;
+ break;
+ }
+ qunlock(&pwrusers);
+ free(p);
+ }
+}
+
+static int
+isshutdown(void *a)
+{
+ return ((Puser*)a)->state == Pwroff;
+}
+
+static long
+powerread(Chan* c, void* a, long n, vlong offset)
+{
+ Puser *p;
+ char *msg;
+
+ switch(c->qid.path & ~CHDIR){
+ case Qdir:
+ return devdirread(c, a, n, powertab, nelem(powertab), devgen);
+ case Qdata:
+ p = c->aux;
+ for(;;){
+ if(!canqlock(&p->rl))
+ error(Einuse); /* only one reader at a time */
+ if(waserror()){
+ qunlock(&p->rl);
+ nexterror();
+ }
+ sleep(&p->r, isshutdown, p);
+ poperror();
+ qunlock(&p->rl);
+ msg = nil;
+ lock(p);
+ if(p->state == Pwroff){
+ msg = "power off";
+ p->state = Pwrack;
+ }
+ unlock(p);
+ if(msg != nil)
+ return readstr(offset, a, n, msg);
+ }
+ break;
+ case Qctl:
+ default:
+ n=0;
+ break;
+ }
+ return n;
+}
+
+static int
+alldown(void*)
+{
+ return pwrusers.nwaiting == 0;
+}
+
+static long
+powerwrite(Chan* c, void *a, long n, vlong)
+{
+ Cmdbuf *cmd;
+ Puser *p;
+
+ if(c->qid.type & QTDIR)
+ error(Ebadusefd);
+ cmd = parsecmd(a, n);
+ if(waserror()){
+ free(cmd);
+ nexterror();
+ }
+ switch(c->qid.path & ~CHDIR){
+ case Qdata:
+ p = c->aux;
+ if(cmd->nf < 2)
+ error(Ebadarg);
+ if(strcmp(cmd->f[0], "ack") == 0){
+ if(strcmp(cmd->f[1], "power") == 0){
+ lock(p);
+ if(p->state == Pwrack){
+ lock(&pwrusers.l);
+ if(pwrusers.shutdown && pwrusers.nwaiting > 0)
+ pwrusers.nwaiting--;
+ unlock(&pwrusers.l);
+ wakeup(&pwrusers.ackr);
+ p->state = Pwridle;
+ }
+ unlock(p);
+ }else
+ error(Ebadarg);
+ }else if(strcmp(cmd->f[0], "alarm") == 0){
+ /* set alarm */
+ }else
+ error(Ebadarg);
+ break;
+ case Qctl:
+ if(cmd->nf < 1)
+ error(Ebadarg);
+ if(strcmp(cmd->f[0], "suspend") == 0){
+ /* start the suspend action */
+ qlock(&pwrusers);
+ //powersuspend(0); /* calls poweringdown, then archsuspend() */
+ qunlock(&pwrusers);
+ }else if(strcmp(cmd->f[0], "shutdown") == 0){
+ /* go to it */
+ qlock(&pwrusers);
+ if(waserror()){
+ lock(&pwrusers.l);
+ pwrusers.shutdown = 0; /* hard luck for those already notified */
+ unlock(&pwrusers.l);
+ qunlock(&pwrusers);
+ nexterror();
+ }
+ lock(&pwrusers.l);
+ pwrusers.shutdown = 1;
+ pwrusers.nwaiting = 0;
+ unlock(&pwrusers.l);
+ for(p = pwrusers.list; p != nil; p = p->next){
+ lock(p);
+ if(p->state == Pwridle){
+ p->state = Pwroff;
+ lock(&pwrusers.l);
+ pwrusers.nwaiting++;
+ unlock(&pwrusers.l);
+ }
+ unlock(p);
+ wakeup(&p->r);
+ /* putting the tsleep here does each in turn; move out of loop to multicast */
+ tsleep(&pwrusers.ackr, alldown, nil, 1000);
+ }
+ poperror();
+ qunlock(&pwrusers);
+ //powersuspend(1);
+ }else
+ error(Ebadarg);
+ free(cmd);
+ break;
+ default:
+ error(Ebadusefd);
+ }
+ poperror();
+ return n;
+}
+
+/*
+ * device-level power management: suspend/resume/shutdown
+ */
+
+struct Power {
+ void (*f)(int);
+ Power* prev;
+ Power* next;
+};
+
+static struct {
+ Lock;
+ Power list;
+} power;
+
+void
+powerenablereset(void)
+{
+ power.list.next = power.list.prev = &power.list;
+ power.list.f = (void*)-1; /* something not nil */
+}
+
+void
+powerenable(void (*f)(int))
+{
+ Power *p, *l;
+
+ p = malloc(sizeof(*p));
+ p->f = f;
+ p->prev = nil;
+ p->next = nil;
+ ilock(&power);
+ for(l = power.list.next; l != &power.list; l = l->next)
+ if(l->f == f){
+ iunlock(&power);
+ free(p);
+ return;
+ }
+ l = &power.list;
+ p->prev = l->prev;
+ l->prev = p;
+ p->next = l;
+ p->prev->next = p;
+ iunlock(&power);
+}
+
+void
+powerdisable(void (*f)(int))
+{
+ Power *l;
+
+ ilock(&power);
+ for(l = power.list.next; l != &power.list; l = l->next)
+ if(l->f == f){
+ l->prev->next = l->next;
+ l->next->prev = l->prev;
+ free(l);
+ break;
+ }
+ iunlock(&power);
+}
+
+/*
+ * interrupts are assumed off so there's no need to lock
+ */
+void
+poweringup(void)
+{
+ Power *l;
+
+ for(l = power.list.next; l != &power.list; l = l->next)
+ (*l->f)(1);
+}
+
+void
+poweringdown(void)
+{
+ Power *l;
+
+ for(l = power.list.prev; l != &power.list; l = l->prev)
+ (*l->f)(0);
+}
+
+Dev powerdevtab = {
+ L'↓',
+ "power",
+
+ devreset,
+ devinit,
+ powerattach,
+ devdetach,
+ powerclone,
+ powerwalk,
+ powerstat,
+ poweropen,
+ devcreate,
+ powerclose,
+ powerread,
+ devbread,
+ powerwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};
diff --git a/os/sa1110/devrtc.c b/os/sa1110/devrtc.c
new file mode 100644
index 00000000..54680fc2
--- /dev/null
+++ b/os/sa1110/devrtc.c
@@ -0,0 +1,169 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include "io.h"
+
+/*
+ * SA11x0 real time clock
+ * TO DO: alarms, wakeup, allow trim setting(?)
+ */
+
+enum{
+ Qdir,
+ Qrtc,
+ Qrtctrim,
+};
+
+static Dirtab rtcdir[]={
+ ".", {Qdir,0,QTDIR}, 0, 0555,
+ "rtc", {Qrtc}, NUMSIZE, 0664,
+ "rtctrim", {Qrtctrim}, 0, 0664,
+};
+#define NRTC (sizeof(rtcdir)/sizeof(rtcdir[0]))
+
+extern ulong boottime;
+
+enum {
+ RTSR_al= 1<<0, /* RTC alarm detected */
+ RTSR_hz= 1<<1, /* 1-Hz rising-edge detected */
+ RTSR_ale= 1<<2, /* RTC alarm interrupt enabled */
+ RTSR_hze= 1<<3, /* 1-Hz interrupt enable */
+};
+
+static void
+rtcreset(void)
+{
+ RtcReg *r;
+
+ r = RTCREG;
+ if((r->rttr & 0xFFFF) == 0){ /* reset state */
+ r->rttr = 32768-1;
+ r->rcnr = boottime; /* typically zero */
+ }
+ r->rtar = ~0;
+ r->rtsr = RTSR_al | RTSR_hz;
+}
+
+static Chan*
+rtcattach(char *spec)
+{
+ return devattach('r', spec);
+}
+
+static Walkqid*
+rtcwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, rtcdir, NRTC, devgen);
+}
+
+static int
+rtcstat(Chan *c, uchar *dp, int n)
+{
+ return devstat(c, dp, n, rtcdir, NRTC, devgen);
+}
+
+static Chan*
+rtcopen(Chan *c, int omode)
+{
+ return devopen(c, omode, rtcdir, NRTC, devgen);
+}
+
+static void
+rtcclose(Chan*)
+{
+}
+
+static long
+rtcread(Chan *c, void *buf, long n, vlong off)
+{
+ if(c->qid.type & QTDIR)
+ return devdirread(c, buf, n, rtcdir, NRTC, devgen);
+
+ switch((ulong)c->qid.path){
+ case Qrtc:
+ return readnum(off, buf, n, RTCREG->rcnr, NUMSIZE);
+ case Qrtctrim:
+ return readnum(off, buf, n, RTCREG->rttr, NUMSIZE);
+ }
+ error(Egreg);
+ return 0; /* not reached */
+}
+
+static long
+rtcwrite(Chan *c, void *buf, long n, vlong off)
+{
+ ulong offset = off;
+ ulong secs;
+ char *cp, sbuf[32];
+
+ switch((ulong)c->qid.path){
+ case Qrtc:
+ /*
+ * write the time
+ */
+ if(offset != 0 || n >= sizeof(sbuf)-1)
+ error(Ebadarg);
+ memmove(sbuf, buf, n);
+ sbuf[n] = '\0';
+ cp = sbuf;
+ while(*cp){
+ if(*cp>='0' && *cp<='9')
+ break;
+ cp++;
+ }
+ secs = strtoul(cp, 0, 0);
+ RTCREG->rcnr = secs;
+ return n;
+
+ case Qrtctrim:
+ if(offset != 0 || n >= sizeof(sbuf)-1)
+ error(Ebadarg);
+ memmove(sbuf, buf, n);
+ sbuf[n] = '\0';
+ RTCREG->rttr = strtoul(sbuf, 0, 0);
+ return n;
+ }
+ error(Egreg);
+ return 0; /* not reached */
+}
+
+static void
+rtcpower(int on)
+{
+ if(on)
+ boottime = RTCREG->rcnr - TK2SEC(MACHP(0)->ticks);
+ else
+ RTCREG->rcnr = seconds();
+}
+
+long
+rtctime(void)
+{
+ return RTCREG->rcnr;
+}
+
+Dev rtcdevtab = {
+ 'r',
+ "rtc",
+
+ rtcreset,
+ devinit,
+ devshutdown,
+ rtcattach,
+ rtcwalk,
+ rtcstat,
+ rtcopen,
+ devcreate,
+ rtcclose,
+ rtcread,
+ devbread,
+ rtcwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+ rtcpower,
+};
diff --git a/os/sa1110/devuart.c b/os/sa1110/devuart.c
new file mode 100644
index 00000000..65eb17a3
--- /dev/null
+++ b/os/sa1110/devuart.c
@@ -0,0 +1,781 @@
+#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"
+
+/*
+ * currently no DMA or flow control (hardware or software)
+ */
+
+enum
+{
+ Stagesize= 1024,
+ Dmabufsize=Stagesize/2,
+ Nuart=7, /* max per machine */
+
+ CTLS= 023,
+ CTLQ= 021,
+};
+
+typedef struct Uart Uart;
+struct Uart
+{
+ QLock;
+
+ int opens;
+
+ int enabled;
+
+ int frame; /* framing errors */
+ int overrun; /* rcvr overruns */
+ int soverrun; /* software overruns */
+ int perror; /* parity error */
+ int bps; /* baud rate */
+ uchar bits;
+ char parity;
+
+ int inters; /* total interrupt count */
+ int rinters; /* interrupts due to read */
+ int winters; /* interrupts due to write */
+
+ int rcount; /* total read count */
+ int wcount; /* total output count */
+
+ int xonoff; /* software flow control on */
+ int blocked; /* output blocked */
+
+ /* buffers */
+ int (*putc)(Queue*, int);
+ Queue *iq;
+ Queue *oq;
+
+ int port;
+ UartReg *reg;
+
+ /* staging areas to avoid some of the per character costs */
+ uchar *ip;
+ uchar *ie;
+ uchar *op;
+ uchar *oe;
+
+ /* put large buffers last to aid register-offset optimizations: */
+ char name[KNAMELEN];
+ uchar istage[Stagesize];
+ uchar ostage[Stagesize];
+};
+
+enum {
+ UTCR0_PE= 0x01,
+ UTCR0_OES= 0x02,
+ UTCR0_SBS= 0x04,
+ UTCR0_DSS= 0x08,
+ UTCR0_SCE= 0x10,
+ UTCR0_RCE= 0x20,
+ UTCR0_TCE= 0x40,
+
+ UTCR3_RXE= 0x01,
+ UTCR3_TXE= 0x02,
+ UTCR3_BRK= 0x04,
+ UTCR3_RIM= 0x08,
+ UTCR3_TIM= 0x10,
+ UTCR3_LBM= 0x20,
+
+ UTSR0_TFS= 0x01,
+ UTSR0_RFS= 0x02,
+ UTSR0_RID= 0x04,
+ UTSR0_RBB= 0x08,
+ UTSR0_REB= 0x10,
+ UTSR0_EIF= 0x20,
+
+ UTSR1_TBY= 0x01,
+ UTSR1_RNE= 0x02,
+ UTSR1_TNF= 0x04,
+ UTSR1_PRE= 0x08,
+ UTSR1_FRE= 0x10,
+ UTSR1_ROR= 0x20,
+};
+
+static Uart *uart[Nuart];
+static int nuart;
+static int uartspcl;
+int redirectconsole;
+
+static void
+uartset(Uart *p)
+{
+ UartReg *reg = p->reg;
+ ulong ocr3;
+ ulong brdiv;
+ int n;
+
+ brdiv = CLOCKFREQ/16/p->bps - 1;
+ ocr3 = reg->utcr3;
+ reg->utcr3 = ocr3&~(UTCR3_RXE|UTCR3_TXE);
+ reg->utcr1 = brdiv >> 8;
+ reg->utcr2 = brdiv & 0xff;
+ /* set PE and OES appropriately for o/e/n: */
+ reg->utcr0 = ((p->parity&3)^UTCR0_OES)|(p->bits&UTCR0_DSS);
+ reg->utcr3 = ocr3;
+
+ /* set buffer length according to speed, to allow
+ * at most a 200ms delay before dumping the staging buffer
+ * into the input queue
+ */
+ n = p->bps/(10*1000/200);
+ p->ie = &p->istage[n < Stagesize ? n : Stagesize];
+}
+
+/*
+ * send break
+ */
+static void
+uartbreak(Uart *p, int ms)
+{
+ UartReg *reg = p->reg;
+ if(ms == 0)
+ ms = 200;
+ reg->utcr3 |= UTCR3_BRK;
+ tsleep(&up->sleep, return0, 0, ms);
+ reg->utcr3 &= ~UTCR3_BRK;
+}
+
+/*
+ * turn on a port
+ */
+static void
+uartenable(Uart *p)
+{
+ UartReg *reg = p->reg;
+
+ if(p->enabled)
+ return;
+
+ archuartpower(p->port, 1);
+ uartset(p);
+ reg->utsr0 = 0xff; // clear all sticky status bits
+ // enable receive, transmit, and receive interrupt:
+ reg->utcr3 = UTCR3_RXE|UTCR3_TXE|UTCR3_RIM;
+ p->blocked = 0;
+ p->xonoff = 0;
+ p->enabled = 1;
+}
+
+/*
+ * turn off a port
+ */
+static void
+uartdisable(Uart *p)
+{
+ p->reg->utcr3 = 0; // disable TX, RX, and ints
+ p->blocked = 0;
+ p->xonoff = 0;
+ p->enabled = 0;
+ archuartpower(p->port, 0);
+}
+
+/*
+ * put some bytes into the local queue to avoid calling
+ * qconsume for every character
+ */
+static int
+stageoutput(Uart *p)
+{
+ int n;
+ Queue *q = p->oq;
+
+ if(q == nil)
+ return 0;
+ n = qconsume(q, p->ostage, Stagesize);
+ if(n <= 0)
+ return 0;
+ p->op = p->ostage;
+ p->oe = p->ostage + n;
+ return n;
+}
+
+static void
+uartxmit(Uart *p)
+{
+ UartReg *reg = p->reg;
+ ulong e = 0;
+
+ if(!p->blocked) {
+ while(p->op < p->oe || stageoutput(p)) {
+ if(reg->utsr1 & UTSR1_TNF) {
+ reg->utdr = *(p->op++);
+ p->wcount++;
+ } else {
+ e = UTCR3_TIM;
+ break;
+ }
+ }
+ }
+ reg->utcr3 = (reg->utcr3&~UTCR3_TIM)|e;
+}
+
+static void
+uartrecvq(Uart *p)
+{
+ uchar *cp = p->istage;
+ int n = p->ip - cp;
+
+ if(n == 0)
+ return;
+ if(p->putc)
+ while(n-- > 0)
+ p->putc(p->iq, *cp++);
+ else if(p->iq)
+ if(qproduce(p->iq, p->istage, n) < n){
+ /* if xonoff, should send XOFF when qwindow(p->iq) < threshold */
+ p->soverrun++;
+ //print("qproduce flow control");
+ }
+ p->ip = p->istage;
+}
+
+static void
+uartrecv(Uart *p)
+{
+ UartReg *reg = p->reg;
+ ulong n;
+ while(reg->utsr1 & UTSR1_RNE) {
+ int c;
+ n = reg->utsr1;
+ c = reg->utdr;
+ if(n & (UTSR1_PRE|UTSR1_FRE|UTSR1_ROR)) {
+ if(n & UTSR1_PRE)
+ p->perror++;
+ if(n & UTSR1_FRE)
+ p->frame++;
+ if(n & UTSR1_ROR)
+ p->overrun++;
+ continue;
+ }
+ if(p->xonoff){
+ if(c == CTLS){
+ p->blocked = 1;
+ }else if (c == CTLQ){
+ p->blocked = 0;
+ }
+ }
+ *p->ip++ = c;
+ if(p->ip >= p->ie)
+ uartrecvq(p);
+ p->rcount++;
+ }
+ if(reg->utsr0 & UTSR0_RID) {
+ reg->utsr0 = UTSR0_RID;
+ uartrecvq(p);
+ }
+}
+
+static void
+uartclock(void)
+{
+ Uart *p;
+ int i;
+
+ for(i=0; i<nuart; i++){
+ p = uart[i];
+ if(p != nil)
+ uartrecvq(p);
+ }
+}
+
+static void
+uartkick(void *a)
+{
+ Uart *p = a;
+ int x;
+
+ x = splhi();
+ uartxmit(p);
+ splx(x);
+}
+
+/*
+ * UART Interrupt Handler
+ */
+static void
+uartintr(Ureg*, void* arg)
+{
+ Uart *p = arg;
+ UartReg *reg = p->reg;
+ ulong m = reg->utsr0;
+ int dokick;
+
+ dokick = p->blocked;
+ p->inters++;
+ if(m & (UTSR0_RFS|UTSR0_RID|UTSR0_EIF)) {
+ p->rinters++;
+ uartrecv(p);
+ }
+ if(p->blocked)
+ dokick = 0;
+ if((m & UTSR0_TFS) && (reg->utcr3&UTCR3_TIM || dokick)) {
+ p->winters++;
+ uartxmit(p);
+ }
+
+ if(m & (UTSR0_RBB|UTSR0_REB)) {
+ //print("<BREAK>");
+ /* reg->utsr0 = UTSR0_RBB|UTSR0_REB; */
+ reg->utsr0 = m & (UTSR0_RBB|UTSR0_REB);
+ /* what to do? if anything */
+ }
+}
+
+static void
+uartsetup(ulong port, char *name)
+{
+ Uart *p;
+
+ if(nuart >= Nuart)
+ return;
+
+ p = xalloc(sizeof(Uart));
+ uart[nuart++] = p;
+ strcpy(p->name, name);
+
+ p->port = port;
+ p->reg = UARTREG(port);
+ p->bps = 9600;
+ p->bits = 8;
+ p->parity = 'n';
+
+ p->iq = qopen(4*1024, 0, 0 , p);
+ p->oq = qopen(4*1024, 0, uartkick, p);
+
+ p->ip = p->istage;
+ p->ie = &p->istage[Stagesize];
+ p->op = p->ostage;
+ p->oe = p->ostage;
+ if(port == 1)
+ GPCLKREG->gpclkr0 |= 1; /* SUS=1 for uart on serial 1 */
+
+ intrenable(UARTbit(port), uartintr, p, BusCPU, name);
+}
+
+static void
+uartinstall(void)
+{
+ static int already;
+
+ if(already)
+ return;
+ already = 1;
+
+ uartsetup(3, "eia0");
+ uartsetup(1, "eia1");
+ addclock0link(uartclock, 22);
+}
+
+/*
+ * called by main() to configure a duart port as a console or a mouse
+ */
+void
+uartspecial(int port, int bps, char parity, Queue **in, Queue **out, int (*putc)(Queue*, int))
+{
+ Uart *p;
+
+ uartinstall();
+ if(port >= nuart)
+ return;
+ p = uart[port];
+ if(bps)
+ p->bps = bps;
+ if(parity)
+ p->parity = parity;
+ uartenable(p);
+ p->putc = putc;
+ if(in)
+ *in = p->iq;
+ if(out)
+ *out = p->oq;
+ p->opens++;
+ uartspcl = 1;
+}
+
+Dirtab *uartdir;
+int ndir;
+
+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 < nuart; i++){
+ p = uart[i];
+ if(p && p->opens && p->iq)
+ uartdir[1+3*i].length = qlen(p->iq);
+ }
+}
+
+/*
+ * all uarts must be uartsetup() by this point or inside of uartinstall()
+ */
+static void
+uartreset(void)
+{
+ int i;
+ Dirtab *dp;
+
+ uartinstall();
+
+ ndir = 1+3*nuart;
+ uartdir = xalloc(ndir * sizeof(Dirtab));
+ dp = uartdir;
+ strcpy(dp->name, ".");
+ mkqid(&dp->qid, 0, 0, QTDIR);
+ dp->length = 0;
+ dp->perm = DMDIR|0555;
+ dp++;
+ for(i = 0; i < nuart; i++){
+ /* 3 directory entries per port */
+ strcpy(dp->name, uart[i]->name);
+ dp->qid.path = NETQID(i, Ndataqid);
+ dp->perm = 0660;
+ dp++;
+ sprint(dp->name, "%sctl", uart[i]->name);
+ dp->qid.path = NETQID(i, Nctlqid);
+ dp->perm = 0660;
+ dp++;
+ sprint(dp->name, "%sstatus", uart[i]->name);
+ dp->qid.path = NETQID(i, Nstatqid);
+ dp->perm = 0444;
+ dp++;
+ }
+}
+
+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, ndir, 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, ndir, devgen);
+}
+
+static Chan*
+uartopen(Chan *c, int omode)
+{
+ Uart *p;
+
+ c = devopen(c, omode, uartdir, ndir, devgen);
+
+ switch(NETTYPE(c->qid.path)){
+ case Nctlqid:
+ case Ndataqid:
+ p = uart[NETID(c->qid.path)];
+ qlock(p);
+ if(p->opens++ == 0){
+ uartenable(p);
+ qreopen(p->iq);
+ qreopen(p->oq);
+ }
+ qunlock(p);
+ break;
+ }
+
+ return c;
+}
+
+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){
+ uartdisable(p);
+ qclose(p->iq);
+ qclose(p->oq);
+ p->ip = p->istage;
+ }
+ qunlock(p);
+ break;
+ }
+}
+
+static long
+uartstatus(Chan *c, Uart *p, void *buf, long n, long offset)
+{
+ char str[256];
+ USED(c);
+
+ str[0] = 0;
+ snprint(str, sizeof(str),
+ "b%d l%d p%c s%d x%d\n"
+ "opens %d ferr %d oerr %d perr %d baud %d parity %c"
+ " intr %d rintr %d wintr %d"
+ " rcount %d wcount %d",
+ p->bps, p->bits, p->parity, (p->reg->utcr0&UTCR0_SBS)?2:1, p->xonoff,
+ p->opens, p->frame, p->overrun+p->soverrun, p->perror, p->bps, p->parity,
+ p->inters, p->rinters, p->winters,
+ p->rcount, p->wcount);
+
+ strcat(str, "\n");
+ return readstr(offset, buf, n, str);
+}
+
+static long
+uartread(Chan *c, void *buf, long n, vlong offset)
+{
+ Uart *p;
+
+ if(c->qid.type & QTDIR){
+ setlength(-1);
+ return devdirread(c, buf, n, uartdir, ndir, 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 uartstatus(c, p, buf, n, offset);
+ }
+
+ return 0;
+}
+
+static void
+uartctl(Uart *p, char *cmd)
+{
+ int i, n;
+
+ /* let output drain for a while (up to 4 secs) */
+ for(i = 0; i < 200 && (qlen(p->oq) || p->reg->utsr1 & UTSR1_TBY); i++)
+ tsleep(&up->sleep, return0, 0, 20);
+
+ if(strncmp(cmd, "break", 5) == 0){
+ uartbreak(p, 0);
+ return;
+ }
+
+ n = atoi(cmd+1);
+ switch(*cmd){
+ case 'B':
+ case 'b':
+ if(n <= 0)
+ error(Ebadarg);
+ p->bps = n;
+ uartset(p);
+ break;
+ case 'f':
+ case 'F':
+ qflush(p->oq);
+ break;
+ case 'H':
+ case 'h':
+ qhangup(p->iq, 0);
+ qhangup(p->oq, 0);
+ break;
+ case 'L':
+ case 'l':
+ if(n < 7 || n > 8)
+ error(Ebadarg);
+ p->bits = n;
+ uartset(p);
+ break;
+ case 'n':
+ case 'N':
+ qnoblock(p->oq, n);
+ break;
+ case 'P':
+ case 'p':
+ p->parity = *(cmd+1);
+ uartset(p);
+ break;
+ case 'K':
+ case 'k':
+ uartbreak(p, n);
+ break;
+ case 'Q':
+ case 'q':
+ qsetlimit(p->iq, n);
+ qsetlimit(p->oq, n);
+ break;
+ case 'X':
+ case 'x':
+ p->xonoff = n;
+ break;
+ }
+}
+
+static long
+uartwrite(Chan *c, void *buf, long n, vlong offset)
+{
+ Uart *p;
+ char cmd[32];
+
+ USED(offset);
+
+ if(c->qid.type & QTDIR)
+ error(Eperm);
+
+ p = uart[NETID(c->qid.path)];
+
+ switch(NETTYPE(c->qid.path)){
+ case Ndataqid:
+ return qwrite(p->oq, buf, n);
+ case Nctlqid:
+
+ if(n >= sizeof(cmd))
+ n = sizeof(cmd)-1;
+ memmove(cmd, buf, n);
+ cmd[n] = 0;
+ uartctl(p, cmd);
+ return n;
+ }
+}
+
+static int
+uartwstat(Chan *c, uchar *dp, int n)
+{
+ Dir d;
+ Dirtab *dt;
+
+ if(!iseve())
+ error(Eperm);
+ if(c->qid.type & QTDIR)
+ 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(d.mode != ~0UL){
+ d.mode &= 0666;
+ dt[0].perm = dt[1].perm = d.mode;
+ }
+ return n;
+}
+
+void
+uartpower(int on)
+{
+ Uart *p;
+ int i;
+
+ for(i=0; i<nuart; i++){
+ p = uart[i];
+ if(p != nil && p->opens){
+ if(on && !p->enabled){
+ p->enabled = 0;
+ uartenable(p);
+ uartkick(p);
+ }else{
+ if(p->port != 3) /* leave the console */
+ uartdisable(p);
+ p->enabled = 0;
+ }
+ }
+ }
+}
+
+Dev uartdevtab = {
+ 't',
+ "uart",
+
+ uartreset,
+ devinit,
+ devshutdown,
+ uartattach,
+ uartwalk,
+ uartstat,
+ uartopen,
+ devcreate,
+ uartclose,
+ uartread,
+ devbread,
+ uartwrite,
+ devbwrite,
+ devremove,
+ uartwstat,
+ uartpower,
+};
+
+/*
+ * for use by iprint
+ */
+void
+uartputc(int c)
+{
+ UartReg *r;
+
+ if(!uartspcl && !redirectconsole)
+ return;
+ if(c == 0)
+ return;
+ r = UARTREG(3);
+ while((r->utsr1 & UTSR1_TNF) == 0)
+ {}
+ r->utdr = c;
+ if(c == '\n')
+ while(r->utsr1 & UTSR1_TBY) /* flush xmit fifo */
+ {}
+}
+
+void
+uartputs(char *data, int len)
+{
+ int s;
+
+ if(!uartspcl && !redirectconsole)
+ return;
+ clockpoll();
+ s = splfhi();
+ while(--len >= 0){
+ if(*data == '\n')
+ uartputc('\r');
+ uartputc(*data++);
+ }
+ splx(s);
+}
+
+/*
+ * for use by debugger
+ */
+int
+uartgetc(void)
+{
+ UartReg *r;
+
+ if(!uartspcl)
+ return -1;
+ clockcheck();
+ r = UARTREG(3);
+ while(!(r->utsr1 & UTSR1_RNE))
+ clockcheck();
+ return r->utdr;
+}
diff --git a/os/sa1110/dma.c b/os/sa1110/dma.c
new file mode 100644
index 00000000..c52e28bb
--- /dev/null
+++ b/os/sa1110/dma.c
@@ -0,0 +1,233 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+enum {
+ /* DMA CSR bits */
+ CSRrun= 1 << 0,
+ CSRie= 1 << 1,
+ CSRerror= 1 << 2,
+ CSRdonea= 1 << 3,
+ CSRstrta= 1 << 4,
+ CSRdoneb= 1 << 5,
+ CSRstrtb= 1 << 6,
+ CSRbiu= 1 << 7,
+
+ Ndma= 6, /* number of dma channels */
+};
+
+/* DDAR configuration: DA 31:8, DS 3:0, data width, burst size */
+#define DMACFG(da, ds, dw, bs) (((da)<<8)|((ds)<<4)|((dw)<<3)|((bs)<<2))
+
+static ulong dmaconfig[16] = {
+[DmaUDC] DMACFG(0x80000A, 0, 0, 1),
+[DmaUART0] DMACFG(0x804005, 4, 0, 0),
+[DmaHSSP] DMACFG(0x81001B, 6, 0, 1),
+[DmaUART1] DMACFG(0x80C005, 6, 0, 0),
+[DmaUART2] DMACFG(0x814005, 8, 0, 0),
+[DmaMCPaudio] DMACFG(0x818002, 10, 1, 1),
+[DmaMCPtelecom] DMACFG(0x818003, 12, 1, 1),
+[DmaSSP] DMACFG(0x81C01B, 14, 1, 0), /* see SSP description not DMA section for correct burst size */
+};
+
+struct Dma {
+ int chan;
+ DmaReg* reg;
+ void (*interrupt)(void*, ulong);
+ void* arg;
+ Rendez r;
+ int intrset;
+};
+
+static struct {
+ Lock;
+ int avail;
+ Dma dma[Ndma];
+} dmachans;
+
+static void dmaintr(Ureg*, void*);
+
+void
+dmareset(void)
+{
+ int i;
+ Dma *d;
+
+ for(i=0; i<nelem(dmachans.dma); i++){
+ dmachans.avail |= 1<<i;
+ d = &dmachans.dma[i];
+ d->chan = i;
+ d->reg = DMAREG(i);
+ d->reg->dcsr_c = 0xFF;
+ }
+ /* this is the place to mask off bits in avail corresponding to broken channels in old revisions */
+}
+
+/*
+ * allocate a DMA channel, reset it, and configure it for the given device
+ */
+Dma*
+dmasetup(int device, int direction, int bigend, void (*interrupt)(void*, ulong), void *arg)
+{
+ Dma *d;
+ DmaReg *dr;
+ ulong cfg;
+ int i;
+ char name[KNAMELEN];
+
+ cfg = dmaconfig[device];
+ if(cfg == 0){
+ print("dmasetup: no device %d\n", device);
+ return nil;
+ }
+
+ ilock(&dmachans);
+ for(i=0; (dmachans.avail & (1<<i)) == 0; i++)
+ if(i >= nelem(dmachans.dma)){
+ iunlock(&dmachans);
+ return nil;
+ }
+ dmachans.avail &= ~(1<<i);
+ iunlock(&dmachans);
+
+ d = &dmachans.dma[i];
+ d->interrupt = interrupt;
+ d->arg = arg;
+ dr = d->reg;
+ dr->dcsr_c = CSRrun | CSRie | CSRerror | CSRdonea | CSRstrta | CSRdoneb | CSRstrtb;
+ dr->ddar = cfg | (direction<<4) | (bigend<<1);
+ if(d->intrset == 0){
+ d->intrset = 1;
+ snprint(name, sizeof(name), "dma%d", i);
+ intrenable(DMAbit(i), dmaintr, d, BusCPU, name);
+ }
+ return d;
+}
+
+void
+dmafree(Dma *dma)
+{
+ dma->reg->dcsr_c = CSRrun | CSRie;
+ ilock(&dmachans);
+ dmachans.avail |= 1<<dma->chan;
+ dma->interrupt = nil;
+ iunlock(&dmachans);
+}
+
+/*
+ * start dma on the given channel on one or two buffers,
+ * each of which must adhere to DMA controller restrictions.
+ * (eg, on some versions of the StrongArm it musn't span 256-byte boundaries).
+ * virtual buffer addresses are assumed to refer to contiguous physical addresses.
+ */
+int
+dmastart(Dma *dma, void *buf, int nbytes)
+{
+ ulong v, csr;
+ DmaReg *dr;
+ int b;
+
+ dr = dma->reg;
+ v = dr->dcsr;
+ if((v & (CSRstrta|CSRstrtb|CSRrun)) == (CSRstrta|CSRstrtb|CSRrun))
+ return 0; /* fully occupied */
+
+ dcflush(buf, nbytes);
+
+ csr = CSRrun | CSRie;
+
+ /* start first xfer with buffer B or A? */
+ b = (v & CSRbiu) != 0 && (v & CSRstrtb) == 0 || (v & CSRstrta) != 0;
+ if(b)
+ csr |= CSRstrtb;
+ else
+ csr |= CSRstrta;
+
+ if(v & csr & (CSRstrtb|CSRstrta))
+ panic("dmasetup csr=%2.2lux %2.2lux", v, csr);
+
+ /* set first src/dst and size */
+ dr->buf[b].start = (ulong)buf;
+ dr->buf[b].count = nbytes;
+ dr->dcsr_s = csr;
+ return 1;
+}
+
+/*
+ * stop dma on a channel
+ */
+void
+dmastop(Dma *dma)
+{
+ // print("dmastop (was %ux)\n", dma->reg->dcsr);
+
+ dma->reg->dcsr_c = CSRrun |
+ CSRie |
+ CSRerror |
+ CSRdonea |
+ CSRstrta |
+ CSRdoneb |
+ CSRstrtb;
+}
+
+/*
+ * return nonzero if there was a memory error during DMA,
+ * and clear the error state
+ */
+int
+dmaerror(Dma *dma)
+{
+ DmaReg *dr;
+ ulong e;
+
+ dr = dma->reg;
+ e = dr->dcsr & CSRerror;
+ dr->dcsr_c = e;
+ return e;
+}
+
+/*
+ * return nonzero if the DMA channel is not busy
+ */
+int
+dmaidle(Dma *d)
+{
+ return (d->reg->dcsr & (CSRstrta|CSRstrtb)) == 0;
+}
+
+static int
+dmaidlep(void *a)
+{
+ return dmaidle((Dma*)a);
+}
+
+void
+dmawait(Dma *d)
+{
+ while(!dmaidle(d))
+ sleep(&d->r, dmaidlep, d);
+}
+
+/*
+ * this interface really only copes with one buffer at once
+ */
+static void
+dmaintr(Ureg*, void *a)
+{
+ Dma *d;
+ ulong s;
+
+ d = (Dma*)a;
+ s = d->reg->dcsr;
+ if(s & CSRerror)
+ iprint("DMA error, chan %d status #%2.2lux\n", d->chan, s);
+ s &= (CSRdonea|CSRdoneb|CSRerror);
+ d->reg->dcsr_c = s;
+ if(d->interrupt != nil)
+ d->interrupt(d->arg, s & (CSRdonea|CSRdoneb));
+ wakeup(&d->r);
+}
diff --git a/os/sa1110/etherif.h b/os/sa1110/etherif.h
new file mode 100644
index 00000000..5c5c679b
--- /dev/null
+++ b/os/sa1110/etherif.h
@@ -0,0 +1,41 @@
+enum {
+ MaxEther = 3,
+ Ntypes = 8,
+};
+
+typedef struct Ether Ether;
+struct Ether {
+RWlock; /* TO DO */
+ ISAConf; /* hardware info */
+ int ctlrno;
+ int minmtu;
+ int maxmtu;
+ uchar ea[Eaddrlen];
+ int encry;
+
+ void (*attach)(Ether*); /* filled in by reset routine */
+ void (*closed)(Ether*);
+ void (*detach)(Ether*);
+ void (*transmit)(Ether*);
+ void (*interrupt)(Ureg*, void*);
+ long (*ifstat)(Ether*, void*, long, ulong);
+ long (*ctl)(Ether*, void*, long); /* custom ctl messages */
+ void (*power)(Ether*, int); /* power on/off */
+ void (*shutdown)(Ether*); /* shutdown hardware before reboot */
+ void *ctlr;
+ int pcmslot; /* PCMCIA */
+ int fullduplex; /* non-zero if full duplex */
+
+ Queue* oq;
+
+ Netif;
+};
+
+extern Block* etheriq(Ether*, Block*, int);
+extern void addethercard(char*, int(*)(Ether*));
+extern int archether(int, Ether*);
+
+#define NEXT(x, l) (((x)+1)%(l))
+#define PREV(x, l) (((x) == 0) ? (l)-1: (x)-1)
+#define HOWMANY(x, y) (((x)+((y)-1))/(y))
+#define ROUNDUP(x, y) (HOWMANY((x), (y))*(y))
diff --git a/os/sa1110/fpi.h b/os/sa1110/fpi.h
new file mode 100644
index 00000000..dfb9b1df
--- /dev/null
+++ b/os/sa1110/fpi.h
@@ -0,0 +1,61 @@
+typedef long Word;
+typedef unsigned long Single;
+typedef struct {
+ unsigned long h;
+ unsigned long l;
+} 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/os/sa1110/fpiarm.c b/os/sa1110/fpiarm.c
new file mode 100644
index 00000000..4acfcd1d
--- /dev/null
+++ b/os/sa1110/fpiarm.c
@@ -0,0 +1,483 @@
+/*
+ * 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 "io.h"
+
+#include "ureg.h"
+
+#include "fpi.h"
+
+// #define R13OK undef this if correct kernel r13 isn't in Ureg; check calculation in fpiarm below
+
+#define REG(x) (*(long*)(((char*)(ur))+roff[(x)]))
+#define FPENV (*(ufp))
+#define FR(x) (*(Internal*)(ufp)->regs[(x)&7])
+
+/* BUG: check fetch (not worthwhile in Inferno) */
+#define getubyte(a) (*(uchar*)(a))
+#define getuword(a) (*(ushort*)(a))
+#define getulong(a) (*(ulong*)(a))
+
+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,
+};
+
+int 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),
+#ifdef R13OK
+ OFR(r12), OFR(r13), OFR(r14), OFR(pc),
+#else
+ OFR(r12), OFR(type), OFR(r14), OFR(pc),
+#endif
+};
+
+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;
+
+ if(IsWeird(m) || IsWeird(n)){
+ /* BUG: should trap if not masked */
+ return V|C;
+ }
+ i = fpicmp(n, m);
+ 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, FPenv *ufp)
+{
+ void *mem;
+
+ mem = (void*)ea;
+ (*f)(&FR(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, FPenv *ufp)
+{
+ Internal tmp;
+ void *mem;
+
+ mem = (void*)ea;
+ tmp = FR(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, FPenv *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(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(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(rn);
+ if(op & (1<<3)){
+ fm = &fpconst[op&7];
+ tag = 'C';
+ }else{
+ fm = &FR(op&7);
+ 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 =%x\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(rn), &REG(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(rn);
+ fpii2w(&REG(rd), &tmp);
+ if(fpemudebug)
+ print("MOV[FD]W F%d, R%d =%ld\n", rn, rd, REG(rd));
+ break;
+ case 2: /* FPSR := Rd */
+ FPENV.status = REG(rd);
+ if(fpemudebug)
+ print("MOVW R%d, FPSR\n", rd);
+ break;
+ case 3: /* Rd := FPSR */
+ REG(rd) = FPENV.status;
+ if(fpemudebug)
+ print("MOVW FPSR, R%d\n", rd);
+ break;
+ case 4: /* FPCR := Rd */
+ FPENV.control = REG(rd);
+ if(fpemudebug)
+ print("MOVW R%d, FPCR\n", rd);
+ break;
+ case 5: /* Rd := FPCR */
+ REG(rd) = FPENV.control;
+ if(fpemudebug)
+ print("MOVW FPCR, R%d\n", rd);
+ break;
+ }
+ return;
+ }
+
+ /*
+ * arithmetic
+ */
+
+ if(op & (1<<3)){ /* constant */
+ fm = &fpconst[op&7];
+ tag = 'C';
+ }else{
+ fm = &FR(op&7);
+ 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(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(rn), &FR(rd));
+ }
+}
+
+/*
+ * returns the number of FP instructions emulated
+ */
+int
+fpiarm(Ureg *ur)
+{
+ ulong op, o;
+ FPenv *ufp;
+ int n;
+
+#ifndef R13OK
+/* ur->type = &ur->pc+1; /* calculate kernel sp/R13 and put it here for roff[13] */
+ ur->type = (ulong)(ur + 1);
+#endif
+ if (up == nil)
+ panic("fpiarm not in a process");
+ ufp = &up->env->fpu; /* because all the state is in Osenv, it need not be saved/restored */
+ if(ufp->fpistate != FPACTIVE) {
+ ufp->fpistate = FPACTIVE;
+ ufp->control = 0;
+ ufp->status = (0x01<<28)|(1<<12); /* software emulation, alternative C flag */
+ for(n = 0; n < 8; n++)
+ FR(n) = fpconst[0];
+ }
+ for(n=0;;n++){
+ op = getulong(ur->pc);
+ o = (op>>24) & 0xF;
+ if(((op>>8) & 0xF) != 1 || o != 0xE && (o&~1) != 0xC)
+ break;
+ if(condok(ur->psr, op>>28))
+ fpemu(ur->pc, op, ur, ufp);
+ ur->pc += 4;
+ if(anyhigher())
+ sched();
+ }
+ return n;
+}
diff --git a/os/sa1110/gscreen.c b/os/sa1110/gscreen.c
new file mode 100644
index 00000000..9949f9d1
--- /dev/null
+++ b/os/sa1110/gscreen.c
@@ -0,0 +1,587 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+
+#include "screen.h"
+
+enum {
+ Backgnd = 0xFF, /* white */
+ Foregnd = 0x00, /* black */
+};
+
+#define DPRINT if(1)iprint
+
+static Memdata xgdata;
+static Memimage xgscreen =
+{
+ {0, 0, 0, 0}, /* r */
+ {0, 0, 0, 0}, /* clipr */
+ 8, /* depth */
+ 1, /* nchan */
+ CMAP8, /* chan */
+ nil, /* cmap */
+ &xgdata, /* data */
+ 0, /* zero */
+ 0, /* width */
+ nil, /* layer */
+ 0, /* flags */
+};
+
+Memimage *gscreen;
+Memimage *conscol;
+Memimage *back;
+
+Memsubfont *memdefont;
+
+static Point curpos;
+static Rectangle window;
+
+typedef struct SWcursor SWcursor;
+
+static Vdisplay *vd;
+
+static char printbuf[1024];
+static int printbufpos = 0;
+static void lcdscreenputs(char*, int);
+static void screenpbuf(char*, int);
+void (*screenputs)(char*, int) = screenpbuf;
+
+static 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,
+ },
+};
+
+static ushort palette16[256];
+static void (*flushpixels)(Rectangle, ulong*, int, ulong*, int);
+static void flush8to4(Rectangle, ulong*, int, ulong*, int);
+static void flush8to4r(Rectangle, ulong*, int, ulong*, int);
+static void flush8to16(Rectangle, ulong*, int, ulong*, int);
+static void flush8to16r(Rectangle, ulong*, int, ulong*, int);
+
+/*
+lccr0=000000b9 lccr1=0b100930 lccr2=0a0108ef lccr3=00300010
+ ---
+vd->wid=320 bwid=640 gscreen->width=60 fb=d0b7cb80 data=d0ba25c0
+ */
+
+int
+setcolor(ulong p, ulong r, ulong g, ulong b)
+{
+ if(vd->depth >= 8)
+ p &= 0xff;
+ else
+ p &= 0xf;
+ vd->colormap[p][0] = r;
+ vd->colormap[p][1] = g;
+ vd->colormap[p][2] = b;
+ palette16[p] = ((r>>(32-4))<<12)|((g>>(32-4))<<7)|((b>>(32-4))<<1);
+ lcd_setcolor(p, r, g, b);
+ return ~0;
+}
+
+void
+getcolor(ulong p, ulong *pr, ulong *pg, ulong *pb)
+{
+ if(vd->depth >= 8)
+ p = (p&0xff)^0xff;
+ else
+ p = (p&0xf)^0xf;
+ *pr = vd->colormap[p][0];
+ *pg = vd->colormap[p][1];
+ *pb = vd->colormap[p][2];
+}
+
+void
+graphicscmap(int invert)
+{
+ int num, den, i, j;
+ int r, g, b, cr, cg, cb, v, p;
+
+ for(r=0,i=0;r!=4;r++) for(v=0;v!=4;v++,i+=16){
+ for(g=0,j=v-r;g!=4;g++) for(b=0;b!=4;b++,j++){
+ den=r;
+ if(g>den) den=g;
+ if(b>den) den=b;
+ if(den==0) /* divide check -- pick grey shades */
+ cr=cg=cb=v*17;
+ else{
+ num=17*(4*den+v);
+ cr=r*num/den;
+ cg=g*num/den;
+ cb=b*num/den;
+ }
+ p = (i+(j&15));
+ if(invert)
+ p ^= 0xFF;
+ if(vd->depth == 4) {
+ if((p&0xf) != (p>>4))
+ continue;
+ p &= 0xf;
+ }
+ setcolor(p,
+ cr*0x01010101,
+ cg*0x01010101,
+ cb*0x01010101);
+ }
+ }
+ lcd_flush();
+}
+
+static uchar lum[256]={
+ 0, 7, 15, 23, 39, 47, 55, 63, 79, 87, 95, 103, 119, 127, 135, 143,
+154, 17, 9, 17, 25, 49, 59, 62, 68, 89, 98, 107, 111, 129, 138, 146,
+157, 166, 34, 11, 19, 27, 59, 71, 69, 73, 99, 109, 119, 119, 139, 148,
+159, 169, 178, 51, 13, 21, 29, 69, 83, 75, 78, 109, 120, 131, 128, 149,
+ 28, 35, 43, 60, 68, 75, 83, 100, 107, 115, 123, 140, 147, 155, 163, 20,
+ 25, 35, 40, 47, 75, 85, 84, 89, 112, 121, 129, 133, 151, 159, 168, 176,
+190, 30, 42, 44, 50, 90, 102, 94, 97, 125, 134, 144, 143, 163, 172, 181,
+194, 204, 35, 49, 49, 54, 105, 119, 103, 104, 137, 148, 158, 154, 175, 184,
+ 56, 63, 80, 88, 96, 103, 120, 128, 136, 143, 160, 168, 175, 183, 40, 48,
+ 54, 63, 69, 90, 99, 107, 111, 135, 144, 153, 155, 173, 182, 190, 198, 45,
+ 50, 60, 70, 74, 100, 110, 120, 120, 150, 160, 170, 167, 186, 195, 204, 214,
+229, 55, 66, 77, 79, 110, 121, 131, 129, 165, 176, 187, 179, 200, 210, 219,
+ 84, 100, 108, 116, 124, 140, 148, 156, 164, 180, 188, 196, 204, 60, 68, 76,
+ 82, 91, 108, 117, 125, 134, 152, 160, 169, 177, 195, 204, 212, 221, 66, 74,
+ 80, 89, 98, 117, 126, 135, 144, 163, 172, 181, 191, 210, 219, 228, 238, 71,
+ 76, 85, 95, 105, 126, 135, 145, 155, 176, 185, 195, 205, 225, 235, 245, 255,
+};
+
+void flushmemscreen(Rectangle r);
+
+void
+screenclear(void)
+{
+ memimagedraw(gscreen, gscreen->r, memwhite, ZP, memopaque, ZP, SoverD);
+ curpos = window.min;
+ flushmemscreen(gscreen->r);
+}
+
+static void
+setscreen(LCDmode *mode)
+{
+ int h;
+
+// if(swc != nil)
+// swcurs_destroy(swc);
+
+ vd = lcd_init(mode);
+ if(vd == nil)
+ panic("can't initialise LCD");
+
+ if(lum[255] == 255) {
+ int i;
+ for(i=0; i<256; i++)
+ lum[i] >>= 4; /* could support depths other than 4 */
+ }
+
+ gscreen = &xgscreen;
+ xgdata.ref = 1;
+
+ if(conf.portrait == 0)
+ gscreen->r = Rect(0, 0, vd->x, vd->y);
+ else
+ gscreen->r = Rect(0, 0, vd->y, vd->x);
+ gscreen->clipr = gscreen->r;
+ gscreen->depth = 8;
+ gscreen->width = wordsperline(gscreen->r, gscreen->depth);
+ if(vd->depth == 4 || vd->depth == 16 || conf.portrait) { /* use 8 to 4 bit fakeout for speed */
+ if((xgdata.bdata = xspanalloc(gscreen->width*gscreen->r.max.y*BY2WD+CACHELINESZ, CACHELINESZ, 0)) == nil)
+ panic("can't alloc vidmem");
+ xgdata.bdata = minicached(xgdata.bdata);
+ if(conf.portrait == 0)
+ flushpixels = vd->depth==4? flush8to4: flush8to16;
+ else
+ flushpixels = vd->depth==4? flush8to4r: flush8to16r;
+ } else{
+ xgdata.bdata = (uchar*)vd->fb;
+ flushpixels = nil;
+ }
+ memimageinit();
+ memdefont = getmemdefont();
+
+ memsetchan(gscreen, CMAP8); /* TO DO: could now use RGB16 */
+ back = memwhite;
+ conscol = memblack;
+ memimagedraw(gscreen, gscreen->r, memwhite, ZP, memopaque, ZP, SoverD);
+
+ DPRINT("vd->wid=%d bwid=%d gscreen->width=%ld fb=%p data=%p\n",
+ vd->x, vd->bwid, gscreen->width, vd->fb, xgdata.bdata);
+ graphicscmap(0);
+ h = memdefont->height;
+ window = insetrect(gscreen->r, 4);
+ window.max.y = window.min.y+(Dy(window)/h)*h;
+ screenclear();
+
+// swc = swcurs_create(gscreendata.data, gscreen.width, gscreen.ldepth, gscreen.r, 1);
+
+ drawcursor(nil);
+}
+
+void
+screeninit(void)
+{
+ LCDmode lcd;
+
+ memset(&lcd, 0, sizeof(lcd));
+ if(archlcdmode(&lcd) < 0)
+ return;
+ setscreen(&lcd);
+ screenputs = lcdscreenputs;
+ if(printbufpos)
+ screenputs("", 0);
+ blanktime = 3; /* minutes */
+}
+
+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 = (gscreen->data->bdata != (uchar*)vd->fb);
+
+ return (uchar*)gscreen->data->bdata;
+}
+
+void
+detachscreen(void)
+{
+}
+
+static void
+flush8to4(Rectangle r, ulong *s, int sw, ulong *d, int dw)
+{
+ int i, h, w;
+
+/*
+ print("1) s=%ux sw=%d d=%ux dw=%d r=(%d,%d)(%d,%d)\n",
+ s, sw, d, dw, r.min.x, r.min.y, r.max.x, r.max.y);
+*/
+
+ r.min.x &= ~7;
+ r.max.x = (r.max.x + 7) & ~7;
+ s += (r.min.y*sw)+(r.min.x>>2);
+ d += (r.min.y*dw)+(r.min.x>>3);
+ h = Dy(r);
+ w = Dx(r) >> 3;
+ sw -= w*2;
+ dw -= w;
+
+ while(h--) {
+ for(i=w; i; i--) {
+ ulong v1 = *s++;
+ ulong v2 = *s++;
+ *d++ = (lum[v2>>24]<<28)
+ |(lum[(v2>>16)&0xff]<<24)
+ |(lum[(v2>>8)&0xff]<<20)
+ |(lum[v2&0xff]<<16)
+ |(lum[v1>>24]<<12)
+ |(lum[(v1>>16)&0xff]<<8)
+ |(lum[(v1>>8)&0xff]<<4)
+ |(lum[v1&0xff])
+ ;
+ }
+ s += sw;
+ d += dw;
+ }
+}
+
+static void
+flush8to16(Rectangle r, ulong *s, int sw, ulong *d, int dw)
+{
+ int i, h, w;
+ ushort *p;
+
+ if(0)
+ iprint("1) s=%p sw=%d d=%p dw=%d r=[%d,%d, %d,%d]\n",
+ s, sw, d, dw, r.min.x, r.min.y, r.max.x, r.max.y);
+
+ r.min.x &= ~3;
+ r.max.x = (r.max.x + 3) & ~3; /* nearest ulong */
+ s += (r.min.y*sw)+(r.min.x>>2);
+ d += (r.min.y*dw)+(r.min.x>>1);
+ h = Dy(r);
+ w = Dx(r) >> 2; /* also ulong */
+ sw -= w;
+ dw -= w*2;
+ if(0)
+ iprint("h=%d w=%d sw=%d dw=%d\n", h, w, sw, dw);
+
+ p = palette16;
+ while(--h >= 0){
+ for(i=w; --i>=0;){
+ ulong v = *s++;
+ *d++ = (p[(v>>8)&0xFF]<<16) | p[v & 0xFF];
+ *d++ = (p[v>>24]<<16) | p[(v>>16)&0xFF];
+ }
+ s += sw;
+ d += dw;
+ }
+}
+
+static void
+flush8to4r(Rectangle r, ulong *s, int sw, ulong *d, int dw)
+{
+ flush8to4(r, s, sw, d, dw); /* rotation not implemented */
+}
+
+static void
+flush8to16r(Rectangle r, ulong *s, int sw, ulong *d, int dw)
+{
+ int x, y, w, dws;
+ ushort *p;
+ ushort *ds;
+
+ if(0)
+ iprint("1) s=%p sw=%d d=%p dw=%d r=[%d,%d, %d,%d]\n",
+ s, sw, d, dw, r.min.x, r.min.y, r.max.x, r.max.y);
+
+ r.min.y &= ~3;
+ r.max.y = (r.max.y+3) & ~3;
+ r.min.x &= ~7;
+ r.max.x = (r.max.x + 7) & ~7;
+ s += (r.min.y*sw)+(r.min.x>>2);
+// d += (r.min.y*dw)+(r.min.x>>1);
+ w = Dx(r) >> 2; /* also ulong */
+ sw -= w;
+ dws = dw*2;
+ if(0)
+ iprint("h=%d w=%d sw=%d dw=%d x,y=%d,%d %d\n", Dy(r), w, sw, dw, r.min.x,r.min.y, dws);
+
+ p = palette16;
+ for(y=r.min.y; y<r.max.y; y++){
+ for(x=r.min.x; x<r.max.x; x+=4){
+ ulong v = *s++;
+ ds = (ushort*)(d + x*dw) + (gscreen->r.max.y-(y+1));
+ ds[0] = p[v & 0xFF];
+ ds[dws] = p[(v>>8)&0xFF];
+ ds[dws*2] = p[(v>>16)&0xFF];
+ ds[dws*3] = p[(v>>24)&0xFF];
+ }
+ s += sw;
+ }
+}
+
+void
+flushmemscreen(Rectangle r)
+{
+ if(rectclip(&r, gscreen->r) == 0)
+ return;
+ if(r.min.x >= r.max.x || r.min.y >= r.max.y)
+ return;
+ if(flushpixels != nil)
+ flushpixels(r, (ulong*)gscreen->data->bdata, gscreen->width, (ulong*)vd->fb, vd->bwid >> 2);
+ lcd_flush();
+}
+
+static void
+scroll(void)
+{
+ int o;
+ Point p;
+ Rectangle r;
+
+ o = 4*memdefont->height;
+ 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, SoverD);
+ flushmemscreen(r);
+ r = Rpt(Pt(window.min.x, window.max.y-o), window.max);
+ memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
+ flushmemscreen(r);
+
+ curpos.y -= o;
+}
+
+static void
+clearline(void)
+{
+ Rectangle r;
+ int yloc = curpos.y;
+
+ r = Rpt(Pt(window.min.x, window.min.y + yloc),
+ Pt(window.max.x, window.min.y+yloc+memdefont->height));
+ memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
+}
+
+static void
+screenputc(char *buf)
+{
+ Point p;
+ int h, w, pos;
+ Rectangle r;
+ static int *xp;
+ static int xbuf[256];
+
+ h = memdefont->height;
+ 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;
+ /* fall through */
+ case '\r':
+ xp = xbuf;
+ curpos.x = window.min.x;
+ break;
+ case '\t':
+ if(curpos.x == window.min.x)
+ clearline();
+ p = memsubfontwidth(memdefont, " ");
+ w = p.x;
+ *xp++ = curpos.x;
+ pos = (curpos.x-window.min.x)/w;
+ pos = 8-(pos%8);
+ r = Rect(curpos.x, curpos.y, curpos.x+pos*w, curpos.y+h);
+ memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
+ flushmemscreen(r);
+ curpos.x += pos*w;
+ break;
+ case '\b':
+ if(xp <= xbuf)
+ break;
+ xp--;
+ r = Rpt(Pt(*xp, curpos.y), Pt(curpos.x, curpos.y + h));
+ memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
+ 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");
+
+ if(curpos.x == window.min.x)
+ clearline();
+ if(xp < xbuf+nelem(xbuf))
+ *xp++ = curpos.x;
+ r = Rect(curpos.x, curpos.y, curpos.x+w, curpos.y+h);
+ memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
+ memimagestring(gscreen, curpos, conscol, ZP, memdefont, buf);
+ flushmemscreen(r);
+ curpos.x += w;
+ }
+}
+
+static void
+screenpbuf(char *s, int n)
+{
+ if(printbufpos+n > sizeof(printbuf))
+ n = sizeof(printbuf)-printbufpos;
+ if(n > 0) {
+ memmove(&printbuf[printbufpos], s, n);
+ printbufpos += n;
+ }
+}
+
+static void
+screendoputs(char *s, int n)
+{
+ int i;
+ Rune r;
+ char buf[4];
+
+ 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);
+ }
+}
+
+void
+screenflush(void)
+{
+ int j = 0;
+ int k;
+
+ for (k = printbufpos; j < k; k = printbufpos) {
+ screendoputs(printbuf + j, k - j);
+ j = k;
+ }
+ printbufpos = 0;
+}
+
+static void
+lcdscreenputs(char *s, int n)
+{
+ static Proc *me;
+
+ if(!canlock(vd)) {
+ /* don't deadlock trying to print in interrupt */
+ /* don't deadlock trying to print while in print */
+ if(islo() == 0 || up != nil && up == me){
+ /* save it for later... */
+ /* In some cases this allows seeing a panic message
+ that would be locked out forever */
+ screenpbuf(s, n);
+ return;
+ }
+ lock(vd);
+ }
+
+ me = up;
+ if(printbufpos)
+ screenflush();
+ screendoputs(s, n);
+ if(printbufpos)
+ screenflush();
+ me = nil;
+
+ unlock(vd);
+}
+
+/*
+ * interface between draw, mouse and cursor
+ */
+void
+cursorupdate(Rectangle r)
+{
+}
+
+void
+cursorenable(void)
+{
+}
+
+void
+cursordisable(void)
+{
+}
+
+void
+drawcursor(Drawcursor* c)
+{
+}
diff --git a/os/sa1110/gscreen.h b/os/sa1110/gscreen.h
new file mode 100644
index 00000000..7c1de585
--- /dev/null
+++ b/os/sa1110/gscreen.h
@@ -0,0 +1,87 @@
+typedef struct Cursor Cursor;
+typedef struct LCDmode LCDmode;
+typedef struct LCDparam LCDparam;
+typedef struct Vmode Vmode;
+typedef struct Physdisplay Physdisplay;
+typedef struct Physcursor Physcursor;
+
+#define CURSWID 16
+#define CURSHGT 16
+
+struct Cursor {
+ Point offset;
+ uchar clr[CURSWID/BI2BY*CURSHGT];
+ uchar set[CURSWID/BI2BY*CURSHGT];
+};
+
+struct Vmode {
+ int x; /* 0 -> default or any match for all fields */
+ int y;
+ uchar depth;
+ uchar hz;
+};
+
+struct Physdisplay {
+ uchar* fb; /* frame buffer */
+ ulong colormap[256][3];
+ Rectangle r;
+ int bwid;
+ void* aux;
+ Memimage* gscreen;
+ Memsubfont* memdefont;
+ Physcursor* cursor;
+ void* cdata; /* cursor data */
+ Lock;
+ Vmode;
+};
+
+struct LCDparam {
+ uchar pbs;
+ uchar dual;
+ uchar mono;
+ uchar active;
+ uchar hsync_wid;
+ uchar sol_wait;
+ uchar eol_wait;
+ uchar vsync_hgt;
+ uchar sof_wait;
+ uchar eof_wait;
+ uchar lines_per_int;
+ uchar palette_delay;
+ uchar acbias_lines;
+ uchar obits;
+ uchar vsynclow;
+ uchar hsynclow;
+};
+
+struct LCDmode {
+ Vmode;
+ LCDparam;
+};
+
+int archlcdmode(LCDmode*);
+
+Vdisplay *lcd_init(LCDmode*);
+void lcd_setcolor(ulong, ulong, ulong, ulong);
+void lcd_flush(void);
+
+extern void blankscreen(int);
+extern void drawblankscreen(int);
+extern ulong blanktime;
+extern Point mousexy(void);
+
+enum {
+ Backgnd = 0xFF, /* white */
+ Foregnd = 0x00, /* black */
+};
+
+struct Physcursor {
+ char* name;
+
+ void (*create)(Physdisplay*);
+ void (*enable)(Physdisplay*);
+ void (*disable)(Physdisplay*);
+ void (*load)(Physdisplay*, Cursor*);
+ int (*move)(Physdisplay*, Point);
+ void (*destroy)(Physdisplay*);
+};
diff --git a/os/sa1110/i2c.h b/os/sa1110/i2c.h
new file mode 100644
index 00000000..fb179606
--- /dev/null
+++ b/os/sa1110/i2c.h
@@ -0,0 +1,16 @@
+/* i2cgpio.c */
+
+int i2c_write_byte(uchar addr, uchar data);
+int i2c_read_byte(uchar addr, uchar *data);
+void i2c_reset(void);
+
+extern unsigned char i2c_iactl[];
+int i2c_setpin(int b);
+int i2c_clrpin(int b);
+int i2c_getpin(int b);
+
+/* GPIO pin assignments (0->31) - defined in arch????.c */
+extern int gpio_i2c_sda; /* in/out, as per i2c protocol */
+extern int gpio_i2c_scl; /* in/out, as per i2c protocol */
+
+
diff --git a/os/sa1110/i2cgpio.c b/os/sa1110/i2cgpio.c
new file mode 100644
index 00000000..1eb69387
--- /dev/null
+++ b/os/sa1110/i2cgpio.c
@@ -0,0 +1,274 @@
+/*
+ * I2C master emulation using GPIO pins.
+ * 7 bit addressing only.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+#include "i2c.h"
+
+/* GPIO bitmasks */
+static struct {
+ Lock;
+ ulong sda;
+ ulong scl;
+} i2c;
+
+
+/* set pin level high by disabling output drive and allowing pull-up to work */
+static void
+i2c_set(int pin)
+{
+ GPIOREG->gpdr &= ~pin; /* configure pin as input */
+}
+
+/* set pin level low with output drive */
+static void
+i2c_clear(int pin)
+{
+ GPIOREG->gpcr = pin; /* set pin output low */
+ GPIOREG->gpdr |= pin; /* configure pin as output */
+}
+
+static int
+i2c_getack(void)
+{
+ /* scl is low, sda is not defined */
+
+ i2c_set(i2c.sda); /* set data high */
+ timer_delay(US2TMR(3));
+
+ i2c_set(i2c.scl); /* raise clock */
+ timer_delay(US2TMR(5));
+
+ /* check for ack from slave! */
+ if (GPIOREG->gplr & i2c.sda)
+ print("I2C: Warning did not get ack!\n");
+
+ i2c_clear(i2c.sda); /* lower data */
+ i2c_clear(i2c.scl); /* lower clock */
+ timer_delay(US2TMR(3));
+
+ /* scl is low, sda is low */
+ return 1;
+}
+
+
+static void
+i2c_putack(void)
+{
+ /* scl is low, sda is not defined */
+
+ timer_delay(US2TMR(3)); /* lower data */
+ i2c_clear(i2c.sda);
+
+ i2c_set(i2c.scl); /* pulse clock */
+ timer_delay(US2TMR(5));
+
+ i2c_clear(i2c.scl); /* lower clock */
+ timer_delay(US2TMR(3));
+
+ /* scl is low, sda is low */
+}
+
+
+static void
+i2c_putbyte(uchar b)
+{
+ uchar m;
+
+ /* start condition has been sent */
+ /* scl is low, sda is low */
+
+ for(m=0x80; m; m >>= 1) {
+
+ /* set data bit */
+ if(b&m)
+ i2c_set(i2c.sda);
+ else
+ i2c_clear(i2c.sda);
+
+ /* pulse clock */
+ timer_delay(US2TMR(3));
+ i2c_set(i2c.scl);
+ timer_delay(US2TMR(5));
+ i2c_clear(i2c.scl);
+ timer_delay(US2TMR(3));
+ }
+
+ i2c_clear(i2c.sda);
+ /* scl is low, sda is low */
+}
+
+
+static uchar
+i2c_getbyte(void)
+{
+ /* start condition, address and ack been done */
+ /* scl is low, sda is high */
+ uchar data = 0x00;
+ int i;
+
+ i2c_set(i2c.sda);
+ for (i=7; i >= 0; i--) {
+
+ timer_delay(US2TMR(3));
+
+ /* raise clock */
+ i2c_set(i2c.scl);
+ timer_delay(US2TMR(5));
+
+ /* sample data */
+ if(GPIOREG->gplr & i2c.sda)
+ data |= 1<<i;
+
+ /* lower clock */
+ i2c_clear(i2c.scl);
+ timer_delay(US2TMR(3));
+ }
+
+ i2c_clear(i2c.sda);
+ return data;
+}
+
+/* generate I2C start condition */
+static int
+i2c_start(void)
+{
+ /* check that both scl and sda are high */
+ if ((GPIOREG->gplr & (i2c.sda | i2c.scl)) != (i2c.sda | i2c.scl))
+ print("I2C: Bus not clear when attempting start condition\n");
+
+ i2c_clear(i2c.sda); /* lower sda */
+ timer_delay(US2TMR(5));
+
+ i2c_clear(i2c.scl); /* lower scl */
+ timer_delay(US2TMR(3));
+
+ return 1;
+}
+
+/* generate I2C stop condition */
+static int
+i2c_stop(void)
+{
+ /* clock is low, data is low */
+ timer_delay(US2TMR(3));
+
+ i2c_set(i2c.scl);
+ timer_delay(US2TMR(5));
+
+ i2c_set(i2c.sda);
+
+ timer_delay(MS2TMR(1)); /* ensure separation between commands */
+
+ return 1;
+}
+
+/*
+ * external I2C interface
+ */
+
+/* write a byte over the i2c bus */
+int
+i2c_write_byte(uchar addr, uchar data)
+{
+ int rc = 0;
+
+ ilock(&i2c);
+ if(i2c_start() < 0) /* start condition */
+ rc = -1;
+
+ i2c_putbyte(addr & 0xfe); /* address byte (LSB = 0 -> write) */
+
+ if (i2c_getack() < 0) /* get ack */
+ rc = -2;
+
+ i2c_putbyte(data); /* data byte */
+
+ if (i2c_getack() < 0) /* get ack */
+ rc = -3;
+
+ if (i2c_stop() < 0)
+ rc = -4; /* stop condition */
+ iunlock(&i2c);
+
+ return rc;
+}
+
+/* read a byte over the i2c bus */
+int
+i2c_read_byte(uchar addr, uchar *data)
+{
+ int rc = 0;
+
+ ilock(&i2c);
+ if(i2c_start() < 0) /* start condition */
+ rc = -1;
+
+ i2c_putbyte(addr | 0x01); /* address byte (LSB = 1 -> read) */
+
+ if(i2c_getack() < 0) /* get ack */
+ rc = -2;
+
+ *data = i2c_getbyte(); /* data byte */
+
+ i2c_putack(); /* put ack */
+
+ if (i2c_stop() < 0) /* stop condition */
+ rc = -4;
+ iunlock(&i2c);
+
+ return rc;
+}
+
+void
+i2c_reset(void)
+{
+ /* initialise bitmasks */
+ i2c.sda = (1 << gpio_i2c_sda);
+ i2c.scl = (1 << gpio_i2c_scl);
+
+ /* ensure that both clock and data are high */
+ i2c_set(i2c.sda);
+ i2c_set(i2c.scl);
+ timer_delay(MS2TMR(5));
+}
+
+
+/*
+ * external pin set/clear interface
+ */
+uchar i2c_iactl[2] = { 0xff, 0xff }; /* defaults overridden in arch?????.c */
+
+int
+i2c_setpin(int b)
+{
+ int i = b>>3;
+
+ ilock(&i2c);
+ i2c_iactl[i] |= (1 << (b&7));
+ iunlock(&i2c);
+ return i2c_write_byte(0x40 | (i << 1), i2c_iactl[i]);
+}
+
+int
+i2c_clrpin(int b)
+{
+ int i = b>>3;
+
+ ilock(&i2c);
+ i2c_iactl[i] &= ~(1 << (b&7));
+ iunlock(&i2c);
+ return i2c_write_byte(0x40 | (i << 1), i2c_iactl[i]);
+}
+
+int
+i2c_getpin(int b)
+{
+ return (i2c_iactl[(b>>3)&1] & (1<<(b&7))) != 0;
+}
diff --git a/os/sa1110/l.s b/os/sa1110/l.s
new file mode 100644
index 00000000..7f731fe6
--- /dev/null
+++ b/os/sa1110/l.s
@@ -0,0 +1,530 @@
+#include "mem.h"
+
+/*
+ * Entered here from the boot loader with
+ * supervisor mode, interrupts disabled;
+ * MMU, IDC and WB disabled.
+ */
+
+TEXT _startup(SB), $-4
+ MOVW $setR12(SB), R12 /* static base (SB) */
+ MOVW $(PsrDirq|PsrDfiq|PsrMsvc), R1 /* ensure SVC mode with interrupts disabled */
+ MOVW R1, CPSR
+ MOVW $(MACHADDR+BY2PG-4), R13 /* stack; 4 bytes for link */
+
+ BL main(SB)
+dead:
+ B dead
+ BL _div(SB) /* hack to get _div etc loaded */
+
+TEXT getcpuid(SB), $-4
+ MRC CpMMU, 0, R0, C(CpCPUID), C(0)
+ RET
+
+TEXT mmugetctl(SB), $-4
+ MRC CpMMU, 0, R0, C(CpControl), C(0)
+ RET
+
+TEXT mmugetdac(SB), $-4
+ MRC CpMMU, 0, R0, C(CpDAC), C(0)
+ RET
+
+TEXT mmugetfar(SB), $-4
+ MRC CpMMU, 0, R0, C(CpFAR), C(0)
+ RET
+
+TEXT mmugetfsr(SB), $-4
+ MRC CpMMU, 0, R0, C(CpFSR), C(0)
+ RET
+
+TEXT mmuputdac(SB), $-4
+ MCR CpMMU, 0, R0, C(CpDAC), C(0)
+ RET
+
+TEXT mmuputfsr(SB), $-4
+ MCR CpMMU, 0, R0, C(CpFSR), C(0)
+ RET
+
+TEXT mmuputttb(SB), $-4
+ MCR CpMMU, 0, R0, C(CpTTB), C(0)
+ RET
+
+TEXT mmuputctl(SB), $-4
+ MCR CpMMU, 0, R0, C(CpControl), C(0)
+ MOVW R0, R0
+ MOVW R0, R0
+ RET
+
+TEXT tlbinvalidateall(SB), $-4
+ MCR CpMMU, 0, R0, C(CpTLBops), C(7)
+ RET
+
+TEXT tlbinvalidate(SB), $-4
+ MCR CpMMU, 0, R0, C(CpTLBops), C(7), 1
+ RET
+
+TEXT mmuenable(SB), $-4
+
+ MOVW $1, R1
+ MCR CpMMU, 0, R1, C(CpDAC), C(3) /* set domain 0 to client */
+
+ /* disable and flush all caches and TLB's before (re-)enabling MMU */
+ MOVW $(CpCi32 | CpCd32 | (1<<6) | CpCsystem), R1
+ MRC CpMMU, 0, R1, C(CpControl), C(0)
+ MOVW $0, R1 /* disable everything */
+ MCR CpMMU, 0, R1, C(CpCacheCtl), C(7), 0 /* Flush I&D Caches */
+ MCR CpMMU, 0, R1, C(CpCacheCtl), C(10), 4 /* drain write buffer */
+ MCR CpMMU, 0, R1, C(CpTLBops), C(7), 0 /* Flush I&D TLB */
+ MCR CpMMU, 0, R1, C(CpRBops), C(0), 0 /* Flush Read Buffer */
+
+ /* enable desired mmu mode (R0) */
+ MCR CpMMU, 0, R0, C(1), C(0)
+ MOVW R0, R0
+ MOVW R0, R0
+ MOVW R0, R0
+ MOVW R0, R0
+ RET /* start running in remapped area */
+
+TEXT setr13(SB), $-4
+ MOVW 4(FP), R1
+
+ MOVW CPSR, R2
+ BIC $PsrMask, R2, R3
+ ORR R0, R3
+ MOVW R3, CPSR
+
+ MOVW R13, R0
+ MOVW R1, R13
+
+ MOVW R2, CPSR
+ RET
+
+TEXT vectors(SB), $-4
+ MOVW 0x18(R15), R15 /* reset */
+ MOVW 0x18(R15), R15 /* undefined */
+ MOVW 0x18(R15), R15 /* SWI */
+ 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), $-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 $_vpab(SB) /* prefetch abort, switch to svc mode */
+ WORD $_vdab(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 */
+
+TEXT _vund(SB), $-4
+ MOVM.DB [R0-R3], (R13)
+ MOVW $PsrMund, R0
+ B _vswitch
+
+TEXT _vsvc(SB), $-4
+ MOVW.W R14, -4(R13)
+ MOVW CPSR, R14
+ MOVW.W R14, -4(R13)
+ BIC $PsrMask, R14
+ ORR $(PsrDirq|PsrDfiq|PsrMsvc), R14
+ MOVW R14, CPSR
+ MOVW $PsrMsvc, R14
+ MOVW.W R14, -4(R13)
+ B _vsaveu
+
+TEXT _vpab(SB), $-4
+ MOVM.DB [R0-R3], (R13)
+ MOVW $PsrMabt, R0
+ B _vswitch
+
+TEXT _vdab(SB), $-4
+ MOVM.DB [R0-R3], (R13)
+ MOVW $(PsrMabt+1), R0
+ B _vswitch
+
+TEXT _vfiq(SB), $-4 /* FIQ */
+ MOVM.DB [R0-R3], (R13)
+ MOVW $PsrMfiq, R0
+ B _vswitch
+
+TEXT _virq(SB), $-4 /* IRQ */
+ MOVM.DB [R0-R3], (R13)
+ MOVW $PsrMirq, R0
+
+_vswitch: /* switch to svc mode */
+ MOVW SPSR, R1
+ MOVW R14, R2
+ MOVW R13, R3
+
+ MOVW CPSR, R14
+ BIC $PsrMask, R14
+ ORR $(PsrDirq|PsrDfiq|PsrMsvc), R14
+ MOVW R14, CPSR
+
+ MOVM.DB.W [R0-R2], (R13)
+ MOVM.DB (R3), [R0-R3]
+
+_vsaveu: /* Save Registers */
+ MOVW.W R14, -4(R13) /* save link */
+ MCR CpMMU, 0, R0, C(0), C(0), 0
+
+ SUB $8, R13
+ MOVM.DB.W [R0-R12], (R13)
+
+ MOVW R0, R0 /* gratuitous noop */
+
+ MOVW $setR12(SB), R12 /* static base (SB) */
+ MOVW R13, R0 /* argument is ureg */
+ SUB $8, R13 /* space for arg+lnk*/
+ BL trap(SB)
+
+_vrfe: /* Restore Regs */
+ MOVW CPSR, R0 /* splhi on return */
+ ORR $(PsrDirq|PsrDfiq), R0, R1
+ MOVW R1, CPSR
+ ADD $(8+4*15), R13 /* [r0-R14]+argument+link */
+ MOVW (R13), R14 /* restore link */
+ MOVW 8(R13), R0
+ MOVW R0, SPSR
+ MOVM.DB.S (R13), [R0-R14] /* restore user registers */
+ MOVW R0, R0 /* gratuitous nop */
+ ADD $12, R13 /* skip saved link+type+SPSR*/
+ RFE /* MOVM.IA.S.W (R13), [R15] */
+
+TEXT splhi(SB), $-4
+ MOVW CPSR, R0
+ ORR $(PsrDirq), R0, R1
+ MOVW R1, CPSR
+ MOVW $(MACHADDR), R6
+ MOVW R14, (R6) /* m->splpc */
+ RET
+
+TEXT spllo(SB), $-4
+ MOVW CPSR, R0
+ BIC $(PsrDirq|PsrDfiq), R0, R1
+ MOVW R1, CPSR
+ RET
+
+TEXT splx(SB), $-4
+ MOVW $(MACHADDR), R6
+ MOVW R14, (R6) /* m->splpc */
+
+TEXT splxpc(SB), $-4
+ MOVW R0, R1
+ MOVW CPSR, R0
+ MOVW R1, CPSR
+ RET
+
+TEXT spldone(SB), $-4
+ RET
+
+TEXT islo(SB), $-4
+ MOVW CPSR, R0
+ AND $(PsrDirq), R0
+ EOR $(PsrDirq), R0
+ RET
+
+TEXT splfhi(SB), $-4
+ MOVW CPSR, R0
+ ORR $(PsrDfiq|PsrDirq), R0, R1
+ MOVW R1, CPSR
+ RET
+
+TEXT splflo(SB), $-4
+ MOVW CPSR, R0
+ BIC $(PsrDfiq), R0, R1
+ MOVW R1, CPSR
+ RET
+
+TEXT getcpsr(SB), $-4
+ MOVW CPSR, R0
+ RET
+
+TEXT getspsr(SB), $-4
+ MOVW SPSR, R0
+ RET
+
+TEXT getcallerpc(SB), $-4
+ MOVW 0(R13), R0
+ RET
+
+TEXT _tas(SB), $-4
+ MOVW R0, R1
+ MOVW $0xDEADDEAD, R2
+ SWPW R2, (R1), R0
+ RET
+
+TEXT setlabel(SB), $-4
+ MOVW R13, 0(R0) /* sp */
+ MOVW R14, 4(R0) /* pc */
+ MOVW $0, R0
+ RET
+
+TEXT gotolabel(SB), $-4
+ MOVW 0(R0), R13 /* sp */
+ MOVW 4(R0), R14 /* pc */
+ MOVW $1, R0
+ RET
+
+/*
+ * flush the whole icache
+ */
+TEXT icflushall(SB), $-4
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(5), 0
+ MOVW R0,R0
+ MOVW R0,R0
+ MOVW R0,R0
+ MOVW R0,R0
+ RET
+
+/*
+ * write back whole data cache and drain write buffer
+ */
+TEXT dcflushall(SB), $-4
+_dcflushall:
+ MOVW $(DCFADDR), R0
+ ADD $8192, R0, R1
+dcflushall1:
+ MOVW.P CACHELINESZ(R0), R2
+ CMP R1,R0
+ BNE dcflushall1
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 4 /* drain write buffer */
+ MOVW R0,R0
+ MOVW R0,R0
+ MOVW R0,R0
+ MOVW R0,R0
+ RET
+
+/*
+ * write back a given region and drain write buffer
+ */
+TEXT dcflush(SB), $-4
+ MOVW 4(FP), R1
+ CMP $(4*1024), R1
+ BGE _dcflushall
+ ADD R0, R1
+ BIC $(CACHELINESZ-1), R0
+dcflush1:
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 1 /* clean entry */
+ ADD $CACHELINESZ, R0
+ CMP R1, R0
+ BLO dcflush1
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 4 /* drain write buffer */
+ MOVW R0,R0
+ MOVW R0,R0
+ MOVW R0,R0
+ MOVW R0,R0
+ RET
+
+/*
+ * write back mini data cache
+ */
+TEXT minidcflush(SB), $-4
+ MOVW $(MCFADDR), R0
+ ADD $(16*CACHELINESZ), R0, R1
+
+wbbflush:
+ MOVW.P CACHELINESZ(R0), R2
+ CMP R1,R0
+ BNE wbbflush
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 4 /* drain write buffer */
+ MOVW R0,R0
+ MOVW R0,R0
+ MOVW R0,R0
+ MOVW R0,R0
+ RET
+
+/*
+ * invalidate data caches (main and mini)
+ */
+TEXT dcinval(SB), $-4
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(6), 0
+ RET
+
+/* for devboot */
+TEXT gotopc(SB), $-4
+ MOVW R0, R1
+ MOVW $0, R0
+ MOVW R1, PC
+ RET
+
+/*
+ * See page 9-26 of the SA1110 developer's manual.
+ * trap copies this to a cache-aligned area.
+ */
+TEXT _idlemode(SB), $-4
+ MOVW $UCDRAMZERO, R1
+ MOVW R0,R0
+ MOVW R0,R0
+ MOVW R0,R0
+ MOVW R0,R0
+ MOVW R0,R0
+ MOVW R0,R0
+ MOVW R0,R0
+ /* the following must be on a cache line boundary */
+ MCR CpPWR, 0, R0, C(CpTest), C(0x2), 2 /* disable clock switching */
+ MOVW (R1), R0 /* non-cacheable memory read */
+ MCR CpPWR, 0, R0, C(CpTest), C(0x8), 2
+ MCR CpPWR, 0, R0, C(CpTest), C(0x2), 1 /* enable clock switching */
+ RET
+
+/*
+ * the following code is considerably modified from the
+ * sleep code by nemo@gsyc.escet.urjc.es for Plan 9, but that's
+ * where it started. in particular, there's no need to save regs in all modes,
+ * since here we're called from kernel main level (a kproc) so nothing is live;
+ * the only regs needed are the various R13s, but we have trap restore them on resume.
+ * similarly there's no need to save SPSR, CPSR, etc. (even CpPID isn't really needed here).
+ */
+
+#define MDREFR_k1db2 (1<<22)
+#define MDREFR_slfrsh (1<<31) /* self refresh */
+#define MDREFR_e1pin (1<<20)
+#define MSC_rt ((3<<16)|(3<<0))
+#define MDCFNG_de ((3<<16)|(3<<0)) /* dram enable, banks (3 2), (1 0) */
+
+TEXT suspenditall(SB), $-4
+ MOVW.W R14, -4(R13)
+ /* push mmu state on stack */
+ MRC CpMMU, 0, R1, C(CpDAC), C(0)
+ MRC CpMMU, 0, R2, C(CpTTB), C(0)
+ MRC CpMMU, 0, R3, C(CpPID), C(0)
+ MRC CpMMU, 0, R4, C(CpControl), C(0)
+ MOVM.DB.W [R1-R4], (R13)
+ /* if pspr by convention held a stack pointer pointing to a pc we wouldn't need power_state */
+ MOVW R13, power_state+0(SB)
+
+ BL dcflushall(SB)
+ /* don't write DRAM after this */
+
+ MOVW $PHYSPOWER, R3
+
+ /* put resume address in scratchpad for boot loader */
+ MOVW $power_resume+0(SB), R2
+ MOVW R2, 0x8(R3) /* pspr */
+
+ /* disable clock switching */
+ MCR CpPWR, 0, R1, C(CpTest), C(0x2), 2
+
+ /* adjust mem timing first to avoid processor bug causing hang */
+ MOVW $MDCNFG, R5
+ MOVW 0x1c(R5), R2
+ ORR $(MDREFR_k1db2), R2
+ MOVW R2, 0x1c(R5)
+
+ /* set PLL to lower speed w/ delay */
+ MOVW $(120*206),R0
+l11: SUB $1,R0
+ BGT l11
+ MOVW $0, R2
+ MOVW R2, 0x14(R3) /* ppcr */
+ MOVW $(120*206),R0
+l12: SUB $1,R0
+ BGT l12
+
+ /*
+ * SA1110 fix for various suspend bugs in pre-B4 chips (err. 14-16, 18):
+ * set up register values here for use in code below that is at most
+ * one cache line (32 bytes) long, to run without DRAM.
+ */
+ /* 1. clear RT in MSCx (R1, R7, R8) without changing other bits */
+ MOVW 0x10(R5), R1 /* MSC0 */
+ BIC $(MSC_rt), R1
+ MOVW 0x14(R5), R7 /* MSC1 */
+ BIC $(MSC_rt), R7
+ MOVW 0x2c(R5), R8 /* MSC2 */
+ BIC $(MSC_rt), R8
+ /* 2. clear DRI0-11 in MDREFR (R4) without changing other bits */
+ MOVW 0x1c(R5), R4
+ BIC $(0xfff0), R4
+ /* 3. set SLFRSH in MDREFR (R6) without changing other bits */
+ ORR $(MDREFR_slfrsh), R4, R6
+ /* 4. clear DE in MDCNFG (R9), and any other bits desired */
+ MOVW 0x0(R5), R9
+ BIC $(MDCFNG_de), R9
+ /* 5. clear SLFRSH and E1PIN (R10), without changing other bits */
+ BIC $(MDREFR_slfrsh), R4, R10
+ BIC $(MDREFR_e1pin), R10
+ /* 6. force sleep mode in PMCR (R2) */
+ MOVW $1,R2
+ MOVW suspendcode+0(SB), R0
+ B (R0) /* off to do it */
+
+/*
+ * the following is copied by trap.c to a cache-aligned area (suspendcode),
+ * so that it can all run during disabling of DRAM
+ */
+TEXT _suspendcode(SB), $-4
+ /* 1: clear RT field of all MSCx registers */
+ MOVW R1, 0x10(R5)
+ MOVW R7, 0x14(R5)
+ MOVW R8, 0x2c(R5)
+ /* 2: clear DRI field in MDREFR */
+ MOVW R4, 0x1c(R5)
+ /* 3: set SLFRSH bit in MDREFR */
+ MOVW R6, 0x1c(R5)
+ /* 4: clear DE bits in MDCFNG */
+ MOVW R9, 0x0(R5)
+ /* 5: clear SLFRSH and E1PIN in MDREFR */
+ MOVW R10, 0x1c(R5)
+ /* 6: suspend request */
+ MOVW R2, 0x0(R3) /* pmcr */
+ B 0(PC) /* wait for it */
+
+/*
+ * The boot loader comes here after the resume.
+ */
+TEXT power_resume(SB), $-4
+ MOVW $(PsrDirq|PsrDfiq|PsrMsvc), R0
+ MOVW R0, CPSR /* svc mode, interrupts off */
+ MOVW $setR12(SB), R12
+
+ /* flush caches */
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(7), 0
+ /* drain prefetch */
+ MOVW R0,R0
+ MOVW R0,R0
+ MOVW R0,R0
+ MOVW R0,R0
+ /* drain write buffer */
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 4
+ /* flush tlb */
+ MCR CpMMU, 0, R0, C(CpTLBops), C(7)
+
+ /* restore state */
+ MOVW power_state+0(SB), R13
+ MOVM.IA.W (R13), [R1-R4]
+ MOVW.P 4(R13), R14
+
+ MCR CpMMU, 0, R1, C(CpDAC), C(0x0)
+ MCR CpMMU, 0, R2, C(CpTTB), C(0x0)
+ MCR CpMMU, 0, R3, C(CpPID), C(0x0)
+ MCR CpMMU, 0, R4, C(CpControl), C(0x0) /* enable cache and mmu */
+ MOVW R0,R0
+ MOVW R0,R0
+ MOVW R0,R0
+ MOVW R0,R0
+ /* flush i&d caches */
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(7)
+ /* flush tlb */
+ MCR CpMMU, 0, R0, C(CpTLBops), C(7)
+ /* drain prefetch */
+ MOVW R0,R0
+ MOVW R0,R0
+ MOVW R0,R0
+ MOVW R0,R0
+ /* enable clock switching */
+ MCR CpPWR, 0, R1, C(CpTest), C(1), 2
+ RET
+
+ GLOBL power_state+0(SB), $4
+
+/* for debugging sleep code: */
+TEXT fastreset(SB), $-4
+ MOVW $PHYSRESET, R7
+ MOVW $1, R1
+ MOVW R1, (R7)
+ RET
diff --git a/os/sa1110/l3gpio.c b/os/sa1110/l3gpio.c
new file mode 100644
index 00000000..8f162fe6
--- /dev/null
+++ b/os/sa1110/l3gpio.c
@@ -0,0 +1,246 @@
+/*
+ * L3 emulation using GPIO pins
+ *
+ * from the Linux sa1100-uda1341.c,
+ * Copyright (c) 2000 Nicolas Pitre <nico@cam.org>
+ * Portions are Copyright (C) 2000 Lernout & Hauspie Speech Products, N.V.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License.
+ *
+ * Modified by Vita Nuova 2001
+ *
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+/*
+ * GPIO based L3 bus support.
+ *
+ * This provides control of Philips L3 type devices.
+ * GPIO lines are used for clock, data and mode pins.
+ *
+ * Note: The L3 pins are shared with I2C devices. This should not present
+ * any problems as long as an I2C start sequence is not generated. This is
+ * defined as a 1->0 transition on the data lines when the clock is high.
+ * It is critical this code only allow data transitions when the clock
+ * is low. This is always legal in L3.
+ *
+ * The IIC interface requires the clock and data pin to be LOW when idle. We
+ * must make sure we leave them in this state.
+ *
+ * It appears the read data is generated on the falling edge of the clock
+ * and should be held stable during the clock high time.
+ */
+
+/*
+ * L3 setup and hold times (expressed in us)
+ */
+enum {
+ L3DataSetupTime = 1, /* 190 ns */
+ L3DataHoldTime = 1, /* 30 ns */
+ L3ModeSetupTime = 1, /* 190 ns */
+ L3ModeHoldTime = 1, /* 190 ns */
+ L3ClockHighTime = 1, /* 250 ns (min is 64*fs, 35us @ 44.1 Khz) */
+ L3ClockLowTime = 1, /* 250 ns (min is 64*fs, 35us @ 44.1 Khz) */
+ L3HaltTime = 1, /* 190 ns */
+};
+
+/*
+ * Grab control of the IIC/L3 shared pins
+ */
+static void
+L3acquirepins(void)
+{
+ GpioReg *g = GPIOREG;
+ int s;
+
+ s = splhi();
+ g->gpsr = (L3Mode | L3Clock | L3Data);
+ g->gpdr |= (L3Mode | L3Clock | L3Data);
+ splx(s);
+// microdelay(2);
+}
+
+/*
+ * Release control of the IIC/L3 shared pins
+ */
+static void
+L3releasepins(void)
+{
+ GpioReg *g = GPIOREG;
+ int s;
+
+ s = splhi();
+ g->gpdr &= ~(L3Mode | L3Clock | L3Data);
+ splx(s);
+}
+
+/*
+ * Initialize the interface
+ */
+void
+L3init(void)
+{
+ GpioReg *g = GPIOREG;
+ int s;
+
+ s = splhi();
+ g->gafr &= ~(L3Data | L3Clock | L3Mode);
+ splx(s);
+ L3releasepins();
+}
+
+/*
+ * Send a byte. The mode line is set or pulsed based on the mode sequence
+ * count. The mode line is high on entry and exit. The mod line is pulsed
+ * before the second data byte and before ech byte thereafter.
+ */
+static void
+L3sendbyte(int data, int mode)
+{
+ int i;
+ GpioReg *g = GPIOREG;
+
+ switch(mode) {
+ case 0: /* Address mode */
+ g->gpcr = L3Mode;
+ break;
+ case 1: /* First data byte */
+ break;
+ default: /* Subsequent bytes */
+ g->gpcr = L3Mode;
+ microdelay(L3HaltTime);
+ g->gpsr = L3Mode;
+ break;
+ }
+
+ microdelay(L3ModeSetupTime);
+
+ for (i = 0; i < 8; i++){
+ microdelay(2);
+ /*
+ * Send a bit. The clock is high on entry and on exit. Data is sent only
+ * when the clock is low (I2C compatibility).
+ */
+ g->gpcr = L3Clock;
+
+ if (data & (1<<i))
+ g->gpsr = L3Data;
+ else
+ g->gpcr = L3Data;
+
+ /* Assumes L3DataSetupTime < L3ClockLowTime */
+ microdelay(L3ClockLowTime);
+
+ g->gpsr = L3Clock;
+ microdelay(L3ClockHighTime);
+ }
+
+ if (mode == 0) /* Address mode */
+ g->gpsr = L3Mode;
+
+ microdelay(L3ModeHoldTime);
+
+}
+
+/*
+ * Get a byte. The mode line is set or pulsed based on the mode sequence
+ * count. The mode line is high on entry and exit. The mod line is pulsed
+ * before the second data byte and before each byte thereafter. This
+ * function is never valid with mode == 0 (address cycle) as the address
+ * is always sent on the bus, not read.
+ */
+static int
+L3getbyte(int mode)
+{
+ int data = 0;
+ int i;
+ GpioReg *g = GPIOREG;
+
+ switch(mode) {
+ case 0: /* Address mode - never valid */
+ break;
+ case 1: /* First data byte */
+ break;
+ default: /* Subsequent bytes */
+ g->gpcr = L3Mode;
+ microdelay(L3HaltTime);
+ g->gpsr = L3Mode;
+ break;
+ }
+
+ microdelay(L3ModeSetupTime);
+
+ for (i = 0; i < 8; i++){
+ /*
+ * Get a bit. The clock is high on entry and on exit. Data is read after
+ * the clock low time has expired.
+ */
+ g->gpcr = L3Clock;
+ microdelay(L3ClockLowTime);
+
+ if(g->gplr & L3Data)
+ data |= 1<<i;
+
+ g->gpsr = L3Clock;
+ microdelay(L3ClockHighTime);
+ }
+
+ microdelay(L3ModeHoldTime);
+
+ return data;
+}
+
+/*
+ * Write data to a device on the L3 bus. The address is passed as well as
+ * the data and length. The length written is returned. The register space
+ * is encoded in the address (low two bits are set and device address is
+ * in the upper 6 bits).
+ */
+int
+L3write(int addr, void *data, int len)
+{
+ int mode = 0;
+ int bytes = len;
+ uchar *b;
+
+ L3acquirepins();
+ L3sendbyte(addr, mode++);
+ for(b = data; --len >= 0;)
+ L3sendbyte(*b++, mode++);
+ L3releasepins();
+
+ return bytes;
+}
+
+/*
+ * Read data from a device on the L3 bus. The address is passed as well as
+ * the data and length. The length read is returned. The register space
+ * is encoded in the address (low two bits are set and device address is
+ * in the upper 6 bits).
+ */
+int
+L3read(int addr, void *data, int len)
+{
+ int mode = 0;
+ int bytes = len;
+ uchar *b;
+ int s;
+
+ L3acquirepins();
+ L3sendbyte(addr, mode++);
+ s = splhi();
+ GPIOREG->gpdr &= ~(L3Data);
+ splx(s);
+ for(b = data; --len >= 0;)
+ *b++ = L3getbyte(mode++);
+ L3releasepins();
+
+ return bytes;
+}
diff --git a/os/sa1110/mmu.c b/os/sa1110/mmu.c
new file mode 100644
index 00000000..1dab239a
--- /dev/null
+++ b/os/sa1110/mmu.c
@@ -0,0 +1,140 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+/*
+ * return physical address corresponding to a given virtual address,
+ * or 0 if there is no such address
+ */
+ulong
+va2pa(void *v)
+{
+ int idx;
+ ulong pte, ste, *ttb;
+
+ idx = MmuL1x((ulong)v);
+ ttb = (ulong*)KTTB;
+ ste = ttb[idx];
+ switch(ste & MmuL1type) {
+ case MmuL1section:
+ return MmuSBA(ste)|((ulong)v & 0x000fffff);
+ case MmuL1page:
+ pte = ((ulong *)MmuPTBA(ste))[MmuL2x((ulong)v)];
+ switch(pte & 3) {
+ case MmuL2large:
+ return (pte & 0xffff0000)|((ulong)v & 0x0000ffff);
+ case MmuL2small:
+ return (pte & 0xfffff000)|((ulong)v & 0x00000fff);
+ }
+ }
+ return 0;
+}
+
+enum {
+ SectionPages = MmuSection/MmuSmallPage,
+ PtAlign = 1<<10,
+
+ MINICACHED = 0x10000000,
+};
+
+/* for debugging */
+void
+prs(char *s)
+{
+ for(; *s; s++)
+ uartputc(*s);
+}
+
+void
+pr16(ulong n)
+{
+ int i;
+
+ for(i=28; i>=0; i-=4)
+ uartputc("0123456789ABCDEF"[(n>>i)&0xF]);
+}
+
+void*
+mmuphysmap(ulong phys, ulong)
+{
+ ulong *ttb;
+ void *va;
+
+ ttb = (ulong*)KTTB;
+ va = KADDR(phys);
+ ttb[MmuL1x((ulong)va)] = phys | 0xC10 | MmuL1section;
+ return va;
+}
+
+/*
+ * Set a 1-1 map of virtual to physical memory, except:
+ * doubly-map page0 at the alternative interrupt vector address,
+ * doubly-map physical memory at KZERO+256*MB as uncached but buffered, and
+ * disable access to 0 (nil pointers).
+ */
+void
+mmuinit(void)
+{
+ int i;
+ ulong *ttb, *ptable, va;
+
+ ttb = (ulong*)KTTB;
+ for(i=0; i<MmuL1x(0x10000000); i++)
+ ttb[i] = 0;
+ for(; i < 0x1000; i++)
+ ttb[i] = (i<<20) | 0xC10 | MmuL1section;
+ for(va = KZERO; va < KZERO+64*MB; va += MB)
+ ttb[MmuL1x(va)] |= MmuWB | MmuIDC; /* DRAM is cacheable */
+ for(i = 0; i < 64*MB; i += MB)
+ ttb[MmuL1x(UCDRAMZERO+i)] = (PHYSMEM0+i) | 0xC10 | MmuL1section;
+ /* TO DO: make the text read only */
+ for(va = KZERO; va < KZERO+64*MB; va += MB)
+ ttb[MmuL1x(va|MINICACHED)] = va | 0xC10 | MmuIDC | MmuL1section; /* cached but unbuffered (thus minicache) for frame buffer */
+ ttb[MmuL1x(DCFADDR)] |= MmuIDC | MmuWB; /* cached and buffered for cache writeback */
+ ttb[MmuL1x(MCFADDR)] |= MmuIDC; /* cached and unbuffered for minicache writeback */
+ /* remap flash */
+ for(i=0; i<32*MB; i+=MB)
+ ttb[MmuL1x(FLASHMEM+i)] = (PHYSFLASH0+i) | 0xC10 | MmuL1section; /* we'll make flash uncached for now */
+
+ /*
+ * build page table for alternative vector page, mapping trap vectors in *page0
+ */
+ ptable = xspanalloc(SectionPages*sizeof(*ptable), PtAlign, 0);
+ ptable[MmuL2x(AIVECADDR)] = PADDR(page0) | MmuL2AP(MmuAPsrw) | MmuWB | MmuIDC | MmuL2small;
+ ttb[MmuL1x(AIVECADDR)] = PADDR(ptable) | MmuL1page;
+ mmuputttb(KTTB);
+ mmuputdac(1); /* client */
+ mmuenable(CpCaltivec | CpCIcache | CpCsystem | (1<<6) | CpCd32 | CpCi32 | CpCwb | CpCDcache | CpCmmu);
+}
+
+/*
+ * flush data in a given address range to memory
+ * and invalidate the region in the instruction cache.
+ */
+int
+segflush(void *a, ulong n)
+{
+ dcflush(a, n);
+ icflushall(); /* can't be more precise */
+ return 0;
+}
+
+/*
+ * map an address to cached but unbuffered memory
+ * forcing load allocations to the mini data cache.
+ * the address a must be in a region that is cache line aligned
+ * with a length that is a multiple of the cache line size
+ */
+void *
+minicached(void *a)
+{
+ if(conf.useminicache == 0)
+ return a;
+ /* must flush and invalidate any data lingering in main cache */
+ dcflushall();
+ minidcflush();
+ dcinval();
+ return (void*)((ulong)a | MINICACHED);
+}
diff --git a/os/sa1110/sa1110break.c b/os/sa1110/sa1110break.c
new file mode 100644
index 00000000..3ab87b06
--- /dev/null
+++ b/os/sa1110/sa1110break.c
@@ -0,0 +1,360 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+//
+// from trap.c
+//
+extern int (*breakhandler)(Ureg *ur, Proc*);
+extern Instr BREAK;
+extern void portbreakinit(void);
+
+//
+// Instructions that can have the PC as a destination register
+//
+enum {
+ IADD = 1,
+ IBRANCH,
+ ILDM,
+ ILDR,
+ IMOV,
+
+ //
+ // These should eventually be implemented
+ //
+ IADC,
+ IAND,
+ IBIC,
+ IEOR,
+ ILDRT,
+ IMRS,
+ IMVN,
+ IORR,
+ IRSB,
+ IRSC,
+ ISBC,
+ ISUB,
+};
+
+static int instrtype(Instr i);
+static ulong iadd(Ureg *ur, Instr i);
+static ulong ibranch(Ureg *ur, Instr i);
+static ulong ildm(Ureg *ur, Instr i);
+static ulong ildr(Ureg *ur, Instr i);
+static ulong imov(Ureg *ur, Instr i);
+static ulong shifterval(Ureg *ur, Instr i);
+static int condpass(Instr i, ulong psr);
+static ulong *address(Ureg *ur, Instr i);
+static ulong* multiaddr(Ureg *ur, Instr i);
+static int nbits(ulong v);
+
+#define COND_N(psr) (((psr) >> 31) & 1)
+#define COND_Z(psr) (((psr) >> 30) & 1)
+#define COND_C(psr) (((psr) >> 29) & 1)
+#define COND_V(psr) (((psr) >> 28) & 1)
+#define REG(i, a, b) (((i) & BITS((a), (b))) >> (a))
+#define REGVAL(ur, r) (*((ulong*)(ur) + (r)))
+#define LSR(v, s) ((ulong)(v) >> (s))
+#define ASR(v, s) ((long)(v) >> (s))
+#define ROR(v, s) (LSR((v), (s)) | (((v) & ((1 << (s))-1)) << (32 - (s))))
+
+void
+machbreakinit(void)
+{
+ portbreakinit();
+ breakhandler = breakhit;
+}
+
+Instr
+machinstr(ulong addr)
+{
+ if (addr < KTZERO)
+ error(Ebadarg);
+ return *(Instr*)addr;
+}
+
+void
+machbreakset(ulong addr)
+{
+ if (addr < KTZERO)
+ error(Ebadarg);
+ *(Instr*)addr = BREAK;
+ segflush((void*)addr, sizeof(Instr));
+}
+
+void
+machbreakclear(ulong addr, Instr i)
+{
+ if (addr < KTZERO)
+ error(Ebadarg);
+ *(Instr*)addr = i;
+ segflush((void*)addr, sizeof(Instr));
+}
+
+//
+// Return the address of the instruction that will be executed after the
+// instruction at address ur->pc.
+//
+// This means decoding the instruction at ur->pc.
+//
+// In the simple case, the PC will simply be the address of the next
+// sequential instruction following ur->pc.
+//
+// In the complex case, the instruction is a branch of some sort, so the
+// value of the PC after the instruction must be computed by decoding
+// and simulating the instruction enough to determine the PC.
+//
+
+ulong
+machnextaddr(Ureg *ur)
+{
+ Instr i;
+ i = machinstr(ur->pc);
+ switch(instrtype(i)) {
+ case IADD: return iadd(ur,i);
+ case IBRANCH: return ibranch(ur,i);
+ case ILDM: return ildm(ur,i);
+ case ILDR: return ildr(ur,i);
+ case IMOV: return imov(ur,i);
+
+ case IADC:
+ case IAND:
+ case IBIC:
+ case IEOR:
+ case ILDRT:
+ case IMRS:
+ case IMVN:
+ case IORR:
+ case IRSB:
+ case IRSC:
+ case ISBC:
+ case ISUB:
+ // XXX - Tad: unimplemented
+ //
+ // any of these instructions could possibly have the
+ // PC as Rd. Eventually, these should all be
+ // checked just like the others.
+ default:
+ return ur->pc+4;
+ }
+
+ return 0;
+}
+
+static int
+instrtype(Instr i)
+{
+ if(i & BITS(26,27) == 0) {
+ switch((i >> 21) & 0xF) {
+ case 0: return IAND;
+ case 1: return IEOR;
+ case 2: return ISUB;
+ case 3: return IRSB;
+ case 4: return IADD;
+ case 5: return IADC;
+ case 6: return ISBC;
+ case 7: return IRSC;
+ case 0xD: return IMOV;
+ case 0xC: return IORR;
+ case 0xE: return IBIC;
+ case 0xF: return IMVN;
+ }
+ if(((i & BIT(25)|BITS(23,24)|BITS(20,21))) >> 20 == 0x10)
+ return IMRS;
+ return 0;
+ }
+
+ if(((i & BITS(27,25)|BIT(20)) >> 20) == 0x81) return ILDM;
+ if(((i & BITS(26,27)|BIT(22)|BIT(20)) >> 20) == 0x41) return ILDR;
+ if(((i & BITS(25,27)) >> 25) == 5) return IBRANCH;
+
+ return 0;
+}
+
+static ulong
+iadd(Ureg *ur, Instr i)
+{
+ ulong Rd = REG(i, 12, 15);
+ ulong Rn = REG(i, 16, 19);
+
+ if(Rd != 15 || !condpass(i, ur->psr))
+ return ur->pc+4;
+
+ return REGVAL(ur, Rn) + shifterval(ur, i);
+}
+
+static ulong
+ibranch(Ureg *ur, Instr i)
+{
+ if(!condpass(i, ur->psr))
+ return ur->pc+4;
+ return ur->pc + ((signed long)(i << 8) >> 6) + 8;
+}
+
+static ulong
+ildm(Ureg *ur, Instr i)
+{
+ if((i & BIT(15)) == 0)
+ return ur->pc+4;
+
+ return *(multiaddr(ur, i) + nbits(i & BITS(15, 0)));
+}
+
+static ulong
+ildr(Ureg *ur, Instr i)
+{
+ if(REG(i, 12, 19) != 15 || !condpass(i, ur->psr))
+ return ur->pc+4;
+
+ return *address(ur, i);
+}
+
+static ulong
+imov(Ureg *ur, Instr i)
+{
+ if(REG(i, 12, 15) != 15 || !condpass(i, ur->psr))
+ return ur->pc+4;
+
+ return shifterval(ur, i);
+}
+
+static int
+condpass(Instr i, ulong psr)
+{
+ uchar n = COND_N(psr);
+ uchar z = COND_Z(psr);
+ uchar c = COND_C(psr);
+ uchar v = COND_V(psr);
+
+ switch(LSR(i,28)) {
+ case 0: return z;
+ case 1: return !z;
+ case 2: return c;
+ case 3: return !c;
+ case 4: return n;
+ case 5: return !n;
+ case 6: return v;
+ case 7: return !v;
+ case 8: return c && !z;
+ case 9: return !c || z;
+ case 10: return n == v;
+ case 11: return n != v;
+ case 12: return !z && (n == v);
+ case 13: return z && (n != v);
+ case 14: return 1;
+ case 15: return 0;
+ }
+}
+
+static ulong
+shifterval(Ureg *ur, Instr i)
+{
+ if(i & BIT(25)) { // IMMEDIATE
+ ulong imm = i & BITS(0,7);
+ ulong s = (i & BITS(8,11)) >> 7; // this contains the * 2
+ return ROR(imm, s);
+ } else {
+ ulong Rm = REGVAL(ur, REG(i, 0, 3));
+ ulong s = (i & BITS(7,11)) >> 7;
+
+ switch((i & BITS(6,4)) >> 4) {
+ case 0: // LSL
+ return Rm << s;
+ case 1: // LSLREG
+ s = REGVAL(ur, s >> 1) & 0xFF;
+ if(s >= 32) return 0;
+ return Rm << s;
+ case 2: // LSRIMM
+ return LSR(Rm, s);
+ case 3: // LSRREG
+ s = REGVAL(ur, s >> 1) & 0xFF;
+ if(s >= 32) return 0;
+ return LSR(Rm, s);
+ case 4: // ASRIMM
+ if(s == 0) {
+ if(Rm & BIT(31) == 0)
+ return 0;
+ return 0xFFFFFFFF;
+ }
+ return ASR(Rm, s);
+ case 5: // ASRREG
+ s = REGVAL(ur, s >> 1) & 0xFF;
+ if(s >= 32) {
+ if(Rm & BIT(31) == 0)
+ return 0;
+ return 0xFFFFFFFF;
+ }
+ return ASR(Rm, s);
+ case 6: // RORIMM
+ if(s == 0)
+ return (COND_C(ur->psr) << 31) | LSR(Rm, 1);
+ return ROR(Rm, s);
+ case 7: // RORREG
+ s = REGVAL(ur, s >> 1) & 0xFF;
+ if(s == 0 || (s & 0xF) == 0)
+ return Rm;
+ return ROR(Rm, s & 0xF);
+ }
+ }
+}
+
+static ulong*
+address(Ureg *ur, Instr i)
+{
+ ulong Rn = REGVAL(ur, REG(i, 16, 19));
+
+ if(i & BIT(24) == 0) // POSTIDX
+ return (ulong*)REGVAL(ur, Rn);
+ if(i & BIT(25) == 0) { // OFFSET
+ if(i & BIT(23))
+ return (ulong*)(REGVAL(ur, Rn) + (i & BITS(0, 11)));
+ return (ulong*)(REGVAL(ur, Rn) - (i & BITS(0, 11)));
+ } else { // REGOFF
+ ulong Rm = REGVAL(ur, REG(i, 0, 3));
+ ulong index = 0;
+ switch(i & BITS(5,6) >> 5) {
+ case 0: index = Rm << ((i & BITS(7, 11)) >> 7); break;
+ case 1: index = LSR(Rm, ((i & BITS(7, 11)) >> 7)); break;
+ case 2: index = ASR(Rm, ((i & BITS(7, 11)) >> 7)); break;
+ case 3:
+ if(i & BITS(7, 11) == 0)
+ index = (COND_C(ur->psr) << 31) | LSR(Rm, 1);
+ else
+ index = ROR(Rm, (i & BITS(7, 11)) >> 7);
+ break;
+ }
+ if(i & BIT(23))
+ return (ulong*)(Rn + index);
+ return (ulong*)(Rn - index);
+ }
+}
+
+static ulong*
+multiaddr(Ureg *ur, Instr i)
+{
+ ulong Rn = REGVAL(ur, REG(i, 16, 19));
+
+ switch((i >> 23) & 3) {
+ case 0: return (ulong*)(Rn - (nbits(i & BITS(0,15))*4)+4);
+ case 1: return (ulong*)Rn;
+ case 2: return (ulong*)(Rn - (nbits(i & BITS(0,15))*4));
+ case 3: return (ulong*)(Rn + 4);
+ }
+}
+
+static int
+nbits(ulong v)
+{
+ int n = 0;
+ int i;
+ for(i = 0; i < 32; i++) {
+ if(v & 1)
+ ++n;
+ v = LSR(v, 1);
+ }
+ return n;
+}
diff --git a/os/sa1110/sa1110io.h b/os/sa1110/sa1110io.h
new file mode 100644
index 00000000..53913c3d
--- /dev/null
+++ b/os/sa1110/sa1110io.h
@@ -0,0 +1,500 @@
+typedef struct PCMconftab PCMconftab;
+typedef struct PCMmap PCMmap;
+typedef struct PCMslot PCMslot;
+
+/*
+ * physical device addresses are mapped to the same virtual ones,
+ * allowing the same addresses to be used with or without mmu.
+ */
+
+#define PCMCIAcard(n) (PHYSPCMCIA0+((n)*PCMCIASIZE))
+#define PCMCIAIO(n) (PCMCIAcard(n)+0x0) /* I/O space */
+#define PCMCIAAttr(n) (PCMCIAcard(n)+0x8000000) /* Attribute space*/
+#define PCMCIAMem(n) (PCMCIAcard(n)+0xC000000) /* Memory space */
+
+#define INTRREG ((IntrReg*)PHYSINTR)
+typedef struct IntrReg IntrReg;
+struct IntrReg {
+ ulong icip; /* IRQ pending */
+ ulong icmr; /* mask */
+ ulong iclr; /* level */
+ ulong iccr; /* control */
+ ulong icfp; /* FIQ pending */
+ ulong rsvd[3];
+ ulong icpr; /* pending */
+};
+
+#define GPIObit(n) (n) /* GPIO Edge Detect bits */
+#define LCDbit (12) /* LCD Service Request */
+#define UDCbit (13) /* UDC Service Request */
+#define SDLCbit (14) /* SDLC Service Request */
+#define UARTbit(n) (15+((n)-1)) /* UART Service Request */
+#define HSSPbit (16) /* HSSP Service Request */
+#define MCPbit (18) /* MCP Service Request */
+#define SSPbit (19) /* SSP Serivce Request */
+#define DMAbit(chan) (20+(chan)) /* DMA channel Request */
+#define OSTimerbit(n) (26+(n)) /* OS Timer Request */
+#define RTCticbit (30) /* One Hz tic occured */
+#define RTCalarmbit (31) /* RTC = alarm register */
+#define MaxIRQbit 31 /* Maximum IRQ */
+#define MaxGPIObit 27 /* Maximum GPIO */
+
+#define GPIOREG ((GpioReg*)PHYSGPIO)
+typedef struct GpioReg GpioReg;
+struct GpioReg {
+ ulong gplr;
+ ulong gpdr;
+ ulong gpsr;
+ ulong gpcr;
+ ulong grer;
+ ulong gfer;
+ ulong gedr;
+ ulong gafr;
+};
+
+enum {
+ /* GPIO alternative functions if gafr bit set (see table on page 9-9) */
+ GPIO_32KHZ_OUT_o = 1<<27, /* raw 32.768kHz oscillator output */
+ GPIO_RCLK_OUT_o = 1<<26, /* internal clock/2 (must also set TUCR) */
+ GPIO_RTC_clock_o = 1<<25, /* real time clock out */
+ GPIO_TREQB_i = 1<<23, /* TIC request B */
+ GPIO_TREQA_i = 1<<22, /* TIC request A (or MBREQ) */
+ GPIO_TICK_ACK_o = 1<<21, /* TIC ack (or MBGNT), when output */
+ GPIO_MCP_CLK_i = 1<<21, /* MCP clock in, when input */
+ GPIO_UART_SCLK3_i = 1<<20, /* serial port 3 UART sample clock input */
+ GPIO_SSP_CLK_i = 1<<19, /* serial port 2 SSP sample clock input */
+ GPIO_UART_SCLK1_i = 1<<18, /* serial port 1 UART sample clock input */
+ GPIO_GPCLK_OUT_o = 1<<16, /* serial port 1 general-purpose clock out */
+ GPIO_UART_RXD_i = 1<<15, /* serial port 1 UART receive */
+ GPIO_UART_TXD_o = 1<<14, /* serial port 1 UART transmit */
+ GPIO_SSP_SFRM_o = 1<<13, /* SSP frame clock out */
+ GPIO_SSP_SCLK_o = 1<<12, /* SSP serial clock out */
+ GPIO_SSP_RXD_i = 1<<11, /* SSP receive */
+ GPIO_SSP_TXD_o = 1<<10, /* SSP transmit */
+ GPIO_LDD8_15_o = 0xFF<<2, /* high-order LCD data (bits 8-15) */
+ GPIO_LDD15_o = 1<<9,
+ GPIO_LDD14_o = 1<<8,
+ GPIO_LDD13_o = 1<<7,
+ GPIO_LDD12_o = 1<<6,
+ GPIO_LDD11_o = 1<<5,
+ GPIO_LDD10_o = 1<<4,
+ GPIO_LDD9_o = 1<<3,
+ GPIO_LDD8_o = 1<<2,
+};
+
+#define RTCREG ((RtcReg*)PHYSRTC)
+typedef struct RtcReg RtcReg;
+struct RtcReg {
+ ulong rtar; /* alarm */
+ ulong rcnr; /* count */
+ ulong rttr; /* trim */
+ ulong rsvd;
+ ulong rtsr; /* status */
+};
+
+#define OSTMRREG ((OstmrReg*)PHYSOSTMR)
+typedef struct OstmrReg OstmrReg;
+struct OstmrReg {
+ ulong osmr[4]; /* match */
+ ulong oscr; /* counter */
+ ulong ossr; /* status */
+ ulong ower; /* watchdog */
+ ulong oier; /* interrupt enable */
+};
+
+#define PMGRREG ((PmgrReg*)PHYSPOWER)
+typedef struct PmgrReg PmgrReg;
+struct PmgrReg {
+ ulong pmcr; /* ctl register */
+ ulong pssr; /* sleep status */
+ ulong pspr; /* scratch pad */
+ ulong pwer; /* wakeup enable */
+ ulong pcfr; /* general conf */
+ ulong ppcr; /* PLL configuration */
+ ulong pgsr; /* GPIO sleep state */
+ ulong posr; /* oscillator status */
+};
+
+enum
+{
+ /* page 9-35 to 40 */
+ PCFR_opde = 1<<0, /* oscillator powers down in sleep */
+ PCFR_fp = 1<<1, /* float pcmcia */
+ PCFR_fs = 1<<2, /* float static memory */
+ PCFR_fo = 1<<3, /* force 32k oscillator on */
+
+ PWER_rtc = 1<<31, /* wakeup by RTC alarm */
+
+ PSSR_sss = 1<<0, /* software sleep status */
+ PSSR_bfs = 1<<1, /* battery fault status */
+ PSSR_vfs = 1<<2, /* VDD fault status */
+ PSSR_dh = 1<<3, /* DRAM control held */
+ PSSR_ph = 1<<4, /* peripheral control hold */
+};
+
+#define RESETREG ((ResetReg*)PHYSRESET)
+typedef struct ResetReg ResetReg;
+struct ResetReg {
+ ulong rsrr; /* software reset */
+ ulong rcsr; /* status */
+ ulong tucr; /* reserved for test */
+};
+
+#define MEMCFGREG ((MemcfgReg*)PHYSMEMCFG)
+typedef struct MemcfgReg MemcfgReg;
+struct MemcfgReg {
+ ulong mdcnfg; /* DRAM config */
+ ulong mdcas0[3]; /* dram banks 0/1 */
+ ulong msc0; /* static memory or devices */
+ ulong msc1;
+ ulong mecr; /* expansion bus (pcmcia, CF) */
+ ulong mdrefr; /* dram refresh */
+ ulong mdcas2[3]; /* dram banks 2/3 */
+ ulong msc2; /* static memory or devices */
+ ulong smcnfg; /* SMROM config */
+};
+
+#define DMAREG(n) ((DmaReg*)(PHYSDMA+0x20*(n)))
+typedef struct DmaReg DmaReg;
+struct DmaReg {
+ ulong ddar; /* DMA device address */
+ ulong dcsr_s; /* set */
+ ulong dcsr_c; /* clear */
+ ulong dcsr; /* read */
+ struct {
+ ulong start;
+ ulong count;
+ } buf[2];
+};
+
+#define LCDREG ((LcdReg*)PHYSLCD)
+typedef struct LcdReg LcdReg;
+struct LcdReg {
+ ulong lccr0; /* control 0 */
+ ulong lcsr; /* status */
+ ulong rsvd[2];
+ ulong dbar1; /* DMA chan 1, base */
+ ulong dcar1; /* DMA chan 1, count */
+ ulong dbar2; /* DMA chan 2, base */
+ ulong dcar2; /* DMA chan 2, count */
+ ulong lccr1; /* control 1 */
+ ulong lccr2; /* control 2 */
+ ulong lccr3; /* control 3 */
+};
+
+/* Serial devices:
+ * 0 USB Serial Port 0
+ * 1 UART Serial Port 1
+ * 2 SDLC "
+ * 3 UART Serial Port 2 (eia1)
+ * 4 ICP/HSSP "
+ * 5 ICP/UART Serial Port 3 (eia0)
+ * 6 MPC Serial Port 4
+ * 7 SSP "
+ */
+
+#define USBREG ((UsbReg*)PHYSUSB)
+typedef struct UsbReg UsbReg;
+struct UsbReg {
+ ulong udccr; /* control */
+ ulong udcar; /* address */
+ ulong udcomp; /* out max packet */
+ ulong udcimp; /* in max packet */
+ ulong udccs0; /* endpoint 0 control/status */
+ ulong udccs1; /* endpoint 1(out) control/status */
+ ulong udccs2; /* endpoint 2(int) control/status */
+ ulong udcd0; /* endpoint 0 data register */
+ ulong udcwc; /* endpoint 0 write control register */
+ ulong rsvd1;
+ ulong udcdr; /* transmit/receive data register (FIFOs) */
+ ulong rsvd2;
+ ulong dcsr; /* status/interrupt register */
+};
+
+#define GPCLKREG ((GpclkReg*)PHYSGPCLK)
+typedef struct GpclkReg GpclkReg;
+struct GpclkReg {
+ ulong gpclkr0;
+ ulong rsvd[2];
+ ulong gpclkr1;
+ ulong gpclkr2;
+};
+
+/* UARTs 1, 2, 3 are mapped to serial devices 1, 3, and 5 */
+#define UARTREG(n) ((UartReg*)(PHYSSERIAL(2*(n)-1)))
+typedef struct UartReg UartReg;
+struct UartReg {
+ ulong utcr0; /* control 0 (bits, parity, clocks) */
+ ulong utcr1; /* control 1 (bps div hi) */
+ ulong utcr2; /* control 2 (bps div lo) */
+ ulong utcr3; /* control 3 */
+ ulong utcr4; /* control 4 (only serial port 2 (device 3)) */
+ ulong utdr; /* data */
+ ulong rsvd;
+ ulong utsr0; /* status 0 */
+ ulong utsr1; /* status 1 */
+};
+
+#define HSSPREG ((HsspReg*)(0x80040060))
+typedef struct HsspReg HsspReg;
+struct HsspReg {
+ ulong hscr0; /* control 0 */
+ ulong hscr1; /* control 1 */
+ ulong rsvd1;
+ ulong hsdr; /* data */
+ ulong rsvd2;
+ ulong hssr0; /* status 0 */
+ ulong hssr1; /* status 1 */
+};
+
+#define MCPREG ((McpReg*)(PHYSMCP))
+typedef struct McpReg McpReg;
+struct McpReg {
+ ulong mccr;
+ ulong rsvd1;
+ ulong mcdr0;
+ ulong mcdr1;
+ ulong mcdr2;
+ ulong rsvd2;
+ ulong mcsr;
+};
+
+enum {
+ MCCR_M_LBM= 0x800000,
+ MCCR_M_ARM= 0x400000,
+ MCCR_M_ATM= 0x200000,
+ MCCR_M_TRM= 0x100000,
+ MCCR_M_TTM= 0x080000,
+ MCCR_M_ADM= 0x040000,
+ MCCR_M_ECS= 0x020000,
+ MCCR_M_MCE= 0x010000,
+ MCCR_V_TSD= 8,
+ MCCR_V_ASD= 0,
+
+ MCDR2_M_nRW= 0x010000,
+ MCDR2_V_RN= 17,
+
+ MCSR_M_TCE= 0x8000,
+ MCSR_M_ACE= 0X4000,
+ MCSR_M_CRC= 0x2000,
+ MCSR_M_CWC= 0x1000,
+ MCSR_M_TNE= 0x0800,
+ MCSR_M_TNF= 0x0400,
+ MCSR_M_ANE= 0x0200,
+ MCSR_M_ANF= 0x0100,
+ MCSR_M_TRO= 0x0080,
+ MCSR_M_TTU= 0x0040,
+ MCSR_M_ARO= 0x0020,
+ MCSR_M_ATU= 0x0010,
+ MCSR_M_TRS= 0x0008,
+ MCSR_M_TTS= 0x0004,
+ MCSR_M_ARS= 0x0002,
+ MCSR_M_ATS= 0x0001,
+};
+
+#define SSPREG ((SspReg*)PHYSSSP)
+typedef struct SspReg SspReg;
+struct SspReg {
+ ulong sscr0; /* control 0 */
+ ulong sscr1; /* control 1 */
+ ulong rsvd1;
+ ulong ssdr; /* data */
+ ulong rsvd2;
+ ulong sssr; /* status */
+};
+
+enum {
+ SSCR0_V_SCR= 0x08,
+ SSCR0_V_SSE= 0x07,
+ SSCR0_V_ECS= 0x06,
+ SSCR0_V_FRF= 0x04,
+
+ SSPCR0_M_DSS= 0x0000000F,
+ SSPCR0_M_FRF= 0x00000030,
+ SSPCR0_M_SSE= 0x00000080,
+ SSPCR0_M_SCR= 0x0000FF00,
+ SSPCR0_V_DSS= 0,
+ SSPCR0_V_FRF= 4,
+ SSPCR0_V_SSE= 7,
+ SSPCR0_V_SCR= 8,
+
+ SSPCR1_M_RIM= 0x00000001,
+ SSPCR1_M_TIN= 0x00000002,
+ SSPCR1_M_LBM= 0x00000004,
+ SSPCR1_V_RIM= 0,
+ SSPCR1_V_TIN= 1,
+ SSPCR1_V_LBM= 2,
+
+ SSPSR_M_TNF= 0x00000002,
+ SSPSR_M_RNE= 0x00000004,
+ SSPSR_M_BSY= 0x00000008,
+ SSPSR_M_TFS= 0x00000010,
+ SSPSR_M_RFS= 0x00000020,
+ SSPSR_M_ROR= 0x00000040,
+ SSPSR_V_TNF= 1,
+ SSPSR_V_RNE= 2,
+ SSPSR_V_BSY= 3,
+ SSPSR_V_TFS= 4,
+ SSPSR_V_RFS= 5,
+ SSPSR_V_ROR= 6,
+};
+
+#define PPCREG ((PpcReg*)PHYSPPC)
+typedef struct PpcReg PpcReg;
+struct PpcReg {
+ ulong ppdr; /* pin direction */
+ ulong ppsr; /* pin state */
+ ulong ppar; /* pin assign */
+ ulong psdr; /* sleep mode */
+ ulong ppfr; /* pin flag reg */
+ uchar rsvd[0x1c]; /* pad to 0x30 */
+ ulong mccr1; /* MCP control register 1 */
+};
+
+enum {
+ /* ppdr and ppsr: =0, pin is general-purpose input; =1, pin is general-purpose output (11-168)*/
+ PPC_LDD0_7= 0xFF<<0, /* LCD data pins 0 to 7 */
+ PPC_L_PCLK= 1<<8, /* LCD pixel clock */
+ PPC_L_LCLK= 1<<9, /* LCD line clock */
+ PPC_L_FCLK= 1<<10, /* LCD frame clock */
+ PPC_L_BIAS= 1<<11, /* LCD AC bias */
+ PPC_TXD1= 1<<12, /* serial port 1 UART transmit */
+ PPC_RXD1= 1<<13, /* serial port 1 UART receive */
+ PPC_TXD2= 1<<14, /* serial port 2 IPC transmit */
+ PPC_RXD2= 1<<15, /* serial port 2 IPC receive */
+ PPC_TXD3= 1<<16, /* serial port 3 UART transmit */
+ PPC_RXD3= 1<<17, /* serial port 3 UART receive */
+ PPC_TXD4= 1<<18, /* serial port 4 MCP/SSP transmit */
+ PPC_RXD4= 1<<19, /* serial port 4 MCP/SSP receive */
+ PPC_SCLK= 1<<20, /* serial port 4 MCP/SSP serial clock */
+ PPC_SFRM= 1<<21, /* serial port 4 MCP/SSP frame clock */
+
+ PPAR_UPR= 1<<12, /* =1, serial port 1 GPCLK/UART pins reassigned */
+ PPAR_SPR= 1<<18, /* =1, SSP pins reassigned */
+};
+
+/*
+ * Irq Bus goo
+ */
+
+enum {
+ BusCPU= 1,
+ BusGPIOfalling= 2, /* falling edge */
+ BusGPIOrising = 3, /* rising edge */
+ BusGPIOboth = 4, /* both edges */
+ BusMAX= 4,
+ BUSUNKNOWN= -1,
+};
+
+enum {
+ /* DMA configuration parameters */
+
+ /* DMA Direction */
+ DmaOUT= 0,
+ DmaIN= 1,
+
+ /* dma endianess */
+ DmaLittle= 0,
+ DmaBig= 1,
+
+ /* dma devices */
+ DmaUDC= 0,
+ DmaSDLC= 2,
+ DmaUART0= 4,
+ DmaHSSP= 6,
+ DmaUART1= 7, /* special case (is really 6) */
+ DmaUART2= 8,
+ DmaMCPaudio= 10,
+ DmaMCPtelecom= 12,
+ DmaSSP= 14,
+};
+
+/*
+ * Interface to platform-specific PCMCIA signals, in arch*.c
+ */
+enum {
+ /* argument to pcmpin() */
+ PCMready,
+ PCMeject,
+ PCMstschng,
+};
+
+/*
+ * PCMCIA structures known by both port/cis.c and the pcmcia driver
+ */
+
+/*
+ * Map between ISA memory space and PCMCIA card memory space.
+ */
+struct PCMmap {
+ ulong ca; /* card address */
+ ulong cea; /* card end address */
+ ulong isa; /* local virtual address */
+ int len; /* length of the ISA area */
+ int attr; /* attribute memory */
+};
+
+/*
+ * a PCMCIA configuration entry
+ */
+struct PCMconftab
+{
+ int index;
+ ushort irqs; /* legal irqs */
+ uchar irqtype;
+ uchar bit16; /* true for 16 bit access */
+ struct {
+ ulong start;
+ ulong len;
+ } io[16];
+ int nio;
+ uchar vpp1;
+ uchar vpp2;
+ uchar memwait;
+ ulong maxwait;
+ ulong readywait;
+ ulong otherwait;
+};
+
+/*
+ * PCMCIA card slot
+ */
+struct PCMslot
+{
+ RWlock;
+
+ Ref ref;
+
+ long memlen; /* memory length */
+ uchar slotno; /* slot number */
+ void *regs; /* i/o registers */
+ void *mem; /* memory */
+ void *attr; /* attribute memory */
+
+ /* status */
+ uchar occupied; /* card in the slot */
+ uchar configed; /* card configured */
+ uchar busy;
+ uchar powered;
+ uchar battery;
+ uchar wrprot;
+ uchar enabled;
+ uchar special;
+ uchar dsize;
+
+ /* cis info */
+ int cisread; /* set when the cis has been read */
+ char verstr[512]; /* version string */
+ int ncfg; /* number of configurations */
+ struct {
+ ushort cpresent; /* config registers present */
+ ulong caddr; /* relative address of config registers */
+ } cfg[8];
+ int nctab; /* number of config table entries */
+ PCMconftab ctab[8];
+ PCMconftab *def; /* default conftab */
+
+ /* maps are fixed */
+ PCMmap memmap;
+ PCMmap attrmap;
+};
diff --git a/os/sa1110/softcursor.c b/os/sa1110/softcursor.c
new file mode 100644
index 00000000..b138cdcc
--- /dev/null
+++ b/os/sa1110/softcursor.c
@@ -0,0 +1,365 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+#include <draw.h>
+#include <memdraw.h>
+#include <cursor.h>
+
+#include "gscreen.h"
+
+/*
+ * Software cursor code: done by hand, might be better to use memimagedraw
+ * but that would need to be done by a process
+ */
+
+typedef struct Cursordata Cursordata;
+struct Cursordata {
+ Physdisplay *vd;
+ ulong *fb; /* screen frame buffer */
+ Rectangle r;
+ int depth; /* depth of screen */
+ int width; /* width of screen in ulongs */
+ int x;
+ int y;
+ int hotx;
+ int hoty;
+ int cbwid; /* cursor byte width */
+ int f; /* flags */
+ int dx;
+ int dy;
+ int hidecount;
+ uchar data[CURSWID*CURSHGT];
+ uchar mask[CURSWID*CURSHGT];
+ uchar save[CURSWID*CURSHGT];
+};
+
+static Cursordata *cd = nil;
+
+enum {
+ Enabled = 0x01, /* cursor is enabled */
+ Drawn = 0x02, /* cursor is currently drawn */
+ Bitswap = 0x10,
+};
+
+static Rectangle cursoroffrect;
+static int cursorisoff;
+
+static void swcursorflush(Point);
+static void swcurs_draw_or_undraw(Cursordata *);
+
+static void
+cursorupdate0(void)
+{
+ int inrect, x, y;
+ Point m;
+
+ m = mousexy();
+ x = m.x - cd->hotx;
+ y = m.y - cd->hoty;
+ inrect = (x >= cursoroffrect.min.x && x < cursoroffrect.max.x
+ && y >= cursoroffrect.min.y && y < cursoroffrect.max.y);
+ if (cursorisoff == inrect)
+ return;
+ cursorisoff = inrect;
+ if (inrect)
+ swcurs_hide(swc);
+ else {
+ cd->hidecount = 0;
+ swcurs_draw_or_undraw(swc);
+ }
+ swcursorflush(m);
+}
+
+void
+cursorupdate(Rectangle r)
+{
+ lock(vd);
+ r.min.x -= 16;
+ r.min.y -= 16;
+ cursoroffrect = r;
+ if (vd->cursor != nil)
+ cursorupdate0();
+ unlock(vd);
+}
+
+void
+cursorenable(void)
+{
+ lock(vd);
+ if(vd->cursor != nil)
+ vd->cursor->enable(swc);
+// swcursorflush(mousexy());
+ unlock(vd);
+}
+
+void
+cursordisable(void)
+{
+
+ lock(vd);
+ if(swc != nil) {
+ swcurs_disable(swc);
+ swcursorflush(mousexy());
+ }
+ unlock(vd);
+}
+
+static void
+swcursupdate(int oldx, int oldy, int x, int y)
+{
+
+ if(!canlock(vd))
+ return; /* if can't lock, don't wake up stuff */
+
+ if(x < gscreen->r.min.x)
+ x = gscreen->r.min.x;
+ if(x >= gscreen->r.max.x)
+ x = gscreen->r.max.x;
+ if(y < gscreen->r.min.y)
+ y = gscreen->r.min.y;
+ if(y >= gscreen->r.max.y)
+ y = gscreen->r.max.y;
+ if(swc != nil) {
+ swcurs_hide(swc);
+ cd->x = x;
+ cd->y = y;
+ cursorupdate0();
+ swcurs_unhide(swc);
+ swcursorflush(oldx, oldy);
+ swcursorflush(x, y);
+ }
+
+ unlock(vd);
+}
+
+void
+drawcursor(Drawcursor* c)
+{
+ Point p;
+ Cursor curs, *cp;
+ int j, i, h, bpl;
+ uchar *bc, *bs, *cclr, *cset;
+
+ if(swc == nil)
+ return;
+
+ /* Set the default system cursor */
+ if(c == nil || c->data == nil){
+ swcurs_disable(swc);
+ return;
+ }
+ else {
+ cp = &curs;
+ p.x = c->hotx;
+ p.y = c->hoty;
+ cp->offset = p;
+ bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1);
+
+ h = (c->maxy-c->miny)/2;
+ if(h > 16)
+ h = 16;
+
+ bc = c->data;
+ bs = c->data + h*bpl;
+
+ cclr = cp->clr;
+ cset = cp->set;
+ for(i = 0; i < h; i++) {
+ for(j = 0; j < 2; j++) {
+ cclr[j] = bc[j];
+ cset[j] = bs[j];
+ }
+ bc += bpl;
+ bs += bpl;
+ cclr += 2;
+ cset += 2;
+ }
+ }
+ swcurs_load(swc, cp);
+ swcursorflush(mousexy());
+ swcurs_enable(swc);
+}
+
+void*
+create(Physdisplay *vd)
+{
+ Cursordata *cd;
+
+ swc = (Cursordata*)malloc(sizeof(Cursordata));
+ cd->vd = vd;
+ cd->fb = vd->gscreen->data->bdata; /* or vd->fb? */
+ cd->r = vd->gscreen->r;
+ cd->d = vd->gscreen->depth;
+ cd->width = vd->gscreen->width;
+// cd->f = bitswap ? Bitswap : 0;
+ cd->f = Bitswap; /* ??? */
+ cd->x = cd->y = 0;
+ cd->hotx = cd->hoty = 0;
+ cd->hidecount = 0;
+ return cd;
+}
+
+void
+swcurs_destroy(Cursordata *cd)
+{
+ swcurs_disable(cd);
+ free(cd);
+}
+
+static void
+swcursorflush(Point p)
+{
+ Rectangle r;
+
+ /* XXX a little too paranoid here */
+ r.min.x = p.x-16;
+ r.min.y = p.y-16;
+ r.max.x = p.x+17;
+ r.max.y = p.y+17;
+ flushmemscreen(r);
+}
+
+static void
+swcurs_draw_or_undraw(Cursordata *cd)
+{
+ uchar *p;
+ uchar *cs;
+ int w, vw;
+ int x1 = cd->r.min.x;
+ int y1 = cd->r.min.y;
+ int x2 = cd->r.max.x;
+ int y2 = cd->r.max.y;
+ int xp = cd->x - cd->hotx;
+ int yp = cd->y - cd->hoty;
+ int ofs;
+
+ if(((cd->f & Enabled) && (cd->hidecount <= 0))
+ == ((cd->f & Drawn) != 0))
+ return;
+ w = cd->cbwid*BI2BY/cd->depth;
+ x1 = xp < x1 ? x1 : xp;
+ y1 = yp < y1 ? y1 : yp;
+ x2 = xp+w >= x2 ? x2 : xp+w;
+ y2 = yp+cd->dy >= y2 ? y2 : yp+cd->dy;
+ if(x2 <= x1 || y2 <= y1)
+ return;
+ p = (uchar*)(cd->fb + cd->width*y1) + x1*(1 << cd->d)/BI2BY;
+ y2 -= y1;
+ x2 = (x2-x1)*cd->depth/BI2BY;
+ vw = cd->width*BY2WD - x2;
+ w = cd->cbwid - x2;
+ ofs = cd->cbwid*(y1-yp)+(x1-xp);
+ cs = cd->save + ofs;
+ if((cd->f ^= Drawn) & Drawn) {
+ uchar *cm = cd->mask + ofs;
+ uchar *cd = cd->data + ofs;
+ while(y2--) {
+ x1 = x2;
+ while(x1--) {
+ *cs++ = *p;
+ *p = (*p & *cm++) ^ *cd++;
+ p++;
+ }
+ cs += w;
+ cm += w;
+ cd += w;
+ p += vw;
+ }
+ } else {
+ while(--y2 >= 0){
+ for(x1 = x2; --x1 >= 0;)
+ *p++ = *cs++;
+ cs += w;
+ p += vw;
+ }
+ }
+}
+
+static void
+swcurs_hide(Cursordata *cd)
+{
+ ++cd->hidecount;
+ swcurs_draw_or_undraw(swc);
+}
+
+static void
+swcurs_unhide(Cursordata *cd)
+{
+ if (--cd->hidecount < 0)
+ cd->hidecount = 0;
+ swcurs_draw_or_undraw(swc);
+}
+
+static void
+swcurs_enable(Cursordata *cd)
+{
+ cd->f |= Enabled;
+ swcurs_draw_or_undraw(swc);
+}
+
+void
+swcurs_disable(Cursordata *cd)
+{
+ cd->f &= ~Enabled;
+ swcurs_draw_or_undraw(swc);
+}
+
+static void
+load(Cursordata *cd, Cursor *c)
+{
+ int i, k;
+ uchar *bc, *bs, *cd, *cm;
+ static uchar bdv[4] = {0,Backgnd,Foregnd,0xff};
+ static uchar bmv[4] = {0xff,0,0,0xff};
+ int bits = 1<<cd->depth;
+ uchar mask = (1<<bits)-1;
+ int bswp = (cd->f&Bitswap) ? 8-bits : 0;
+
+ bc = c->clr;
+ bs = c->set;
+
+ swcurs_hide(swc);
+ cd = cd->data;
+ cm = cd->mask;
+ cd->hotx = c->offset.x;
+ cd->hoty = c->offset.y;
+ cd->dy = CURSHGT;
+ cd->dx = CURSWID;
+ cd->cbwid = CURSWID*(1<<cd->d)/BI2BY;
+ for(i = 0; i < CURSWID/BI2BY*CURSHGT; i++) {
+ uchar bcb = *bc++;
+ uchar bsb = *bs++;
+ for(k=0; k<BI2BY;) {
+ uchar cdv = 0;
+ uchar cmv = 0;
+ int z;
+ for(z=0; z<BI2BY; z += bits) {
+ int n = ((bsb&(0x80))|((bcb&(0x80))<<1))>>7;
+ int s = z^bswp;
+ cdv |= (bdv[n]&mask) << s;
+ cmv |= (bmv[n]&mask) << s;
+ bcb <<= 1;
+ bsb <<= 1;
+ k++;
+ }
+ *cd++ = cdv;
+ *cm++ = cmv;
+ }
+ }
+ swcurs_unhide(swc);
+}
+
+Physcursor softcursor = {
+ .name = "softcursor",
+ .create = create,
+ .enable = swenable,
+ .disable = swdisable,
+ .load = load,
+ .move = move,
+ .destroy = destroy,
+};
diff --git a/os/sa1110/suspend.c b/os/sa1110/suspend.c
new file mode 100644
index 00000000..873e23cb
--- /dev/null
+++ b/os/sa1110/suspend.c
@@ -0,0 +1,161 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+
+/*
+ * Originally written by nemo@gsyc.escet.urjc.es, and
+ * reworked by forsyth@vitanuova.com
+ */
+enum {
+ DEBUG = 0,
+};
+
+/*
+ * TO DO: pcmcia, lcd properly
+ */
+
+/*
+ * it's not clear yet whether we should do it this way,
+ * or using powerenable/powerdisable
+ */
+void
+chandevpower(int up)
+{
+ int i;
+
+ if(up){
+ for(i=0; devtab[i] != nil; i++)
+ if(devtab[i]->power != nil)
+ devtab[i]->power(1);
+ }else{
+ /* power down in reverse order */
+ for(i=0; devtab[i] != nil; i++)
+ ;
+ while(--i >= 0)
+ if(devtab[i]->power != nil)
+ devtab[i]->power(0);
+ }
+}
+
+static void
+dumpitall(void)
+{
+ iprint("intr: icip %lux iclr %lux iccr %lux icmr %lux\n",
+ INTRREG->icip,
+ INTRREG->iclr, INTRREG->iccr, INTRREG->icmr );
+ iprint("gpio: lvl %lux dir %lux, re %lux, fe %lux sts %lux alt %lux\n",
+ GPIOREG->gplr,
+ GPIOREG->gpdr, GPIOREG->grer, GPIOREG->gfer,
+ GPIOREG->gpsr, GPIOREG->gafr);
+ iprint("uart1: %lux %lux %lux\nuart3: %lux %lux %lux\n",
+ UARTREG(1)->utcr0, UARTREG(1)->utsr0, UARTREG(1)->utsr1,
+ UARTREG(3)->utcr0, UARTREG(3)->utsr0, UARTREG(3)->utsr1);
+ iprint("tmr: osmr %lux %lux %lux %lux oscr %lux ossr %lux oier %lux\n",
+ OSTMRREG->osmr[0], OSTMRREG->osmr[1],
+ OSTMRREG->osmr[2], OSTMRREG->osmr[3],
+ OSTMRREG->oscr, OSTMRREG->ossr, OSTMRREG->oier);
+ iprint("dram: mdcnfg %lux mdrefr %lux cas %lux %lux %lux %lux %lux %lux\n",
+ MEMCFGREG->mdcnfg, MEMCFGREG->mdrefr,
+ MEMCFGREG->mdcas0[0], MEMCFGREG->mdcas0[1],MEMCFGREG->mdcas0[2],
+ MEMCFGREG->mdcas2[0], MEMCFGREG->mdcas2[1],MEMCFGREG->mdcas2[2]);
+ iprint("dram: mdcnfg msc %lux %lux %lux mecr %lux\n",
+ MEMCFGREG->msc0, MEMCFGREG->msc1,MEMCFGREG->msc2,
+ MEMCFGREG->mecr);
+}
+
+static ulong *coreregs[] = {
+ /* can't trust the bootstrap to put these back */
+ &MEMCFGREG->mecr,
+ &MEMCFGREG->msc0,
+ &MEMCFGREG->msc1,
+ &MEMCFGREG->msc2,
+
+ &PPCREG->ppdr,
+ &PPCREG->ppsr, /* should save? */
+ &PPCREG->ppar,
+ &PPCREG->psdr,
+
+ &GPIOREG->grer,
+ &GPIOREG->gfer,
+ &GPIOREG->gafr,
+ &GPIOREG->gpdr,
+ /* gplr handled specially */
+
+ &GPCLKREG->gpclkr1,
+ &GPCLKREG->gpclkr2,
+ &GPCLKREG->gpclkr0,
+
+ &OSTMRREG->osmr[0],
+ &OSTMRREG->osmr[1],
+ &OSTMRREG->osmr[2],
+ &OSTMRREG->osmr[3],
+ &OSTMRREG->oscr,
+ &OSTMRREG->oier,
+ /* skip ower */
+
+ &INTRREG->iclr,
+ &INTRREG->iccr,
+ &INTRREG->icmr, /* interrupts enabled */
+
+ nil,
+};
+
+static ulong corestate[nelem(coreregs)];
+
+void
+powersuspend(void)
+{
+ extern void suspenditall(void);
+ GpioReg *g;
+ ulong back = 0x43219990; /* check that the stack's right */
+ ulong pwer, gplr;
+ ulong *rp;
+ int i, s;
+
+ s = splfhi();
+ archpowerdown(); /* sets PMGR and PPC appropriately */
+ if(DEBUG)
+ dumpitall();
+ blankscreen(1);
+ chandevpower(0);
+ gplr = GPIOREG->gplr;
+ for(i=0; (rp = coreregs[i]) != nil; i++)
+ corestate[i] = *rp;
+ pwer = PMGRREG->pwer;
+ if(pwer == 0)
+ pwer = 1<<0;
+ g = GPIOREG;
+ g->grer &= pwer; /* just the ones archpowerdown requested */
+ g->gfer &= pwer;
+ g->gedr = g->gedr;
+ RESETREG->rcsr = 0xF; /* reset all status */
+ minidcflush();
+ if(DEBUG)
+ iprint("suspenditall...\n");
+
+ suspenditall(); /* keep us in suspense */
+
+ PMGRREG->pspr = 0;
+ archpowerup();
+ trapstacks();
+ /* set output latches before gpdr restored */
+ GPIOREG->gpsr = gplr;
+ GPIOREG->gpcr = ~gplr;
+ for(i=0; (rp = coreregs[i]) != nil; i++)
+ *rp = corestate[i];
+ GPIOREG->gedr = GPIOREG->gedr; /* reset GPIO interrupts (should we?) */
+ PMGRREG->pssr = PSSR_ph; /* cancel peripheral hold */
+ chandevpower(1);
+ if(back != 0x43219990){
+ iprint("back %8.8lux\n", back);
+ panic("powersuspend");
+ }
+ blankscreen(0);
+ if(DEBUG)
+ dumpitall();
+ splx(s);
+}
diff --git a/os/sa1110/trap.c b/os/sa1110/trap.c
new file mode 100644
index 00000000..a625535d
--- /dev/null
+++ b/os/sa1110/trap.c
@@ -0,0 +1,601 @@
+#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 waslo(sr) (!((sr) & (PsrDirq|PsrDfiq)))
+
+typedef struct Handler Handler;
+
+struct Handler {
+ void (*r)(Ureg*, void*);
+ void *a;
+ int v;
+ char name[KNAMELEN];
+ Handler *next;
+};
+
+enum {
+ MinGpioIRQbit = 11,
+ NumGpioIRQbits = MaxGPIObit-MinGpioIRQbit+1,
+ GpioIRQmask = ((1<<NumGpioIRQbits)-1)<<MinGpioIRQbit,
+};
+
+static Handler irqvec[MaxIRQbit+1];
+static Handler gpiovec[NumGpioIRQbits];
+static Lock veclock;
+
+Instr BREAK = 0xE6BAD010;
+
+int (*breakhandler)(Ureg*, Proc*);
+int (*catchdbg)(Ureg *, uint);
+void (*idle)(void);
+void (*suspendcode)(void);
+
+extern void (*serwrite)(char *, int);
+
+/*
+ * Interrupt sources not masked by splhi(): special
+ * interrupt handlers (eg, profiler or watchdog), not allowed
+ * to share regular kernel data structures. All interrupts are
+ * masked by splfhi(), which should only be used sparingly.
+ * splflo enables FIQ but no others.
+ */
+enum {
+ IRQ_NONMASK = (1 << OSTimerbit(3)) | (1 << OSTimerbit(2)),
+};
+
+void
+intrenable(int v, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
+{
+ int x;
+ GpioReg *g;
+ Handler *ie;
+
+ ilock(&veclock);
+ switch(tbdf) {
+ case BusGPIOfalling:
+ case BusGPIOrising:
+ case BusGPIOboth:
+ if(v < 0 || v > MaxGPIObit)
+ panic("intrenable: gpio source %d out of range", v);
+ g = GPIOREG;
+ switch(tbdf){
+ case BusGPIOfalling:
+ g->gfer |= 1<<v;
+ g->grer &= ~(1<<v);
+ break;
+ case BusGPIOrising:
+ g->grer |= 1<<v;
+ g->gfer &= ~(1<<v);
+ break;
+ case BusGPIOboth:
+ g->grer |= 1<<v;
+ g->gfer |= 1<<v;
+ break;
+ }
+ g->gpdr &= ~(1<<v);
+ if(v >= MinGpioIRQbit) {
+ ie = &gpiovec[v-MinGpioIRQbit];
+ if(ie->r != nil)
+ iprint("duplicate gpio irq: %d (%s)\n", v, ie->name);
+ ie->r = f;
+ ie->a = a;
+ strncpy(ie->name, name, KNAMELEN-1);
+ ie->name[KNAMELEN-1] = 0;
+ iunlock(&veclock);
+ return;
+ }
+ /*FALLTHROUGH for GPIO sources 0-10 */
+ case BUSUNKNOWN:
+ case BusCPU:
+ if(v < 0 || v > MaxIRQbit)
+ panic("intrenable: irq source %d out of range", v);
+ ie = &irqvec[v];
+ if(ie->r != nil)
+ iprint("duplicate irq: %d (%s)\n", v, ie->name);
+ ie->r = f;
+ ie->a = a;
+ strncpy(ie->name, name, KNAMELEN-1);
+ ie->name[KNAMELEN-1] = 0;
+
+ x = splfhi();
+ /* Enable the interrupt by setting the mask bit */
+ INTRREG->icmr |= 1 << v;
+ splx(x);
+ break;
+ default:
+ panic("intrenable: unknown irq bus %d", tbdf);
+ }
+ iunlock(&veclock);
+}
+
+void
+intrdisable(int v, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
+{
+ int x;
+ GpioReg *g;
+ Handler *ie;
+
+ ilock(&veclock);
+ switch(tbdf) {
+ case BusGPIOfalling:
+ case BusGPIOrising:
+ case BusGPIOboth:
+ if(v < 0 || v > MaxGPIObit)
+ panic("intrdisable: gpio source %d out of range", v);
+ if(v >= MinGpioIRQbit)
+ ie = &gpiovec[v-MinGpioIRQbit];
+ else
+ ie = &irqvec[v];
+ if(ie->r != f || ie->a != a || strcmp(ie->name, name) != 0)
+ break;
+ ie->r = nil;
+ if(v < MinGpioIRQbit){
+ x = splfhi();
+ INTRREG->icmr &= ~(1<<v);
+ splx(x);
+ }
+ g = GPIOREG;
+ switch(tbdf){
+ case BusGPIOfalling:
+ g->gfer &= ~(1<<v);
+ break;
+ case BusGPIOrising:
+ g->grer &= ~(1<<v);
+ break;
+ case BusGPIOboth:
+ g->grer &= ~(1<<v);
+ g->gfer &= ~(1<<v);
+ break;
+ }
+ break;
+ case BUSUNKNOWN:
+ case BusCPU:
+ if(v < 0 || v > MaxIRQbit)
+ panic("intrdisable: irq source %d out of range", v);
+ ie = &irqvec[v];
+ if(ie->r != f || ie->a != a || strcmp(ie->name, name) != 0)
+ break;
+ ie->r = nil;
+ x = splfhi();
+ INTRREG->icmr &= ~(1<<v);
+ splx(x);
+ break;
+ default:
+ panic("intrdisable: unknown irq bus %d", tbdf);
+ }
+ iunlock(&veclock);
+}
+
+static void
+gpiointr(Ureg *ur, void*)
+{
+ Handler *cur;
+ ulong e;
+ int i;
+
+ e = GPIOREG->gedr & GpioIRQmask;
+ GPIOREG->gedr = e;
+ for(i = MinGpioIRQbit; i <= MaxGPIObit && e != 0; i++){
+ if(e & (1<<i)){
+ cur = &gpiovec[i-MinGpioIRQbit];
+ if(cur->r != nil){
+ cur->r(ur, cur->a);
+ e &= ~(1<<i);
+ }
+ }
+ }
+ if(e != 0){
+ GPIOREG->gfer &= ~e;
+ GPIOREG->grer &= ~e;
+ iprint("spurious GPIO interrupt: %8.8lux\n", e);
+ }
+}
+
+static void
+intrs(Ureg *ur, ulong ibits)
+{
+ Handler *cur;
+ int i, s;
+
+ for(i=0; i<nelem(irqvec) && ibits; i++)
+ if(ibits & (1<<i)){
+ cur = &irqvec[i];
+ if(cur->r != nil){
+ cur->r(ur, cur->a);
+ ibits &= ~(1<<i);
+ }
+ }
+ if(ibits != 0){
+ iprint("spurious irq interrupt: %8.8lux\n", ibits);
+ s = splfhi();
+ INTRREG->icmr &= ~ibits;
+ splx(s);
+ }
+}
+
+/*
+ * initialise R13 in each trap mode, at the start and after suspend reset.
+ */
+void
+trapstacks(void)
+{
+ setr13(PsrMfiq, m->fiqstack+nelem(m->fiqstack));
+ setr13(PsrMirq, m->irqstack+nelem(m->irqstack));
+ setr13(PsrMabt, m->abtstack+nelem(m->abtstack));
+ setr13(PsrMund, m->undstack+nelem(m->undstack));
+}
+
+void
+trapinit(void)
+{
+ int v;
+ IntrReg *intr = INTRREG;
+
+ intr->icmr = 0;
+ intr->iclr = IRQ_NONMASK;
+
+ trapstacks();
+
+ for(v = 0; v < nelem(irqvec); v++) {
+ irqvec[v].r = nil;
+ irqvec[v].a = nil;
+ irqvec[v].v = v;
+ }
+ for(v = 0; v < nelem(gpiovec); v++) {
+ gpiovec[v].r = nil;
+ gpiovec[v].a = nil;
+ gpiovec[v].v = v+MinGpioIRQbit;
+ }
+
+ memmove(page0->vectors, vectors, sizeof(page0->vectors));
+ memmove(page0->vtable, vtable, sizeof(page0->vtable));
+ dcflush(page0, sizeof(*page0));
+
+ idle = xspanalloc(13*sizeof(ulong), CACHELINESZ, 0);
+ memmove(idle, _idlemode, 13*sizeof(ulong));
+ dcflush(idle, 13*sizeof(ulong));
+
+ suspendcode = xspanalloc(16*sizeof(ulong), CACHELINESZ, 0);
+ memmove(suspendcode, _suspendcode, 16*sizeof(ulong));
+ dcflush(suspendcode, 8*sizeof(ulong));
+
+ icflushall();
+
+ intrenable(MinGpioIRQbit, gpiointr, nil, BusCPU, "gpio");
+}
+
+static char *trapnames[PsrMask+1] = {
+ [ PsrMfiq ] "Fiq interrupt",
+ [ PsrMirq ] "Mirq interrupt",
+ [ PsrMsvc ] "SVC/SWI Exception",
+ [ PsrMabt ] "Prefetch Abort/Data Abort",
+ [ PsrMabt+1 ] "Data Abort",
+ [ PsrMund ] "Undefined instruction",
+ [ PsrMsys ] "Sys trap"
+};
+
+static char *
+trapname(int psr)
+{
+ char *s;
+
+ s = trapnames[psr & PsrMask];
+ if(s == nil)
+ s = "Undefined trap";
+ return s;
+}
+
+static void
+sys_trap_error(int type)
+{
+ char errbuf[ERRMAX];
+ sprint(errbuf, "sys: trap: %s\n", trapname(type));
+ error(errbuf);
+}
+
+static void
+faultarm(Ureg *ureg, ulong far)
+{
+ char buf[ERRMAX];
+
+ sprint(buf, "sys: trap: fault pc=%8.8lux addr=0x%lux", (ulong)ureg->pc, far);
+ if(0){
+ iprint("%s\n", buf);
+ dumpregs(ureg);
+ }
+ if(far == ~0)
+ disfault(ureg, "dereference of nil");
+ disfault(ureg, buf);
+}
+
+/*
+ * All traps come here. It might be slightly slower to have all traps call trap
+ * rather than directly vectoring the handler.
+ * However, this avoids a lot of code duplication and possible bugs.
+ * trap is called splfhi().
+ */
+void
+trap(Ureg* ureg)
+{
+ ulong far, fsr;
+ int rem, t, itype;
+ Proc *oup;
+
+ if(up != nil)
+ rem = ((char*)ureg)-up->kstack;
+ else
+ rem = ((char*)ureg)-(char*)m->stack;
+ if(ureg->type != PsrMfiq && rem < 256)
+ panic("trap %d bytes remaining (%s), up=#%8.8lux ureg=#%8.8lux pc=#%8.8ux",
+ rem, up?up->text:"", up, ureg, ureg->pc);
+
+ /*
+ * All interrupts/exceptions should be resumed at ureg->pc-4,
+ * except for Data Abort which resumes at ureg->pc-8.
+ */
+ itype = ureg->type;
+ if(itype == PsrMabt+1)
+ ureg->pc -= 8;
+ else
+ ureg->pc -= 4;
+ ureg->sp = (ulong)(ureg+1);
+ if(itype == PsrMfiq){ /* fast interrupt (eg, profiler) */
+ oup = up;
+ up = nil;
+ intrs(ureg, INTRREG->icfp);
+ up = oup;
+ return;
+ }
+
+ /* All other traps */
+
+ if(ureg->psr & PsrDfiq)
+ panic("FIQ disabled");
+
+ if(up){
+ up->pc = ureg->pc;
+ up->dbgreg = ureg;
+ }
+ switch(itype) {
+ case PsrMirq:
+ t = m->ticks; /* CPU time per proc */
+ up = nil; /* no process at interrupt level */
+ splflo(); /* allow fast interrupts */
+ intrs(ureg, INTRREG->icip);
+ up = m->proc;
+ preemption(m->ticks - t);
+ break;
+
+ case PsrMund: /* Undefined instruction */
+ if(*(ulong*)ureg->pc == BREAK && breakhandler) {
+ int s;
+ Proc *p;
+
+ p = up;
+ /* if(!waslo(ureg->psr) || ureg->pc >= (ulong)splhi && ureg->pc < (ulong)islo)
+ p = 0; */
+ s = breakhandler(ureg, p);
+ if(s == BrkSched) {
+ p->preempted = 0;
+ sched();
+ } else if(s == BrkNoSched) {
+ p->preempted = 1; /* stop it being preempted until next instruction */
+ if(up)
+ up->dbgreg = 0;
+ return;
+ }
+ break;
+ }
+ if(up == nil)
+ goto faultpanic;
+ spllo();
+ if(waserror()) {
+ if(waslo(ureg->psr) && up->type == Interp)
+ disfault(ureg, up->env->errstr);
+ setpanic();
+ dumpregs(ureg);
+ panic("%s", up->env->errstr);
+ }
+ if(!fpiarm(ureg)) {
+ dumpregs(ureg);
+ sys_trap_error(ureg->type);
+ }
+ poperror();
+ break;
+
+ case PsrMsvc: /* Jump through 0 or SWI */
+ if(waslo(ureg->psr) && up && up->type == Interp) {
+ spllo();
+ dumpregs(ureg);
+ sys_trap_error(ureg->type);
+ }
+ setpanic();
+ dumpregs(ureg);
+ panic("SVC/SWI exception");
+ break;
+
+ case PsrMabt: /* Prefetch abort */
+ if(catchdbg && catchdbg(ureg, 0))
+ break;
+ /* FALL THROUGH */
+ case PsrMabt+1: /* Data abort */
+ fsr = mmugetfsr();
+ far = mmugetfar();
+ if(fsr & (1<<9)) {
+ mmuputfsr(fsr & ~(1<<9));
+ if(catchdbg && catchdbg(ureg, fsr))
+ break;
+ print("Debug/");
+ }
+ if(waslo(ureg->psr) && up && up->type == Interp) {
+ spllo();
+ faultarm(ureg, far);
+ }
+ print("Data Abort: FSR %8.8luX FAR %8.8luX\n", fsr, far);
+ /* FALL THROUGH */
+
+ default: /* ??? */
+faultpanic:
+ setpanic();
+ dumpregs(ureg);
+ panic("exception %uX %s\n", ureg->type, trapname(ureg->type));
+ break;
+ }
+
+ splhi();
+ if(up)
+ up->dbgreg = 0; /* becomes invalid after return from trap */
+}
+
+void
+setpanic(void)
+{
+ if(breakhandler != 0) /* don't mess up debugger */
+ return;
+ INTRREG->icmr = 0;
+ spllo();
+ consoleprint = 1;
+ serwrite = uartputs;
+}
+
+int
+isvalid_wa(void *v)
+{
+ return (ulong)v >= KZERO && (ulong)v < conf.topofmem && !((ulong)v & 3);
+}
+
+int
+isvalid_va(void *v)
+{
+ return (ulong)v >= KZERO && (ulong)v < conf.topofmem;
+}
+
+void
+dumplongs(char *msg, ulong *v, int n)
+{
+ int i, l;
+
+ l = 0;
+ iprint("%s at %.8p: ", msg, v);
+ for(i=0; i<n; i++){
+ if(l >= 4){
+ iprint("\n %.8p: ", v);
+ l = 0;
+ }
+ if(isvalid_va(v)){
+ iprint(" %.8lux", *v++);
+ l++;
+ }else{
+ iprint(" invalid");
+ break;
+ }
+ }
+ iprint("\n");
+}
+
+static void
+_dumpstack(Ureg *ureg)
+{
+ ulong *v, *l;
+ ulong inst;
+ ulong *estack;
+ int i;
+
+ l = (ulong*)(ureg+1);
+ if(!isvalid_wa(l)){
+ iprint("invalid ureg/stack: %.8p\n", l);
+ return;
+ }
+ print("ktrace /kernel/path %.8ux %.8ux %.8ux\n", ureg->pc, ureg->sp, ureg->r14);
+ if(up != nil && l >= (ulong*)up->kstack && l <= (ulong*)(up->kstack+KSTACK-4))
+ estack = (ulong*)(up->kstack+KSTACK);
+ else if(l >= (ulong*)m->stack && l <= (ulong*)((ulong)m+BY2PG-4))
+ estack = (ulong*)((ulong)m+BY2PG-4);
+ else{
+ iprint("unknown stack\n");
+ return;
+ }
+ i = 0;
+ for(; l<estack; l++) {
+ if(!isvalid_wa(l)) {
+ iprint("invalid(%8.8p)", l);
+ break;
+ }
+ v = (ulong*)*l;
+ if(isvalid_wa(v)) {
+ inst = v[-1];
+ if((inst & 0x0ff0f000) == 0x0280f000 &&
+ (*(v-2) & 0x0ffff000) == 0x028fe000 ||
+ (inst & 0x0f000000) == 0x0b000000) {
+ iprint("%8.8p=%8.8lux ", l, v);
+ i++;
+ }
+ }
+ if(i == 4){
+ iprint("\n");
+ i = 0;
+ }
+ }
+ if(i)
+ print("\n");
+}
+
+void
+dumpregs(Ureg* ureg)
+{
+ print("TRAP: %s", trapname(ureg->type));
+ if((ureg->psr & PsrMask) != PsrMsvc)
+ print(" in %s", trapname(ureg->psr));
+ print("\n");
+ print("PSR %8.8uX type %2.2uX PC %8.8uX LINK %8.8uX\n",
+ ureg->psr, ureg->type, ureg->pc, ureg->link);
+ print("R14 %8.8uX R13 %8.8uX R12 %8.8uX R11 %8.8uX R10 %8.8uX\n",
+ ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10);
+ print("R9 %8.8uX R8 %8.8uX R7 %8.8uX R6 %8.8uX R5 %8.8uX\n",
+ ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5);
+ print("R4 %8.8uX R3 %8.8uX R2 %8.8uX R1 %8.8uX R0 %8.8uX\n",
+ ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0);
+ print("Stack is at: %8.8luX\n", ureg);
+ print("PC %8.8lux LINK %8.8lux\n", (ulong)ureg->pc, (ulong)ureg->link);
+
+ if(up)
+ print("Process stack: %8.8lux-%8.8lux\n",
+ up->kstack, up->kstack+KSTACK-4);
+ else
+ print("System stack: %8.8lux-%8.8lux\n",
+ (ulong)(m+1), (ulong)m+BY2PG-4);
+ dumplongs("stack", (ulong *)(ureg + 1), 16);
+ _dumpstack(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 = (ulong)&fn;
+ ureg.r14 = 0;
+ fn(&ureg);
+}
+
+void
+dumpstack(void)
+{
+ callwithureg(_dumpstack);
+}
+
+void
+trapspecial(int (*f)(Ureg *, uint))
+{
+ catchdbg = f;
+}