plan 9 kernel history: overview | file list | diff list

2001/0403/alphapc/ether2114x.c (diff list | history)

alphapc/ether2114x.c on 1999/0422
1999/0422    
/* 
 * Digital Semiconductor DECchip 2114x PCI Fast Ethernet LAN Controller. 
 * To do: 
 *	thresholds; 
 *	ring sizing; 
 *	handle more error conditions; 
 *	tidy setup packet mess; 
 *	push initialisation back to attach; 
 *	full SROM decoding. 
 */ 
#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" 
 
2001/0403    
#define DEBUG		(1) 
1999/0422    
#define debug		if(DEBUG)print 
 
enum { 
	Nrde		= 64, 
	Ntde		= 64, 
}; 
 
2001/0403    
#define Rbsz		ROUNDUP(sizeof(Etherpkt)+4, 4) 
 
1999/0422    
enum {					/* CRS0 - Bus Mode */ 
	Swr		= 0x00000001,	/* Software Reset */ 
	Bar		= 0x00000002,	/* Bus Arbitration */ 
2001/0403    
	DslMASK		= 0x0000007C,	/* Descriptor Skip Length */ 
	DslSHIFT	= 2, 
1999/0422    
	Ble		= 0x00000080,	/* Big/Little Endian */ 
2001/0403    
	PblMASK		= 0x00003F00,	/* Programmable Burst Length (field) */ 
	PblSHIFT	= 8, 
1999/0422    
	Cal		= 0x0000C000,	/* Cache Alignment (field) */ 
	Cal8		= 0x00004000,	/* 8 longword boundary alignment */ 
	Cal16		= 0x00008000,	/* 16 longword boundary alignment */ 
	Cal32		= 0x0000C000,	/* 32 longword boundary alignment */ 
2001/0403    
	TapMASK		= 0x000E0000,	/* Transmit Automatic Polling */ 
	TapSHIFT	= 17, 
1999/0422    
	Dbo		= 0x00100000,	/* Descriptor Byte Ordering Mode */ 
	Rml		= 0x00200000,	/* Read Multiple */ 
};  
 
enum {					/* CSR[57] - Status and Interrupt Enable */ 
	Ti		= 0x00000001,	/* Transmit Interrupt */ 
	Tps		= 0x00000002,	/* Transmit Process Stopped */ 
	Tu		= 0x00000004,	/* Transmit buffer Unavailable */ 
	Tjt		= 0x00000008,	/* Transmit Jabber Timeout */ 
	Unf		= 0x00000020,	/* transmit UNderFlow */ 
	Ri		= 0x00000040,	/* Receive Interrupt */ 
	Ru		= 0x00000080,	/* Receive buffer Unavailable */ 
	Rps		= 0x00000100,	/* Receive Process Stopped */ 
	Rwt		= 0x00000200,	/* Receive Watchdog Timeout */ 
	Eti		= 0x00000400,	/* Early Transmit Interrupt */ 
	Gte		= 0x00000800,	/* General purpose Timer Expired */ 
	Fbe		= 0x00002000,	/* Fatal Bit Error */ 
	Ais		= 0x00008000,	/* Abnormal Interrupt Summary */ 
	Nis		= 0x00010000,	/* Normal Interrupt Summary */ 
	Rs		= 0x000E0000,	/* Receive process State (field) */ 
	Ts		= 0x00700000,	/* Transmit process State (field) */ 
	Eb		= 0x03800000,	/* Error bits */ 
}; 
 
enum {					/* CSR6 - Operating Mode */ 
	Hp		= 0x00000001,	/* Hash/Perfect receive filtering mode */ 
	Sr		= 0x00000002,	/* Start/stop Receive */ 
	Ho		= 0x00000004,	/* Hash-Only filtering mode */ 
	Pb		= 0x00000008,	/* Pass Bad frames */ 
	If		= 0x00000010,	/* Inverse Filtering */ 
	Sb		= 0x00000020,	/* Start/stop Backoff counter */ 
	Pr		= 0x00000040,	/* Promiscuous Mode */ 
	Pm		= 0x00000080,	/* Pass all Multicast */ 
	Fd		= 0x00000200,	/* Full Duplex mode */ 
	Om		= 0x00000C00,	/* Operating Mode (field) */ 
	Fc		= 0x00001000,	/* Force Collision */ 
	St		= 0x00002000,	/* Start/stop Transmission Command */ 
	Tr		= 0x0000C000,	/* ThReshold control bits (field) */ 
	Tr128		= 0x00000000, 
	Tr256		= 0x00004000, 
	Tr512		= 0x00008000, 
	Tr1024		= 0x0000C000, 
	Ca		= 0x00020000,	/* CApture effect enable */ 
	Ps		= 0x00040000,	/* Port Select */ 
	Hbd		= 0x00080000,	/* HeartBeat Disable */ 
	Imm		= 0x00100000,	/* IMMediate mode */ 
	Sf		= 0x00200000,	/* Store and Forward */ 
	Ttm		= 0x00400000,	/* Transmit Threshold Mode */ 
	Pcs		= 0x00800000,	/* PCS function */ 
	Scr		= 0x01000000,	/* SCRambler mode */ 
	Mbo		= 0x02000000,	/* Must Be One */ 
	Ra		= 0x40000000,	/* Receive All */ 
	Sc		= 0x80000000,	/* Special Capture effect enable */ 
 
	TrMODE		= Tr512,	/* default transmission threshold */ 
}; 
 
enum {					/* CSR9 - ROM and MII Management */ 
	Scs		= 0x00000001,	/* serial ROM chip select */ 
	Sclk		= 0x00000002,	/* serial ROM clock */ 
	Sdi		= 0x00000004,	/* serial ROM data in */ 
	Sdo		= 0x00000008,	/* serial ROM data out */ 
	Ss		= 0x00000800,	/* serial ROM select */ 
	Wr		= 0x00002000,	/* write */ 
	Rd		= 0x00004000,	/* read */ 
 
	Mdc		= 0x00010000,	/* MII management clock */ 
	Mdo		= 0x00020000,	/* MII management write data */ 
	Mii		= 0x00040000,	/* MII management operation mode (W) */ 
	Mdi		= 0x00080000,	/* MII management data in */ 
}; 
 
enum {					/* CSR12 - General-Purpose Port */ 
	Gpc		= 0x00000100,	/* General Purpose Control */ 
}; 
 
typedef struct Des { 
	int	status; 
	int	control; 
	ulong	addr; 
	Block*	bp; 
} Des; 
 
enum {					/* status */ 
	Of		= 0x00000001,	/* Rx: OverFlow */ 
	Ce		= 0x00000002,	/* Rx: CRC Error */ 
	Db		= 0x00000004,	/* Rx: Dribbling Bit */ 
	Re		= 0x00000008,	/* Rx: Report on MII Error */ 
	Rw		= 0x00000010,	/* Rx: Receive Watchdog */ 
	Ft		= 0x00000020,	/* Rx: Frame Type */ 
	Cs		= 0x00000040,	/* Rx: Collision Seen */ 
	Tl		= 0x00000080,	/* Rx: Frame too Long */ 
	Ls		= 0x00000100,	/* Rx: Last deScriptor */ 
	Fs		= 0x00000200,	/* Rx: First deScriptor */ 
	Mf		= 0x00000400,	/* Rx: Multicast Frame */ 
	Rf		= 0x00000800,	/* Rx: Runt Frame */ 
	Dt		= 0x00003000,	/* Rx: Data Type (field) */ 
	De		= 0x00004000,	/* Rx: Descriptor Error */ 
	Fl		= 0x3FFF0000,	/* Rx: Frame Length (field) */ 
	Ff		= 0x40000000,	/* Rx: Filtering Fail */ 
 
