fixed for my hw.  the really key bit was setting the endianness
properly.  this may have been set up by your pxe hardware or bios,
if your bcm chip was built-in.

i've updated 9atom with this driver.

- erik
/*
 * Broadcom BCM57xx
 * Not implemented:
 *  proper fatal error handling
 *  multiple rings
 *  QoS
 *  checksum offloading
 */

/* this driver needs some work, and probablly doesn't conform to style(6) */

#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"

#define dprint(...)     do{ if(debug)print(__VA_ARGS__); }while(0)
#define Rbsz            ROUNDUP(sizeof(Etherpkt)+4, 4)
#define Pciwaddrl(x)    PCIWADDR(x)
#define Pciwaddrh(x)    (sizeof(uintptr)>4? (uvlong)PCIWADDR(x)>>32: 0)

typedef struct Ctlr Ctlr;
struct Ctlr {
        Lock txlock, imlock;
        Ctlr *next;
        Pcidev *pdev;
        ulong *nic, *status;

        ulong *recvret, *recvprod, *sendr;
        ulong port;
        ulong recvreti, recvprodi, sendri, sendcleani;
        Block **sends;
        Block **rxs;
        int active, duplex;

        uint    nobuf;
        uint    partial;
        uint    rxerr;
        uint    qfull;
        uint    dmaerr;
};

enum {
        RecvRetRingLen = 0x200,
        RecvProdRingLen = 0x200,
        SendRingLen = 0x200,

        Reset = 1<<0,
        Enable = 1<<1,
        Attn = 1<<2,
        
        PowerControlStatus = 0x4C,

        MiscHostCtl = 0x68,
        TaggedStatus            = 1<<9,
        IndirectAccessEnable    = 1<<7,
        EnableClockControl      = 1<<5,
        EnablePCIStateRegister  = 1<<4,
        WordSwap                = 1<<3,
        ByteSwap                = 1<<2,
        MaskPCIInt              = 1<<1,
        ClearIntA               = 1<<0,
        
        Fwmbox          = 0x0b50,       /* magic value exchange */
        Fwmagic         = 0x4b657654,

        DMARWControl = 0x6C,
        DMAWatermarkMask = ~(7<<19),
        DMAWatermarkValue = 3<<19,

        MemoryWindow = 0x7C,
        MemoryWindowData = 0x84,
        
        SendRCB = 0x100,
        RecvRetRCB = 0x200,
        
        InterruptMailbox = 0x204,
        
        RecvProdBDRingIndex = 0x26c,
        RecvBDRetRingIndex = 0x284,
        SendBDRingHostIndex = 0x304,
        
        MACMode = 0x400,
        MACPortMask = ~((1<<3)|(1<<2)),
        MACPortGMII = 1<<3,
        MACPortMII = 1<<2,
        MACEnable = (1<<23) | (1<<22) | (1<<21) | (1 << 15) | (1 << 14) | 
(1<<12) | (1<<11),
        MACHalfDuplex = 1<<1,
        
        MACEventStatus = 0x404,
        MACEventEnable = 0x408,
        MACAddress = 0x410,
        EthernetRandomBackoff = 0x438,
        ReceiveMTU = 0x43C,
        MIComm = 0x44C,
        MIStatus = 0x450,
        MIMode = 0x454,
        ReceiveMACMode = 0x468,
        TransmitMACMode = 0x45C,
        TransmitMACLengths = 0x464,
        MACHash = 0x470,
        ReceiveRules = 0x480,
        
        ReceiveRulesConfiguration = 0x500,
        LowWatermarkMaximum = 0x504,
        LowWatermarkMaxMask = ~0xFFFF,
        LowWatermarkMaxValue = 2,

        SendDataInitiatorMode = 0xC00,
        SendInitiatorConfiguration = 0x0C08,
        SendStats = 1<<0,
        SendInitiatorMask = 0x0C0C,
        
