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);
}