d6e0e9c402
the only architecture dependence of devether was enabling interrupts, which is now done at the end of the driver's reset() function now. the wifi stack and dummy ethersink also go to port/. do the IRQ2->IRQ9 hack for pc kernels in intrenabale(), so not every caller of intrenable() has to be aware of it.
202 lines
3.8 KiB
C
202 lines
3.8 KiB
C
/* Pci/pcmcia code for wavelan.c */
|
|
|
|
#include "u.h"
|
|
#include "../port/lib.h"
|
|
#include "mem.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
#include "io.h"
|
|
#include "../port/error.h"
|
|
#include "../port/netif.h"
|
|
#include "../port/etherif.h"
|
|
|
|
#include "wavelan.h"
|
|
|
|
static int
|
|
wavelanpcmciareset(Ether *ether)
|
|
{
|
|
int i;
|
|
char *p;
|
|
Ctlr *ctlr;
|
|
|
|
if((ctlr = malloc(sizeof(Ctlr))) == nil)
|
|
return -1;
|
|
|
|
ilock(ctlr);
|
|
ctlr->ctlrno = ether->ctlrno;
|
|
|
|
if (ether->port==0)
|
|
ether->port=WDfltIOB;
|
|
ctlr->iob = ether->port;
|
|
|
|
if (ether->irq==0)
|
|
ether->irq=WDfltIRQ;
|
|
|
|
if (ioalloc(ether->port,WIOLen,0,"wavelan")<0){
|
|
// print("#l%d: port 0x%lx in use\n",
|
|
// ether->ctlrno, ether->port);
|
|
goto abort1;
|
|
}
|
|
|
|
/*
|
|
* If id= is specified, card must match. Otherwise try generic.
|
|
*/
|
|
ctlr->slot = -1;
|
|
for(i=0; i<ether->nopt; i++){
|
|
if(cistrncmp(ether->opt[i], "id=", 3) == 0){
|
|
if((ctlr->slot = pcmspecial(ðer->opt[i][3], ether)) < 0)
|
|
goto abort;
|
|
break;
|
|
}
|
|
}
|
|
if(ctlr->slot == -1){
|
|
for (i=0; wavenames[i]; i++)
|
|
if((ctlr->slot = pcmspecial(wavenames[i], ether))>=0)
|
|
break;
|
|
if(!wavenames[i]){
|
|
DEBUG("no wavelan found\n");
|
|
goto abort;
|
|
}
|
|
}
|
|
|
|
// DEBUG("#l%d: port=0x%lx irq=%ld\n",
|
|
// ether->ctlrno, ether->port, ether->irq);
|
|
|
|
if(wavelanreset(ether, ctlr) < 0){
|
|
abort:
|
|
iofree(ether->port);
|
|
abort1:
|
|
iunlock(ctlr);
|
|
free(ctlr);
|
|
ether->ctlr = nil;
|
|
return -1;
|
|
}
|
|
|
|
for(i = 0; i < ether->nopt; i++){
|
|
if(p = strchr(ether->opt[i], '='))
|
|
*p = ' ';
|
|
w_option(ctlr, ether->opt[i], strlen(ether->opt[i]));
|
|
}
|
|
|
|
iunlock(ctlr);
|
|
return 0;
|
|
}
|
|
|
|
static struct {
|
|
int vid;
|
|
int did;
|
|
} wavelanpci[] = {
|
|
0x1260, 0x3873, /* Intersil Prism2.5 */
|
|
0x1737, 0x0019, /* Linksys WPC-11 untested */
|
|
};
|
|
|
|
static Ctlr *ctlrhead, *ctlrtail;
|
|
|
|
static void
|
|
wavelanpciscan(void)
|
|
{
|
|
int i;
|
|
void *mem;
|
|
Pcidev *p;
|
|
Ctlr *ctlr;
|
|
|
|
p = nil;
|
|
while(p = pcimatch(p, 0, 0)){
|
|
for(i=0; i<nelem(wavelanpci); i++)
|
|
if(p->vid == wavelanpci[i].vid && p->did == wavelanpci[i].did)
|
|
break;
|
|
if(i==nelem(wavelanpci))
|
|
continue;
|
|
|
|
/*
|
|
* On the Prism, bar[0] is the memory-mapped register address (4KB),
|
|
*/
|
|
if(p->mem[0].size != 4096){
|
|
print("wavelanpci: %.4ux %.4ux: unlikely mmio size\n", p->vid, p->did);
|
|
continue;
|
|
}
|
|
|
|
ctlr = malloc(sizeof(Ctlr));
|
|
if(ctlr == nil){
|
|
print("wavelanpci: can't allocate memory\n");
|
|
continue;
|
|
}
|
|
ctlr->pcidev = p;
|
|
mem = vmap(p->mem[0].bar&~0xF, p->mem[0].size);
|
|
if(mem == nil){
|
|
print("wavelanpci: %.4ux %.4ux: vmap 0x%.8lux %d failed\n", p->vid, p->did, p->mem[0].bar&~0xF, p->mem[0].size);
|
|
free(ctlr);
|
|
continue;
|
|
}
|
|
ctlr->mmb = mem;
|
|
if(ctlrhead != nil)
|
|
ctlrtail->next = ctlr;
|
|
else
|
|
ctlrhead = ctlr;
|
|
ctlrtail = ctlr;
|
|
pcisetbme(p);
|
|
}
|
|
}
|
|
|
|
static int
|
|
wavelanpcireset(Ether *ether)
|
|
{
|
|
int i;
|
|
char *p;
|
|
Ctlr *ctlr;
|
|
|
|
if(ctlrhead == nil)
|
|
wavelanpciscan();
|
|
|
|
/*
|
|
* Allow plan9.ini to set vid, did?
|
|
*/
|
|
for(ctlr=ctlrhead; ctlr!=nil; ctlr=ctlr->next)
|
|
if(ctlr->active == 0)
|
|
break;
|
|
if(ctlr == nil)
|
|
return -1;
|
|
|
|
ctlr->active = 1;
|
|
ilock(ctlr);
|
|
ether->irq = ctlr->pcidev->intl;
|
|
ether->tbdf = ctlr->pcidev->tbdf;
|
|
|
|
/*
|
|
* Really hard reset.
|
|
*/
|
|
csr_outs(ctlr, WR_PciCor, 0x0080);
|
|
delay(250);
|
|
csr_outs(ctlr, WR_PciCor, 0x0000);
|
|
delay(500);
|
|
for(i=0; i<2*10; i++){
|
|
if(!(csr_ins(ctlr, WR_Cmd)&WCmdBusy))
|
|
break;
|
|
delay(100);
|
|
}
|
|
if(i >= 2*10)
|
|
print("wavelan pci %.4ux %.4ux: reset timeout %.4ux\n",
|
|
ctlr->pcidev->vid, ctlr->pcidev->did, csr_ins(ctlr, WR_Cmd));
|
|
|
|
if(wavelanreset(ether, ctlr) < 0){
|
|
iunlock(ctlr);
|
|
ether->ctlr = nil;
|
|
return -1;
|
|
}
|
|
|
|
for(i = 0; i < ether->nopt; i++){
|
|
if(p = strchr(ether->opt[i], '='))
|
|
*p = ' ';
|
|
w_option(ctlr, ether->opt[i], strlen(ether->opt[i]));
|
|
}
|
|
iunlock(ctlr);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
etherwavelanlink(void)
|
|
{
|
|
addethercard("wavelan", wavelanpcmciareset);
|
|
addethercard("wavelanpci", wavelanpcireset);
|
|
}
|