shithub: riscv

Download patch

ref: d64f76c96c5ecfedf6c2a3fcf4b5ce6fa53df714
parent: 17ebe55031ae6945ad1f671b69267a672328e4b1
author: aiju <devnull@localhost>
date: Fri Jan 10 13:49:33 EST 2020

add cycv ethernet driver

--- a/sys/src/9/cycv/cycv
+++ b/sys/src/9/cycv/cycv
@@ -24,6 +24,7 @@
 	segment
 	
 link
+	ethercycv
 	ethermedium
 	loopbackmedium
 	netdevmedium
--- a/sys/src/9/cycv/dat.h
+++ b/sys/src/9/cycv/dat.h
@@ -200,3 +200,5 @@
 };
 
 #define mpcore ((ulong*)MPCORE_BASE)
+#define resetmgr ((ulong*)RESETMGR_BASE)
+#define sysmgr ((ulong*)SYSMGR_BASE)
--- /dev/null
+++ b/sys/src/9/cycv/ethercycv.c
@@ -1,0 +1,438 @@
+#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"
+
+#define Rbsz ROUNDUP(sizeof(Etherpkt)+16, 64)
+
+enum {
+	Linkdelay = 500,
+	RXRING = 512,
+	TXRING = 512
+};
+
+enum {
+	PERMODRST_EMAC1 = 1<<1,
+	
+	SYSMGR_EMAC_CTRL = 0x60/4,
+	
+	MAC_CONFIG = 0x0/4,
+	MAC_FRAME_FILTER = 0x4/4,
+	
+	GMII_ADDRESS = 0x10/4,
+	GMII_DATA = 0x14/4,
+	
+	INTERRUPT_STATUS = 0x38/4,
+	INTERRUPT_MASK = 0x3C/4,
+	
+	MAC_ADDRESS = 0x40/4,
+	HASH_TABLE = 0x500/4,
+	
+	DMA_BUS_MODE = 0x1000/4,
+	DMA_BUS_MODE_SWR = 1<<0,
+	DMA_TX_POLL = 0x1004/4,
+	DMA_RX_POLL = 0x1008/4,
+	DMA_STATUS = 0x1014/4,
+	DMA_OPERATION_MODE = 0x1018/4,
+	DMA_INTERRUPT_ENABLE = 0x101C/4,
+	DMA_AXI_STATUS = 0x102C/4,
+	
+	RXRING_ADDRESS = 0x100C/4,
+	TXRING_ADDRESS = 0x1010/4,
+
+};
+
+enum {
+	MDCTRL,
+	MDSTATUS,
+	MDID1,
+	MDID2,
+	MDAUTOADV,
+	MDAUTOPART,
+	MDAUTOEX,
+	MDAUTONEXT,
+	MDAUTOLINK,
+	MDGCTRL,
+	MDGSTATUS,
+	MDPHYCTRL = 0x1f,
+
+	/* MDCTRL */
+	MDRESET = 1<<15,
+	AUTONEG = 1<<12,
+	FULLDUP = 1<<8,
+	/* MDSTATUS */
+	LINK = 1<<2,
+	/* MDGSTATUS */
+	RECVOK = 3<<12,
+};
+
+
+typedef struct Ctlr Ctlr;
+
+struct Ctlr {
+	ulong *r;
+	ulong *rxr, *txr;
+	Block **rxs, **txs;
+	int rxprodi, rxconsi, txi;
+	int attach;
+	Lock txlock;
+	uchar (*mc)[6];
+	int nmc;
+};
+
+static void
+mdwrite(Ctlr *c, int r, u16int v)
+{
+	while((c->r[GMII_ADDRESS] & 1<<0) != 0);
+	c->r[GMII_DATA] = v;
+	c->r[GMII_ADDRESS] = 1<<11 | (r&31)<<6 | 1<<1 | 1<<0;
+	while((c->r[GMII_ADDRESS] & 1<<0) != 0);
+}
+
+static u16int
+mdread(Ctlr *c, int r)
+{
+	while((c->r[GMII_ADDRESS] & 1<<0) != 0);
+	c->r[GMII_ADDRESS] = 1<<11 | (r&31)<<6 | 1<<0;
+	while((c->r[GMII_ADDRESS] & 1<<0) != 0);
+	return c->r[GMII_DATA];
+}
+
+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[MAC_CONFIG] &= ~(1<<15);
+
+		}else if((v & 0x20) != 0){
+			sp = "100BASE-TX";
+			edev->mbps = 100;
+			c->r[MAC_CONFIG] = c->r[MAC_CONFIG] | (1<<15|1<<14);
+		}else if((v & 0x10) != 0){
+			sp = "10BASE-T";
+			edev->mbps = 10;
+			c->r[MAC_CONFIG] = c->r[MAC_CONFIG] & ~(1<<14) | 1<<15;
+		}else
+			sp = "???";
+		if((v & 0x08) != 0){
+			dpl = "full";
+			c->r[MAC_CONFIG] |= 1<<11;
+		}else{
+			dpl = "half";
+			c->r[MAC_CONFIG] &= ~(1<<11);
+		}
+		edev->link = 1;
+		print("eth: %s %s duplex link\n", sp, dpl);
+		c->r[MAC_CONFIG] |= 1<<3 | 1<<2;
+		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[4 * i];
+		r[0] = 0;
+		cleandse(bp->base, bp->lim);
+		r[1] = Rbsz;
+		if(i == RXRING - 1) r[1] |= 1<<15;
+		r[2] = PADDR(bp->rp);
+		r[3] = 0;
+		r[0] |= 1<<31;
+		c->rxprodi = (c->rxprodi + 1) & (RXRING - 1);
+	}
+	c->r[DMA_RX_POLL] = 0xBA5EBA11;
+	return 0;
+}
+
+static void
+ethrx(Ether *edev)
+{
+	Ctlr *c;
+	ulong *r;
+	Block *bp;
+	
+	c = edev->ctlr;
+	for(;;){
+		r = &c->rxr[4 * c->rxconsi];
+		if((r[0] >> 31) != 0)
+			break;
+		if((r[0] & 1<<15) != 0)
+			iprint("eth: error frame\n");
+		if((r[0] & (3<<8)) != (3<<8))
+			iprint("eth: lilu dallas multidescriptor\n");
+		bp = c->rxs[c->rxconsi];
+		bp->wp = bp->rp + (r[0] >> 16 & 0x3fff);
+		invaldse(bp->rp, bp->wp);
+		etheriq(edev, bp);
+		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[4 * c->txi];
+		if((r[0] >> 31) != 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);
+		r[0] = 1<<30 | 1<<29 | 1<<28;
+		if(c->txi == TXRING - 1)
+			r[0] |= 1<<21;
+		r[1] = BLEN(bp);
+		r[2] = PADDR(bp->rp);
+		r[3] = 0;
+		r[0] |= 1<<31;
+		coherence();
+		c->r[DMA_TX_POLL] = 0xBA5EBA11;
+		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[DMA_STATUS];
+	c->r[DMA_STATUS] = fl;
+	if((fl & 1<<1) != 0)
+		iprint("eth: transmit stop\n");
+	if((fl & (1<<0|1<<2)) != 0)
+		ethtx(edev);
+	if((fl & 1<<4) != 0)
+		iprint("eth: receive overflow\n");
+	if((fl & (1<<6|1<<7)) != 0)
+		ethrx(edev);
+	if((fl & 1<<8) != 0)
+		iprint("eth: receive stop\n");
+}
+
+static int
+ethinit(Ether *edev)
+{
+	Ctlr *c;
+	
+	c = edev->ctlr;
+	resetmgr[PERMODRST] |= PERMODRST_EMAC1;
+	/* assume bootloader set up clock */
+	sysmgr[SYSMGR_EMAC_CTRL] = 1<<2 | 1; /* RGMII */
+	sysmgr[FPGA_MODULE] = sysmgr[FPGA_MODULE] & ~(1<<2 | 1<<3);
+	microdelay(1);
+	resetmgr[PERMODRST] &= ~PERMODRST_EMAC1;
+	
+	/* reset DMA */
+	c->r[DMA_BUS_MODE] |= DMA_BUS_MODE_SWR;
+	do microdelay(1);
+	while((c->r[DMA_BUS_MODE] & DMA_BUS_MODE_SWR) != 0);
+	/* wait for AXI transactions to finish */
+	while((c->r[DMA_AXI_STATUS] & 3) != 0)
+		microdelay(1);
+	/* set up bus mode (32 beat bursts) */
+	c->r[DMA_BUS_MODE] |= 32 << 8;
+	
+	c->r[MAC_ADDRESS] = 1<<31 | edev->ea[5] << 8 | edev->ea[4];
+	c->r[MAC_ADDRESS+1] = edev->ea[3] << 24 | edev->ea[2] << 16 | edev->ea[1] << 8 | edev->ea[0];
+	c->r[MAC_FRAME_FILTER] = 0;
+	
+	c->rxr = ucalloc(16 * RXRING);
+	c->txr = ucalloc(16 * TXRING);
+	c->rxs = xspanalloc(4 * RXRING, 4, 0);
+	c->txs = xspanalloc(4 * TXRING, 4, 0);
+	memset(c->rxr, 0, 16 * RXRING);
+	memset(c->txr, 0, 16 * TXRING);
+	c->rxconsi = 1;
+	replenish(c);
+	c->rxconsi = 0;
+	replenish(c);
+	
+	c->r[RXRING_ADDRESS] = PADDR(c->rxr);
+	c->r[TXRING_ADDRESS] = PADDR(c->txr);
+	c->r[DMA_STATUS] = -1;
+	c->r[INTERRUPT_MASK] = -1;
+	c->r[DMA_INTERRUPT_ENABLE] = 1<<16 | 1<<15 | 1<<8 | 1<<6 | 1<<2 | 1<<1 | 1<<0;
+	c->r[DMA_OPERATION_MODE] = 1<<1 | 1<<13;
+	return 0;
+}
+
+static void
+ethattach(Ether *edev)
+{
+	Ctlr *c;
+
+	c = edev->ctlr;
+	if(c->attach)
+		return;
+	c->attach = 1;
+	kproc("ethproc", ethproc, edev);
+}
+
+static void
+ethprom(void *arg, int on)
+{
+	Ether *edev;
+	Ctlr *c;
+	
+	edev = arg;
+	c = edev->ctlr;
+	if(on)
+		c->r[MAC_FRAME_FILTER] |=  0x80000001;
+	else
+		c->r[MAC_FRAME_FILTER] &= ~0x80000001;
+}
+
+static void
+sethash(uchar *ea, ulong *hash)
+{
+	ulong crc;
+	int i;
+	uchar n;
+	
+	crc = ethercrc(ea, 6);
+	n = 0;
+	for(i = 0; i < 8; i++){
+		n = n << 1 | crc & 1;
+		crc >>= 1;
+	}
+	n ^= 0xff;
+	hash[n>>5] |= (1<<(n & 31));
+}
+
+static void
+ethmcast(void *arg, uchar *ea, int on)
+{
+	enum { MCSlots = 31 };
+	Ether *edev;
+	Ctlr *c;
+	int i, p;
+	ulong hash[8];
+	
+	edev = arg;
+	c = edev->ctlr;
+	if(on){
+		c->mc = realloc(c->mc, (c->nmc + 1) * 6);
+		memmove(c->mc[c->nmc++], ea, 6);
+	}else{
+		for(i = 0; i < c->nmc; i++)
+			if(memcmp(c->mc[i], ea, 6) == 0)
+				break;
+		if(i == c->nmc)
+			return;
+		memmove(c->mc[i], c->mc[i+1], (c->nmc - i - 1) * 6);
+	}
+	p = c->r[MAC_FRAME_FILTER];
+	/* set promiscuous in order to not lose packets while updating */
+	c->r[MAC_FRAME_FILTER] = p | 0x80000001;
+	if(c->nmc <= MCSlots){
+		for(i = 0; i < c->nmc; i++){
+			c->r[MAC_ADDRESS + 2 * (i + 1)] = 1<<31 | c->mc[i][5] << 8 | c->mc[i][4];
+			c->r[MAC_ADDRESS + 2 * (i + 1) + 1] = c->mc[i][3] << 24 | c->mc[i][2] << 16 | c->mc[i][1] << 8 | c->mc[i][0];
+		}
+		for(i = 2 * i; i < 2*MCSlots; i++)
+			c->r[MAC_ADDRESS + 2 + i] = 0;
+		c->r[MAC_FRAME_FILTER] = p & ~(1<<2);
+	}else{
+		memset(hash, 0, sizeof(hash));
+		for(i = 0; i < c->nmc; i++)
+			sethash(c->mc[i], hash);
+		for(i = 0; i < 8; i++)
+			c->r[HASH_TABLE + i] = hash[i];
+		c->r[MAC_FRAME_FILTER] = p | 1<<2;
+	}
+}
+
+static int
+etherpnp(Ether *edev)
+{
+	static Ctlr ct;
+	static uchar mac[] = {0x0e, 0xa7, 0xde, 0xad, 0xca, 0xfe};
+	
+	if(ct.r != nil)
+		return -1;
+	
+	memmove(edev->ea, mac, 6);
+	edev->ctlr = &ct;
+	edev->port = EMAC1_BASE;
+	ct.r = (ulong *) edev->port;
+	edev->irq = EMAC1IRQ;
+	edev->ctlr = &ct;
+	edev->attach = ethattach;
+	edev->transmit = ethtx;
+	edev->arg = edev;
+	edev->mbps = 1000;
+	edev->promiscuous = ethprom;
+	edev->multicast = ethmcast;
+
+	if(ethinit(edev) < 0){
+		edev->ctlr = nil;
+		return -1;
+	}
+	
+	intrenable(edev->irq, ethirq, edev, LEVEL, edev->name);
+	return 0;
+}
+
+void
+ethercycvlink(void)
+{
+	addethercard("eth", etherpnp);
+}
--- a/sys/src/9/cycv/io.h
+++ b/sys/src/9/cycv/io.h
@@ -2,11 +2,21 @@
 #define MPCORE_BASE 0xFFFEC000
 #define L2_BASE 0xFFFEF000
 #define CLOCKMGR_BASE 0xFFD04000
+#define EMAC1_BASE 0xFF702000
+#define RESETMGR_BASE 0xFFD05000
+#define SYSMGR_BASE 0xFFD08000
+#define OCRAM 0xFFFF0000
 
+/*RESETMGR*/
+#define PERMODRST (0x14/4)
+/*SYSMGR*/
+#define FPGA_MODULE (0x28/4)
+
 #define HPS_CLK 25
 
 #define TIMERIRQ 29
 #define UART0IRQ 194
+#define EMAC1IRQ 152
 
 #define LEVEL 0
 #define EDGE 1
--- a/sys/src/9/cycv/l.s
+++ b/sys/src/9/cycv/l.s
@@ -7,6 +7,12 @@
 	MOVW $(KTZERO-KZERO), R13
 	MOVW $(UART_BASE), R8
 	
+	/* disable watchdog */
+	MOVW $(RESETMGR_BASE + 4*PERMODRST), R1
+	MOVW (R1), R0
+	ORR $(3<<6), R0
+	MOVW R0, (R1)
+	
 	/* disable L2 cache */
 	MOVW $0, R0
 	MOVW $(L2_BASE+0x100), R1
@@ -24,10 +30,10 @@
 	ISB
 	PUTC('l')
 	
-	/* clean up to KTZERO */
+	/* clean up to CONFADDR */
 	MOVW $0, R0
 	MOVW R0, R1
-	MOVW $(KTZERO-KZERO), R2
+	MOVW $(CONFADDR-KZERO), R2
 _clrstart:
 	MOVW.P R0, 4(R1)
 	CMP.S R1, R2
--- a/sys/src/9/cycv/mmu.c
+++ b/sys/src/9/cycv/mmu.c
@@ -374,3 +374,26 @@
 	coherence();
 	flushpg(v);
 }
+
+void *
+ucalloc(ulong len)
+{
+	static Lock l;
+	static uchar *free = nil;
+	uchar *va;
+	
+	if(len == 0)
+		panic("ucalloc: len == 0");
+	ilock(&l);
+	if(free == nil)
+		free = (uchar*)-BY2PG;
+	len = PGROUND(len);
+	free -= len;
+	if(free < (uchar*)OCRAM)
+		panic("ucalloc: out of uncached memory");
+	va = free;
+	iunlock(&l);
+	
+	invaldse(va, va + len);
+	return (void *) va;
+}