summaryrefslogtreecommitdiff
path: root/os/pxa
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 21:39:35 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 21:39:35 +0000
commit74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a (patch)
treec6e220ba61db3a6ea4052e6841296d829654e664 /os/pxa
parent46439007cf417cbd9ac8049bb4122c890097a0fa (diff)
20060303
Diffstat (limited to 'os/pxa')
-rw-r--r--os/pxa/NOTICE2
-rw-r--r--os/pxa/clock.c318
-rw-r--r--os/pxa/devether.c617
-rw-r--r--os/pxa/devrtc.c169
-rw-r--r--os/pxa/devuart.c1070
-rw-r--r--os/pxa/dma.c244
-rw-r--r--os/pxa/etherif.h41
-rw-r--r--os/pxa/fpi.h61
-rw-r--r--os/pxa/fpiarm.c483
-rw-r--r--os/pxa/gpio.c88
-rw-r--r--os/pxa/i2c.c561
-rw-r--r--os/pxa/l.s548
-rw-r--r--os/pxa/mmu.c252
-rw-r--r--os/pxa/pxaio.h383
-rw-r--r--os/pxa/sa1110break.c360
-rw-r--r--os/pxa/trap.c587
16 files changed, 5784 insertions, 0 deletions
diff --git a/os/pxa/NOTICE b/os/pxa/NOTICE
new file mode 100644
index 00000000..61b95632
--- /dev/null
+++ b/os/pxa/NOTICE
@@ -0,0 +1,2 @@
+Inferno® Copyright © 1996-1999 Lucent Technologies Inc. All rights reserved.
+PXA 25x Inferno port Copyright © 2000,2003,2004 Vita Nuova Holdings Limited. All rights reserved.
diff --git a/os/pxa/clock.c b/os/pxa/clock.c
new file mode 100644
index 00000000..2fd6eba6
--- /dev/null
+++ b/os/pxa/clock.c
@@ -0,0 +1,318 @@
+#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;
+
+{static int tag, led; if(++tag >= HZ){ledset(led ^= 1);tag=0;}}
+ 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(IRQ, IRQtimer0+timer, f, a, 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/pxa/devether.c b/os/pxa/devether.c
new file mode 100644
index 00000000..4b9da104
--- /dev/null
+++ b/os/pxa/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->itype, ether->irq, ether->interrupt, ether, 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/pxa/devrtc.c b/os/pxa/devrtc.c
new file mode 100644
index 00000000..ace6cc82
--- /dev/null
+++ b/os/pxa/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/pxa/devuart.c b/os/pxa/devuart.c
new file mode 100644
index 00000000..4b8cc140
--- /dev/null
+++ b/os/pxa/devuart.c
@@ -0,0 +1,1070 @@
+#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"
+
+/*
+ * Driver for the uart.
+ * TO DO: replace by uartpxa.c
+ */
+enum
+{
+ /*
+ * register numbers
+ */
+ Data= 0, /* xmit/rcv buffer */
+ Iena= 1, /* interrupt enable */
+ Ircv= (1<<0), /* for char rcv'd */
+ Ixmt= (1<<1), /* for xmit buffer empty */
+ Irstat=(1<<2), /* for change in rcv'er status */
+ Imstat=(1<<3), /* for change in modem status */
+ Rtoie= 1<<4, /* character timeout indication */
+ Nrze= 1<<5, /* NRZ encoding enabled */
+ Uue= 1<<6, /* Uart unit enabled */
+ Dmae= 1<<7, /* DMA requests enabled */
+ Istat= 2, /* interrupt flag (read) */
+ Ipend= 1, /* interrupt pending (not) */
+ Fenabd=(3<<6), /* on if fifo's enabled */
+ Fifoctl=2, /* fifo control (write) */
+ Fena= (1<<0), /* enable xmit/rcv fifos */
+ Fdma= (1<<3), /* dma on */
+ Ftrig= (1<<6), /* trigger after 4 input characters */
+ Fclear=(3<<1), /* clear xmit & rcv fifos */
+ Format= 3, /* byte format */
+ Bits8= (3<<0), /* 8 bits/byte */
+ Stop2= (1<<2), /* 2 stop bits */
+ Pena= (1<<3), /* generate parity */
+ Peven= (1<<4), /* even parity */
+ Pforce=(1<<5), /* force parity */
+ Break= (1<<6), /* generate a break */
+ Dra= (1<<7), /* address the divisor */
+ Mctl= 4, /* modem control */
+ Dtr= (1<<0), /* data terminal ready */
+ Rts= (1<<1), /* request to send */
+ Ri= (1<<2), /* ring */
+ Inton= (1<<3), /* turn on interrupts */
+ Loop= (1<<4), /* loop back */
+ Lstat= 5, /* line status */
+ Inready=(1<<0), /* receive buffer full */
+ Oerror=(1<<1), /* receiver overrun */
+ Perror=(1<<2), /* receiver parity error */
+ Ferror=(1<<3), /* rcv framing error */
+ Berror=(1<<4), /* break alarm */
+ Outready=(1<<5), /* output buffer full */
+ Mstat= 6, /* modem status */
+ Ctsc= (1<<0), /* clear to send changed */
+ Dsrc= (1<<1), /* data set ready changed */
+ Rire= (1<<2), /* rising edge of ring indicator */
+ Dcdc= (1<<3), /* data carrier detect changed */
+ Cts= (1<<4), /* complement of clear to send line */
+ Dsr= (1<<5), /* complement of data set ready line */
+ Ringl= (1<<6), /* complement of ring indicator line */
+ Dcd= (1<<7), /* complement of data carrier detect line */
+ Scratch=7, /* scratchpad */
+ Dlsb= 0, /* divisor lsb */
+ Dmsb= 1, /* divisor msb */
+
+ CTLS= 023,
+ CTLQ= 021,
+
+ Stagesize= 1024,
+ Nuart= 4, /* max per machine */
+};
+
+typedef struct Uart Uart;
+struct Uart
+{
+ QLock;
+ int opens;
+
+ int enabled;
+ Uart *elist; /* next enabled interface */
+ char name[KNAMELEN];
+
+ ulong sticky[8]; /* sticky write register values */
+ void* regs;
+ ulong port;
+ ulong freq; /* clock frequency */
+ uchar mask; /* bits/char */
+ int dev;
+ int baud; /* baud rate */
+
+ uchar istat; /* last istat read */
+ int frame; /* framing errors */
+ int overrun; /* rcvr overruns */
+
+ /* buffers */
+ int (*putc)(Queue*, int);
+ Queue *iq;
+ Queue *oq;
+
+ Lock flock; /* fifo */
+ uchar fifoon; /* fifo's enabled */
+ uchar nofifo; /* earlier chip version with nofifo */
+
+ Lock rlock; /* receive */
+ uchar istage[Stagesize];
+ uchar *ip;
+ uchar *ie;
+
+ int haveinput;
+
+ Lock tlock; /* transmit */
+ uchar ostage[Stagesize];
+ uchar *op;
+ uchar *oe;
+
+ int modem; /* hardware flow control on */
+ int xonoff; /* software flow control on */
+ int blocked;
+ int cts, dsr, dcd; /* keep track of modem status */
+ int ctsbackoff;
+ int hup_dsr, hup_dcd; /* send hangup upstream? */
+ int dohup;
+
+ Rendez r;
+};
+
+static Uart *uart[Nuart];
+static int nuart;
+
+struct Uartalloc {
+ Lock;
+ Uart *elist; /* list of enabled interfaces */
+} uartalloc;
+
+static void uartintr(Uart*);
+
+/*
+ * pick up architecture specific routines and definitions
+ */
+#include "uart.h"
+
+/*
+ * set the baud rate by calculating and setting the baudrate
+ * generator constant. This will work with fairly non-standard
+ * baud rates.
+ */
+static void
+uartsetbaud(Uart *p, int rate)
+{
+ ulong brconst;
+
+ if(rate <= 0)
+ return;
+
+ p->freq = archuartclock(p->port, rate);
+ if(p->freq == 0)
+ return;
+
+ brconst = (p->freq+8*rate-1)/(16*rate);
+
+ uartwrreg(p, Format, Dra);
+ uartwr(p, Dmsb, (brconst>>8) & 0xff);
+ uartwr(p, Dlsb, brconst & 0xff);
+ uartwrreg(p, Format, 0);
+
+ p->baud = rate;
+}
+
+/*
+ * decide if we should hangup when dsr or dcd drops.
+ */
+static void
+uartdsrhup(Uart *p, int n)
+{
+ p->hup_dsr = n;
+}
+
+static void
+uartdcdhup(Uart *p, int n)
+{
+ p->hup_dcd = n;
+}
+
+static void
+uartparity(Uart *p, char type)
+{
+ switch(type){
+ case 'e':
+ p->sticky[Format] |= Pena|Peven;
+ break;
+ case 'o':
+ p->sticky[Format] &= ~Peven;
+ p->sticky[Format] |= Pena;
+ break;
+ default:
+ p->sticky[Format] &= ~(Pena|Peven);
+ break;
+ }
+ uartwrreg(p, Format, 0);
+}
+
+/*
+ * set bits/character, default 8
+ */
+void
+uartbits(Uart *p, int bits)
+{
+ if(bits < 5 || bits > 8)
+ error(Ebadarg);
+
+ p->sticky[Format] &= ~3;
+ p->sticky[Format] |= bits-5;
+
+ uartwrreg(p, Format, 0);
+}
+
+
+/*
+ * toggle DTR
+ */
+void
+uartdtr(Uart *p, int n)
+{
+ if(n)
+ p->sticky[Mctl] |= Dtr;
+ else
+ p->sticky[Mctl] &= ~Dtr;
+
+ uartwrreg(p, Mctl, 0);
+}
+
+/*
+ * toggle RTS
+ */
+void
+uartrts(Uart *p, int n)
+{
+ if(n)
+ p->sticky[Mctl] |= Rts;
+ else
+ p->sticky[Mctl] &= ~Rts;
+
+ uartwrreg(p, Mctl, 0);
+}
+
+/*
+ * send break
+ */
+static void
+uartbreak(Uart *p, int ms)
+{
+ if(ms == 0)
+ ms = 200;
+
+ uartwrreg(p, Format, Break);
+ tsleep(&up->sleep, return0, 0, ms);
+ uartwrreg(p, Format, 0);
+}
+
+static void
+uartfifoon(Uart *p)
+{
+ ulong i, x;
+
+ if(p->nofifo || uartrdreg(p, Istat) & Fenabd)
+ return;
+
+ x = splhi();
+
+ /* reset fifos */
+ p->sticky[Fifoctl] = 0;
+ uartwrreg(p, Fifoctl, Fclear);
+
+ /* empty buffer and interrupt conditions */
+ for(i = 0; i < 16; i++){
+ if(uartrdreg(p, Istat)){
+ /* nothing to do */
+ }
+ if(uartrdreg(p, Data)){
+ /* nothing to do */
+ }
+ }
+
+ /* turn on fifo */
+ p->fifoon = 1;
+ p->sticky[Fifoctl] = Fena|Ftrig;
+ uartwrreg(p, Fifoctl, 0);
+ p->istat = uartrdreg(p, Istat);
+ if((p->istat & Fenabd) == 0) {
+ /* didn't work, must be an earlier chip type */
+ p->nofifo = 1;
+ }
+
+ splx(x);
+}
+
+/*
+ * modem flow control on/off (rts/cts)
+ */
+static void
+uartmflow(Uart *p, int n)
+{
+ ilock(&p->tlock);
+ if(n){
+ p->sticky[Iena] |= Imstat;
+ uartwrreg(p, Iena, 0);
+ p->modem = 1;
+ p->cts = uartrdreg(p, Mstat) & Cts;
+ } else {
+ p->sticky[Iena] &= ~Imstat;
+ uartwrreg(p, Iena, 0);
+ p->modem = 0;
+ p->cts = 1;
+ }
+ iunlock(&p->tlock);
+
+// ilock(&p->flock);
+// if(1)
+// /* turn on fifo's */
+// uartfifoon(p);
+// else {
+// /* turn off fifo's */
+// p->fifoon = 0;
+// p->sticky[Fifoctl] = 0;
+// uartwrreg(p, Fifoctl, Fclear);
+// }
+// iunlock(&p->flock);
+}
+
+/*
+ * turn on a port's interrupts. set DTR and RTS
+ */
+static void
+uartenable(Uart *p)
+{
+ Uart **l;
+
+ if(p->enabled)
+ return;
+
+ uartportpower(p, 1);
+
+ p->hup_dsr = p->hup_dcd = 0;
+ p->cts = p->dsr = p->dcd = 0;
+
+ /*
+ * turn on interrupts
+ */
+ p->sticky[Iena] = Ircv | Ixmt | Irstat | Uue;
+ uartwrreg(p, Iena, 0);
+
+ /*
+ * turn on DTR and RTS
+ */
+ uartdtr(p, 1);
+ uartrts(p, 1);
+
+ uartfifoon(p);
+
+ /*
+ * assume we can send
+ */
+ ilock(&p->tlock);
+ p->cts = 1;
+ p->blocked = 0;
+ iunlock(&p->tlock);
+
+ /*
+ * set baud rate to the last used
+ */
+ uartsetbaud(p, p->baud);
+
+ lock(&uartalloc);
+ for(l = &uartalloc.elist; *l; l = &(*l)->elist){
+ if(*l == p)
+ break;
+ }
+ if(*l == 0){
+ p->elist = uartalloc.elist;
+ uartalloc.elist = p;
+ }
+ p->enabled = 1;
+ unlock(&uartalloc);
+}
+
+/*
+ * turn off a port's interrupts. reset DTR and RTS
+ */
+static void
+uartdisable(Uart *p)
+{
+ Uart **l;
+
+ /*
+ * turn off interrupts
+ */
+ p->sticky[Iena] = Uue;
+ uartwrreg(p, Iena, 0);
+
+ /*
+ * revert to default settings
+ */
+ p->sticky[Format] = Bits8;
+ uartwrreg(p, Format, 0);
+
+ /*
+ * turn off DTR, RTS, hardware flow control & fifo's
+ */
+ uartdtr(p, 0);
+ uartrts(p, 0);
+ uartmflow(p, 0);
+ ilock(&p->tlock);
+ p->xonoff = p->blocked = 0;
+ iunlock(&p->tlock);
+
+ uartportpower(p, 0);
+
+ lock(&uartalloc);
+ for(l = &uartalloc.elist; *l; l = &(*l)->elist){
+ if(*l == p){
+ *l = p->elist;
+ break;
+ }
+ }
+ p->enabled = 0;
+ unlock(&uartalloc);
+}
+
+/*
+ * put some bytes into the local queue to avoid calling
+ * qconsume for every character
+ */
+static int
+stageoutput(Uart *p)
+{
+ int n;
+
+ n = qconsume(p->oq, p->ostage, Stagesize);
+ if(n <= 0)
+ return 0;
+ p->op = p->ostage;
+ p->oe = p->ostage + n;
+ return n;
+}
+
+/*
+ * (re)start output
+ */
+static void
+uartkick0(Uart *p)
+{
+ int i;
+ if((p->modem && (p->cts == 0)) || p->blocked)
+ return;
+
+ /*
+ * 128 here is an arbitrary limit to make sure
+ * we don't stay in this loop too long. If the
+ * chips output queue is longer than 128, too
+ * bad -- presotto
+ */
+ for(i = 0; i < 128; i++){
+ if(!(uartrdreg(p, Lstat) & Outready))
+ break;
+ if(p->op >= p->oe && stageoutput(p) == 0)
+ break;
+ uartwr(p, Data, *p->op++);
+ }
+}
+
+static void
+uartkick(void *v)
+{
+ Uart *p;
+
+ p = v;
+ ilock(&p->tlock);
+ uartkick0(p);
+ iunlock(&p->tlock);
+}
+
+/*
+ * restart input if it's off
+ */
+static void
+uartflow(void *v)
+{
+ Uart *p;
+
+ p = v;
+ if(p->modem)
+ uartrts(p, 1);
+ ilock(&p->rlock);
+ p->haveinput = 1;
+ iunlock(&p->rlock);
+}
+
+/*
+ * default is 9600 baud, 1 stop bit, 8 bit chars, no interrupts,
+ * transmit and receive enabled, interrupts disabled.
+ */
+static void
+uartsetup0(Uart *p)
+{
+ memset(p->sticky, 0, sizeof(p->sticky));
+ /*
+ * set rate to 9600 baud.
+ * 8 bits/character.
+ * 1 stop bit.
+ * interrupts enabled.
+ */
+ p->sticky[Format] = Bits8;
+ uartwrreg(p, Format, 0);
+ p->sticky[Mctl] |= Inton;
+ uartwrreg(p, Mctl, 0x0);
+
+// uartsetbaud(p, 9600);
+ uartsetbaud(p, 38400);
+
+ p->iq = qopen(4*1024, 0, uartflow, p);
+ p->oq = qopen(4*1024, 0, uartkick, p);
+ if(p->iq == nil || p->oq == nil)
+ panic("uartsetup0");
+
+ p->ip = p->istage;
+ p->ie = &p->istage[Stagesize];
+ p->op = p->ostage;
+ p->oe = p->ostage;
+}
+
+/*
+ * called by uartinstall() to create a new duart
+ */
+void
+uartsetup(ulong port, void *regs, ulong freq, char *name)
+{
+ Uart *p;
+
+ if(nuart >= Nuart)
+ return;
+
+ p = xalloc(sizeof(Uart));
+ uart[nuart] = p;
+ strcpy(p->name, name);
+ p->dev = nuart;
+ nuart++;
+ p->port = port;
+ p->regs = regs;
+ p->freq = freq;
+ uartsetup0(p);
+}
+
+/*
+ * called by main() to configure a duart port as a console or a mouse
+ */
+void
+uartspecial(int port, int baud, Queue **in, Queue **out, int (*putc)(Queue*, int))
+{
+ Uart *p = uart[port];
+ uartenable(p);
+ if(baud)
+ uartsetbaud(p, baud);
+ p->putc = putc;
+ if(in)
+ *in = p->iq;
+ if(out)
+ *out = p->oq;
+ p->opens++;
+}
+
+/*
+ * handle an interrupt to a single uart
+ */
+static void
+uartintr(Uart *p)
+{
+ uchar ch;
+ int s, l;
+
+ for (s = uartrdreg(p, Istat); !(s&Ipend); s = uartrdreg(p, Istat)) {
+ switch(s&0x3f){
+ case 4: /* received data available */
+ case 6: /* receiver line status (alarm or error) */
+ case 12: /* character timeout indication */
+ while ((l = uartrdreg(p, Lstat)) & Inready) {
+ if(l & Ferror)
+ p->frame++;
+ if(l & Oerror)
+ p->overrun++;
+ ch = uartrdreg(p, Data) & 0xff;
+ if (l & (Berror|Perror|Ferror)) {
+ /* ch came with break, parity or framing error - consume */
+ continue;
+ }
+ if (ch == CTLS || ch == CTLQ) {
+ ilock(&p->tlock);
+ if(p->xonoff){
+ if(ch == CTLS)
+ p->blocked = 1;
+ else
+ p->blocked = 0; /* clock gets output going again */
+ }
+ iunlock(&p->tlock);
+ }
+ if(p->putc)
+ p->putc(p->iq, ch);
+ else {
+ ilock(&p->rlock);
+ if(p->ip < p->ie)
+ *p->ip++ = ch;
+ else
+ p->overrun++;
+ p->haveinput = 1;
+ iunlock(&p->rlock);
+ }
+ }
+ break;
+
+ case 2: /* transmitter not full */
+ uartkick(p);
+ break;
+
+ case 0: /* modem status */
+ ch = uartrdreg(p, Mstat);
+ if(ch & Ctsc){
+ ilock(&p->tlock);
+ l = p->cts;
+ p->cts = ch & Cts;
+ if(l == 0 && p->cts)
+ p->ctsbackoff = 2; /* clock gets output going again */
+ iunlock(&p->tlock);
+ }
+ if (ch & Dsrc) {
+ l = ch & Dsr;
+ if(p->hup_dsr && p->dsr && !l){
+ ilock(&p->rlock);
+ p->dohup = 1;
+ iunlock(&p->rlock);
+ }
+ p->dsr = l;
+ }
+ if (ch & Dcdc) {
+ l = ch & Dcd;
+ if(p->hup_dcd && p->dcd && !l){
+ ilock(&p->rlock);
+ p->dohup = 1;
+ iunlock(&p->rlock);
+ }
+ p->dcd = l;
+ }
+ break;
+
+ default:
+ iprint("weird uart interrupt #%2.2ux\n", s);
+ break;
+ }
+ }
+ p->istat = s;
+}
+
+/*
+ * we save up input characters till clock time
+ *
+ * There's also a bit of code to get a stalled print going.
+ * It shouldn't happen, but it does. Obviously I don't
+ * understand something. Since it was there, I bundled a
+ * restart after flow control with it to give some hysteresis
+ * to the hardware flow control. This makes compressing
+ * modems happier but will probably bother something else.
+ * -- presotto
+ */
+void
+uartclock(void)
+{
+ int n;
+ Uart *p;
+
+ for(p = uartalloc.elist; p; p = p->elist){
+
+ /* this amortizes cost of qproduce to many chars */
+ if(p->haveinput){
+ ilock(&p->rlock);
+ if(p->haveinput){
+ n = p->ip - p->istage;
+ if(n > 0 && p->iq){
+ if(n > Stagesize)
+ panic("uartclock");
+ if(qproduce(p->iq, p->istage, n) < 0)
+ uartrts(p, 0);
+ else
+ p->ip = p->istage;
+ }
+ p->haveinput = 0;
+ }
+ iunlock(&p->rlock);
+ }
+ if(p->dohup){
+ ilock(&p->rlock);
+ if(p->dohup){
+ qhangup(p->iq, 0);
+ qhangup(p->oq, 0);
+ }
+ p->dohup = 0;
+ iunlock(&p->rlock);
+ }
+
+ /* this adds hysteresis to hardware flow control */
+ if(p->ctsbackoff){
+ ilock(&p->tlock);
+ if(p->ctsbackoff){
+ if(--(p->ctsbackoff) == 0)
+ uartkick0(p);
+ }
+ iunlock(&p->tlock);
+ }
+ }
+}
+
+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(); /* architecture specific */
+
+ 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 = 0666;
+ dp++;
+ sprint(dp->name, "%sctl", uart[i]->name);
+ dp->qid.path = NETQID(i, Nctlqid);
+ dp->perm = 0666;
+ 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;
+ p->dcd = p->dsr = p->dohup = 0;
+ }
+ qunlock(p);
+ break;
+ }
+}
+
+static long
+uartstatus(Chan*, Uart *p, void *buf, long n, long offset)
+{
+ uchar mstat, fstat, istat, tstat;
+ char str[256];
+
+ str[0] = 0;
+ tstat = p->sticky[Mctl];
+ mstat = uartrdreg(p, Mstat);
+ istat = p->sticky[Iena];
+ fstat = p->sticky[Format];
+ snprint(str, sizeof str,
+ "b%d c%d d%d e%d l%d m%d p%c r%d s%d\n"
+ "%d %d %d%s%s%s%s%s\n",
+
+ p->baud,
+ p->hup_dcd,
+ (tstat & Dtr) != 0,
+ p->hup_dsr,
+ (fstat & Bits8) + 5,
+ (istat & Imstat) != 0,
+ (fstat & Pena) ? ((fstat & Peven) ? 'e' : 'o') : 'n',
+ (tstat & Rts) != 0,
+ (fstat & Stop2) ? 2 : 1,
+
+ p->dev,
+ p->frame,
+ p->overrun,
+ uartrdreg(p, Istat) & Fenabd ? " fifo" : "",
+ (mstat & Cts) ? " cts" : "",
+ (mstat & Dsr) ? " dsr" : "",
+ (mstat & Dcd) ? " dcd" : "",
+ (mstat & Ringl) ? " ring" : ""
+ );
+ return readstr(offset, buf, n, str);
+}
+
+static long
+uartread(Chan *c, void *buf, long n, vlong off)
+{
+ Uart *p;
+ ulong offset = off;
+
+ if(c->qid.type & QTDIR){
+ setlength(-1);
+ return devdirread(c, buf, n, uartdir, 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 */
+ for(i = 0; i < 16 && qlen(p->oq); i++)
+ tsleep(&p->r, (int(*)(void*))qlen, p->oq, 125);
+
+ if(strncmp(cmd, "break", 5) == 0){
+ uartbreak(p, 0);
+ return;
+ }
+
+
+ n = atoi(cmd+1);
+ switch(*cmd){
+ case 'B':
+ case 'b':
+ uartsetbaud(p, n);
+ break;
+ case 'C':
+ case 'c':
+ uartdcdhup(p, n);
+ break;
+ case 'D':
+ case 'd':
+ uartdtr(p, n);
+ break;
+ case 'E':
+ case 'e':
+ uartdsrhup(p, n);
+ 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':
+ uartbits(p, n);
+ break;
+ case 'm':
+ case 'M':
+ uartmflow(p, n);
+ break;
+ case 'n':
+ case 'N':
+ qnoblock(p->oq, n);
+ break;
+ case 'P':
+ case 'p':
+ uartparity(p, *(cmd+1));
+ break;
+ case 'K':
+ case 'k':
+ uartbreak(p, n);
+ break;
+ case 'R':
+ case 'r':
+ uartrts(p, n);
+ break;
+ case 'Q':
+ case 'q':
+ qsetlimit(p->iq, n);
+ qsetlimit(p->oq, n);
+ break;
+ case 'W':
+ case 'w':
+ /* obsolete */
+ break;
+ case 'X':
+ case 'x':
+ ilock(&p->tlock);
+ p->xonoff = n;
+ iunlock(&p->tlock);
+ break;
+ }
+}
+
+static long
+uartwrite(Chan *c, void *buf, long n, vlong)
+{
+ Uart *p;
+ char cmd[32];
+
+ if(c->qid.type & QTDIR)
+ error(Eperm);
+
+ p = uart[NETID(c->qid.path)];
+
+ /*
+ * The fifo's turn themselves off sometimes.
+ * It must be something I don't understand. -- presotto
+ */
+ lock(&p->flock);
+ if((p->istat & Fenabd) == 0 && p->fifoon && p->nofifo == 0)
+ uartfifoon(p);
+ unlock(&p->flock);
+
+ 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(n == 0)
+ error(Eshortstat);
+ if(d.mode != ~0UL){
+ d.mode &= 0666;
+ dt[0].perm = dt[1].perm = d.mode;
+ }
+ return n;
+}
+
+Dev uartdevtab = {
+ 't',
+ "uart",
+
+ uartreset,
+ devinit,
+ devshutdown,
+ uartattach,
+ uartwalk,
+ uartstat,
+ uartopen,
+ devcreate,
+ uartclose,
+ uartread,
+ devbread,
+ uartwrite,
+ devbwrite,
+ devremove,
+ uartwstat,
+};
diff --git a/os/pxa/dma.c b/os/pxa/dma.c
new file mode 100644
index 00000000..9fcdbb4f
--- /dev/null
+++ b/os/pxa/dma.c
@@ -0,0 +1,244 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+#define DMAREGS ((Dmaregs*)PHYSDMA)
+typedef struct Dmadesc Dmadesc;
+typedef struct Dmaregs Dmaregs;
+
+struct Dmadesc {
+ ulong ddadr; /* next descriptor address (0 mod 16) */
+ ulong dsadr; /* source address (0 mod 8 if external, 0 mod 4 internal) */
+ ulong dtadr; /* target address (same) */
+ ulong dcmd; /* command */
+};
+
+struct Dmaregs {
+ ulong dcsr[16]; /* control and status */
+ uchar pad0[0xF0-0x40];
+ ulong dint; /* mask of interrupting channels: 0 is bit 0 */
+ uchar pad1[0x100-0xF4];
+ ulong drcmr[40];
+ Dmadesc chan[16]; /* offset 0x200 */
+};
+
+enum {
+ /* dcsr */
+ DcsRun= 1<<31, /* start the channel */
+ DcsNodesc= 1<<30, /* set if channel is in no-descriptor fetch mode */
+ DcsStopirq= 1<<29, /* enable interrupt if channel is uninitialised or stopped */
+ DcsReqpend= 1<<8, /* channel has pending request */
+ DcsStopstate= 1<<3, /* channel is uninitialised or stopped */
+ DcsEndintr= 1<<2, /* transaction complete, length now 0 */
+ DcsStartintr= 1<<1, /* successful descriptor fetch */
+ DcsBuserr= 1<<0, /* bus error */
+
+ /* drcmr */
+ DmrValid= 1<<7, /* mapped to channel given by bits 0-3 */
+ DmrChan= 0xF, /* channel number mask */
+
+ /* ddadr */
+ DdaStop= 1<<1, /* =0, run channel; =1, stop channel after this descriptor */
+
+ /* dcmd */
+ DcmIncsrc= 1<<31, /* increment source address after use */
+ DcmIncdest= 1<<30, /* increment destination address after use */
+ DcmFlowsrc= 1<<29, /* enable flow control on source */
+ DcmFlowdest= 1<<28, /* enable flow control on target */
+ DcmStartirq= 1<<22, /* interrupt when descriptor loaded (fetch mode) */
+ DcmEndirq= 1<<21, /* interrupt when transfer complete */
+ DcmEndian= 1<<18, /* must be zero (little endian) */
+ DcmBurst8= 1<<16, /* burst size in bytes */
+ DcmBurst16= 2<<16,
+ DcmBurst32= 3<<16,
+ DcmWidth0= 0<<14, /* width for external memory */
+ DcmWidth1= 1<<14, /* width of on-chip peripheral */
+ DcmWidth2= 2<<14,
+ DcmWidth4= 3<<14,
+ DcmLength= (1<<13)-1,
+
+ Ndma= 16, /* number of dma channels */
+ MaxDMAbytes= 8192-1, /* annoyingly small limit */
+};
+
+struct Dma {
+ int chan;
+ Dmadesc* desc;
+ Dmadesc stop;
+ ulong *csr;
+ void (*interrupt)(void*, ulong);
+ void* arg;
+ Rendez r;
+ ulong attrs; /* transfer attributes: flow control, burst size, width */
+};
+
+static struct {
+ Lock;
+ ulong avail;
+ Dma dma[Ndma];
+} dmachans;
+
+static void dmaintr(Ureg*, void*);
+
+void
+dmareset(void)
+{
+ int i;
+ Dma *d;
+
+ for(i=0; i<Ndma; i++){
+ dmachans.avail |= 1<<i;
+ d = &dmachans.dma[i];
+ d->chan = i;
+ d->csr = &DMAREGS->dcsr[i];
+ d->desc = &DMAREGS->chan[i];
+ d->stop.ddadr = (ulong)&d->stop | DdaStop;
+ d->stop.dcmd = 0;
+ }
+ intrenable(IRQ, IRQdma, dmaintr, nil, "dma");
+}
+
+/*
+ * allocate a DMA channel, reset it, and configure it for the given device
+ */
+Dma*
+dmasetup(int owner, void (*interrupt)(void*, ulong), void *arg, ulong attrs)
+{
+ Dma *d;
+ Dmadesc *dc;
+ int i;
+
+ ilock(&dmachans);
+ for(i=0; (dmachans.avail & (1<<i)) == 0; i++)
+ if(i >= Ndma){
+ iunlock(&dmachans);
+ return nil;
+ }
+ dmachans.avail &= ~(1<<i);
+ iunlock(&dmachans);
+
+ d = &dmachans.dma[i];
+ d->owner = owner;
+ d->interrupt = interrupt;
+ d->arg = arg;
+ d->attrs = attrs;
+ dc = d->desc;
+ dc->ddadr = (ulong)&d->stop | DdaStop; /* empty list */
+ dc->dcmd = 0;
+ *d->csr = DcsEndintr | DcsStartintr | DcsBuserr; /* clear status, stopped */
+ DMAREGS->drcmr[owner] = DmrValid | i;
+ return d;
+}
+
+void
+dmafree(Dma *dma)
+{
+ dmastop(dma);
+ DMAREGS->drcmr[d->owner] = 0;
+ ilock(&dmachans);
+ dmachans.avail |= 1<<dma->chan;
+ dma->interrupt = nil;
+ iunlock(&dmachans);
+}
+
+/*
+ * simple dma transfer on a channel, using `no fetch descriptor' mode.
+ * virtual buffer addresses are assumed to refer to contiguous physical addresses.
+ */
+int
+dmastart(Dma *dma, void *from, void *to, int nbytes)
+{
+ Dmadesc *dc;
+
+ if((ulong)nbytes > MaxDMAbytes)
+ panic("dmastart");
+ if((*dma->csr & DcsStopstate) == 0)
+ return 0; /* busy */
+ dc = dma->desc;
+ dc->ddadr = DdaStop;
+ dc->dsadr = PADDR(from);
+ dc->dtadr = PADDR(to);
+ dc->dcmd = dma->attrs | DcmEndirq | nbytes;
+ *dma->csr = DcsRun | DcsNodesc | DcsEndintr | DcsStartintr | DcsBuserr;
+ return 1;
+}
+
+/*
+ * stop dma on a channel
+ */
+void
+dmastop(Dma *dma)
+{
+ *dma->csr = 0;
+ while((*dma->csr & DcsStopstate) == 0)
+ ;
+ *dma->csr = DcsStopstate;
+}
+
+/*
+ * return nonzero if there was a memory error during DMA,
+ * and clear the error state
+ */
+int
+dmaerror(Dma *dma)
+{
+ ulong e;
+
+ e = *dma->csr & DcsBuserr;
+ *dma->csr |= e;
+ return e;
+}
+
+/*
+ * return nonzero if the DMA channel is not busy
+ */
+int
+dmaidle(Dma *d)
+{
+ return (*d->csr & DcsStopstate) == 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*)
+{
+ Dma *d;
+ Dmaregs *dr;
+ int i;
+ ulong s, csr;
+
+ dr = DMAREGS;
+ s = dr->dint;
+ dr->dint = s;
+ for(i=0; i<Ndma && s != 0; i++)
+ if(s & (1<<i)){
+ d = &dmachans.dma[i];
+ csr = *d->csr;
+ if(csr & DcsBuserr)
+ iprint("DMA error, chan %d status #%8.8lux\n", d->chan, csr);
+ *d->csr = csr & (DcsRun | DcsNodesc | DcsEndintr | DcsStartintr | DcsBuserr);
+ if(d->interrupt != nil)
+ d->interrupt(d->arg, csr);
+ else
+ wakeup(&d->r);
+ }
+}
diff --git a/os/pxa/etherif.h b/os/pxa/etherif.h
new file mode 100644
index 00000000..5c5c679b
--- /dev/null
+++ b/os/pxa/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/pxa/fpi.h b/os/pxa/fpi.h
new file mode 100644
index 00000000..dfb9b1df
--- /dev/null
+++ b/os/pxa/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/pxa/fpiarm.c b/os/pxa/fpiarm.c
new file mode 100644
index 00000000..4acfcd1d
--- /dev/null
+++ b/os/pxa/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/pxa/gpio.c b/os/pxa/gpio.c
new file mode 100644
index 00000000..5914f8c4
--- /dev/null
+++ b/os/pxa/gpio.c
@@ -0,0 +1,88 @@
+#include "u.h"
+#include "mem.h"
+#include "../port/lib.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+
+static ulong gpioreserved[3];
+static Lock gpiolock;
+
+void
+gpioreserve(int n)
+{
+ ulong mask, *r;
+
+ r = &gpioreserved[GPR(n)];
+ mask = GPB(n);
+ ilock(&gpiolock);
+ if(*r & mask)
+ panic("gpioreserve: duplicate use of GPIO %d", n);
+ *r |= mask;
+ iunlock(&gpiolock);
+}
+
+/*
+ * set direction and alternative function bits in the GPIO control register,
+ * following the configuration bits in cfg.
+ */
+void
+gpioconfig(int n, ulong cfg)
+{
+ GpioReg *g;
+ ulong o, m, *r;
+
+ m = GPB(n);
+ o = n>>5;
+ ilock(&gpiolock);
+ g = GPIOREG;
+ r = &g->gpdr[o];
+ if(cfg & Gpio_out)
+ *r |= m;
+ else
+ *r &= ~m;
+ r = &g->gafr[o*2];
+ *r = (*r & ~GPAF(n, 3)) | GPAF(n, cfg&3);
+ iunlock(&gpiolock);
+}
+
+ulong
+gpioget(int n)
+{
+ ulong mask, o;
+
+ mask = GPB(n);
+ o = GPR(n);
+ return GPIOREG->gplr[o] & mask;
+}
+
+void
+gpioset(int n, int v)
+{
+ GpioReg *g;
+ ulong mask, o;
+
+ g = GPIOREG;
+ mask = GPB(n);
+ o = GPR(n);
+ ilock(&gpiolock);
+ if(v)
+ g->gpsr[o] = mask;
+ else
+ g->gpcr[o] = mask;
+ iunlock(&gpiolock);
+}
+
+void
+gpiorelease(int n)
+{
+ ulong mask, *r;
+
+ mask = GPB(n);
+ r = &gpioreserved[GPR(n)];
+ ilock(&gpiolock);
+ if((*r & mask) != mask)
+ panic("gpiorelease: unexpected release of GPIO %d", n);
+ *r &= ~mask;
+ iunlock(&gpiolock);
+}
diff --git a/os/pxa/i2c.c b/os/pxa/i2c.c
new file mode 100644
index 00000000..b45542ae
--- /dev/null
+++ b/os/pxa/i2c.c
@@ -0,0 +1,561 @@
+/*
+ * basic read/write interface to PXA25x I⁲C bus (master mode)
+ * 7 bit addressing only.
+ * TO DO:
+ * - enable unit clock
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+#include "io.h"
+
+typedef struct Ctlr Ctlr;
+typedef struct I2Cregs I2Cregs;
+struct I2Cregs {
+ ulong ibmr; /* bus monitor */
+ ulong pad0;
+ ulong idbr; /* data buffer */
+ ulong pad1;
+ ulong icr; /* control */
+ ulong pad2;
+ ulong isr; /* status */
+ ulong pad3;
+ ulong isar; /* slave address */
+};
+
+enum {
+ /* ibmr */
+ Scls= 1<<1, /* SCL pin status */
+ Sdas= 1<<0, /* SDA pin status */
+
+ /* icr */
+ Fm= 1<<15, /* =0, 100 kb/sec; =1, 400 kb/sec */
+ Ur= 1<<14, /* reset the i2c unit only */
+ Sadie= 1<<13, /* slave address detected interrupt enable */
+ Aldie= 1<<12, /* arbitration loss detected interrupt enable (master mode) */
+ Ssdie= 1<<11, /* stop detected interrupt enable (slave mode) */
+ Beie= 1<<10, /* bus error interrupt enable */
+ Irfie= 1<<9, /* idbr receive full, interrupt enable */
+ Iteie= 1<<8, /* idbr transmit empty interrupt enable */
+ Gcd= 1<<7, /* disable response to general call message (slave); must be set if master uses g.c. */
+ Scle= 1<<6, /* SCL enable: enable clock output for master mode */
+ Iue= 1<<5, /* enable i2c (default: slave) */
+ Ma= 1<<4, /* master abort (send STOP without data) */
+ Tb= 1<<3, /* transfer byte on i2c bus */
+ Ack= 0<<2,
+ Nak= 1<<2,
+ Stop= 1<<1, /* send a stop */
+ Start= 1<<0, /* send a stop */
+
+ /* isr */
+ Bed= 1<<10, /* bus error detected */
+ Sad= 1<<9, /* slave address detected */
+ Gcad= 1<<8, /* general call address detected */
+ Irf= 1<<7, /* idbr receive full */
+ Ite= 1<<6, /* idbr transmit empty */
+ Ald= 1<<5, /* arbitration loss detected (multi-master) */
+ Ssd= 1<<4, /* slave stop detected */
+ Ibb= 1<<3, /* i2c bus is busy */
+ Ub= 1<<2, /* unit is busy (between start and stop) */
+ Nakrcv= 1<<1, /* nak received or sent a NAK */
+ Rwm= 1<<0, /* =0, master transmit (or slave receive); =1, master receive (or slave transmit) */
+ Err= Bed | Ssd,
+
+ /* isar address (0x7F bits) */
+
+ /* others */
+ Rbit = 1<<0, /* bit in address byte denoting read */
+ Wbit= 0<<0,
+
+ MaxIO = 8192, /* largest transfer done at once (can change) */
+ MaxSA= 2, /* largest subaddress; could be FIFOsize */
+ Bufsize = MaxIO, /* subaddress bytes don't go in buffer */
+ Freq = 0, /* set to Fm for high-speed */
+// I2Ctimeout = 125, /* msec (can change) */
+ I2Ctimeout = 10000, /* msec when Chatty */
+
+ Chatty = 0,
+};
+
+#define DPRINT if(Chatty)print
+
+/*
+ * I2C software structures
+ */
+
+struct Ctlr {
+ Lock;
+ QLock io;
+ int init;
+ int polling; /* eg, when running before system set up */
+ I2Cregs* regs; /* hardware registers */
+
+ /* controller state (see below) */
+ int status;
+ int phase;
+ Rendez r;
+
+ /* transfer parameters */
+ int addr;
+ int salen; /* bytes remaining of subaddress */
+ int offset; /* sub-addressed offset */
+ int cntl; /* everything but transfer length */
+ int rdcount; /* requested read transfer size */
+ Block* b;
+};
+
+enum {
+ /* Ctlr.state */
+ Idle,
+ Done,
+ Failed,
+ Busy,
+ Address,
+ Subaddress,
+ Read,
+ Write,
+ Halting,
+};
+
+static Ctlr i2cctlr[1];
+
+static void interrupt(Ureg*, void*);
+static int readyxfer(Ctlr*, int);
+static void rxstart(Ctlr*);
+static void txstart(Ctlr*);
+static void stopxfer(Ctlr*);
+static void txoffset(Ctlr*, ulong, int);
+static int idlectlr(Ctlr*);
+
+static void
+i2cdump(char *t, I2Cregs *i2c)
+{
+ iprint("i2c %s: ibmr=%.4lux icr=%.4lux isr=%.4lux\n", t, i2c->ibmr, i2c->icr, i2c->isr);
+}
+
+static void
+initialise(I2Cregs *i2c, int eintr)
+{
+ int ctl;
+
+ /* initialisation (see p. 9-11 on) */
+ i2c->isar = 0;
+ ctl = Freq | Gcd | Scle | Iue;
+ if(eintr)
+ ctl |= Beie | Irfie; /* Iteie set by txstart */
+ i2c->icr = ctl;
+ if(Chatty)
+ iprint("ctl=%4.4ux icr=%4.4lux\n", ctl, i2c->icr);
+}
+
+/*
+ * called by the reset routine of any driver using the IIC
+ */
+void
+i2csetup(int polling)
+{
+ I2Cregs *i2c;
+ Ctlr *ctlr;
+
+ ctlr = i2cctlr;
+ ctlr->polling = polling;
+ i2c = KADDR(PHYSI2C);
+ ctlr->regs = i2c;
+ if(!polling){
+ if(ctlr->init == 0){
+ initialise(i2c, 1);
+ ctlr->init = 1;
+ intrenable(IRQ, IRQi2c, interrupt, i2cctlr, "i2c");
+ if(Chatty)
+ i2cdump("init", i2c);
+ }
+ }else
+ initialise(i2c, 0);
+}
+
+static void
+done(Ctlr *ctlr)
+{
+ ctlr->phase = Done;
+ wakeup(&ctlr->r);
+}
+
+static void
+failed(Ctlr *ctlr)
+{
+ ctlr->phase = Failed;
+ wakeup(&ctlr->r);
+}
+
+static void
+interrupt(Ureg*, void *arg)
+{
+ int sts, idl;
+ Ctlr *ctlr;
+ Block *b;
+ I2Cregs *i2c;
+ char xx[12];
+
+ ctlr = arg;
+ i2c = ctlr->regs;
+ idl = (i2c->ibmr & 3) == 3;
+ if(Chatty && ctlr->phase != Read && ctlr->phase != Write){
+ snprint(xx, sizeof(xx), "intr %d", ctlr->phase);
+ i2cdump(xx, i2c);
+ }
+ sts = i2c->isr;
+ if(sts & (Bed | Sad | Gcad | Ald))
+ iprint("i2c: unexpected status: %.4ux", sts);
+ i2c->isr = sts;
+ ctlr->status = sts;
+ i2c->icr &= ~(Start | Stop | Nak | Ma | Iteie);
+ if(sts & Err){
+ failed(ctlr);
+ return;
+ }
+ switch(ctlr->phase){
+ default:
+ iprint("i2c: unexpected interrupt: p-%d s=%.4ux\n", ctlr->phase, sts);
+ break;
+
+ case Halting:
+ ctlr->phase = Idle;
+ break;
+
+ case Subaddress:
+ if(ctlr->salen){
+ /* push out next byte of subaddress */
+ ctlr->salen -= 8;
+ i2c->idbr = ctlr->offset >> ctlr->salen;
+ i2c->icr |= Aldie | Tb | Iteie;
+ break;
+ }
+ /* subaddress finished */
+ if(ctlr->cntl & Rbit){
+ /* must readdress if reading to change mode */
+ i2c->idbr = (ctlr->addr << 1) | Rbit;
+ i2c->icr |= Start | Tb | Iteie;
+ ctlr->phase = Address; /* readdress */
+ break;
+ }
+ /* FALL THROUGH if writing */
+ case Address:
+ /* if not sub-addressed, rxstart/txstart */
+ if(ctlr->cntl & Rbit)
+ rxstart(ctlr);
+ else
+ txstart(ctlr);
+ break;
+
+ case Read:
+ b = ctlr->b;
+ if(b == nil)
+ panic("i2c: no buffer");
+ /* master receive: next byte */
+ if(sts & Irf){
+ ctlr->rdcount--;
+ if(b->wp < b->lim)
+ *b->wp++ = i2c->idbr;
+ }
+ if(ctlr->rdcount <= 0 || sts & Nakrcv || idl){
+ if(Chatty)
+ iprint("done: %.4ux\n", sts);
+ done(ctlr);
+ break;
+ }
+ rxstart(ctlr);
+ break;
+
+ case Write:
+ b = ctlr->b;
+ if(b == nil)
+ panic("i2c: no buffer");
+ /* account for data transmitted */
+ if(BLEN(b) <= 0 || sts & Nakrcv){
+ done(ctlr);
+ break;
+ }
+ txstart(ctlr);
+ break;
+ }
+}
+
+static int
+isdone(void *a)
+{
+ return ((Ctlr*)a)->phase < Busy;
+}
+
+static int
+i2cerror(char *s)
+{
+ DPRINT("i2c error: %s\n", s);
+ if(up)
+ error(s);
+ /* no current process, don't call error */
+ return -1;
+}
+
+static char*
+startxfer(I2Cdev *d, int op, Block *b, int n, ulong offset)
+{
+ I2Cregs *i2c;
+ Ctlr *ctlr;
+ int i, p, s;
+
+ ctlr = i2cctlr;
+ if(up){
+ qlock(&ctlr->io);
+ if(waserror()){
+ qunlock(&ctlr->io);
+ nexterror();
+ }
+ }
+ ilock(ctlr);
+ if(!idlectlr(ctlr)){
+ iunlock(ctlr);
+ if(up)
+ error("bus confused");
+ return "bus confused";
+ }
+ if(ctlr->phase >= Busy)
+ panic("i2c: ctlr busy");
+ ctlr->cntl = op;
+ ctlr->b = b;
+ ctlr->rdcount = n;
+ ctlr->addr = d->addr;
+ i2c = ctlr->regs;
+ ctlr->salen = d->salen*8;
+ ctlr->offset = offset;
+ if(ctlr->salen){
+ ctlr->phase = Subaddress;
+ op = Wbit;
+ }else
+ ctlr->phase = Address;
+ i2c->idbr = (d->addr<<1) | op; /* 7-bit address + R/nW */
+ i2c->icr |= Start | Tb | Iteie;
+ if(Chatty)
+ i2cdump("start", i2c);
+ iunlock(ctlr);
+
+ /* wait for it */
+ if(ctlr->polling){
+ for(i=0; !isdone(ctlr); i++){
+ delay(2);
+ interrupt(nil, ctlr);
+ }
+ }else
+ tsleep(&ctlr->r, isdone, ctlr, I2Ctimeout);
+
+ ilock(ctlr);
+ p = ctlr->phase;
+ s = ctlr->status;
+ ctlr->b = nil;
+ if(ctlr->phase != Done && ctlr->phase != Idle)
+ stopxfer(ctlr);
+ iunlock(ctlr);
+
+ if(up){
+ poperror();
+ qunlock(&ctlr->io);
+ }
+ if(p != Done || s & (Bed|Ald)){ /* CHECK; time out */
+ if(s & Ald)
+ return "i2c lost arbitration";
+ if(s & Bed)
+ return "i2c bus error";
+ if(s & Ssd)
+ return "i2c transfer aborted"; /* ?? */
+ if(0 && p != Done)
+ return "i2c timed out";
+ sprint(up->genbuf, "i2c error: phase=%d status=%.4ux", p, s);
+ return up->genbuf;
+ }
+ return nil;
+}
+
+long
+i2csend(I2Cdev *d, void *buf, long n, ulong offset)
+{
+ Block *b;
+ char *e;
+
+ if(n <= 0)
+ return 0;
+ if(n > MaxIO)
+ n = MaxIO;
+
+ if(up){
+ b = allocb(n);
+ if(b == nil)
+ error(Enomem);
+ if(waserror()){
+ freeb(b);
+ nexterror();
+ }
+ }else{
+ b = iallocb(n);
+ if(b == nil)
+ return -1;
+ }
+ memmove(b->wp, buf, n);
+ b->wp += n;
+ e = startxfer(d, 0, b, 0, offset);
+ if(up)
+ poperror();
+ n -= BLEN(b); /* residue */
+ freeb(b);
+ if(e)
+ return i2cerror(e);
+ return n;
+}
+
+long
+i2crecv(I2Cdev *d, void *buf, long n, ulong offset)
+{
+ Block *b;
+ long nr;
+ char *e;
+
+ if(n <= 0)
+ return 0;
+ if(n > MaxIO)
+ n = MaxIO;
+
+ if(up){
+ b = allocb(n);
+ if(b == nil)
+ error(Enomem);
+ if(waserror()){
+ freeb(b);
+ nexterror();
+ }
+ }else{
+ b = iallocb(n);
+ if(b == nil)
+ return -1;
+ }
+ e = startxfer(d, Rbit, b, n, offset);
+ nr = BLEN(b);
+ if(nr > 0)
+ memmove(buf, b->rp, nr);
+ if(up)
+ poperror();
+ freeb(b);
+ if(e)
+ return i2cerror(e);
+ return nr;
+}
+
+/*
+ * the controller must be locked for the following functions
+ */
+
+static int
+readyxfer(Ctlr *ctlr, int phase)
+{
+ I2Cregs *i2c;
+
+ i2c = ctlr->regs;
+ if((i2c->isr & Bed) != 0){
+ failed(ctlr);
+ return 0;
+ }
+ ctlr->phase = phase;
+ return 1;
+}
+
+/*
+ * start a master transfer to receive the next byte of data
+ */
+static void
+rxstart(Ctlr *ctlr)
+{
+ Block *b;
+ int cntl;
+
+ b = ctlr->b;
+ if(b == nil || ctlr->rdcount<= 0){
+ done(ctlr);
+ return;
+ }
+ if(!readyxfer(ctlr, Read))
+ return;
+ cntl = Aldie | Tb;
+ if(ctlr->rdcount == 1)
+ cntl |= Stop | Nak | Iteie; /* last byte of transfer */
+ ctlr->regs->icr |= cntl;
+}
+
+/*
+ * start a master transfer to send the next chunk of data
+ */
+static void
+txstart(Ctlr *ctlr)
+{
+ Block *b;
+ int cntl;
+ long nb;
+ I2Cregs *i2c;
+
+ b = ctlr->b;
+ if(b == nil || (nb = BLEN(b)) <= 0){
+ done(ctlr);
+ return;
+ }
+ if(!readyxfer(ctlr, Write))
+ return;
+ i2c = ctlr->regs;
+ i2c->idbr = *b->rp++;
+ cntl = Aldie | Tb | Iteie;
+ if(nb == 1)
+ cntl |= Stop;
+ i2c->icr |= cntl;
+}
+
+/*
+ * stop a transfer if one is in progress
+ */
+static void
+stopxfer(Ctlr *ctlr)
+{
+ I2Cregs *i2c;
+
+ i2c = ctlr->regs;
+ if((i2c->isr & Ub) == 0){
+ ctlr->phase = Idle;
+ return;
+ }
+ if((i2c->isr & Ibb) == 0 && ctlr->phase != Halting){
+ ctlr->phase = Halting; /* interrupt will clear the state */
+ i2c->icr |= Ma;
+ }
+ /* if that doesn't clear it by the next operation, idlectlr will do so below */
+}
+
+static int
+idlectlr(Ctlr *ctlr)
+{
+ I2Cregs *i2c;
+
+ i2c = ctlr->regs;
+ if((i2c->isr & Ibb) == 0){
+ if((i2c->isr & Ub) == 0){
+ ctlr->phase = Idle;
+ return 1;
+ }
+ iprint("i2c: bus free, ctlr busy: isr=%.4lux icr=%.4lux\n", i2c->isr, i2c->icr);
+ }
+ /* hit it with the hammer, soft reset */
+ iprint("i2c: soft reset\n");
+ i2c->icr = Ur;
+ iunlock(ctlr);
+ delay(1);
+ ilock(ctlr);
+ initialise(i2c, !ctlr->polling);
+ ctlr->phase = Idle;
+ return (i2c->isr & (Ibb | Ub)) == 0;
+}
diff --git a/os/pxa/l.s b/os/pxa/l.s
new file mode 100644
index 00000000..df246a04
--- /dev/null
+++ b/os/pxa/l.s
@@ -0,0 +1,548 @@
+#include "mem.h"
+
+#define CPWAIT MRC CpMMU, 0, R2, C(2), C(0), 0; MOVW R2, R2; SUB $4, R15
+
+/*
+ * 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)
+ CPWAIT
+ RET
+
+TEXT mmuputfsr(SB), $-4
+ MCR CpMMU, 0, R0, C(CpFSR), C(0)
+ CPWAIT
+ RET
+
+TEXT mmuputttb(SB), $-4
+ MCR CpMMU, 0, R0, C(CpTTB), C(0)
+ CPWAIT
+ RET
+
+TEXT mmuputctl(SB), $-4
+ MCR CpMMU, 0, R0, C(CpControl), C(0)
+ CPWAIT
+ RET
+
+TEXT tlbinvalidateall(SB), $-4
+ MCR CpMMU, 0, R0, C(CpTLBops), C(7)
+ CPWAIT
+ RET
+
+TEXT itlbinvalidate(SB), $-4
+ MCR CpMMU, 0, R0, C(CpTLBops), C(5), 1
+ CPWAIT
+ RET
+
+TEXT dtlbinvalidate(SB), $-4
+ MCR CpMMU, 0, R0, C(CpTLBops), C(6), 1
+ CPWAIT
+ RET
+
+TEXT mmuenable(SB), $-4
+
+ MOVW $1, R1
+ MCR CpMMU, 0, R1, C(CpDAC), C(3) /* set domain 0 to client */
+
+ /* disable and invalidate all caches and TLB's before (re-)enabling MMU */
+ MOVW $(CpCwpd | CpCsystem), R1
+ MRC CpMMU, 0, R1, C(CpControl), C(0)
+ CPWAIT
+
+ MOVW $0, R1 /* disable everything */
+ MCR CpMMU, 0, R1, C(CpCacheCtl), C(7), 0 /* invalidate I&D Caches and BTB */
+ MCR CpMMU, 0, R1, C(CpCacheCtl), C(10), 4 /* drain write and fill buffer */
+ MCR CpMMU, 0, R1, C(CpTLBops), C(7), 0 /* invalidate I&D TLB */
+
+ /* enable desired mmu mode (R0) */
+ MCR CpMMU, 0, R0, C(1), C(0)
+ CPWAIT
+ 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 (invalidate) the whole icache
+ */
+TEXT icflushall(SB), $-4
+_icflushall:
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(5), 0 /* clean i-cache and branch buffer */
+ CPWAIT
+ RET
+
+/*
+ * invalidate part of i-cache and invalidate branch target buffer
+ */
+TEXT icflush(SB), $-4
+ MOVW 4(FP), R1
+ CMP $(CACHESIZE/2), R1
+ BGE _icflushall
+ ADD R0, R1
+ BIC $(CACHELINESZ-1), R0
+icflush1:
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(5), 1 /* clean entry */
+ ADD $CACHELINESZ, R0
+ CMP R1, R0
+ BLO icflush1
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(5), 6 /* invalidate branch target buffer */
+ CPWAIT
+ RET
+
+/*
+ * write back whole data cache and drain write buffer
+ */
+TEXT dcflushall(SB), $-4
+_dcflushall:
+ MOVW $(DCFADDR), R0
+ ADD $CACHESIZE, R0, R1
+dcflushall1:
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(2), 5 /* allocate line */
+ ADD $CACHELINESZ, R0
+ CMP R1,R0
+ BNE dcflushall1
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(10), 4 /* drain write buffer */
+ CPWAIT
+ RET
+
+/*
+ * write back a given region and drain write buffer
+ */
+TEXT dcflush(SB), $-4
+ MOVW 4(FP), R1
+ CMP $(CACHESIZE/2), 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 */
+ CPWAIT
+ RET
+
+#ifdef NOTYET
+/*
+ * write back mini data cache
+ * TO DO: need to allocate pair of unused 2k blocks and read them alternately
+ */
+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 */
+ CPWAIT
+ RET
+#endif
+
+/*
+ * invalidate data caches (main and mini)
+ */
+TEXT dcinval(SB), $-4
+ MCR CpMMU, 0, R0, C(CpCacheCtl), C(6), 0
+ CPWAIT
+ RET
+
+/* for devboot */
+TEXT gotopc(SB), $-4
+ MOVW R0, R1
+ MOVW $0, R0
+ MOVW R1, PC
+ RET
+
+TEXT idle(SB), $-4
+ MOVW $1, R0 /* idle mode */
+ MCR CpPWR, 0, R0, C(7), C(0), 0
+ RET
+
+TEXT getcclkcfg(SB), $-4
+ MRC CpPWR, 0, R0, C(6), C(0), 0
+ RET
+
+TEXT putcclkcfg(SB), $-4
+ MCR CpPWR, 0, R0, C(6), C(0), 0
+ RET
+
+#ifdef NOTYET
+/*
+ * 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
+#endif
+
+ GLOBL power_state+0(SB), $4
+
+#ifdef YYY
+/* for debugging sleep code: */
+TEXT fastreset(SB), $-4
+ MOVW $PHYSRESET, R7
+ MOVW $1, R1
+ MOVW R1, (R7)
+ RET
+#endif
diff --git a/os/pxa/mmu.c b/os/pxa/mmu.c
new file mode 100644
index 00000000..52326237
--- /dev/null
+++ b/os/pxa/mmu.c
@@ -0,0 +1,252 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+/*
+ * Small pages:
+ * L1: 12-bit index -> 4096 descriptors -> 16Kb
+ * L2: 8-bit index -> 256 descriptors -> 1Kb
+ * Each L2 descriptor has access permissions for 4 1Kb sub-pages.
+ *
+ * TTB + L1Tx gives address of L1 descriptor
+ * L1 descriptor gives PTBA
+ * PTBA + L2Tx gives address of L2 descriptor
+ * L2 descriptor gives PBA
+ *
+ * on the Xscale, X, C & B are interpreted as follows:
+ * X = 0:
+ * C=0 B=0 uncached, unbuffered, stall until data access complete
+ * C=0 B=1 uncached, buffered (different from Strongarm)
+ * C=1 B=0 cached, buffered, write through, read allocate (different from Strongarm)
+ * C=1 B=1 cached, buffered, write back, read allocate
+ * X = 1:
+ * C=0 B=0 undefined
+ * C=0 B=1 uncached, buffered, writes will not coalesce
+ * C=1 B=0 mini data cache (policy set by auxiliary control reg)
+ * C=1 B=1 cached, buffered, write back, read/write allocate
+ * and the i-cache uses only the C bit (cached if non-zero).
+ */
+#define TTB(pa) ((pa) & ~0x3FFF) /* translation table base */
+#define L1x(pa) (((pa)>>20) & 0xFFF) /* L1 table index */
+#define PTBA(pa) ((pa) & ~0x3FF) /* page table base address */
+#define L2x(pa) (((pa)>>12) & 0xFF) /* L2 table index */
+#define PBA(pa) ((pa) & ~0xFFF) /* page base address */
+#define SBA(pa) ((pa) & ~0xFFFFF) /* section base address */
+
+enum {
+ /* sizes */
+ Section= 1<<20,
+ LargePage= 1<<16,
+ SmallPage= 1<<12,
+ EsmallPage= 1<<10,
+ SectionPages = Section/SmallPage,
+ PtAlign = 1<<10,
+
+ /* L1 descriptor format */
+ L1type= 3<<0, /* mask for type */
+ L1page= 1<<0, /* descriptor is for L2 pages */
+ L1section= 2<<0, /* descriptor is for section */
+ L1fpage= 3<<0, /* descriptor is for fine (1k) L2 pages */
+ L1buffered= 1<<2,
+ L1cached= 1<<3,
+ L1P= 1<<9, /* application-processor specific */
+ L1sectionX= 1<<12, /* X bit in section descriptor */
+ L1minicache= (L1sectionX | L1cached),
+
+ /* L2 descriptor format for coarse page table */
+ L2type= 3<<0, /* mask for type */
+ L2invalid= 0<<0,
+ L2large= 1<<0, /* large page */
+ L2small= 2<<0, /* small page */
+ L2esmall= 3<<0, /* extended small page */
+ L2buffered= 1<<2,
+ L2cached= 1<<3,
+ /* then access permissions */
+ L2smallX= 1<<6,
+ L2largeX= 1<<12,
+
+ /* domains */
+ Dnone= 0,
+ Dclient= 1,
+ Dmanager= 3,
+
+ /* access permissions */
+ APsro= 0, /* supervisor ro if S|R */
+ APsrw= 1, /* supervisor rw */
+ APuro= 2, /* supervisor rw + user ro */
+ APurw= 3, /* supervisor rw + user rw */
+
+ MINICACHED = 0x10000000,
+};
+
+#define L1dom(d) (((d) & 0xF)<<5) /* L1 domain */
+#define AP(i, v) ((v)<<(((i)*2)+4)) /* access permissions */
+#define L1AP(v) AP(3, (v))
+#define L2AP(v) AP(3, (v))|AP(2, (v))|AP(1, (v))|AP(0, (v))
+
+#define L1krw (L1AP(APsrw) | L1dom(0))
+
+/*
+ * 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 = L1x((ulong)v);
+ ttb = (ulong*)KTTB;
+ ste = ttb[idx];
+ switch(ste & L1type) {
+ case L1section:
+ return SBA(ste)|((ulong)v & 0x000fffff);
+ case L1page:
+ pte = ((ulong *)PTBA(ste))[L2x((ulong)v)];
+ switch(pte & 3) {
+ case L2large:
+ return (pte & 0xffff0000)|((ulong)v & 0x0000ffff);
+ case L2small:
+ return (pte & 0xfffff000)|((ulong)v & 0x00000fff);
+ }
+ }
+ return 0;
+}
+
+/* for debugging */
+void
+prs(char *s)
+{
+ for(; *s; s++)
+ uartputc(*s);
+}
+
+void
+pr16(ulong n)
+{
+ int i, c;
+
+ for(i=28; i>=0; i-=4){
+ c = (n>>i) & 0xF;
+ if(c >= 0 && c <= 9)
+ c += '0';
+ else
+ c += 'A'-10;
+ uartputc(c);
+ }
+}
+
+void
+xdelay(int n)
+{
+ int j;
+
+ for(j=0; j<1000000/4; j++)
+ n++;
+ USED(n);
+}
+
+void*
+mmuphysmap(ulong phys, ulong)
+{
+ ulong *ttb;
+ void *va;
+
+ ttb = (ulong*)KTTB;
+ va = KADDR(phys);
+ ttb[L1x((ulong)va)] = phys | L1krw | L1section;
+ 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,
+ * map flash to virtual space away from 0,
+ * disable access to 0 (nil pointers).
+ *
+ * Other section maps are added later as required by mmuphysmap.
+ */
+void
+mmuinit(void)
+{
+ int i;
+ ulong *ttb, *ptable, va;
+
+ ttb = (ulong*)KTTB;
+ for(i=0; i<L1x(0x10000000); i++)
+ ttb[i] = 0;
+ for(; i < 0x1000; i++)
+ ttb[i] = (i<<20) | L1krw | L1section;
+
+ /* cached dram at normal kernel addresses */
+ for(va = KZERO; va < KZERO+64*MB; va += MB)
+ ttb[L1x(va)] = va | L1krw | L1section | L1cached | L1buffered;
+
+ /* aliases for uncached dram */
+ for(i = 0; i < 64*MB; i += MB)
+ ttb[L1x(UCDRAMZERO+i)] = (PHYSMEM0+i) | L1krw | L1section;
+
+ /* TO DO: make the text read only */
+
+ /* minicached region; used for frame buffer (if present) */
+ if(0)
+ for(va = KZERO; va < KZERO+64*MB; va += MB)
+ ttb[L1x(va|MINICACHED)] = va | L1krw | L1minicache | L1section;
+
+ ttb[L1x(DCFADDR)] |= L1cached | L1buffered; /* cached and buffered for cache writeback */
+
+#ifdef NOTYET
+ /* TO DO: mini cache writeback */
+ ttb[L1x(MCFADDR)] |= L1minicache; /* cached and unbuffered for minicache writeback */
+#endif
+
+ /* remap flash */
+ for(i=0; i<32*MB; i+=MB)
+ ttb[L1x(FLASHMEM+i)] = (PHYSFLASH0+i) | L1krw | L1section; /* 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[L2x(AIVECADDR)] = PADDR(page0) | L2AP(APsrw) | L2cached | L2buffered | L2small;
+ ttb[L1x(AIVECADDR)] = PADDR(ptable) | L1page;
+ mmuputttb(KTTB);
+ mmuputdac(Dclient);
+ mmuenable(CpCaltivec | CpCIcache | CpCsystem | CpCwpd | 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);
+ icflush(a, n);
+ return 0;
+}
+
+#ifdef NOTYET
+/*
+ * 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);
+}
+#endif
diff --git a/os/pxa/pxaio.h b/os/pxa/pxaio.h
new file mode 100644
index 00000000..d7f40a34
--- /dev/null
+++ b/os/pxa/pxaio.h
@@ -0,0 +1,383 @@
+typedef struct I2Cdev I2Cdev;
+typedef struct PCMconftab PCMconftab;
+typedef struct PCMmap PCMmap;
+typedef struct PCMslot PCMslot;
+
+#define INTRREG ((IntrReg*)PHYSINTR)
+typedef struct IntrReg IntrReg;
+struct IntrReg {
+ ulong icip; /* IRQ pending */
+ ulong icmr; /* mask */
+ ulong iclr; /* level */
+ ulong icfp; /* FIQ pending */
+ ulong icpr; /* pending */
+ ulong iccr; /* control */
+};
+
+/*
+ * types of interrupts
+ */
+enum
+{
+ GPIOrising,
+ GPIOfalling,
+ GPIOboth,
+ IRQ,
+};
+
+enum {
+ /* first-level interrupts (table 4-36) */
+ IRQrtc= 31,
+ IRQhz= 30,
+ IRQtimer3= 29,
+ IRQtimer2= 28,
+ IRQtimer1= 27,
+ IRQtimer0= 26,
+ IRQdma= 25,
+ IRQssp= 24,
+ IRQmmc= 23,
+ IRQffuart= 22,
+ IRQbtuart= 21,
+ IRQstuart= 20,
+ IRQicp= 19,
+ IRQi2c= 18,
+ IRQlcd= 17,
+ IRQnssp= 16,
+ IRQac97= 14,
+ IRQi2s= 13,
+ IRQpmu= 12,
+ IRQusb= 11,
+ IRQgpio= 10,
+ IRQgpio1= 9,
+ IRQgpio0= 8,
+ IRQhwuart= 7,
+};
+
+#define GPIOREG ((GpioReg*)PHYSGPIO)
+typedef struct GpioReg GpioReg;
+struct GpioReg {
+ ulong gplr[3];
+ ulong gpdr[3];
+ ulong gpsr[3];
+ ulong gpcr[3];
+ ulong grer[3];
+ ulong gfer[3];
+ ulong gedr[3];
+ ulong gafr[6];
+};
+
+enum {
+ /* GPIO alternative functions if gafr bits set (see table 4-1, pp. 4-3 to 4-6) */
+ GPIO_GP_RST_1_i= 1, /* active low GP_reset */
+ GPIO_FFRXD_1_i= 34, /* FFUART receive */
+ GPIO_FFTXD_2_o= 39, /* FFUART transmit */
+
+ MaxGPIObit= 84,
+ MaxGPIOIRQ= 1,
+};
+#define GPB(n) (1<<((n)&31))
+#define GPR(n) ((n)>>5)
+#define GPAF(n,v) ((v)<<(((n)&15)*2))
+
+void gpioreserve(int);
+void gpioconfig(int, ulong);
+ulong gpioget(int);
+void gpioset(int, int);
+void gpiorelease(int);
+
+enum {
+ /* software configuration bits for gpioconfig */
+ Gpio_gpio= 0<<0,
+ Gpio_Alt1= 1<<0,
+ Gpio_Alt2= 2<<0,
+ Gpio_Alt3= 3<<0,
+ Gpio_in= 1<<2,
+ Gpio_out= 1<<3,
+};
+
+/*
+ * software structures used by ../port/devi2c.c and iic.c
+ */
+struct I2Cdev {
+ int addr;
+ int salen; /* length in bytes of subaddress, if used; 0 otherwise */
+ int tenbit; /* 10-bit addresses */
+};
+
+long i2crecv(I2Cdev*, void*, long, ulong);
+long i2csend(I2Cdev*, void*, long, ulong);
+void i2csetup(int);
+
+#define COREREG ((Coreregs*)PHYSCORE)
+typedef struct Coreregs Coreregs;
+struct Coreregs {
+ ulong cccr; /* core clock config */
+ ulong cken; /* clock enable */
+ ulong oscc; /* oscillator configuration */
+};
+
+#define RTCREG ((RTCreg*)PHYSRTC)
+typedef struct RTCreg RTCreg;
+struct RTCreg {
+ ulong rcnr; /* count */
+ ulong rtar; /* alarm */
+ ulong rtsr; /* status */
+ ulong rttr; /* trim */
+};
+
+#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 prer; /* rising-edge detect enable */
+ ulong pfer; /* falling-edge detect enable */
+ ulong pedr; /* GPIO edge detect status */
+ ulong pcfr; /* general configuration */
+ ulong pgsr[3]; /* GPIO sleep state */
+ ulong rsvd;
+ ulong rcsr; /* reset controller status register */
+};
+
+enum {
+ /* pp. 3-25 to 3-31 */
+ PWER_rtc = 1<<31, /* wakeup by RTC alarm */
+ PWER_we0 = 1<<0, /* wake-up on GP0 edge detect */
+
+ PSSR_sss = 1<<0, /* software sleep status */
+ PSSR_bfs = 1<<1, /* battery fault status */
+ PSSR_vfs = 1<<2, /* VDD fault status */
+ PSSR_ph = 1<<4, /* peripheral control hold */
+ PSSR_rdh = 1<<5, /* read disable hold */
+
+ PMFW_fwake= 1<<1, /* fast wakeup enable (no power stabilisation delay) */
+
+ RSCR_gpr= 1<<3, /* gpio reset has occurred */
+ RSCR_smr= 1<<2, /* sleep mode has occurred */
+ RSCR_wdr= 1<<1, /* watchdog reset has occurred */
+ RSCR_hwr= 1<<0, /* hardware reset has occurred */
+};
+
+#define MEMCFGREG ((MemcfgReg*)PHYSMEMCFG)
+typedef struct MemcfgReg MemcfgReg;
+struct MemcfgReg {
+ ulong mdcnfg; /* SDRAM config */
+ ulong mdrefr; /* dram refresh */
+ ulong msc0; /* static memory or devices */
+ ulong msc1;
+ ulong msc2; /* static memory or devices */
+ ulong mecr; /* expansion bus (pcmcia, CF) */
+ ulong sxcnfg; /* synchronous static memory control */
+ ulong sxmrs; /* MRS value to write to SMROM */
+ ulong mcmem0; /* card interface socket 0 memory timing */
+ ulong mcmem1; /* card interface socket 1 memory timing */
+ ulong mcatt0; /* socket 0 attribute memory timing */
+ ulong mcatt1; /* socket 1 attribute memory timing */
+ ulong mcio0; /* socket 0 i/o timing */
+ ulong mcio1; /* socket 1 i/o timing */
+ ulong mdmrs; /* MRS value to write to SDRAM */
+ ulong boot_def; /* read-only boot-time register */
+ ulong mdmrslp; /* low-power SDRAM mode register set config */
+ ulong sa1111cr; /* SA1111 compatibility */
+};
+
+#define LCDREG ((LcdReg*)PHYSLCD)
+typedef struct LcdReg LcdReg;
+struct LcdReg {
+ ulong lccr0; /* control 0 */
+ ulong lccr1; /* control 1 */
+ ulong lccr2; /* control 2 */
+ ulong lccr3; /* control 3 */
+ struct {
+ ulong fdadr; /* dma frame descriptor address register */
+ ulong fsadr; /* dma frame source address register */
+ ulong fidr; /* dma frame ID register */
+ ulong ldcmd; /* dma command */
+ } frame[2];
+ ulong fbr[2]; /* frame branch register */
+ ulong lcsr; /* status */
+ ulong liidr; /* interrupt ID register */
+ ulong trgbr; /* TMED RGB seed register */
+ ulong tcr; /* TMED control register */
+};
+
+#define USBREG ((UsbReg*)PHYSUSB)
+typedef struct UsbReg UsbReg;
+struct UsbReg {
+ ulong udccr; /* control */
+ ulong udccs[16]; /* endpoint control/status */
+ ulong ufnrh; /* frame number high */
+ ulong ufnrl; /* frame number low */
+ ulong udbcr2;
+ ulong udbcr4;
+ ulong udbcr7;
+ ulong udbcr9;
+ ulong udbcr12;
+ ulong udbcr14;
+ ulong uddr[16]; /* endpoint data */
+ ulong uicr0;
+ ulong uicr1;
+ ulong usir0;
+ ulong usir1;
+};
+
+enum {
+ /* DMA configuration parameters */
+
+ /* DMA Direction */
+ DmaOut= 0,
+ DmaIn= 1,
+
+ /* dma devices */
+ DmaDREQ0= 0,
+ DmaDREQ1,
+ DmaI2S_i,
+ DmaI2S_o,
+ DmaBTUART_i,
+ DmaBTUART_o,
+ DmaFFUART_i,
+ DmaFFUART_o,
+ DmaAC97mic,
+ DmaAC97modem_i,
+ DmaAC97modem_o,
+ DmaAC97audio_i,
+ DmaAC97audio_o,
+ DmaSSP_i,
+ DmaSSP_o,
+ DmaNSSP_i,
+ DmaNSSP_o,
+ DmaICP_i,
+ DmaICP_o,
+ DmaSTUART_i,
+ DmaSTUART_o,
+ DmaMMC_i,
+ DmaMMC_o,
+ DmaRsvd0,
+ DmaRsvd1,
+ DmaUSB1,
+ DmaUSB2,
+ DmaUSB3,
+ DmaUSB4,
+ DmaHWUART_i,
+ DmaUSB6,
+ DmaUSB7,
+ DmaUSB8,
+ DmaUSB9,
+ DmaHWUART_o,
+ DmaUSB11,
+ DmaUSB12,
+ DmaUSB13,
+ DmaUSB14,
+ DmaRsvd2,
+};
+
+/*
+ * Interface to platform-specific PCMCIA signals, in arch*.c
+ */
+enum {
+ /* argument to pcmpin() */
+ PCMready,
+ PCMeject,
+ PCMstschng,
+};
+
+/*
+ * 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 */
+
+/*
+ * 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 */
+ uchar nlines;
+ struct {
+ ulong start;
+ ulong len;
+ } io[16];
+ int nio;
+ uchar vcc;
+ 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 */
+ uchar cpresent; /* config registers present */
+ ulong caddr; /* relative address of config registers */
+ 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/pxa/sa1110break.c b/os/pxa/sa1110break.c
new file mode 100644
index 00000000..3ab87b06
--- /dev/null
+++ b/os/pxa/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/pxa/trap.c b/os/pxa/trap.c
new file mode 100644
index 00000000..43117330
--- /dev/null
+++ b/os/pxa/trap.c
@@ -0,0 +1,587 @@
+#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;
+ char name[KNAMELEN];
+ Handler *next;
+};
+
+static Handler irqvec[32];
+static Handler gpiovec[MaxGPIObit+1];
+static Lock veclock;
+
+Instr BREAK = 0xE6BAD010;
+
+int (*breakhandler)(Ureg*, Proc*);
+int (*catchdbg)(Ureg *, uint);
+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 << IRQtimer3) | (1 << IRQtimer2),
+};
+
+void
+intrenable(int sort, int v, void (*f)(Ureg*, void*), void* a, char *name)
+{
+ int x, o;
+ ulong m;
+ GpioReg *g;
+ Handler *ie;
+
+ ilock(&veclock);
+ switch(sort) {
+ case GPIOfalling:
+ case GPIOrising:
+ case GPIOboth:
+ if(v < 0 || v > MaxGPIObit)
+ panic("intrenable: gpio %d out of range", v);
+ g = GPIOREG;
+ o = GPR(v);
+ m = GPB(v);
+ switch(sort){
+ case GPIOfalling:
+ g->gfer[o] |= m;
+ g->grer[o] &= ~m;
+ break;
+ case GPIOrising:
+ g->grer[o] |= m;
+ g->gfer[o] &= ~m;
+ break;
+ case GPIOboth:
+ g->grer[o] |= m;
+ g->gfer[o] |= m;
+ break;
+ }
+ g->gpdr[o] &= ~m; /* must be input */
+ if(v > MaxGPIOIRQ) {
+ ie = &gpiovec[v];
+ 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;
+ }
+ v += IRQgpio0;
+ /*FALLTHROUGH for GPIO sources 0-MaxGPIOIRQ */
+ case IRQ:
+ if(v < 0 || v > 31)
+ 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", sort);
+ }
+ iunlock(&veclock);
+}
+
+void
+intrdisable(int sort, int v, void (*f)(Ureg*, void*), void* a, char *name)
+{
+ int x, o;
+ GpioReg *g;
+ Handler *ie;
+ ulong m;
+
+ ilock(&veclock);
+ switch(sort) {
+ case GPIOfalling:
+ case GPIOrising:
+ case GPIOboth:
+ if(v < 0 || v > MaxGPIObit)
+ panic("intrdisable: gpio source %d out of range", v);
+ if(v > MaxGPIOIRQ)
+ ie = &gpiovec[v];
+ else
+ ie = &irqvec[v];
+ if(ie->r != f || ie->a != a || strcmp(ie->name, name) != 0)
+ break;
+ ie->r = nil;
+ if(v <= MaxGPIOIRQ){
+ v += IRQgpio0;
+ x = splfhi();
+ INTRREG->icmr &= ~(1<<v);
+ splx(x);
+ }
+ g = GPIOREG;
+ o = GPR(v);
+ m = GPB(v);
+ switch(sort){
+ case GPIOfalling:
+ g->gfer[o] &= ~m;
+ break;
+ case GPIOrising:
+ g->grer[o] &= ~m;
+ break;
+ case GPIOboth:
+ g->grer[o] &= ~m;
+ g->gfer[o] &= ~m;
+ break;
+ }
+ break;
+ case IRQ:
+ if(v < 0 || v > 31)
+ 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", sort);
+ }
+ iunlock(&veclock);
+}
+
+static void
+gpiointr(Ureg *ur, void*)
+{
+ Handler *cur;
+ ulong e;
+ int i, o;
+
+ for(o=0; o<3; o++){
+ e = GPIOREG->gedr[o];
+ GPIOREG->gedr[o] = e;
+ for(i = 0; i < 32 && e != 0; i++){
+ if(e & (1<<i)){
+ cur = &gpiovec[(o<<5)+i];
+ if(cur->r != nil){
+ cur->r(ur, cur->a);
+ e &= ~(1<<i);
+ }
+ }
+ }
+ if(e != 0){
+ GPIOREG->gfer[o] &= ~e;
+ GPIOREG->grer[o] &= ~e;
+ iprint("spurious GPIO[%d] %8.8lux interrupt\n", o,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)
+{
+ IntrReg *intr = INTRREG;
+
+ intr->icmr = 0; /* mask everything */
+ intr->iccr = 1; /* only enabled and unmasked interrupts wake us */
+ intr->iclr = IRQ_NONMASK;
+
+ trapstacks();
+
+ memmove(page0->vectors, vectors, sizeof(page0->vectors));
+ memmove(page0->vtable, vtable, sizeof(page0->vtable));
+ dcflush(page0, sizeof(*page0));
+
+#ifdef NOTYET
+ suspendcode = xspanalloc(16*sizeof(ulong), CACHELINESZ, 0);
+ memmove(suspendcode, _suspendcode, 16*sizeof(ulong));
+ dcflush(suspendcode, 8*sizeof(ulong));
+#endif
+
+ icflushall();
+
+ intrenable(IRQ, IRQgpio, gpiointr, nil, "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(1){
+ 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(0 && 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);
+ }
+ iprint("Data Abort: FSR %8.8luX FAR %8.8luX\n", fsr, far); xdelay(1);
+ /* 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;
+}