	Def		= 0x00000001,	/* Tx: DEFerred */ 
	Uf		= 0x00000002,	/* Tx: UnderFlow error */ 
	Lf		= 0x00000004,	/* Tx: Link Fail report */ 
	Cc		= 0x00000078,	/* Tx: Collision Count (field) */ 
	Hf		= 0x00000080,	/* Tx: Heartbeat Fail */ 
	Ec		= 0x00000100,	/* Tx: Excessive Collisions */ 
	Lc		= 0x00000200,	/* Tx: Late Collision */ 
	Nc		= 0x00000400,	/* Tx: No Carrier */ 
	Lo		= 0x00000800,	/* Tx: LOss of carrier */ 
	To		= 0x00004000,	/* Tx: Transmission jabber timeOut */ 
 
	Es		= 0x00008000,	/* [RT]x: Error Summary */ 
	Own		= 0x80000000,	/* [RT]x: OWN bit */ 
}; 
 
enum {					/* control */ 
	Bs1		= 0x000007FF,	/* [RT]x: Buffer 1 Size */ 
	Bs2		= 0x003FF800,	/* [RT]x: Buffer 2 Size */ 
 
	Ch		= 0x01000000,	/* [RT]x: second address CHained */ 
	Er		= 0x02000000,	/* [RT]x: End of Ring */ 
 
	Ft0		= 0x00400000,	/* Tx: Filtering Type 0 */ 
	Dpd		= 0x00800000,	/* Tx: Disabled PaDding */ 
	Ac		= 0x04000000,	/* Tx: Add CRC disable */ 
	Set		= 0x08000000,	/* Tx: SETup packet */ 
	Ft1		= 0x10000000,	/* Tx: Filtering Type 1 */ 
	Fseg		= 0x20000000,	/* Tx: First SEGment */ 
	Lseg		= 0x40000000,	/* Tx: Last SEGment */ 
	Ic		= 0x80000000,	/* Tx: Interrupt on Completion */ 
}; 
 
enum {					/* PHY registers */ 
	Bmcr		= 0,		/* Basic Mode Control */ 
	Bmsr		= 1,		/* Basic Mode Status */ 
	Phyidr1		= 2,		/* PHY Identifier #1 */ 
	Phyidr2		= 3,		/* PHY Identifier #2 */ 
	Anar		= 4,		/* Auto-Negotiation Advertisment */ 
	Anlpar		= 5,		/* Auto-Negotiation Link Partner Ability */ 
	Aner		= 6,		/* Auto-Negotiation Expansion */ 
}; 
 
2001/0403    
enum {					/* Variants */ 
	Tulip0		= (0x0009<<16)|0x1011, 
	Tulip3		= (0x0019<<16)|0x1011, 
	Pnic		= (0x0002<<16)|0x11AD, 
	Pnic2		= (0xC115<<16)|0x11AD, 
}; 
 
1999/0422    
typedef struct Ctlr Ctlr; 
typedef struct Ctlr { 
	int	port; 
	Pcidev*	pcidev; 
	Ctlr*	next; 
	int	active; 
2001/0403    
	int	id;			/* (pcidev->did<<16)|pcidev->vid */ 
1999/0422    
 
	uchar	srom[128]; 
	uchar*	sromea;			/* MAC address */ 
	uchar*	leaf; 
	int	sct;			/* selected connection type */ 
	int	k;			/* info block count */ 
	uchar*	infoblock[16]; 
	int	sctk;			/* sct block index */ 
	int	curk;			/* current block index */ 
	uchar*	type5block; 
 
	int	phy[32];		/* logical to physical map */ 
	int	phyreset;		/* reset bitmap */ 
	int	curphyad; 
	int	fdx; 
	int	ttm; 
 
	uchar	fd;			/* option */ 
	int	medium;			/* option */ 
 
	int	csr6;			/* CSR6 - operating mode */ 
	int	mask;			/* CSR[57] - interrupt mask */ 
	int	mbps; 
 
	Lock	lock; 
 
	Des*	rdr;			/* receive descriptor ring */ 
	int	nrdr;			/* size of rdr */ 
	int	rdrx;			/* index into rdr */ 
 
	Lock	tlock; 
	Des*	tdr;			/* transmit descriptor ring */ 
	int	ntdr;			/* size of tdr */ 
	int	tdrh;			/* host index into tdr */ 
	int	tdri;			/* interface index into tdr */ 
	int	ntq;			/* descriptors active */ 
	int	ntqmax; 
	Block*	setupbp; 
 
	ulong	of;			/* receive statistics */ 
	ulong	ce; 
	ulong	cs; 
	ulong	tl; 
	ulong	rf; 
	ulong	de; 
 
	ulong	ru; 
	ulong	rps; 
	ulong	rwt; 
 
	ulong	uf;			/* transmit statistics */ 
	ulong	ec; 
	ulong	lc; 
	ulong	nc; 
	ulong	lo; 
	ulong	to; 
 
	ulong	tps; 
	ulong	tu; 
	ulong	tjt; 
	ulong	unf; 
} Ctlr; 
 
static Ctlr* ctlrhead; 
static Ctlr* ctlrtail; 
 
#define csr32r(c, r)	(inl((c)->port+((r)*8))) 
#define csr32w(c, r, l)	(outl((c)->port+((r)*8), (ulong)(l))) 
 
static void 
promiscuous(void* arg, int on) 
{ 
	Ctlr *ctlr; 
 
	ctlr = ((Ether*)arg)->ctlr; 
	ilock(&ctlr->lock); 
	if(on) 
		ctlr->csr6 |= Pr; 
	else 
		ctlr->csr6 &= ~Pr; 
	csr32w(ctlr, 6, ctlr->csr6); 
	iunlock(&ctlr->lock); 
} 
 
static void 
attach(Ether* ether) 
{ 
	Ctlr *ctlr; 
 
	ctlr = ether->ctlr; 
	ilock(&ctlr->lock); 
	if(!(ctlr->csr6 & Sr)){ 
		ctlr->csr6 |= Sr; 
		csr32w(ctlr, 6, ctlr->csr6); 
	} 
	iunlock(&ctlr->lock); 
} 
 
