summaryrefslogtreecommitdiff
path: root/sys/src/9/zynq/etherzynq.c
diff options
context:
space:
mode:
authoraiju <aiju@phicode.de>2014-12-24 10:21:51 +0100
committeraiju <aiju@phicode.de>2014-12-24 10:21:51 +0100
commit7a3f0998a0ce7470d70c1a13bc4646abafdcc236 (patch)
tree9cea39d933719c0b82eb242ec286c25eed0d728c /sys/src/9/zynq/etherzynq.c
parent6dafa424805d128fcd08c71dca3e3abdc40aa6c6 (diff)
added zynq kernel
Diffstat (limited to 'sys/src/9/zynq/etherzynq.c')
-rw-r--r--sys/src/9/zynq/etherzynq.c380
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);
+}