ref: 3bc4e5a6d568889eeeb2d0b7d443c0a3e02fb499
parent: 2a4c767c41025b3fd07790cb031f3c12fc25dd21
author: cinap_lenrek <[email protected]>
date: Thu Jul 25 05:11:53 EDT 2019
bcm64: work in progress genet ethernet driver for raspberry pi 4
--- /dev/null
+++ b/sys/src/9/bcm64/ethergenet.c
@@ -1,0 +1,1075 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/netif.h"
+#include "../port/etherif.h"
+#include "../port/ethermii.h"
+
+enum
+{
+ Rbsz = 2048,
+ Maxtu = 1536,
+
+ DmaOWN = 0x8000,
+ DmaSOP = 0x2000,
+ DmaEOP = 0x4000,
+ DmaRxLg = 0x10,
+ DmaRxNo = 0x08,
+ DmaRxErr = 0x04,
+ DmaRxCrc = 0x02,
+ DmaRxOv = 0x01,
+ DmaRxErrors = DmaRxLg|DmaRxNo|DmaRxErr|DmaRxCrc|DmaRxOv,
+
+ DmaTxQtag = 0x1F80,
+ DmaTxUnderrun = 0x0200,
+ DmaTxAppendCrc = 0x0040,
+ DmaTxOwCrc = 0x0020,
+ DmaTxDoCsum = 0x0010,
+
+ /* Ctlr->regs */
+ SysRevision = 0x00/4,
+ SysPortCtrl = 0x04/4,
+ PortModeIntEphy = 0,
+ PortModeIntGphy = 1,
+ PortModeExtEphy = 2,
+ PortModeExtGphy = 3,
+ PortModeExtRvmii50 = 4,
+ PortModeExtRvmii25 = 16 | 4,
+ LedActSourceMac = 1 << 9,
+
+ SysRbufFlushCtrl = 0x08/4,
+ SysTbufFlushCtrl = 0x0C/4,
+
+ ExtRgmiiOobCtrl = 0x8C/4,
+ RgmiiLink = 1 << 4,
+ OobDisable = 1 << 5,
+ RgmiiModeEn = 1 << 6,
+ IdModeDis = 1 << 16,
+
+ Intrl0 = 0x200/4,
+ IrqScb = 1 << 0,
+ IrqEphy = 1 << 1,
+ IrqPhyDetR = 1 << 2,
+ IrqPhyDetF = 1 << 3,
+ IrqLinkUp = 1 << 4,
+ IrqLinkDown = 1 << 5,
+ IrqUmac = 1 << 6,
+ IrqUmacTsv = 1 << 7,
+ IrqTbufUnderrun = 1 << 8,
+ IrqRbufOverflow = 1 << 9,
+ IrqHfbSm = 1 << 10,
+ IrqHfbMm = 1 << 11,
+ IrqMpdR = 1 << 12,
+ IrqRxDmaDone = 1 << 13,
+ IrqRxDmaPDone = 1 << 14,
+ IrqRxDmaBDone = 1 << 15,
+ IrqTxDmaDone = 1 << 16,
+ IrqTxDmaPDone = 1 << 17,
+ IrqTxDmaBDone = 1 << 18,
+ IrqMdioDone = 1 << 23,
+ IrqMdioError = 1 << 24,
+ Intrl1 = 0x240/4,
+ /* Intrl0/1 + ... */
+ IntrSts = 0x00/4,
+ IntrSet = 0x04/4,
+ IntrClr = 0x08/4,
+ IntrMaskSts = 0x0C/4,
+ IntrMaskSet = 0x10/4,
+ IntrMaskClr = 0x14/4,
+
+ RbufCtrl = 0x300/4,
+ Rbuf64En = 1 << 0,
+ RbufAlign2B = 1 << 1,
+ RbufBadDis = 1 << 2,
+
+ RbufChkCtrl = 0x314/4,
+ RbufChkRxChkEn = 1 << 0,
+ RbufChkSkipFcs = 1 << 4,
+
+ RbufOvflCnt = 0x394/4,
+ RbufErrCnt = 0x398/4,
+
+ RbufEnergyCtrl = 0x39c/4,
+ RbufEeeEn = 1 << 0,
+ RbufPmEn = 1 << 1,
+
+ RbufTbufSizeCtrl= 0x3b4/4,
+
+ TbufCtrl = 0x600/4,
+ TbufBpMc = 0x60C/4,
+ TbufEnergyCtrl = 0x614/4,
+
+ UmacCmd = 0x808/4,
+ CmdTxEn = 1 << 0,
+ CmdRxEn = 1 << 1,
+ CmdSpeed10 = 0 << 2,
+ CmdSpeed100 = 1 << 2,
+ CmdSpeed1000 = 2 << 2,
+ CmdSpeedMask = 3 << 2,
+ CmdProm = 1 << 4,
+ CmdPadEn = 1 << 5,
+ CmdCrcFwd = 1 << 6,
+ CmdPauseFwd = 1 << 7,
+ CmdRxPauseIgn = 1 << 8,
+ CmdTxAddrIn = 1 << 9,
+ CmdHdEn = 1 << 10,
+ CmdSwReset = 1 << 13,
+ CmdLclLoopEn = 1 << 15,
+ CmdAutoConfig = 1 << 22,
+ CmdCntlFrmEn = 1 << 23,
+ CmdNoLenChk = 1 << 24,
+ CmdRmtLoopEn = 1 << 25,
+ CmdPrblEn = 1 << 27,
+ CmdTxPauseIgn = 1 << 28,
+ CmdTxRxEn = 1 << 29,
+ CmdRuntFilterDis= 1 << 30,
+
+ UmacMac0 = 0x80C/4,
+ UmacMac1 = 0x810/4,
+ UmacMaxFrameLen = 0x814/4,
+
+ UmacEeeCtrl = 0x864/4,
+ UmacEeeEn = 1<<3,
+
+ UmacEeeLpiTimer = 0x868/4,
+ UmacEeeWakeTimer= 0x86C/4,
+ UmacEeeRefCount = 0x870/4,
+ EeeRefCountMask = 0xFFFF,
+
+ UmacTxFlush = 0xb34/4,
+
+ UmacMibCtrl = 0xd80/4,
+ MibResetRx = 1 << 0,
+ MibResetRunt = 1 << 1,
+ MibResetTx = 1 << 2,
+
+ MdioCmd = 0xe14/4,
+ MdioStartBusy = 1 << 29,
+ MdioReadFail = 1 << 28,
+ MdioRead = 2 << 26,
+ MdioWrite = 1 << 26,
+ MdioPhyShift = 21,
+ MdioPhyMask = 0x1F,
+ MdioAddrShift = 16,
+ MdioAddrMask = 0x1F,
+
+ UmacMpdCtrl = 0xe20/4,
+ MpdEn = 1 << 0,
+ MpdPwEn = 1 << 27,
+
+ UmacMdfCtrl = 0xe50/4,
+ UmacMdfAddr0 = 0xe54/4,
+
+ RdmaOffset = 0x2000/4,
+ TdmaOffset = 0x4000/4,
+ HfbOffset = 0x8000/4,
+
+ HfbCtlr = 0xFC00/4,
+ HfbFltEnable = 0xFC04/4,
+ HfbFltLen = 0xFC1C/4,
+
+ /* common Ring->regs */
+ RdmaWP = 0x00/4,
+ TdmaRP = 0x00/4,
+ RxWP = 0x08/4,
+ TxRP = 0x08/4,
+ TxWP = 0x0C/4,
+ RxRP = 0x0C/4,
+ DmaRingBufSize = 0x10/4,
+ DmaStart = 0x14/4,
+ DmaEnd = 0x1C/4,
+ DmaDoneThresh = 0x24/4,
+ TdmaFlowPeriod = 0x28/4,
+ RdmaXonXoffThresh=0x28/4,
+ TdmaWP = 0x2C/4,
+ RdmaRP = 0x2C/4,
+
+ /*
+ * reg offsets only for RING16
+ * ctlr->rx->regs / ctlr->tx->regs
+ */
+ RingCfg = 0x40/4,
+ RxRingCfgMask = 0x10000,
+ TxRingCfgMask = 0x1000F,
+
+ DmaCtrl = 0x44/4,
+ DmaCtrlEn = 1 << 0,
+ DmaStatus = 0x48/4,
+ DmaStatusDis = 1 << 0,
+ DmaScbBurstSize = 0x4C/4,
+
+ TdmaArbCtrl = 0x6C/4,
+ TdmaPriority0 = 0x70/4,
+ TdmaPriority1 = 0x74/4,
+ TdmaPriority2 = 0x78/4,
+
+ RdmaTimeout0 = 0x6C/4,
+ RdmaIndex2Ring0 = 0xB0/4,
+};
+
+typedef struct Desc Desc;
+typedef struct Ring Ring;
+typedef struct Ctlr Ctlr;
+
+struct Desc
+{
+ u32int *d; /* hw descriptor */
+ Block *b;
+};
+
+struct Ring
+{
+ Rendez;
+ u32int *regs;
+ u32int *intregs;
+ u32int intmask;
+
+ Desc *d;
+
+ u32int m;
+ u32int cp;
+ u32int rp;
+ u32int wp;
+
+ int num;
+};
+
+struct Ctlr
+{
+ Lock;
+ u32int *regs;
+
+ Desc rd[256];
+ Desc td[256];
+
+ Ring rx[1+0];
+ Ring tx[1+0];
+
+ Rendez avail[1];
+ Rendez link[1];
+ struct {
+ Mii;
+ Rendez;
+ } mii[1];
+
+ QLock;
+ char attached;
+};
+
+static Block *scratch;
+
+/*
+this driver causes the ethernet controller to stall the gisb bus
+which causes other devices to fail.
+*/
+#ifdef XXXDEBUG
+
+static u32int *lastgenetregaddr;
+static uintptr lastgenetregpc;
+static ulong lastgenetregtime;
+static Ctlr *xxx;
+
+#define REG(x) *(logreg(&(x)))
+
+static u32int*
+logreg(u32int *x)
+{
+ coherence();
+ lastgenetregtime = MACHP(0)->ticks;
+ lastgenetregpc = getcallerpc(&x);
+ lastgenetregaddr = x;
+ return x;
+}
+
+static void
+dumparb(void)
+{
+ static int once;
+ static u32int *regs = (u32int*)(VIRTIO + 0x9800);
+
+ if(!once){
+ once = 1;
+ regs[0x00/4] |= 1;
+ regs[0x40/4] = (regs[0x40/4] & ~0x1F) | 9 | 0x40000000;
+ }
+ iprint("arb %.8ux %.8ux %.8ux %.8ux; "
+ "%.8ux %.8ux %.8ux %.8ux; "
+ "%.8ux %.8ux %.8ux\n",
+ regs[0x40/4], regs[0x44/4], regs[0x48/4], regs[0x4C/4],
+ regs[0x50/4], regs[0x54/4], regs[0x58/4], regs[0x5C/4],
+ regs[0x60/4], regs[0x64/4], regs[0x68/4]);
+}
+
+void
+genetclock(void)
+{
+ static int ctr;
+
+ if(xxx == nil)
+ return;
+
+ if((++ctr & 0xFF) != 0)
+ return;
+ iprint("%d %#p @ %#p; "
+ "rx=(%.2ux %.2ux [%.2ux]); "
+ "tx=(%.2ux %.2ux %.2ux [%.2ux]); "
+ "(%lud)\n",
+ m->machno,
+ lastgenetregaddr, lastgenetregpc,
+ xxx->rx->rp, xxx->rx->wp, xxx->rx->wp - xxx->rx->rp,
+ xxx->tx->cp, xxx->tx->rp, xxx->tx->wp, xxx->tx->wp - xxx->tx->rp,
+ tk2ms(MACHP(0)->ticks-lastgenetregtime));
+ dumparb();
+}
+
+#else
+
+#define REG(x) (x)
+
+#endif
+
+static void
+interrupt0(Ureg*, void *arg)
+{
+ Ether *edev = arg;
+ Ctlr *ctlr = edev->ctlr;
+ u32int sts;
+
+ sts = REG(ctlr->regs[Intrl0 + IntrSts]) & ~REG(ctlr->regs[Intrl0 + IntrMaskSts]);
+ REG(ctlr->regs[Intrl0 + IntrClr]) = sts;
+ REG(ctlr->regs[Intrl0 + IntrMaskSet]) = sts;
+
+ if(sts & ctlr->rx->intmask)
+ wakeup(ctlr->rx);
+ if(sts & ctlr->tx->intmask)
+ wakeup(ctlr->tx);
+
+ if(sts & (IrqMdioDone|IrqMdioError))
+ wakeup(ctlr->mii);
+ if(sts & (IrqLinkUp|IrqLinkDown))
+ wakeup(ctlr->link);
+}
+
+static void
+interrupt1(Ureg*, void *arg)
+{
+ Ether *edev = arg;
+ Ctlr *ctlr = edev->ctlr;
+ u32int sts;
+ int i;
+
+ sts = REG(ctlr->regs[Intrl1 + IntrSts]) & ~REG(ctlr->regs[Intrl1 + IntrMaskSts]);
+ REG(ctlr->regs[Intrl1 + IntrClr]) = sts;
+ REG(ctlr->regs[Intrl1 + IntrMaskSet]) = sts;
+
+ for(i = 1; i < nelem(ctlr->rx); i++)
+ if(sts & ctlr->rx[i].intmask)
+ wakeup(&ctlr->rx[i]);
+
+ for(i = 1; i < nelem(ctlr->tx); i++)
+ if(sts & ctlr->tx[i].intmask)
+ wakeup(&ctlr->tx[i]);
+}
+
+static void
+setdma(Desc *d, void *v)
+{
+ u64int pa = PADDR(v);
+ REG(d->d[1]) = pa;
+ REG(d->d[2]) = pa >> 32;
+}
+
+static void
+replenish(Desc *d)
+{
+ d->b = allocb(Rbsz);
+ dmaflush(1, d->b->rp, Rbsz);
+ setdma(d, d->b->rp);
+}
+
+static int
+rxdone(void *arg)
+{
+ Ring *r = arg;
+
+ r->wp = REG(r->regs[RxWP]) & 0xFFFF;
+ if(r->rp != r->wp)
+ return 1;
+ REG(r->intregs[IntrMaskClr]) = r->intmask;
+ return 0;
+}
+
+static void
+recvproc(void *arg)
+{
+ Ether *edev = arg;
+ Ctlr *ctlr = edev->ctlr;
+ Desc *d;
+ Block *b;
+ u32int s;
+
+#ifdef XXXDEBUG
+ procwired(up, 1);
+ sched();
+#endif
+
+ while(waserror())
+ ;
+
+ for(;;){
+ if(ctlr->rx->rp == ctlr->rx->wp){
+ sleep(ctlr->rx, rxdone, ctlr->rx);
+ continue;
+ }
+ d = &ctlr->rx->d[ctlr->rx->rp & ctlr->rx->m];
+ b = d->b;
+ dmaflush(0, b->rp, Rbsz);
+ s = REG(d->d[0]);
+ replenish(d);
+ coherence();
+ ctlr->rx->rp = (ctlr->rx->rp + 1) & 0xFFFF;
+ REG(ctlr->rx->regs[RxRP]) = ctlr->rx->rp;
+ if((s & (DmaSOP|DmaEOP|DmaRxErrors)) != (DmaSOP|DmaEOP)){
+ freeb(b);
+ continue;
+ }
+ b->wp += (s & 0x0FFF0000) >> 16;
+ etheriq(edev, b);
+ }
+}
+
+static int
+txavail(void *arg)
+{
+ Ring *r = arg;
+
+ return ((r->wp+1) & r->m) != (r->cp & r->m);
+}
+
+static void
+sendproc(void *arg)
+{
+ Ether *edev = arg;
+ Ctlr *ctlr = edev->ctlr;
+ Desc *d;
+ Block *b;
+
+#ifdef XXXDEBUG
+ procwired(up, 1);
+ sched();
+#endif
+
+ while(waserror())
+ ;
+
+ for(;;){
+ if(!txavail(ctlr->tx)){
+ sleep(ctlr->avail, txavail, ctlr->tx);
+ continue;
+ }
+ if((b = qbread(edev->oq, 100000)) == nil)
+ break;
+ d = &ctlr->tx->d[ctlr->tx->wp & ctlr->tx->m];
+ assert(d->b == nil);
+ d->b = b;
+ dmaflush(1, b->rp, BLEN(b));
+ setdma(d, b->rp);
+ REG(d->d[0]) = BLEN(b)<<16 | DmaTxQtag | DmaSOP | DmaEOP | DmaTxAppendCrc;
+ coherence();
+ ctlr->tx->wp = (ctlr->tx->wp+1) & 0xFFFF;
+ REG(ctlr->tx->regs[TxWP]) = ctlr->tx->wp;
+ }
+}
+
+static int
+txdone(void *arg)
+{
+ Ring *r = arg;
+
+ if(r->cp != r->wp){
+ r->rp = REG(r->regs[TxRP]) & 0xFFFF;
+ if(r->cp != r->rp)
+ return 1;
+ }
+ REG(r->intregs[IntrMaskClr]) = r->intmask;
+ return 0;
+}
+
+static void
+freeproc(void *arg)
+{
+ Ether *edev = arg;
+ Ctlr *ctlr = edev->ctlr;
+ Desc *d;
+
+#ifdef XXXDEBUG
+ procwired(up, 1);
+ sched();
+#endif
+
+ while(waserror())
+ ;
+
+ for(;;){
+ if(ctlr->tx->cp == ctlr->tx->rp){
+ wakeup(ctlr->avail);
+ sleep(ctlr->tx, txdone, ctlr->tx);
+ continue;
+ }
+ d = &ctlr->tx->d[ctlr->tx->cp & ctlr->tx->m];
+ assert(d->b != nil);
+ freeb(d->b);
+ d->b = nil;
+ coherence();
+ ctlr->tx->cp = (ctlr->tx->cp+1) & 0xFFFF;
+ }
+}
+
+static void
+initring(Ring *ring, Desc *desc, int start, int size)
+{
+ ring->d = &desc[start];
+ ring->m = size - 1;
+ ring->cp = ring->rp = ring->wp = 0;
+ REG(ring->regs[RxWP]) = 0;
+ REG(ring->regs[RxRP]) = 0;
+ REG(ring->regs[DmaStart]) = start*3;
+ REG(ring->regs[DmaEnd]) = (start+size)*3 - 1;
+ REG(ring->regs[RdmaWP]) = start*3;
+ REG(ring->regs[RdmaRP]) = start*3;
+ REG(ring->regs[DmaRingBufSize]) = (size << 16) | Rbsz;
+ REG(ring->regs[DmaDoneThresh]) = 1;
+}
+
+static void
+introff(Ctlr *ctlr)
+{
+ REG(ctlr->regs[Intrl0 + IntrMaskSet]) = -1;
+ REG(ctlr->regs[Intrl0 + IntrClr]) = -1;
+ REG(ctlr->regs[Intrl1 + IntrMaskSet]) = -1;
+ REG(ctlr->regs[Intrl1 + IntrClr]) = -1;
+}
+
+static void
+dmaoff(Ctlr *ctlr)
+{
+ REG(ctlr->rx->regs[DmaCtrl]) &= ~(RxRingCfgMask<<1 | DmaCtrlEn);
+ REG(ctlr->tx->regs[DmaCtrl]) &= ~(TxRingCfgMask<<1 | DmaCtrlEn);
+
+ REG(ctlr->regs[UmacTxFlush]) = 1;
+ microdelay(10);
+ REG(ctlr->regs[UmacTxFlush]) = 0;
+
+ while((REG(ctlr->rx->regs[DmaStatus]) & DmaStatusDis) == 0)
+ microdelay(10);
+ while((REG(ctlr->tx->regs[DmaStatus]) & DmaStatusDis) == 0)
+ microdelay(10);
+}
+
+static void
+dmaon(Ctlr *ctlr)
+{
+ REG(ctlr->rx->regs[DmaCtrl]) |= DmaCtrlEn;
+ REG(ctlr->tx->regs[DmaCtrl]) |= DmaCtrlEn;
+
+ while(REG(ctlr->rx->regs[DmaStatus]) & DmaStatusDis)
+ microdelay(10);
+ while(REG(ctlr->tx->regs[DmaStatus]) & DmaStatusDis)
+ microdelay(10);
+}
+
+static void
+allocbufs(Ctlr *ctlr)
+{
+ int i;
+
+ if(scratch == nil){
+ scratch = allocb(Rbsz);
+ memset(scratch->rp, 0xFF, Rbsz);
+ dmaflush(1, scratch->rp, Rbsz);
+ }
+
+ for(i = 0; i < nelem(ctlr->rd); i++){
+ ctlr->rd[i].d = &ctlr->regs[RdmaOffset + i*3];
+ replenish(&ctlr->rd[i]);
+ }
+
+ for(i = 0; i < nelem(ctlr->td); i++){
+ ctlr->td[i].d = &ctlr->regs[TdmaOffset + i*3];
+ setdma(&ctlr->td[i], scratch->rp);
+ REG(ctlr->td[i].d[0]) = DmaTxUnderrun;
+ }
+}
+
+static void
+freebufs(Ctlr *ctlr)
+{
+ int i;
+
+ for(i = 0; i < nelem(ctlr->rd); i++){
+ if(ctlr->rd[i].b != nil){
+ freeb(ctlr->rd[i].b);
+ ctlr->rd[i].b = nil;
+ }
+ }
+ for(i = 0; i < nelem(ctlr->td); i++){
+ if(ctlr->td[i].b != nil){
+ freeb(ctlr->td[i].b);
+ ctlr->td[i].b = nil;
+ }
+ }
+}
+
+static void
+initrings(Ctlr *ctlr)
+{
+ u32int rcfg, tcfg, dmapri[3];
+ int i;
+
+ ctlr->rx->intregs = &ctlr->regs[Intrl0];
+ ctlr->rx->intmask = IrqRxDmaDone;
+ ctlr->rx->num = 16;
+ rcfg = 1<<16;
+ for(i = 1; i < nelem(ctlr->rx); i++){
+ ctlr->rx[i].regs = &ctlr->regs[RdmaOffset + nelem(ctlr->rd)*3 + (i-1)*RingCfg];
+ ctlr->rx[i].intregs = &ctlr->regs[Intrl1];
+ ctlr->rx[i].intmask = 0x10000 << (i - 1);
+ ctlr->rx[i].num = i - 1;
+ rcfg |= 1<<(i-1);
+ }
+ assert(rcfg && (rcfg & ~RxRingCfgMask) == 0);
+
+ ctlr->tx->intregs = &ctlr->regs[Intrl0];
+ ctlr->tx->intmask = IrqTxDmaDone;
+ ctlr->tx->num = 16;
+ tcfg = 1<<16;
+ for(i = 1; i < nelem(ctlr->tx); i++){
+ ctlr->tx[i].regs = &ctlr->regs[TdmaOffset + nelem(ctlr->td)*3 + (i-1)*RingCfg];
+ ctlr->tx[i].intregs = &ctlr->regs[Intrl1];
+ ctlr->tx[i].intmask = 1 << (i - 1);
+ ctlr->tx[i].num = i - 1;
+ tcfg |= 1<<(i-1);
+ }
+ assert(tcfg && (tcfg & ~TxRingCfgMask) == 0);
+
+ REG(ctlr->rx->regs[DmaScbBurstSize]) = 0x08;
+ for(i = 1; i < nelem(ctlr->rx); i++)
+ initring(&ctlr->rx[i], ctlr->rd, (i-1)*32, 32);
+ initring(ctlr->rx, ctlr->rd, (i-1)*32, nelem(ctlr->rd) - (i-1)*32);
+
+ for(i = 0; i < nelem(ctlr->rx); i++){
+ REG(ctlr->rx[i].regs[DmaDoneThresh]) = 1;
+ REG(ctlr->rx[i].regs[RdmaXonXoffThresh]) = (5 << 16) | ((ctlr->rx[i].m+1) >> 4);
+
+ // set dma timeout to 50µs
+ REG(ctlr->rx->regs[RdmaTimeout0 + ctlr->rx[i].num]) = ((50*1000 + 8191)/8192);
+ }
+
+ REG(ctlr->tx->regs[DmaScbBurstSize]) = 0x08;
+ for(i = 1; i < nelem(ctlr->tx); i++)
+ initring(&ctlr->tx[i], ctlr->td, (i-1)*32, 32);
+ initring(ctlr->tx, ctlr->td, (i-1)*32, nelem(ctlr->td) - (i-1)*32);
+
+ dmapri[0] = dmapri[1] = dmapri[2] = 0;
+ for(i = 0; i < nelem(ctlr->tx); i++){
+ REG(ctlr->tx[i].regs[DmaDoneThresh]) = 10;
+ REG(ctlr->tx[i].regs[TdmaFlowPeriod]) = i ? 0 : Maxtu << 16;
+ dmapri[ctlr->tx[i].num/6] |= i << ((ctlr->tx[i].num%6)*5);
+ }
+
+ REG(ctlr->tx->regs[TdmaArbCtrl]) = 2;
+ REG(ctlr->tx->regs[TdmaPriority0]) = dmapri[0];
+ REG(ctlr->tx->regs[TdmaPriority1]) = dmapri[1];
+ REG(ctlr->tx->regs[TdmaPriority2]) = dmapri[2];
+
+ REG(ctlr->rx->regs[RingCfg]) = rcfg;
+ REG(ctlr->tx->regs[RingCfg]) = tcfg;
+
+ REG(ctlr->rx->regs[DmaCtrl]) |= rcfg<<1;
+ REG(ctlr->tx->regs[DmaCtrl]) |= tcfg<<1;
+}
+
+static void
+umaccmd(Ctlr *ctlr, u32int set, u32int clr)
+{
+ ilock(ctlr);
+ REG(ctlr->regs[UmacCmd]) = (REG(ctlr->regs[UmacCmd]) & ~clr) | set;
+ iunlock(ctlr);
+}
+
+static void
+reset(Ctlr *ctlr)
+{
+ u32int r;
+
+ // reset umac
+ r = REG(ctlr->regs[SysRbufFlushCtrl]);
+ REG(ctlr->regs[SysRbufFlushCtrl]) = r | 2;
+ microdelay(10);
+ REG(ctlr->regs[SysRbufFlushCtrl]) = r & ~2;
+ microdelay(10);
+
+ // umac reset
+ REG(ctlr->regs[SysRbufFlushCtrl]) = 0;
+ microdelay(10);
+
+ REG(ctlr->regs[UmacCmd]) = 0;
+ REG(ctlr->regs[UmacCmd]) = CmdSwReset | CmdLclLoopEn;
+ microdelay(2);
+ REG(ctlr->regs[UmacCmd]) = 0;
+}
+
+static void
+setmac(Ctlr *ctlr, uchar *ea)
+{
+ REG(ctlr->regs[UmacMac0]) = ea[0]<<24 | ea[1]<<16 | ea[2]<<8 | ea[3];
+ REG(ctlr->regs[UmacMac1]) = ea[4]<<8 | ea[5];
+}
+
+static void
+sethfb(Ctlr *ctlr)
+{
+ int i;
+
+ REG(ctlr->regs[HfbCtlr]) = 0;
+ REG(ctlr->regs[HfbFltEnable]) = 0;
+ REG(ctlr->regs[HfbFltEnable+1]) = 0;
+
+ for(i = 0; i < 8; i++)
+ REG(ctlr->rx->regs[RdmaIndex2Ring0+i]) = 0;
+
+ for(i = 0; i < 48/4; i++)
+ REG(ctlr->regs[HfbFltLen + i]) = 0;
+
+ for(i = 0; i < 48*128; i++)
+ REG(ctlr->regs[HfbOffset + i]) = 0;
+}
+
+static int
+mdiodone(void *arg)
+{
+ Ctlr *ctlr = arg;
+ REG(ctlr->regs[Intrl0 + IntrMaskClr]) = (IrqMdioDone|IrqMdioError);
+ return (REG(ctlr->regs[MdioCmd]) & MdioStartBusy) == 0;
+}
+
+static int
+mdiowait(Ctlr *ctlr)
+{
+ REG(ctlr->regs[MdioCmd]) |= MdioStartBusy;
+ while(REG(ctlr->regs[MdioCmd]) & MdioStartBusy)
+ tsleep(ctlr->mii, mdiodone, ctlr, 10);
+ return 0;
+}
+
+static int
+mdiow(Mii* mii, int phy, int addr, int data)
+{
+ Ctlr *ctlr = mii->ctlr;
+
+ if(phy > MdioPhyMask)
+ return -1;
+ addr &= MdioAddrMask;
+ REG(ctlr->regs[MdioCmd]) = MdioWrite
+ | (phy << MdioPhyShift) | (addr << MdioAddrShift) | (data & 0xFFFF);
+ return mdiowait(ctlr);
+}
+
+static int
+mdior(Mii* mii, int phy, int addr)
+{
+ Ctlr *ctlr = mii->ctlr;
+
+ if(phy > MdioPhyMask)
+ return -1;
+ addr &= MdioAddrMask;
+ REG(ctlr->regs[MdioCmd]) = MdioRead
+ | (phy << MdioPhyShift) | (addr << MdioAddrShift);
+ if(mdiowait(ctlr) < 0)
+ return -1;
+ if(REG(ctlr->regs[MdioCmd]) & MdioReadFail)
+ return -1;
+ return REG(ctlr->regs[MdioCmd]) & 0xFFFF;
+}
+
+static int
+bcmshdr(Mii *mii, int reg)
+{
+ miimiw(mii, 0x1C, (reg & 0x1F) << 10);
+ return miimir(mii, 0x1C) & 0x3FF;
+}
+
+static int
+bcmshdw(Mii *mii, int reg, int dat)
+{
+ return miimiw(mii, 0x1C, 0x8000 | (reg & 0x1F) << 10 | (dat & 0x3FF));
+}
+
+static int
+linkevent(void *arg)
+{
+ Ctlr *ctlr = arg;
+ REG(ctlr->regs[Intrl0 + IntrMaskClr]) = IrqLinkUp|IrqLinkDown;
+ return 0;
+}
+
+static void
+linkproc(void *arg)
+{
+ Ether *edev = arg;
+ Ctlr *ctlr = edev->ctlr;
+ MiiPhy *phy;
+ int link = -1;
+
+#ifdef XXXDEBUG
+ procwired(up, 1);
+ sched();
+#endif
+
+ while(waserror())
+ ;
+
+ for(;;){
+ tsleep(ctlr->link, linkevent, ctlr, 1000);
+ miistatus(ctlr->mii);
+ phy = ctlr->mii->curphy;
+ if(phy == nil || phy->link == link)
+ continue;
+ link = phy->link;
+ if(link){
+ u32int cmd = CmdRxEn|CmdTxEn;
+ switch(phy->speed){
+ case 1000: cmd |= CmdSpeed1000; break;
+ case 100: cmd |= CmdSpeed100; break;
+ case 10: cmd |= CmdSpeed10; break;
+ }
+ if(!phy->fd)
+ cmd |= CmdHdEn;
+ if(!phy->rfc)
+ cmd |= CmdRxPauseIgn;
+ if(!phy->tfc)
+ cmd |= CmdTxPauseIgn;
+
+ REG(ctlr->regs[ExtRgmiiOobCtrl]) = (REG(ctlr->regs[ExtRgmiiOobCtrl]) & ~OobDisable) | RgmiiLink;
+ umaccmd(ctlr, cmd, CmdSpeedMask|CmdHdEn|CmdRxPauseIgn|CmdTxPauseIgn);
+
+ edev->mbps = phy->speed;
+ }
+ edev->link = link;
+ // print("#l%d: link %d speed %d\n", edev->ctlrno, edev->link, edev->mbps);
+ }
+}
+
+static void
+setmdfaddr(Ctlr *ctlr, int i, uchar *ea)
+{
+ REG(ctlr->regs[UmacMdfAddr0 + i*2 + 0]) = ea[0] << 8 | ea[1];
+ REG(ctlr->regs[UmacMdfAddr0 + i*2 + 1]) = ea[2] << 24 | ea[3] << 16 | ea[4] << 8 | ea[5];
+}
+
+static void
+rxmode(Ether *edev, int prom)
+{
+ Ctlr *ctlr = edev->ctlr;
+ Netaddr *na;
+ int i;
+
+ if(prom || edev->nmaddr > 16-2){
+ REG(ctlr->regs[UmacMdfCtrl]) = 0;
+ umaccmd(ctlr, CmdProm, 0);
+ return;
+ }
+ setmdfaddr(ctlr, 0, edev->bcast);
+ setmdfaddr(ctlr, 1, edev->ea);
+ for(i = 2, na = edev->maddr; na != nil; na = na->next, i++)
+ setmdfaddr(ctlr, i, na->addr);
+ REG(ctlr->regs[UmacMdfCtrl]) = (-0x10000 >> i) & 0x1FFFF;
+ umaccmd(ctlr, 0, CmdProm);
+}
+
+static void
+shutdown(Ether *edev)
+{
+ Ctlr *ctlr = edev->ctlr;
+
+ dmaoff(ctlr);
+ introff(ctlr);
+}
+
+static void
+attach(Ether *edev)
+{
+ Ctlr *ctlr = edev->ctlr;
+
+ eqlock(ctlr);
+ if(ctlr->attached){
+ qunlock(ctlr);
+ return;
+ }
+ if(waserror()){
+ print("#l%d: %s\n", edev->ctlrno, up->errstr);
+ shutdown(edev);
+ freebufs(ctlr);
+ qunlock(ctlr);
+ nexterror();
+ }
+
+ // statistics
+ REG(ctlr->regs[UmacMibCtrl]) = MibResetRx | MibResetTx | MibResetRunt;
+ REG(ctlr->regs[UmacMibCtrl]) = 0;
+
+ // wol
+ REG(ctlr->regs[UmacMpdCtrl]) &= ~(MpdPwEn|MpdEn);
+
+ // power
+ REG(ctlr->regs[UmacEeeCtrl]) &= ~UmacEeeEn;
+ REG(ctlr->regs[RbufEnergyCtrl]) &= ~(RbufEeeEn|RbufPmEn);
+ REG(ctlr->regs[TbufEnergyCtrl]) &= ~(RbufEeeEn|RbufPmEn);
+ REG(ctlr->regs[TbufBpMc]) = 0;
+
+ REG(ctlr->regs[UmacMaxFrameLen]) = Maxtu;
+
+ REG(ctlr->regs[RbufTbufSizeCtrl]) = 1;
+
+ REG(ctlr->regs[TbufCtrl]) &= ~(Rbuf64En);
+ REG(ctlr->regs[RbufCtrl]) &= ~(Rbuf64En|RbufAlign2B);
+ REG(ctlr->regs[RbufChkCtrl]) &= ~(RbufChkRxChkEn|RbufChkSkipFcs);
+
+ allocbufs(ctlr);
+ initrings(ctlr);
+ dmaon(ctlr);
+
+ setmac(ctlr, edev->ea);
+ sethfb(ctlr);
+ rxmode(edev, 0);
+
+ REG(ctlr->regs[SysPortCtrl]) = PortModeExtGphy;
+ REG(ctlr->regs[ExtRgmiiOobCtrl]) |= RgmiiModeEn | IdModeDis;
+
+ ctlr->mii->ctlr = ctlr;
+ ctlr->mii->mir = mdior;
+ ctlr->mii->miw = mdiow;
+ mii(ctlr->mii, ~0);
+
+ if(ctlr->mii->curphy == nil)
+ error("no phy");
+
+ print("#l%d: phy%d id %.8ux oui %x\n",
+ edev->ctlrno, ctlr->mii->curphy->phyno,
+ ctlr->mii->curphy->id, ctlr->mii->curphy->oui);
+
+ miireset(ctlr->mii);
+
+ switch(ctlr->mii->curphy->id){
+ case 0x600d84a2: /* BCM54312PE */
+ /* mask interrupts */
+ miimiw(ctlr->mii, 0x10, miimir(ctlr->mii, 0x10) | 0x1000);
+
+ /* SCR3: clear DLLAPD_DIS */
+ bcmshdw(ctlr->mii, 0x05, bcmshdr(ctlr->mii, 0x05) &~0x0002);
+ /* APD: set APD_EN */
+ bcmshdw(ctlr->mii, 0x0a, bcmshdr(ctlr->mii, 0x0a) | 0x0020);
+
+ /* blinkenlights */
+ bcmshdw(ctlr->mii, 0x09, bcmshdr(ctlr->mii, 0x09) | 0x0010);
+ bcmshdw(ctlr->mii, 0x0d, 3<<0 | 0<<4);
+ break;
+ }
+
+ /* don't advertise EEE */
+ miimmdw(ctlr->mii, 7, 60, 0);
+
+ miiane(ctlr->mii, ~0, ~0, ~0);
+
+#ifdef XXXDEBUG
+ xxx = ctlr;
+#endif
+
+ ctlr->attached = 1;
+
+ kproc("genet-recv", recvproc, edev);
+ kproc("genet-send", sendproc, edev);
+ kproc("genet-free", freeproc, edev);
+ kproc("genet-link", linkproc, edev);
+
+ qunlock(ctlr);
+ poperror();
+}
+
+static void
+prom(void *arg, int on)
+{
+ Ether *edev = arg;
+ rxmode(edev, on);
+}
+
+static void
+multi(void *arg, uchar*, int)
+{
+ Ether *edev = arg;
+ rxmode(edev, edev->prom > 0);
+}
+
+static long
+ctl(Ether *edev, void *data, long len)
+{
+ Ctlr *ctlr = edev->ctlr;
+ char *s = data;
+
+ if(len >= 4 && strncmp(s, "tron", 4) == 0){
+ umaccmd(ctlr, CmdTxEn, 0);
+ } else if(len >= 5 && strncmp(s, "troff", 5) == 0){
+ umaccmd(ctlr, 0, CmdTxEn);
+ } else if(len >= 3 && strncmp(s, "ron", 3) == 0){
+ umaccmd(ctlr, CmdRxEn, 0);
+ } else if(len >= 4 && strncmp(s, "roff", 4) == 0){
+ umaccmd(ctlr, 0, CmdRxEn);
+ }
+
+ return len;
+}
+
+static int
+pnp(Ether *edev)
+{
+ static Ctlr ctlr[1];
+
+ if(ctlr->regs != nil)
+ return -1;
+
+ ctlr->regs = (u32int*)(VIRTIO1 + 0x580000);
+ ctlr->rx->regs = &ctlr->regs[RdmaOffset + nelem(ctlr->rd)*3 + 16*RingCfg];
+ ctlr->tx->regs = &ctlr->regs[TdmaOffset + nelem(ctlr->td)*3 + 16*RingCfg];
+
+ edev->port = (uintptr)ctlr->regs;
+ edev->irq = IRQether;
+ edev->ctlr = ctlr;
+ edev->attach = attach;
+ edev->shutdown = shutdown;
+ edev->promiscuous = prom;
+ edev->multicast = multi;
+ edev->ctl = ctl;
+ edev->arg = edev;
+ edev->mbps = 1000;
+ edev->maxmtu = Maxtu;
+
+ parseether(edev->ea, getethermac());
+
+ reset(ctlr);
+ dmaoff(ctlr);
+ introff(ctlr);
+
+ intrenable(edev->irq+0, interrupt0, edev, BUSUNKNOWN, edev->name);
+ intrenable(edev->irq+1, interrupt1, edev, BUSUNKNOWN, edev->name);
+
+ return 0;
+}
+
+void
+ethergenetlink(void)
+{
+ addethercard("genet", pnp);
+}
--- a/sys/src/9/port/ethermii.c
+++ b/sys/src/9/port/ethermii.c
@@ -14,7 +14,8 @@
mii(Mii* mii, int mask)
{
MiiPhy *miiphy;
- int bit, oui, phyno, r, rmask;
+ int bit, oui, phyno, rmask;
+ u32int id;
/*
* Probe through mii for PHYs in mask;
@@ -33,10 +34,9 @@
}
if(mii->mir(mii, phyno, Bmsr) == -1)
continue;
- r = mii->mir(mii, phyno, Phyidr1);
- oui = (r & 0x3FFF)<<6;
- r = mii->mir(mii, phyno, Phyidr2);
- oui |= r>>10;
+ id = mii->mir(mii, phyno, Phyidr1) << 16;
+ id |= mii->mir(mii, phyno, Phyidr2);
+ oui = (id & 0x3FFFFC00)>>10;
if(oui == 0xFFFFF || oui == 0)
continue;
@@ -44,6 +44,7 @@
continue;
miiphy->mii = mii;
+ miiphy->id = id;
miiphy->oui = oui;
miiphy->phyno = phyno;
@@ -232,4 +233,30 @@
phy->link = 1;
return 0;
+}
+
+int
+miimmdr(Mii* mii, int a, int r)
+{
+ a &= 0x1F;
+ if(miimiw(mii, Mmdctrl, a) == -1)
+ return -1;
+ if(miimiw(mii, Mmddata, r) == -1)
+ return -1;
+ if(miimiw(mii, Mmdctrl, a | 0x4000) == -1)
+ return -1;
+ return miimir(mii, Mmddata);
+}
+
+int
+miimmdw(Mii* mii, int a, int r, int data)
+{
+ a &= 0x1F;
+ if(miimiw(mii, Mmdctrl, a) == -1)
+ return -1;
+ if(miimiw(mii, Mmddata, r) == -1)
+ return -1;
+ if(miimiw(mii, Mmdctrl, a | 0x4000) == -1)
+ return -1;
+ return miimiw(mii, Mmddata, data);
}
--- a/sys/src/9/port/ethermii.h
+++ b/sys/src/9/port/ethermii.h
@@ -13,6 +13,8 @@
Annprr = 0x08, /* AN Next Page RX */
Mscr = 0x09, /* MASTER-SLAVE Control */
Mssr = 0x0A, /* MASTER-SLAVE Status */
+ Mmdctrl = 0x0D, /* MMD Access Control */
+ Mmddata = 0x0E, /* MMD Access Data Register */
Esr = 0x0F, /* Extended Status */
NMiiPhyr = 32,
@@ -94,6 +96,7 @@
typedef struct MiiPhy {
Mii* mii;
+ u32int id;
int oui;
int phyno;
@@ -114,3 +117,6 @@
extern int miimiw(Mii*, int, int);
extern int miireset(Mii*);
extern int miistatus(Mii*);
+
+extern int miimmdr(Mii*, int, int);
+extern int miimmdw(Mii*, int, int, int);