static long 
ifstat(Ether* ether, void* a, long n, ulong offset) 
{ 
	Ctlr *ctlr; 
	char *buf, *p; 
	int i, l, len; 
 
	ctlr = ether->ctlr; 
 
	ether->crcs = ctlr->ce; 
	ether->frames = ctlr->rf+ctlr->cs; 
	ether->buffs = ctlr->de+ctlr->tl; 
	ether->overflows = ctlr->of; 
 
	if(n == 0) 
		return 0; 
 
	p = malloc(READSTR); 
	l = snprint(p, READSTR, "Overflow: %lud\n", ctlr->of); 
	l += snprint(p+l, READSTR-l, "Ru: %lud\n", ctlr->ru); 
	l += snprint(p+l, READSTR-l, "Rps: %lud\n", ctlr->rps); 
	l += snprint(p+l, READSTR-l, "Rwt: %lud\n", ctlr->rwt); 
	l += snprint(p+l, READSTR-l, "Tps: %lud\n", ctlr->tps); 
	l += snprint(p+l, READSTR-l, "Tu: %lud\n", ctlr->tu); 
	l += snprint(p+l, READSTR-l, "Tjt: %lud\n", ctlr->tjt); 
	l += snprint(p+l, READSTR-l, "Unf: %lud\n", ctlr->unf); 
	l += snprint(p+l, READSTR-l, "CRC Error: %lud\n", ctlr->ce); 
	l += snprint(p+l, READSTR-l, "Collision Seen: %lud\n", ctlr->cs); 
	l += snprint(p+l, READSTR-l, "Frame Too Long: %lud\n", ctlr->tl); 
	l += snprint(p+l, READSTR-l, "Runt Frame: %lud\n", ctlr->rf); 
	l += snprint(p+l, READSTR-l, "Descriptor Error: %lud\n", ctlr->de); 
	l += snprint(p+l, READSTR-l, "Underflow Error: %lud\n", ctlr->uf); 
	l += snprint(p+l, READSTR-l, "Excessive Collisions: %lud\n", ctlr->ec); 
	l += snprint(p+l, READSTR-l, "Late Collision: %lud\n", ctlr->lc); 
	l += snprint(p+l, READSTR-l, "No Carrier: %lud\n", ctlr->nc); 
	l += snprint(p+l, READSTR-l, "Loss of Carrier: %lud\n", ctlr->lo); 
	l += snprint(p+l, READSTR-l, "Transmit Jabber Timeout: %lud\n", 
		ctlr->to); 
	l += snprint(p+l, READSTR-l, "csr6: %luX %uX\n", csr32r(ctlr, 6), 
		ctlr->csr6); 
	snprint(p+l, READSTR-l, "ntqmax: %d\n", ctlr->ntqmax); 
	ctlr->ntqmax = 0; 
	buf = a; 
	len = readstr(offset, buf, n, p); 
	if(offset > l) 
		offset -= l; 
	else 
		offset = 0; 
	buf += len; 
	n -= len; 
 
	l = snprint(p, READSTR, "srom:"); 
	for(i = 0; i < sizeof(ctlr->srom); i++){ 
		if(i && ((i & 0x0F) == 0)) 
			l += snprint(p+l, READSTR-l, "\n     "); 
		l += snprint(p+l, READSTR-l, " %2.2uX", ctlr->srom[i]); 
	} 
 
	snprint(p+l, READSTR-l, "\n"); 
	len += readstr(offset, buf, n, p); 
	free(p); 
 
	return len; 
} 
 
static void 
txstart(Ether* ether) 
{ 
	Ctlr *ctlr; 
	Block *bp; 
	Des *des; 
	int control; 
 
	ctlr = ether->ctlr; 
	while(ctlr->ntq < (ctlr->ntdr-1)){ 
		if(ctlr->setupbp){ 
			bp = ctlr->setupbp; 
			ctlr->setupbp = 0; 
			control = Ic|Set|BLEN(bp); 
		} 
		else{ 
			bp = qget(ether->oq); 
			if(bp == nil) 
				break; 
			control = Ic|Lseg|Fseg|BLEN(bp); 
		} 
 
		ctlr->tdr[PREV(ctlr->tdrh, ctlr->ntdr)].control &= ~Ic; 
		des = &ctlr->tdr[ctlr->tdrh]; 
		des->bp = bp; 
1999/0423    
		des->addr = PCIWADDR(bp->rp); 
1999/0422    
		des->control |= control; 
		ctlr->ntq++; 
		coherence(); 
		des->status = Own; 
		csr32w(ctlr, 1, 0); 
		ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr); 
	} 
 
	if(ctlr->ntq > ctlr->ntqmax) 
		ctlr->ntqmax = ctlr->ntq; 
} 
 
static void 
transmit(Ether* ether) 
{ 
	Ctlr *ctlr; 
 
	ctlr = ether->ctlr; 
	ilock(&ctlr->tlock); 
	txstart(ether); 
	iunlock(&ctlr->tlock); 
} 
 
static void 
interrupt(Ureg*, void* arg) 
{ 
	Ctlr *ctlr; 
	Ether *ether; 
	int len, status; 
	Des *des; 
	Block *bp; 
 
	ether = arg; 
	ctlr = ether->ctlr; 
 
	while((status = csr32r(ctlr, 5)) & (Nis|Ais)){ 
		/* 
		 * Acknowledge the interrupts and mask-out 
		 * the ones that are implicitly handled. 
		 */ 
		csr32w(ctlr, 5, status); 
		status &= (ctlr->mask & ~(Nis|Ti)); 
 
		if(status & Ais){ 
			if(status & Tps) 
				ctlr->tps++; 
			if(status & Tu) 
				ctlr->tu++; 
			if(status & Tjt) 
				ctlr->tjt++; 
			if(status & Ru) 
				ctlr->ru++; 
			if(status & Rps) 
				ctlr->rps++; 
			if(status & Rwt) 
				ctlr->rwt++; 
			status &= ~(Ais|Rwt|Rps|Ru|Tjt|Tu|Tps); 
		} 
 
		/* 
		 * Received packets. 
		 */ 
		if(status & Ri){ 
			des = &ctlr->rdr[ctlr->rdrx]; 
			while(!(des->status & Own)){ 
				if(des->status & Es){ 
					if(des->status & Of) 
						ctlr->of++; 
					if(des->status & Ce) 
						ctlr->ce++; 
					if(des->status & Cs) 
						ctlr->cs++; 
					if(des->status & Tl) 
						ctlr->tl++; 
					if(des->status & Rf) 
						ctlr->rf++; 
					if(des->status & De) 
						ctlr->de++; 
				} 
2001/0403    
				else if(bp = iallocb(Rbsz)){ 
1999/0422    
					len = ((des->status & Fl)>>16)-4; 
					des->bp->wp = des->bp->rp+len; 
					etheriq(ether, des->bp, 1); 
					des->bp = bp; 
1999/0423    
					des->addr = PCIWADDR(bp->rp); 
1999/0422    
				} 
 
				des->control &= Er; 
2001/0403    
				des->control |= Rbsz; 
1999/0422    
				coherence(); 
				des->status = Own; 
 
				ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr); 
				des = &ctlr->rdr[ctlr->rdrx]; 
			} 
			status &= ~Ri; 
		} 
 
		/* 
		 * Check the transmit side: 
		 *	check for Transmit Underflow and Adjust 
		 *	the threshold upwards; 
		 *	free any transmitted buffers and try to 
		 *	top-up the ring. 
		 */ 
		if(status & Unf){ 
			ctlr->unf++; 
			ilock(&ctlr->lock); 
			csr32w(ctlr, 6, ctlr->csr6 & ~St); 
			switch(ctlr->csr6 & Tr){ 
			case Tr128: 
				len = Tr256; 
				break; 
			case Tr256: 
				len = Tr512; 
				break; 
			case Tr512: 
				len = Tr1024; 
				break; 
			default: 
			case Tr1024: 
				len = Sf; 
				break; 
			} 
			ctlr->csr6 = (ctlr->csr6 & ~Tr)|len; 
			csr32w(ctlr, 6, ctlr->csr6); 
			iunlock(&ctlr->lock); 
			csr32w(ctlr, 5, Tps); 
			status &= ~(Unf|Tps); 
		} 
 
		ilock(&ctlr->tlock); 
		while(ctlr->ntq){ 
			des = &ctlr->tdr[ctlr->tdri]; 
			if(des->status & Own) 
				break; 
 
			if(des->status & Es){ 
				if(des->status & Uf) 
					ctlr->uf++; 
				if(des->status & Ec) 
					ctlr->ec++; 
				if(des->status & Lc) 
					ctlr->lc++; 
				if(des->status & Nc) 
					ctlr->nc++; 
				if(des->status & Lo) 
					ctlr->lo++; 
				if(des->status & To) 
					ctlr->to++; 
				ether->oerrs++; 
			} 
 
			freeb(des->bp); 
			des->control &= Er; 
 
			ctlr->ntq--; 
			ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr); 
		} 
		txstart(ether); 
		iunlock(&ctlr->tlock); 
 
		/* 
		 * Anything left not catered for? 
		 */ 
		if(status) 
			panic("#l%d: status %8.8uX\n", ether->ctlrno, status); 
	} 
} 
 
