shithub: riscv

ref: bc0e5ffa2293526ed3cf65a025263d2599c1f331
dir: /sys/src/cmd/nusb/ether/smsc.c/

View raw version
/*
 * SMSC LAN95XX
 */

#include <u.h>
#include <libc.h>
#include <thread.h>
#include <bio.h>
#include <auth.h>
#include <fcall.h>
#include <9p.h>
#include <ip.h>

#include "usb.h"
#include "dat.h"

enum {
	Doburst		= 1,
	Resettime	= 1000,
	E2pbusytime	= 1000,
	Afcdefault	= 0xF830A1,
//	Hsburst		= 37,	/* from original linux driver */
	Hsburst		= 8,
	Fsburst		= 129,
	Defbulkdly	= 0x2000,

	Ethp8021q	= 0x8100,
	MACoffset 	= 1,
	PHYinternal	= 1,
	Rxerror		= 0x8000,
	Txfirst		= 0x2000,
	Txlast		= 0x1000,

	/* USB vendor requests */
	Writereg	= 0xA0,
	Readreg		= 0xA1,

	/* device registers */
	Intsts		= 0x08,
	Txcfg		= 0x10,
		Txon	= 1<<2,
	Hwcfg		= 0x14,
		Bir	= 1<<12,
		Rxdoff	= 3<<9,
		Mef	= 1<<5,
		Lrst	= 1<<3,
		Bce	= 1<<1,
	Pmctrl		= 0x20,
		Phyrst	= 1<<4,
	Ledgpio		= 0x24,
		Ledspd	= 1<<24,
		Ledlnk	= 1<<20,
		Ledfdx	= 1<<16,
	Afccfg		= 0x2C,
	E2pcmd		= 0x30,
		Busy	= 1<<31,
		Timeout	= 1<<10,
		Read	= 0,
	E2pdata		= 0x34,
	Burstcap	= 0x38,
	Intepctl	= 0x68,
		Phyint	= 1<<15,
	Bulkdelay	= 0x6C,
	Maccr		= 0x100,
		Mcpas	= 1<<19,
		Prms	= 1<<18,
		Hpfilt	= 1<<13,
		Txen	= 1<<3,
		Rxen	= 1<<2,
	Addrh		= 0x104,
	Addrl		= 0x108,
	Hashh		= 0x10C,
	Hashl		= 0x110,
	MIIaddr		= 0x114,
		MIIwrite= 1<<1,
		MIIread	= 0<<1,
		MIIbusy	= 1<<0,
	MIIdata		= 0x118,
	Flow		= 0x11C,
	Vlan1		= 0x120,
	Coecr		= 0x130,
		Txcoe	= 1<<16,
		Rxcoemd	= 1<<1,
		Rxcoe	= 1<<0,

	/* MII registers */
	Bmcr		= 0,
		Bmcrreset= 1<<15,
		Speed100= 1<<13,
		Anenable= 1<<12,
		Anrestart= 1<<9,
		Fulldpx	= 1<<8,
	Bmsr		= 1,
	Advertise	= 4,
		Adcsma	= 0x0001,
		Ad10h	= 0x0020,
		Ad10f	= 0x0040,
		Ad100h	= 0x0080,
		Ad100f	= 0x0100,
		Adpause	= 0x0400,
		Adpauseasym= 0x0800,
		Adall	= Ad10h|Ad10f|Ad100h|Ad100f,
	Phyintsrc	= 29,
	Phyintmask	= 30,
		Anegcomp= 1<<6,
		Linkdown= 1<<4,
};

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)
		fprint(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);
	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);
	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, Advertise, Adcsma|Ad10f|Ad10h|Adpause|Adpauseasym);
	miird(d, Phyintsrc);
	miiwr(d, Phyintmask, Anegcomp|Linkdown);
	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
smscread(Dev *ep, uchar *p, int plen)
{
	int n, m;
	uint hd;

	if(nbin < 4)
		nbin = read(ep->dfd, bin, Doburst ? Hsburst*512: sizeof(bin));
	if(nbin < 0)
		return -1;
	if(nbin < 4)
		return 0;
	hd = GET4(bin);
	n = hd >> 16;
	m = (n + 4 + 3) & ~3;
	if(n < 6 || m > nbin){
		nbin = 0;
		return 0;
	}
	if(hd & Rxerror){
		fprint(2, "smsc rx error %8.8ux\n", hd);
		n = 0;
	}else{
		if(n > plen)
			n = plen;
		if(n > 0)
			memmove(p, bin+4, n);
	}
	if(m < nbin)
		memmove(bin, bin+m, nbin - m);
	nbin -= m;
	return n;
}

static void
smscwrite(Dev *ep, uchar *p, int n)
{
	if(n > sizeof(bout)-8)
		n = sizeof(bout)-8;
	PUT4(bout, n | Txfirst | Txlast);
	PUT4(bout+4, n);
	memmove(bout+8, p, n);
	write(ep->dfd, bout, n+8);
}

int
smscinit(Dev *d)
{
	if(!doreset(d, Hwcfg, Lrst) || !doreset(d, Pmctrl, Phyrst))
		return -1;
	if(!setmac)
		if(eepromr(d, MACoffset, macaddr, 6) < 0)
			return -1;
	wr(d, Addrl, GET4(macaddr));
	wr(d, Addrh, GET2(macaddr+4));
	if(Doburst){
		wr(d, Hwcfg, (rr(d,Hwcfg)&~Rxdoff)|Bir|Mef|Bce);
		wr(d, Burstcap, Hsburst);
	}else{
		wr(d, Hwcfg, (rr(d,Hwcfg)&~(Rxdoff|Mef|Bce))|Bir);
		wr(d, Burstcap, 0);
	}
	wr(d, Bulkdelay, Defbulkdly);
	wr(d, Intsts, ~0);
	wr(d, Ledgpio, Ledspd|Ledlnk|Ledfdx);
	wr(d, Flow, 0);
	wr(d, Afccfg, Afcdefault);
	wr(d, Vlan1, Ethp8021q);
	wr(d, Coecr, rr(d,Coecr)&~(Txcoe|Rxcoe)); /* TODO could offload checksums? */

	wr(d, Hashh, 0);
	wr(d, Hashl, 0);
	wr(d, Maccr, rr(d,Maccr)&~(Prms|Mcpas|Hpfilt));

	phyinit(d);

	wr(d, Intepctl, rr(d, Intepctl)|Phyint);
	wr(d, Maccr, rr(d, Maccr)|Txen|Rxen);
	wr(d, Txcfg, Txon);

	epwrite = smscwrite;
	epread = smscread;
	return 0;
}