ref: 402741bc017cfa9602e7d8ea8645696ace54f4cd
dir: /sys/src/9/bitsy/devpcmcia.c/
#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "io.h" /* * BUG: insertion events are detected by polling. * Should look into the compaq docs to see if * there's an interrupt for card insertion * there's probably one. */ static PCMslot slot[2]; int nslot = 2; struct { Ref; Rendez event; // where to wait for card events int evreader; // there's a reader for events } pcmcia; enum { Qdir, Qmem, Qattr, Qctl, Qevs, Nents = 3, }; 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 */ }; static void increfp(PCMslot*); static void decrefp(PCMslot*); static void slotmap(int, ulong, ulong, ulong); static void slottiming(int, int, int, int, int); static void slotinfo(Ureg*, void*); #define TYPE(c) (((ulong)c->qid.path)&0xff) #define PATH(s,t) (((s)<<8)|(t)) static PCMslot* slotof(Chan *c) { ulong x; x = c->qid.path; return slot + ((x>>8)&0xff); } static int pcmgen(Chan *c, char *, Dirtab * , int, int i, Dir *dp) { int slotno; Qid qid; long len; PCMslot *sp; if(i == DEVDOTDOT){ mkqid(&qid, Qdir, 0, QTDIR); devdir(c, qid, "#y", 0, eve, 0555, dp); return 1; } if(i >= Nents*nslot + 1) return -1; if(i == Nents*nslot){ len = 0; qid.path = PATH(0, Qevs); snprint(up->genbuf, sizeof up->genbuf, "pcmevs"); goto found; } slotno = i/Nents; sp = slot + slotno; len = 0; switch(i%Nents){ case 0: qid.path = PATH(slotno, Qmem); snprint(up->genbuf, sizeof up->genbuf, "pcm%dmem", slotno); len = sp->memlen; break; case 1: qid.path = PATH(slotno, Qattr); snprint(up->genbuf, sizeof up->genbuf, "pcm%dattr", slotno); len = sp->memlen; break; case 2: qid.path = PATH(slotno, Qctl); snprint(up->genbuf, sizeof up->genbuf, "pcm%dctl", slotno); break; } found: qid.vers = 0; qid.type = QTFILE; devdir(c, qid, up->genbuf, len, eve, 0660, dp); return 1; } static int bitno(ulong x) { int i; for(i = 0; i < 8*sizeof(x); i++) if((1<<i) & x) break; return i; } /* * set up the cards, default timing is 300 ns */ static void pcmciareset(void) { /* staticly map the whole area */ slotmap(0, PHYSPCM0REGS, PYHSPCM0ATTR, PYHSPCM0MEM); slotmap(1, PHYSPCM1REGS, PYHSPCM1ATTR, PYHSPCM1MEM); /* set timing to the default, 300 */ slottiming(0, 300, 300, 300, 0); slottiming(1, 300, 300, 300, 0); /* if there's no pcmcia sleave, no interrupts */ if(gpioregs->level & GPIO_OPT_IND_i) return; /* sleave there, interrupt on card removal */ intrenable(GPIOrising, bitno(GPIO_CARD_IND1_i), slotinfo, nil, "pcmcia slot1 status"); intrenable(GPIOrising, bitno(GPIO_CARD_IND0_i), slotinfo, nil, "pcmcia slot0 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) { PCMslot *slotp; if(c->qid.type & QTDIR){ if(omode != OREAD) error(Eperm); } else { slotp = slotof(c); increfp(slotp); } 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(slotof(c)); } /* a memmove using only bytes */ static void memmoveb(uchar *to, uchar *from, int n) { while(n-- > 0) *to++ = *from++; } /* a memmove using only shorts & bytes */ static void memmoves(uchar *to, uchar *from, int n) { ushort *t, *f; if((((ulong)to) & 1) || (((ulong)from) & 1) || (n & 1)){ while(n-- > 0) *to++ = *from++; } else { n = n/2; t = (ushort*)to; f = (ushort*)from; while(n-- > 0) *t++ = *f++; } } static long pcmread(void *a, long n, ulong off, PCMslot *sp, uchar *start, ulong len) { rlock(sp); if(waserror()){ runlock(sp); nexterror(); } if(off > len) return 0; if(off + n > len) n = len - off; memmoveb(a, start+off, n); runlock(sp); poperror(); return n; } static long pcmctlread(void *a, long n, ulong off, PCMslot *sp) { char *p, *buf, *e; buf = p = malloc(READSTR); if(waserror()){ free(buf); nexterror(); } e = p + READSTR; buf[0] = 0; if(sp->occupied){ p = seprint(p, e, "occupied\n"); if(sp->verstr[0]) p = seprint(p, e, "version %s\n", sp->verstr); } USED(p); n = readstr(off, a, n, buf); free(buf); poperror(); return n; } static int inserted(void *) { if (slot[0].inserted) return 1; if (slot[1].inserted) return 2; return 0; } static long pcmevsread(void *a, long n, ulong off) { int i; char *buf = nil; char *e; if (pcmcia.evreader) error("At most one reader"); off = 0; pcmcia.evreader++; if (waserror()){ free(buf); pcmcia.evreader--; nexterror(); } while((i = inserted(nil)) == 0){ slotinfo(nil, nil); tsleep(&pcmcia.event, inserted, nil, 500); } pcmcia.evreader--; slot[i-1].inserted = 0; buf = malloc(READSTR); e = buf + READSTR; buf[0] = 0; seprint(buf, e, "#y/pcm%dctl\n", i-1); n = readstr(off, a, n, buf); free(buf); poperror(); return n; } static long pcmciaread(Chan *c, void *a, long n, vlong off) { PCMslot *sp; ulong offset = off; sp = slotof(c); switch(TYPE(c)){ case Qdir: return devdirread(c, a, n, 0, 0, pcmgen); case Qmem: if(!sp->occupied) error(Eio); return pcmread(a, n, offset, sp, sp->mem, 64*OneMeg); case Qattr: if(!sp->occupied) error(Eio); return pcmread(a, n, offset, sp, sp->attr, OneMeg); case Qevs: return pcmevsread(a, n, offset); case Qctl: return pcmctlread(a, n, offset, sp); } error(Ebadarg); return -1; /* not reached */ } static long pcmwrite(void *a, long n, ulong off, PCMslot *sp, uchar *start, ulong len) { rlock(sp); if(waserror()){ runlock(sp); nexterror(); } if(off > len) error(Eio); if(off + n > len) error(Eio); memmoveb(start+off, a, n); poperror(); runlock(sp); return n; } static long pcmctlwrite(char *p, long n, ulong, PCMslot *sp) { Cmdbuf *cmd; uchar *cp; int index, i, dtx; Rune r; DevConf cf; Devport port; cmd = parsecmd(p, n); if(strcmp(cmd->f[0], "configure") == 0){ wlock(sp); if(waserror()){ wunlock(sp); nexterror(); } /* see if driver exists and is configurable */ if(cmd->nf < 3) error(Ebadarg); p = cmd->f[1]; if(*p++ != '#') error(Ebadarg); p += chartorune(&r, p); dtx = devno(r, 1); if(dtx < 0) error("no such device type"); if(devtab[dtx]->config == nil) error("not a dynamicly configurable device"); /* set pcmcia card configuration */ index = 0; if(sp->def != nil) index = sp->def->index; if(cmd->nf > 3){ i = atoi(cmd->f[3]); if(i < 0 || i >= sp->nctab) error("bad configuration index"); index = i; } if(sp->cfg[0].cpresent & (1<<Rconfig)){ cp = sp->attr; cp += sp->cfg[0].caddr + Rconfig; *cp = index; } /* configure device */ memset(&cf, 0, sizeof cf); kstrdup(&cf.type, cmd->f[2]); cf.mem = (ulong)sp->mem; cf.ports = &port; cf.ports[0].port = (ulong)sp->regs; cf.ports[0].size = 0; cf.nports = 1; cf.itype = GPIOfalling; cf.intnum = bitno(sp == slot ? GPIO_CARD_IRQ0_i : GPIO_CARD_IRQ1_i); if(devtab[dtx]->config(1, p, &cf) < 0) error("couldn't configure device"); sp->dev = devtab[dtx]; free(cf.type); wunlock(sp); poperror(); /* don't let the power turn off */ increfp(sp); }else if(strcmp(cmd->f[0], "remove") == 0){ /* see if driver exists and is configurable */ if(cmd->nf != 2) error(Ebadarg); p = cmd->f[1]; if(*p++ != '#') error(Ebadarg); p += chartorune(&r, p); dtx = devno(r, 1); if(dtx < 0) error("no such device type"); if(devtab[dtx]->config == nil) error("not a dynamicly configurable device"); if(devtab[dtx]->config(0, p, nil) < 0) error("couldn't unconfigure device"); /* let the power turn off */ decrefp(sp); } free(cmd); return 0; } static long pcmciawrite(Chan *c, void *a, long n, vlong off) { PCMslot *sp; ulong offset = off; sp = slotof(c); switch(TYPE(c)){ case Qmem: if(!sp->occupied) error(Eio); return pcmwrite(a, n, offset, sp, sp->mem, 64*OneMeg); case Qattr: if(!sp->occupied) error(Eio); return pcmwrite(a, n, offset, sp, sp->attr, OneMeg); case Qevs: break; case Qctl: if(!sp->occupied) error(Eio); return pcmctlwrite(a, n, offset, sp); } error(Ebadarg); return -1; /* not reached */ } /* * power up/down pcmcia */ void pcmciapower(int on) { PCMslot *sp; /* if there's no pcmcia sleave, no interrupts */ iprint("pcmciapower %d\n", on); if (on){ /* set timing to the default, 300 */ slottiming(0, 300, 300, 300, 0); slottiming(1, 300, 300, 300, 0); /* if there's no pcmcia sleave, no interrupts */ if(gpioregs->level & GPIO_OPT_IND_i){ iprint("pcmciapower: no sleeve\n"); return; } for (sp = slot; sp < slot + nslot; sp++){ if (sp->dev){ increfp(sp); iprint("pcmciapower: %s\n", sp->verstr); delay(10000); if (sp->dev->power) sp->dev->power(on); } } }else{ if(gpioregs->level & GPIO_OPT_IND_i){ iprint("pcmciapower: no sleeve\n"); return; } for (sp = slot; sp < slot + nslot; sp++){ if (sp->dev){ if (sp->dev->power) sp->dev->power(on); decrefp(sp); } sp->occupied = 0; sp->cisread = 0; } egpiobits(EGPIO_exp_nvram_power|EGPIO_exp_full_power, 0); } } Dev pcmciadevtab = { 'y', "pcmcia", pcmciareset, devinit, devshutdown, pcmciaattach, pcmciawalk, pcmciastat, pcmciaopen, devcreate, pcmciaclose, pcmciaread, devbread, pcmciawrite, devbwrite, devremove, devwstat, pcmciapower, }; /* see what's there */ static void slotinfo(Ureg*, void*) { ulong x = gpioregs->level; if(x & GPIO_OPT_IND_i){ /* no expansion pack */ slot[0].occupied = slot[0].inserted = 0; slot[1].occupied = slot[1].inserted = 0; } else { if(x & GPIO_CARD_IND0_i){ slot[0].occupied = slot[0].inserted = 0; slot[0].cisread = 0; } else { if(slot[0].occupied == 0){ slot[0].inserted = 1; slot[0].cisread = 0; } slot[0].occupied = 1; } if(x & GPIO_CARD_IND1_i){ slot[1].occupied = slot[1].inserted = 0; slot[1].cisread = 0; } else { if(slot[1].occupied == 0){ slot[1].inserted = 1; slot[1].cisread = 0; } slot[1].occupied = 1; } if (inserted(nil)) wakeup(&pcmcia.event); } } /* use reference card to turn cards on and off */ static void increfp(PCMslot *sp) { wlock(sp); if(waserror()){ wunlock(sp); nexterror(); } iprint("increfp %ld\n", sp - slot); if(incref(&pcmcia) == 1){ iprint("increfp full power\n"); egpiobits(EGPIO_exp_nvram_power|EGPIO_exp_full_power, 1); delay(200); egpiobits(EGPIO_pcmcia_reset, 1); delay(100); egpiobits(EGPIO_pcmcia_reset, 0); delay(500); } incref(&sp->ref); slotinfo(nil, nil); if(sp->occupied && sp->cisread == 0) { pcmcisread(sp); } wunlock(sp); poperror(); } static void decrefp(PCMslot *sp) { iprint("decrefp %ld\n", sp - slot); decref(&sp->ref); if(decref(&pcmcia) == 0){ iprint("increfp power down\n"); egpiobits(EGPIO_exp_nvram_power|EGPIO_exp_full_power, 0); } } /* * the regions are staticly mapped */ static void slotmap(int slotno, ulong regs, ulong attr, ulong mem) { PCMslot *sp; sp = &slot[slotno]; sp->slotno = slotno; sp->memlen = 64*OneMeg; sp->verstr[0] = 0; sp->mem = mapmem(mem, 64*OneMeg, 0); sp->memmap.ca = 0; sp->memmap.cea = 64*MB; sp->memmap.isa = (ulong)mem; sp->memmap.len = 64*OneMeg; sp->memmap.attr = 0; sp->attr = mapmem(attr, OneMeg, 0); sp->attrmap.ca = 0; sp->attrmap.cea = MB; sp->attrmap.isa = (ulong)attr; sp->attrmap.len = OneMeg; sp->attrmap.attr = 1; sp->regs = mapspecial(regs, 32*1024); } 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/(conf.hz/1000); /* get 10 times ns/(cycle*6) */ y = (1000*ns)/(6*y); /* round up */ y += 9; y /= 10; /* subtract 1 */ return y-1; } static void slottiming(int slotno, int tio, int tattr, int tmem, int fast) { ulong x; x = 0; if(fast) x |= 1<<MECR_fast0; x |= ns2count(tio) << MECR_io0; x |= ns2count(tattr) << MECR_attr0; x |= ns2count(tmem) << MECR_mem0; if(slotno == 0){ x |= memconfregs->mecr & 0xffff0000; } else { x <<= 16; x |= memconfregs->mecr & 0xffff; } memconfregs->mecr = x; } /* For compat with ../pc devices. Don't use it for the bitsy */ int pcmspecial(char*, ISAConf*) { return -1; }