<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv=Content-Type content="text/html; charset=utf8">
<title>/usr/web/sources/contrib/aiju/etherbcm.c - Plan 9 from Bell Labs</title>
<!-- THIS FILE IS AUTOMATICALLY GENERATED. -->
<!-- EDIT sources.tr INSTEAD. -->
</meta>
</head>
<body>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="line-height: 1.2em; margin-left: 1.00in; text-indent: 0.00in; margin-right: 1.00in; margin-top: 0; margin-bottom: 0; text-align: center;">
<span style="font-size: 10pt"><a href="/plan9/">Plan 9 from Bell Labs</a>&rsquo;s /usr/web/sources/contrib/aiju/etherbcm.c</span></p>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center><font size=-1>
Copyright © 2009 Alcatel-Lucent.<br />
Distributed under the
<a href="/plan9/license.html">Lucent Public License version 1.02</a>.
<br />
<a href="/plan9/download.html">Download the Plan 9 distribution.</a>
</font>
</center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<table width="100%" cellspacing=0 border=0><tr><td align="center">
<table cellspacing=0 cellpadding=5 bgcolor="#eeeeff"><tr><td align="left">
<pre>
<!-- END HEADER -->
/*
* 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&lt;&lt;0,
       Enable = 1&lt;&lt;1,
       Attn = 1&lt;&lt;2,

       PowerControlStatus = 0x4C,

       MiscHostCtl = 0x68,
       ClearIntA = 1&lt;&lt;0,
       MaskPCIInt = 1&lt;&lt;1,
       IndirectAccessEnable = 1&lt;&lt;7,
       EnablePCIStateRegister = 1&lt;&lt;4,
       EnableClockControlRegister = 1&lt;&lt;5,
       TaggedStatus = 1&lt;&lt;9,

       DMARWControl = 0x6C,
       DMAWatermarkMask = ~(7&lt;&lt;19),
       DMAWatermarkValue = 3&lt;&lt;19,

       MemoryWindow = 0x7C,
       MemoryWindowData = 0x84,

       SendRCB = 0x100,
       RecvRetRCB = 0x200,

       InterruptMailbox = 0x204,

       RecvProdBDRingIndex = 0x26c,
       RecvBDRetRingIndex = 0x284,
       SendBDRingHostIndex = 0x304,

       MACMode = 0x400,
       MACPortMask = ~((1&lt;&lt;3)|(1&lt;&lt;2)),
       MACPortGMII = 1&lt;&lt;3,
       MACPortMII = 1&lt;&lt;2,
       MACEnable = (1&lt;&lt;23) | (1&lt;&lt;22) | (1&lt;&lt;21) | (1 &lt;&lt; 15) | (1 &lt;&lt; 14) | (1&lt;&lt;12) | (1&lt;&lt;11),
       MACHalfDuplex = 1&lt;&lt;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&lt;&lt;0,
       SendInitiatorMask = 0x0C0C,

       SendDataCompletionMode = 0x1000,
       SendBDSelectorMode = 0x1400,
       SendBDInitiatorMode = 0x1800,
       SendBDCompletionMode = 0x1C00,

       ReceiveListPlacementMode = 0x2000,
       ReceiveListPlacement = 0x2010,
       ReceiveListPlacementConfiguration = 0x2014,
       ReceiveStats = 1&lt;&lt;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&lt;&lt;4)|(1&lt;&lt;5)|(1&lt;&lt;2),//|(1&lt;&lt;1),
       HostStackUp = 1&lt;&lt;16,
       HostSendBDs = 1&lt;&lt;17,
       InterruptOnMAC = 1&lt;&lt;26,

       MiscConfiguration = 0x6804,
       CoreClockBlocksReset = 1&lt;&lt;0,
       GPHYPowerDownOverride = 1&lt;&lt;26,
       DisableGRCResetOnPCIE = 1&lt;&lt;29,
       TimerMask = ~0xFF,
       TimerValue = 65&lt;&lt;1,
       MiscLocalControl = 0x6808,
       InterruptOnAttn = 1&lt;&lt;3,
       AutoSEEPROM = 1&lt;&lt;24,

       SwArbitration = 0x7020,
       SwArbitSet1 = 1&lt;&lt;1,
       SwArbitWon1 = 1&lt;&lt;9,
       TLPControl = 0x7C00,

       PhyControl = 0x00,
       PhyStatus = 0x01,
       PhyLinkStatus = 1&lt;&lt;2,
       PhyAutoNegComplete = 1&lt;&lt;5,
       PhyPartnerStatus = 0x05,
       Phy100FD = 1&lt;&lt;8,
       Phy100HD = 1&lt;&lt;7,
       Phy10FD = 1&lt;&lt;6,
       Phy10HD = 1&lt;&lt;5,
       PhyGbitStatus = 0x0A,
       Phy1000FD = 1&lt;&lt;12,
       Phy1000HD = 1&lt;&lt;11,
       PhyAuxControl = 0x18,
       PhyIntStatus = 0x1A,
       PhyIntMask = 0x1B,

       Updated = 1&lt;&lt;0,
       LinkStateChange = 1&lt;&lt;1,
       Error = 1&lt;&lt;2,

       PacketEnd = 1&lt;&lt;2,
       FrameError = 1&lt;&lt;10,
};

#define csr32(c, r)     ((c)-&gt;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) &amp; (1&lt;&lt;29));
       csr32(ctlr, MIComm) = (ra &lt;&lt; 16) | (1 &lt;&lt; 21) | (1 &lt;&lt; 27) | (1 &lt;&lt; 29);
       while(csr32(ctlr, MIComm) &amp; (1&lt;&lt;29));
       if(csr32(ctlr, MIComm) &amp; (1&lt;&lt;28)) return -1;
       return csr32(ctlr, MIComm) &amp; 0xFFFF;
}

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

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

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

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

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

static int
replenish(Ctlr *ctlr)
{
       ulong *next;
       ulong incr;
       Block *bp;

       incr = (ctlr-&gt;recvprodi + 1) &amp; (RecvProdRingLen - 1);
       if(incr == (ctlr-&gt;status[2] &gt;&gt; 16)) return -1;
       bp = iallocb(Rbsz);
       if(bp == nil) {
               print("bcm: out of memory for receive buffers\n");
               return -1;
       }
       next = ctlr-&gt;recvprod + ctlr-&gt;recvprodi * 8;
       memset(next, 0, 32);
       next[1] = PADDR(bp-&gt;rp);
       next[2] = Rbsz;
       next[7] = (ulong) bp;
       csr32(ctlr, RecvProdBDRingIndex) = ctlr-&gt;recvprodi = incr;
       return 0;
}

static void
bcmreceive(Ether *edev)
{
       Ctlr *ctlr;
       Block *bp;
       ulong *pkt, len;

       ctlr = edev-&gt;ctlr;
       for(; pkt = currentrecvret(ctlr); replenish(ctlr), consumerecvret(ctlr)) {
               bp = (Block*) pkt[7];
               len = pkt[2] &amp; 0xFFFF;
               bp-&gt;wp = bp-&gt;rp + len;
               if((pkt[3] &amp; PacketEnd) == 0) print("bcm: partial frame received -- shouldn't happen\n");
               if(pkt[3] &amp; FrameError) {
                       freeb(bp); /* dump erroneous packets */
               } else {
                       etheriq(edev, bp, 1);
               }
       }
}

