diff options
Diffstat (limited to 'os/pxa')
| -rw-r--r-- | os/pxa/NOTICE | 2 | ||||
| -rw-r--r-- | os/pxa/clock.c | 318 | ||||
| -rw-r--r-- | os/pxa/devether.c | 617 | ||||
| -rw-r--r-- | os/pxa/devrtc.c | 169 | ||||
| -rw-r--r-- | os/pxa/devuart.c | 1070 | ||||
| -rw-r--r-- | os/pxa/dma.c | 244 | ||||
| -rw-r--r-- | os/pxa/etherif.h | 41 | ||||
| -rw-r--r-- | os/pxa/fpi.h | 61 | ||||
| -rw-r--r-- | os/pxa/fpiarm.c | 483 | ||||
| -rw-r--r-- | os/pxa/gpio.c | 88 | ||||
| -rw-r--r-- | os/pxa/i2c.c | 561 | ||||
| -rw-r--r-- | os/pxa/l.s | 548 | ||||
| -rw-r--r-- | os/pxa/mmu.c | 252 | ||||
| -rw-r--r-- | os/pxa/pxaio.h | 383 | ||||
| -rw-r--r-- | os/pxa/sa1110break.c | 360 | ||||
| -rw-r--r-- | os/pxa/trap.c | 587 |
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 = ðer->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, ðer->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), ®(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(®(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 IC 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; +} |
