summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/src/9/pc/etherbcm.c669
-rw-r--r--sys/src/9/pc/pc1
2 files changed, 670 insertions, 0 deletions
diff --git a/sys/src/9/pc/etherbcm.c b/sys/src/9/pc/etherbcm.c
new file mode 100644
index 000000000..2da03fde2
--- /dev/null
+++ b/sys/src/9/pc/etherbcm.c
@@ -0,0 +1,669 @@
+/*
+ * Broadcom BCM57xx
+ * Not implemented:
+ * proper fatal error handling
+ * multiple rings
+ * QoS
+ * checksum offloading
+ */
+
+#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 Rbsz ROUNDUP(sizeof(Etherpkt)+4, 4)
+
+typedef struct Ctlr Ctlr;
+struct Ctlr {
+ Lock txlock;
+ Ctlr *link;
+ Pcidev *pdev;
+ ulong *nic, *status;
+ /* One Ring to find them, One Ring to bring them all and in the darkness bind them */
+ ulong *recvret, *recvprod, *sendr;
+ ulong port;
+ ulong recvreti, recvprodi, sendri, sendcleani;
+ Block **sends;
+ int active, duplex;
+};
+
+enum {
+ RecvRetRingLen = 0x200,
+ RecvProdRingLen = 0x200,
+ SendRingLen = 0x200,
+};
+
+enum {
+ Reset = 1<<0,
+ Enable = 1<<1,
+ Attn = 1<<2,
+
+ PowerControlStatus = 0x4C,
+
+ MiscHostCtl = 0x68,
+ ClearIntA = 1<<0,
+ MaskPCIInt = 1<<1,
+ IndirectAccessEnable = 1<<7,
+ EnablePCIStateRegister = 1<<4,
+ EnableClockControlRegister = 1<<5,
+ TaggedStatus = 1<<9,
+
+ 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])
+#define mem32(c, r) csr32(c, (r)+0x8000)
+
+static Ctlr *bcmhead, *bcmtail;
+
+static ulong
+dummyread(ulong x)
+{
+ return x;
+}
+
+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)
+{
+ Ctlr *ctlr;
+ ulong i;
+
+ ctlr = edev->ctlr;
+ miir(ctlr, PhyStatus); /* dummy read necessary */
+ if(!(miir(ctlr, PhyStatus) & PhyLinkStatus)) {
+ edev->link = 0;
+ edev->mbps = 1000;
+ ctlr->duplex = 1;
+ print("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;
+ print("bcm: link partner supports neither 10/100/1000 Mbps\n");
+ goto out;
+ }
+ print("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)
+{
+ csr32(ctlr, RecvBDRetRingIndex) = ctlr->recvreti = (ctlr->recvreti + 1) & (RecvRetRingLen - 1);
+}
+
+static int
+replenish(Ctlr *ctlr)
+{
+ ulong *next;
+ ulong incr;
+ Block *bp;
+
+ incr = (ctlr->recvprodi + 1) & (RecvProdRingLen - 1);
+ if(incr == (ctlr->status[2] >> 16)) return -1;
+ bp = iallocb(Rbsz);
+ if(bp == nil) {
+ print("bcm: out of memory for receive buffers\n");
+ return -1;
+ }
+ next = ctlr->recvprod + ctlr->recvprodi * 8;
+ memset(next, 0, 32);
+ next[1] = PADDR(bp->rp);
+ next[2] = Rbsz;
+ next[7] = (ulong) bp;
+ csr32(ctlr, RecvProdBDRingIndex) = ctlr->recvprodi = incr;
+ return 0;
+}
+
+static void
+bcmreceive(Ether *edev)
+{
+ Ctlr *ctlr;
+ Block *bp;
+ ulong *pkt, len;
+
+ ctlr = edev->ctlr;
+ for(; pkt = currentrecvret(ctlr); replenish(ctlr), consumerecvret(ctlr)) {
+ bp = (Block*) pkt[7];
+ len = pkt[2] & 0xFFFF;
+ bp->wp = bp->rp + len;
+ if((pkt[3] & PacketEnd) == 0) print("bcm: partial frame received -- shouldn't happen\n");
+ if(pkt[3] & FrameError) {
+ freeb(bp); /* dump erroneous packets */
+ } else {
+ etheriq(edev, bp, 1);
+ }
+ }
+}
+
+static void
+bcmtransclean(Ether *edev)
+{
+ Ctlr *ctlr;
+ ulong incr;
+
+ ctlr = edev->ctlr;
+ ilock(&ctlr->txlock);
+ while(ctlr->sendcleani != (ctlr->status[4] >> 16)) {
+ freeb(ctlr->sends[ctlr->sendri]);
+ ctlr->sends[ctlr->sendri] = 0;
+ ctlr->sendcleani = (ctlr->sendcleani + 1) & (SendRingLen - 1);
+ }
+ iunlock(&ctlr->txlock);
+}
+
+static void
+bcmtransmit(Ether *edev)
+{
+ Ctlr *ctlr;
+ Block *bp;
+ ulong *next;
+ ulong incr;
+
+ ctlr = edev->ctlr;
+ ilock(&ctlr->txlock);
+ while(1) {
+ incr = (ctlr->sendri + 1) & (SendRingLen - 1);
+ if(incr == (ctlr->status[4] >> 16)) {
+ print("bcm: send queue full\n");
+ break;
+ }
+ bp = qget(edev->oq);
+ if(bp == nil) break;
+ next = ctlr->sendr + ctlr->sendri * 4;
+ next[0] = 0;
+ next[1] = PADDR(bp->rp);
+ next[2] = (BLEN(bp) << 16) | PacketEnd;
+ next[3] = 0;
+ ctlr->sends[ctlr->sendri] = bp;
+ 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) & 0xF8FF8080UL) {
+ panic("bcm: fatal error %#.8ux", csr32(ctlr, FlowAttention));
+ }
+ csr32(ctlr, FlowAttention) = 0;
+ }
+ csr32(ctlr, MACEventStatus) = 0; /* worth ignoring */
+ if(csr32(ctlr, ReadDMAStatus) || csr32(ctlr, WriteDMAStatus)) {
+ print("bcm: DMA error\n");
+ csr32(ctlr, ReadDMAStatus) = 0;
+ csr32(ctlr, WriteDMAStatus) = 0;
+ }
+ if(csr32(ctlr, RISCState)) {
+ if(csr32(ctlr, RISCState) & 0x78000403) {
+ panic("bcm: RISC halted %#.8ux", csr32(ctlr, RISCState));
+ }
+ csr32(ctlr, RISCState) = 0;
+ }
+}
+
+static void
+bcminterrupt(Ureg*, void *arg)
+{
+ Ether *edev;
+ Ctlr *ctlr;
+ ulong status, tag;
+
+ edev = arg;
+ ctlr = edev->ctlr;
+ dummyread(csr32(ctlr, InterruptMailbox));
+ 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);
+// print("bcm: interrupt %8ulx %8ulx\n", ctlr->status[2], ctlr->status[4]);
+ bcmreceive(edev);
+ bcmtransclean(edev);
+ bcmtransmit(edev);
+ csr32(ctlr, InterruptMailbox) = tag << 24;
+}
+
+static void
+bcminit(Ether *edev)
+{
+ ulong i, j;
+ Ctlr *ctlr;
+
+ ctlr = edev->ctlr;
+ print("bcm: reset\n");
+ /* initialization procedure according to the datasheet */
+ csr32(ctlr, MiscHostCtl) |= MaskPCIInt | ClearIntA;
+ csr32(ctlr, SwArbitration) |= SwArbitSet1;
+ while((csr32(ctlr, SwArbitration) & SwArbitWon1) == 0);
+ csr32(ctlr, MemArbiterMode) |= Enable;
+ csr32(ctlr, MiscHostCtl) |= IndirectAccessEnable | EnablePCIStateRegister | EnableClockControlRegister;
+ csr32(ctlr, MemoryWindow) = 0;
+ mem32(ctlr, 0xB50) = 0x4B657654; /* magic number bullshit */
+ csr32(ctlr, MiscConfiguration) |= GPHYPowerDownOverride | DisableGRCResetOnPCIE;
+ csr32(ctlr, MiscConfiguration) |= CoreClockBlocksReset;
+ microdelay(100000);
+ ctlr->pdev->pcr |= 1<<1;
+ pcisetbme(ctlr->pdev);
+ csr32(ctlr, MiscHostCtl) |= MaskPCIInt;
+ csr32(ctlr, MemArbiterMode) |= Enable;
+ csr32(ctlr, MiscHostCtl) |= IndirectAccessEnable | EnablePCIStateRegister | EnableClockControlRegister | TaggedStatus;
+ csr32(ctlr, ModeControl) |= ByteWordSwap;
+ csr32(ctlr, MACMode) = (csr32(ctlr, MACMode) & MACPortMask) | MACPortGMII;
+ microdelay(40000);
+ while(mem32(ctlr, 0xB50) != 0xB49A89AB);
+ 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) = 0;
+ csr32(ctlr, ReceiveBDHostAddr + 4) = PADDR(ctlr->recvprod);
+ csr32(ctlr, ReceiveBDFlags) = RecvProdRingLen << 16;
+ csr32(ctlr, ReceiveBDNIC) = 0x6000;
+ csr32(ctlr, ReceiveBDRepl) = 25;
+ csr32(ctlr, SendBDRingHostIndex) = 0;
+ csr32(ctlr, SendBDRingHostIndex+4) = 0;
+ mem32(ctlr, SendRCB) = 0;
+ mem32(ctlr, SendRCB + 4) = PADDR(ctlr->sendr);
+ mem32(ctlr, SendRCB + 8) = SendRingLen << 16;
+ mem32(ctlr, SendRCB + 12) = 0x4000;
+ for(i=1;i<4;i++)
+ mem32(ctlr, RecvRetRCB + i * 0x10 + 8) = 2;
+ mem32(ctlr, RecvRetRCB) = 0;
+ mem32(ctlr, RecvRetRCB + 4) = PADDR(ctlr->recvret);
+ mem32(ctlr, RecvRetRCB + 8) = RecvRetRingLen << 16;
+ csr32(ctlr, RecvProdBDRingIndex) = 0;
+ csr32(ctlr, RecvProdBDRingIndex+4) = 0;
+ /* this delay is not in the datasheet, but necessary; Broadcom is fucking with us */
+ microdelay(1000);
+ i = csr32(ctlr, 0x410);
+ 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) = 0;
+ csr32(ctlr, StatusBlockHostAddr + 4) = PADDR(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));
+ 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;
+ while(csr32(ctlr, MSIMode) & Enable);
+ csr32(ctlr, MiscHostCtl) &= ~(MaskPCIInt | ClearIntA);
+}
+
+static void
+bcmpci(void)
+{
+ Pcidev *pdev;
+
+ pdev = nil;
+ while(pdev = pcimatch(pdev, 0, 0)) {
+ Ctlr *ctlr;
+ void *mem;
+
+ if(pdev->ccrb != 2 || pdev->ccru != 0)
+ continue;
+
+ switch((pdev->vid<<16) | pdev->did){
+ default: continue;
+ case 0x14e4165a:
+ case 0x14e4167d:
+ case 0x14e41670:
+ case 0x14e41672:
+ case 0x14e41673:
+ case 0x14e41674:
+ case 0x14e4167A:
+ case 0x14e4167b:
+ case 0x14e41693:
+ case 0x14e4169B:
+ case 0x14e41712:
+ case 0x14e41713:
+ break;
+ }
+ pcisetbme(pdev);
+ pcisetpms(pdev, 0);
+ ctlr = malloc(sizeof(Ctlr));
+ if(ctlr == nil) {
+ print("bcm: unable to alloc Ctlr\n");
+ continue;
+ }
+ mem = vmap(pdev->mem[0].bar & ~0x0F, pdev->mem[0].size);
+ if(mem == nil) {
+ print("bcm: can't map %8.8luX\n", pdev->mem[0].bar);
+ free(ctlr);
+ continue;
+ }
+ ctlr->pdev = pdev;
+ ctlr->nic = mem;
+ ctlr->port = pdev->mem[0].bar & ~0x0F;
+ 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(Block) * SendRingLen);
+ if(bcmhead != nil)
+ bcmtail->link = ctlr;
+ else
+ bcmhead = ctlr;
+ bcmtail = ctlr;
+ }
+}
+
+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;
+
+ if(bcmhead == nil)
+ bcmpci();
+
+ for(ctlr = bcmhead; ctlr != nil; ctlr = ctlr->link) {
+ if(ctlr->active)
+ continue;
+
+ if(edev->port == 0 || edev->port == ctlr->port) {
+ ctlr->active = 1;
+ break;
+ }
+ }
+
+ if(ctlr == nil)
+ return -1;
+
+ edev->ctlr = ctlr;
+ edev->port = ctlr->port;
+ edev->irq = ctlr->pdev->intl;
+ edev->tbdf = ctlr->pdev->tbdf;
+ edev->interrupt = bcminterrupt;
+ edev->transmit = bcmtransmit;
+ edev->multicast = bcmmulticast;
+ edev->promiscuous = bcmpromiscuous;
+ edev->arg = edev;
+ edev->mbps = 1000;
+
+ bcminit(edev);
+ return 0;
+}
+
+void
+etherbcmlink(void)
+{
+ addethercard("BCM57xx", bcmpnp);
+}
diff --git a/sys/src/9/pc/pc b/sys/src/9/pc/pc
index ba897e94a..84017436b 100644
--- a/sys/src/9/pc/pc
+++ b/sys/src/9/pc/pc
@@ -52,6 +52,7 @@ link
ether82563 pci
ether82557 pci
ether83815 pci
+ etherbcm pci
etherdp83820 pci
etherec2t ether8390
etherelnk3 pci