static void
bcmtransclean(Ether *edev)
{
       Ctlr *ctlr;

       ctlr = edev-&gt;ctlr;
       ilock(&amp;ctlr-&gt;txlock);
       while(ctlr-&gt;sendcleani != (ctlr-&gt;status[4] &gt;&gt; 16)) {
               freeb(ctlr-&gt;sends[ctlr-&gt;sendri]);
               ctlr-&gt;sends[ctlr-&gt;sendri] = 0;
               ctlr-&gt;sendcleani = (ctlr-&gt;sendcleani + 1) &amp; (SendRingLen - 1);
       }
       iunlock(&amp;ctlr-&gt;txlock);
}

static void
bcmtransmit(Ether *edev)
{
       Ctlr *ctlr;
       Block *bp;
       ulong *next;
       ulong incr;

       ctlr = edev-&gt;ctlr;
       ilock(&amp;ctlr-&gt;txlock);
       while(1) {
               incr = (ctlr-&gt;sendri + 1) &amp; (SendRingLen - 1);
               if(incr == (ctlr-&gt;status[4] &gt;&gt; 16)) {
                       print("bcm: send queue full\n");
                       break;
               }
               bp = qget(edev-&gt;oq);
               if(bp == nil) break;
               next = ctlr-&gt;sendr + ctlr-&gt;sendri * 4;
               next[0] = 0;
               next[1] = PADDR(bp-&gt;rp);
               next[2] = (BLEN(bp) &lt;&lt; 16) | PacketEnd;
               next[3] = 0;
               ctlr-&gt;sends[ctlr-&gt;sendri] = bp;
               csr32(ctlr, SendBDRingHostIndex) = ctlr-&gt;sendri = incr;
       }
       iunlock(&amp;ctlr-&gt;txlock);
}

