#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<mlock); /* look for an existing map that covers the right area */ io = m->iomem; nm = nil; for(i=0; immap[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<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; imemlen = 0; continue; } io->per |= (Cbvs1_c|Cbvs2_c|Cbwp_c|Cbcd2_c|Cbcd1_c|Cbbvd2_c|Cbbvd1_c)<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<irqs) == 0){ for(t = pp->ctab; t < et; t++) if(t->nlines && t->io[0].start == isa->port && ((1<irqs)){ ct = t; break; } } if(ct->nlines == 0 || ((1<irqs) == 0){ for(t = pp->ctab; t < et; t++) if(t->nlines && ((1<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; inio; 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<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; }