static void 
ctlrinit(Ether* ether) 
{ 
	Ctlr *ctlr; 
	Des *des; 
	Block *bp; 
	int i; 
	uchar bi[Eaddrlen*2]; 
 
	ctlr = ether->ctlr; 
 
	/* 
	 * Allocate and initialise the receive ring; 
	 * allocate and initialise the transmit ring; 
	 * unmask interrupts and start the transmit side; 
	 * create and post a setup packet to initialise 
	 * the physical ethernet address. 
	 */ 
2001/0403    
	ctlr->rdr = malloc(ctlr->nrdr*sizeof(Des)); 
1999/0422    
	for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){ 
2001/0403    
		des->bp = allocb(Rbsz); 
1999/0422    
		des->status = Own; 
2001/0403    
		des->control = Rbsz; 
1999/0423    
		des->addr = PCIWADDR(des->bp->rp); 
1999/0422    
	} 
	ctlr->rdr[ctlr->nrdr-1].control |= Er; 
	ctlr->rdrx = 0; 
1999/0423    
	csr32w(ctlr, 3, PCIWADDR(ctlr->rdr)); 
1999/0422    
 
	ctlr->tdr = xspanalloc(ctlr->ntdr*sizeof(Des), 8*sizeof(ulong), 0); 
	ctlr->tdr[ctlr->ntdr-1].control |= Er; 
	ctlr->tdrh = 0; 
	ctlr->tdri = 0; 
1999/0423    
	csr32w(ctlr, 4, PCIWADDR(ctlr->tdr)); 
1999/0422    
 
2001/0403    
	/* 
	 * Clear any bits in the Status Register (CSR5) as 
	 * the PNIC has a different reset value from a true 2114x. 
	 */ 
1999/0422    
	ctlr->mask = Nis|Ais|Fbe|Rwt|Rps|Ru|Ri|Unf|Tjt|Tps|Ti; 
2001/0403    
	csr32w(ctlr, 5, ctlr->mask); 
1999/0422    
	csr32w(ctlr, 7, ctlr->mask); 
	ctlr->csr6 |= St; 
	csr32w(ctlr, 6, ctlr->csr6); 
 
	for(i = 0; i < Eaddrlen/2; i++){ 
		bi[i*4] = ether->ea[i*2]; 
		bi[i*4+1] = ether->ea[i*2+1]; 
		bi[i*4+2] = ether->ea[i*2+1]; 
		bi[i*4+3] = ether->ea[i*2]; 
	} 
	bp = allocb(Eaddrlen*2*16); 
	memset(bp->rp, 0xFF, sizeof(bi)); 
	for(i = sizeof(bi); i < sizeof(bi)*16; i += sizeof(bi)) 
		memmove(bp->rp+i, bi, sizeof(bi)); 
	bp->wp += sizeof(bi)*16; 
 
	ctlr->setupbp = bp; 
	ether->oq = qopen(256*1024, 1, 0, 0); 
	transmit(ether); 
} 
 
static void 
csr9w(Ctlr* ctlr, int data) 
{ 
	csr32w(ctlr, 9, data); 
	microdelay(1); 
} 
 
static int 
miimdi(Ctlr* ctlr, int n) 
{ 
	int data, i; 
 
	/* 
	 * Read n bits from the MII Management Register. 
	 */ 
	data = 0; 
	for(i = n-1; i >= 0; i--){ 
		if(csr32r(ctlr, 9) & Mdi) 
			data |= (1<<i); 
		csr9w(ctlr, Mii|Mdc); 
		csr9w(ctlr, Mii); 
	} 
	csr9w(ctlr, 0); 
 
	return data; 
} 
 
static void 
miimdo(Ctlr* ctlr, int bits, int n) 
{ 
	int i, mdo; 
 
	/* 
	 * Write n bits to the MII Management Register. 
	 */ 
	for(i = n-1; i >= 0; i--){ 
		if(bits & (1<<i)) 
			mdo = Mdo; 
		else 
			mdo = 0; 
		csr9w(ctlr, mdo); 
		csr9w(ctlr, mdo|Mdc); 
		csr9w(ctlr, mdo); 
	} 
} 
 
static int 
miir(Ctlr* ctlr, int phyad, int regad) 
{ 
2001/0403    
	int data, i; 
1999/0422    
 
2001/0403    
	if(ctlr->id == Pnic){ 
		i = 1000; 
		csr32w(ctlr, 20, 0x60020000|(phyad<<23)|(regad<<18)); 
		do{ 
			microdelay(1); 
			data = csr32r(ctlr, 20); 
		}while((data & 0x80000000) && --i); 
 
		if(i == 0) 
			return -1; 
		return data & 0xFFFF; 
	} 
 
1999/0422    
	/* 
	 * Preamble; 
	 * ST+OP+PHYAD+REGAD; 
	 * TA + 16 data bits. 
	 */ 
	miimdo(ctlr, 0xFFFFFFFF, 32); 
	miimdo(ctlr, 0x1800|(phyad<<5)|regad, 14); 
	data = miimdi(ctlr, 18); 
 
	if(data & 0x10000) 
		return -1; 
 
	return data & 0xFFFF; 
} 
 
static void 
miiw(Ctlr* ctlr, int phyad, int regad, int data) 
{ 
	/* 
	 * Preamble; 
	 * ST+OP+PHYAD+REGAD+TA + 16 data bits; 
	 * Z. 
	 */ 
	miimdo(ctlr, 0xFFFFFFFF, 32); 
	data &= 0xFFFF; 
	data |= (0x05<<(5+5+2+16))|(phyad<<(5+2+16))|(regad<<(2+16))|(0x02<<16); 
	miimdo(ctlr, data, 32); 
	csr9w(ctlr, Mdc); 
	csr9w(ctlr, 0); 
} 
 