static void
bcmerror(Ether *edev)
{
       Ctlr *ctlr;

       ctlr = edev-&gt;ctlr;
       if(csr32(ctlr, FlowAttention)) {
               if(csr32(ctlr, FlowAttention) &amp; 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) &amp; 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-&gt;ctlr;
       dummyread(csr32(ctlr, InterruptMailbox));
       csr32(ctlr, InterruptMailbox) = 1;
       status = ctlr-&gt;status[0];
       tag = ctlr-&gt;status[1];
       ctlr-&gt;status[0] = 0;
       if(status &amp; Error) bcmerror(edev);
       if(status &amp; LinkStateChange) checklink(edev);
//      print("bcm: interrupt %8ulx %8ulx\n", ctlr-&gt;status[2], ctlr-&gt;status[4]);
       bcmreceive(edev);
       bcmtransclean(edev);
       bcmtransmit(edev);
       csr32(ctlr, InterruptMailbox) = tag &lt;&lt; 24;
}

static void
bcminit(Ether *edev)
{
       ulong i, j;
       Ctlr *ctlr;

       ctlr = edev-&gt;ctlr;
       print("bcm: reset\n");
       /* initialization procedure according to the datasheet */
       csr32(ctlr, MiscHostCtl) |= MaskPCIInt | ClearIntA;
       csr32(ctlr, SwArbitration) |= SwArbitSet1;
       while((csr32(ctlr, SwArbitration) &amp; 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-&gt;pdev-&gt;pcr |= 1&lt;&lt;1;
       pcisetbme(ctlr-&gt;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) &amp; MACPortMask) | MACPortGMII;
       microdelay(40000);
       while(mem32(ctlr, 0xB50) != 0xB49A89AB);
       csr32(ctlr, TLPControl) |= (1&lt;&lt;25) | (1&lt;&lt;29);
       memset(ctlr-&gt;status, 0, 20);
       csr32(ctlr, DMARWControl) = (csr32(ctlr, DMARWControl) &amp; DMAWatermarkMask) | DMAWatermarkValue;
       csr32(ctlr, ModeControl) |= HostSendBDs | HostStackUp | InterruptOnMAC;
       csr32(ctlr, MiscConfiguration) = (csr32(ctlr, MiscConfiguration) &amp; TimerMask) | TimerValue;
       csr32(ctlr, MBUFLowWatermark) = 0x20;
       csr32(ctlr, MBUFHighWatermark) = 0x60;
       csr32(ctlr, LowWatermarkMaximum) = (csr32(ctlr, LowWatermarkMaximum) &amp; LowWatermarkMaxMask) | LowWatermarkMaxValue;
       csr32(ctlr, BufferManMode) |= Enable | Attn;
       while((csr32(ctlr, BufferManMode) &amp; Enable) == 0);
       csr32(ctlr, FTQReset) = -1;
       csr32(ctlr, FTQReset) = 0;
       while(csr32(ctlr, FTQReset));
       csr32(ctlr, ReceiveBDHostAddr) = 0;
       csr32(ctlr, ReceiveBDHostAddr + 4) = PADDR(ctlr-&gt;recvprod);
       csr32(ctlr, ReceiveBDFlags) = RecvProdRingLen &lt;&lt; 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-&gt;sendr);
       mem32(ctlr, SendRCB + 8) = SendRingLen &lt;&lt; 16;
       mem32(ctlr, SendRCB + 12) = 0x4000;
       for(i=1;i&lt;4;i++)
               mem32(ctlr, RecvRetRCB + i * 0x10 + 8) = 2;
       mem32(ctlr, RecvRetRCB) = 0;
       mem32(ctlr, RecvRetRCB + 4) = PADDR(ctlr-&gt;recvret);
       mem32(ctlr, RecvRetRCB + 8) = RecvRetRingLen &lt;&lt; 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-&gt;ea[0] = i &gt;&gt; 8;
       j += edev-&gt;ea[1] = i;
       i = csr32(ctlr, MACAddress + 4);
       j += edev-&gt;ea[2] = i &gt;&gt; 24;
       j += edev-&gt;ea[3] = i &gt;&gt; 16;
       j += edev-&gt;ea[4] = i &gt;&gt; 8;
       j += edev-&gt;ea[5] = i;
       csr32(ctlr, EthernetRandomBackoff) = j &amp; 0x3FF;
       csr32(ctlr, ReceiveMTU) = Rbsz;
       csr32(ctlr, TransmitMACLengths) = 0x2620;
       csr32(ctlr, ReceiveListPlacement) = 1&lt;&lt;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-&gt;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&lt;&lt;4);
       csr32(ctlr, SendDataInitiatorMode) |= Enable;
       csr32(ctlr, SendBDInitiatorMode) |= Enable | Attn;
       csr32(ctlr, SendBDSelectorMode) |= Enable | Attn;
       ctlr-&gt;recvprodi = 0;
       while(replenish(ctlr) &gt;= 0);
       csr32(ctlr, TransmitMACMode) |= Enable;
       csr32(ctlr, ReceiveMACMode) |= Enable;
       csr32(ctlr, PowerControlStatus) &amp;= ~3;
       csr32(ctlr, MIStatus) |= 1&lt;&lt;0;
       csr32(ctlr, MACEventEnable) = 0;
       csr32(ctlr, MACEventStatus) |= (1&lt;&lt;12);
       csr32(ctlr, MIMode) = 0xC0000;
       microdelay(40);
       miiw(ctlr, PhyControl, 1&lt;&lt;15);
       while(miir(ctlr, PhyControl) &amp; (1&lt;&lt;15));
       miiw(ctlr, PhyAuxControl, 2);
       miir(ctlr, PhyIntStatus);
       miir(ctlr, PhyIntStatus);
       miiw(ctlr, PhyIntMask, ~(1&lt;&lt;1));
       checklink(edev);
       csr32(ctlr, MACEventEnable) |= 1&lt;&lt;12;
       csr32(ctlr, MACHash) = -1;
       csr32(ctlr, MACHash+4) = -1;
       csr32(ctlr, MACHash+8) = -1;
       csr32(ctlr, MACHash+12) = -1;
       for(i = 0; i &lt; 8; i++) csr32(ctlr, ReceiveRules + 8 * i) = 0;
       csr32(ctlr, ReceiveRulesConfiguration) = 1 &lt;&lt; 3;
       csr32(ctlr, MSIMode) &amp;= ~Enable;
       while(csr32(ctlr, MSIMode) &amp; Enable);
       csr32(ctlr, MiscHostCtl) &amp;= ~(MaskPCIInt | ClearIntA);
}

