Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

/*
 * 3C589 and 3C562.
 * To do:
 *      check xcvr10Base2 still works (is GlobalReset necessary?).
 */
#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"

enum {                                          /* all windows */
        CommandR                = 0x000E,
        IntStatusR              = 0x000E,
};

enum {                                          /* Commands */
        GlobalReset             = 0x0000,
        SelectRegisterWindow    = 0x0001,
        RxReset                 = 0x0005,
        TxReset                 = 0x000B,
        AcknowledgeInterrupt    = 0x000D,
};

enum {                                          /* IntStatus bits */
        commandInProgress       = 0x1000,
};

#define COMMAND(port, cmd, a)   outs((port)+CommandR, ((cmd)<<11)|(a))
#define STATUS(port)            ins((port)+IntStatusR)

enum {                                          /* Window 0 - setup */
        Wsetup                  = 0x0000,
                                                /* registers */
        ManufacturerID          = 0x0000,       /* 3C5[08]*, 3C59[27] */
        ProductID               = 0x0002,       /* 3C5[08]*, 3C59[27] */
        ConfigControl           = 0x0004,       /* 3C5[08]*, 3C59[27] */
        AddressConfig           = 0x0006,       /* 3C5[08]*, 3C59[27] */
        ResourceConfig          = 0x0008,       /* 3C5[08]*, 3C59[27] */
        EepromCommand           = 0x000A,
        EepromData              = 0x000C,
                                                /* AddressConfig Bits */
        autoSelect9             = 0x0080,
        xcvrMask9               = 0xC000,
                                                /* ConfigControl bits */
        Ena                     = 0x0001,
        base10TAvailable9       = 0x0200,
        coaxAvailable9          = 0x1000,
        auiAvailable9           = 0x2000,
                                                /* EepromCommand bits */
        EepromReadRegister      = 0x0080,
        EepromBusy              = 0x8000,
};

enum {                                          /* Window 1 - operating set */
        Wop                     = 0x0001,
};

enum {                                          /* Window 3 - FIFO management */
        Wfifo                   = 0x0003,
                                                /* registers */
        InternalConfig          = 0x0000,       /* 3C509B, 3C589, 3C59[0257] */
                                                /* InternalConfig bits */
        xcvr10BaseT             = 0x00000000,
        xcvr10Base2             = 0x00300000,
};

enum {                                          /* Window 4 - diagnostic */
        Wdiagnostic             = 0x0004,
                                                /* registers */
        MediaStatus             = 0x000A,
                                                /* MediaStatus bits */
        linkBeatDetect          = 0x0800,
};

extern int etherelnk3reset(Ether*);

static char *tcmpcmcia[] = {
        "3C589",                        /* 3COM 589[ABCD] */
        "3C562",                        /* 3COM 562 */
        "589E",                         /* 3COM Megahertz 589E */
        nil,
};

static int
configASIC(Ether* ether, int port, int xcvr)
{
        int x;

        /* set Window 0 configuration registers */
        COMMAND(port, SelectRegisterWindow, Wsetup);
        outs(port+ConfigControl, Ena);

        /* IRQ must be 3 on 3C589/3C562 */
        outs(port + ResourceConfig, 0x3F00);

        x = ins(port+AddressConfig) & ~xcvrMask9;
        x |= (xcvr>>20)<<14;
        outs(port+AddressConfig, x);

        COMMAND(port, TxReset, 0);
        while(STATUS(port) & commandInProgress)
                ;
        COMMAND(port, RxReset, 0);
        while(STATUS(port) & commandInProgress)
                ;

        return etherelnk3reset(ether);
}

static int
reset(Ether* ether)
{
        int i, t, slot;
        char *type;
        int port;
        enum { WantAny, Want10BT, Want10B2 };
        int want;
        uchar ea[6];
        char *p;

        if(ether->irq == 0)
                ether->irq = 10;
        if(ether->port == 0)
                ether->port = 0x240;
        port = ether->port;

        if(ioalloc(port, 0x10, 0, "3C589") < 0)
                return -1;

        type = nil;
        slot = -1;
        for(i = 0; tcmpcmcia[i] != nil; i++){
                type = tcmpcmcia[i];
                if((slot = pcmspecial(type, ether)) >= 0)
                        break;
        }
        ether->type = type;     /* must be set before calling configASIC */
        if(slot < 0){
                iofree(port);
                return -1;
        }

        /*
         * Read Ethernet address from card memory
         * on 3C562, but only if the user has not 
         * overridden it.
         */
        memset(ea, 0, sizeof ea);
        if(memcmp(ea, ether->ea, 6) == 0 && strcmp(type, "3C562") == 0) {
                if(pcmcistuple(slot, 0x88, -1, ea, 6) == 6) {
                        for(i = 0; i < 6; i += 2){
                                t = ea[i];
                                ea[i] = ea[i+1];
                                ea[i+1] = t;
                        }
                        memmove(ether->ea, ea, 6);
                }
        }
        /*
         * Allow user to specify desired media in plan9.ini
         */
        want = WantAny;
        for(i = 0; i < ether->nopt; i++){
                if(cistrncmp(ether->opt[i], "media=", 6) != 0)
                        continue;
                p = ether->opt[i]+6;
                if(cistrcmp(p, "10base2") == 0)
                        want = Want10B2;
                else if(cistrcmp(p, "10baseT") == 0)
                        want = Want10BT;
        }
        
        /* try configuring as a 10BaseT */
        if(want==WantAny || want==Want10BT){
                if(configASIC(ether, port, xcvr10BaseT) < 0){
                        pcmspecialclose(slot);
                        iofree(port);
                        return -1;
                }
                delay(100);
                COMMAND(port, SelectRegisterWindow, Wdiagnostic);
                if((ins(port+MediaStatus)&linkBeatDetect) || want==Want10BT){
                        COMMAND(port, SelectRegisterWindow, Wop);
                        print("#l%d: xcvr10BaseT %s\n", ether->ctlrno, type);
                        return 0;
                }
        }

        /* try configuring as a 10base2 */
        if(want==WantAny || want==Want10B2){
                COMMAND(port, GlobalReset, 0);
                if(configASIC(ether, port, xcvr10Base2) < 0){
                        pcmspecialclose(slot);
                        iofree(port);
                        return -1;
                }
                print("#l%d: xcvr10Base2 %s\n", ether->ctlrno, type);
                return 0;
        }
        return -1;              /* not reached */
}

void
ether589link(void)
{
        addethercard("3C589", reset);
        addethercard("3C562", reset);
        addethercard("589E", reset);
}