diff options
Diffstat (limited to 'os/sa1110/devpcmcia.c')
| -rw-r--r-- | os/sa1110/devpcmcia.c | 761 |
1 files changed, 761 insertions, 0 deletions
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++; +} |