static void
bcmpci(void)
{
       Pcidev *pdev;

       pdev = nil;
       while(pdev = pcimatch(pdev, 0, 0)) {
               Ctlr *ctlr;
               void *mem;

               if(pdev-&gt;ccrb != 2 || pdev-&gt;ccru != 0)
                       continue;

               switch((pdev-&gt;vid&lt;&lt;16) | pdev-&gt;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-&gt;mem[0].bar &amp; ~0x0F, pdev-&gt;mem[0].size);
               if(mem == nil) {
                       print("bcm: can't map %8.8luX\n", pdev-&gt;mem[0].bar);
                       free(ctlr);
                       continue;
               }
               ctlr-&gt;pdev = pdev;
               ctlr-&gt;nic = mem;
               ctlr-&gt;port = pdev-&gt;mem[0].bar &amp; ~0x0F;
               ctlr-&gt;status = xspanalloc(20, 16, 0);
               ctlr-&gt;recvprod = xspanalloc(32 * RecvProdRingLen, 16, 0);
               ctlr-&gt;recvret = xspanalloc(32 * RecvRetRingLen, 16, 0);
               ctlr-&gt;sendr = xspanalloc(16 * SendRingLen, 16, 0);
               ctlr-&gt;sends = malloc(sizeof(Block) * SendRingLen);
               if(bcmhead != nil)
                       bcmtail-&gt;link = ctlr;
               else
                       bcmhead = ctlr;
               bcmtail = ctlr;
       }
}

