--- /sys/src/9/pc/etherbcm.c
+++ /sys/src/9/pc/etherbcm.c Fri Feb 19 00:00:00 2016
@@ -0,0 +1,898 @@
+/*
+ * Broadcom BCM57xx
+ * Not implemented:
+ * proper fatal error handling
+ * multiple rings
+ * 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"
+#include "ethermii.h"
+
+#define dprint(...) do{ if(debug)print(__VA_ARGS__); }while(0)
+#define Rbsz ROUNDUP(1514+4, 4)
+#define Pciwaddrh(va) (sizeof(uintptr)>4? (uvlong)PCIWADDR(va)>>32: 0)
+#define Pciwaddrl(va) PCIWADDR(va)
+
+typedef struct Ctlr Ctlr;
+struct Ctlr {
+ Lock txlock, imlock;
+ Ether *ether;
+ Ctlr *next;
+ Pcidev *pdev;
+ u32int *nic, *status;
+
+ u32int *recvret, *recvprod, *sendr;
+ uintptr port;
+ uint recvreti, recvprodi, sendri, sendcleani;
+ Block **sends;
+ Block **rxs;
+ int active, duplex;
+ int type;
+
+ uint nobuf;
+ uint partial;
+ uint rxerr;
+ uint qfull;
+ uint dmaerr;
+};
+
+enum {
+ /* configurable constants */
+ RxRetRingLen = 0x200,
+ RxProdRingLen = 0x200,
+ TxRingLen = 0x200,
+
+ Reset = 1<<0,
+ Enable = 1<<1,
+ Attn = 1<<2,
+
+ Pwrctlstat = 0x4C,
+
+ MiscHostCtl = 0x68,
+ TaggedStatus = 1<<9,
+ IndirAccessEn = 1<<7,
+ EnableClockCtl = 1<<5,
+ PCIStateRegEn = 1<<4,
+ WordSwap = 1<<3,
+ ByteSwap = 1<<2,
+ MaskPCIInt = 1<<1,
+ ClearIntA = 1<<0,
+
+ Fwmbox = 0x0b50, /* magic value exchange */
+ Fwmagic = 0x4b657654,
+
+ Dmarwctl = 0x6C,
+ DMAWaterMask = ~(7<<19),
+ DMAWaterValue = 3<<19,
+
+ Memwind = 0x7C,
+ MemwindData = 0x84,
+
+ TxRCB = 0x100,
+ RxRetRCB = 0x200,
+
+ InterruptMailbox = 0x204,
+
+ RxProdBDRingIdx = 0x26c,
+ RxBDRetRingIdx = 0x284,
+ TxBDRingHostIdx = 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,
+ RandomBackoff = 0x438,
+ RxMTU = 0x43C,
+ MIComm = 0x44C,
+ MIStatus = 0x450,
+ MIMode = 0x454,
+ RxMACMode = 0x468,
+ TxMACMode = 0x45C,
+ TxMACLengths = 0x464,
+ MACHash = 0x470,
+ RxRules = 0x480,
+
+ RxRulesConf = 0x500,
+ LowWaterMax = 0x504,
+ LowWaterMaxMask = ~0xFFFF,
+ LowWaterMaxValue = 2,
+
+ TxDataInitiatorMode = 0xC00,
+ TxInitiatorConf = 0x0C08,
+ TxStats = 1<<0,
+ TxInitiatorMask = 0x0C0C,
+
+ TxDataCompletionMode = 0x1000,
+ TxBDSelectorMode = 0x1400,
+ TxBDInitiatorMode = 0x1800,
+ TxBDCompletionMode = 0x1C00,
+
+ RxListPlacementMode = 0x2000,
+ RxListPlacement = 0x2010,
+ RxListPlacementConf = 0x2014,
+ RxStats = 1<<0,
+ RxListPlacementMask = 0x2018,
+
+ RxDataBDInitiatorMode = 0x2400,
+ RxBDHostAddr = 0x2450,
+ RxBDFlags = 0x2458,
+ RxBDNIC = 0x245C,
+ RxDataCompletionMode = 0x2800,
+ RxBDInitiatorMode = 0x2C00,
+ RxBDRepl = 0x2C18,
+
+ RxBDCompletionMode = 0x3000,
+ HostCoalMode = 0x3C00,
+ HostCoalRxTicks = 0x3C08,
+ HostCoalTxTicks = 0x3C0C,
+ RxMaxCoalFrames = 0x3C10,
+ TxMaxCoalFrames = 0x3C14,
+ RxMaxCoalFramesInt = 0x3C20,
+ TxMaxCoalFramesInt = 0x3C24,
+ StatusBlockHostAddr = 0x3C38,
+ FlowAttention = 0x3C48,
+
+ MemArbiterMode = 0x4000,
+
+ BufferManMode = 0x4400,
+
+ MBUFLowWater = 0x4414,
+ MBUFHighWater = 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,
+ HostTxBDs = 1<<17,
+ InterruptOnMAC = 1<<26,
+
+ MiscConf = 0x6804,
+ CoreClockBlocksReset = 1<<0,
+ GPHYPwrdnOverride = 1<<26,
+ DisableGRCRstOnPpcie = 1<<29,
+ TimerMask = ~0xFF,
+ TimerValue = 65<<1,
+ MiscLocalControl = 0x6808,
+ InterruptOnAttn = 1<<3,
+ AutoSEEPROM = 1<<24,
+
+ SwArbit = 0x7020,
+ SwArbitSet1 = 1<<1,
+ SwArbitWon1 = 1<<9,
+ Pcitlplpl = 0x7C00, /* "lower 1k of the pcie pl regs" ?? */
+
+ PhyAuxControl = 0x18,
+ PhyIntStatus = 0x1A,
+ PhyIntMask = 0x1B,
+
+ Updated = 1<<0,
+ LinkStateChange = 1<<1,
+ Error = 1<<2,
+
+ PacketEnd = 1<<2,
+ FrameError = 1<<10,
+};
+
+enum {
+ b5709,
+ b5722,
+ b5751,
+ b5754,
+ b5755,
+ b5756,
+ b5782,
+ b5787,
+ b5906,
+ Nctlrtype,
+};
+
+typedef struct Ctlrtype Ctlrtype;
+struct Ctlrtype {
+ int mtu;
+ int flag;
+ char *name;
+};
+
+static Ctlrtype cttab[Nctlrtype] = {
+[b5709] 1514, 0, "b5709",
+[b5722] 1514, 0, "b5722",
+[b5751] 1514, 0, "b5751",
+[b5754] 1514, 0, "b5754",
+[b5755] 1514, 0, "b5755",
+[b5756] 1514, 0, "b5756",
+[b5782] 1514, 0, "b5782",
+[b5787] 1514, 0, "b5787",
+[b5906] 1514, 0, "b5906",
+};
+
+#define csr32(c, r) ((c)->nic[(r)/4])
+
+static Ctlr *bcmhead;
+static int debug;
+
+static char*
+cname(Ctlr *c)
+{
+ return cttab[c->type].name;
+}
+
+static long
+bcmifstat(Ether *edev, void *a, long n, usize 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);
+ p = seprint(p, e, "type: %s\n", cname(c));
+
+ USED(p);
+ n = readstr(offset, a, n, s);
+ free(s);
+
+ return n;
+}
+
+enum {
+ Phybusy = 1<<29,
+ Phyrdfail = 1<<28,
+ Phyrd = 1<<27,
+ Phywr = 1<<26,
+};
+Lock miilock;
+
+static uint
+miiwait(Ctlr *ctlr)
+{
+ uint i, v;
+
+ for(i = 0; i < 100; i += 5){
+ microdelay(10);
+ v = csr32(ctlr, MIComm);
+ if((v & Phybusy) == 0){
+ microdelay(5);
+ return csr32(ctlr, MIComm);
+ }
+ microdelay(5);
+ }
+ print("#l%d: bcm: miiwait: timeout\n", ctlr->ether->ctlrno);
+ return ~0;
+}
+
+static int
+miir(Ctlr *ctlr, int r)
+{
+ uint v, phyno;
+
+ phyno = 1;
+ lock(&miilock);
+ csr32(ctlr, MIComm) = r<<16 | phyno<<21 | Phyrd | Phybusy;
+ v = miiwait(ctlr);
+ unlock(&miilock);
+ if(v == ~0)
+ return -1;
+ if(v & Phyrdfail){
+ print("#l%d: bcm: miir: fail\n", ctlr->ether->ctlrno);
+ return -1;
+ }
+ return v & 0xffff;
+}
+
+static int
+miiw(Ctlr *ctlr, int r, int v)
+{
+ uint phyno, w;
+
+ phyno = 1;
+ lock(&miilock);
+ csr32(ctlr, MIComm) = r<<16 | v&0xffff | phyno<<21 | Phywr | Phybusy;
+ w = miiwait(ctlr);
+ unlock(&miilock);
+ if(w == ~0)
+ return -1;
+ return 0;
+}
+
+static void
+checklink(Ether *edev)
+{
+ uint i;
+ Ctlr *ctlr;
+
+ ctlr = edev->ctlr;
+ miir(ctlr, Bmsr); /* read twice for current status as per 802.3 */
+ if(!(miir(ctlr, Bmsr) & BmsrLs)) {
+ edev->link = 0;
+ edev->mbps = 1000;
+ ctlr->duplex = 1;
+ dprint("bcm: no link\n");
+ goto out;
+ }
+ edev->link = 1;
+ while((miir(ctlr, Bmsr) & BmsrAnc) == 0)
+ ;
+ i = miir(ctlr, Mssr);
+ if(i & (Mssr1000THD | Mssr1000TFD)) {
+ edev->mbps = 1000;
+ ctlr->duplex = (i & Mssr1000TFD) != 0;
+ } else if(i = miir(ctlr, Anlpar), i & (AnaTXHD | AnaTXFD)) {
+ edev->mbps = 100;
+ ctlr->duplex = (i & AnaTXFD) != 0;
+ } else if(i & (Ana10HD | Ana10FD)) {
+ edev->mbps = 10;
+ ctlr->duplex = (i & Ana10FD) != 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 uint*
+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 & RxRetRingLen-1;
+ csr32(ctlr, RxBDRetRingIdx) = ctlr->recvreti;
+}
+
+static int
+replenish(Ctlr *ctlr)
+{
+ uint incr;
+ u32int *next;
+ Block *bp;
+
+ incr = (ctlr->recvprodi + 1) & (RxProdRingLen - 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, RxProdBDRingIdx) = ctlr->recvprodi = incr;
+ return 0;
+}
+
+static void
+bcmreceive(Ether *edev)
+{
+ uint len;
+ u32int *pkt;
+ 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) & (TxRingLen - 1);
+ }
+ iunlock(&ctlr->txlock);
+}
+
+static void
+bcmtransmit(Ether *edev)
+{
+ uint incr;
+ u32int *next;
+ Ctlr *ctlr;
+ Block *bp;
+
+ ctlr = edev->ctlr;
+ ilock(&ctlr->txlock);
+ for(;;){
+ incr = (ctlr->sendri + 1) & (TxRingLen - 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, TxBDRingHostIdx) = 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 %#.8ux", 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 %#.8ux", csr32(ctlr, RISCState));
+ csr32(ctlr, RISCState) = 0;
+ }
+}
+
+static void
+bcminterrupt(Ureg*, void *arg)
+{
+ u32int 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);
+ if(0)
+ iprint("bcm: interrupt %.8ux %.8ux\n", ctlr->status[2], ctlr->status[4]);
+ bcmreceive(edev);
+ bcmtransclean(edev);
+ bcmtransmit(edev);
+ csr32(ctlr, InterruptMailbox) = tag << 24;
+ iunlock(&ctlr->imlock);
+}
+
+static void
+mem32w(Ctlr *c, uint r, uint v)
+{
+ pcicfgw32(c->pdev, Memwind, r);
+ pcicfgw32(c->pdev, MemwindData, v);
+}
+
+static u32int
+mem32r(Ctlr *c, uint r)
+{
+ u32int v;
+
+ pcicfgw32(c->pdev, Memwind, r);
+ v = pcicfgr32(c->pdev, MemwindData);
+ pcicfgw32(c->pdev, Memwind, 0);
+ return v;
+}
+
+static int
+bcmµwait(Ctlr *ctlr, uint to, uint r, uint m, uint v)
+{
+ int i;
+
+ for(i = 0;; i += 100){
+ if((csr32(ctlr, r) & m) == v)
+ return 0;
+ if(i == to /* µs */)
+ return -1;
+ microdelay(100);
+ }
+}
+
+static int
+bcminit(Ether *edev)
+{
+ uint i;
+ u32int j;
+ Ctlr *ctlr;
+
+ ctlr = edev->ctlr;
+ dprint("bcm: reset\n");
+ /* initialization procedure according to the datasheet */
+ if(ctlr->type == b5709){
+ csr32(ctlr, 0x800) |= 1<<4; /* swreset */
+ microdelay(5);
+ }
+ csr32(ctlr, MiscHostCtl) |= MaskPCIInt | ClearIntA | WordSwap | IndirAccessEn;
+ if(ctlr->type != b5709){
+ csr32(ctlr, SwArbit) |= SwArbitSet1;
+ if(bcmµwait(ctlr, 2000, SwArbit, SwArbitWon1, SwArbitWon1) == -1){
+ print("bcm: arbiter failed to respond\n");
+ return -1;
+ }
+ }
+ csr32(ctlr, MemArbiterMode) |= Enable;
+ csr32(ctlr, MiscHostCtl) = WordSwap | IndirAccessEn | PCIStateRegEn | EnableClockCtl
+ | MaskPCIInt | ClearIntA;
+ csr32(ctlr, Memwind) = 0;
+ mem32w(ctlr, Fwmbox, Fwmagic);
+ csr32(ctlr, MiscConf) |= GPHYPwrdnOverride | DisableGRCRstOnPpcie | 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 | IndirAccessEn | PCIStateRegEn | EnableClockCtl | 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 %#.8ux\n", mem32r(ctlr, Fwmbox));
+ break; //return -1;
+ }
+ microdelay(100);
+ }
+ /*
+ * there appears to be no justification for setting these bits in any driver
+ * i can find. nor to i have a datasheet that recommends this. - quanstro
+ * csr32(ctlr, Pcitlplpl) |= 1<<25 | 1<<29;
+ */
+ memset(ctlr->status, 0, 20);
+ csr32(ctlr, Dmarwctl) = (csr32(ctlr, Dmarwctl) & DMAWaterMask) | DMAWaterValue;
+ csr32(ctlr, ModeControl) |= HostTxBDs | HostStackUp | InterruptOnMAC;
+ csr32(ctlr, MiscConf) = (csr32(ctlr, MiscConf) & TimerMask) | TimerValue;
+ csr32(ctlr, MBUFLowWater) = 0x20;
+ csr32(ctlr, MBUFHighWater) = 0x60;
+ csr32(ctlr, LowWaterMax) = (csr32(ctlr, LowWaterMax) & LowWaterMaxMask) | LowWaterMaxValue;
+ csr32(ctlr, BufferManMode) |= Enable | Attn;
+ if(bcmµwait(ctlr, 2000, BufferManMode, Enable, Enable) == -1){
+ print("bcm: failed to enable buffers\n");
+ return -1;
+ }
+ csr32(ctlr, FTQReset) = ~0;
+ csr32(ctlr, FTQReset) = 0;
+ if(bcmµwait(ctlr, 2000, FTQReset, ~0, 0) == -1){
+ print("bcm: failed to bring ftq out of reset\n");
+ return -1;
+ }
+ csr32(ctlr, RxBDHostAddr) = Pciwaddrh(ctlr->recvprod);
+ csr32(ctlr, RxBDHostAddr + 4) = Pciwaddrl(ctlr->recvprod);
+ csr32(ctlr, RxBDFlags) = RxProdRingLen << 16;
+ csr32(ctlr, RxBDNIC) = 0x6000;
+ csr32(ctlr, RxBDRepl) = 25;
+ csr32(ctlr, TxBDRingHostIdx) = 0;
+ csr32(ctlr, TxBDRingHostIdx+4) = 0;
+ mem32w(ctlr, TxRCB, Pciwaddrh(ctlr->sendr));
+ mem32w(ctlr, TxRCB + 4, Pciwaddrl(ctlr->sendr));
+ mem32w(ctlr, TxRCB + 8, TxRingLen << 16);
+ mem32w(ctlr, TxRCB + 12, 0x4000);
+ for(i=1; i<4; i++)
+ mem32w(ctlr, RxRetRCB + i * 0x10 + 8, 2);
+ mem32w(ctlr, RxRetRCB, Pciwaddrh(ctlr->recvret));
+ mem32w(ctlr, RxRetRCB + 4, Pciwaddrl(ctlr->recvret));
+ mem32w(ctlr, RxRetRCB + 8, RxRetRingLen << 16);
+ csr32(ctlr, RxProdBDRingIdx) = 0;
+ csr32(ctlr, RxProdBDRingIdx+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, RandomBackoff) = j & 0x3FF;
+ csr32(ctlr, RxMTU) = Rbsz;
+ csr32(ctlr, TxMACLengths) = 0x2620;
+ csr32(ctlr, RxListPlacement) = 1<<3; /* one list */
+ csr32(ctlr, RxListPlacementMask) = 0xFFFFFF;
+ csr32(ctlr, RxListPlacementConf) |= RxStats;
+ csr32(ctlr, TxInitiatorMask) = 0xFFFFFF;
+ csr32(ctlr, TxInitiatorConf) |= TxStats;
+ csr32(ctlr, HostCoalMode) = 0;
+ if(bcmµwait(ctlr, 2000, HostCoalMode, ~0, 0) == -1){
+ print("bcm: failed to unset coalescing\n");
+ return -1;
+ }
+ csr32(ctlr, HostCoalRxTicks) = 150;
+ csr32(ctlr, HostCoalTxTicks) = 150;
+ csr32(ctlr, RxMaxCoalFrames) = 10;
+ csr32(ctlr, TxMaxCoalFrames) = 10;
+ csr32(ctlr, RxMaxCoalFramesInt) = 0;
+ csr32(ctlr, TxMaxCoalFramesInt) = 0;
+ csr32(ctlr, StatusBlockHostAddr) = Pciwaddrh(ctlr->status);
+ csr32(ctlr, StatusBlockHostAddr + 4) = Pciwaddrl(ctlr->status);
+ csr32(ctlr, HostCoalMode) |= Enable;
+ csr32(ctlr, RxBDCompletionMode) |= Enable | Attn;
+ csr32(ctlr, RxListPlacementMode) |= 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, RxDataCompletionMode) |= Enable | Attn;
+ csr32(ctlr, TxDataCompletionMode) |= Enable;
+ csr32(ctlr, TxBDCompletionMode) |= Enable | Attn;
+ csr32(ctlr, RxBDInitiatorMode) |= Enable | Attn;
+ csr32(ctlr, RxDataBDInitiatorMode) |= Enable | (1<<4);
+ csr32(ctlr, TxDataInitiatorMode) |= Enable;
+ csr32(ctlr, TxBDInitiatorMode) |= Enable | Attn;
+ csr32(ctlr, TxBDSelectorMode) |= Enable | Attn;
+ ctlr->recvprodi = 0;
+ while(replenish(ctlr) >= 0)
+ ;
+ csr32(ctlr, TxMACMode) |= Enable;
+ csr32(ctlr, RxMACMode) |= Enable;
+ csr32(ctlr, Pwrctlstat) &= ~3;
+ csr32(ctlr, MIStatus) |= 1<<0;
+ csr32(ctlr, MACEventEnable) = 0;
+ csr32(ctlr, MACEventStatus) |= (1<<12);
+ csr32(ctlr, MIMode) = 0xC0000; /* set base mii clock */
+ microdelay(40);
+
+ if(0){
+ /* bug (ours): can't reset phy without dropping into 100mbit mode */
+ miiw(ctlr, Bmcr, BmcrR);
+ for(i = 0;; i += 100){
+ if((miir(ctlr, Bmcr) & BmcrR) == 0)
+ break;
+ if(i == 10000 /* µs */){
+ print("bcm: phy reset failure\n");
+ return -1;
+ }
+ microdelay(100);
+ }
+ }
+ miiw(ctlr, Bmcr, BmcrAne | BmcrRan);
+
+ miiw(ctlr, PhyAuxControl, 2);
+ miir(ctlr, PhyIntStatus);
+ miir(ctlr, PhyIntStatus);
+ miiw(ctlr, PhyIntMask, ~(1<<1));
+ csr32(ctlr, MACEventEnable) |= 1<<12;
+ for(i = 0; i < 4; i++)
+ csr32(ctlr, MACHash + 4*i) = ~0;
+ for(i = 0; i < 8; i++)
+ csr32(ctlr, RxRules + 8 * i) = 0;
+ csr32(ctlr, RxRulesConf) = 1 << 3;
+ csr32(ctlr, MSIMode) |= Enable;
+ csr32(ctlr, MiscHostCtl) &= ~(MaskPCIInt | ClearIntA);
+ dprint("bcm: reset: fin\n");
+ return 0;
+}
+
+static int
+didtype(Pcidev *p)
+{
+ if(p->vid != 0x14e4)
+ return -1;
+
+ switch(p->did){
+ default:
+ return -1;
+// case 0x163a: /* 5709s gbe */
+// return b5709;
+ case 0x165a: /* 5722 gbe */
+ return b5722;
+ case 0x1670: /* ?? */
+ return b5751;
+ case 0x1672: /* 5754m */
+ return b5754;
+ case 0x1673: /* 5755m gbe */
+ return b5755;
+ case 0x1674: /* 5756me gbe */
+ return b5756;
+ case 0x1677: /* 5751 gbe */
+ return b5751;
+ case 0x167a: /* 5754 gbe */
+ return b5754;
+ case 0x167b: /* 5755 gbe */
+ return b5755;
+ case 0x1693: /* 5787m gbe */
+ return b5787;
+ case 0x1696: /* 5782 gbe; steve */
+ return b5782;
+ case 0x169b: /* 5787 gbe */
+ return b5787;
+ case 0x1712: /* 5906 fast */
+ case 0x1713: /* 5906m fast */
+ return b5906;
+ case 0x167d: /* 5751m gbe */
+ case 0x167e: /* 5751f fast */
+ return b5751;
+ }
+}
+
+static void
+bcmpci(void)
+{
+ int type;
+ void *mem;
+ Ctlr *ctlr, **xx;
+ Pcidev *p;
+
+ xx = &bcmhead;
+ for(p = nil; p = pcimatch(p, 0, 0); ) {
+ if(p->ccrb != 2 || p->ccru != 0 || (type = didtype(p)) == -1)
+ continue;
+ pcisetbme(p);
+ pcisetpms(p, 0);
+ ctlr = malloc(sizeof(Ctlr));
+ if(ctlr == nil)
+ continue;
+ ctlr->type = type;
+ ctlr->port = p->mem[0].bar & ~(uintptr)0xf;
+ mem = vmap(ctlr->port, p->mem[0].size);
+ if(mem == nil) {
+ print("bcm: can't map %#ulx\n", ctlr->port);
+ free(ctlr);
+ continue;
+ }
+ ctlr->pdev = p;
+ ctlr->nic = mem;
+ ctlr->status = mallocalign(20, 16, 0, 0);
+ ctlr->recvprod = mallocalign(32 * RxProdRingLen, 16, 0, 0);
+ ctlr->recvret = mallocalign(32 * RxRetRingLen, 16, 0, 0);
+ ctlr->sendr = mallocalign(16 * TxRingLen, 16, 0, 0);
+ ctlr->sends = malloc(sizeof *ctlr->sends * TxRingLen);
+ ctlr->rxs = malloc(sizeof *ctlr->sends * TxRingLen);
+ *xx = ctlr;
+ xx = &ctlr->next;
+ }
+}
+
+static void
+bcmpromiscuous(void* arg, int on)
+{
+ Ctlr *ctlr;
+
+ ctlr = ((Ether*)arg)->ctlr;
+ if(on)
+ csr32(ctlr, RxMACMode) |= 1<<8;
+ else
+ csr32(ctlr, RxMACMode) &= ~(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;
+ }
+ }
+
+ ctlr->ether = edev;
+ 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);
+}
|