static int 
sromr(Ctlr* ctlr, int r) 
{ 
	int i, op, data; 
 
2001/0403    
	if(ctlr->id == Pnic){ 
		i = 1000; 
		csr32w(ctlr, 19, 0x600|r); 
		do{ 
			microdelay(1); 
			data = csr32r(ctlr, 19); 
		}while((data & 0x80000000) && --i); 
 
		return csr32r(ctlr, 9) & 0xFFFF; 
	} 
 
1999/0422    
	/* 
	 * This sequence for reading a 16-bit register 'r' 
	 * in the EEPROM is taken straight from Section 
	 * 7.4 of the 21140 Hardware Reference Manual. 
	 */ 
	csr9w(ctlr, Rd|Ss); 
	csr9w(ctlr, Rd|Ss|Scs); 
	csr9w(ctlr, Rd|Ss|Sclk|Scs); 
	csr9w(ctlr, Rd|Ss); 
 
	op = 0x06; 
	for(i = 3-1; i >= 0; i--){ 
		data = Rd|Ss|(((op>>i) & 0x01)<<2)|Scs; 
		csr9w(ctlr, data); 
		csr9w(ctlr, data|Sclk); 
		csr9w(ctlr, data); 
	} 
 
	for(i = 6-1; i >= 0; i--){ 
		data = Rd|Ss|(((r>>i) & 0x01)<<2)|Scs; 
		csr9w(ctlr, data); 
		csr9w(ctlr, data|Sclk); 
		csr9w(ctlr, data); 
	} 
 
	data = 0; 
	for(i = 16-1; i >= 0; i--){ 
		csr9w(ctlr, Rd|Ss|Sclk|Scs); 
		if(csr32r(ctlr, 9) & Sdo) 
			data |= (1<<i); 
		csr9w(ctlr, Rd|Ss|Scs); 
	} 
 
	csr9w(ctlr, 0); 
 
	return data & 0xFFFF; 
} 
 
static void 
softreset(Ctlr* ctlr) 
{ 
	/* 
	 * Soft-reset the controller and initialise bus mode. 
	 * Delay should be >= 50 PCI cycles (2×S @ 25MHz). 
	 */ 
	csr32w(ctlr, 0, Swr); 
	microdelay(10); 
2001/0403    
	csr32w(ctlr, 0, Rml|Cal16|(32<<PblSHIFT)); 
1999/0422    
	delay(1); 
} 
 
static int 
type5block(Ctlr* ctlr, uchar* block) 
{ 
	int csr15, i, len; 
 
	/* 
	 * Reset or GPR sequence. Reset should be once only, 
	 * before the GPR sequence. 
	 * Note 'block' is not a pointer to the block head but 
	 * a pointer to the data in the block starting at the 
	 * reset length value so type5block can be used for the 
	 * sequences contained in type 1 and type 3 blocks. 
	 * The SROM docs state the 21140 type 5 block is the 
	 * same as that for the 21143, but the two controllers 
	 * use different registers and sequence-element lengths 
	 * so the 21140 code here is a guess for a real type 5 
	 * sequence. 
	 */ 
	len = *block++; 
2001/0403    
	if(ctlr->id != Tulip3){ 
1999/0422    
		for(i = 0; i < len; i++){ 
			csr32w(ctlr, 12, *block); 
			block++; 
		} 
		return len; 
	} 
 
	for(i = 0; i < len; i++){ 
		csr15 = *block++<<16; 
		csr15 |= *block++<<24; 
		csr32w(ctlr, 15, csr15); 
		debug("%8.8uX ", csr15); 
	} 
	return 2*len; 
} 
 
static int 
typephylink(Ctlr* ctlr, uchar*) 
{ 
	int an, bmcr, bmsr, csr6, x; 
 
	/* 
	 * Fail if 
	 *	auto-negotiataion enabled but not complete; 
	 *	no valid link established. 
	 */ 
	bmcr = miir(ctlr, ctlr->curphyad, Bmcr); 
	miir(ctlr, ctlr->curphyad, Bmsr); 
	bmsr = miir(ctlr, ctlr->curphyad, Bmsr); 
	debug("bmcr 0x%2.2uX bmsr 0x%2.2uX\n", bmcr, bmsr); 
	if(((bmcr & 0x1000) && !(bmsr & 0x0020)) || !(bmsr & 0x0004)) 
		return 0; 
 
	if(bmcr & 0x1000){ 
		an = miir(ctlr, ctlr->curphyad, Anar); 
		an &= miir(ctlr, ctlr->curphyad, Anlpar) & 0x3E0; 
		debug("an 0x%2.uX 0x%2.2uX 0x%2.2uX\n", 
	    		miir(ctlr, ctlr->curphyad, Anar), 
			miir(ctlr, ctlr->curphyad, Anlpar), 
			an); 
	 
		if(an & 0x0100) 
			x = 0x4000; 
		else if(an & 0x0080) 
			x = 0x2000; 
		else if(an & 0x0040) 
			x = 0x1000; 
		else if(an & 0x0020) 
			x = 0x0800; 
		else 
			x = 0; 
	} 
	else if((bmcr & 0x2100) == 0x2100) 
		x = 0x4000; 
2001/0403    
	else if(bmcr & 0x2000){ 
		/* 
		 * If FD capable, force it if necessary. 
		 */ 
		if((bmsr & 0x4000) && ctlr->fd){ 
			miiw(ctlr, ctlr->curphyad, Bmcr, 0x2100); 
			x = 0x4000; 
		} 
		else 
			x = 0x2000; 
	} 
1999/0422    
	else if(bmcr & 0x0100) 
		x = 0x1000; 
	else 
		x = 0x0800; 
 
	csr6 = Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE; 
	if(ctlr->fdx & x) 
		csr6 |= Fd; 
	if(ctlr->ttm & x) 
		csr6 |= Ttm; 
	debug("csr6 0x%8.8uX 0x%8.8uX 0x%8.8luX\n", 
		csr6, ctlr->csr6, csr32r(ctlr, 6)); 
	if(csr6 != ctlr->csr6){ 
		ctlr->csr6 = csr6; 
		csr32w(ctlr, 6, csr6); 
	} 
 
	return 1; 
} 
 
