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