        SendDataCompletionMode = 0x1000,
        SendBDSelectorMode = 0x1400,
        SendBDInitiatorMode = 0x1800,
        SendBDCompletionMode = 0x1C00,
        
        ReceiveListPlacementMode = 0x2000,
        ReceiveListPlacement = 0x2010,
        ReceiveListPlacementConfiguration = 0x2014,
        ReceiveStats = 1<<0,
        ReceiveListPlacementMask = 0x2018,
        
        ReceiveDataBDInitiatorMode = 0x2400,
        ReceiveBDHostAddr = 0x2450,
        ReceiveBDFlags = 0x2458,
        ReceiveBDNIC = 0x245C,
        ReceiveDataCompletionMode = 0x2800,
        ReceiveBDInitiatorMode = 0x2C00,
        ReceiveBDRepl = 0x2C18,
        
        ReceiveBDCompletionMode = 0x3000,
        HostCoalescingMode = 0x3C00,
        HostCoalescingRecvTicks = 0x3C08,
        HostCoalescingSendTicks = 0x3C0C,
        RecvMaxCoalescedFrames = 0x3C10,
        SendMaxCoalescedFrames = 0x3C14,
        RecvMaxCoalescedFramesInt = 0x3C20,
        SendMaxCoalescedFramesInt = 0x3C24,
        StatusBlockHostAddr = 0x3C38,
        FlowAttention = 0x3C48,

        MemArbiterMode = 0x4000,
        
        BufferManMode = 0x4400,
        
        MBUFLowWatermark = 0x4414,
        MBUFHighWatermark = 0x4418,
        
        ReadDMAMode = 0x4800,
        ReadDMAStatus = 0x4804,
        WriteDMAMode = 0x4C00,
        WriteDMAStatus = 0x4C04,
        
        RISCState = 0x5004,
        FTQReset = 0x5C00,
        MSIMode = 0x6000,
        
        ModeControl = 0x6800,
        ByteWordSwap = (1<<4)|(1<<5)|(1<<2),//|(1<<1),
        HostStackUp = 1<<16,
        HostSendBDs = 1<<17,
        InterruptOnMAC = 1<<26,
        
        MiscConfiguration = 0x6804,
        CoreClockBlocksReset = 1<<0,
        GPHYPowerDownOverride = 1<<26,
        DisableGRCResetOnPCIE = 1<<29,
        TimerMask = ~0xFF,
        TimerValue = 65<<1,
        MiscLocalControl = 0x6808,
        InterruptOnAttn = 1<<3,
        AutoSEEPROM = 1<<24,
        
        SwArbitration = 0x7020,
        SwArbitSet1 = 1<<1,
        SwArbitWon1 = 1<<9,
        TLPControl = 0x7C00,
        
        PhyControl = 0x00,
        PhyStatus = 0x01,
        PhyLinkStatus = 1<<2,
        PhyAutoNegComplete = 1<<5,
        PhyPartnerStatus = 0x05,
        Phy100FD = 1<<8,
        Phy100HD = 1<<7,
        Phy10FD = 1<<6,
        Phy10HD = 1<<5,
        PhyGbitStatus = 0x0A,
        Phy1000FD = 1<<12,
        Phy1000HD = 1<<11,
        PhyAuxControl = 0x18,
        PhyIntStatus = 0x1A,
        PhyIntMask = 0x1B,
        
        Updated = 1<<0,
        LinkStateChange = 1<<1,
        Error = 1<<2,
        
        PacketEnd = 1<<2,
        FrameError = 1<<10,
};

#define csr32(c, r)     ((c)->nic[(r)/4])

static Ctlr *bcmhead;
static int debug=1;

