plan9fox/sys/src/9/pc/devi82365.c
cinap_lenrek 5f1b70f437 pc, pc64: new memory map code
This replaces the memory map code for both pc and pc64
kernels with a unified implementation using the new
portable memory map code.

The main motivation is to be robust against broken
e820 memory maps by the bios and delay the Conf.mem[]
allocation after archinit(), so mp and acpi tables
can be reserved and excluded from user memory.

There are a few changes:

new memreserve() function has been added for archinit()
to reserve bios and acpi tables.

upareserve() has been replaced by upaalloc(), which now
has an address argument.

umbrwmalloc() and umbmalloc() have been replaced by
umballoc().

both upaalloc() and umballoc() return physical addresses
or -1 on error. the physical address -1 is now used as
a sentinel value instead of 0 when dealing with physical
addresses.

archmp and archacpi now always use vmap() to access
the bios tables and reserve the ranges. more overflow
checks have been added.

ramscan() has been rewritten using vmap().

to handle the population of kernel memory, pc and pc64
now have pmap() and punmap() functions to do permanent
mappings.
2020-04-04 16:48:37 +02:00

1054 lines
20 KiB
C

#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "io.h"
/*
* Intel 82365SL PCIC controller and compatibles.
*/
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 */
/*
* 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 */
Cirq= (1<<2), /* IRQ enable */
Cdecode= (1<<1), /* address decode */
Cfunc= (1<<0), /* function enable */
Riobase0= 5,
Riobase1= 6,
Riosize= 9,
};
#define MAP(x,o) (Rmap + (x)*0x8 + o)
typedef struct I82365 I82365;
/* 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;
static PCMslot *slot;
static PCMslot *lastslot;
static nslot;
static void i82365intr(Ureg*, 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(PCMslot*);
/*
* reading and writing card registers
*/
static uchar
rdreg(PCMslot *pp, int index)
{
outb(((I82365*)pp->cp)->xreg, pp->base + index);
return inb(((I82365*)pp->cp)->dreg);
}
static void
wrreg(PCMslot *pp, int index, uchar val)
{
outb(((I82365*)pp->cp)->xreg, pp->base + index);
outb(((I82365*)pp->cp)->dreg, val);
}
/*
* get info about card
*/
static void
slotinfo(PCMslot *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);
pp->msec = TK2MS(MACHP(0)->ticks);
}
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(PCMslot *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){
pcmcisread(pp);
pp->enabled = 1;
} else
wrreg(pp, Rpc, Fautopower);
}
/*
* disable the slot card
*/
static void
slotdis(PCMslot *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;
PCMslot *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)
{
PCMslot *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[nelem(pp->mmap)]; 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->len){
umbfree(m->isa, m->len);
m->len = 0;
}
m->isa = umballoc(-1, len, Mgran);
if(m->isa == -1){
m->isa = 0;
print("pcmmap: out of isa space\n");
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)
{
PCMslot *pp;
pp = slot + slotno;
lock(&pp->mlock);
m->ref--;
unlock(&pp->mlock);
}
static void
increfp(PCMslot *pp)
{
lock(pp);
if(pp->ref++ == 0)
slotena(pp);
unlock(pp);
}
static void
decrefp(PCMslot *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)
{
PCMslot *pp;
extern char *strstr(char*, char*);
int enabled;
for(pp = slot; pp < lastslot; pp++){
if(pp->special)
continue; /* already taken */
/*
* make sure we don't power on cards when we already know what's
* in them. We'll reread every two minutes if necessary
*/
enabled = 0;
if (pp->msec == ~0 || TK2MS(MACHP(0)->ticks) - pp->msec > 120000){
increfp(pp);
enabled++;
}
if(pp->occupied) {
if(strstr(pp->verstr, idstr)){
if (!enabled){
enabled = 1;
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)
{
PCMslot *pp;
if(slotno >= nslot)
panic("pcmspecialclose");
pp = slot + slotno;
pp->special = 0;
decrefp(pp);
}
enum
{
Qdir,
Qmem,
Qattr,
Qctl,
Nents = 3,
};
#define SLOTNO(c) ((ulong)((c->qid.path>>8)&0xff))
#define TYPE(c) ((ulong)(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 == DEVDOTDOT){
mkqid(&qid, Qdir, 0, QTDIR);
devdir(c, qid, "#y", 0, eve, 0555, dp);
return 1;
}
if(i >= Nents*nslot)
return -1;
slotno = i/Nents;
pp = slot + slotno;
len = 0;
switch(i%Nents){
case 0:
qid.path = QID(slotno, Qmem);
snprint(up->genbuf, sizeof up->genbuf, "pcm%dmem", slotno);
len = pp->memlen;
break;
case 1:
qid.path = QID(slotno, Qattr);
snprint(up->genbuf, sizeof up->genbuf, "pcm%dattr", slotno);
len = pp->memlen;
break;
case 2:
qid.path = QID(slotno, Qctl);
snprint(up->genbuf, sizeof up->genbuf, "pcm%dctl", slotno);
break;
}
qid.vers = 0;
qid.type = QTFILE;
devdir(c, qid, up->genbuf, len, eve, 0660, dp);
return 1;
}
static char *chipname[] =
{
[Ti82365] "Intel 82365SL",
[Tpd6710] "Cirrus Logic CL-PD6710",
[Tpd6720] "Cirrus Logic CL-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 a memory & I/O card */
if((id & 0x0f) == 0x00)
return 0; /* no revision number, not possible */
cp = xalloc(sizeof(I82365));
if(cp == nil){
print("i82365probe: out of memory\n");
return 0;
}
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 it's not a Cirrus, it could be a Vadem... */
if(cp->type == Ti82365){
/* unlock the Vadem extended regs */
outb(x, 0x0E + (dev<<7));
outb(x, 0x37 + (dev<<7));
/* make the id register show the Vadem id */
outb(x, 0x3A + (dev<<7));
c = inb(d);
outb(d, c|0xC0);
outb(x, Rid + (dev<<7));
c = inb(d);
if(c & 0x08)
cp->type = Tvg46x;
/* go back to Intel compatible id */
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 = IrqPCMCIA;
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(PCMslot *pp)
{
int i;
for(i = 0; i < 0x40; i++){
if((i&0x0F) == 0)
print("\n%2.2uX: ", i);
print("%2.2uX ", rdreg(pp, i));
if(((i+1) & 0x0F) == 0x08)
print(" - ");
}
print("\n");
}
/*
* set up for slot cards
*/
void
devi82365link(void)
{
static int already;
int i, j;
I82365 *cp;
PCMslot *pp;
char buf[32], *p;
if(already)
return;
already = 1;
if((p=getconf("pcmcia0")) && strncmp(p, "disabled", 8)==0)
return;
if(_pcmspecial)
return;
/* look for controllers if the ports aren't already taken */
if(ioalloc(0x3E0, 2, 0, "i82365.0") >= 0){
i82365probe(0x3E0, 0x3E1, 0);
i82365probe(0x3E0, 0x3E1, 1);
if(ncontroller == 0)
iofree(0x3E0);
}
if(ioalloc(0x3E2, 2, 0, "i82365.1") >= 0){
i = ncontroller;
i82365probe(0x3E2, 0x3E3, 0);
i82365probe(0x3E2, 0x3E3, 1);
if(ncontroller == i)
iofree(0x3E2);
}
if(ncontroller == 0)
return;
for(i = 0; i < ncontroller; i++)
nslot += controller[i]->nslot;
slot = xalloc(nslot * sizeof(PCMslot));
if(slot == nil){
print("i82365link: out of memory\n");
nslot = 0;
return;
}
_pcmspecial = pcmcia_pcmspecial;
_pcmspecialclose = pcmcia_pcmspecialclose;
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;
pp->msec = ~0;
pp->verstr[0] = 0;
slotdis(pp);
/* interrupt on status change */
wrreg(pp, Rcscic, (cp->irq<<4) | Fchangeena);
rdreg(pp, Rcsc);
}
/* for card management interrupts */
snprint(buf, sizeof buf, "i82365.%d", i);
intrenable(cp->irq, i82365intr, 0, BUSUNKNOWN, buf);
}
}
static Chan*
i82365attach(char *spec)
{
return devattach('y', spec);
}
static Walkqid*
i82365walk(Chan *c, Chan *nc, char **name, int nname)
{
return devwalk(c, nc, name, nname, 0, 0, pcmgen);
}
static int
i82365stat(Chan *c, uchar *db, int n)
{
return devstat(c, db, n, 0, 0, pcmgen);
}
static Chan*
i82365open(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
i82365close(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++;
}
/* 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(int slotno, int attr, void *a, long n, vlong off)
{
int i, len;
PCMmap *m;
uchar *ac;
PCMslot *pp;
ulong offset = off;
pp = slot + slotno;
if(pp->memlen < offset)
return 0;
if(pp->memlen < offset + n)
n = pp->memlen - offset;
m = 0;
if(waserror()){
if(m)
pcmunmap(pp->slotno, m);
nexterror();
}
ac = a;
for(len = n; len > 0; len -= i){
m = pcmmap(pp->slotno, offset, 0, attr);
if(m == 0)
error("cannot map PCMCIA card");
if(offset + len > m->cea)
i = m->cea - offset;
else
i = len;
memmoveb(ac, KADDR(m->isa + offset - m->ca), i);
pcmunmap(pp->slotno, m);
offset += i;
ac += i;
}
poperror();
return n;
}
static long
i82365read(Chan *c, void *a, long n, vlong off)
{
char *p, *buf, *e;
PCMslot *pp;
ulong offset = off;
switch(TYPE(c)){
case Qdir:
return devdirread(c, a, n, 0, 0, pcmgen);
case Qmem:
case Qattr:
return pcmread(SLOTNO(c), TYPE(c) == Qattr, a, n, off);
case Qctl:
buf = p = smalloc(READSTR);
e = p + READSTR;
pp = slot + SLOTNO(c);
buf[0] = 0;
if(pp->occupied){
p = seprint(p, e, "occupied\n");
if(pp->verstr[0])
p = seprint(p, e, "version %s\n", pp->verstr);
}
if(pp->enabled)
p = seprint(p, e, "enabled\n");
if(pp->powered)
p = seprint(p, e, "powered\n");
if(pp->configed)
p = seprint(p, e, "configed\n");
if(pp->wrprot)
p = seprint(p, e, "write protected\n");
if(pp->busy)
p = seprint(p, e, "busy\n");
seprint(p, e, "battery lvl %d\n", pp->battery);
n = readstr(offset, a, n, buf);
free(buf);
return n;
}
error(Ebadarg);
return -1; /* not reached */
}
static long
pcmwrite(int dev, int attr, void *a, long n, vlong off)
{
int i, len;
PCMmap *m;
uchar *ac;
PCMslot *pp;
ulong offset = off;
pp = slot + dev;
if(pp->memlen < offset)
return 0;
if(pp->memlen < offset + n)
n = pp->memlen - offset;
m = 0;
if(waserror()){
if(m)
pcmunmap(pp->slotno, m);
nexterror();
}
ac = a;
for(len = n; len > 0; len -= i){
m = pcmmap(pp->slotno, offset, 0, attr);
if(m == 0)
error("cannot map PCMCIA card");
if(offset + len > m->cea)
i = m->cea - offset;
else
i = len;
memmoveb(KADDR(m->isa + offset - m->ca), ac, i);
pcmunmap(pp->slotno, m);
offset += i;
ac += i;
}
poperror();
return n;
}
static long
i82365write(Chan *c, void *a, long n, vlong off)
{
PCMslot *pp;
char buf[32];
switch(TYPE(c)){
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)
wrreg(pp, Rpc, vcode(strtol(buf+3, 0, 0))|Fautopower|Foutena|Fcardena);
return n;
case Qmem:
case Qattr:
pp = slot + SLOTNO(c);
if(pp->occupied == 0 || pp->enabled == 0)
error(Eio);
n = pcmwrite(pp->slotno, TYPE(c) == Qattr, a, n, off);
if(n < 0)
error(Eio);
return n;
}
error(Ebadarg);
return -1; /* not reached */
}
Dev i82365devtab = {
'y',
"i82365",
devreset,
devinit,
devshutdown,
i82365attach,
i82365walk,
i82365stat,
i82365open,
devcreate,
i82365close,
i82365read,
devbread,
i82365write,
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)
{
uchar we, x, *p;
PCMslot *pp;
PCMconftab *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
* the 'top' register value includes the last valid address
*/
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 */
m = pcmmap(slotno, pp->cfg[0].caddr + Rconfig, 0x20, 1);
p = KADDR(m->isa + pp->cfg[0].caddr - m->ca);
if(pp->cfg[0].cpresent & (1<<Rconfig)){
/* Reset adapter */
/* set configuration and interrupt type.
* if level is possible on the card, use it.
*/
x = ct->index;
if(ct->irqtype & 0x20)
x |= Clevel;
/* enable the device, enable address decode and
* irq enable.
*/
x |= Cfunc|Cdecode|Cirq;
p[0] = x;
//delay(5);
microdelay(40);
}
if(pp->cfg[0].cpresent & (1<<Riobase0)){
/* set up the iobase 0 */
p[Riobase0 << 1] = isa->port;
p[Riobase1 << 1] = isa->port >> 8;
}
if(pp->cfg[0].cpresent & (1<<Riosize))
p[Riosize << 1] = ct->io[0].len;
pcmunmap(slotno, m);
return 0;
}