ref: ae1cfc9082b3f56a8502938b892bf0555110a741
dir: /sys/src/9/pc/etherbcm.c/
/* * 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/pci.h" #include "../port/error.h" #include "../port/netif.h" #include "../port/etherif.h" #define Rbsz ROUNDUP(sizeof(Etherpkt)+4, 4) typedef struct Ctlr Ctlr; struct Ctlr { Lock txlock, imlock; Ctlr *link; uvlong port; 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 recvreti, recvprodi, sendri, sendcleani; Block **sends, **recvs; int active, duplex; int phy; }; 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, ByteSwap = 1<<2, WordSwap = 1<<3, EnablePCIStateRegister = 1<<4, EnableClockControlRegister = 1<<5, IndirectAccessEnable = 1<<7, 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, }; enum { BCM5752 = 0x1600, BCM5752M = 0x1601, BCM5709 = 0x1639, BCM5709S = 0x163a, BCM5716 = 0x163b, BCM5716S = 0x163c, BCM5700 = 0x1644, BCM5701 = 0x1645, BCM5702 = 0x1646, BCM5703 = 0x1647, BCM5704 = 0x1648, BCM5704S_2 = 0x1649, BCM5706 = 0x164a, BCM5708 = 0x164c, BCM5702FE = 0x164d, BCM57710 = 0x164e, BCM57711 = 0x164f, BCM57711E = 0x1650, BCM5705 = 0x1653, BCM5705_2 = 0x1654, BCM5717 = 0x1655, BCM5718 = 0x1656, BCM5719 = 0x1657, BCM5721 = 0x1659, BCM5722 = 0x165a, BCM5723 = 0x165b, BCM5724 = 0x165c, BCM5705M = 0x165d, BCM5705M_2 = 0x165e, BCM5720 = 0x165f, BCM5714 = 0x1668, BCM5780 = 0x166a, BCM5780S = 0x166b, BCM5754M = 0x1672, BCM5755M = 0x1673, BCM5756ME = 0x1674, BCM5750 = 0x1676, BCM5751 = 0x1677, BCM5715 = 0x1678, BCM5715S = 0x1679, BCM5754 = 0x167a, BCM5755 = 0x167b, BCM5750M = 0x167c, BCM5751M = 0x167d, BCM5751F = 0x167e, BCM5787F = 0x167f, BCM5761e = 0x1680, BCM5761 = 0x1681, BCM5764M = 0x1684, BCM57766 = 0x1686, BCM5762 = 0x1687, BCM57760 = 0x1690, BCM57788 = 0x1691, BCM57780 = 0x1692, BCM5787M = 0x1693, BCM57790 = 0x1694, BCM5782 = 0x1696, BCM5784M = 0x1698, BCM5785 = 0x1699, BCM5786 = 0x169a, BCM5787 = 0x169b, BCM5788 = 0x169c, BCM5789 = 0x169d, BCM5785_2 = 0x16a0, BCM5702X = 0x16a6, BCM5703X = 0x16a7, BCM5704S = 0x16a8, BCM5706S = 0x16aa, BCM5708S = 0x16ac, BCM57761 = 0x16b0, BCM57781 = 0x16b1, BCM57791 = 0x16b2, BCM57765 = 0x16b4, BCM57785 = 0x16b5, BCM57795 = 0x16b6, BCM5702A3 = 0x16c6, BCM5703_2 = 0x16c7, BCM5781 = 0x16dd, BCM5753 = 0x16f7, BCM5753M = 0x16fd, BCM5753F = 0x16fe, BCM5906 = 0x1712, BCM5906M = 0x1713, }; #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 phyno(Ctlr *ctlr) { switch(ctlr->pdev->did) { case BCM5717: case BCM5718: case BCM5719: case BCM5720: return BUSFNO(ctlr->pdev->tbdf) + 1; } return 1; } static int miir(Ctlr *ctlr, int ra) { while(csr32(ctlr, MIComm) & (1<<29)); csr32(ctlr, MIComm) = (ra << 16) | (ctlr->phy << 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) | (ctlr->phy << 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, idx; Block *bp; u64int pa; idx = ctlr->recvprodi; incr = (idx + 1) & (RecvProdRingLen - 1); if(incr == (ctlr->status[2] >> 16)) return -1; if(ctlr->recvs[idx] != 0) return -1; bp = iallocb(Rbsz); if(bp == nil) { print("bcm: out of memory for receive buffers\n"); return -1; } ctlr->recvs[idx] = bp; next = ctlr->recvprod + idx * 8; memset(next, 0, 32); pa = PCIWADDR(bp->rp); next[0] = pa >> 32; next[1] = pa; next[2] = Rbsz; next[7] = idx; coherence(); csr32(ctlr, RecvProdBDRingIndex) = ctlr->recvprodi = incr; return 0; } static void bcmreceive(Ether *edev) { Ctlr *ctlr; Block *bp; ulong *pkt, len, idx; ctlr = edev->ctlr; for(; pkt = currentrecvret(ctlr); replenish(ctlr), consumerecvret(ctlr)) { idx = pkt[7] & (RecvProdRingLen - 1); bp = ctlr->recvs[idx]; if(bp == 0) { print("bcm: nil block at %lux -- shouldn't happen\n", idx); break; } ctlr->recvs[idx] = 0; 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); } } } static void bcmtransclean(Ether *edev, int dolock) { Ctlr *ctlr; ctlr = edev->ctlr; if(dolock) ilock(&ctlr->txlock); while(ctlr->sendcleani != (ctlr->status[4] >> 16)) { freeb(ctlr->sends[ctlr->sendcleani]); ctlr->sends[ctlr->sendcleani] = 0; ctlr->sendcleani = (ctlr->sendcleani + 1) & (SendRingLen - 1); } if(dolock) iunlock(&ctlr->txlock); } static void bcmtransmit(Ether *edev) { Ctlr *ctlr; Block *bp; ulong *next; ulong incr; u64int pa; 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; pa = PCIWADDR(bp->rp); next[0] = pa >> 32; next[1] = pa; next[2] = (BLEN(bp) << 16) | PacketEnd; next[3] = 0; if(ctlr->sends[ctlr->sendri] != 0) freeb(ctlr->sends[ctlr->sendri]); 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) & 0xF8FF8080UL) { panic("bcm: fatal error %#.8ulx", 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 %#.8ulx", 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; ilock(&ctlr->imlock); 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, 1); bcmtransmit(edev); csr32(ctlr, InterruptMailbox) = tag << 24; iunlock(&ctlr->imlock); } static int bcminit(Ether *edev) { ulong i, j; Ctlr *ctlr; u64int pa; ctlr = edev->ctlr; print("bcm: reset\n"); /* initialization procedure according to the datasheet */ csr32(ctlr, MiscHostCtl) |= MaskPCIInt | ClearIntA; csr32(ctlr, SwArbitration) |= SwArbitSet1; for(i = 0; i < 10000 && (csr32(ctlr, SwArbitration) & SwArbitWon1) == 0; i++) microdelay(100); if(i == 10000){ iprint("bcm: arbiter failed to respond\n"); return -1; } csr32(ctlr, MemArbiterMode) |= Enable; csr32(ctlr, MiscHostCtl) |= IndirectAccessEnable | EnablePCIStateRegister | EnableClockControlRegister; csr32(ctlr, MiscHostCtl) = (csr32(ctlr, MiscHostCtl) & ~(ByteSwap|WordSwap)) | WordSwap; csr32(ctlr, ModeControl) |= ByteWordSwap; 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; /* pci memory access enable */ pcisetbme(ctlr->pdev); csr32(ctlr, MiscHostCtl) |= MaskPCIInt; csr32(ctlr, MemArbiterMode) |= Enable; csr32(ctlr, MiscHostCtl) = (csr32(ctlr, MiscHostCtl) & ~(ByteSwap|WordSwap)) | WordSwap; csr32(ctlr, MiscHostCtl) |= IndirectAccessEnable | EnablePCIStateRegister | EnableClockControlRegister | TaggedStatus; csr32(ctlr, ModeControl) |= ByteWordSwap; csr32(ctlr, MACMode) = (csr32(ctlr, MACMode) & MACPortMask) | MACPortGMII; microdelay(40000); for(i = 0; i < 100000 && mem32(ctlr, 0xB50) != 0xB49A89AB; i++) microdelay(100); if(i == 100000){ iprint("bcm: chip failed to reset\n"); return -1; } switch(ctlr->pdev->did){ case BCM5721: case BCM5751: case BCM5752: csr32(ctlr, TLPControl) |= (1<<25) | (1<<29); break; } 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; for(i = 0; i < 100 && (csr32(ctlr, BufferManMode) & Enable) == 0; i++) microdelay(100); if(i == 100){ iprint("bcm: buffer manager failed to start\n"); return -1; } csr32(ctlr, FTQReset) = -1; csr32(ctlr, FTQReset) = 0; for(i = 0; i < 1000 && csr32(ctlr, FTQReset) != 0; i++) microdelay(100); if(i == 1000){ iprint("bcm: ftq failed to reset\n"); return -1; } pa = PCIWADDR(ctlr->recvprod); csr32(ctlr, ReceiveBDHostAddr + 0) = pa >> 32; csr32(ctlr, ReceiveBDHostAddr + 4) = pa; switch(ctlr->pdev->did) { case BCM5717: case BCM5718: case BCM5719: case BCM5720: /* 5717 series have a different receive bd nic addr, * and a max frame length field in bits 2 to 15. */ csr32(ctlr, ReceiveBDFlags) = RecvProdRingLen << 16 | 1536 << 2; csr32(ctlr, ReceiveBDNIC) = 0x40000; break; case BCM5762: case BCM57765: case BCM57766: /* 57765 series have the max frame length field, * but not the different receive bd addr */ csr32(ctlr, ReceiveBDFlags) = RecvProdRingLen << 16 | 1536 << 2; csr32(ctlr, ReceiveBDNIC) = 0x6000; break; default: csr32(ctlr, ReceiveBDFlags) = RecvProdRingLen << 16; csr32(ctlr, ReceiveBDNIC) = 0x6000; } csr32(ctlr, ReceiveBDRepl) = 25; csr32(ctlr, SendBDRingHostIndex) = 0; csr32(ctlr, SendBDRingHostIndex+4) = 0; pa = PCIWADDR(ctlr->sendr); mem32(ctlr, SendRCB + 0) = pa >> 32; mem32(ctlr, SendRCB + 4) = pa; mem32(ctlr, SendRCB + 8) = SendRingLen << 16; mem32(ctlr, SendRCB + 12) = 0x4000; for(i=1;i<4;i++) mem32(ctlr, RecvRetRCB + i * 0x10 + 8) = 2; pa = PCIWADDR(ctlr->recvret); mem32(ctlr, RecvRetRCB + 0) = pa >> 32; mem32(ctlr, RecvRetRCB + 4) = pa; 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; for(i = 0; i < 200 && csr32(ctlr, HostCoalescingMode) != 0; i++) microdelay(100); if(i == 200){ iprint("bcm: host coalescing engine failed to stop\n"); return -1; } csr32(ctlr, HostCoalescingRecvTicks) = 150; csr32(ctlr, HostCoalescingSendTicks) = 150; csr32(ctlr, RecvMaxCoalescedFrames) = 10; csr32(ctlr, SendMaxCoalescedFrames) = 10; csr32(ctlr, RecvMaxCoalescedFramesInt) = 0; csr32(ctlr, SendMaxCoalescedFramesInt) = 0; pa = PCIWADDR(ctlr->status); csr32(ctlr, StatusBlockHostAddr + 0) = pa >> 32; csr32(ctlr, StatusBlockHostAddr + 4) = pa; 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); for(i = 0; i < 1000 && miir(ctlr, PhyControl) & (1<<15); i++) microdelay(100); if(i == 1000){ iprint("bcm: PHY failed to reset\n"); return -1; } 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) { Pcidev *pdev; pdev = nil; while(pdev = pcimatch(pdev, 0, 0)) { Ctlr *ctlr; void *mem; if(pdev->ccrb != 2 || pdev->ccru != 0) continue; if(pdev->vid != 0x14e4) continue; if(pdev->mem[0].bar & 1) continue; switch(pdev->did){ default: continue; case BCM5752: case BCM5752M: case BCM5709: case BCM5709S: case BCM5716: case BCM5716S: case BCM5700: case BCM5701: case BCM5702: case BCM5703: case BCM5704: case BCM5704S_2: case BCM5706: case BCM5708: case BCM5702FE: case BCM57710: case BCM57711: case BCM57711E: case BCM5705: case BCM5705_2: case BCM5717: case BCM5718: case BCM5719: case BCM5721: case BCM5722: case BCM5723: case BCM5724: case BCM5705M: case BCM5705M_2: case BCM5720: case BCM5714: case BCM5780: case BCM5780S: case BCM5754M: case BCM5755M: case BCM5756ME: case BCM5750: case BCM5751: case BCM5715: case BCM5715S: case BCM5754: case BCM5755: case BCM5750M: case BCM5751M: case BCM5751F: case BCM5787F: case BCM5761e: case BCM5761: case BCM5764M: case BCM57766: case BCM5762: case BCM57760: case BCM57788: case BCM57780: case BCM5787M: case BCM57790: case BCM5782: case BCM5784M: case BCM5785: case BCM5786: case BCM5787: case BCM5788: case BCM5789: case BCM5785_2: case BCM5702X: case BCM5703X: case BCM5704S: case BCM5706S: case BCM5708S: case BCM57761: case BCM57781: case BCM57791: case BCM57765: case BCM57785: case BCM57795: case BCM5702A3: case BCM5703_2: case BCM5781: case BCM5753: case BCM5753M: case BCM5753F: case BCM5906: /* ??? */ case BCM5906M: /* ??? */ case 0x1670: /* ??? */ break; } ctlr = malloc(sizeof(Ctlr)); if(ctlr == nil) { print("bcm: unable to alloc Ctlr\n"); continue; } ctlr->sends = malloc(sizeof(ctlr->sends[0]) * SendRingLen); ctlr->recvs = malloc(sizeof(ctlr->recvs[0]) * RecvProdRingLen); if(ctlr->sends == nil || ctlr->recvs == nil){ print("bcm: unable to alloc ctlr->sends and ctlr->recvs\n"); free(ctlr->sends); free(ctlr->recvs); free(ctlr); continue; } ctlr->port = pdev->mem[0].bar & ~0xF; mem = vmap(ctlr->port, pdev->mem[0].size); if(mem == nil) { print("bcm: can't map %llux\n", ctlr->port); free(ctlr->sends); free(ctlr->recvs); free(ctlr); continue; } ctlr->pdev = pdev; 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->phy = phyno(ctlr); 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; again: 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; pcienable(ctlr->pdev); pcisetbme(ctlr->pdev); edev->ctlr = ctlr; edev->port = ctlr->port; edev->irq = ctlr->pdev->intl; edev->tbdf = ctlr->pdev->tbdf; edev->transmit = bcmtransmit; edev->multicast = bcmmulticast; edev->promiscuous = bcmpromiscuous; edev->arg = edev; edev->mbps = 1000; if(bcminit(edev) < 0){ edev->ctlr = nil; goto again; } intrenable(edev->irq, bcminterrupt, edev, edev->tbdf, edev->name); return 0; } void etherbcmlink(void) { addethercard("bcm", bcmpnp); }