static long
bcmifstat(Ether *edev, void *a, long n, ulong offset)
{
        char *s, *p, *e;
        Ctlr *c;

        c = edev->ctlr;
        p = s = malloc(READSTR);
        e = p + READSTR;

        p = seprint(p, e, "nobuf        %ud\n", c->nobuf);
        p = seprint(p, e, "partial      %ud\n", c->partial);
        p = seprint(p, e, "rxerr        %ud\n", c->rxerr);
        p = seprint(p, e, "qfull        %ud\n", c->qfull);
        p = seprint(p, e, "dmaerr       %ud\n", c->dmaerr);

        USED(p);
        n = readstr(offset, a, n, s);
        free(s);

        return n;
}

static int
miir(Ctlr *ctlr, int ra)
{
        while(csr32(ctlr, MIComm) & (1<<29))
                ;
        csr32(ctlr, MIComm) = (ra << 16) | (1 << 21) | (1 << 27) | (1 << 29);
        while(csr32(ctlr, MIComm) & (1<<29))
                ;
        if(csr32(ctlr, MIComm) & (1<<28))
                return -1;
        return csr32(ctlr, MIComm) & 0xFFFF;
}

static int
miiw(Ctlr *ctlr, int ra, int value)
{
        while(csr32(ctlr, MIComm) & (1<<29))
                ;
        csr32(ctlr, MIComm) = (value & 0xFFFF) | (ra << 16) | (1 << 21) | (1 << 
27) | (1 << 29);
        while(csr32(ctlr, MIComm) & (1<<29))
                ;
        return 0;
}

static void
checklink(Ether *edev)
{
        ulong i;
        Ctlr *ctlr;

        ctlr = edev->ctlr;
        miir(ctlr, PhyStatus); /* read twice for current status as per 802.3 */
        if(!(miir(ctlr, PhyStatus) & PhyLinkStatus)) {
                edev->link = 0;
                edev->mbps = 1000;
                ctlr->duplex = 1;
                dprint("bcm: no link\n");
                goto out;
        }
        edev->link = 1;
        while((miir(ctlr, PhyStatus) & PhyAutoNegComplete) == 0)
                ;
        i = miir(ctlr, PhyGbitStatus);
        if(i & (Phy1000FD | Phy1000HD)) {
                edev->mbps = 1000;
                ctlr->duplex = (i & Phy1000FD) != 0;
        } else if(i = miir(ctlr, PhyPartnerStatus), i & (Phy100FD | Phy100HD)) {
                edev->mbps = 100;
                ctlr->duplex = (i & Phy100FD) != 0;
        } else if(i & (Phy10FD | Phy10HD)) {
                edev->mbps = 10;
                ctlr->duplex = (i & Phy10FD) != 0;
        } else {
                edev->link = 0;
                edev->mbps = 1000;
                ctlr->duplex = 1;
                dprint("bcm: link partner supports neither 10/100/1000 Mbps\n");
                goto out;
        }
        dprint("bcm: %d Mbps link, %s duplex\n", edev->mbps, ctlr->duplex ? 
"full" : "half");
out:
        if(ctlr->duplex)
                csr32(ctlr, MACMode) &= ~MACHalfDuplex;
        else
                csr32(ctlr, MACMode) |= MACHalfDuplex;
        if(edev->mbps >= 1000)
                csr32(ctlr, MACMode) = (csr32(ctlr, MACMode) & MACPortMask) | 
MACPortGMII;
        else
                csr32(ctlr, MACMode) = (csr32(ctlr, MACMode) & MACPortMask) | 
MACPortMII;
        csr32(ctlr, MACEventStatus) |= (1<<4) | (1<<3); /* undocumented bits 
(sync and config changed) */
}

static ulong*
currentrecvret(Ctlr *ctlr)
{
        if(ctlr->recvreti == (ctlr->status[4] & 0xFFFF))
                return 0;
        return ctlr->recvret + ctlr->recvreti * 8;
}

static void
consumerecvret(Ctlr *ctlr)
{
        ctlr->recvreti = ctlr->recvreti+1 & RecvRetRingLen-1;
        csr32(ctlr, RecvBDRetRingIndex) = ctlr->recvreti;
}

