Plan 9 from Bell Labs’s /usr/web/sources/contrib/yk/dist/9legacy/applied/usb-ether-lan78xx.diff

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


Add support for Microchip (ex SMSC) LAN78xx usb ethernet, as used in
LAN7515 chip on Raspberry Pi 3B+.  Also tweak performance a bit
for SMSC ethernet as used in earlier Raspberry Pi models.

Note: this patch incorporates all changes from usbether-rpi and usb-ether-cdc
and therefore supersedes those two patches.  It may be necessary to revert
them before applying this patch.

Reference: /n/sources/patch/usb-ether-lan78xx
Date: Thu Apr  5 21:37:20 GMT 2018
Signed-off-by: [email protected]

--- /sys/src/cmd/usb/ether/ether.h	Thu Apr  5 09:52:59 2018
+++ /sys/src/cmd/usb/ether/ether.h	Thu Apr  5 09:52:57 2018
@@ -14,6 +14,7 @@
 	A88179,
 	A88772,
 	S95xx,		/* SMSC */
+	S78xx,
 
 	Eaddrlen = 6,
 	Epktlen = 1514,
@@ -114,6 +115,7 @@
 int	ethermain(Dev *dev, int argc, char **argv);
 int	asixreset(Ether*);
 int smscreset(Ether*);
+int lan78xxreset(Ether*);
 int	cdcreset(Ether*);
 int	parseaddr(uchar *m, char *s);
 void	dumpframe(char *tag, void *p, int n);
--- /sys/src/cmd/usb/ether/ether.c	Thu Apr  5 09:53:05 2018
+++ /sys/src/cmd/usb/ether/ether.c	Thu Apr  5 09:53:01 2018
@@ -82,6 +82,9 @@
 	{0x0424, 0x9505, S95xx},
 	{0x0424, 0x9E00, S95xx},
 	{0x0424, 0x9E01, S95xx},
+	/* LAN78xx family - gigabit ethernet
+	 */
+	{0x0424, 0x7800, S78xx},	/* raspberry pi 3 B+ */
 	{0, 0, 0},
 };
 
@@ -121,6 +124,7 @@
 {
 	asixreset,
 	smscreset,
+	lan78xxreset,
 	cdcreset,	/* keep last */
 };
 
--- /sys/src/cmd/usb/ether/mkfile	Thu Apr  5 09:53:08 2018
+++ /sys/src/cmd/usb/ether/mkfile	Thu Apr  5 09:53:06 2018
@@ -8,6 +8,7 @@
 	ether.$O\
 	asix.$O\
 	smsc.$O\
+	lan78xx.$O\
 	cdc.$O\
 
 HFILES=\
