Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/* 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 "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)
                        error(Enomem);
                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);
}