diff options
Diffstat (limited to 'os/boot/pc/devi82365.c')
| -rw-r--r-- | os/boot/pc/devi82365.c | 1205 |
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; +} |
