diff --git a/sys/src/9/bcm64/ethergenet.c b/sys/src/9/bcm64/ethergenet.c new file mode 100644 index 000000000..e7b9e61d3 --- /dev/null +++ b/sys/src/9/bcm64/ethergenet.c @@ -0,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); +} diff --git a/sys/src/9/port/ethermii.c b/sys/src/9/port/ethermii.c index 16d766ef4..d75c35072 100644 --- a/sys/src/9/port/ethermii.c +++ b/sys/src/9/port/ethermii.c @@ -14,7 +14,8 @@ int 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 @@ mii(Mii* mii, int mask) } 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 @@ mii(Mii* mii, int mask) continue; miiphy->mii = mii; + miiphy->id = id; miiphy->oui = oui; miiphy->phyno = phyno; @@ -233,3 +234,29 @@ miistatus(Mii* mii) 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); +} diff --git a/sys/src/9/port/ethermii.h b/sys/src/9/port/ethermii.h index 02a45ee5f..4fea978b8 100644 --- a/sys/src/9/port/ethermii.h +++ b/sys/src/9/port/ethermii.h @@ -13,6 +13,8 @@ enum { /* registers */ 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 Mii { typedef struct MiiPhy { Mii* mii; + u32int id; int oui; int phyno; @@ -114,3 +117,6 @@ extern int miimir(Mii*, int); 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);