static int 
typephymode(Ctlr* ctlr, uchar* block, int wait) 
{ 
	uchar *p; 
	int len, mc, nway, phyx, timeo; 
 
	if(DEBUG){ 
		int i; 
 
		len = (block[0] & ~0x80)+1; 
		for(i = 0; i < len; i++) 
			debug("%2.2uX ", block[i]); 
		debug("\n"); 
	} 
 
	if(block[1] == 1) 
		len = 1; 
	else if(block[1] == 3) 
		len = 2; 
	else 
		return -1; 
 
	/* 
	 * Snarf the media capabilities, nway advertisment, 
	 * FDX and TTM bitmaps. 
	 */ 
	p = &block[5+len*block[3]+len*block[4+len*block[3]]]; 
	mc = *p++; 
	mc |= *p++<<8; 
	nway = *p++; 
	nway |= *p++<<8; 
	ctlr->fdx = *p++; 
	ctlr->fdx |= *p++<<8; 
	ctlr->ttm = *p++; 
	ctlr->ttm |= *p<<8; 
	debug("mc %4.4uX nway %4.4uX fdx %4.4uX ttm %4.4uX\n", 
		mc, nway, ctlr->fdx, ctlr->ttm); 
	USED(mc); 
 
	phyx = block[2]; 
	ctlr->curphyad = ctlr->phy[phyx]; 
 
	ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE; 
	//csr32w(ctlr, 6, ctlr->csr6); 
	if(typephylink(ctlr, block)) 
		return 0; 
 
	if(!(ctlr->phyreset & (1<<phyx))){ 
		debug("reset seq: len %d: ", block[3]); 
		if(ctlr->type5block) 
			type5block(ctlr, &ctlr->type5block[2]); 
		else 
			type5block(ctlr, &block[4+len*block[3]]); 
		debug("\n"); 
		ctlr->phyreset |= (1<<phyx); 
	} 
 
	/* 
	 * GPR sequence. 
	 */ 
	debug("gpr seq: len %d: ", block[3]); 
	type5block(ctlr, &block[3]); 
	debug("\n"); 
 
	ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE; 
	//csr32w(ctlr, 6, ctlr->csr6); 
	if(typephylink(ctlr, block)) 
		return 0; 
 
	/* 
	 * Turn off auto-negotiation, set the auto-negotiation 
	 * advertisment register then start the auto-negotiation 
	 * process again. 
	 */ 
	miiw(ctlr, ctlr->curphyad, Bmcr, 0); 
	miiw(ctlr, ctlr->curphyad, Anar, nway|1); 
	miiw(ctlr, ctlr->curphyad, Bmcr, 0x1000); 
 
	if(!wait) 
		return 0; 
 
	for(timeo = 0; timeo < 30; timeo++){ 
		if(typephylink(ctlr, block)) 
			return 0; 
		delay(100); 
	} 
 
	return -1; 
} 
 
static int 
type0link(Ctlr* ctlr, uchar* block) 
{ 
	int m, polarity, sense; 
 
	m = (block[3]<<8)|block[2]; 
	sense = 1<<((m & 0x000E)>>1); 
	if(m & 0x0080) 
		polarity = sense; 
	else 
		polarity = 0; 
 
	return (csr32r(ctlr, 12) & sense)^polarity; 
} 
 
static int 
type0mode(Ctlr* ctlr, uchar* block, int wait) 
{ 
	int csr6, m, timeo; 
 
2001/0403    
//	csr6 = Sc|Mbo|Hbd|Ca|Sb|TrMODE; 
	csr6 = Mbo|Hbd|Sb|TrMODE; 
1999/0422    
debug("type0: medium 0x%uX, fd %d: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n", 
    ctlr->medium, ctlr->fd, block[0], block[1], block[2], block[3]);  
	switch(block[0]){ 
	default: 
		break; 
 
	case 0x04:			/* 10BASE-TFD */ 
	case 0x05:			/* 100BASE-TXFD */ 
	case 0x08:			/* 100BASE-FXFD */ 
		/* 
		 * Don't attempt full-duplex 
		 * unless explicitly requested. 
		 */ 
		if(!ctlr->fd) 
			return -1; 
		csr6 |= Fd; 
		break; 
	} 
 
	m = (block[3]<<8)|block[2]; 
	if(m & 0x0001) 
		csr6 |= Ps; 
	if(m & 0x0010) 
		csr6 |= Ttm; 
	if(m & 0x0020) 
		csr6 |= Pcs; 
	if(m & 0x0040) 
		csr6 |= Scr; 
 
	csr32w(ctlr, 12, block[1]); 
	microdelay(10); 
	csr32w(ctlr, 6, csr6); 
	ctlr->csr6 = csr6; 
 
	if(!wait) 
		return 0; 
 
	for(timeo = 0; timeo < 30; timeo++){ 
		if(type0link(ctlr, block)) 
			return 0; 
		delay(100); 
	} 
 
	return -1; 
} 
 
static int 
mediaxx(Ether* ether, int wait) 
{ 
	Ctlr* ctlr; 
	uchar *block; 
 
	ctlr = ether->ctlr; 
	block = ctlr->infoblock[ctlr->curk]; 
	if(block[0] & 0x80){ 
		switch(block[1]){ 
		default: 
			return -1; 
		case 0: 
			if(ctlr->medium >= 0 && block[2] != ctlr->medium) 
				return 0; 
/* need this test? */	if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[2]) 
				return 0; 
			if(type0mode(ctlr, block+2, wait)) 
				return 0; 
			break; 
		case 1: 
			if(typephymode(ctlr, block, wait)) 
				return 0; 
			break; 
		case 3: 
			if(typephymode(ctlr, block, wait)) 
				return 0; 
			break; 
		} 
	} 
	else{ 
		if(ctlr->medium >= 0 && block[0] != ctlr->medium) 
			return 0; 
/* need this test? */if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[0]) 
			return 0; 
		if(type0mode(ctlr, block, wait)) 
			return 0; 
	} 
 
	if(ctlr->csr6){ 
		if(!(ctlr->csr6 & Ps) || (ctlr->csr6 & Ttm)) 
			return 10; 
		return 100; 
	} 
 
	return 0; 
} 
 
static int 
media(Ether* ether, int wait) 
{ 
	Ctlr* ctlr; 
	int k, mbps; 
 
	ctlr = ether->ctlr; 
	for(k = 0; k < ctlr->k; k++){ 
		mbps = mediaxx(ether, wait); 
		if(mbps > 0) 
			return mbps; 
		if(ctlr->curk == 0) 
			ctlr->curk = ctlr->k-1; 
		else 
			ctlr->curk--; 
	} 
 
	return 0; 
} 
 
static char* mediatable[9] = { 
	"10BASE-T",				/* TP */ 
	"10BASE-2",				/* BNC */ 
	"10BASE-5",				/* AUI */ 
	"100BASE-TX", 
	"10BASE-TFD", 
	"100BASE-TXFD", 
	"100BASE-T4", 
	"100BASE-FX", 
	"100BASE-FXFD", 
}; 
 
static uchar en1207[] = {		/* Accton EN1207-COMBO */ 
	0x00, 0x00, 0xE8,		/* [0]  vendor ethernet code */ 
	0x00,				/* [3]  spare */ 
 
	0x00, 0x08,			/* [4]  connection (LSB+MSB = 0x0800) */ 
	0x1F,				/* [6]  general purpose control */ 
	2,				/* [7]  block count */ 
 
	0x00,				/* [8]  media code (10BASE-TX) */ 
	0x0B,				/* [9]  general purpose port data */ 
	0x9E, 0x00,			/* [10] command (LSB+MSB = 0x009E) */ 
 
	0x03,				/* [8]  media code (100BASE-TX) */ 
	0x1B,				/* [9]  general purpose port data */ 
	0x6D, 0x00,			/* [10] command (LSB+MSB = 0x006D) */ 
 
					/* There is 10BASE-2 as well, but... */ 
}; 
 
