#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "io.h" static PCMslot slot[2]; int nslot = 2; struct { Ref; } pcmcia; enum { Qdir, Qmem, Qattr, Qctl, 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 */ }; #define SLOTNO(c) ((c->qid.path>>8)&0xff) #define TYPE(c) (c->qid.path&0xff) #define QID(s,t) (((s)<<8)|(t)) 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*); static int pcmgen(Chan *c, Dirtab *, int , int i, Dir *dp) { int slotno; Qid qid; long len; PCMslot *sp; char name[NAMELEN]; if(i == DEVDOTDOT){ devdir(c, (Qid){CHDIR, 0}, "#y", 0, eve, 0555, dp); return 1; } if(i >= Nents*nslot) return -1; slotno = i/Nents; sp = slot + slotno; len = 0; switch(i%Nents){ case 0: qid.path = QID(slotno, Qmem); sprint(name, "pcm%dmem", slotno); len = sp->memlen; break; case 1: qid.path = QID(slotno, Qattr); sprint(name, "pcm%dattr", slotno); len = sp->memlen; break; case 2: qid.path = QID(slotno, Qctl); sprint(name, "pcm%dctl", slotno); break; } qid.vers = 0; devdir(c, qid, name, len, eve, 0660, dp); return 1; } static int bitno(ulong x) { int i; for(i = 0; i < 8*sizeof(x); i++) if((1<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 int pcmciawalk(Chan *c, char *name) { return devwalk(c, name, 0, 0, pcmgen); } static void pcmciastat(Chan *c, char *db) { devstat(c, db, 0, 0, pcmgen); } static Chan* pcmciaopen(Chan *c, int omode) { if(c->qid.path == CHDIR){ 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.path != CHDIR) decrefp(slot+SLOTNO(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 long pcmciaread(Chan *c, void *a, long n, vlong off) { PCMslot *sp; ulong offset = off; sp = slot + SLOTNO(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 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; 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->cpresent & (1<attr; cp += sp->caddr + Rconfig; *cp = index; } /* configure device */ strncpy(cf.type, cmd->f[2], sizeof(cf.type)-1); cf.type[sizeof(cf.type)-1] = 0; cf.mem = (ulong)sp->mem; cf.port = (ulong)sp->regs; cf.itype = GPIOfalling; cf.interrupt = bitno(sp == slot ? GPIO_CARD_IRQ0_i : GPIO_CARD_IRQ1_i); cf.size = 0; if(devtab[dtx]->config(1, p, &cf) < 0) error("couldn't configure device"); wunlock(sp); poperror(); /* don't let the power turn off */ increfp(sp); } free(cmd); return 0; } static long pcmciawrite(Chan *c, void *a, long n, vlong off) { PCMslot *sp; ulong offset = off; sp = slot + SLOTNO(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 Qctl: if(!sp->occupied) error(Eio); return pcmctlwrite(a, n, offset, sp); } error(Ebadarg); return -1; /* not reached */ } /* * look for a card whose version contains 'idstr' */ int pcmgrep(char *idstr) { PCMslot *pp; extern char *strstr(char*, char*); int i; for(i=0; ioccupied) { if(strstr(pp->verstr, idstr)){ return i; } } } return -1; } Dev pcmciadevtab = { 'y', "pcmcia", pcmciareset, devinit, pcmciaattach, devclone, pcmciawalk, pcmciastat, pcmciaopen, devcreate, pcmciaclose, pcmciaread, devbread, pcmciawrite, devbwrite, devremove, devwstat, }; /* 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 = 0; slot[1].occupied = 0; } else { if(x & GPIO_CARD_IND0_i){ slot[0].occupied = 0; slot[0].cisread = 0; } else { if(slot[0].occupied == 0) slot[0].cisread = 0; slot[0].occupied = 1; } if(x & GPIO_CARD_IND1_i){ slot[1].occupied = 0; slot[1].cisread = 0; } else { if(slot[1].occupied == 0) slot[1].cisread = 0; slot[1].occupied = 1; } } } /* use reference card to turn cards on and off */ static void increfp(PCMslot *sp) { wlock(sp); if(waserror()){ wunlock(sp); nexterror(); } if(incref(&pcmcia) == 1){ 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) { decref(&sp->ref); if(decref(&pcmcia) == 0) egpiobits(EGPIO_exp_nvram_power|EGPIO_exp_full_power, 0); } /* * the regions are staticly masped */ 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 & 0xffff0000; } else { x <<= 16; x |= memconfregs->mecr & 0xffff; } memconfregs->mecr = x; }