--- /sys/src/cmd/usb/ether/lan78xx.c	Thu Jan  1 00:00:00 1970
+++ /sys/src/cmd/usb/ether/lan78xx.c	Thu Apr  5 09:53:10 2018
@@ -0,0 +1,452 @@
+/*
+ * Microchip (ex SMSC) LAN78XX
+ *	 Also used as ethernet core in LAN7515 usb hub + ethernet
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include "usb.h"
+#include "usbfs.h"
+#include "ether.h"
+
+enum {
+	Doburst		= 1,
+	Resettime	= 1000,
+	E2pbusytime	= 1000,
+	Hsburst		= 32,
+	Defbulkdly	= 1000,
+	Rxfifosize	= (12*1024),
+	Txfifosize	= (12*1024),
+
+	MACoffset 	= 1,
+	PHYinternal	= 1,
+	Rxerror		= 0x00400000,
+	Txfcs		= 1<<22,
+
+	/* USB vendor requests */
+	Writereg	= 0xA0,
+	Readreg		= 0xA1,
+
+	/* device registers */
+	Idrev		= 0x00,
+	Intsts		= 0x0C,
+	Hwcfg		= 0x10,
+		Led0en	= 1<<20,
+		Led1en	= 1<<21,
+		Mef	= 1<<4,
+		Lrst	= 1<<1,
+	Pmctrl		= 0x14,
+		Ready	= 1<<7,
+		Phyrst	= 1<<4,
+	Gpiocfg0	= 0x18,
+	Gpiocfg1	= 0x1C,
+	E2pcmd		= 0x40,
+		Busy	= 1<<31,
+		Timeout	= 1<<10,
+		Loaded	= 1<<9,
+		Read	= 0,
+	E2pdata		= 0x44,
+	Burstcap	= 0x90,
+	Intepctl	= 0x98,
+		Phyint	= 1<<17,
+	Bulkdelay	= 0x94,
+	Rfectl		= 0xB0,
+		Rxcoe	= 0xF<<11,
+		Ab		= 1<<10,
+		Am		= 1<<9,
+		Au		= 1<<8,
+		Dpf		= 1<<1,
+	Usbcfg0		= 0x80,
+		Bir	= 1<<6,
+		Bce	= 1<<5,
+	Usbcfg1		= 0x84,
+	Rxfifoctl		= 0xC0,
+		Rxen	= 1<<31,	
+	Txfifoctl		= 0xC4,
+		Txen	= 1<<31,
+	Rxfifo		= 0xC8,
+	Txfifo		= 0xCc,
+	Fctflow		= 0xD0,
+	Maccr		= 0x100,
+		Add		= 1<<12,
+		Asd		= 1<<11,
+	Macrx		= 0x104,
+		Macfcs	= 1<<4,
+		Macrxen	= 1<<0,
+	Mactx		= 0x108,
+		Mactxen	= 1<<0,
+	Addrh		= 0x118,
+	Addrl		= 0x11C,
+	MIIaddr		= 0x120,
+		MIIwrite= 1<<1,
+		MIIread	= 0<<1,
+		MIIbusy	= 1<<0,
+	MIIdata		= 0x124,
+	Flow		= 0x10C,
+	Addrfilth	= 0x400,
+		Afvalid	= 1<<31,
+	Addrfiltl	= 0x404,
+
+	/* MII registers */
+	Bmcr		= 0,
+		Bmcrreset= 1<<15,
+		Speed100= 1<<13,
+		Anenable= 1<<12,
+		Anrestart= 1<<9,
+		Fulldpx	= 1<<8,
+		Speed1000= 1<<6,
+	Bmsr		= 1,
+	Advertise	= 4,
+		Adcsma	= 0x0001,
+		Ad10h	= 0x0020,
+		Ad10f	= 0x0040,
+		Ad100h	= 0x0080,
+		Ad100f	= 0x0100,
+		Adpause	= 0x0400,
+		Adpauseasym= 0x0800,
+		Adall	= Ad10h|Ad10f|Ad100h|Ad100f,
+	Lpa		= 5,
+	Ctrl1000	= 9,
+		Ad1000h = 0x0400,
+		Ad1000f = 0x0200,
+	Ledmodes	= 29,
+		Led0shift = 0,
+		Led1shift = 4,
+		Linkact = 0x0,
+		Link1000 = 0x1,
+	Phyintmask	= 25,
+		Anegcomp= 1<<10,
+		Linkchg = 1<<13,
+};
+
+static int burstcap = Hsburst, bulkdelay = Defbulkdly;
+
+static int
+wr(Dev *d, int reg, int val)
+{
+	int ret;
+
+	ret = usbcmd(d, Rh2d|Rvendor|Rdev, Writereg, 0, reg,
+		(uchar*)&val, sizeof(val));
+	if(ret < 0)
+		deprint(2, "%s: wr(%x, %x): %r", argv0, reg, val);
+	return ret;
+}
+
+static int
+rr(Dev *d, int reg)
+{
+	int ret, rval;
+
+	ret = usbcmd(d, Rd2h|Rvendor|Rdev, Readreg, 0, reg,
+		(uchar*)&rval, sizeof(rval));
+	if(ret < 0){
+		fprint(2, "%s: rr(%x): %r", argv0, reg);
+		return 0;
+	}
+	return rval;
+}
+
+static int
+miird(Dev *d, int idx)
+{
+	while(rr(d, MIIaddr) & MIIbusy)
+		;
+	wr(d, MIIaddr, PHYinternal<<11 | idx<<6 | MIIread | MIIbusy);
+	while(rr(d, MIIaddr) & MIIbusy)
+		;
+	return rr(d, MIIdata);
+}
+
+static void
+miiwr(Dev *d, int idx, int val)
+{
+	while(rr(d, MIIaddr) & MIIbusy)
+		;
+	wr(d, MIIdata, val);
+	wr(d, MIIaddr, PHYinternal<<11 | idx<<6 | MIIwrite | MIIbusy);
+	while(rr(d, MIIaddr) & MIIbusy)
+		;
+}
+
+static int
+eepromr(Dev *d, int off, uchar *buf, int len)
+{
+	int i, v;
+
+	for(i = 0; i < E2pbusytime; i++)
+		if((rr(d, E2pcmd) & Busy) == 0)
+			break;
+	if(i == E2pbusytime)
+		return -1;
+	for(i = 0; i < len; i++){
+		wr(d, E2pcmd, Busy|Read|(i+off));
+		while((v = rr(d, E2pcmd) & (Busy|Timeout)) == Busy)
+			;
+		if(v & Timeout)
+			return -1;
+		buf[i] = rr(d, E2pdata);
+	}
+	return 0;
+}
+
+static void
+phyinit(Dev *d)
+{
+	int i;
+
+	miiwr(d, Bmcr, Bmcrreset|Anenable);
+	for(i = 0; i < Resettime/10; i++){
+		if((miird(d, Bmcr) & Bmcrreset) == 0)
+			break;
+		sleep(10);
+	}
+	miiwr(d, Advertise, Adcsma|Adall|Adpause|Adpauseasym);
+	miiwr(d, Ctrl1000, Ad1000f);
+	miiwr(d, Phyintmask, 0);
+	miiwr(d, Ledmodes, (Linkact<<Led1shift) | (Link1000<<Led0shift));
+	miiwr(d, Bmcr, miird(d, Bmcr)|Anenable|Anrestart);
+}
+
+
+static int
+doreset(Dev *d, int reg, int bit)
+{
+	int i;
+
+	if(wr(d, reg, bit) < 0)
+		return -1;
+	for(i = 0; i < Resettime/10; i++){
+		 if((rr(d, reg) & bit) == 0)
+			return 1;
+		sleep(10);
+	}
+	return 0;
+}
+
+static int
+getmac(Dev *d, uchar buf[])
+{
+	int i;
+	uchar ea[Eaddrlen];
+
+	if(eepromr(d, MACoffset, ea, Eaddrlen) < 0)
+		return -1;
+	for(i = 0; i < Eaddrlen; i++)
+		if(ea[i] != 0 && ea[i] != 0xFF){
+			memmove(buf, ea, Eaddrlen);
+			break;
+		}
+	return Eaddrlen;
+}
+
+static int
+lan78xxinit(Ether *ether)
+{
+	Dev *d;
+	u32int a;
+	int i;
+
+	if(ether->cid != S78xx)
+		return -1;
+	d = ether->dev;
+	deprint(2, "%s: setting up LAN78XX\n", argv0);
+	deprint(2, "chip id/rev = %8.8ux\n", rr(d, Idrev));
+	if(!doreset(d, Hwcfg, Lrst) || !doreset(d, Pmctrl, Phyrst))
+		return -1;
+	for(i = 0; i < Resettime/10; i++){
+		 if(rr(d, Pmctrl) & Ready)
+			break;
+		sleep(10);
+	}
+	if((rr(d, Pmctrl) & Ready) == 0){
+		deprint(2, "%s: device not ready after reset\n", argv0);
+		return -1;
+	}
+	if(getmac(d, ether->addr) < 0)
+		deprint(2, "%s: can't read etheraddr from EEPROM\n", argv0);
+	a = GET4(ether->addr);
+	wr(d, Addrl, a);
+	wr(d, Addrfiltl, a);
+	a = GET2(ether->addr+4);
+	wr(d, Addrh, a);
+	wr(d, Addrfilth, a|Afvalid);
+	deprint(2, "Address filter %8.8ux %8.8ux\n", rr(d, Addrfilth), rr(d, Addrfiltl));
+
+	wr(d, Usbcfg0, rr(d, Usbcfg0) | Bir);
+	if(Doburst){
+		wr(d, Hwcfg, rr(d, Hwcfg)|Mef);
+		wr(d, Usbcfg0, rr(d, Usbcfg0)|Bce);
+		wr(d, Burstcap, burstcap);
+		wr(d, Bulkdelay, bulkdelay);
+	}else{
+		wr(d, Hwcfg, rr(d, Hwcfg)&~Mef);
+		wr(d, Usbcfg0, rr(d, Usbcfg0)&~Bce);
+		wr(d, Burstcap, 0);
+		wr(d, Bulkdelay, 0);
+	}
+	wr(d, Rxfifo, (Rxfifosize-512)/512);
+	wr(d, Txfifo, (Txfifosize-512)/512);
+	wr(d, Intsts, ~0);
+	wr(d, Hwcfg, rr(d, Hwcfg) | Led0en|Led1en);
+	wr(d, Flow, 0);
+	wr(d, Fctflow, 0);
+	wr(d, Rfectl, (rr(d, Rfectl) & ~Rxcoe) | Ab|Dpf); /* TODO could offload checksums? */
+
+	phyinit(d);
+
+	wr(d, Maccr, rr(d,Maccr)|Add|Asd);
+
+	wr(d, Intepctl, rr(d, Intepctl)|Phyint);
+	wr(d, Mactx, Mactxen);
+	wr(d, Macrx, rr(d, Macrx) | Macfcs|Macrxen);
+	wr(d, Txfifoctl, Txen);
+	wr(d, Rxfifoctl, Rxen);
+
+	return 0;
+}
+
+static long
+lan78xxbread(Ether *e, Buf *bp)
+{
+	uint hd;
+	int n, m;
+	Buf *rbp;
+
+	rbp = e->aux;
+	if(rbp->ndata < 10){
+		rbp->rp = rbp->data;
+		rbp->ndata = read(e->epin->dfd, rbp->rp, Doburst? burstcap*512:
+			Maxpkt);
+		if(rbp->ndata < 0)
+			return -1;
+	}
+	if(rbp->ndata < 10){
+		werrstr("short frame");
+		fprint(2, "lan78xx short frame %d bytes\n", rbp->ndata);
+		sleep(1000);
+		return 0;
+	}
+	hd = GET4(rbp->rp);
+	n = hd & 0x3FFF;
+	rbp->rp += 10;
+	rbp->ndata -= 10;
+	if(n < 6 || n > rbp->ndata){
+		werrstr("frame length");
+		fprint(2, "lan78xx length error packet %d buf %d\n", n, rbp->ndata);
+		rbp->ndata = 0;
+		return 0;
+	}
+	if(hd & Rxerror){
+		fprint(2, "lan78xx rx error %8.8ux\n", hd);
+		n = 0;
+	}else{
+		bp->rp = bp->data + Hdrsize;
+		memmove(bp->rp, rbp->rp, n);
+	}
+	bp->ndata = n;
+	rbp->rp += n;
+	rbp->ndata -= n;
+	if(rbp->ndata > 0){
+		m = rbp->rp - rbp->data;
+		if(m&3){
+			m = 4 - (m&3);
+			rbp->rp += m;
+			rbp->ndata -= m;
+		}
+	}
+	return n;
+}
+
+static long
+lan78xxbwrite(Ether *e, Buf *bp)
+{
+	int n;
+
+	n = bp->ndata & 0xFFFFF;
+	bp->rp -= 8;
+	bp->ndata += 8;
+	PUT4(bp->rp, n | Txfcs);
+	PUT4(bp->rp+4, 0);
+	n = write(e->epout->dfd, bp->rp, bp->ndata);
+	if(n != bp->ndata)
+		deprint(2, "bwrite %d: %r\n", n);
+	return n;
+}
+
+static int
+lan78xxpromiscuous(Ether *e, int on)
+{
+	Dev *d;
+	int rxctl;
+
+	d = e->dev;
+	rxctl = rr(d, Rfectl);
+	if(on)
+		rxctl |= Am|Au;
+	else
+		rxctl &= ~(Am|Au);
+	return wr(d, Rfectl, rxctl);
+}
+
+static int
+lan78xxmulticast(Ether *e, uchar *addr, int on)
+{
+	int rxctl;
+	Dev *d;
+
+	USED(addr, on);
+	/* BUG: should write multicast filter */
+	d = e->dev;
+	rxctl = rr(d, Rfectl);
+	if(e->nmcasts != 0)
+		rxctl |= Am;
+	else
+		rxctl &= ~Am;
+	deprint(2, "%s: lan78xxmulticast %d\n", argv0, e->nmcasts);
+	return wr(d, Rfectl, rxctl);
+}
+
+static void
+lan78xxfree(Ether *ether)
+{
+	free(ether->aux);
+	ether->aux = nil;
+}
+
+int
+lan78xxreset(Ether *ether)
+{
+	Cinfo *ip;
+	Dev *dev;
+
+	dev = ether->dev;
+	for(ip = cinfo; ip->vid != 0; ip++)
+		if(ip->vid == dev->usb->vid && ip->did == dev->usb->did){
+			ether->cid = ip->cid;
+			if(lan78xxinit(ether) < 0){
+				deprint(2, "%s: lan78xx init failed: %r\n", argv0);
+				return -1;
+			}
+			deprint(2, "%s: lan78xx reset done\n", argv0);
+			ether->name = "lan78xx";
+			if(Doburst){
+				ether->bufsize = burstcap*512;
+				ether->aux = emallocz(sizeof(Buf) +
+					ether->bufsize - Maxpkt, 1);
+			}else{
+				ether->bufsize = Maxpkt;
+				ether->aux = emallocz(sizeof(Buf), 1);
+			}
+			ether->free = lan78xxfree;
+			ether->bread = lan78xxbread;
+			ether->bwrite = lan78xxbwrite;
+			ether->promiscuous = lan78xxpromiscuous;
+			ether->multicast = lan78xxmulticast;
+			ether->mbps = 100;	/* BUG */
+			return 0;
+		}
+	return -1;
+}
--- /sys/src/cmd/usb/ether/smsc.c	Thu Apr  5 09:53:13 2018
+++ /sys/src/cmd/usb/ether/smsc.c	Thu Apr  5 09:53:11 2018
@@ -15,10 +15,9 @@
 	Resettime	= 1000,
 	E2pbusytime	= 1000,
 	Afcdefault	= 0xF830A1,
