summaryrefslogtreecommitdiff
path: root/os/boot/pc/devi82365.c
diff options
context:
space:
mode:
Diffstat (limited to 'os/boot/pc/devi82365.c')
-rw-r--r--os/boot/pc/devi82365.c1205
1 files changed, 1205 insertions, 0 deletions
diff --git a/os/boot/pc/devi82365.c b/os/boot/pc/devi82365.c
new file mode 100644
index 00000000..a4e09d2d
--- /dev/null
+++ b/os/boot/pc/devi82365.c
@@ -0,0 +1,1205 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#include "io.h"
+
+/*
+ * Support for up to 4 Slot card slots. Generalizing above that is hard
+ * since addressing is not obvious. - presotto
+ *
+ * WARNING: This has never been tried with more than one card slot.
+ */
+
+/*
+ * Intel 82365SL PCIC controller for the PCMCIA or
+ * Cirrus Logic PD6710/PD6720 which is mostly register compatible
+ */
+enum
+{
+ /*
+ * registers indices
+ */
+ Rid= 0x0, /* identification and revision */
+ Ris= 0x1, /* interface status */
+ Rpc= 0x2, /* power control */
+ Foutena= (1<<7), /* output enable */
+ Fautopower= (1<<5), /* automatic power switching */
+ Fcardena= (1<<4), /* PC card enable */
+ Rigc= 0x3, /* interrupt and general control */
+ Fiocard= (1<<5), /* I/O card (vs memory) */
+ Fnotreset= (1<<6), /* reset if not set */
+ FSMIena= (1<<4), /* enable change interrupt on SMI */
+ Rcsc= 0x4, /* card status change */
+ Rcscic= 0x5, /* card status change interrupt config */
+ Fchangeena= (1<<3), /* card changed */
+ Fbwarnena= (1<<1), /* card battery warning */
+ Fbdeadena= (1<<0), /* card battery dead */
+ Rwe= 0x6, /* address window enable */
+ Fmem16= (1<<5), /* use A23-A12 to decode address */
+ Rio= 0x7, /* I/O control */
+ Fwidth16= (1<<0), /* 16 bit data width */
+ Fiocs16= (1<<1), /* IOCS16 determines data width */
+ Fzerows= (1<<2), /* zero wait state */
+ Ftiming= (1<<3), /* timing register to use */
+ Riobtm0lo= 0x8, /* I/O address 0 start low byte */
+ Riobtm0hi= 0x9, /* I/O address 0 start high byte */
+ Riotop0lo= 0xa, /* I/O address 0 stop low byte */
+ Riotop0hi= 0xb, /* I/O address 0 stop high byte */
+ Riobtm1lo= 0xc, /* I/O address 1 start low byte */
+ Riobtm1hi= 0xd, /* I/O address 1 start high byte */
+ Riotop1lo= 0xe, /* I/O address 1 stop low byte */
+ Riotop1hi= 0xf, /* I/O address 1 stop high byte */
+ Rmap= 0x10, /* map 0 */
+
+ /*
+ * CL-PD67xx extension registers
+ */
+ Rmisc1= 0x16, /* misc control 1 */
+ F5Vdetect= (1<<0),
+ Fvcc3V= (1<<1),
+ Fpmint= (1<<2),
+ Fpsirq= (1<<3),
+ Fspeaker= (1<<4),
+ Finpack= (1<<7),
+ Rfifo= 0x17, /* fifo control */
+ Fflush= (1<<7), /* flush fifo */
+ Rmisc2= 0x1E, /* misc control 2 */
+ Flowpow= (1<<1), /* low power mode */
+ Rchipinfo= 0x1F, /* chip information */
+ Ratactl= 0x26, /* ATA control */
+
+ /*
+ * offsets into the system memory address maps
+ */
+ Mbtmlo= 0x0, /* System mem addr mapping start low byte */
+ Mbtmhi= 0x1, /* System mem addr mapping start high byte */
+ F16bit= (1<<7), /* 16-bit wide data path */
+ Mtoplo= 0x2, /* System mem addr mapping stop low byte */
+ Mtophi= 0x3, /* System mem addr mapping stop high byte */
+ Ftimer1= (1<<6), /* timer set 1 */
+ Mofflo= 0x4, /* Card memory offset address low byte */
+ Moffhi= 0x5, /* Card memory offset address high byte */
+ Fregactive= (1<<6), /* attribute memory */
+
+ Mbits= 13, /* msb of Mchunk */
+ Mchunk= 1<<Mbits, /* logical mapping granularity */
+ Nmap= 4, /* 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 */
+
+ Maxctab= 8, /* maximum configuration table entries */
+};
+
+static int pcmcia_pcmspecial(char *, ISAConf *);
+static void pcmcia_pcmspecialclose(int);
+
+#define MAP(x,o) (Rmap + (x)*0x8 + o)
+
+typedef struct I82365 I82365;
+typedef struct Slot Slot;
+typedef struct Conftab Conftab;
+typedef struct Cisdat Cisdat;
+/* a controller */
+enum
+{
+ Ti82365,
+ Tpd6710,
+ Tpd6720,
+ Tvg46x,
+};
+struct I82365
+{
+ int type;
+ int dev;
+ int nslot;
+ int xreg; /* index register address */
+ int dreg; /* data register address */
+ int irq;
+};
+static I82365 *controller[4];
+static int ncontroller;
+
+/* configuration table entry */
+struct Conftab
+{
+ int index;
+ ushort irqs; /* legal irqs */
+ uchar irqtype;
+ uchar bit16; /* true for 16 bit access */
+ struct {
+ ulong start;
+ ulong len;
+ } io[16];
+ int nio;
+ uchar vpp1;
+ uchar vpp2;
+ uchar memwait;
+ ulong maxwait;
+ ulong readywait;
+ ulong otherwait;
+};
+
+/* cis memory walking */
+struct Cisdat
+{
+ uchar *cisbase;
+ int cispos;
+ int cisskip;
+ int cislen;
+};
+
+/* a card slot */
+struct Slot
+{
+ Lock;
+ int ref;
+
+ I82365 *cp; /* controller for this slot */
+ long memlen; /* memory length */
+ uchar base; /* index register base */
+ uchar slotno; /* slot number */
+
+ /* status */
+ uchar special; /* in use for a special device */
+ uchar already; /* already inited */
+ uchar occupied;
+ uchar battery;
+ uchar wrprot;
+ uchar powered;
+ uchar configed;
+ uchar enabled;
+ uchar busy;
+
+ /* cis info */
+ char verstr[512]; /* version string */
+ uchar cpresent; /* config registers present */
+ ulong caddr; /* relative address of config registers */
+ int nctab; /* number of config table entries */
+ Conftab ctab[Maxctab];
+ Conftab *def; /* default conftab */
+
+ /* for walking through cis */
+ Cisdat;
+
+ /* memory maps */
+ Lock mlock; /* lock down the maps */
+ int time;
+ PCMmap mmap[Nmap]; /* maps, last is always for the kernel */
+};
+static Slot *slot;
+static Slot *lastslot;
+static nslot;
+
+static void cisread(Slot*);
+static void i82365intr(Ureg*, void*);
+static void i82365reset(void);
+static int pcmio(int, ISAConf*);
+static long pcmread(int, int, void*, long, vlong);
+static long pcmwrite(int, int, void*, long, vlong);
+
+static void i82365dump(Slot*);
+
+void
+devi82365link(void)
+{
+ static int already;
+
+ if(already)
+ return;
+ already = 1;
+
+ if (_pcmspecial)
+ return;
+
+ _pcmspecial = pcmcia_pcmspecial;
+ _pcmspecialclose = pcmcia_pcmspecialclose;
+}
+
+/*
+ * reading and writing card registers
+ */
+static uchar
+rdreg(Slot *pp, int index)
+{
+ outb(pp->cp->xreg, pp->base + index);
+ return inb(pp->cp->dreg);
+}
+static void
+wrreg(Slot *pp, int index, uchar val)
+{
+ outb(pp->cp->xreg, pp->base + index);
+ outb(pp->cp->dreg, val);
+}
+
+/*
+ * get info about card
+ */
+static void
+slotinfo(Slot *pp)
+{
+ uchar isr;
+
+ isr = rdreg(pp, Ris);
+ pp->occupied = (isr & (3<<2)) == (3<<2);
+ pp->powered = isr & (1<<6);
+ pp->battery = (isr & 3) == 3;
+ pp->wrprot = isr & (1<<4);
+ pp->busy = isr & (1<<5);
+}
+
+static int
+vcode(int volt)
+{
+ switch(volt){
+ case 5:
+ return 1;
+ case 12:
+ return 2;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * enable the slot card
+ */
+static void
+slotena(Slot *pp)
+{
+ if(pp->enabled)
+ return;
+
+ /* power up and unreset, wait's are empirical (???) */
+ wrreg(pp, Rpc, Fautopower|Foutena|Fcardena);
+ delay(300);
+ wrreg(pp, Rigc, 0);
+ delay(100);
+ wrreg(pp, Rigc, Fnotreset);
+ delay(500);
+
+ /* get configuration */
+ slotinfo(pp);
+ if(pp->occupied){
+ cisread(pp);
+ pp->enabled = 1;
+ } else
+ wrreg(pp, Rpc, Fautopower);
+}
+
+/*
+ * disable the slot card
+ */
+static void
+slotdis(Slot *pp)
+{
+ wrreg(pp, Rpc, 0); /* turn off card power */
+ wrreg(pp, Rwe, 0); /* no windows */
+ pp->enabled = 0;
+}
+
+/*
+ * status change interrupt
+ */
+static void
+i82365intr(Ureg *, void *)
+{
+ uchar csc, was;
+ Slot *pp;
+
+ if(slot == 0)
+ return;
+
+ for(pp = slot; pp < lastslot; pp++){
+ csc = rdreg(pp, Rcsc);
+ was = pp->occupied;
+ slotinfo(pp);
+ if(csc & (1<<3) && was != pp->occupied){
+ if(!pp->occupied)
+ slotdis(pp);
+ }
+ }
+}
+
+enum
+{
+ Mshift= 12,
+ Mgran= (1<<Mshift), /* granularity of maps */
+ Mmask= ~(Mgran-1), /* mask for address bits important to the chip */
+};
+
+/*
+ * get a map for pc card region, return corrected len
+ */
+PCMmap*
+pcmmap(int slotno, ulong offset, int len, int attr)
+{
+ Slot *pp;
+ uchar we, bit;
+ PCMmap *m, *nm;
+ int i;
+ ulong e;
+
+ pp = slot + slotno;
+ lock(&pp->mlock);
+
+ /* convert offset to granularity */
+ if(len <= 0)
+ len = 1;
+ e = ROUND(offset+len, Mgran);
+ offset &= Mmask;
+ len = e - offset;
+
+ /* look for a map that covers the right area */
+ we = rdreg(pp, Rwe);
+ bit = 1;
+ nm = 0;
+ for(m = pp->mmap; m < &pp->mmap[Nmap]; m++){
+ if((we & bit))
+ if(m->attr == attr)
+ if(offset >= m->ca && e <= m->cea){
+
+ m->ref++;
+ unlock(&pp->mlock);
+ return m;
+ }
+ bit <<= 1;
+ if(nm == 0 && m->ref == 0)
+ nm = m;
+ }
+ m = nm;
+ if(m == 0){
+ unlock(&pp->mlock);
+ return 0;
+ }
+
+ /* if isa space isn't big enough, free it and get more */
+ if(m->len < len){
+ if(m->isa){
+ umbfree(m->isa, m->len);
+ m->len = 0;
+ }
+ m->isa = PADDR(umbmalloc(0, len, Mgran));
+ if(m->isa == 0){
+ print("pcmmap %d: out of isa space\n", len);
+ unlock(&pp->mlock);
+ return 0;
+ }
+ m->len = len;
+ }
+
+ /* set up new map */
+ m->ca = offset;
+ m->cea = m->ca + m->len;
+ m->attr = attr;
+ i = m-pp->mmap;
+ bit = 1<<i;
+ wrreg(pp, Rwe, we & ~bit); /* disable map before changing it */
+ wrreg(pp, MAP(i, Mbtmlo), m->isa>>Mshift);
+ wrreg(pp, MAP(i, Mbtmhi), (m->isa>>(Mshift+8)) | F16bit);
+ wrreg(pp, MAP(i, Mtoplo), (m->isa+m->len-1)>>Mshift);
+ wrreg(pp, MAP(i, Mtophi), ((m->isa+m->len-1)>>(Mshift+8)));
+ offset -= m->isa;
+ offset &= (1<<25)-1;
+ offset >>= Mshift;
+ wrreg(pp, MAP(i, Mofflo), offset);
+ wrreg(pp, MAP(i, Moffhi), (offset>>8) | (attr ? Fregactive : 0));
+ wrreg(pp, Rwe, we | bit); /* enable map */
+ m->ref = 1;
+
+ unlock(&pp->mlock);
+ return m;
+}
+
+void
+pcmunmap(int slotno, PCMmap* m)
+{
+ Slot *pp;
+
+ pp = slot + slotno;
+ lock(&pp->mlock);
+ m->ref--;
+ unlock(&pp->mlock);
+}
+
+static void
+increfp(Slot *pp)
+{
+ lock(pp);
+ if(pp->ref++ == 0)
+ slotena(pp);
+ unlock(pp);
+}
+
+static void
+decrefp(Slot *pp)
+{
+ lock(pp);
+ if(pp->ref-- == 1)
+ slotdis(pp);
+ unlock(pp);
+}
+
+/*
+ * look for a card whose version contains 'idstr'
+ */
+static int
+pcmcia_pcmspecial(char *idstr, ISAConf *isa)
+{
+ Slot *pp;
+ extern char *strstr(char*, char*);
+ int enabled;
+
+ i82365reset();
+ for(pp = slot; pp < lastslot; pp++){
+ if(pp->special)
+ continue; /* already taken */
+ enabled = 0;
+ /* make sure we don't power on cards when we already know what's
+ * in them. We'll reread every two minutes if necessary
+ */
+ if (pp->verstr[0] == '\0') {
+ increfp(pp);
+ enabled++;
+ }
+
+ if(pp->occupied) {
+ if(strstr(pp->verstr, idstr)) {
+ if (!enabled)
+ increfp(pp);
+ if(isa == 0 || pcmio(pp->slotno, isa) == 0){
+ pp->special = 1;
+ return pp->slotno;
+ }
+ }
+ } else
+ pp->special = 1;
+ if (enabled)
+ decrefp(pp);
+ }
+ return -1;
+}
+
+static void
+pcmcia_pcmspecialclose(int slotno)
+{
+ Slot *pp;
+
+ print("pcmspecialclose called\n");
+ if(slotno >= nslot)
+ panic("pcmspecialclose");
+ pp = slot + slotno;
+ pp->special = 0;
+ decrefp(pp);
+}
+
+static char *chipname[] =
+{
+[Ti82365] "Intel 82365SL",
+[Tpd6710] "Cirrus Logic PD6710",
+[Tpd6720] "Cirrus Logic PD6720",
+[Tvg46x] "Vadem VG-46x",
+};
+
+static I82365*
+i82365probe(int x, int d, int dev)
+{
+ uchar c, id;
+ I82365 *cp;
+ ISAConf isa;
+ int i, nslot;
+
+ outb(x, Rid + (dev<<7));
+ id = inb(d);
+ if((id & 0xf0) != 0x80)
+ return 0; /* not this family */
+
+ cp = xalloc(sizeof(I82365));
+ cp->xreg = x;
+ cp->dreg = d;
+ cp->dev = dev;
+ cp->type = Ti82365;
+ cp->nslot = 2;
+
+ switch(id){
+ case 0x82:
+ case 0x83:
+ case 0x84:
+ /* could be a cirrus */
+ outb(x, Rchipinfo + (dev<<7));
+ outb(d, 0);
+ c = inb(d);
+ if((c & 0xc0) != 0xc0)
+ break;
+ c = inb(d);
+ if((c & 0xc0) != 0x00)
+ break;
+ if(c & 0x20){
+ cp->type = Tpd6720;
+ } else {
+ cp->type = Tpd6710;
+ cp->nslot = 1;
+ }
+
+ /* low power mode */
+ outb(x, Rmisc2 + (dev<<7));
+ c = inb(d);
+ outb(d, c & ~Flowpow);
+ break;
+ }
+
+ if(cp->type == Ti82365){
+ outb(x, 0x0E + (dev<<7));
+ outb(x, 0x37 + (dev<<7));
+ outb(x, 0x3A + (dev<<7));
+ c = inb(d);
+ outb(d, c|0xC0);
+ outb(x, Rid + (dev<<7));
+ c = inb(d);
+ if(c != id && !(c & 0x08))
+ print("#y%d: id %uX changed to %uX\n", ncontroller, id, c);
+ if(c & 0x08)
+ cp->type = Tvg46x;
+ outb(x, 0x3A + (dev<<7));
+ c = inb(d);
+ outb(d, c & ~0xC0);
+ }
+
+ memset(&isa, 0, sizeof(ISAConf));
+ if(isaconfig("pcmcia", ncontroller, &isa) && isa.irq)
+ cp->irq = isa.irq;
+ else
+ cp->irq = VectorPCMCIA - VectorPIC;
+
+ for(i = 0; i < isa.nopt; i++){
+ if(cistrncmp(isa.opt[i], "nslot=", 6))
+ continue;
+ nslot = strtol(&isa.opt[i][6], nil, 0);
+ if(nslot > 0 && nslot <= 2)
+ cp->nslot = nslot;
+ }
+
+ controller[ncontroller++] = cp;
+ return cp;
+}
+
+static void
+i82365dump(Slot *pp)
+{
+ int i;
+
+ for(i = 0; i < 0x40; i++){
+ if((i&0x0F) == 0)
+ print("\n%2.2uX: ", i);
+ if(((i+1) & 0x0F) == 0x08)
+ print(" - ");
+ print("%2.2uX ", rdreg(pp, i));
+ }
+ print("\n");
+}
+
+/*
+ * set up for slot cards
+ */
+static void
+i82365reset(void)
+{
+ static int already;
+ int i, j;
+ I82365 *cp;
+ Slot *pp;
+
+ if(already)
+ return;
+ already = 1;
+
+
+ /* look for controllers */
+ i82365probe(0x3E0, 0x3E1, 0);
+ i82365probe(0x3E0, 0x3E1, 1);
+ i82365probe(0x3E2, 0x3E3, 0);
+ i82365probe(0x3E2, 0x3E3, 1);
+
+ for(i = 0; i < ncontroller; i++)
+ nslot += controller[i]->nslot;
+ slot = xalloc(nslot * sizeof(Slot));
+
+ /* if the card is there turn on 5V power to keep its battery alive */
+ lastslot = slot;
+ for(i = 0; i < ncontroller; i++){
+ cp = controller[i];
+ print("#y%d: %d slot %s: port 0x%uX irq %d\n",
+ i, cp->nslot, chipname[cp->type], cp->xreg, cp->irq);
+ for(j = 0; j < cp->nslot; j++){
+ pp = lastslot++;
+ pp->slotno = pp - slot;
+ pp->memlen = 64*MB;
+ pp->base = (cp->dev<<7) | (j<<6);
+ pp->cp = cp;
+ slotdis(pp);
+
+ /* interrupt on status change */
+ wrreg(pp, Rcscic, (cp->irq<<4) | Fchangeena);
+ rdreg(pp, Rcsc);
+ }
+
+ /* for card management interrupts */
+ setvec(cp->irq+VectorPIC, i82365intr, 0);
+ }
+}
+
+/*
+ * configure the Slot 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)
+{
+ uchar we, x, *p;
+ Slot *pp;
+ Conftab *ct, *et, *t;
+ PCMmap *m;
+ int i, index, irq;
+ char *cp;
+
+ 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];
+
+ ct = 0;
+ 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 >= pp->nctab)
+ return -1;
+ ct = &pp->ctab[index];
+ }
+ if(ct == 0){
+
+ /* assume default is right */
+ if(pp->def)
+ ct = pp->def;
+ else
+ ct = pp->ctab;
+
+ /* try for best match */
+ if(ct->nio == 0
+ || ct->io[0].start != isa->port || ((1<<irq) & ct->irqs) == 0){
+ for(t = pp->ctab; t < et; t++)
+ if(t->nio
+ && t->io[0].start == isa->port
+ && ((1<<irq) & t->irqs)){
+ ct = t;
+ break;
+ }
+ }
+ if(ct->nio == 0 || ((1<<irq) & ct->irqs) == 0){
+ for(t = pp->ctab; t < et; t++)
+ if(t->nio && ((1<<irq) & t->irqs)){
+ ct = t;
+ break;
+ }
+ }
+ if(ct->nio == 0){
+ for(t = pp->ctab; t < et; t++)
+ if(t->nio){
+ ct = t;
+ break;
+ }
+ }
+ }
+
+ if(ct == et || ct->nio == 0)
+ return -1;
+ if(isa->port == 0 && ct->io[0].start == 0)
+ return -1;
+
+ /* route interrupts */
+ isa->irq = irq;
+ wrreg(pp, Rigc, irq | Fnotreset | Fiocard);
+
+ /* set power and enable device */
+ x = vcode(ct->vpp1);
+ wrreg(pp, Rpc, x|Fautopower|Foutena|Fcardena);
+
+ /* 16-bit data path */
+ if(ct->bit16)
+ x = Ftiming|Fiocs16|Fwidth16;
+ else
+ x = Ftiming;
+ if(ct->nio == 2 && ct->io[1].start)
+ x |= x<<4;
+ wrreg(pp, Rio, x);
+
+ /* enable io port map 0 */
+ if(isa->port == 0)
+ isa->port = ct->io[0].start;
+ we = rdreg(pp, Rwe);
+ wrreg(pp, Riobtm0lo, isa->port);
+ wrreg(pp, Riobtm0hi, isa->port>>8);
+ i = isa->port+ct->io[0].len-1;
+ wrreg(pp, Riotop0lo, i);
+ wrreg(pp, Riotop0hi, i>>8);
+ we |= 1<<6;
+ if(ct->nio == 2 && ct->io[1].start){
+ wrreg(pp, Riobtm1lo, ct->io[1].start);
+ wrreg(pp, Riobtm1hi, ct->io[1].start>>8);
+ i = ct->io[1].start+ct->io[1].len-1;
+ wrreg(pp, Riotop1lo, i);
+ wrreg(pp, Riotop1hi, i>>8);
+ we |= 1<<7;
+ }
+ wrreg(pp, Rwe, we);
+
+ /* only touch Rconfig if it is present */
+ if(pp->cpresent & (1<<Rconfig)){
+ /* Reset adapter */
+ m = pcmmap(slotno, pp->caddr + Rconfig, 1, 1);
+ p = KADDR(m->isa + pp->caddr + Rconfig - m->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(slotno, m);
+ }
+ return 0;
+}
+
+/*
+ * read and crack the card information structure enough to set
+ * important parameters like power
+ */
+static void tcfig(Slot*, Cisdat*, int);
+static void tentry(Slot*, Cisdat*, int);
+static void tvers1(Slot*, Cisdat*, int);
+
+struct {
+ int n;
+ void (*parse)(Slot*, Cisdat*, int);
+} cistab[] = {
+ 0x15, tvers1,
+ 0x1A, tcfig,
+ 0x1B, tentry,
+};
+
+static int
+readc(Cisdat *pp, uchar *x)
+{
+ if(pp->cispos >= pp->cislen)
+ return 0;
+ *x = pp->cisbase[pp->cisskip*pp->cispos];
+ pp->cispos++;
+ return 1;
+}
+
+static int
+xcistuple(int slotno, int tuple, void *v, int nv, int attr)
+{
+ PCMmap *m;
+ Cisdat cis;
+ int i, l;
+ uchar *p;
+ uchar type, link;
+ int this;
+
+ m = pcmmap(slotno, 0, 0, attr);
+ if(m == 0) {
+if(debug) print("could not map\n");
+ return -1;
+ }
+
+ cis.cisbase = KADDR(m->isa);
+ cis.cispos = 0;
+ cis.cisskip = attr ? 2 : 1;
+ cis.cislen = Mchunk;
+
+if(debug) print("cis %d %d #%lux srch %x...", attr, cis.cisskip, cis.cisbase, tuple);
+ /* loop through all the tuples */
+ for(i = 0; i < 1000; i++){
+ this = cis.cispos;
+ if(readc(&cis, &type) != 1)
+ break;
+if(debug) print("%2ux...", type);
+ if(type == 0xFF)
+ break;
+ if(readc(&cis, &link) != 1)
+ break;
+ if(link == 0xFF)
+ break;
+ if(type == tuple) {
+ p = v;
+ for(l=0; l<nv && l<link; l++)
+ if(readc(&cis, p++) != 1)
+ break;
+ pcmunmap(slotno, m);
+if(debug) print("pcm find %2.2ux %d %d\n", type, link, l);
+ return l;
+ }
+ cis.cispos = this + (2+link);
+ }
+ pcmunmap(slotno, m);
+ return -1;
+}
+
+int
+pcmcistuple(int slotno, int tuple, void *v, int nv)
+{
+ int n;
+
+ /* try attribute space, then memory */
+ if((n = xcistuple(slotno, tuple, v, nv, 1)) >= 0)
+ return n;
+ return xcistuple(slotno, tuple, v, nv, 0);
+}
+
+static void
+cisread(Slot *pp)
+{
+ uchar v[256];
+ int i, nv;
+ Cisdat cis;
+
+ memset(pp->ctab, 0, sizeof(pp->ctab));
+ pp->caddr = 0;
+ pp->cpresent = 0;
+ pp->configed = 0;
+ pp->nctab = 0;
+
+ for(i = 0; i < nelem(cistab); i++) {
+ if((nv = pcmcistuple(pp->slotno, cistab[i].n, v, sizeof(v))) >= 0) {
+ cis.cisbase = v;
+ cis.cispos = 0;
+ cis.cisskip = 1;
+ cis.cislen = nv;
+
+ (*cistab[i].parse)(pp, &cis, cistab[i].n);
+ }
+ }
+}
+
+static ulong
+getlong(Cisdat *cis, int size)
+{
+ uchar c;
+ int i;
+ ulong x;
+
+ x = 0;
+ for(i = 0; i < size; i++){
+ if(readc(cis, &c) != 1)
+ break;
+ x |= c<<(i*8);
+ }
+ return x;
+}
+
+static void
+tcfig(Slot *pp, Cisdat *cis, int )
+{
+ uchar size, rasize, rmsize;
+ uchar last;
+
+ if(readc(cis, &size) != 1)
+ return;
+ rasize = (size&0x3) + 1;
+ rmsize = ((size>>2)&0xf) + 1;
+ if(readc(cis, &last) != 1)
+ return;
+ pp->caddr = getlong(cis, rasize);
+ pp->cpresent = getlong(cis, rmsize);
+}
+
+static ulong vexp[8] =
+{
+ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000
+};
+static ulong vmant[16] =
+{
+ 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, 90,
+};
+
+static ulong
+microvolt(Cisdat *cis)
+{
+ uchar c;
+ ulong microvolts;
+ ulong exp;
+
+ if(readc(cis, &c) != 1)
+ return 0;
+ exp = vexp[c&0x7];
+ microvolts = vmant[(c>>3)&0xf]*exp;
+ while(c & 0x80){
+ if(readc(cis, &c) != 1)
+ return 0;
+ switch(c){
+ case 0x7d:
+ break; /* high impedence when sleeping */
+ case 0x7e:
+ case 0x7f:
+ microvolts = 0; /* no connection */
+ break;
+ default:
+ exp /= 10;
+ microvolts += exp*(c&0x7f);
+ }
+ }
+ return microvolts;
+}
+
+static ulong
+nanoamps(Cisdat *cis)
+{
+ uchar c;
+ ulong nanoamps;
+
+ if(readc(cis, &c) != 1)
+ return 0;
+ nanoamps = vexp[c&0x7]*vmant[(c>>3)&0xf];
+ while(c & 0x80){
+ if(readc(cis, &c) != 1)
+ return 0;
+ if(c == 0x7d || c == 0x7e || c == 0x7f)
+ nanoamps = 0;
+ }
+ return nanoamps;
+}
+
+/*
+ * only nominal voltage is important for config
+ */
+static ulong
+power(Cisdat *cis)
+{
+ uchar feature;
+ ulong mv;
+
+ mv = 0;
+ if(readc(cis, &feature) != 1)
+ return 0;
+ if(feature & 1)
+ mv = microvolt(cis);
+ if(feature & 2)
+ microvolt(cis);
+ if(feature & 4)
+ microvolt(cis);
+ if(feature & 8)
+ nanoamps(cis);
+ if(feature & 0x10)
+ nanoamps(cis);
+ if(feature & 0x20)
+ nanoamps(cis);
+ if(feature & 0x40)
+ nanoamps(cis);
+ return mv/1000000;
+}
+
+static ulong mantissa[16] =
+{ 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, };
+
+static ulong exponent[8] =
+{ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, };
+
+static ulong
+ttiming(Cisdat *cis, int scale)
+{
+ uchar unscaled;
+ ulong nanosecs;
+
+ if(readc(cis, &unscaled) != 1)
+ return 0;
+ nanosecs = (mantissa[(unscaled>>3)&0xf]*exponent[unscaled&7])/10;
+ nanosecs = nanosecs * vexp[scale];
+ return nanosecs;
+}
+
+static void
+timing(Cisdat *cis, Conftab *ct)
+{
+ uchar c, i;
+
+ if(readc(cis, &c) != 1)
+ return;
+ i = c&0x3;
+ if(i != 3)
+ ct->maxwait = ttiming(cis, i); /* max wait */
+ i = (c>>2)&0x7;
+ if(i != 7)
+ ct->readywait = ttiming(cis, i); /* max ready/busy wait */
+ i = (c>>5)&0x7;
+ if(i != 7)
+ ct->otherwait = ttiming(cis, i); /* reserved wait */
+}
+
+static void
+iospaces(Cisdat *cis, Conftab *ct)
+{
+ uchar c;
+ int i, nio;
+
+ ct->nio = 0;
+ if(readc(cis, &c) != 1)
+ return;
+
+ ct->bit16 = ((c>>5)&3) >= 2;
+ if(!(c & 0x80)){
+ ct->io[0].start = 0;
+ ct->io[0].len = 1<<(c&0x1f);
+ ct->nio = 1;
+ return;
+ }
+
+ if(readc(cis, &c) != 1)
+ return;
+
+ nio = (c&0xf)+1;
+ for(i = 0; i < nio; i++){
+ ct->io[i].start = getlong(cis, (c>>4)&0x3);
+ ct->io[i].len = getlong(cis, (c>>6)&0x3)+1;
+ }
+ ct->nio = nio;
+}
+
+static void
+irq(Cisdat *cis, Conftab *ct)
+{
+ uchar c;
+
+ if(readc(cis, &c) != 1)
+ return;
+ ct->irqtype = c & 0xe0;
+ if(c & 0x10)
+ ct->irqs = getlong(cis, 2);
+ else
+ ct->irqs = 1<<(c&0xf);
+ ct->irqs &= 0xDEB8; /* levels available to card */
+}
+
+static void
+memspace(Cisdat *cis, int asize, int lsize, int host)
+{
+ ulong haddress, address, len;
+
+ len = getlong(cis, lsize)*256;
+ address = getlong(cis, asize)*256;
+ USED(len, address);
+ if(host){
+ haddress = getlong(cis, asize)*256;
+ USED(haddress);
+ }
+}
+
+static void
+tentry(Slot *pp, Cisdat *cis, int )
+{
+ uchar c, i, feature;
+ Conftab *ct;
+
+ if(pp->nctab >= Maxctab)
+ return;
+ if(readc(cis, &c) != 1)
+ return;
+ ct = &pp->ctab[pp->nctab++];
+
+ /* copy from last default config */
+ if(pp->def)
+ *ct = *pp->def;
+
+ ct->index = c & 0x3f;
+
+ /* is this the new default? */
+ if(c & 0x40)
+ pp->def = ct;
+
+ /* memory wait specified? */
+ if(c & 0x80){
+ if(readc(cis, &i) != 1)
+ return;
+ if(i&0x80)
+ ct->memwait = 1;
+ }
+
+ if(readc(cis, &feature) != 1)
+ return;
+ switch(feature&0x3){
+ case 1:
+ ct->vpp1 = ct->vpp2 = power(cis);
+ break;
+ case 2:
+ power(cis);
+ ct->vpp1 = ct->vpp2 = power(cis);
+ break;
+ case 3:
+ power(cis);
+ ct->vpp1 = power(cis);
+ ct->vpp2 = power(cis);
+ break;
+ default:
+ break;
+ }
+ if(feature&0x4)
+ timing(cis, ct);
+ if(feature&0x8)
+ iospaces(cis, ct);
+ if(feature&0x10)
+ irq(cis, ct);
+ switch((feature>>5)&0x3){
+ case 1:
+ memspace(cis, 0, 2, 0);
+ break;
+ case 2:
+ memspace(cis, 2, 2, 0);
+ break;
+ case 3:
+ if(readc(cis, &c) != 1)
+ return;
+ for(i = 0; i <= (c&0x7); i++)
+ memspace(cis, (c>>5)&0x3, (c>>3)&0x3, c&0x80);
+ break;
+ }
+ pp->configed++;
+}
+
+static void
+tvers1(Slot *pp, Cisdat *cis, int )
+{
+ uchar c, major, minor;
+ int i;
+
+ if(readc(cis, &major) != 1)
+ return;
+ if(readc(cis, &minor) != 1)
+ return;
+ for(i = 0; i < sizeof(pp->verstr)-1; i++){
+ if(readc(cis, &c) != 1)
+ return;
+ if(c == 0)
+ c = '\n';
+ if(c == 0xff)
+ break;
+ pp->verstr[i] = c;
+ }
+ pp->verstr[i] = 0;
+}