1269 lines · 28.0 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * Realtek RTL8110S/8169S Gigabit Ethernet Controllers. * Mostly there. There are some magic register values used * which are not described in any datasheet or driver but seem * to be necessary. * No tuning has been done. Only tested on an RTL8110S, there * are slight differences between the chips in the series so some * tweaks may be needed. */#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "io.h"#include "../port/error.h"#include "../port/netif.h" #include "etherif.h"#include "ethermii.h" enum {					/* registers */	Idr0		= 0x00,		/* MAC address */	Mar0		= 0x08,		/* Multicast address */	Dtccr		= 0x10,		/* Dump Tally Counter Command */	Tnpds		= 0x20,		/* Transmit Normal Priority Descriptors */	Thpds		= 0x28,		/* Transmit High Priority Descriptors */	Flash		= 0x30,		/* Flash Memory Read/Write */	Erbcr		= 0x34,		/* Early Receive Byte Count */	Ersr		= 0x36,		/* Early Receive Status */	Cr		= 0x37,		/* Command Register */	Tppoll		= 0x38,		/* Transmit Priority Polling */	Imr		= 0x3C,		/* Interrupt Mask */	Isr		= 0x3E,		/* Interrupt Status */	Tcr		= 0x40,		/* Transmit Configuration */	Rcr		= 0x44,		/* Receive Configuration */	Tctr		= 0x48,		/* Timer Count */	Mpc		= 0x4C,		/* Missed Packet Counter */	Cr9346		= 0x50,		/* 9346 Command Register */	Config0		= 0x51,		/* Configuration Register 0 */	Config1		= 0x52,		/* Configuration Register 1 */	Config2		= 0x53,		/* Configuration Register 2 */	Config3		= 0x54,		/* Configuration Register 3 */	Config4		= 0x55,		/* Configuration Register 4 */	Config5		= 0x56,		/* Configuration Register 5 */	Timerint	= 0x58,		/* Timer Interrupt */	Mulint		= 0x5C,		/* Multiple Interrupt Select */	Phyar		= 0x60,		/* PHY Access */	Tbicsr0		= 0x64,		/* TBI Control and Status */	Tbianar		= 0x68,		/* TBI Auto-Negotiation Advertisment */	Tbilpar		= 0x6A,		/* TBI Auto-Negotiation Link Partner */	Phystatus	= 0x6C,		/* PHY Status */ 	Rms		= 0xDA,		/* Receive Packet Maximum Size */	Cplusc		= 0xE0,		/* C+ Command */	Coal		= 0xE2,		/* Interrupt Mitigation (Coalesce) */	Rdsar		= 0xE4,		/* Receive Descriptor Start Address */	Etx		= 0xEC,		/* Early Transmit Threshold */}; enum {					/* Dtccr */	Cmd		= 0x00000008,	/* Command */}; enum {					/* Cr */	Te		= 0x04,		/* Transmitter Enable */	Re		= 0x08,		/* Receiver Enable */	Rst		= 0x10,		/* Software Reset */}; enum {					/* Tppoll */	Fswint		= 0x01,		/* Forced Software Interrupt */	Npq		= 0x40,		/* Normal Priority Queue polling */	Hpq		= 0x80,		/* High Priority Queue polling */}; enum {					/* Imr/Isr */	Rok		= 0x0001,	/* Receive OK */	Rer		= 0x0002,	/* Receive Error */	Tok		= 0x0004,	/* Transmit OK */	Ter		= 0x0008,	/* Transmit Error */	Rdu		= 0x0010,	/* Receive Descriptor Unavailable */	Punlc		= 0x0020,	/* Packet Underrun or Link Change */	Fovw		= 0x0040,	/* Receive FIFO Overflow */	Tdu		= 0x0080,	/* Transmit Descriptor Unavailable */	Swint		= 0x0100,	/* Software Interrupt */	Timeout		= 0x4000,	/* Timer */	Serr		= 0x8000,	/* System Error */}; enum {					/* Tcr */	MtxdmaSHIFT	= 8,		/* Max. DMA Burst Size */	MtxdmaMASK	= 0x00000700,	Mtxdmaunlimited	= 0x00000700,	Acrc		= 0x00010000,	/* Append CRC (not) */	Lbk0		= 0x00020000,	/* Loopback Test 0 */	Lbk1		= 0x00040000,	/* Loopback Test 1 */	Ifg2		= 0x00080000,	/* Interframe Gap 2 */	HwveridSHIFT	= 23,		/* Hardware Version ID */	HwveridMASK	= 0x7C800000,	Macv01		= 0x00000000,	/* RTL8169 */	Macv02		= 0x00800000,	/* RTL8169S/8110S */	Macv03		= 0x04000000,	/* RTL8169S/8110S */	Macv04		= 0x10000000,	/* RTL8169SB/8110SB */	Macv05		= 0x18000000,	/* RTL8169SC/8110SC */	Macv07		= 0x24800000,	/* RTL8102e */	Macv07a		= 0x34800000,	/* RTL8102e */	Macv11		= 0x30000000,	/* RTL8168B/8111B */	Macv12		= 0x38000000,	/* RTL8169B/8111B */	Macv12a		= 0x3c000000,	/* RTL8169C/8111C */	Macv13		= 0x34000000,	/* RTL8101E */	Macv14		= 0x30800000,	/* RTL8100E */	Macv15		= 0x38800000,	/* RTL8100E *///	Macv19		= 0x3c000000,	/* dup Macv12a: RTL8111c-gr */	Macv25		= 0x28000000,	/* RTL8168D */	Macv2c		= 0x2c000000,	/* RTL8168E */	Macv34		= 0x2c800000,	/* RTL8168E */	Ifg0		= 0x01000000,	/* Interframe Gap 0 */	Ifg1		= 0x02000000,	/* Interframe Gap 1 */}; enum {					/* Rcr */	Aap		= 0x00000001,	/* Accept All Packets */	Apm		= 0x00000002,	/* Accept Physical Match */	Am		= 0x00000004,	/* Accept Multicast */	Ab		= 0x00000008,	/* Accept Broadcast */	Ar		= 0x00000010,	/* Accept Runt */	Aer		= 0x00000020,	/* Accept Error */	Sel9356		= 0x00000040,	/* 9356 EEPROM used */	MrxdmaSHIFT	= 8,		/* Max. DMA Burst Size */	MrxdmaMASK	= 0x00000700,	Mrxdmaunlimited	= 0x00000700,	RxfthSHIFT	= 13,		/* Receive Buffer Length */	RxfthMASK	= 0x0000E000,	Rxfth256	= 0x00008000,	Rxfthnone	= 0x0000E000,	Rer8		= 0x00010000,	/* Accept Error Packets > 8 bytes */	MulERINT	= 0x01000000,	/* Multiple Early Interrupt Select */}; enum {					/* Cr9346 */	Eedo		= 0x01,		/* */	Eedi		= 0x02,		/* */	Eesk		= 0x04,		/* */	Eecs		= 0x08,		/* */	Eem0		= 0x40,		/* Operating Mode */	Eem1		= 0x80,}; enum {					/* Phyar */	DataMASK	= 0x0000FFFF,	/* 16-bit GMII/MII Register Data */	DataSHIFT	= 0,	RegaddrMASK	= 0x001F0000,	/* 5-bit GMII/MII Register Address */	RegaddrSHIFT	= 16,	Flag		= 0x80000000,	/* */}; enum {					/* Phystatus */	Fd		= 0x01,		/* Full Duplex */	Linksts		= 0x02,		/* Link Status */	Speed10		= 0x04,		/* */	Speed100	= 0x08,		/* */	Speed1000	= 0x10,		/* */	Rxflow		= 0x20,		/* */	Txflow		= 0x40,		/* */	Entbi		= 0x80,		/* */}; enum {					/* Cplusc */	Mulrw		= 0x0008,	/* PCI Multiple R/W Enable */	Dac		= 0x0010,	/* PCI Dual Address Cycle Enable */	Rxchksum	= 0x0020,	/* Receive Checksum Offload Enable */	Rxvlan		= 0x0040,	/* Receive VLAN De-tagging Enable */	Endian		= 0x0200,	/* Endian Mode */}; typedef struct D D;			/* Transmit/Receive Descriptor */struct D {	u32int	control;	u32int	vlan;	u32int	addrlo;	u32int	addrhi;}; enum {					/* Transmit Descriptor control */	TxflMASK	= 0x0000FFFF,	/* Transmit Frame Length */	TxflSHIFT	= 0,	Tcps		= 0x00010000,	/* TCP Checksum Offload */	Udpcs		= 0x00020000,	/* UDP Checksum Offload */	Ipcs		= 0x00040000,	/* IP Checksum Offload */	Lgsen		= 0x08000000,	/* TSO; WARNING: contains lark's vomit */}; enum {					/* Receive Descriptor control */	RxflMASK	= 0x00001FFF,	/* Receive Frame Length */	Tcpf		= 0x00004000,	/* TCP Checksum Failure */	Udpf		= 0x00008000,	/* UDP Checksum Failure */	Ipf		= 0x00010000,	/* IP Checksum Failure */	Pid0		= 0x00020000,	/* Protocol ID0 */	Pid1		= 0x00040000,	/* Protocol ID1 */	Crce		= 0x00080000,	/* CRC Error */	Runt		= 0x00100000,	/* Runt Packet */	Res		= 0x00200000,	/* Receive Error Summary */	Rwt		= 0x00400000,	/* Receive Watchdog Timer Expired */	Fovf		= 0x00800000,	/* FIFO Overflow */	Bovf		= 0x01000000,	/* Buffer Overflow */	Bar		= 0x02000000,	/* Broadcast Address Received */	Pam		= 0x04000000,	/* Physical Address Matched */	Mar		= 0x08000000,	/* Multicast Address Received */}; enum {					/* General Descriptor control */	Ls		= 0x10000000,	/* Last Segment Descriptor */	Fs		= 0x20000000,	/* First Segment Descriptor */	Eor		= 0x40000000,	/* End of Descriptor Ring */	Own		= 0x80000000,	/* Ownership */}; /* */enum {					/* Ring sizes  (<= 1024) */	Ntd		= 64,		/* Transmit Ring */	Nrd		= 1024,		/* Receive Ring */ 	Mtu		= ETHERMAXTU,	Mps		= ROUNDUP(ETHERMAXTU+4, 128),//	Mps		= Mtu + 8 + 14,	/* if(mtu>ETHERMAXTU) */}; typedef struct Dtcc Dtcc;struct Dtcc {	u64int	txok;	u64int	rxok;	u64int	txer;	u32int	rxer;	u16int	misspkt;	u16int	fae;	u32int	tx1col;	u32int	txmcol;	u64int	rxokph;	u64int	rxokbrd;	u32int	rxokmu;	u16int	txabt;	u16int	txundrn;}; enum {						/* Variants */	Rtl8100e	= (0x8136<<16)|0x10EC,	/* RTL810[01]E: pci -e */	Rtl8169c	= (0x0116<<16)|0x16EC,	/* RTL8169C+ (USR997902) */	Rtl8169sc	= (0x8167<<16)|0x10EC,	/* RTL8169SC */	Rtl8168b	= (0x8168<<16)|0x10EC,	/* RTL8168B: pci-e */	Rtl8169		= (0x8169<<16)|0x10EC,	/* RTL8169 */}; typedef struct Ctlr Ctlr;typedef struct Ctlr {	int	port;	Pcidev*	pcidev;	Ctlr*	next;	int	active; 	QLock	alock;			/* attach */	Lock	ilock;			/* init */	int	init;			/*  */ 	int	pciv;			/*  */	int	macv;			/* MAC version */	int	phyv;			/* PHY version */	int	pcie;			/* flag: pci-express device? */ 	uvlong	mchash;			/* multicast hash */ 	Mii*	mii; 	Lock	tlock;			/* transmit */	D*	td;			/* descriptor ring */	Block**	tb;			/* transmit buffers */	int	ntd; 	int	tdh;			/* head - producer index (host) */	int	tdt;			/* tail - consumer index (NIC) */	int	ntdfree;	int	ntq; //	int	rbsz;			/* receive buffer size */ 	Lock	rlock;			/* receive */	D*	rd;			/* descriptor ring */	Block**	rb;			/* receive buffers */	int	nrd; 	int	rdh;			/* head - producer index (NIC) */	int	rdt;			/* tail - consumer index (host) */	int	nrdfree; 	int	tcr;			/* transmit configuration register */	int	rcr;			/* receive configuration register */	int	imr; 	QLock	slock;			/* statistics */	Dtcc*	dtcc;	uint	txdu;	uint	tcpf;	uint	udpf;	uint	ipf;	uint	fovf;	uint	ierrs;	uint	rer;	uint	rdu;	uint	punlc;	uint	fovw;	uint	mcast;	uint	frag;			/* partial packets; rb was too small */} Ctlr; static Ctlr* rtl8169ctlrhead;static Ctlr* rtl8169ctlrtail; #define csr8r(c, r)	(inb((c)->port+(r)))#define csr16r(c, r)	(ins((c)->port+(r)))#define csr32r(c, r)	(inl((c)->port+(r)))#define csr8w(c, r, b)	(outb((c)->port+(r), (u8int)(b)))#define csr16w(c, r, w)	(outs((c)->port+(r), (u16int)(w)))#define csr32w(c, r, l)	(outl((c)->port+(r), (u32int)(l))) static intrtl8169miimir(Mii* mii, int pa, int ra){	uint r;	int timeo;	Ctlr *ctlr; 	if(pa != 1)		return -1;	ctlr = mii->ctlr; 	r = (ra<<16) & RegaddrMASK;	csr32w(ctlr, Phyar, r);	delay(1);	for(timeo = 0; timeo < 2000; timeo++){		if((r = csr32r(ctlr, Phyar)) & Flag)			break;		microdelay(100);	}	if(!(r & Flag))		return -1; 	return (r & DataMASK)>>DataSHIFT;} static intrtl8169miimiw(Mii* mii, int pa, int ra, int data){	uint r;	int timeo;	Ctlr *ctlr; 	if(pa != 1)		return -1;	ctlr = mii->ctlr; 	r = Flag|((ra<<16) & RegaddrMASK)|((data<<DataSHIFT) & DataMASK);	csr32w(ctlr, Phyar, r);	delay(1);	for(timeo = 0; timeo < 2000; timeo++){		if(!((r = csr32r(ctlr, Phyar)) & Flag))			break;		microdelay(100);	}	if(r & Flag)		return -1; 	return 0;} static intrtl8169mii(Ctlr* ctlr){	MiiPhy *phy; 	/*	 * Link management.	 */	if((ctlr->mii = malloc(sizeof(Mii))) == nil)		return -1;	ctlr->mii->mir = rtl8169miimir;	ctlr->mii->miw = rtl8169miimiw;	ctlr->mii->ctlr = ctlr; 	/*	 * Get rev number out of Phyidr2 so can config properly.	 * There's probably more special stuff for Macv0[234] needed here.	 */	ctlr->phyv = rtl8169miimir(ctlr->mii, 1, Phyidr2) & 0x0F;	if(ctlr->macv == Macv02){		csr8w(ctlr, 0x82, 1);				/* magic */		rtl8169miimiw(ctlr->mii, 1, 0x0B, 0x0000);	/* magic */	} 	if(mii(ctlr->mii, (1<<1)) == 0 || (phy = ctlr->mii->curphy) == nil){		free(ctlr->mii);		ctlr->mii = nil;		return -1;	}	print("oui %#ux phyno %d, macv = %#8.8ux phyv = %#4.4ux\n",		phy->oui, phy->phyno, ctlr->macv, ctlr->phyv); 	miiane(ctlr->mii, ~0, ~0, ~0); 	return 0;} static voidrtl8169promiscuous(void* arg, int on){	Ether *edev;	Ctlr * ctlr; 	edev = arg;	ctlr = edev->ctlr;	ilock(&ctlr->ilock); 	if(on)		ctlr->rcr |= Aap;	else		ctlr->rcr &= ~Aap;	csr32w(ctlr, Rcr, ctlr->rcr);	iunlock(&ctlr->ilock);} enum {	/* everyone else uses 0x04c11db7, but they both produce the same crc */	Etherpolybe = 0x04c11db6,	Bytemask = (1<<8) - 1,}; static ulongethercrcbe(uchar *addr, long len){	int i, j;	ulong c, crc, carry; 	crc = ~0UL;	for (i = 0; i < len; i++) {		c = addr[i];		for (j = 0; j < 8; j++) {			carry = ((crc & (1UL << 31))? 1: 0) ^ (c & 1);			crc <<= 1;			c >>= 1;			if (carry)				crc = (crc ^ Etherpolybe) | carry;		}	}	return crc;} static ulongswabl(ulong l){	return l>>24 | (l>>8) & (Bytemask<<8) |		(l<<8) & (Bytemask<<16) | l<<24;} static voidrtl8169multicast(void* ether, uchar *eaddr, int add){	Ether *edev;	Ctlr *ctlr; 	if (!add)		return;	/* ok to keep receiving on old mcast addrs */ 	edev = ether;	ctlr = edev->ctlr;	ilock(&ctlr->ilock); 	ctlr->mchash |= 1ULL << (ethercrcbe(eaddr, Eaddrlen) >> 26); 	ctlr->rcr |= Am;	csr32w(ctlr, Rcr, ctlr->rcr); 	/* pci-e variants reverse the order of the hash byte registers */	if (ctlr->pcie) {		csr32w(ctlr, Mar0,   swabl(ctlr->mchash>>32));		csr32w(ctlr, Mar0+4, swabl(ctlr->mchash));	} else {		csr32w(ctlr, Mar0,   ctlr->mchash);		csr32w(ctlr, Mar0+4, ctlr->mchash>>32);	} 	iunlock(&ctlr->ilock);

Lines 1–500 of 1269.