-//	Hsburst		= 37,	/* from original linux driver */
-	Hsburst		= 8,
+	Hsburst		= 24,
 	Fsburst		= 129,
-	Defbulkdly	= 0x2000,
+	Defbulkdly	= 1000,
 
 	Ethp8021q	= 0x8100,
 	MACoffset 	= 1,
@@ -102,6 +101,8 @@
 		Linkdown= 1<<4,
 };
 
+static int burstcap = Hsburst, bulkdelay = Defbulkdly;
+
 static int
 wr(Dev *d, int reg, int val)
 {
@@ -238,12 +239,13 @@
 	wr(d, Addrh, GET2(ether->addr+4));
 	if(Doburst){
 		wr(d, Hwcfg, (rr(d,Hwcfg)&~Rxdoff)|Bir|Mef|Bce);
-		wr(d, Burstcap, Hsburst);
+		wr(d, Burstcap, burstcap);
+		wr(d, Bulkdelay, bulkdelay);
 	}else{
 		wr(d, Hwcfg, (rr(d,Hwcfg)&~(Rxdoff|Mef|Bce))|Bir);
 		wr(d, Burstcap, 0);
+		wr(d, Bulkdelay, 0);
 	}
-	wr(d, Bulkdelay, Defbulkdly);
 	wr(d, Intsts, ~0);
 	wr(d, Ledgpio, Ledspd|Ledlnk|Ledfdx);
 	wr(d, Flow, 0);
@@ -274,7 +276,7 @@
 	rbp = e->aux;
 	if(rbp->ndata < 4){
 		rbp->rp = rbp->data;
-		rbp->ndata = read(e->epin->dfd, rbp->rp, Doburst? Hsburst*512:
+		rbp->ndata = read(e->epin->dfd, rbp->rp, Doburst? burstcap*512:
 			Maxpkt);
 		if(rbp->ndata < 0)
 			return -1;
@@ -285,20 +287,24 @@
 		return 0;
 	}
 	hd = GET4(rbp->rp);
+	rbp->rp += 4;
+	rbp->ndata -= 4;
 	n = hd >> 16;
-	m = (n + 4 + 3) & ~3;
-	if(n < 6 || m > rbp->ndata){
+	if(n < 6 || n > rbp->ndata){
 		werrstr("frame length");
 		fprint(2, "smsc length error packet %d buf %d\n", n, rbp->ndata);
 		rbp->ndata = 0;
 		return 0;
 	}
+	m = n;
+	if(rbp->ndata - m < 4)
+		m = rbp->ndata;
 	if(hd & Rxerror){
 		fprint(2, "smsc rx error %8.8ux\n", hd);
 		n = 0;
 	}else{
 		bp->rp = bp->data + Hdrsize;
-		memmove(bp->rp, rbp->rp+4, n);
+		memmove(bp->rp, rbp->rp, n);
 	}
 	bp->ndata = n;
 	rbp->rp += m;
@@ -381,7 +383,7 @@
 			deprint(2, "%s: smsc reset done\n", argv0);
 			ether->name = "smsc";
 			if(Doburst){
-				ether->bufsize = Hsburst*512;
+				ether->bufsize = burstcap*512;
 				ether->aux = emallocz(sizeof(Buf) +
 					ether->bufsize - Maxpkt, 1);
 			}else{
--- /sys/src/cmd/usb/usbd/usbdb	Thu Apr  5 09:53:16 2018
+++ /sys/src/cmd/usb/usbd/usbdb	Thu Apr  5 09:53:15 2018
@@ -5,6 +5,7 @@
 	disk	class=storage			args=
 	ether	class=255 csp=0x00ffff vid=0x0b95		args=
 	ether	class=255 csp=0xff00ff vid=0x0424 did=0xec00	args=
+	ether	class=255 csp=0xff00ff vid=0x0424 did=0x7800 	args=
	ether	class=255 csp=0x000602 vid=0x0bda did=0x8152	args=
 	serial	class=255 csp=0xffffff vid=0x9e88 did=0x9e8f	args=
 	serial	class=255 csp=0xffffff vid=0x0403		args=

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].