static void
bcmpromiscuous(void* arg, int on)
{
       Ctlr *ctlr;

       ctlr = ((Ether*)arg)-&gt;ctlr;
       if(on)
               csr32(ctlr, ReceiveMACMode) |= 1&lt;&lt;8;
       else
               csr32(ctlr, ReceiveMACMode) &amp;= ~(1&lt;&lt;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-&gt;link) {
               if(ctlr-&gt;active)
                       continue;

               if(edev-&gt;port == 0 || edev-&gt;port == ctlr-&gt;port) {
                       ctlr-&gt;active = 1;
                       break;
               }
       }

       if(ctlr == nil)
               return -1;

       edev-&gt;ctlr = ctlr;
       edev-&gt;port = ctlr-&gt;port;
       edev-&gt;irq = ctlr-&gt;pdev-&gt;intl;
       edev-&gt;tbdf = ctlr-&gt;pdev-&gt;tbdf;
       edev-&gt;interrupt = bcminterrupt;
       edev-&gt;transmit = bcmtransmit;
       edev-&gt;multicast = bcmmulticast;
       edev-&gt;promiscuous = bcmpromiscuous;
       edev-&gt;arg = edev;
       edev-&gt;mbps = 1000;

       bcminit(edev);
       return 0;
}

void
etherbcmlink(void)
{
       addethercard("BCM5755", bcmpnp);
}
<!-- BEGIN TAIL -->
</pre>
</td></tr></table>
</td></tr></table>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="line-height: 1.2em; margin-left: 1.00in; text-indent: 0.00in; margin-right: 1.00in; margin-top: 0; margin-bottom: 0; text-align: center;">
<span style="font-size: 10pt"></span></p>
<p style="margin-top: 0; margin-bottom: 0.50in"></p>
<p style="margin-top: 0; margin-bottom: 0.33in"></p>
<center><table border="0"><tr>
<td valign="middle"><a href="http://www.alcatel-lucent.com/"><img border="0" src="/plan9/img/logo_ft.gif" alt="Bell Labs" />
</a></td>
<td valign="middle"><a href="http://www.opensource.org"><img border="0" alt="OSI certified" src="/plan9/img/osi-certified-60x50.gif" />
</a></td>
<td><img style="padding-right: 45px;" alt="Powered by Plan 9" src="/plan9/img/power36.gif" />
</td>
</tr></table></center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center>
<span style="font-size: 10pt">(<a href="/plan9/">Return to Plan 9 Home Page</a>)</span>
</center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center><font size=-1>
<span style="font-size: 10pt"><a href="http://www.lucent.com/copyright.html">Copyright</a></span>
<span style="font-size: 10pt">© 2009 Alcatel-Lucent.</span>
<span style="font-size: 10pt">All Rights Reserved.</span>
<br />
<span style="font-size: 10pt">Comments to</span>
<span style="font-size: 10pt"><a href="mailto:[email protected]">[email protected]</a>.</span>
</font></center>
</body>
</html>