static uchar ana6910fx[] = {		/* Adaptec (Cogent) ANA-6910FX */ 
	0x00, 0x00, 0x92,		/* [0]  vendor ethernet code */ 
	0x00,				/* [3]  spare */ 
 
	0x00, 0x08,			/* [4]  connection (LSB+MSB = 0x0800) */ 
	0x3F,				/* [6]  general purpose control */ 
	1,				/* [7]  block count */ 
 
	0x07,				/* [8]  media code (100BASE-FX) */ 
	0x03,				/* [9]  general purpose port data */ 
	0x2D, 0x00			/* [10] command (LSB+MSB = 0x000D) */ 
}; 
 
static uchar smc9332[] = {		/* SMC 9332 */ 
	0x00, 0x00, 0xC0,		/* [0]  vendor ethernet code */ 
	0x00,				/* [3]  spare */ 
 
	0x00, 0x08,			/* [4]  connection (LSB+MSB = 0x0800) */ 
	0x1F,				/* [6]  general purpose control */ 
	2,				/* [7]  block count */ 
 
	0x00,				/* [8]  media code (10BASE-TX) */ 
	0x00,				/* [9]  general purpose port data */ 
	0x9E, 0x00,			/* [10] command (LSB+MSB = 0x009E) */ 
 
	0x03,				/* [8]  media code (100BASE-TX) */ 
	0x09,				/* [9]  general purpose port data */ 
	0x6D, 0x00,			/* [10] command (LSB+MSB = 0x006D) */ 
}; 
 
static uchar* leaf21140[] = { 
	en1207,				/* Accton EN1207-COMBO */ 
	ana6910fx,			/* Adaptec (Cogent) ANA-6910FX */ 
	smc9332,			/* SMC 9332 */ 
	nil, 
}; 
 
2001/0403    
/* 
 * Copied to ctlr->srom at offset 20. 
 */ 
static uchar leafpnic[] = { 
	0x00, 0x00, 0x00, 0x00,		/* MAC address */ 
	0x00, 0x00, 
	0x00,				/* controller 0 device number */ 
	0x1E, 0x00,			/* controller 0 info leaf offset */ 
	0x00,				/* reserved */ 
	0x00, 0x08,			/* selected connection type */ 
	0x00,				/* general purpose control */ 
	0x01,				/* block count */ 
 
	0x8C,				/* format indicator and count */ 
	0x01,				/* block type */ 
	0x00,				/* PHY number */ 
	0x00,				/* GPR sequence length */ 
	0x00,				/* reset sequence length */ 
	0x00, 0x78,			/* media capabilities */ 
	0xE0, 0x01,			/* Nway advertisment */ 
	0x00, 0x50,			/* FDX bitmap */ 
	0x00, 0x18,			/* TTM bitmap */ 
}; 
 
1999/0422    
static int 
srom(Ctlr* ctlr) 
{ 
	int i, k, oui, phy, x; 
	uchar *p; 
 
	/* 
	 * This is a partial decoding of the SROM format described in 
	 * 'Digital Semiconductor 21X4 Serial ROM Format, Version 4.05, 
	 * 2-Mar-98'. Only the 2114[03] are handled, support for other 
	 * controllers can be added as needed. 
	 */ 
	for(i = 0; i < sizeof(ctlr->srom)/2; i++){ 
		x = sromr(ctlr, i); 
		ctlr->srom[2*i] = x; 
		ctlr->srom[2*i+1] = x>>8; 
	} 
 
	/* 
	 * There are 2 SROM layouts: 
	 *	e.g. Digital EtherWORKS	station address at offset 20; 
	 *				this complies with the 21140A SROM 
	 *				application note from Digital; 
	 * 	e.g. SMC9332		station address at offset 0 followed by 
	 *				2 additional bytes, repeated at offset 
	 *				6; the 8 bytes are also repeated in 
	 *				reverse order at offset 8. 
	 * To check which it is, read the SROM and check for the repeating 
	 * patterns of the non-compliant cards; if that fails use the one at 
	 * offset 20. 
	 */ 
	ctlr->sromea = ctlr->srom; 
	for(i = 0; i < 8; i++){ 
		x = ctlr->srom[i]; 
		if(x != ctlr->srom[15-i] || x != ctlr->srom[16+i]){ 
			ctlr->sromea = &ctlr->srom[20]; 
			break; 
		} 
	} 
 
	/* 
2001/0403    
	 * Fake up the SROM for the PNIC. 
	 * It looks like a 21140 with a PHY. 
	 * The MAC address is byte-swapped in the orginal SROM data. 
	 */ 
	if(ctlr->id == Pnic){ 
		memmove(&ctlr->srom[20], leafpnic, sizeof(leafpnic)); 
		for(i = 0; i < Eaddrlen; i += 2){ 
			ctlr->srom[20+i] = ctlr->srom[i+1]; 
			ctlr->srom[20+i+1] = ctlr->srom[i]; 
		} 
	} 
 
	/* 
1999/0422    
	 * Next, try to find the info leaf in the SROM for media detection. 
	 * If it's a non-conforming card try to match the vendor ethernet code 
	 * and point p at a fake info leaf with compact 21140 entries. 
	 */ 
	if(ctlr->sromea == ctlr->srom){ 
		p = nil; 
		for(i = 0; leaf21140[i] != nil; i++){ 
			if(memcmp(leaf21140[i], ctlr->sromea, 3) == 0){ 
				p = &leaf21140[i][4]; 
				break; 
			} 
		} 
		if(p == nil) 
			return -1; 
	} 
	else 
		p = &ctlr->srom[(ctlr->srom[28]<<8)|ctlr->srom[27]]; 
 
	/* 
	 * Set up the info needed for later media detection. 
	 * For the 21140, set the general-purpose mask in CSR12. 
	 * The info block entries are stored in order of increasing 
	 * precedence, so detection will work backwards through the 
	 * stored indexes into ctlr->srom. 
	 * If an entry is found which matches the selected connection 
	 * type, save the index. Otherwise, start at the last entry. 
	 * If any MII entries are found (type 1 and 3 blocks), scan 
	 * for PHYs. 
	 */ 
	ctlr->leaf = p; 
	ctlr->sct = *p++; 
	ctlr->sct |= *p++<<8; 
2001/0403    
	if(ctlr->id != Tulip3){ 
1999/0422    
		csr32w(ctlr, 12, Gpc|*p++); 
		delay(200); 
	} 
	ctlr->k = *p++; 
	if(ctlr->k >= nelem(ctlr->infoblock)) 
		ctlr->k = nelem(ctlr->infoblock)-1; 
	ctlr->sctk = ctlr->k-1; 
	phy = 0; 
	for(k = 0; k < ctlr->k; k++){ 
		ctlr->infoblock[k] = p; 
		/* 
		 * The RAMIX PMC665 has a badly-coded SROM, 
		 * hence the test for 21143 and type 3. 
		 */ 
2001/0403    
		if((*p & 0x80) || (ctlr->id == Tulip3 && *(p+1) == 3)){ 
1999/0422    
			*p |= 0x80; 
			if(*(p+1) == 1 || *(p+1) == 3) 
				phy = 1; 
			if(*(p+1) == 5) 
				ctlr->type5block = p; 
			p += (*p & ~0x80)+1; 
		} 
		else{ 
			debug("type0: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n", 
				p[0], p[1], p[2], p[3]);  
			if(ctlr->sct != 0x0800 && *p == (ctlr->sct & 0xFF)) 
				ctlr->sctk = k; 
			p += 4; 
		} 
	} 
	ctlr->curk = ctlr->sctk; 
	debug("sct 0x%uX medium 0x%uX k %d curk %d phy %d\n", 
		ctlr->sct, ctlr->medium, ctlr->k, ctlr->curk, phy); 
 
	if(phy){ 
		x = 0; 
		for(k = 0; k < nelem(ctlr->phy); k++){ 
			if((oui = miir(ctlr, k, 2)) == -1 || oui == 0) 
				continue; 
			if(DEBUG){ 
				oui = (oui & 0x3FF)<<6; 
				oui |= miir(ctlr, k, 3)>>10; 
				miir(ctlr, k, 1); 
				debug("phy%d: index %d oui %uX reg1 %uX\n", 
					x, k, oui, miir(ctlr, k, 1)); 
				USED(oui); 
			} 
			ctlr->phy[x] = k; 
		} 
	} 
 
	ctlr->fd = 0; 
	ctlr->medium = -1; 
 
	return 0; 
} 
 