static int
replenish(Ctlr *ctlr)
{
        ulong *next, incr;
        Block *bp;
        
        incr = (ctlr->recvprodi + 1) & (RecvProdRingLen - 1);
        if(incr == (ctlr->status[2] >> 16))
                return -1;
        bp = iallocb(Rbsz);
        if(bp == nil) {
                /* iallocb never fails.  this code is unnecessary */
                dprint("bcm: out of memory for receive buffers\n");
                ctlr->nobuf++;
                return -1;
        }
        next = ctlr->recvprod + ctlr->recvprodi * 8;
        memset(next, 0, 32);
        next[0] = Pciwaddrh(bp->rp);
        next[1] = Pciwaddrl(bp->rp);
        next[2] = Rbsz;
        next[7] = ctlr->recvprodi;
        ctlr->rxs[ctlr->recvprodi] = bp;
        coherence();
        csr32(ctlr, RecvProdBDRingIndex) = ctlr->recvprodi = incr;
        return 0;
}

static void
bcmreceive(Ether *edev)
{
        ulong *pkt, len;
        Ctlr *ctlr;
        Block *bp;
        
        ctlr = edev->ctlr;
        for(; pkt = currentrecvret(ctlr); replenish(ctlr), 
consumerecvret(ctlr)) {
                bp = ctlr->rxs[pkt[7]];
                len = pkt[2] & 0xFFFF;
                bp->wp = bp->rp + len;
                if((pkt[3] & PacketEnd) == 0){
                        dprint("bcm: partial frame received -- shouldn't 
happen\n");
                        ctlr->partial++;
                        freeb(bp);
                        continue;
                }
                if(pkt[3] & FrameError){
                        ctlr->rxerr++;
                        freeb(bp);
                        continue;
                }
                etheriq(edev, bp, 1);
        }
}

static void
bcmtransclean(Ether *edev)
{
        Ctlr *ctlr;
        
        ctlr = edev->ctlr;
        ilock(&ctlr->txlock);
        while(ctlr->sendcleani != (ctlr->status[4] >> 16)) {
                freeb(ctlr->sends[ctlr->sendcleani]);
                ctlr->sends[ctlr->sendcleani] = nil;
                ctlr->sendcleani = (ctlr->sendcleani + 1) & (SendRingLen - 1);
        }
        iunlock(&ctlr->txlock);
}

static void
bcmtransmit(Ether *edev)
{
        ulong *next, incr;
        Ctlr *ctlr;
        Block *bp;
        
        ctlr = edev->ctlr;
        ilock(&ctlr->txlock);
        for(;;){
                incr = (ctlr->sendri + 1) & (SendRingLen - 1);
                if(incr == ctlr->sendcleani) {
                        dprint("bcm: send queue full\n");
                        ctlr->qfull++;
                        break;
                }
                bp = qget(edev->oq);
                if(bp == nil)
                        break;
                next = ctlr->sendr + ctlr->sendri * 4;
                next[0] = Pciwaddrh(bp->rp);
                next[1] = Pciwaddrl(bp->rp);
                next[2] = (BLEN(bp) << 16) | PacketEnd;
                next[3] = 0;
                ctlr->sends[ctlr->sendri] = bp;
                coherence();
                csr32(ctlr, SendBDRingHostIndex) = ctlr->sendri = incr;
        }
        iunlock(&ctlr->txlock);
}

static void
bcmerror(Ether *edev)
{
        Ctlr *ctlr;
        
        ctlr = edev->ctlr;
        if(csr32(ctlr, FlowAttention)) {
                if(csr32(ctlr, FlowAttention) & 0xf8ff8080)
                        print("bcm: fatal error %#.8lux", csr32(ctlr, 
FlowAttention));
                csr32(ctlr, FlowAttention) = 0;
        }
        csr32(ctlr, MACEventStatus) = 0; /* worth ignoring */
        if(csr32(ctlr, ReadDMAStatus) || csr32(ctlr, WriteDMAStatus)) {
                dprint("bcm: DMA error\n");
                ctlr->dmaerr++;
                csr32(ctlr, ReadDMAStatus) = 0;
                csr32(ctlr, WriteDMAStatus) = 0;
        }
        if(csr32(ctlr, RISCState)) {
                if(csr32(ctlr, RISCState) & 0x78000403)
                        print("bcm: RISC halted %#.8lux", csr32(ctlr, 
RISCState));
                csr32(ctlr, RISCState) = 0;
        }
}

static void
bcminterrupt(Ureg*, void *arg)
{
        ulong status, tag, dummy;
        Ether *edev;
        Ctlr *ctlr;
        
        edev = arg;
        ctlr = edev->ctlr;
        ilock(&ctlr->imlock);
        dummy = csr32(ctlr, InterruptMailbox);
        USED(dummy);
        csr32(ctlr, InterruptMailbox) = 1;
        status = ctlr->status[0];
        tag = ctlr->status[1];
        ctlr->status[0] = 0;
        if(status & Error)
                bcmerror(edev);
        if(status & LinkStateChange)
                checklink(edev);
//      iprint("bcm: interrupt %.8lux %.8lux\n", ctlr->status[2], 
ctlr->status[4]);
        bcmreceive(edev);
        bcmtransclean(edev);
        bcmtransmit(edev);
        csr32(ctlr, InterruptMailbox) = tag << 24;
        iunlock(&ctlr->imlock);
}

