summaryrefslogtreecommitdiff
path: root/os/mpc/devpcmcia.c
diff options
context:
space:
mode:
Diffstat (limited to 'os/mpc/devpcmcia.c')
-rw-r--r--os/mpc/devpcmcia.c1076
1 files changed, 1076 insertions, 0 deletions
diff --git a/os/mpc/devpcmcia.c b/os/mpc/devpcmcia.c
new file mode 100644
index 00000000..4c1d8311
--- /dev/null
+++ b/os/mpc/devpcmcia.c
@@ -0,0 +1,1076 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+/*
+ * MPC821/3 PCMCIA driver (prototype)
+ *
+ * unlike the i82365 adapter, there isn't an offset register:
+ * card addresses are simply the lower order 26 bits of the host address.
+ *
+ * to do:
+ * split allocation of memory/attrib (all 26 bits valid) and io space (typically 10 or 12 bits)
+ * correct config
+ * interrupts and i/o space access
+ * DMA?
+ * power control
+ */
+
+enum
+{
+ Maxctlr= 1,
+ Maxslot= 2,
+ Slotashift= 16,
+
+ /* pipr */
+ Cbvs1= 1<<15,
+ Cbvs2= 1<<14,
+ Cbwp= 1<<13,
+ Cbcd2= 1<<12,
+ Cbcd1= 1<<11,
+ Cbbvd2= 1<<10,
+ Cbbvd1= 1<<9,
+ Cbrdy= 1<<8,
+
+ /* pscr and per */
+ Cbvs1_c= 1<<15,
+ Cbvs2_c= 1<<14,
+ Cbwp_c= 1<<13,
+ Cbcd2_c= 1<<12,
+ Cbcd1_c= 1<<11,
+ Cbbvd2_c= 1<<10,
+ Cbbvd1_c= 1<<9,
+ Cbrdy_l= 1<<7,
+ Cbrdy_h= 1<<6,
+ Cbrdy_r= 1<<5,
+ Cbrdy_f= 1<<4,
+
+ /* pgcr[n] */
+ Cbdreq_int= 0<<14,
+ Cbdreq_iois16= 2<<14,
+ Cbdreq_spkr= 3<<14,
+ Cboe= 1<<7,
+ Cbreset= 1<<6,
+
+ /* porN */
+ Rport8= 0<<6,
+ Rport16= 1<<6,
+ Rmtype= 7<<3, /* memory type field */
+ Rmem= 0<<3, /* common memory space */
+ Rattrib= 2<<3, /* attribute space */
+ Rio= 3<<3,
+ Rdma= 4<<3, /* normal DMA */
+ Rdmalx= 5<<3, /* DMA, last transaction */
+ RA22_23= 6<<3, /* ``drive A22 and A23 signals on CE2 and CE1'' */
+ RslotB= 1<<2, /* select slot B (always, on MPC823) */
+ Rwp= 1<<1, /* write protect */
+ Rvalid= 1<<0, /* region valid */
+
+ Nmap= 8, /* max number of maps to use */
+
+ /*
+ * 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 */
+ Rccsr= 2,
+ Ciack = (1<<0),
+ Cipend = (1<<1),
+ Cpwrdown= (1<<2),
+ Caudioen= (1<<3),
+ Ciois8= (1<<5),
+ Cchgena= (1<<6),
+ Cchange= (1<<7),
+ Rpin= 4, /* pin replacement register */
+ Rscpr= 6, /* socket and copy register */
+ Riob0= 10,
+ Riob1= 12,
+ Riob2= 14,
+ Riob3= 16,
+ Riolim= 18,
+
+ Maxctab= 8, /* maximum configuration table entries */
+ MaxCIS = 8192, /* maximum CIS size in bytes */
+ Mgran = 8192, /* maximum size of reads and writes */
+
+ Statusbounce=20, /* msec debounce time */
+};
+
+typedef struct Ctlr Ctlr;
+
+/* a controller (there's only one) */
+struct Ctlr
+{
+ int dev;
+ int nslot;
+
+ /* memory maps */
+ Lock mlock; /* lock down the maps */
+ PCMmap mmap[Nmap]; /* maps */
+
+ /* IO port allocation */
+ ulong nextport;
+};
+static Ctlr controller[Maxctlr];
+
+static PCMslot *slot;
+static PCMslot *lastslot;
+static int nslot;
+
+static Map pcmmapv[Nmap+1];
+static RMap pcmmaps = {"PCMCIA mappings"};
+
+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 slotdis(PCMslot*);
+
+static ulong pcmmalloc(ulong, long);
+static void pcmfree(ulong, long);
+static void pcmciaproc(void*);
+
+static void pcmciadump(PCMslot*);
+
+/*
+ * get info about card
+ */
+static void
+slotinfo(PCMslot *pp)
+{
+ ulong pipr;
+
+ pipr = (m->iomem->pipr >> pp->slotshift) & 0xFF00;
+print("pipr=%8.8lux/%lux\n", m->iomem->pipr, pipr);
+ pp->v3_3 = (pipr&Cbvs1)!=0;
+ pp->voltage = (((pipr & Cbvs2)!=0)<<1) | ((pipr & Cbvs1)!=0);
+ pp->occupied = (pipr&(Cbcd1|Cbcd2))==0;
+ pp->powered = pcmpowered(pp->slotno);
+ pp->battery = (pipr & (Cbbvd1|Cbbvd2))>>9;
+ pp->wrprot = (pipr&Cbwp)!=0;
+ pp->busy = (pipr&Cbrdy)==0;
+}
+
+static void
+pcmdelay(int ms)
+{
+ if(up == nil)
+ delay(ms);
+ else
+ tsleep(&up->sleep, return0, nil, ms);
+}
+
+/*
+ * enable the slot card
+ */
+static void
+slotena(PCMslot *pp)
+{
+ IMM *io;
+
+ if(pp->enabled)
+ return;
+ m->iomem->pgcr[pp->slotno] &= ~Cboe;
+ pcmpower(pp->slotno, 1);
+ eieio();
+ pcmdelay(300);
+ io = m->iomem;
+ io->pgcr[pp->slotno] |= Cbreset; /* active high */
+ eieio();
+ pcmdelay(100);
+ io->pgcr[pp->slotno] &= ~Cbreset;
+ eieio();
+ pcmdelay(500); /* ludicrous delay */
+
+ /* get configuration */
+ slotinfo(pp);
+ if(pp->occupied){
+ if(pp->cisread == 0){
+ pcmcisread(pp);
+ pp->cisread = 1;
+ }
+ pp->enabled = 1;
+ } else{
+ print("empty slot\n");
+ slotdis(pp);
+ }
+}
+
+/*
+ * disable the slot card
+ */
+static void
+slotdis(PCMslot *pp)
+{
+ int i;
+ Ctlr *ctlr;
+ PCMmap *pm;
+
+iprint("slotdis %d\n", pp->slotno);
+ pcmpower(pp->slotno, 0);
+ m->iomem->pgcr[pp->slotno] |= Cboe;
+ ctlr = pp->ctlr;
+ for(i = 0; i < nelem(ctlr->mmap); i++){
+ pm = &ctlr->mmap[i];
+ if(m->iomem->pcmr[i].option & Rvalid && pm->slotno == pp->slotno)
+ pcmunmap(pp->slotno, pm);
+ }
+ pp->enabled = 0;
+ pp->cisread = 0;
+}
+
+static void
+pcmciardy(Ureg *ur, void *a)
+{
+ PCMslot *pp;
+ ulong w;
+
+ pp = a;
+ w = (m->iomem->pipr >> pp->slotshift) & 0xFF00;
+ if(pp->occupied && (w & Cbrdy) == 0){ /* interrupt */
+print("PCM.%dI#%lux|", pp->slotno, w);
+ if(pp->intr.f != nil)
+ pp->intr.f(ur, pp->intr.arg);
+ }
+}
+
+void
+pcmintrenable(int slotno, void (*f)(Ureg*, void*), void *arg)
+{
+ PCMslot *pp;
+ IMM *io;
+ char name[KNAMELEN];
+
+ if(slotno < 0 || slotno >= nslot)
+ panic("pcmintrenable");
+ snprint(name, sizeof(name), "pcmcia.irq%d", slotno);
+ io = ioplock();
+ pp = slot+slotno;
+ pp->intr.f = f;
+ pp->intr.arg = arg;
+ intrenable(PCMCIAio, pcmciardy, pp, BUSUNKNOWN, name);
+ io->per |= Cbrdy_l; /* assumes used for irq, not rdy; assumes level interrupt always right */
+ iopunlock();
+}
+
+void
+pcmintrdisable(int slotno, void (*f)(Ureg*, void*), void *arg)
+{
+ PCMslot *pp;
+ IMM *io;
+ char name[KNAMELEN];
+
+ if(slotno < 0 || slotno >= nslot)
+ panic("pcmintrdisable");
+ snprint(name, sizeof(name), "pcmcia.irq%d", slotno);
+ io = ioplock();
+ pp = slot+slotno;
+ if(pp->intr.f == f && pp->intr.arg == arg){
+ pp->intr.f = nil;
+ pp->intr.arg = nil;
+ intrdisable(PCMCIAio, pcmciardy, pp, BUSUNKNOWN, name);
+ io->per &= ~Cbrdy_l;
+ }
+ iopunlock();
+}
+
+/*
+ * status change interrupt
+ *
+ * this wakes a monitoring process to read the CIS,
+ * rather than holding other interrupts out here.
+ */
+
+static Rendez pcmstate;
+
+static int
+statechanged(void *a)
+{
+ PCMslot *pp;
+ int in;
+
+ pp = a;
+ in = (m->iomem->pipr & (Cbcd1|Cbcd2))==0;
+ return in != pp->occupied;
+}
+
+static void
+pcmciaintr(Ureg*, void*)
+{
+ ulong events;
+
+ if(slot == 0)
+ return;
+ events = m->iomem->pscr & (Cbvs1_c|Cbvs2_c|Cbwp_c|Cbcd2_c|Cbcd1_c|Cbbvd2_c|Cbbvd1_c);
+ eieio();
+ m->iomem->pscr = events;
+ /* TO DO: other slot */
+iprint("PCM: #%lux|", events);
+iprint("pipr=#%lux|", m->iomem->pipr & 0xFF00);
+ wakeup(&pcmstate);
+}
+
+static void
+pcmciaproc(void*)
+{
+ ulong csc;
+ PCMslot *pp;
+ int was;
+
+ for(;;){
+ sleep(&pcmstate, statechanged, slot+1);
+ tsleep(&up->sleep, return0, nil, Statusbounce);
+ /*
+ * voltage change 1,2
+ * write protect change
+ * card detect 1,2
+ * battery voltage 1 change (or SPKR-bar)
+ * battery voltage 2 change (or STSCHG-bar)
+ * card B rdy / IRQ-bar low
+ * card B rdy / IRQ-bar high
+ * card B rdy / IRQ-bar rising edge
+ * card B rdy / IRQ-bar falling edge
+ *
+ * TO DO: currently only handle card-present changes
+ */
+
+ for(pp = slot; pp < lastslot; pp++){
+ if(pp->memlen == 0)
+ continue;
+ csc = (m->iomem->pipr>>pp->slotshift) & (Cbcd1|Cbcd2);
+ was = pp->occupied;
+ slotinfo(pp);
+ if(csc == 0 && was != pp->occupied){
+ if(!pp->occupied){
+ slotdis(pp);
+ if(pp->special && pp->notify.f != nil)
+ pp->notify.f(pp->notify.arg, 1);
+ }
+ }
+ }
+ }
+}
+
+static uchar greycode[] = {
+ 0, 1, 3, 2, 6, 7, 5, 4, 014, 015, 017, 016, 012, 013, 011, 010,
+ 030, 031, 033, 032, 036, 037, 035, 034, 024, 025, 027
+};
+
+/*
+ * get a map for pc card region, return corrected len
+ */
+PCMmap*
+pcmmap(int slotno, ulong offset, int len, int attr)
+{
+ Ctlr *ctlr;
+ PCMslot *pp;
+ PCMmap *pm, *nm;
+ IMM *io;
+ int i;
+ ulong e, bsize, code, opt;
+
+ if(0)
+ print("pcmmap: %d #%lux %d #%x\n", slotno, offset, len, attr);
+ pp = slot + slotno;
+ if(!pp->occupied)
+ return nil;
+ if(attr == 1){ /* account for ../port/cis.c's conventions */
+ attr = Rattrib;
+ if(len <= 0)
+ len = MaxCIS*2; /* TO DO */
+ }
+ ctlr = pp->ctlr;
+
+ /* convert offset to granularity */
+ if(len <= 0)
+ len = 1;
+ e = offset+len;
+ for(i=0;; i++){
+ if(i >= nelem(greycode))
+ return nil;
+ bsize = 1<<i;
+ offset &= ~(bsize-1);
+ if(e <= offset+bsize)
+ break;
+ }
+ code = greycode[i];
+ if(0)
+ print("i=%d bsize=%lud code=0%luo\n", i, bsize, code);
+ e = offset+bsize;
+ len = bsize;
+
+ lock(&ctlr->mlock);
+
+ /* look for an existing map that covers the right area */
+ io = m->iomem;
+ nm = nil;
+ for(i=0; i<Nmap; i++){
+ pm = &ctlr->mmap[i];
+ if(io->pcmr[i].option & Rvalid &&
+ pm->slotno == slotno &&
+ pm->attr == attr &&
+ offset >= pm->ca && e <= pm->cea){
+ pm->ref++;
+ unlock(&ctlr->mlock);
+ return pm;
+ }
+ if(nm == 0 && pm->ref == 0)
+ nm = pm;
+ }
+ pm = nm;
+ if(pm == nil){
+ unlock(&ctlr->mlock);
+ return nil;
+ }
+
+ /* set up new map */
+ pm->isa = pcmmalloc(offset, len);
+ if(pm->isa == 0){
+ /* address not available: in use, or too much to map */
+ unlock(&ctlr->mlock);
+ return 0;
+ }
+ if(0)
+ print("mx=%d isa=#%lux\n", (int)(pm - ctlr->mmap), pm->isa);
+
+ pm->len = len;
+ pm->ca = offset;
+ pm->cea = pm->ca + pm->len;
+ pm->attr = attr;
+ i = pm - ctlr->mmap;
+ io->pcmr[i].option &= ~Rvalid; /* disable map before changing it */
+ io->pcmr[i].base = pm->isa;
+ opt = attr;
+ opt |= code<<27;
+ if((attr&Rmtype) == Rio){
+ opt |= 4<<12; /* PSST */
+ opt |= 8<<7; /* PSL */
+ opt |= 2<<16; /* PSHT */
+ }else{
+ opt |= 6<<12; /* PSST */
+ opt |= 24<<7; /* PSL */
+ opt |= 8<<16; /* PSHT */
+ }
+ if((attr & Rport16) == 0)
+ opt |= Rport8;
+ if(slotno == 1)
+ opt |= RslotB;
+ io->pcmr[i].option = opt | Rvalid;
+ pm->slotno = slotno;
+ pm->ref = 1;
+
+ unlock(&ctlr->mlock);
+ return pm;
+}
+
+static void
+pcmiomap(PCMslot *pp, PCMconftab *ct, int i)
+{
+ int n, attr;
+ Ctlr *ctlr;
+
+ if(0)
+ print("pcm iomap #%lux %lud\n", ct->io[i].start, ct->io[i].len);
+ if(ct->io[i].len <= 0)
+ return;
+ if(ct->io[i].start == 0){
+ n = 1<<ct->nlines;
+ ctlr = pp->ctlr;
+ lock(&ctlr->mlock);
+ if(ctlr->nextport == 0)
+ ctlr->nextport = 0xF000;
+ ctlr->nextport = (ctlr->nextport + n - 1) & ~(n-1);
+ ct->io[i].start = ctlr->nextport;
+ ct->io[i].len = n;
+ ctlr->nextport += n;
+ unlock(&ctlr->mlock);
+ }
+ attr = Rio;
+ if(ct->bit16)
+ attr |= Rport16;
+ ct->io[i].map = pcmmap(pp->slotno, ct->io[i].start, ct->io[i].len, attr);
+}
+
+void
+pcmunmap(int slotno, PCMmap* pm)
+{
+ int i;
+ PCMslot *pp;
+ Ctlr *ctlr;
+
+ pp = slot + slotno;
+ if(pp->memlen == 0)
+ return;
+ ctlr = pp->ctlr;
+ lock(&ctlr->mlock);
+ if(pp->slotno == pm->slotno && --pm->ref == 0){
+ i = pm - ctlr->mmap;
+ m->iomem->pcmr[i].option = 0;
+ m->iomem->pcmr[i].base = 0;
+ pcmfree(pm->isa, pm->len);
+ }
+ unlock(&ctlr->mlock);
+}
+
+static void
+increfp(PCMslot *pp)
+{
+ if(incref(pp) == 1)
+ slotena(pp);
+}
+
+static void
+decrefp(PCMslot *pp)
+{
+ if(decref(pp) == 0)
+ slotdis(pp);
+}
+
+/*
+ * look for a card whose version contains 'idstr'
+ */
+int
+pcmspecial(char *idstr, ISAConf *isa)
+{
+ PCMslot *pp;
+ extern char *strstr(char*, char*);
+
+ pcmciareset();
+ for(pp = slot; pp < lastslot; pp++){
+ if(pp->special || pp->memlen == 0)
+ continue; /* already taken */
+ increfp(pp);
+ if(pp->occupied && strstr(pp->verstr, idstr)){
+ print("PCMslot #%d: Found %s - ",pp->slotno, idstr);
+ if(isa == 0 || pcmio(pp->slotno, isa) == 0){
+ print("ok.\n");
+ pp->special = 1;
+ return pp->slotno;
+ }
+ print("error with isa io\n");
+ }
+ decrefp(pp);
+ }
+ return -1;
+}
+
+void
+pcmspecialclose(int slotno)
+{
+ PCMslot *pp;
+
+ if(slotno >= nslot)
+ panic("pcmspecialclose");
+ pp = slot + slotno;
+ pp->special = 0;
+ decrefp(pp);
+}
+
+void
+pcmnotify(int slotno, void (*f)(void*, int), void* a)
+{
+ PCMslot *pp;
+
+ if(slotno < 0 || slotno >= nslot)
+ panic("pcmnotify");
+ pp = slot + slotno;
+ if(pp->occupied && pp->special){
+ pp->notify.f = f;
+ pp->notify.arg = a;
+ }
+}
+
+/*
+ * reserve pcmcia slot address space [addr, addr+size[,
+ * returning a pointer to it, or nil if the space was already reserved.
+ */
+static ulong
+pcmmalloc(ulong addr, long size)
+{
+ return rmapalloc(&pcmmaps, PHYSPCMCIA+addr, size, size);
+}
+
+static void
+pcmfree(ulong a, long size)
+{
+ if(a != 0 && size > 0)
+ mapfree(&pcmmaps, a, size);
+}
+
+enum
+{
+ Qdir,
+ Qmem,
+ Qattr,
+ Qctl,
+};
+
+#define SLOTNO(c) ((c->qid.path>>8)&0xff)
+#define TYPE(c) (c->qid.path&0xff)
+#define QID(s,t) (((s)<<8)|(t))
+
+static int
+pcmgen(Chan *c, char*, Dirtab*, int, int i, Dir *dp)
+{
+ int slotno;
+ Qid qid;
+ long len;
+ PCMslot *pp;
+
+ if(i>=3*nslot)
+ return -1;
+ slotno = i/3;
+ pp = slot + slotno;
+ if(pp->memlen == 0)
+ return 0;
+ 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;
+ devdir(c, qid, up->genbuf, len, eve, 0660, dp);
+ return 1;
+}
+
+/*
+ * used only when debugging
+ */
+static void
+pcmciadump(PCMslot *)
+{
+ IMM *io;
+ int i;
+
+ io = m->iomem;
+ print("pipr #%4.4lux pscr #%4.4lux per #%4.4lux pgcr[1] #%8.8lux\n",
+ io->pipr & 0xFFFF, io->pscr & 0xFFFF, io->per & 0xFFFF, io->pgcr[1]);
+ for(i=0; i<8; i++)
+ print("pbr%d #%8.8lux por%d #%8.8lux\n", i, io->pcmr[i].base, i, io->pcmr[i].option);
+}
+
+/*
+ * set up for slot cards
+ */
+static void
+pcmciareset(void)
+{
+ static int already;
+ int i;
+ Ctlr *cp;
+ IMM *io;
+ PCMslot *pp;
+
+ if(already)
+ return;
+ already = 1;
+
+ cp = controller;
+ /* TO DO: set low power mode? ... */
+
+ mapinit(&pcmmaps, pcmmapv, sizeof(pcmmapv));
+ mapfree(&pcmmaps, PHYSPCMCIA, PCMCIALEN);
+
+ io = m->iomem;
+
+ for(i=0; i<8; i++){
+ io->pcmr[i].option = 0;
+ io->pcmr[i].base = 0;
+ }
+
+ io->pscr = ~0; /* reset status */
+ /* TO DO: Cboe, Cbreset */
+ /* TO DO: two slots except on 823 */
+ pcmenable();
+ /* TO DO: if the card is there turn on 5V power to keep its battery alive */
+ slot = xalloc(Maxslot * sizeof(PCMslot));
+ lastslot = slot;
+ slot[0].slotshift = Slotashift;
+ slot[1].slotshift = 0;
+ for(i=0; i<Maxslot; i++){
+ pp = &slot[i];
+ if(!pcmslotavail(i)){
+ pp->memlen = 0;
+ continue;
+ }
+ io->per |= (Cbvs1_c|Cbvs2_c|Cbwp_c|Cbcd2_c|Cbcd1_c|Cbbvd2_c|Cbbvd1_c)<<pp->slotshift; /* enable status interrupts */
+ io->pgcr[i] = (1<<(31-PCMCIAio)) | (1<<(23-PCMCIAstatus));
+ pp->slotno = i;
+ pp->memlen = 8*MB;
+ pp->ctlr = cp;
+ //slotdis(pp);
+ lastslot = slot;
+ nslot = i+1;
+ }
+ if(1)
+ pcmciadump(slot);
+ intrenable(PCMCIAstatus, pcmciaintr, cp, BUSUNKNOWN, "pcmcia");
+ print("pcmcia reset\n");
+}
+
+static void
+pcmciainit(void)
+{
+ kproc("pcmcia", pcmciaproc, nil, 0);
+}
+
+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 *dp, int n)
+{
+ return devstat(c, dp, 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)
+{
+ int i, len;
+ PCMmap *m;
+ void *ka;
+ uchar *ac;
+ PCMslot *pp;
+
+ pp = slot + slotno;
+ if(pp->memlen < offset)
+ return 0;
+ if(pp->memlen < offset + n)
+ n = pp->memlen - offset;
+
+ ac = a;
+ for(len = n; len > 0; len -= i){
+ if((i = len) > Mgran)
+ i = Mgran;
+ m = pcmmap(pp->slotno, offset, i, attr? Rattrib: Rmem);
+ if(m == 0)
+ error("can't map PCMCIA card");
+ if(waserror()){
+ if(m)
+ pcmunmap(pp->slotno, m);
+ nexterror();
+ }
+ if(offset + len > m->cea)
+ i = m->cea - offset;
+ else
+ i = len;
+ ka = (char*)KADDR(m->isa) + (offset - m->ca);
+ memmoveb(ac, ka, i);
+ poperror();
+ pcmunmap(pp->slotno, m);
+ offset += i;
+ ac += i;
+ }
+
+ return n;
+}
+
+static long
+pcmciaread(Chan *c, void *a, long n, vlong offset)
+{
+ char *cp, *buf;
+ ulong p;
+ PCMslot *pp;
+
+ 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(READSTR);
+ if(buf == nil)
+ error(Enomem);
+ 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->wrprot)
+ cp += sprint(cp, "write protected\n");
+ if(pp->busy)
+ cp += sprint(cp, "busy\n");
+ if(pp->v3_3)
+ cp += sprint(cp, "3.3v ok\n");
+ cp += sprint(cp, "battery lvl %d\n", pp->battery);
+ cp += sprint(cp, "voltage select %d\n", pp->voltage);
+ /* TO DO: could return pgcr[] values for debugging */
+ *cp = 0;
+ n = readstr(offset, a, n, buf);
+ poperror();
+ free(buf);
+ break;
+ default:
+ n=0;
+ break;
+ }
+ return n;
+}
+
+static long
+pcmwrite(int dev, int attr, void *a, long n, ulong offset)
+{
+ int i, len;
+ PCMmap *m;
+ void *ka;
+ uchar *ac;
+ PCMslot *pp;
+
+ pp = slot + dev;
+ if(pp->memlen < offset)
+ return 0;
+ if(pp->memlen < offset + n)
+ n = pp->memlen - offset;
+
+ ac = a;
+ for(len = n; len > 0; len -= i){
+ if((i = len) > Mgran)
+ i = Mgran;
+ m = pcmmap(pp->slotno, offset, i, attr? Rattrib: Rmem);
+ if(m == 0)
+ error("can't map PCMCIA card");
+ if(waserror()){
+ if(m)
+ pcmunmap(pp->slotno, m);
+ nexterror();
+ }
+ if(offset + len > m->cea)
+ i = m->cea - offset;
+ else
+ i = len;
+ ka = (char*)KADDR(m->isa) + (offset - m->ca);
+ memmoveb(ka, ac, i);
+ poperror();
+ pcmunmap(pp->slotno, m);
+ offset += i;
+ ac += i;
+ }
+
+ return n;
+}
+
+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);
+
+ /* set vpp on card */
+ if(strncmp(buf, "vpp", 3) == 0){
+ p = strtol(buf+3, nil, 0);
+ pcmsetvpp(pp->slotno, p);
+ }
+ break;
+ case Qmem:
+ case Qattr:
+ pp = slot + SLOTNO(c);
+ if(pp->occupied == 0 || pp->enabled == 0)
+ error(Eio);
+ n = pcmwrite(pp->slotno, p == Qattr, a, n, offset);
+ if(n < 0)
+ error(Eio);
+ break;
+ default:
+ error(Ebadusefd);
+ }
+ return n;
+}
+
+Dev pcmciadevtab = {
+ 'y',
+ "pcmcia",
+
+ pcmciareset,
+ pcmciainit,
+ 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
+pcmio(int slotno, ISAConf *isa)
+{
+ PCMslot *pp;
+ PCMconftab *ct, *et, *t;
+ PCMmap *pm;
+ uchar *p;
+ int irq, i, x;
+
+ irq = isa->irq;
+ if(irq == 2)
+ irq = 9;
+
+ if(slotno > nslot)
+ return -1;
+ pp = slot + slotno;
+
+ if(!pp->occupied)
+ return -1;
+
+ et = &pp->ctab[pp->nctab];
+
+ /* assume default is right */
+ if(pp->def)
+ ct = pp->def;
+ else
+ ct = pp->ctab;
+ /* try for best match */
+ if(ct->nlines == 0 || ct->io[0].start != isa->port || ((1<<irq) & ct->irqs) == 0){
+ for(t = pp->ctab; t < et; t++)
+ if(t->nlines && t->io[0].start == isa->port && ((1<<irq) & t->irqs)){
+ ct = t;
+ break;
+ }
+ }
+ if(ct->nlines == 0 || ((1<<irq) & ct->irqs) == 0){
+ for(t = pp->ctab; t < et; t++)
+ if(t->nlines && ((1<<irq) & t->irqs)){
+ ct = t;
+ break;
+ }
+ }
+ if(ct->nlines == 0){
+ for(t = pp->ctab; t < et; t++)
+ if(t->nlines){
+ ct = t;
+ break;
+ }
+ }
+print("slot %d: nlines=%d iolen=%lud irq=%d ct->index=%d nport=%d ct->port=#%lux/%lux\n", slotno, ct->nlines, ct->io[0].len, irq, ct->index, ct->nio, ct->io[0].start, isa->port);
+ if(ct == et || ct->nlines == 0)
+ return -1;
+ /* route interrupts */
+ isa->irq = irq;
+ //wrreg(pp, Rigc, irq | Fnotreset | Fiocard);
+ delay(2);
+
+ /* set power and enable device */
+ pcmsetvcc(pp->slotno, ct->vcc);
+ pcmsetvpp(pp->slotno, ct->vpp1);
+
+ delay(2); /* could poll BSY during power change */
+
+ for(i=0; i<ct->nio; i++)
+ pcmiomap(pp, ct, i);
+
+ if(ct->nio)
+ isa->port = ct->io[0].start;
+
+ /* only touch Rconfig if it is present */
+ if(pp->cpresent & (1<<Rconfig)){
+print("Rconfig present: #%lux\n", pp->caddr+Rconfig);
+ /* Reset adapter */
+ pm = pcmmap(slotno, pp->caddr + Rconfig, 1, Rattrib);
+ if(pm == nil)
+ return -1;
+
+ p = (uchar*)KADDR(pm->isa) + (pp->caddr + Rconfig - pm->ca);
+
+ /* set configuration and interrupt type */
+ x = ct->index;
+ if((ct->irqtype & 0x20) && ((ct->irqtype & 0x40)==0 || isa->irq>7))
+ x |= Clevel;
+ *p = x;
+ delay(5);
+
+ pcmunmap(pp->slotno, pm);
+print("Adapter reset\n");
+ }
+
+ return 0;
+}