static void 
dec2114xpci(void) 
{ 
	Ctlr *ctlr; 
	Pcidev *p; 
	int x; 
 
	p = nil; 
2001/0403    
	while(p = pcimatch(p, 0, 0)){ 
		if(p->ccrb != 0x02 || p->ccru != 0) 
			continue; 
		switch((p->did<<16)|p->vid){ 
1999/0422    
		default: 
			continue; 
 
2001/0403    
		case Tulip3:			/* 21143 */ 
1999/0422    
			/* 
			 * Exit sleep mode. 
			 */ 
			x = pcicfgr32(p, 0x40); 
			x &= ~0xc0000000; 
			pcicfgw32(p, 0x40, x); 
			/*FALLTHROUGH*/ 
 
2001/0403    
		case Pnic:			/* PNIC */ 
		case Pnic2:			/* PNIC-II */ 
		case Tulip0:			/* 21140 */ 
1999/0422    
			break; 
		} 
 
		/* 
		 * bar[0] is the I/O port register address and 
		 * bar[1] is the memory-mapped register address. 
		 */ 
		ctlr = malloc(sizeof(Ctlr)); 
		ctlr->port = p->mem[0].bar & ~0x01; 
		ctlr->pcidev = p; 
2001/0403    
		ctlr->id = (p->did<<16)|p->vid; 
1999/0422    
 
2000/0401    
		if(ioalloc(ctlr->port, p->mem[0].size, 0, "dec2114x") < 0){ 
2001/0403    
			print("dec2114x: port 0x%uX in use\n", ctlr->port); 
2000/0401    
			free(ctlr); 
			continue; 
		} 
 
1999/0422    
		/* 
		 * Some cards (e.g. ANA-6910FX) seem to need the Ps bit 
		 * set or they don't always work right after a hardware 
		 * reset. 
		 */ 
		csr32w(ctlr, 6, Mbo|Ps); 
		softreset(ctlr); 
 
		if(srom(ctlr)){ 
2001/0403    
			iofree(ctlr->port); 
1999/0422    
			free(ctlr); 
2001/0403    
			continue; 
		} 
 
		switch(ctlr->id){ 
		default: 
//			break; 
// 
//		case Pnic:			/* PNIC */ 
			/* 
			 * Turn off the jabber timer. 
			 */ 
			csr32w(ctlr, 15, 0x00000001); 
1999/0422    
			break; 
		} 
 
		if(ctlrhead != nil) 
			ctlrtail->next = ctlr; 
		else 
			ctlrhead = ctlr; 
		ctlrtail = ctlr; 
	} 
} 
 
static int 
reset(Ether* ether) 
{ 
	Ctlr *ctlr; 
	int i, x; 
	uchar ea[Eaddrlen]; 
	static int scandone; 
 
	if(scandone == 0){ 
		dec2114xpci(); 
		scandone = 1; 
	} 
 
	/* 
	 * Any adapter matches if no ether->port is supplied, 
	 * otherwise the ports must match. 
	 */ 
	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ 
		if(ctlr->active) 
			continue; 
		if(ether->port == 0 || ether->port == ctlr->port){ 
			ctlr->active = 1; 
			break; 
		} 
	} 
	if(ctlr == nil) 
		return -1; 
 
	ether->ctlr = ctlr; 
	ether->port = ctlr->port; 
	ether->irq = ctlr->pcidev->intl; 
	ether->tbdf = ctlr->pcidev->tbdf; 
 
	/* 
	 * Check if the adapter's station address is to be overridden. 
	 * If not, read it from the EEPROM and set in ether->ea prior to 
	 * loading the station address in the hardware. 
	 */ 
	memset(ea, 0, Eaddrlen); 
	if(memcmp(ea, ether->ea, Eaddrlen) == 0) 
		memmove(ether->ea, ctlr->sromea, Eaddrlen); 
 
	/* 
	 * Look for a medium override in case there's no autonegotiation 
	 * (no MII) or the autonegotiation fails. 
	 */ 
	for(i = 0; i < ether->nopt; i++){ 
		if(cistrcmp(ether->opt[i], "FD") == 0){ 
			ctlr->fd = 1; 
			continue; 
		} 
		for(x = 0; x < nelem(mediatable); x++){ 
			debug("compare <%s> <%s>\n", mediatable[x], 
				ether->opt[i]); 
			if(cistrcmp(mediatable[x], ether->opt[i])) 
				continue; 
			ctlr->medium = x; 
	 
			switch(ctlr->medium){ 
			default: 
				ctlr->fd = 0; 
				break; 
	 
			case 0x04:		/* 10BASE-TFD */ 
			case 0x05:		/* 100BASE-TXFD */ 
			case 0x08:		/* 100BASE-FXFD */ 
				ctlr->fd = 1; 
				break; 
			} 
			break; 
		} 
	} 
 
	ether->mbps = media(ether, 1); 
 
	/* 
	 * Initialise descriptor rings, ethernet address. 
	 */ 
	ctlr->nrdr = Nrde; 
	ctlr->ntdr = Ntde; 
	pcisetbme(ctlr->pcidev); 
	ctlrinit(ether); 
 
	/* 
	 * Linkage to the generic ethernet driver. 
	 */ 
	ether->attach = attach; 
	ether->transmit = transmit; 
	ether->interrupt = interrupt; 
	ether->ifstat = ifstat; 
 
	ether->arg = ether; 
	ether->promiscuous = promiscuous; 
 
	return 0; 
} 
 
void 
ether2114xlink(void) 
{ 
2001/0403    
	addethercard("21140",  reset); 
1999/0507    
	addethercard("2114x",  reset); 
1999/0422    
} 


source code copyright © 1990-2005 Lucent Technologies; see license
Plan 9 distribution
comments to russ cox (rsc@swtch.com)