void
mem32w(Ctlr *c, uint r, uint v)
{
        pcicfgw32(c->pdev, MemoryWindow, r);
        pcicfgw32(c->pdev, MemoryWindowData, v);
}

ulong
mem32r(Ctlr *c, uint r)
{
        ulong v;

        pcicfgw32(c->pdev, MemoryWindow, r);
        v = pcicfgr32(c->pdev, MemoryWindowData);
        pcicfgw32(c->pdev, MemoryWindow, 0);
        return v;
}

static int
bcminit(Ether *edev)
{
        ulong i, j;
        Ctlr *ctlr;
        
        ctlr = edev->ctlr;
        dprint("bcm: reset\n");
        /* initialization procedure according to the datasheet */
        csr32(ctlr, MiscHostCtl) |= MaskPCIInt | ClearIntA;
        csr32(ctlr, SwArbitration) |= SwArbitSet1;
        for(i = 0;; i += 100){
                if((csr32(ctlr, SwArbitration) & SwArbitWon1))
                        break;
                if(i == 2000 /* µs */){
                        print("bcm: arbiter failed to respond\n");
                        return -1;
                }
                microdelay(100);
        }
        csr32(ctlr, MemArbiterMode) |= Enable;
        csr32(ctlr, MiscHostCtl) = WordSwap | IndirectAccessEnable | 
EnablePCIStateRegister | EnableClockControl
                | MaskPCIInt | ClearIntA;
        csr32(ctlr, MemoryWindow) = 0;
        mem32w(ctlr, Fwmbox, Fwmagic);
        csr32(ctlr, MiscConfiguration) |= GPHYPowerDownOverride | 
DisableGRCResetOnPCIE | CoreClockBlocksReset;
        delay(100);
        pcicfgw32(ctlr->pdev, PciPCR, ctlr->pdev->pcr); /* restore pci bits 
lost */
        csr32(ctlr, MiscHostCtl) |= MaskPCIInt | ClearIntA;
        csr32(ctlr, MemArbiterMode) |= Enable;
        csr32(ctlr, MiscHostCtl) |= WordSwap | IndirectAccessEnable | 
EnablePCIStateRegister | EnableClockControl | TaggedStatus;
        csr32(ctlr, ModeControl) |= ByteWordSwap;
        csr32(ctlr, MACMode) = (csr32(ctlr, MACMode) & MACPortMask) | 
MACPortGMII;
        delay(40);
        for(i = 0;; i += 100){
                if(mem32r(ctlr, Fwmbox) == ~Fwmagic)
                        break;
                if(i == 20*10000 /* µs */){
                        print("bcm: fw failed to respond %#.8lux\n", 
mem32r(ctlr, Fwmbox));
                        break; //return -1;
                }
                microdelay(100);
        }
        csr32(ctlr, TLPControl) |= (1<<25) | (1<<29);
        memset(ctlr->status, 0, 20);
        csr32(ctlr, DMARWControl) = (csr32(ctlr, DMARWControl) & 
DMAWatermarkMask) | DMAWatermarkValue;
        csr32(ctlr, ModeControl) |= HostSendBDs | HostStackUp | InterruptOnMAC;
        csr32(ctlr, MiscConfiguration) = (csr32(ctlr, MiscConfiguration) & 
TimerMask) | TimerValue;
        csr32(ctlr, MBUFLowWatermark) = 0x20;
        csr32(ctlr, MBUFHighWatermark) = 0x60;
        csr32(ctlr, LowWatermarkMaximum) = (csr32(ctlr, LowWatermarkMaximum) & 
LowWatermarkMaxMask) | LowWatermarkMaxValue;
        csr32(ctlr, BufferManMode) |= Enable | Attn;
        while((csr32(ctlr, BufferManMode) & Enable) == 0)
                ;
        csr32(ctlr, FTQReset) = -1;
        csr32(ctlr, FTQReset) = 0;
        while(csr32(ctlr, FTQReset))
                ;
        csr32(ctlr, ReceiveBDHostAddr) = Pciwaddrh(ctlr->recvprod);
        csr32(ctlr, ReceiveBDHostAddr + 4) = Pciwaddrl(ctlr->recvprod);
        csr32(ctlr, ReceiveBDFlags) = RecvProdRingLen << 16;
        csr32(ctlr, ReceiveBDNIC) = 0x6000;
        csr32(ctlr, ReceiveBDRepl) = 25;
        csr32(ctlr, SendBDRingHostIndex) = 0;
        csr32(ctlr, SendBDRingHostIndex+4) = 0;
        mem32w(ctlr, SendRCB, Pciwaddrh(ctlr->sendr));
        mem32w(ctlr, SendRCB + 4, Pciwaddrl(ctlr->sendr));
        mem32w(ctlr, SendRCB + 8, SendRingLen << 16);
        mem32w(ctlr, SendRCB + 12, 0x4000);
        for(i=1;i<4;i++)
                mem32w(ctlr, RecvRetRCB + i * 0x10 + 8, 2);
        mem32w(ctlr, RecvRetRCB, Pciwaddrh(ctlr->recvret));
        mem32w(ctlr, RecvRetRCB + 4, Pciwaddrl(ctlr->recvret));
        mem32w(ctlr, RecvRetRCB + 8, RecvRetRingLen << 16);
        csr32(ctlr, RecvProdBDRingIndex) = 0;
        csr32(ctlr, RecvProdBDRingIndex+4) = 0;
        /* this delay is not in the datasheet, but necessary */
        delay(1);
        i = csr32(ctlr, MACAddress);
        j = edev->ea[0] = i >> 8;
        j += edev->ea[1] = i;
        i = csr32(ctlr, MACAddress + 4);
        j += edev->ea[2] = i >> 24;
        j += edev->ea[3] = i >> 16;
        j += edev->ea[4] = i >> 8;
        j += edev->ea[5] = i;
        csr32(ctlr, EthernetRandomBackoff) = j & 0x3FF;
        csr32(ctlr, ReceiveMTU) = Rbsz;
        csr32(ctlr, TransmitMACLengths) = 0x2620;
        csr32(ctlr, ReceiveListPlacement) = 1<<3; /* one list */
        csr32(ctlr, ReceiveListPlacementMask) = 0xFFFFFF;
        csr32(ctlr, ReceiveListPlacementConfiguration) |= ReceiveStats;
        csr32(ctlr, SendInitiatorMask) = 0xFFFFFF;
        csr32(ctlr, SendInitiatorConfiguration) |= SendStats;
        csr32(ctlr, HostCoalescingMode) = 0;
        while(csr32(ctlr, HostCoalescingMode) != 0)
                ;
        csr32(ctlr, HostCoalescingRecvTicks) = 150;
        csr32(ctlr, HostCoalescingSendTicks) = 150;
        csr32(ctlr, RecvMaxCoalescedFrames) = 10;
        csr32(ctlr, SendMaxCoalescedFrames) = 10;
        csr32(ctlr, RecvMaxCoalescedFramesInt) = 0;
        csr32(ctlr, SendMaxCoalescedFramesInt) = 0;
        csr32(ctlr, StatusBlockHostAddr) = Pciwaddrh(ctlr->status);
        csr32(ctlr, StatusBlockHostAddr + 4) = Pciwaddrl(ctlr->status);
        csr32(ctlr, HostCoalescingMode) |= Enable;
        csr32(ctlr, ReceiveBDCompletionMode) |= Enable | Attn;
        csr32(ctlr, ReceiveListPlacementMode) |= Enable;
        csr32(ctlr, MACMode) |= MACEnable;
        csr32(ctlr, MiscLocalControl) |= InterruptOnAttn | AutoSEEPROM;
        csr32(ctlr, InterruptMailbox) = 0;
        csr32(ctlr, WriteDMAMode) |= 0x200003fe; /* pulled out of my nose */
        csr32(ctlr, ReadDMAMode) |= 0x3fe;
        csr32(ctlr, ReceiveDataCompletionMode) |= Enable | Attn;
        csr32(ctlr, SendDataCompletionMode) |= Enable;
        csr32(ctlr, SendBDCompletionMode) |= Enable | Attn;
        csr32(ctlr, ReceiveBDInitiatorMode) |= Enable | Attn;
        csr32(ctlr, ReceiveDataBDInitiatorMode) |= Enable | (1<<4);
        csr32(ctlr, SendDataInitiatorMode) |= Enable;
        csr32(ctlr, SendBDInitiatorMode) |= Enable | Attn;
        csr32(ctlr, SendBDSelectorMode) |= Enable | Attn;
        ctlr->recvprodi = 0;
        while(replenish(ctlr) >= 0)
                ;
        csr32(ctlr, TransmitMACMode) |= Enable;
        csr32(ctlr, ReceiveMACMode) |= Enable;
        csr32(ctlr, PowerControlStatus) &= ~3;
        csr32(ctlr, MIStatus) |= 1<<0;
        csr32(ctlr, MACEventEnable) = 0;
        csr32(ctlr, MACEventStatus) |= (1<<12);
        csr32(ctlr, MIMode) = 0xC0000;
        microdelay(40);
        miiw(ctlr, PhyControl, 1<<15);
        while(miir(ctlr, PhyControl) & (1<<15))
                ;
        for(i = 0;; i += 100){
                if((miir(ctlr, PhyControl) & (1<<15)) == 0)
                        break;
                if(i == 10000 /* µs */){
                        print("bcm: phy reset failure\n");
                        return -1;
                }
                microdelay(100);
        }
        miiw(ctlr, PhyAuxControl, 2);
        miir(ctlr, PhyIntStatus);
        miir(ctlr, PhyIntStatus);
        miiw(ctlr, PhyIntMask, ~(1<<1));
        checklink(edev);
        csr32(ctlr, MACEventEnable) |= 1<<12;
        csr32(ctlr, MACHash) = -1;
        csr32(ctlr, MACHash+4) = -1;
        csr32(ctlr, MACHash+8) = -1;
        csr32(ctlr, MACHash+12) = -1;
        for(i = 0; i < 8; i++)
                csr32(ctlr, ReceiveRules + 8 * i) = 0;
        csr32(ctlr, ReceiveRulesConfiguration) = 1 << 3;
        csr32(ctlr, MSIMode) |= Enable;
        csr32(ctlr, MiscHostCtl) &= ~(MaskPCIInt | ClearIntA);
        return 0;
}

