plan9fox/sys/src/9/pc/etherwavelan.c
cinap_lenrek d6e0e9c402 kernel: move devether and wifi to port/
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.
2018-02-11 18:08:03 +01:00

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(&ether->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);
}