| /* 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. | 
 |  * 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 <vfs.h> | 
 | #include <kfs.h> | 
 | #include <slab.h> | 
 | #include <kmalloc.h> | 
 | #include <kref.h> | 
 | #include <string.h> | 
 | #include <stdio.h> | 
 | #include <assert.h> | 
 | #include <error.h> | 
 | #include <cpio.h> | 
 | #include <pmap.h> | 
 | #include <smp.h> | 
 | #include <arch/pci.h> | 
 | #include <assert.h> | 
 | #include <ip.h> | 
 | #include <ns.h> | 
 |  | 
 | #define ilock(x) spin_lock_irqsave(x) | 
 | #define iunlock(x) spin_unlock_irqsave(x) | 
 |  | 
 | #include "ethermii.h" | 
 |  | 
 | #define HOWMANY(x, y)	(((x)+((y)-1))/(y)) | 
 | 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 */ | 
 |  | 
 | 	Rms		= 0xDA,		/* Receive Packet Maximum Size */ | 
 | 	Cplusc		= 0xE0,		/* C+ Command */ | 
 | 	Rdsar		= 0xE4,		/* Receive Descriptor Start Address */ | 
 | 	Mtps		= 0xEC,		/* Max. Transmit Packet Size */ | 
 | }; | 
 |  | 
 | 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 */ | 
 | 	Macv11		= 0x30000000,	/* RTL8168B/8111B */ | 
 | 	Macv12		= 0x38000000,	/* RTL8169B/8111B */ | 
 | 	Macv13		= 0x34000000,	/* RTL8101E */ | 
 | 	Macv14		= 0x30800000,	/* RTL8100E */ | 
 | 	Macv15		= 0x38800000,	/* RTL8100E */ | 
 | 	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 {					/* 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 { | 
 | 	uint32_t	control; | 
 | 	uint32_t	vlan; | 
 | 	uint32_t	addrlo; | 
 | 	uint32_t	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,	/* Large Send */ | 
 | }; | 
 |  | 
 | enum {					/* Receive Descriptor control */ | 
 | 	RxflMASK	= 0x00003FFF,	/* Receive Frame Length */ | 
 | 	RxflSHIFT	= 0, | 
 | 	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		= 32,		/* Transmit Ring */ | 
 | 	Nrd		= 128,		/* Receive Ring */ | 
 | }; | 
 |  | 
 | #define Mps ROUNDUP(ETHERMAXTU + 4, 128) | 
 |  | 
 | typedef struct Dtcc Dtcc; | 
 | struct Dtcc { | 
 | 	uint64_t	txok; | 
 | 	uint64_t	rxok; | 
 | 	uint64_t	txer; | 
 | 	uint32_t	rxer; | 
 | 	uint16_t	misspkt; | 
 | 	uint16_t	fae; | 
 | 	uint32_t	tx1col; | 
 | 	uint32_t	txmcol; | 
 | 	uint64_t	rxokph; | 
 | 	uint64_t	rxokbrd; | 
 | 	uint32_t	rxokmu; | 
 | 	uint16_t	txabt; | 
 | 	uint16_t	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 */ | 
 | }; | 
 |  | 
 | struct ctlr { | 
 | 	int	port; | 
 | 	struct pci_device *pci; | 
 | 	struct ctlr*	next; | 
 | 	int	active; | 
 |  | 
 | 	qlock_t	alock;			/* attach */ | 
 | 	spinlock_t	ilock;			/* init */ | 
 | 	int	init;			/*  */ | 
 |  | 
 | 	int	pciv;			/*  */ | 
 | 	int	macv;			/* MAC version */ | 
 | 	int	phyv;			/* PHY version */ | 
 | 	int	pcie;			/* flag: pci-express device? */ | 
 |  | 
 | 	uint64_t	mchash;			/* multicast hash */ | 
 |  | 
 | 	struct mii*	mii; | 
 |  | 
 | 	spinlock_t	tlock;			/* transmit */ | 
 | 	D*	td;			/* descriptor ring */ | 
 | 	struct block**	tb;			/* transmit buffers */ | 
 | 	int	ntd; | 
 |  | 
 | 	int	tdh;			/* head - producer index (host) */ | 
 | 	int	tdt;			/* tail - consumer index (NIC) */ | 
 | 	int	ntdfree; | 
 | 	int	ntq; | 
 |  | 
 | 	int	mtps;			/* Max. Transmit Packet Size */ | 
 |  | 
 | 	spinlock_t	rlock;			/* receive */ | 
 | 	D*	rd;			/* descriptor ring */ | 
 | 	struct 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_t	slock;			/* statistics */ | 
 | 	Dtcc*	dtcc; | 
 | 	unsigned int	txdu; | 
 | 	unsigned int	tcpf; | 
 | 	unsigned int	udpf; | 
 | 	unsigned int	ipf; | 
 | 	unsigned int	fovf; | 
 | 	unsigned int	ierrs; | 
 | 	unsigned int	rer; | 
 | 	unsigned int	rdu; | 
 | 	unsigned int	punlc; | 
 | 	unsigned int	fovw; | 
 | 	unsigned int	mcast; | 
 | }; | 
 |  | 
 | static struct ctlr* rtl8169ctlrhead; | 
 | static struct ctlr* rtl8169ctlrtail; | 
 |  | 
 | #define csr8r(c, r)	(inb((c)->port+(r))) | 
 | #define csr16r(c, r)	(inw((c)->port+(r))) | 
 | #define csr32r(c, r)	(inl((c)->port+(r))) | 
 | #define csr8w(c, r, b)	(outb((c)->port+(r), (uint8_t)(b))) | 
 | #define csr16w(c, r, w)	(outw((c)->port+(r), (uint16_t)(w))) | 
 | #define csr32w(c, r, l)	(outl((c)->port+(r), (uint32_t)(l))) | 
 |  | 
 | static int | 
 | rtl8169miimir(struct ctlr* ctlr, int pa, int ra) | 
 | { | 
 | 	unsigned int r; | 
 | 	int timeo; | 
 |  | 
 | 	if(pa != 1) | 
 | 		return -1; | 
 |  | 
 | 	r = (ra<<16) & RegaddrMASK; | 
 | 	csr32w(ctlr, Phyar, r); | 
 | 	udelay(1000*1); | 
 | 	for(timeo = 0; timeo < 2000; timeo++){ | 
 | 		if((r = csr32r(ctlr, Phyar)) & Flag) | 
 | 			break; | 
 | 		udelay(100); | 
 | 	} | 
 | 	if(!(r & Flag)) | 
 | 		return -1; | 
 |  | 
 | 	return (r & DataMASK)>>DataSHIFT; | 
 | } | 
 |  | 
 | static int | 
 | rtl8169miimiw(struct ctlr* ctlr, int pa, int ra, int data) | 
 | { | 
 | 	unsigned int r; | 
 | 	int timeo; | 
 |  | 
 | 	if(pa != 1) | 
 | 		return -1; | 
 |  | 
 | 	r = Flag|((ra<<16) & RegaddrMASK)|((data<<DataSHIFT) & DataMASK); | 
 | 	csr32w(ctlr, Phyar, r); | 
 | 	udelay(1000*1); | 
 | 	for(timeo = 0; timeo < 2000; timeo++){ | 
 | 		if(!((r = csr32r(ctlr, Phyar)) & Flag)) | 
 | 			break; | 
 | 		udelay(100); | 
 | 	} | 
 | 	if(r & Flag) | 
 | 		return -1; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int | 
 | rtl8169miirw(struct mii* mii, int write, int pa, int ra, int data) | 
 | { | 
 | 	if(write) | 
 | 		return rtl8169miimiw(mii->ctlr, pa, ra, data); | 
 |  | 
 | 	return rtl8169miimir(mii->ctlr, pa, ra); | 
 | } | 
 |  | 
 | static struct mii* | 
 | rtl8169mii(struct ctlr* ctlr) | 
 | { | 
 | 	struct mii* mii; | 
 | 	struct miiphy *phy; | 
 |  | 
 | 	/* | 
 | 	 * Link management. | 
 | 	 * | 
 | 	 * 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, 1, Phyidr2) & 0x0F; | 
 | 	if(ctlr->macv == Macv02){ | 
 | 		csr8w(ctlr, 0x82, 1);				/* magic */ | 
 | 		rtl8169miimiw(ctlr, 1, 0x0B, 0x0000);		/* magic */ | 
 | 	} | 
 | 	if((mii = miiattach(ctlr, (1<<1), rtl8169miirw)) == NULL) | 
 | 		return NULL; | 
 |  | 
 | 	phy = mii->curphy; | 
 | 	printd("oui %#ux phyno %d, macv = %#8.8ux phyv = %#4.4ux\n", | 
 | 		phy->oui, phy->phyno, ctlr->macv, ctlr->phyv); | 
 |  | 
 | 	if(miistatus(mii) < 0){ | 
 | 		miireset(mii); | 
 | 		miiane(mii, ~0, ~0, ~0); | 
 | 	} | 
 |  | 
 | 	return mii; | 
 | } | 
 |  | 
 | static void | 
 | rtl8169promiscuous(void* arg, int on) | 
 | { | 
 | 	struct ether *edev; | 
 | 	struct 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 uint32_t | 
 | ethercrcbe(uint8_t *addr, long len) | 
 | { | 
 | 	int i, j; | 
 | 	uint32_t c, crc, carry; | 
 |  | 
 | 	crc = ~0U; | 
 | 	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 uint32_t | 
 | swabl(uint32_t l) | 
 | { | 
 | 	return (l>>24) | ((l>>8) & (Bytemask<<8)) | | 
 | 		((l<<8) & (Bytemask<<16)) | (l<<24); | 
 | } | 
 |  | 
 | static void | 
 | rtl8169multicast(void* ether, uint8_t *eaddr, int add) | 
 | { | 
 | 	struct ether *edev; | 
 | 	struct 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); | 
 | } | 
 |  | 
 | static long | 
 | rtl8169ifstat(struct ether* edev, void* a, long n, uint32_t offset) | 
 | { | 
 | 	ERRSTACK(2); | 
 | 	struct ctlr *ctlr; | 
 | 	Dtcc *dtcc; | 
 | 	int timeo; | 
 | 	char *alloc, *e, *p; | 
 |  | 
 | 	ctlr = edev->ctlr; | 
 | 	qlock(&ctlr->slock); | 
 |  | 
 | 	alloc = NULL; | 
 | 	if(waserror()){ | 
 | 		qunlock(&ctlr->slock); | 
 | 		kfree(alloc); | 
 | 		nexterror(); | 
 | 	} | 
 |  | 
 | 	csr32w(ctlr, Dtccr+4, 0); | 
 | 	csr32w(ctlr, Dtccr, paddr_low32(ctlr->dtcc)|Cmd); | 
 | 	for(timeo = 0; timeo < 1000; timeo++){ | 
 | 		if(!(csr32r(ctlr, Dtccr) & Cmd)) | 
 | 			break; | 
 | 		udelay(1000*1); | 
 | 	} | 
 | 	if(csr32r(ctlr, Dtccr) & Cmd) | 
 | 		error(Eio); | 
 | 	dtcc = ctlr->dtcc; | 
 |  | 
 | 	edev->netif.oerrs = dtcc->txer; | 
 | 	edev->netif.crcs = dtcc->rxer; | 
 | 	edev->netif.frames = dtcc->fae; | 
 | 	edev->netif.buffs = dtcc->misspkt; | 
 | 	edev->netif.overflows = ctlr->txdu+ctlr->rdu; | 
 |  | 
 | 	if(n == 0){ | 
 | 		qunlock(&ctlr->slock); | 
 | 		poperror(); | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	if((alloc = kzmalloc(READSTR, 0)) == NULL) | 
 | 		error(Enomem); | 
 | 	e = alloc+READSTR; | 
 |  | 
 | 	p = seprintf(alloc, e, "TxOk: %llu\n", dtcc->txok); | 
 | 	p = seprintf(p, e, "RxOk: %llu\n", dtcc->rxok); | 
 | 	p = seprintf(p, e, "TxEr: %llu\n", dtcc->txer); | 
 | 	p = seprintf(p, e, "RxEr: %u\n", dtcc->rxer); | 
 | 	p = seprintf(p, e, "MissPkt: %u\n", dtcc->misspkt); | 
 | 	p = seprintf(p, e, "FAE: %u\n", dtcc->fae); | 
 | 	p = seprintf(p, e, "Tx1Col: %u\n", dtcc->tx1col); | 
 | 	p = seprintf(p, e, "TxMCol: %u\n", dtcc->txmcol); | 
 | 	p = seprintf(p, e, "RxOkPh: %llu\n", dtcc->rxokph); | 
 | 	p = seprintf(p, e, "RxOkBrd: %llu\n", dtcc->rxokbrd); | 
 | 	p = seprintf(p, e, "RxOkMu: %u\n", dtcc->rxokmu); | 
 | 	p = seprintf(p, e, "TxAbt: %u\n", dtcc->txabt); | 
 | 	p = seprintf(p, e, "TxUndrn: %u\n", dtcc->txundrn); | 
 |  | 
 | 	p = seprintf(p, e, "txdu: %u\n", ctlr->txdu); | 
 | 	p = seprintf(p, e, "tcpf: %u\n", ctlr->tcpf); | 
 | 	p = seprintf(p, e, "udpf: %u\n", ctlr->udpf); | 
 | 	p = seprintf(p, e, "ipf: %u\n", ctlr->ipf); | 
 | 	p = seprintf(p, e, "fovf: %u\n", ctlr->fovf); | 
 | 	p = seprintf(p, e, "ierrs: %u\n", ctlr->ierrs); | 
 | 	p = seprintf(p, e, "rer: %u\n", ctlr->rer); | 
 | 	p = seprintf(p, e, "rdu: %u\n", ctlr->rdu); | 
 | 	p = seprintf(p, e, "punlc: %u\n", ctlr->punlc); | 
 | 	p = seprintf(p, e, "fovw: %u\n", ctlr->fovw); | 
 |  | 
 | 	p = seprintf(p, e, "tcr: 0x%#8.8u\n", ctlr->tcr); | 
 | 	p = seprintf(p, e, "rcr: 0x%#8.8u\n", ctlr->rcr); | 
 | 	p = seprintf(p, e, "multicast: %u\n", ctlr->mcast); | 
 |  | 
 | 	if(ctlr->mii != NULL && ctlr->mii->curphy != NULL) | 
 | 		miidumpphy(ctlr->mii, p, e); | 
 |  | 
 | 	n = readstr(offset, a, n, alloc); | 
 |  | 
 | 	qunlock(&ctlr->slock); | 
 | 	poperror(); | 
 | 	kfree(alloc); | 
 |  | 
 | 	return n; | 
 | } | 
 |  | 
 | static void | 
 | rtl8169halt(struct ctlr* ctlr) | 
 | { | 
 | 	csr8w(ctlr, Cr, 0); | 
 | 	csr16w(ctlr, Imr, 0); | 
 | 	csr16w(ctlr, Isr, ~0); | 
 | } | 
 |  | 
 | static int | 
 | rtl8169reset(struct ctlr* ctlr) | 
 | { | 
 | 	uint32_t r; | 
 | 	int timeo; | 
 |  | 
 | 	/* | 
 | 	 * Soft reset the controller. | 
 | 	 */ | 
 | 	csr8w(ctlr, Cr, Rst); | 
 | 	for(r = timeo = 0; timeo < 1000; timeo++){ | 
 | 		r = csr8r(ctlr, Cr); | 
 | 		if(!(r & Rst)) | 
 | 			break; | 
 | 		udelay(1000*1); | 
 | 	} | 
 | 	rtl8169halt(ctlr); | 
 |  | 
 | 	if(r & Rst) | 
 | 		return -1; | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void | 
 | rtl8169replenish(struct ctlr* ctlr) | 
 | { | 
 | 	D *d; | 
 | 	int rdt; | 
 | 	struct block *bp; | 
 |  | 
 | 	rdt = ctlr->rdt; | 
 | 	while(NEXT_RING(rdt, ctlr->nrd) != ctlr->rdh){ | 
 | 		d = &ctlr->rd[rdt]; | 
 | 		if(ctlr->rb[rdt] == NULL){ | 
 | 			/* | 
 | 			 * Simple allocation for now. | 
 | 			 * This better be aligned on 8. | 
 | 			 */ | 
 | 			bp = iallocb(Mps); | 
 | 			if(bp == NULL){ | 
 | 				printk("no available buffers\n"); | 
 | 				break; | 
 | 			} | 
 | 			ctlr->rb[rdt] = bp; | 
 | 			d->addrlo = paddr_low32(bp->rp); | 
 | 			d->addrhi = paddr_high32(bp->rp); | 
 | 		} | 
 | 		wmb(); | 
 | 		d->control |= Own|Mps; | 
 | 		rdt = NEXT_RING(rdt, ctlr->nrd); | 
 | 		ctlr->nrdfree++; | 
 | 	} | 
 | 	ctlr->rdt = rdt; | 
 | } | 
 |  | 
 | static int | 
 | rtl8169init(struct ether* edev) | 
 | { | 
 | 	int i; | 
 | 	uint32_t r; | 
 | 	struct block *bp; | 
 | 	struct ctlr *ctlr; | 
 | 	uint8_t cplusc; | 
 |  | 
 | 	ctlr = edev->ctlr; | 
 | 	ilock(&ctlr->ilock); | 
 |  | 
 | 	rtl8169halt(ctlr); | 
 |  | 
 | 	/* | 
 | 	 * MAC Address. | 
 | 	 * Must put chip into config register write enable mode. | 
 | 	 */ | 
 | 	csr8w(ctlr, Cr9346, Eem1|Eem0); | 
 | 	r = (edev->ea[3]<<24)|(edev->ea[2]<<16)|(edev->ea[1]<<8)|edev->ea[0]; | 
 | 	csr32w(ctlr, Idr0, r); | 
 | 	r = (edev->ea[5]<<8)|edev->ea[4]; | 
 | 	csr32w(ctlr, Idr0+4, r); | 
 |  | 
 | 	/* | 
 | 	 * Transmitter. | 
 | 	 */ | 
 | 	memset(ctlr->td, 0, sizeof(D)*ctlr->ntd); | 
 | 	ctlr->tdh = ctlr->tdt = 0; | 
 | 	ctlr->td[ctlr->ntd-1].control = Eor; | 
 |  | 
 | 	/* | 
 | 	 * Receiver. | 
 | 	 * Need to do something here about the multicast filter. | 
 | 	 */ | 
 | 	memset(ctlr->rd, 0, sizeof(D)*ctlr->nrd); | 
 | 	ctlr->nrdfree = ctlr->rdh = ctlr->rdt = 0; | 
 | 	ctlr->rd[ctlr->nrd-1].control = Eor; | 
 |  | 
 | 	for(i = 0; i < ctlr->nrd; i++){ | 
 | 		if((bp = ctlr->rb[i]) != NULL){ | 
 | 			ctlr->rb[i] = NULL; | 
 | 			freeb(bp); | 
 | 		} | 
 | 	} | 
 | 	rtl8169replenish(ctlr); | 
 | 	ctlr->rcr = Rxfthnone|Mrxdmaunlimited|Ab|Am|Apm; | 
 |  | 
 | 	/* | 
 | 	 * Mtps is in units of 128 except for the RTL8169 | 
 | 	 * where is is 32. If using jumbo frames should be | 
 | 	 * set to 0x3F. | 
 | 	 * Setting Mulrw in Cplusc disables the Tx/Rx DMA burst | 
 | 	 * settings in Tcr/Rcr; the (1<<14) is magic. | 
 | 	 */ | 
 | 	ctlr->mtps = HOWMANY(Mps, 128); | 
 | 	cplusc = csr16r(ctlr, Cplusc) & ~(1<<14); | 
 | 	cplusc |= /*Rxchksum|*/Mulrw; | 
 | 	switch(ctlr->macv){ | 
 | 	default: | 
 | 		printd("rtl8169: unsupported macv %#ux\n", ctlr->macv); | 
 | 		break;	/* perhaps it just works */ | 
 | 	case Macv01: | 
 | 		ctlr->mtps = HOWMANY(Mps, 32); | 
 | 		break; | 
 | 	case Macv02: | 
 | 	case Macv03: | 
 | 		cplusc |= (1<<14);			/* magic */ | 
 | 		break; | 
 | 	case Macv05: | 
 | 		/* | 
 | 		 * This is interpreted from clearly bogus code | 
 | 		 * in the manufacturer-supplied driver, it could | 
 | 		 * be wrong. Untested. | 
 | 		 */ | 
 | 		printk("untested\n"); | 
 | 		break; | 
 | #if 0	       | 
 | 		r = csr8r(ctlr, Config2) & 0x07; | 
 | 		if(r == 0x01)				/* 66MHz PCI */ | 
 | 			csr32w(ctlr, 0x7C, 0x0007FFFF);	/* magic */ | 
 | 		else | 
 | 			csr32w(ctlr, 0x7C, 0x0007FF00);	/* magic */ | 
 | 		pciclrmwi(ctlr->pcidev); | 
 | #endif | 
 | 		break; | 
 | 	case Macv13: | 
 | 		printk("untested macv13 write\n"); | 
 | 		break; | 
 | #if 0 | 
 | 		/* | 
 | 		 * This is interpreted from clearly bogus code | 
 | 		 * in the manufacturer-supplied driver, it could | 
 | 		 * be wrong. Untested. | 
 | 		 */ | 
 | 		pcicfgw8(ctlr->pcidev, 0x68, 0x00);	/* magic */ | 
 | 		pcicfgw8(ctlr->pcidev, 0x69, 0x08);	/* magic */ | 
 | 		break; | 
 | #endif | 
 | 	case Macv04: | 
 | 	case Macv11: | 
 | 	case Macv12: | 
 | 	case Macv14: | 
 | 	case Macv15: | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * Enable receiver/transmitter. | 
 | 	 * Need to do this first or some of the settings below | 
 | 	 * won't take. | 
 | 	 */ | 
 | 	switch(ctlr->pciv){ | 
 | 	default: | 
 | 		csr8w(ctlr, Cr, Te|Re); | 
 | 		csr32w(ctlr, Tcr, Ifg1|Ifg0|Mtxdmaunlimited); | 
 | 		csr32w(ctlr, Rcr, ctlr->rcr); | 
 | 		csr32w(ctlr, Mar0,   0); | 
 | 		csr32w(ctlr, Mar0+4, 0); | 
 | 		ctlr->mchash = 0; | 
 | 	case Rtl8169sc: | 
 | 	case Rtl8168b: | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * Interrupts. | 
 | 	 * Disable Tdu|Tok for now, the transmit routine will tidy. | 
 | 	 * Tdu means the NIC ran out of descriptors to send, so it | 
 | 	 * doesn't really need to ever be on. | 
 | 	 */ | 
 | 	csr32w(ctlr, Timerint, 0); | 
 | 	ctlr->imr = Serr|Timeout|Fovw|Punlc|Rdu|Ter|Rer|Rok; | 
 | 	csr16w(ctlr, Imr, ctlr->imr); | 
 |  | 
 | 	/* | 
 | 	 * Clear missed-packet counter; | 
 | 	 * initial early transmit threshold value; | 
 | 	 * set the descriptor ring base addresses; | 
 | 	 * set the maximum receive packet size; | 
 | 	 * no early-receive interrupts. | 
 | 	 */ | 
 | 	csr32w(ctlr, Mpc, 0); | 
 | 	csr8w(ctlr, Mtps, ctlr->mtps); | 
 | 	csr32w(ctlr, Tnpds + 4, paddr_high32(ctlr->td)); | 
 | 	csr32w(ctlr, Tnpds, paddr_low32(ctlr->td)); | 
 | 	csr32w(ctlr, Rdsar + 4, paddr_high32(ctlr->rd)); | 
 | 	csr32w(ctlr, Rdsar, paddr_low32(ctlr->rd)); | 
 | 	csr16w(ctlr, Rms, Mps); | 
 | 	r = csr16r(ctlr, Mulint) & 0xF000; | 
 | 	csr16w(ctlr, Mulint, r); | 
 | 	csr16w(ctlr, Cplusc, cplusc); | 
 |  | 
 | 	/* | 
 | 	 * Set configuration. | 
 | 	 */ | 
 | 	switch(ctlr->pciv){ | 
 | 	default: | 
 | 		break; | 
 | 	case Rtl8169sc: | 
 | 		csr16w(ctlr, 0xE2, 0);			/* magic */ | 
 | 		csr8w(ctlr, Cr, Te|Re); | 
 | 		csr32w(ctlr, Tcr, Ifg1|Ifg0|Mtxdmaunlimited); | 
 | 		csr32w(ctlr, Rcr, ctlr->rcr); | 
 | 		break; | 
 | 	case Rtl8168b: | 
 | 	case Rtl8169c: | 
 | 		csr16w(ctlr, 0xE2, 0);			/* magic */ | 
 | 		csr16w(ctlr, Cplusc, 0x2000);		/* magic */ | 
 | 		csr8w(ctlr, Cr, Te|Re); | 
 | 		csr32w(ctlr, Tcr, Ifg1|Ifg0|Mtxdmaunlimited); | 
 | 		csr32w(ctlr, Rcr, ctlr->rcr); | 
 | 		csr16w(ctlr, Rms, 0x0800); | 
 | 		csr8w(ctlr, Mtps, 0x3F); | 
 | 		break; | 
 | 	} | 
 | 	ctlr->tcr = csr32r(ctlr, Tcr); | 
 | 	csr8w(ctlr, Cr9346, 0); | 
 |  | 
 | 	iunlock(&ctlr->ilock); | 
 |  | 
 | //	rtl8169mii(ctlr); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void | 
 | rtl8169attach(struct ether* edev) | 
 | { | 
 | 	int timeo; | 
 | 	struct ctlr *ctlr; | 
 | 	struct miiphy *phy; | 
 |  | 
 | 	ctlr = edev->ctlr; | 
 | 	qlock(&ctlr->alock); | 
 | 	if(ctlr->init == 0){ | 
 | 		/* | 
 | 		 * Handle allocation/init errors here. | 
 | 		 */ | 
 | 		ctlr->td = kzmalloc_align(sizeof(D) * Ntd, KMALLOC_WAIT, 256); | 
 | 		ctlr->tb = kzmalloc(Ntd * sizeof(struct block *), KMALLOC_WAIT); | 
 | 		ctlr->ntd = Ntd; | 
 | 		ctlr->rd = kzmalloc_align(sizeof(D) * Nrd, KMALLOC_WAIT, 256); | 
 | 		ctlr->rb = kzmalloc(Nrd * sizeof(struct block *), KMALLOC_WAIT); | 
 | 		ctlr->nrd = Nrd; | 
 | 		ctlr->dtcc = kzmalloc_align(sizeof(Dtcc), KMALLOC_WAIT, 64); | 
 | 		rtl8169init(edev); | 
 | 		ctlr->init = 1; | 
 | 	} | 
 | 	qunlock(&ctlr->alock); | 
 |  | 
 | 	/* | 
 | 	 * Wait for link to be ready. | 
 | 	 */ | 
 | 	for(timeo = 0; timeo < 350; timeo++){ | 
 | 		if(miistatus(ctlr->mii) == 0) | 
 | 			break; | 
 | 		udelay_sched(10000); | 
 | 	} | 
 | 	phy = ctlr->mii->curphy; | 
 | 	printd("%s: speed %d fd %d link %d rfc %d tfc %d\n", | 
 | 		edev->netif.name, phy->speed, phy->fd, phy->link, phy->rfc, phy->tfc); | 
 | } | 
 |  | 
 | static void | 
 | rtl8169link(struct ether* edev) | 
 | { | 
 | 	int limit; | 
 | 	struct ctlr *ctlr; | 
 | 	struct miiphy *phy; | 
 |  | 
 | 	ctlr = edev->ctlr; | 
 |  | 
 | 	/* | 
 | 	 * Maybe the link changed - do we care very much? | 
 | 	 * Could stall transmits if no link, maybe? | 
 | 	 */ | 
 | 	if(ctlr->mii == NULL || ctlr->mii->curphy == NULL) | 
 | 		return; | 
 |  | 
 | 	phy = ctlr->mii->curphy; | 
 | 	if(miistatus(ctlr->mii) < 0){ | 
 | 		// TODO : no name here | 
 | 		printk("%slink n: speed %d fd %d link %d rfc %d tfc %d\n", | 
 | 			edev->netif.name, phy->speed, phy->fd, phy->link, | 
 | 			phy->rfc, phy->tfc); | 
 | 		edev->netif.link = 0; | 
 | 		return; | 
 | 	} | 
 | 	edev->netif.link = 1; | 
 |  | 
 | 	limit = 256*1024; | 
 | 	if(phy->speed == 10){ | 
 | 		edev->netif.mbps = 10; | 
 | 		limit = 65*1024; | 
 | 	} | 
 | 	else if(phy->speed == 100) | 
 | 		edev->netif.mbps = 100; | 
 | 	else if(phy->speed == 1000) | 
 | 		edev->netif.mbps = 1000; | 
 | 	printk("%slink y: speed %d fd %d link %d rfc %d tfc %d\n", | 
 | 		edev->netif.name, phy->speed, phy->fd, phy->link, | 
 | 		phy->rfc, phy->tfc); | 
 |  | 
 | 	if(edev->oq != NULL) | 
 | 		qsetlimit(edev->oq, limit); | 
 | } | 
 |  | 
 | static void | 
 | rtl8169transmit(struct ether* edev) | 
 | { | 
 | 	D *d; | 
 | 	struct block *bp; | 
 | 	struct ctlr *ctlr; | 
 | 	int control, x; | 
 |  | 
 | 	ctlr = edev->ctlr; | 
 |  | 
 | 	ilock(&ctlr->tlock); | 
 | 	for(x = ctlr->tdh; ctlr->ntq > 0; x = NEXT_RING(x, ctlr->ntd)){ | 
 | 		d = &ctlr->td[x]; | 
 | 		if((control = d->control) & Own) | 
 | 			break; | 
 |  | 
 | 		/* | 
 | 		 * Check errors and log here. | 
 | 		 */ | 
 |  | 
 | 		/* | 
 | 		 * Free it up. | 
 | 		 * Need to clean the descriptor here? Not really. | 
 | 		 * Simple freeb for now (no chain and freeblist). | 
 | 		 * Use ntq count for now. | 
 | 		 */ | 
 | 		freeb(ctlr->tb[x]); | 
 | 		ctlr->tb[x] = NULL; | 
 | 		d->control &= Eor; | 
 |  | 
 | 		ctlr->ntq--; | 
 | 	} | 
 | 	ctlr->tdh = x; | 
 |  | 
 | 	x = ctlr->tdt; | 
 | 	while(ctlr->ntq < (ctlr->ntd-1)){ | 
 | 		if((bp = qget(edev->oq)) == NULL) | 
 | 			break; | 
 |  | 
 | 		d = &ctlr->td[x]; | 
 | 		d->addrlo = paddr_low32(bp->rp); | 
 | 		d->addrhi = paddr_high32(bp->rp); | 
 | 		ctlr->tb[x] = bp; | 
 | 		wmb(); | 
 | 		d->control |= Own|Fs|Ls|((BLEN(bp)<<TxflSHIFT) & TxflMASK); | 
 |  | 
 | 		x = NEXT_RING(x, ctlr->ntd); | 
 | 		ctlr->ntq++; | 
 | 	} | 
 | 	if(x != ctlr->tdt){ | 
 | 		ctlr->tdt = x; | 
 | 		csr8w(ctlr, Tppoll, Npq); | 
 | 	} | 
 | 	else if(ctlr->ntq >= (ctlr->ntd-1)) | 
 | 		ctlr->txdu++; | 
 |  | 
 | 	iunlock(&ctlr->tlock); | 
 | } | 
 |  | 
 | static void | 
 | rtl8169receive(struct ether* edev) | 
 | { | 
 | 	D *d; | 
 | 	int rdh; | 
 | 	struct block *bp; | 
 | 	struct ctlr *ctlr; | 
 | 	uint32_t control; | 
 |  | 
 | 	ctlr = edev->ctlr; | 
 |  | 
 | 	rdh = ctlr->rdh; | 
 | 	for(;;){ | 
 | 		d = &ctlr->rd[rdh]; | 
 |  | 
 | 		if(d->control & Own) | 
 | 			break; | 
 |  | 
 | 		control = d->control; | 
 | 		if((control & (Fs|Ls|Res)) == (Fs|Ls)){ | 
 | 			bp = ctlr->rb[rdh]; | 
 | 			ctlr->rb[rdh] = NULL; | 
 | 			bp->wp = bp->rp + ((control & RxflMASK)>>RxflSHIFT)-4; | 
 | 			bp->next = NULL; | 
 |  | 
 | 			if(control & Fovf) | 
 | 				ctlr->fovf++; | 
 | 			if(control & Mar) | 
 | 				ctlr->mcast++; | 
 |  | 
 | 			switch(control & (Pid1|Pid0)){ | 
 | 			default: | 
 | 				break; | 
 | 			case Pid0: | 
 | 				if(control & Tcpf){ | 
 | 					ctlr->tcpf++; | 
 | 					break; | 
 | 				} | 
 | 				bp->flag |= Btcpck; | 
 | 				break; | 
 | 			case Pid1: | 
 | 				if(control & Udpf){ | 
 | 					ctlr->udpf++; | 
 | 					break; | 
 | 				} | 
 | 				bp->flag |= Budpck; | 
 | 				break; | 
 | 			case Pid1|Pid0: | 
 | 				if(control & Ipf){ | 
 | 					ctlr->ipf++; | 
 | 					break; | 
 | 				} | 
 | 				bp->flag |= Bipck; | 
 | 				break; | 
 | 			} | 
 | 			etheriq(edev, bp, 1); | 
 | 		} | 
 | 		else{ | 
 | 			/* | 
 | 			 * Error stuff here. | 
 | 			print("control %#8.8ux\n", control); | 
 | 			 */ | 
 | 		} | 
 | 		d->control &= Eor; | 
 | 		ctlr->nrdfree--; | 
 | 		rdh = NEXT_RING(rdh, ctlr->nrd); | 
 |  | 
 | 		if(ctlr->nrdfree < ctlr->nrd/2) | 
 | 			rtl8169replenish(ctlr); | 
 | 	} | 
 | 	ctlr->rdh = rdh; | 
 | } | 
 |  | 
 | static void | 
 | rtl8169interrupt(struct hw_trapframe *hw_tf, void *arg) | 
 | { | 
 | 	struct ctlr *ctlr; | 
 | 	struct ether *edev; | 
 | 	uint32_t isr; | 
 |  | 
 | 	edev = arg; | 
 | 	ctlr = edev->ctlr; | 
 |  | 
 | 	while((isr = csr16r(ctlr, Isr)) != 0 && isr != 0xFFFF){ | 
 | 		csr16w(ctlr, Isr, isr); | 
 | 		if((isr & ctlr->imr) == 0) | 
 | 			break; | 
 | 		if(isr & (Fovw|Punlc|Rdu|Rer|Rok)){ | 
 | 			rtl8169receive(edev); | 
 | 			if(!(isr & (Punlc|Rok))) | 
 | 				ctlr->ierrs++; | 
 | 			if(isr & Rer) | 
 | 				ctlr->rer++; | 
 | 			if(isr & Rdu) | 
 | 				ctlr->rdu++; | 
 | 			if(isr & Punlc) | 
 | 				ctlr->punlc++; | 
 | 			if(isr & Fovw) | 
 | 				ctlr->fovw++; | 
 | 			isr &= ~(Fovw|Rdu|Rer|Rok); | 
 | 		} | 
 |  | 
 | 		if(isr & (Tdu|Ter|Tok)){ | 
 | 			rtl8169transmit(edev); | 
 | 			isr &= ~(Tdu|Ter|Tok); | 
 | 		} | 
 |  | 
 | 		if(isr & Punlc){ | 
 | 			rtl8169link(edev); | 
 | 			isr &= ~Punlc; | 
 | 		} | 
 |  | 
 | 		/* | 
 | 		 * Some of the reserved bits get set sometimes... | 
 | 		 */ | 
 | 		if(isr & (Serr|Timeout|Tdu|Fovw|Punlc|Rdu|Ter|Tok|Rer|Rok)) | 
 | 			panic("rtl8169interrupt: imr %#4.4ux isr %#4.4ux\n", | 
 | 				csr16r(ctlr, Imr), isr); | 
 | 	} | 
 | } | 
 |  | 
 | static void | 
 | rtl8169pci(void) | 
 | { | 
 | 	struct pci_device *pcidev; | 
 |  | 
 | 	struct ctlr *ctlr; | 
 | 	int id, port, pcie; | 
 |  | 
 | 	STAILQ_FOREACH(pcidev, &pci_devices, all_dev) { | 
 | 		/* This checks that pcidev is a Network Controller for Ethernet */ | 
 | 		if (pcidev->class != 0x02 || pcidev->subclass != 0x00) | 
 | 			continue; | 
 | 		id = pcidev->dev_id << 16 | pcidev->ven_id; | 
 |  | 
 | 		pcie = 0; | 
 | 		switch(id) { | 
 | 		default: | 
 | 			continue; | 
 | 		case Rtl8100e:			/* RTL810[01]E ? */ | 
 | 		case Rtl8168b:			/* RTL8168B */ | 
 | 			pcie = 1; | 
 | 			break; | 
 | 		case Rtl8169c:			/* RTL8169C */ | 
 | 		case Rtl8169sc:			/* RTL8169SC */ | 
 | 		case Rtl8169:			/* RTL8169 */ | 
 | 			break; | 
 | 		case (0xC107<<16)|0x1259:	/* Corega CG-LAPCIGT */ | 
 | 			id = Rtl8169; | 
 | 			break; | 
 | 		} | 
 | 		printk("rtl8169 driver found 0x%04x:%04x at %02x:%02x.%x\n", | 
 | 		       pcidev->ven_id, pcidev->dev_id, | 
 | 		       pcidev->bus, pcidev->dev, pcidev->func); | 
 |  | 
 | 		port = pcidev->bar[0].pio_base; | 
 |  | 
 | 		ctlr = kzmalloc(sizeof(struct ctlr), KMALLOC_WAIT); | 
 | 		spinlock_init_irqsave(&ctlr->ilock); | 
 | 		spinlock_init_irqsave(&ctlr->tlock); | 
 | 		spinlock_init_irqsave(&ctlr->rlock); | 
 | 		qlock_init(&ctlr->alock); | 
 | 		qlock_init(&ctlr->slock); | 
 |  | 
 | 		ctlr->port = port; | 
 | 		ctlr->pci = pcidev; | 
 | 		ctlr->pciv = id; | 
 | 		ctlr->pcie = pcie; | 
 |  | 
 | 		/* pcipms is something related to power mgmt, i think */ | 
 | 		#if 0 | 
 | 		if(pcigetpms(p) > 0){ | 
 | 			pcisetpms(p, 0); | 
 |  | 
 | 			for(int i = 0; i < 6; i++) | 
 | 				pcicfgw32(p, PciBAR0+i*4, p->mem[i].bar); | 
 | 			pcicfgw8(p, PciINTL, p->intl); | 
 | 			pcicfgw8(p, PciLTR, p->ltr); | 
 | 			pcicfgw8(p, PciCLS, p->cls); | 
 | 			pcicfgw16(p, PciPCR, p->pcr); | 
 | 		} | 
 | 		#endif | 
 |  | 
 | 		if(rtl8169reset(ctlr)){ | 
 | 			kfree(ctlr); | 
 | 			continue; | 
 | 		} | 
 |  | 
 | 		/* | 
 | 		 * Extract the chip hardware version, | 
 | 		 * needed to configure each properly. | 
 | 		 */ | 
 | 		ctlr->macv = csr32r(ctlr, Tcr) & HwveridMASK; | 
 | 		if((ctlr->mii = rtl8169mii(ctlr)) == NULL){ | 
 | 			kfree(ctlr); | 
 | 			continue; | 
 | 		} | 
 |  | 
 | 		pci_set_bus_master(pcidev); | 
 |  | 
 | 		if(rtl8169ctlrhead != NULL) | 
 | 			rtl8169ctlrtail->next = ctlr; | 
 | 		else | 
 | 			rtl8169ctlrhead = ctlr; | 
 | 		rtl8169ctlrtail = ctlr; | 
 | 	} | 
 | } | 
 |  | 
 | static int | 
 | rtl8169pnp(struct ether* edev) | 
 | { | 
 | 	uint32_t r; | 
 | 	struct ctlr *ctlr; | 
 | 	uint8_t ea[Eaddrlen]; | 
 |  | 
 | 	run_once(rtl8169pci()); | 
 |  | 
 | 	/* | 
 | 	 * Any adapter matches if no edev->port is supplied, | 
 | 	 * otherwise the ports must match. | 
 | 	 */ | 
 | 	for(ctlr = rtl8169ctlrhead; ctlr != NULL; ctlr = ctlr->next){ | 
 | 		if(ctlr->active) | 
 | 			continue; | 
 | 		if(edev->port == 0 || edev->port == ctlr->port){ | 
 | 			ctlr->active = 1; | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | 	if(ctlr == NULL) | 
 | 		return -1; | 
 |  | 
 | 	edev->ctlr = ctlr; | 
 | 	edev->port = ctlr->port; | 
 | 	edev->irq = ctlr->pci->irqline; | 
 | 	edev->netif.mbps = 100; | 
 |  | 
 | 	/* | 
 | 	 * Check if the adapter's station address is to be overridden. | 
 | 	 * If not, read it from the device and set in edev->ea. | 
 | 	 */ | 
 | 	memset(ea, 0, Eaddrlen); | 
 | 	if(memcmp(ea, edev->ea, Eaddrlen) == 0){ | 
 | 		r = csr32r(ctlr, Idr0); | 
 | 		edev->ea[0] = r; | 
 | 		edev->ea[1] = r>>8; | 
 | 		edev->ea[2] = r>>16; | 
 | 		edev->ea[3] = r>>24; | 
 | 		r = csr32r(ctlr, Idr0+4); | 
 | 		edev->ea[4] = r; | 
 | 		edev->ea[5] = r>>8; | 
 | 	} | 
 |  | 
 | 	edev->tbdf = MKBUS(BusPCI, ctlr->pci->bus, ctlr->pci->dev, | 
 | 	                   ctlr->pci->func); | 
 | 	edev->attach = rtl8169attach; | 
 | 	edev->transmit = rtl8169transmit; | 
 | 	edev->ifstat = rtl8169ifstat; | 
 |  | 
 | 	edev->netif.arg = edev; | 
 | 	edev->netif.promiscuous = rtl8169promiscuous; | 
 | 	edev->netif.multicast = rtl8169multicast; | 
 | //	edev->netif.shutdown = rtl8169shutdown; | 
 |  | 
 | 	rtl8169link(edev); | 
 | 	register_irq(edev->irq, rtl8169interrupt, edev, edev->tbdf); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | linker_func_3(ether8169link) | 
 | { | 
 | 	addethercard("rtl8169", rtl8169pnp); | 
 | } |