static void
bcmpci(void)
{
        void *mem;
        Ctlr *ctlr, **xx;
        Pcidev *p;

        xx = &bcmhead;
        for(p = nil; p = pcimatch(p, 0, 0); ) {
                if(p->ccrb != 2 || p->ccru != 0)
                        continue;
                
                switch(p->vid<<16 | p->did){
                default:
                        continue;
                case 0x14e4165a:
                case 0x14e4167d:
                case 0x14e41670:
                case 0x14e41672:
                case 0x14e41673:
                case 0x14e41674:
                case 0x14e41677:
                case 0x14e4167a:
                case 0x14e4167b:
                case 0x14e41693:
                case 0x14e4169b:
                case 0x14e41712:
                case 0x14e41713:
                        break;
                }
                pcisetbme(p);
                pcisetpms(p, 0);
                ctlr = malloc(sizeof(Ctlr));
                if(ctlr == nil)
                        continue;
                ctlr->port = p->mem[0].bar & ~0x0F;
                mem = vmap(ctlr->port, p->mem[0].size);
                if(mem == nil) {
                        print("bcm: can't map %#p\n", ctlr->port);
                        free(ctlr);
                        continue;
                }
                ctlr->pdev = p;
                ctlr->nic = mem;
                ctlr->status = xspanalloc(20, 16, 0);
                ctlr->recvprod = xspanalloc(32 * RecvProdRingLen, 16, 0);
                ctlr->recvret = xspanalloc(32 * RecvRetRingLen, 16, 0);
                ctlr->sendr = xspanalloc(16 * SendRingLen, 16, 0);
                ctlr->sends = malloc(sizeof *ctlr->sends * SendRingLen);
                ctlr->rxs = malloc(sizeof *ctlr->sends * SendRingLen);
                *xx = ctlr;
                xx = &ctlr->next;
        }
}

