1054 lines
20 KiB
C
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->isa){
|
|
umbfree(m->isa, m->len);
|
|
m->len = 0;
|
|
}
|
|
m->isa = PADDR(umbmalloc(0, len, Mgran));
|
|
if(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(atoi(buf+3))|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;
|
|
}
|