diff options
author | aiju <aiju@phicode.de> | 2014-12-24 10:21:51 +0100 |
---|---|---|
committer | aiju <aiju@phicode.de> | 2014-12-24 10:21:51 +0100 |
commit | 7a3f0998a0ce7470d70c1a13bc4646abafdcc236 (patch) | |
tree | 9cea39d933719c0b82eb242ec286c25eed0d728c /sys/src/9/zynq/etherzynq.c | |
parent | 6dafa424805d128fcd08c71dca3e3abdc40aa6c6 (diff) |
added zynq kernel
Diffstat (limited to 'sys/src/9/zynq/etherzynq.c')
-rw-r--r-- | sys/src/9/zynq/etherzynq.c | 380 |
1 files changed, 380 insertions, 0 deletions
diff --git a/sys/src/9/zynq/etherzynq.c b/sys/src/9/zynq/etherzynq.c new file mode 100644 index 000000000..775d219f4 --- /dev/null +++ b/sys/src/9/zynq/etherzynq.c @@ -0,0 +1,380 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/netif.h" +#include "etherif.h" + +#define Rbsz ROUNDUP(sizeof(Etherpkt)+16, 64) + +enum { + RXRING = 0x200, + TXRING = 0x200, + Linkdelay = 500, + MDC_DIV = 6, +}; + +enum { + NET_CTRL, + NET_CFG, + NET_STATUS, + DMA_CFG = 4, + TX_STATUS, + RX_QBAR, + TX_QBAR, + RX_STATUS, + INTR_STATUS, + INTR_EN, + INTR_DIS, + INTR_MASK, + PHY_MAINT, + RX_PAUSEQ, + TX_PAUSEQ, + HASH_BOT = 32, + HASH_TOP, + SPEC_ADDR1_BOT, + SPEC_ADDR1_TOP, +}; + +enum { + MDCTRL, + MDSTATUS, + MDID1, + MDID2, + MDAUTOADV, + MDAUTOPART, + MDAUTOEX, + MDAUTONEXT, + MDAUTOLINK, + MDGCTRL, + MDGSTATUS, + MDPHYCTRL = 0x1f, +}; + +enum { + /* NET_CTRL */ + RXEN = 1<<2, + TXEN = 1<<3, + MDEN = 1<<4, + STARTTX = 1<<9, + /* NET_CFG */ + SPEED = 1<<0, + FDEN = 1<<1, + RX1536EN = 1<<8, + GIGE_EN = 1<<10, + RXCHKSUMEN = 1<<24, + /* NET_STATUS */ + PHY_IDLE = 1<<2, + /* DMA_CFG */ + TXCHKSUMEN = 1<<11, + /* TX_STATUS */ + TXCOMPL = 1<<5, + /* INTR_{EN,DIS} */ + MGMTDONE = 1<<0, + RXCOMPL = 1<<1, + RXUSED = 1<<2, + TXUNDER = 1<<4, + RXOVER = 1<<10, + /* MDCTRL */ + MDRESET = 1<<15, + AUTONEG = 1<<12, + FULLDUP = 1<<8, + /* MDSTATUS */ + LINK = 1<<2, + /* MDGSTATUS */ + RECVOK = 3<<12, +}; + +enum { + RxUsed = 1, + TxUsed = 1<<31, + FrameEnd = 1<<15, +}; + +typedef struct Ctlr Ctlr; + +struct Ctlr { + ulong *r; + Rendez phy; + int phyaddr; + int rxconsi, rxprodi, txi; + ulong *rxr, *txr; + Block **rxs, **txs; + Lock txlock; + int attach; +}; + +static int +phyidle(void *v) +{ + return ((Ctlr*)v)->r[NET_STATUS] & PHY_IDLE; +} + +static void +mdwrite(Ctlr *c, int r, u16int v) +{ + sleep(&c->phy, phyidle, c); + c->r[PHY_MAINT] = 1<<30 | 1<<28 | 1<<17 | c->phyaddr << 23 | r << 18 | v; + sleep(&c->phy, phyidle, c); +} + +static u16int +mdread(Ctlr *c, int r) +{ + sleep(&c->phy, phyidle, c); + c->r[PHY_MAINT] = 1<<30 | 1<< 29 | 1<<17 | c->phyaddr << 23 | r << 18; + sleep(&c->phy, phyidle, c); + return c->r[PHY_MAINT]; +} + +static void +ethproc(void *ved) +{ + Ether *edev; + Ctlr *c; + char *sp, *dpl; + u16int v; + + edev = ved; + c = edev->ctlr; + mdwrite(c, MDCTRL, AUTONEG); + for(;;){ + if((mdread(c, MDSTATUS) & LINK) == 0){ + edev->link = 0; + print("eth: no link\n"); + while((mdread(c, MDSTATUS) & LINK) == 0) + tsleep(&up->sleep, return0, nil, Linkdelay); + } + v = mdread(c, MDPHYCTRL); + if((v & 0x40) != 0){ + sp = "1000BASE-T"; + while((mdread(c, MDGSTATUS) & RECVOK) != RECVOK) + ; + edev->mbps = 1000; + c->r[NET_CFG] |= GIGE_EN; + }else if((v & 0x20) != 0){ + sp = "100BASE-TX"; + edev->mbps = 100; + c->r[NET_CFG] = NET_CFG & ~GIGE_EN | SPEED; + }else if((v & 0x10) != 0){ + sp = "10BASE-T"; + edev->mbps = 10; + c->r[NET_CFG] = NET_CFG & ~(GIGE_EN | SPEED); + }else + sp = "???"; + if((v & 0x08) != 0){ + dpl = "full"; + c->r[NET_CFG] |= FDEN; + }else{ + dpl = "half"; + c->r[NET_CFG] &= ~FDEN; + } + edev->link = 1; + print("eth: %s %s duplex link\n", sp, dpl); + while((mdread(c, MDSTATUS) & LINK) != 0) + tsleep(&up->sleep, return0, nil, Linkdelay); + } +} + +static int +replenish(Ctlr *c) +{ + Block *bp; + int i; + ulong *r; + + while(c->rxprodi != c->rxconsi){ + i = c->rxprodi; + bp = iallocb(Rbsz); + if(bp == nil){ + print("eth: out of memory for receive buffers\n"); + return -1; + } + c->rxs[i] = bp; + r = &c->rxr[2 * i]; + r[0] = RxUsed | PADDR(bp->rp); + if(i == RXRING - 1) + r[0] |= 2; + r[1] = 0; + cleandse(bp->base, bp->lim); + clean2pa(PADDR(bp->base), PADDR(bp->lim)); + r[0] &= ~RxUsed; + c->rxprodi = (c->rxprodi + 1) & (RXRING - 1); + } + return 0; +} + +static void +ethrx(Ether *edev) +{ + Ctlr *c; + ulong *r; + Block *bp; + + c = edev->ctlr; +// print("rx! %p %p\n", PADDR(&c->rxr[2 * c->rxconsi]), c->r[RX_QBAR]); + for(;;){ + r = &c->rxr[2 * c->rxconsi]; + if((r[0] & RxUsed) == 0) + break; + if((r[1] & FrameEnd) == 0) + print("eth: partial frame received -- shouldn't happen\n"); + bp = c->rxs[c->rxconsi]; + bp->wp = bp->rp + (r[1] & 0x1fff); + invaldse(bp->rp, bp->wp); + inval2pa(PADDR(bp->rp), PADDR(bp->wp)); + etheriq(edev, bp, 1); + c->rxconsi = (c->rxconsi + 1) & (RXRING - 1); + replenish(c); + } +} + +static void +ethtx(Ether *edev) +{ + Ctlr *c; + ulong *r; + Block *bp; + + c = edev->ctlr; + ilock(&c->txlock); + for(;;){ + r = &c->txr[2 * c->txi]; + if((r[1] & TxUsed) == 0){ + print("eth: transmit buffer full\n"); + break; + } + bp = qget(edev->oq); + if(bp == nil) + break; + if(c->txs[c->txi] != nil) + freeb(c->txs[c->txi]); + c->txs[c->txi] = bp; + cleandse(bp->rp, bp->wp); + clean2pa(PADDR(bp->rp), PADDR(bp->wp)); + r[0] = PADDR(bp->rp); + r[1] = BLEN(bp) | FrameEnd | TxUsed; + if(r == c->txr + 2 * (TXRING - 1)) + r[1] |= 1<<30; + coherence(); + r[1] &= ~TxUsed; + coherence(); + c->r[NET_CTRL] |= STARTTX; + c->txi = (c->txi + 1) & (TXRING - 1); + } + iunlock(&c->txlock); +} + +static void +ethirq(Ureg *, void *arg) +{ + Ether *edev; + Ctlr *c; + ulong fl; + + edev = arg; + c = edev->ctlr; + fl = c->r[INTR_STATUS]; + c->r[INTR_STATUS] = fl; + if((fl & MGMTDONE) != 0) + wakeup(&c->phy); + if((fl & TXUNDER) != 0) + ethtx(edev); + if((fl & RXCOMPL) != 0) + ethrx(edev); + if((fl & RXUSED) != 0) + print("eth: DMA read RX descriptor with used bit set, shouldn't happen\n"); + if((fl & RXOVER) != 0) + print("eth: RX overrun, shouldn't happen\n"); +} + +static int +ethinit(Ether *edev) +{ + Ctlr *c; + int i; + uintptr rxrpa, txrpa; + + c = edev->ctlr; + c->r[NET_CTRL] = 0; + c->r[RX_STATUS] = 0xf; + c->r[TX_STATUS] = 0xff; + c->r[INTR_DIS] = 0x7FFFEFF; + c->r[NET_CFG] = MDC_DIV << 18 | FDEN | SPEED | RX1536EN | GIGE_EN | RXCHKSUMEN; + c->r[SPEC_ADDR1_BOT] = edev->ea[0] | edev->ea[1] << 8 | edev->ea[2] << 16 | edev->ea[3] << 24; + c->r[SPEC_ADDR1_TOP] = edev->ea[4] | edev->ea[5] << 8; + c->r[DMA_CFG] = TXCHKSUMEN | (Rbsz/64) << 16 | 1 << 10 | 3 << 8 | 0x10; + + rxrpa = ualloc(8 * RXRING, &c->rxr); + txrpa = ualloc(8 * TXRING, &c->txr); + c->rxs = xspanalloc(4 * RXRING, 4, 0); + c->txs = xspanalloc(4 * TXRING, 4, 0); + for(i = 0; i < 2 * RXRING; ){ + c->rxr[i++] = 1; + c->rxr[i++] = 0; + } + c->rxconsi = 1; + replenish(c); + c->rxconsi = 0; + replenish(c); + for(i = 0; i < 2 * TXRING; ){ + c->txr[i++] = 0; + c->txr[i++] = 1<<31; + } + c->txr[2 * (TXRING - 1)] |= 1<<30; + c->r[RX_QBAR] = rxrpa; + c->r[TX_QBAR] = txrpa; + + c->r[NET_CTRL] = MDEN | TXEN | RXEN; + c->r[INTR_EN] = MGMTDONE | TXUNDER | RXCOMPL | RXUSED | RXOVER; + return 0; +} + +static void +ethattach(Ether *edev) +{ + Ctlr *c; + + c = edev->ctlr; + if(c->attach) + return; + c->attach = 1; + kproc("ethproc", ethproc, edev); +} + +static int +etherpnp(Ether *edev) +{ + static Ctlr ct; + static uchar mac[] = {0x0e, 0xa7, 0xde, 0xad, 0xbe, 0xef}; + + if(ct.r != nil) + return -1; + + memmove(edev->ea, mac, 6); + edev->ctlr = &ct; + edev->port = ETH0_BASE; + ct.r = vmap(edev->port, BY2PG); + edev->irq = ETH0IRQ; + edev->irqlevel = LEVEL; + edev->ctlr = &ct; + edev->interrupt = ethirq; + edev->transmit = ethtx; + edev->attach = ethattach; + edev->arg = edev; + edev->mbps = 1000; + + if(ethinit(edev) < 0){ + edev->ctlr = nil; + return -1; + } + return 0; +} + +void +etherzynqlink(void) +{ + addethercard("eth", etherpnp); +} |