static void
bcmpromiscuous(void* arg, int on)
{
        Ctlr *ctlr;
        
        ctlr = ((Ether*)arg)->ctlr;
        if(on)
                csr32(ctlr, ReceiveMACMode) |= 1<<8;
        else
                csr32(ctlr, ReceiveMACMode) &= ~(1<<8);
}

static void
bcmmulticast(void*, uchar*, int)
{
}

static int
bcmpnp(Ether* edev)
{
        Ctlr *ctlr;
        static int done;

        if(done == 0){
                bcmpci();
                done = 1;
        }
        
redux:
        for(ctlr = bcmhead; ; ctlr = ctlr->next) {
                if(ctlr == nil)
                        return -1;
                if(ctlr->active)
                        continue;
                if(edev->port == 0 || edev->port == ctlr->port) {
                        ctlr->active = 1;
                        break;
                }
        }

        edev->ctlr = ctlr;
        edev->port = ctlr->port;
        edev->irq = ctlr->pdev->intl;
        edev->tbdf = ctlr->pdev->tbdf;
        edev->interrupt = bcminterrupt;
        edev->ifstat = bcmifstat;
        edev->transmit = bcmtransmit;
        edev->multicast = bcmmulticast;
        edev->promiscuous = bcmpromiscuous;
        edev->arg = edev;
        edev->mbps = 1000;
        
        if(bcminit(edev) == -1)
                goto redux;
        return 0;
}

void
etherbcmlink(void)
{
        addethercard("bcm57xx", bcmpnp);
}

Reply via email to