xref: /plan9/sys/src/9/mtx/ether2114x.c (revision 3ff48bf5ed603850fcd251ddf13025d23d693782)
19a747e4fSDavid du Colombier /*
29a747e4fSDavid du Colombier  * Digital Semiconductor DECchip 2114x PCI Fast Ethernet LAN Controller.
39a747e4fSDavid du Colombier  * To do:
49a747e4fSDavid du Colombier  *	thresholds;
59a747e4fSDavid du Colombier  *	ring sizing;
69a747e4fSDavid du Colombier  *	handle more error conditions;
79a747e4fSDavid du Colombier  *	tidy setup packet mess;
89a747e4fSDavid du Colombier  *	push initialisation back to attach;
99a747e4fSDavid du Colombier  *	full SROM decoding.
109a747e4fSDavid du Colombier  */
119a747e4fSDavid du Colombier #include "u.h"
129a747e4fSDavid du Colombier #include "../port/lib.h"
139a747e4fSDavid du Colombier #include "mem.h"
149a747e4fSDavid du Colombier #include "dat.h"
159a747e4fSDavid du Colombier #include "fns.h"
169a747e4fSDavid du Colombier #include "io.h"
179a747e4fSDavid du Colombier #include "../port/error.h"
189a747e4fSDavid du Colombier #include "../port/netif.h"
199a747e4fSDavid du Colombier 
209a747e4fSDavid du Colombier #include "etherif.h"
219a747e4fSDavid du Colombier 
229a747e4fSDavid du Colombier #define DEBUG		(0)
239a747e4fSDavid du Colombier #define debug		if(DEBUG)print
249a747e4fSDavid du Colombier 
259a747e4fSDavid du Colombier enum {
269a747e4fSDavid du Colombier 	Nrde		= 64,
279a747e4fSDavid du Colombier 	Ntde		= 64,
289a747e4fSDavid du Colombier };
299a747e4fSDavid du Colombier 
309a747e4fSDavid du Colombier #define Rbsz		ROUNDUP(sizeof(Etherpkt)+4, 4)
319a747e4fSDavid du Colombier 
329a747e4fSDavid du Colombier enum {					/* CRS0 - Bus Mode */
339a747e4fSDavid du Colombier 	Swr		= 0x00000001,	/* Software Reset */
349a747e4fSDavid du Colombier 	Bar		= 0x00000002,	/* Bus Arbitration */
359a747e4fSDavid du Colombier 	Dsl		= 0x0000007C,	/* Descriptor Skip Length (field) */
369a747e4fSDavid du Colombier 	Ble		= 0x00000080,	/* Big/Little Endian */
379a747e4fSDavid du Colombier 	Pbl		= 0x00003F00,	/* Programmable Burst Length (field) */
389a747e4fSDavid du Colombier 	Cal		= 0x0000C000,	/* Cache Alignment (field) */
399a747e4fSDavid du Colombier 	Cal8		= 0x00004000,	/* 8 longword boundary alignment */
409a747e4fSDavid du Colombier 	Cal16		= 0x00008000,	/* 16 longword boundary alignment */
419a747e4fSDavid du Colombier 	Cal32		= 0x0000C000,	/* 32 longword boundary alignment */
429a747e4fSDavid du Colombier 	Tap		= 0x000E0000,	/* Transmit Automatic Polling (field) */
439a747e4fSDavid du Colombier 	Dbo		= 0x00100000,	/* Descriptor Byte Ordering Mode */
449a747e4fSDavid du Colombier 	Rml		= 0x00200000,	/* Read Multiple */
459a747e4fSDavid du Colombier };
469a747e4fSDavid du Colombier 
479a747e4fSDavid du Colombier enum {					/* CSR[57] - Status and Interrupt Enable */
489a747e4fSDavid du Colombier 	Ti		= 0x00000001,	/* Transmit Interrupt */
499a747e4fSDavid du Colombier 	Tps		= 0x00000002,	/* Transmit Process Stopped */
509a747e4fSDavid du Colombier 	Tu		= 0x00000004,	/* Transmit buffer Unavailable */
519a747e4fSDavid du Colombier 	Tjt		= 0x00000008,	/* Transmit Jabber Timeout */
529a747e4fSDavid du Colombier 	Unf		= 0x00000020,	/* transmit UNderFlow */
539a747e4fSDavid du Colombier 	Ri		= 0x00000040,	/* Receive Interrupt */
549a747e4fSDavid du Colombier 	Ru		= 0x00000080,	/* Receive buffer Unavailable */
559a747e4fSDavid du Colombier 	Rps		= 0x00000100,	/* Receive Process Stopped */
569a747e4fSDavid du Colombier 	Rwt		= 0x00000200,	/* Receive Watchdog Timeout */
579a747e4fSDavid du Colombier 	Eti		= 0x00000400,	/* Early Transmit Interrupt */
589a747e4fSDavid du Colombier 	Gte		= 0x00000800,	/* General purpose Timer Expired */
599a747e4fSDavid du Colombier 	Fbe		= 0x00002000,	/* Fatal Bus Error */
609a747e4fSDavid du Colombier 	Ais		= 0x00008000,	/* Abnormal Interrupt Summary */
619a747e4fSDavid du Colombier 	Nis		= 0x00010000,	/* Normal Interrupt Summary */
629a747e4fSDavid du Colombier 	Rs		= 0x000E0000,	/* Receive process State (field) */
639a747e4fSDavid du Colombier 	Ts		= 0x00700000,	/* Transmit process State (field) */
649a747e4fSDavid du Colombier 	Eb		= 0x03800000,	/* Error bits */
659a747e4fSDavid du Colombier };
669a747e4fSDavid du Colombier 
679a747e4fSDavid du Colombier enum {					/* CSR6 - Operating Mode */
689a747e4fSDavid du Colombier 	Hp		= 0x00000001,	/* Hash/Perfect receive filtering mode */
699a747e4fSDavid du Colombier 	Sr		= 0x00000002,	/* Start/stop Receive */
709a747e4fSDavid du Colombier 	Ho		= 0x00000004,	/* Hash-Only filtering mode */
719a747e4fSDavid du Colombier 	Pb		= 0x00000008,	/* Pass Bad frames */
729a747e4fSDavid du Colombier 	If		= 0x00000010,	/* Inverse Filtering */
739a747e4fSDavid du Colombier 	Sb		= 0x00000020,	/* Start/stop Backoff counter */
749a747e4fSDavid du Colombier 	Pr		= 0x00000040,	/* Promiscuous Mode */
759a747e4fSDavid du Colombier 	Pm		= 0x00000080,	/* Pass all Multicast */
769a747e4fSDavid du Colombier 	Fd		= 0x00000200,	/* Full Duplex mode */
779a747e4fSDavid du Colombier 	Om		= 0x00000C00,	/* Operating Mode (field) */
789a747e4fSDavid du Colombier 	Fc		= 0x00001000,	/* Force Collision */
799a747e4fSDavid du Colombier 	St		= 0x00002000,	/* Start/stop Transmission Command */
809a747e4fSDavid du Colombier 	Tr		= 0x0000C000,	/* ThReshold control bits (field) */
819a747e4fSDavid du Colombier 	Tr128		= 0x00000000,
829a747e4fSDavid du Colombier 	Tr256		= 0x00004000,
839a747e4fSDavid du Colombier 	Tr512		= 0x00008000,
849a747e4fSDavid du Colombier 	Tr1024		= 0x0000C000,
859a747e4fSDavid du Colombier 	Ca		= 0x00020000,	/* CApture effect enable */
869a747e4fSDavid du Colombier 	Ps		= 0x00040000,	/* Port Select */
879a747e4fSDavid du Colombier 	Hbd		= 0x00080000,	/* HeartBeat Disable */
889a747e4fSDavid du Colombier 	Imm		= 0x00100000,	/* IMMediate mode */
899a747e4fSDavid du Colombier 	Sf		= 0x00200000,	/* Store and Forward */
909a747e4fSDavid du Colombier 	Ttm		= 0x00400000,	/* Transmit Threshold Mode */
919a747e4fSDavid du Colombier 	Pcs		= 0x00800000,	/* PCS function */
929a747e4fSDavid du Colombier 	Scr		= 0x01000000,	/* SCRambler mode */
939a747e4fSDavid du Colombier 	Mbo		= 0x02000000,	/* Must Be One */
949a747e4fSDavid du Colombier 	Ra		= 0x40000000,	/* Receive All */
959a747e4fSDavid du Colombier 	Sc		= 0x80000000,	/* Special Capture effect enable */
969a747e4fSDavid du Colombier 
979a747e4fSDavid du Colombier 	TrMODE		= Tr512,	/* default transmission threshold */
989a747e4fSDavid du Colombier };
999a747e4fSDavid du Colombier 
1009a747e4fSDavid du Colombier enum {					/* CSR9 - ROM and MII Management */
1019a747e4fSDavid du Colombier 	Scs		= 0x00000001,	/* serial ROM chip select */
1029a747e4fSDavid du Colombier 	Sclk		= 0x00000002,	/* serial ROM clock */
1039a747e4fSDavid du Colombier 	Sdi		= 0x00000004,	/* serial ROM data in */
1049a747e4fSDavid du Colombier 	Sdo		= 0x00000008,	/* serial ROM data out */
1059a747e4fSDavid du Colombier 	Ss		= 0x00000800,	/* serial ROM select */
1069a747e4fSDavid du Colombier 	Wr		= 0x00002000,	/* write */
1079a747e4fSDavid du Colombier 	Rd		= 0x00004000,	/* read */
1089a747e4fSDavid du Colombier 
1099a747e4fSDavid du Colombier 	Mdc		= 0x00010000,	/* MII management clock */
1109a747e4fSDavid du Colombier 	Mdo		= 0x00020000,	/* MII management write data */
1119a747e4fSDavid du Colombier 	Mii		= 0x00040000,	/* MII management operation mode (W) */
1129a747e4fSDavid du Colombier 	Mdi		= 0x00080000,	/* MII management data in */
1139a747e4fSDavid du Colombier };
1149a747e4fSDavid du Colombier 
1159a747e4fSDavid du Colombier enum {					/* CSR12 - General-Purpose Port */
1169a747e4fSDavid du Colombier 	Gpc		= 0x00000100,	/* General Purpose Control */
1179a747e4fSDavid du Colombier };
1189a747e4fSDavid du Colombier 
1199a747e4fSDavid du Colombier typedef struct Des {
1209a747e4fSDavid du Colombier 	int	status;
1219a747e4fSDavid du Colombier 	int	control;
1229a747e4fSDavid du Colombier 	ulong	addr;
1239a747e4fSDavid du Colombier 	Block*	bp;
1249a747e4fSDavid du Colombier } Des;
1259a747e4fSDavid du Colombier 
1269a747e4fSDavid du Colombier enum {					/* status */
1279a747e4fSDavid du Colombier 	Of		= 0x00000001,	/* Rx: OverFlow */
1289a747e4fSDavid du Colombier 	Ce		= 0x00000002,	/* Rx: CRC Error */
1299a747e4fSDavid du Colombier 	Db		= 0x00000004,	/* Rx: Dribbling Bit */
1309a747e4fSDavid du Colombier 	Re		= 0x00000008,	/* Rx: Report on MII Error */
1319a747e4fSDavid du Colombier 	Rw		= 0x00000010,	/* Rx: Receive Watchdog */
1329a747e4fSDavid du Colombier 	Ft		= 0x00000020,	/* Rx: Frame Type */
1339a747e4fSDavid du Colombier 	Cs		= 0x00000040,	/* Rx: Collision Seen */
1349a747e4fSDavid du Colombier 	Tl		= 0x00000080,	/* Rx: Frame too Long */
1359a747e4fSDavid du Colombier 	Ls		= 0x00000100,	/* Rx: Last deScriptor */
1369a747e4fSDavid du Colombier 	Fs		= 0x00000200,	/* Rx: First deScriptor */
1379a747e4fSDavid du Colombier 	Mf		= 0x00000400,	/* Rx: Multicast Frame */
1389a747e4fSDavid du Colombier 	Rf		= 0x00000800,	/* Rx: Runt Frame */
1399a747e4fSDavid du Colombier 	Dt		= 0x00003000,	/* Rx: Data Type (field) */
1409a747e4fSDavid du Colombier 	De		= 0x00004000,	/* Rx: Descriptor Error */
1419a747e4fSDavid du Colombier 	Fl		= 0x3FFF0000,	/* Rx: Frame Length (field) */
1429a747e4fSDavid du Colombier 	Ff		= 0x40000000,	/* Rx: Filtering Fail */
1439a747e4fSDavid du Colombier 
1449a747e4fSDavid du Colombier 	Def		= 0x00000001,	/* Tx: DEFerred */
1459a747e4fSDavid du Colombier 	Uf		= 0x00000002,	/* Tx: UnderFlow error */
1469a747e4fSDavid du Colombier 	Lf		= 0x00000004,	/* Tx: Link Fail report */
1479a747e4fSDavid du Colombier 	Cc		= 0x00000078,	/* Tx: Collision Count (field) */
1489a747e4fSDavid du Colombier 	Hf		= 0x00000080,	/* Tx: Heartbeat Fail */
1499a747e4fSDavid du Colombier 	Ec		= 0x00000100,	/* Tx: Excessive Collisions */
1509a747e4fSDavid du Colombier 	Lc		= 0x00000200,	/* Tx: Late Collision */
1519a747e4fSDavid du Colombier 	Nc		= 0x00000400,	/* Tx: No Carrier */
1529a747e4fSDavid du Colombier 	Lo		= 0x00000800,	/* Tx: LOss of carrier */
1539a747e4fSDavid du Colombier 	To		= 0x00004000,	/* Tx: Transmission jabber timeOut */
1549a747e4fSDavid du Colombier 
1559a747e4fSDavid du Colombier 	Es		= 0x00008000,	/* [RT]x: Error Summary */
1569a747e4fSDavid du Colombier 	Own		= 0x80000000,	/* [RT]x: OWN bit */
1579a747e4fSDavid du Colombier };
1589a747e4fSDavid du Colombier 
1599a747e4fSDavid du Colombier enum {					/* control */
1609a747e4fSDavid du Colombier 	Bs1		= 0x000007FF,	/* [RT]x: Buffer 1 Size */
1619a747e4fSDavid du Colombier 	Bs2		= 0x003FF800,	/* [RT]x: Buffer 2 Size */
1629a747e4fSDavid du Colombier 
1639a747e4fSDavid du Colombier 	Ch		= 0x01000000,	/* [RT]x: second address CHained */
1649a747e4fSDavid du Colombier 	Er		= 0x02000000,	/* [RT]x: End of Ring */
1659a747e4fSDavid du Colombier 
1669a747e4fSDavid du Colombier 	Ft0		= 0x00400000,	/* Tx: Filtering Type 0 */
1679a747e4fSDavid du Colombier 	Dpd		= 0x00800000,	/* Tx: Disabled PaDding */
1689a747e4fSDavid du Colombier 	Ac		= 0x04000000,	/* Tx: Add CRC disable */
1699a747e4fSDavid du Colombier 	Set		= 0x08000000,	/* Tx: SETup packet */
1709a747e4fSDavid du Colombier 	Ft1		= 0x10000000,	/* Tx: Filtering Type 1 */
1719a747e4fSDavid du Colombier 	Fseg		= 0x20000000,	/* Tx: First SEGment */
1729a747e4fSDavid du Colombier 	Lseg		= 0x40000000,	/* Tx: Last SEGment */
1739a747e4fSDavid du Colombier 	Ic		= 0x80000000,	/* Tx: Interrupt on Completion */
1749a747e4fSDavid du Colombier };
1759a747e4fSDavid du Colombier 
1769a747e4fSDavid du Colombier enum {					/* PHY registers */
1779a747e4fSDavid du Colombier 	Bmcr		= 0,		/* Basic Mode Control */
1789a747e4fSDavid du Colombier 	Bmsr		= 1,		/* Basic Mode Status */
1799a747e4fSDavid du Colombier 	Phyidr1		= 2,		/* PHY Identifier #1 */
1809a747e4fSDavid du Colombier 	Phyidr2		= 3,		/* PHY Identifier #2 */
1819a747e4fSDavid du Colombier 	Anar		= 4,		/* Auto-Negotiation Advertisment */
1829a747e4fSDavid du Colombier 	Anlpar		= 5,		/* Auto-Negotiation Link Partner Ability */
1839a747e4fSDavid du Colombier 	Aner		= 6,		/* Auto-Negotiation Expansion */
1849a747e4fSDavid du Colombier };
1859a747e4fSDavid du Colombier 
1869a747e4fSDavid du Colombier enum {					/* Variants */
1879a747e4fSDavid du Colombier 	Tulip0		= (0x0009<<16)|0x1011,
1889a747e4fSDavid du Colombier 	Tulip3		= (0x0019<<16)|0x1011,
1899a747e4fSDavid du Colombier 	Pnic		= (0x0002<<16)|0x11AD,
1909a747e4fSDavid du Colombier 	Pnic2		= (0xC115<<16)|0x11AD,
1919a747e4fSDavid du Colombier };
1929a747e4fSDavid du Colombier 
1939a747e4fSDavid du Colombier typedef struct Ctlr Ctlr;
1949a747e4fSDavid du Colombier typedef struct Ctlr {
1959a747e4fSDavid du Colombier 	int	port;
1969a747e4fSDavid du Colombier 	Pcidev*	pcidev;
1979a747e4fSDavid du Colombier 	Ctlr*	next;
1989a747e4fSDavid du Colombier 	int	active;
1999a747e4fSDavid du Colombier 	int	id;			/* (pcidev->did<<16)|pcidev->vid */
2009a747e4fSDavid du Colombier 
2019a747e4fSDavid du Colombier 	uchar*	srom;
2029a747e4fSDavid du Colombier 	int	sromsz;			/* address size in bits */
2039a747e4fSDavid du Colombier 	uchar*	sromea;			/* MAC address */
2049a747e4fSDavid du Colombier 	uchar*	leaf;
2059a747e4fSDavid du Colombier 	int	sct;			/* selected connection type */
2069a747e4fSDavid du Colombier 	int	k;			/* info block count */
2079a747e4fSDavid du Colombier 	uchar*	infoblock[16];
2089a747e4fSDavid du Colombier 	int	sctk;			/* sct block index */
2099a747e4fSDavid du Colombier 	int	curk;			/* current block index */
2109a747e4fSDavid du Colombier 	uchar*	type5block;
2119a747e4fSDavid du Colombier 
2129a747e4fSDavid du Colombier 	int	phy[32];		/* logical to physical map */
2139a747e4fSDavid du Colombier 	int	phyreset;		/* reset bitmap */
2149a747e4fSDavid du Colombier 	int	curphyad;
2159a747e4fSDavid du Colombier 	int	fdx;
2169a747e4fSDavid du Colombier 	int	ttm;
2179a747e4fSDavid du Colombier 
2189a747e4fSDavid du Colombier 	uchar	fd;			/* option */
2199a747e4fSDavid du Colombier 	int	medium;			/* option */
2209a747e4fSDavid du Colombier 
2219a747e4fSDavid du Colombier 	int	csr6;			/* CSR6 - operating mode */
2229a747e4fSDavid du Colombier 	int	mask;			/* CSR[57] - interrupt mask */
2239a747e4fSDavid du Colombier 	int	mbps;
2249a747e4fSDavid du Colombier 
2259a747e4fSDavid du Colombier 	Lock	lock;
2269a747e4fSDavid du Colombier 
2279a747e4fSDavid du Colombier 	Des*	rdr;			/* receive descriptor ring */
2289a747e4fSDavid du Colombier 	int	nrdr;			/* size of rdr */
2299a747e4fSDavid du Colombier 	int	rdrx;			/* index into rdr */
2309a747e4fSDavid du Colombier 
2319a747e4fSDavid du Colombier 	Lock	tlock;
2329a747e4fSDavid du Colombier 	Des*	tdr;			/* transmit descriptor ring */
2339a747e4fSDavid du Colombier 	int	ntdr;			/* size of tdr */
2349a747e4fSDavid du Colombier 	int	tdrh;			/* host index into tdr */
2359a747e4fSDavid du Colombier 	int	tdri;			/* interface index into tdr */
2369a747e4fSDavid du Colombier 	int	ntq;			/* descriptors active */
2379a747e4fSDavid du Colombier 	int	ntqmax;
2389a747e4fSDavid du Colombier 	Block*	setupbp;
2399a747e4fSDavid du Colombier 
2409a747e4fSDavid du Colombier 	ulong	of;			/* receive statistics */
2419a747e4fSDavid du Colombier 	ulong	ce;
2429a747e4fSDavid du Colombier 	ulong	cs;
2439a747e4fSDavid du Colombier 	ulong	tl;
2449a747e4fSDavid du Colombier 	ulong	rf;
2459a747e4fSDavid du Colombier 	ulong	de;
2469a747e4fSDavid du Colombier 
2479a747e4fSDavid du Colombier 	ulong	ru;
2489a747e4fSDavid du Colombier 	ulong	rps;
2499a747e4fSDavid du Colombier 	ulong	rwt;
2509a747e4fSDavid du Colombier 
2519a747e4fSDavid du Colombier 	ulong	uf;			/* transmit statistics */
2529a747e4fSDavid du Colombier 	ulong	ec;
2539a747e4fSDavid du Colombier 	ulong	lc;
2549a747e4fSDavid du Colombier 	ulong	nc;
2559a747e4fSDavid du Colombier 	ulong	lo;
2569a747e4fSDavid du Colombier 	ulong	to;
2579a747e4fSDavid du Colombier 
2589a747e4fSDavid du Colombier 	ulong	tps;
2599a747e4fSDavid du Colombier 	ulong	tu;
2609a747e4fSDavid du Colombier 	ulong	tjt;
2619a747e4fSDavid du Colombier 	ulong	unf;
2629a747e4fSDavid du Colombier } Ctlr;
2639a747e4fSDavid du Colombier 
2649a747e4fSDavid du Colombier static Ctlr* ctlrhead;
2659a747e4fSDavid du Colombier static Ctlr* ctlrtail;
2669a747e4fSDavid du Colombier 
2679a747e4fSDavid du Colombier #define csr32r(c, r)	(inl((c)->port+((r)*8)))
2689a747e4fSDavid du Colombier #define csr32w(c, r, l)	(outl((c)->port+((r)*8), (ulong)(l)))
2699a747e4fSDavid du Colombier 
2709a747e4fSDavid du Colombier static void
promiscuous(void * arg,int on)2719a747e4fSDavid du Colombier promiscuous(void* arg, int on)
2729a747e4fSDavid du Colombier {
2739a747e4fSDavid du Colombier 	Ctlr *ctlr;
2749a747e4fSDavid du Colombier 
2759a747e4fSDavid du Colombier 	ctlr = ((Ether*)arg)->ctlr;
2769a747e4fSDavid du Colombier 	ilock(&ctlr->lock);
2779a747e4fSDavid du Colombier 	if(on)
2789a747e4fSDavid du Colombier 		ctlr->csr6 |= Pr;
2799a747e4fSDavid du Colombier 	else
2809a747e4fSDavid du Colombier 		ctlr->csr6 &= ~Pr;
2819a747e4fSDavid du Colombier 	csr32w(ctlr, 6, ctlr->csr6);
2829a747e4fSDavid du Colombier 	iunlock(&ctlr->lock);
2839a747e4fSDavid du Colombier }
2849a747e4fSDavid du Colombier 
2859a747e4fSDavid du Colombier static void
attach(Ether * ether)2869a747e4fSDavid du Colombier attach(Ether* ether)
2879a747e4fSDavid du Colombier {
2889a747e4fSDavid du Colombier 	Ctlr *ctlr;
2899a747e4fSDavid du Colombier 
2909a747e4fSDavid du Colombier 	ctlr = ether->ctlr;
2919a747e4fSDavid du Colombier 	ilock(&ctlr->lock);
2929a747e4fSDavid du Colombier 	if(!(ctlr->csr6 & Sr)){
2939a747e4fSDavid du Colombier 		ctlr->csr6 |= Sr;
2949a747e4fSDavid du Colombier 		csr32w(ctlr, 6, ctlr->csr6);
2959a747e4fSDavid du Colombier 	}
2969a747e4fSDavid du Colombier 	iunlock(&ctlr->lock);
2979a747e4fSDavid du Colombier }
2989a747e4fSDavid du Colombier 
2999a747e4fSDavid du Colombier static long
ifstat(Ether * ether,void * a,long n,ulong offset)3009a747e4fSDavid du Colombier ifstat(Ether* ether, void* a, long n, ulong offset)
3019a747e4fSDavid du Colombier {
3029a747e4fSDavid du Colombier 	Ctlr *ctlr;
3039a747e4fSDavid du Colombier 	char *buf, *p;
3049a747e4fSDavid du Colombier 	int i, l, len;
3059a747e4fSDavid du Colombier 
3069a747e4fSDavid du Colombier 	ctlr = ether->ctlr;
3079a747e4fSDavid du Colombier 
3089a747e4fSDavid du Colombier 	ether->crcs = ctlr->ce;
3099a747e4fSDavid du Colombier 	ether->frames = ctlr->rf+ctlr->cs;
3109a747e4fSDavid du Colombier 	ether->buffs = ctlr->de+ctlr->tl;
3119a747e4fSDavid du Colombier 	ether->overflows = ctlr->of;
3129a747e4fSDavid du Colombier 
3139a747e4fSDavid du Colombier 	if(n == 0)
3149a747e4fSDavid du Colombier 		return 0;
3159a747e4fSDavid du Colombier 
3169a747e4fSDavid du Colombier 	p = malloc(READSTR);
3179a747e4fSDavid du Colombier 	l = snprint(p, READSTR, "Overflow: %lud\n", ctlr->of);
3189a747e4fSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Ru: %lud\n", ctlr->ru);
3199a747e4fSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Rps: %lud\n", ctlr->rps);
3209a747e4fSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Rwt: %lud\n", ctlr->rwt);
3219a747e4fSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Tps: %lud\n", ctlr->tps);
3229a747e4fSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Tu: %lud\n", ctlr->tu);
3239a747e4fSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Tjt: %lud\n", ctlr->tjt);
3249a747e4fSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Unf: %lud\n", ctlr->unf);
3259a747e4fSDavid du Colombier 	l += snprint(p+l, READSTR-l, "CRC Error: %lud\n", ctlr->ce);
3269a747e4fSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Collision Seen: %lud\n", ctlr->cs);
3279a747e4fSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Frame Too Long: %lud\n", ctlr->tl);
3289a747e4fSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Runt Frame: %lud\n", ctlr->rf);
3299a747e4fSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Descriptor Error: %lud\n", ctlr->de);
3309a747e4fSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Underflow Error: %lud\n", ctlr->uf);
3319a747e4fSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Excessive Collisions: %lud\n", ctlr->ec);
3329a747e4fSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Late Collision: %lud\n", ctlr->lc);
3339a747e4fSDavid du Colombier 	l += snprint(p+l, READSTR-l, "No Carrier: %lud\n", ctlr->nc);
3349a747e4fSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Loss of Carrier: %lud\n", ctlr->lo);
3359a747e4fSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Transmit Jabber Timeout: %lud\n",
3369a747e4fSDavid du Colombier 		ctlr->to);
3379a747e4fSDavid du Colombier 	l += snprint(p+l, READSTR-l, "csr6: %luX %uX\n", csr32r(ctlr, 6),
3389a747e4fSDavid du Colombier 		ctlr->csr6);
3399a747e4fSDavid du Colombier 	snprint(p+l, READSTR-l, "ntqmax: %d\n", ctlr->ntqmax);
3409a747e4fSDavid du Colombier 	ctlr->ntqmax = 0;
3419a747e4fSDavid du Colombier 	buf = a;
3429a747e4fSDavid du Colombier 	len = readstr(offset, buf, n, p);
3439a747e4fSDavid du Colombier 	if(offset > l)
3449a747e4fSDavid du Colombier 		offset -= l;
3459a747e4fSDavid du Colombier 	else
3469a747e4fSDavid du Colombier 		offset = 0;
3479a747e4fSDavid du Colombier 	buf += len;
3489a747e4fSDavid du Colombier 	n -= len;
3499a747e4fSDavid du Colombier 
3509a747e4fSDavid du Colombier 	l = snprint(p, READSTR, "srom:");
3519a747e4fSDavid du Colombier 	for(i = 0; i < (1<<(ctlr->sromsz)*sizeof(ushort)); i++){
3529a747e4fSDavid du Colombier 		if(i && ((i & 0x0F) == 0))
3539a747e4fSDavid du Colombier 			l += snprint(p+l, READSTR-l, "\n     ");
3549a747e4fSDavid du Colombier 		l += snprint(p+l, READSTR-l, " %2.2uX", ctlr->srom[i]);
3559a747e4fSDavid du Colombier 	}
3569a747e4fSDavid du Colombier 
3579a747e4fSDavid du Colombier 	snprint(p+l, READSTR-l, "\n");
3589a747e4fSDavid du Colombier 	len += readstr(offset, buf, n, p);
3599a747e4fSDavid du Colombier 	free(p);
3609a747e4fSDavid du Colombier 
3619a747e4fSDavid du Colombier 	return len;
3629a747e4fSDavid du Colombier }
3639a747e4fSDavid du Colombier 
3649a747e4fSDavid du Colombier static void
txstart(Ether * ether)3659a747e4fSDavid du Colombier txstart(Ether* ether)
3669a747e4fSDavid du Colombier {
3679a747e4fSDavid du Colombier 	Ctlr *ctlr;
3689a747e4fSDavid du Colombier 	Block *bp;
3699a747e4fSDavid du Colombier 	Des *des;
3709a747e4fSDavid du Colombier 	int control;
3719a747e4fSDavid du Colombier 
3729a747e4fSDavid du Colombier 	ctlr = ether->ctlr;
3739a747e4fSDavid du Colombier 	while(ctlr->ntq < (ctlr->ntdr-1)){
3749a747e4fSDavid du Colombier 		if(ctlr->setupbp){
3759a747e4fSDavid du Colombier 			bp = ctlr->setupbp;
3769a747e4fSDavid du Colombier 			ctlr->setupbp = 0;
3779a747e4fSDavid du Colombier 			control = Ic|Set|BLEN(bp);
3789a747e4fSDavid du Colombier 		}
3799a747e4fSDavid du Colombier 		else{
3809a747e4fSDavid du Colombier 			bp = qget(ether->oq);
3819a747e4fSDavid du Colombier 			if(bp == nil)
3829a747e4fSDavid du Colombier 				break;
3839a747e4fSDavid du Colombier 			control = Ic|Lseg|Fseg|BLEN(bp);
3849a747e4fSDavid du Colombier 		}
3859a747e4fSDavid du Colombier 
3869a747e4fSDavid du Colombier 		ctlr->tdr[PREV(ctlr->tdrh, ctlr->ntdr)].control &= ~Ic;
3879a747e4fSDavid du Colombier 		des = &ctlr->tdr[ctlr->tdrh];
3889a747e4fSDavid du Colombier 		des->bp = bp;
3899a747e4fSDavid du Colombier 		des->addr = PCIWADDR(bp->rp);
3909a747e4fSDavid du Colombier 		des->control |= control;
3919a747e4fSDavid du Colombier 		ctlr->ntq++;
3929a747e4fSDavid du Colombier 		coherence();
3939a747e4fSDavid du Colombier 		des->status = Own;
3949a747e4fSDavid du Colombier 		csr32w(ctlr, 1, 0);
3959a747e4fSDavid du Colombier 		ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr);
3969a747e4fSDavid du Colombier 	}
3979a747e4fSDavid du Colombier 
3989a747e4fSDavid du Colombier 	if(ctlr->ntq > ctlr->ntqmax)
3999a747e4fSDavid du Colombier 		ctlr->ntqmax = ctlr->ntq;
4009a747e4fSDavid du Colombier }
4019a747e4fSDavid du Colombier 
4029a747e4fSDavid du Colombier static void
transmit(Ether * ether)4039a747e4fSDavid du Colombier transmit(Ether* ether)
4049a747e4fSDavid du Colombier {
4059a747e4fSDavid du Colombier 	Ctlr *ctlr;
4069a747e4fSDavid du Colombier 
4079a747e4fSDavid du Colombier 	ctlr = ether->ctlr;
4089a747e4fSDavid du Colombier 	ilock(&ctlr->tlock);
4099a747e4fSDavid du Colombier 	txstart(ether);
4109a747e4fSDavid du Colombier 	iunlock(&ctlr->tlock);
4119a747e4fSDavid du Colombier }
4129a747e4fSDavid du Colombier 
4139a747e4fSDavid du Colombier static void
interrupt(Ureg *,void * arg)4149a747e4fSDavid du Colombier interrupt(Ureg*, void* arg)
4159a747e4fSDavid du Colombier {
4169a747e4fSDavid du Colombier 	Ctlr *ctlr;
4179a747e4fSDavid du Colombier 	Ether *ether;
4189a747e4fSDavid du Colombier 	int len, status;
4199a747e4fSDavid du Colombier 	Des *des;
4209a747e4fSDavid du Colombier 	Block *bp;
4219a747e4fSDavid du Colombier 
4229a747e4fSDavid du Colombier 	ether = arg;
4239a747e4fSDavid du Colombier 	ctlr = ether->ctlr;
4249a747e4fSDavid du Colombier 
4259a747e4fSDavid du Colombier 	while((status = csr32r(ctlr, 5)) & (Nis|Ais)){
4269a747e4fSDavid du Colombier 		/*
4279a747e4fSDavid du Colombier 		 * Acknowledge the interrupts and mask-out
4289a747e4fSDavid du Colombier 		 * the ones that are implicitly handled.
4299a747e4fSDavid du Colombier 		 */
4309a747e4fSDavid du Colombier 		csr32w(ctlr, 5, status);
4319a747e4fSDavid du Colombier 		status &= (ctlr->mask & ~(Nis|Ti));
4329a747e4fSDavid du Colombier 
4339a747e4fSDavid du Colombier 		if(status & Ais){
4349a747e4fSDavid du Colombier 			if(status & Tps)
4359a747e4fSDavid du Colombier 				ctlr->tps++;
4369a747e4fSDavid du Colombier 			if(status & Tu)
4379a747e4fSDavid du Colombier 				ctlr->tu++;
4389a747e4fSDavid du Colombier 			if(status & Tjt)
4399a747e4fSDavid du Colombier 				ctlr->tjt++;
4409a747e4fSDavid du Colombier 			if(status & Ru)
4419a747e4fSDavid du Colombier 				ctlr->ru++;
4429a747e4fSDavid du Colombier 			if(status & Rps)
4439a747e4fSDavid du Colombier 				ctlr->rps++;
4449a747e4fSDavid du Colombier 			if(status & Rwt)
4459a747e4fSDavid du Colombier 				ctlr->rwt++;
4469a747e4fSDavid du Colombier 			status &= ~(Ais|Rwt|Rps|Ru|Tjt|Tu|Tps);
4479a747e4fSDavid du Colombier 		}
4489a747e4fSDavid du Colombier 
4499a747e4fSDavid du Colombier 		/*
4509a747e4fSDavid du Colombier 		 * Received packets.
4519a747e4fSDavid du Colombier 		 */
4529a747e4fSDavid du Colombier 		if(status & Ri){
4539a747e4fSDavid du Colombier 			des = &ctlr->rdr[ctlr->rdrx];
4549a747e4fSDavid du Colombier 			while(!(des->status & Own)){
4559a747e4fSDavid du Colombier 				if(des->status & Es){
4569a747e4fSDavid du Colombier 					if(des->status & Of)
4579a747e4fSDavid du Colombier 						ctlr->of++;
4589a747e4fSDavid du Colombier 					if(des->status & Ce)
4599a747e4fSDavid du Colombier 						ctlr->ce++;
4609a747e4fSDavid du Colombier 					if(des->status & Cs)
4619a747e4fSDavid du Colombier 						ctlr->cs++;
4629a747e4fSDavid du Colombier 					if(des->status & Tl)
4639a747e4fSDavid du Colombier 						ctlr->tl++;
4649a747e4fSDavid du Colombier 					if(des->status & Rf)
4659a747e4fSDavid du Colombier 						ctlr->rf++;
4669a747e4fSDavid du Colombier 					if(des->status & De)
4679a747e4fSDavid du Colombier 						ctlr->de++;
4689a747e4fSDavid du Colombier 				}
4699a747e4fSDavid du Colombier 				else if(bp = iallocb(Rbsz)){
4709a747e4fSDavid du Colombier 					len = ((des->status & Fl)>>16)-4;
4719a747e4fSDavid du Colombier 					des->bp->wp = des->bp->rp+len;
4729a747e4fSDavid du Colombier 					etheriq(ether, des->bp, 1);
4739a747e4fSDavid du Colombier 					des->bp = bp;
4749a747e4fSDavid du Colombier 					des->addr = PCIWADDR(bp->rp);
4759a747e4fSDavid du Colombier 				}
4769a747e4fSDavid du Colombier 
4779a747e4fSDavid du Colombier 				des->control &= Er;
4789a747e4fSDavid du Colombier 				des->control |= Rbsz;
4799a747e4fSDavid du Colombier 				coherence();
4809a747e4fSDavid du Colombier 				des->status = Own;
4819a747e4fSDavid du Colombier 
4829a747e4fSDavid du Colombier 				ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr);
4839a747e4fSDavid du Colombier 				des = &ctlr->rdr[ctlr->rdrx];
4849a747e4fSDavid du Colombier 			}
4859a747e4fSDavid du Colombier 			status &= ~Ri;
4869a747e4fSDavid du Colombier 		}
4879a747e4fSDavid du Colombier 
4889a747e4fSDavid du Colombier 		/*
4899a747e4fSDavid du Colombier 		 * Check the transmit side:
4909a747e4fSDavid du Colombier 		 *	check for Transmit Underflow and Adjust
4919a747e4fSDavid du Colombier 		 *	the threshold upwards;
4929a747e4fSDavid du Colombier 		 *	free any transmitted buffers and try to
4939a747e4fSDavid du Colombier 		 *	top-up the ring.
4949a747e4fSDavid du Colombier 		 */
4959a747e4fSDavid du Colombier 		if(status & Unf){
4969a747e4fSDavid du Colombier 			ctlr->unf++;
4979a747e4fSDavid du Colombier 			ilock(&ctlr->lock);
4989a747e4fSDavid du Colombier 			csr32w(ctlr, 6, ctlr->csr6 & ~St);
4999a747e4fSDavid du Colombier 			switch(ctlr->csr6 & Tr){
5009a747e4fSDavid du Colombier 			case Tr128:
5019a747e4fSDavid du Colombier 				len = Tr256;
5029a747e4fSDavid du Colombier 				break;
5039a747e4fSDavid du Colombier 			case Tr256:
5049a747e4fSDavid du Colombier 				len = Tr512;
5059a747e4fSDavid du Colombier 				break;
5069a747e4fSDavid du Colombier 			case Tr512:
5079a747e4fSDavid du Colombier 				len = Tr1024;
5089a747e4fSDavid du Colombier 				break;
5099a747e4fSDavid du Colombier 			default:
5109a747e4fSDavid du Colombier 			case Tr1024:
5119a747e4fSDavid du Colombier 				len = Sf;
5129a747e4fSDavid du Colombier 				break;
5139a747e4fSDavid du Colombier 			}
5149a747e4fSDavid du Colombier 			ctlr->csr6 = (ctlr->csr6 & ~Tr)|len;
5159a747e4fSDavid du Colombier 			csr32w(ctlr, 6, ctlr->csr6);
5169a747e4fSDavid du Colombier 			iunlock(&ctlr->lock);
5179a747e4fSDavid du Colombier 			csr32w(ctlr, 5, Tps);
5189a747e4fSDavid du Colombier 			status &= ~(Unf|Tps);
5199a747e4fSDavid du Colombier 		}
5209a747e4fSDavid du Colombier 
5219a747e4fSDavid du Colombier 		ilock(&ctlr->tlock);
5229a747e4fSDavid du Colombier 		while(ctlr->ntq){
5239a747e4fSDavid du Colombier 			des = &ctlr->tdr[ctlr->tdri];
5249a747e4fSDavid du Colombier 			if(des->status & Own)
5259a747e4fSDavid du Colombier 				break;
5269a747e4fSDavid du Colombier 
5279a747e4fSDavid du Colombier 			if(des->status & Es){
5289a747e4fSDavid du Colombier 				if(des->status & Uf)
5299a747e4fSDavid du Colombier 					ctlr->uf++;
5309a747e4fSDavid du Colombier 				if(des->status & Ec)
5319a747e4fSDavid du Colombier 					ctlr->ec++;
5329a747e4fSDavid du Colombier 				if(des->status & Lc)
5339a747e4fSDavid du Colombier 					ctlr->lc++;
5349a747e4fSDavid du Colombier 				if(des->status & Nc)
5359a747e4fSDavid du Colombier 					ctlr->nc++;
5369a747e4fSDavid du Colombier 				if(des->status & Lo)
5379a747e4fSDavid du Colombier 					ctlr->lo++;
5389a747e4fSDavid du Colombier 				if(des->status & To)
5399a747e4fSDavid du Colombier 					ctlr->to++;
5409a747e4fSDavid du Colombier 				ether->oerrs++;
5419a747e4fSDavid du Colombier 			}
5429a747e4fSDavid du Colombier 
5439a747e4fSDavid du Colombier 			freeb(des->bp);
5449a747e4fSDavid du Colombier 			des->control &= Er;
5459a747e4fSDavid du Colombier 
5469a747e4fSDavid du Colombier 			ctlr->ntq--;
5479a747e4fSDavid du Colombier 			ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr);
5489a747e4fSDavid du Colombier 		}
5499a747e4fSDavid du Colombier 		txstart(ether);
5509a747e4fSDavid du Colombier 		iunlock(&ctlr->tlock);
5519a747e4fSDavid du Colombier 
5529a747e4fSDavid du Colombier 		/*
5539a747e4fSDavid du Colombier 		 * Anything left not catered for?
5549a747e4fSDavid du Colombier 		 */
5559a747e4fSDavid du Colombier 		if(status)
5569a747e4fSDavid du Colombier 			panic("#l%d: status %8.8uX\n", ether->ctlrno, status);
5579a747e4fSDavid du Colombier 	}
5589a747e4fSDavid du Colombier }
5599a747e4fSDavid du Colombier 
5609a747e4fSDavid du Colombier static void
ctlrinit(Ether * ether)5619a747e4fSDavid du Colombier ctlrinit(Ether* ether)
5629a747e4fSDavid du Colombier {
5639a747e4fSDavid du Colombier 	Ctlr *ctlr;
5649a747e4fSDavid du Colombier 	Des *des;
5659a747e4fSDavid du Colombier 	Block *bp;
5669a747e4fSDavid du Colombier 	int i;
5679a747e4fSDavid du Colombier 	uchar bi[Eaddrlen*2];
5689a747e4fSDavid du Colombier 
5699a747e4fSDavid du Colombier 	ctlr = ether->ctlr;
5709a747e4fSDavid du Colombier 
5719a747e4fSDavid du Colombier 	/*
5729a747e4fSDavid du Colombier 	 * Allocate and initialise the receive ring;
5739a747e4fSDavid du Colombier 	 * allocate and initialise the transmit ring;
5749a747e4fSDavid du Colombier 	 * unmask interrupts and start the transmit side;
5759a747e4fSDavid du Colombier 	 * create and post a setup packet to initialise
5769a747e4fSDavid du Colombier 	 * the physical ethernet address.
5779a747e4fSDavid du Colombier 	 */
5789a747e4fSDavid du Colombier 	ctlr->rdr = xspanalloc(ctlr->nrdr*sizeof(Des), 8*sizeof(ulong), 0);
5799a747e4fSDavid du Colombier 	for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){
5809a747e4fSDavid du Colombier 		des->bp = iallocb(Rbsz);
5819a747e4fSDavid du Colombier 		if(des->bp == nil)
5829a747e4fSDavid du Colombier 			panic("can't allocate ethernet receive ring\n");
5839a747e4fSDavid du Colombier 		des->status = Own;
5849a747e4fSDavid du Colombier 		des->control = Rbsz;
5859a747e4fSDavid du Colombier 		des->addr = PCIWADDR(des->bp->rp);
5869a747e4fSDavid du Colombier 	}
5879a747e4fSDavid du Colombier 	ctlr->rdr[ctlr->nrdr-1].control |= Er;
5889a747e4fSDavid du Colombier 	ctlr->rdrx = 0;
5899a747e4fSDavid du Colombier 	csr32w(ctlr, 3, PCIWADDR(ctlr->rdr));
5909a747e4fSDavid du Colombier 
5919a747e4fSDavid du Colombier 	ctlr->tdr = xspanalloc(ctlr->ntdr*sizeof(Des), 8*sizeof(ulong), 0);
5929a747e4fSDavid du Colombier 	ctlr->tdr[ctlr->ntdr-1].control |= Er;
5939a747e4fSDavid du Colombier 	ctlr->tdrh = 0;
5949a747e4fSDavid du Colombier 	ctlr->tdri = 0;
5959a747e4fSDavid du Colombier 	csr32w(ctlr, 4, PCIWADDR(ctlr->tdr));
5969a747e4fSDavid du Colombier 
5979a747e4fSDavid du Colombier 	/*
5989a747e4fSDavid du Colombier 	 * Clear any bits in the Status Register (CSR5) as
5999a747e4fSDavid du Colombier 	 * the PNIC has a different reset value from a true 2114x.
6009a747e4fSDavid du Colombier 	 */
6019a747e4fSDavid du Colombier 	ctlr->mask = Nis|Ais|Fbe|Rwt|Rps|Ru|Ri|Unf|Tjt|Tps|Ti;
6029a747e4fSDavid du Colombier 	csr32w(ctlr, 5, ctlr->mask);
6039a747e4fSDavid du Colombier 	csr32w(ctlr, 7, ctlr->mask);
6049a747e4fSDavid du Colombier 	ctlr->csr6 |= St;
6059a747e4fSDavid du Colombier 	csr32w(ctlr, 6, ctlr->csr6);
6069a747e4fSDavid du Colombier 
6079a747e4fSDavid du Colombier 	for(i = 0; i < Eaddrlen/2; i++){
6089a747e4fSDavid du Colombier 		bi[i*4] = ether->ea[i*2];
6099a747e4fSDavid du Colombier 		bi[i*4+1] = ether->ea[i*2+1];
6109a747e4fSDavid du Colombier 		bi[i*4+2] = ether->ea[i*2+1];
6119a747e4fSDavid du Colombier 		bi[i*4+3] = ether->ea[i*2];
6129a747e4fSDavid du Colombier 	}
6139a747e4fSDavid du Colombier 	bp = iallocb(Eaddrlen*2*16);
6149a747e4fSDavid du Colombier 	if(bp == nil)
6159a747e4fSDavid du Colombier 		panic("can't allocate ethernet setup buffer\n");
6169a747e4fSDavid du Colombier 	memset(bp->rp, 0xFF, sizeof(bi));
6179a747e4fSDavid du Colombier 	for(i = sizeof(bi); i < sizeof(bi)*16; i += sizeof(bi))
6189a747e4fSDavid du Colombier 		memmove(bp->rp+i, bi, sizeof(bi));
6199a747e4fSDavid du Colombier 	bp->wp += sizeof(bi)*16;
6209a747e4fSDavid du Colombier 
6219a747e4fSDavid du Colombier 	ctlr->setupbp = bp;
622*3ff48bf5SDavid du Colombier 	ether->oq = qopen(256*1024, Qmsg, 0, 0);
6239a747e4fSDavid du Colombier 	transmit(ether);
6249a747e4fSDavid du Colombier }
6259a747e4fSDavid du Colombier 
6269a747e4fSDavid du Colombier static void
csr9w(Ctlr * ctlr,int data)6279a747e4fSDavid du Colombier csr9w(Ctlr* ctlr, int data)
6289a747e4fSDavid du Colombier {
6299a747e4fSDavid du Colombier 	csr32w(ctlr, 9, data);
6309a747e4fSDavid du Colombier 	microdelay(1);
6319a747e4fSDavid du Colombier }
6329a747e4fSDavid du Colombier 
6339a747e4fSDavid du Colombier static int
miimdi(Ctlr * ctlr,int n)6349a747e4fSDavid du Colombier miimdi(Ctlr* ctlr, int n)
6359a747e4fSDavid du Colombier {
6369a747e4fSDavid du Colombier 	int data, i;
6379a747e4fSDavid du Colombier 
6389a747e4fSDavid du Colombier 	/*
6399a747e4fSDavid du Colombier 	 * Read n bits from the MII Management Register.
6409a747e4fSDavid du Colombier 	 */
6419a747e4fSDavid du Colombier 	data = 0;
6429a747e4fSDavid du Colombier 	for(i = n-1; i >= 0; i--){
6439a747e4fSDavid du Colombier 		if(csr32r(ctlr, 9) & Mdi)
6449a747e4fSDavid du Colombier 			data |= (1<<i);
6459a747e4fSDavid du Colombier 		csr9w(ctlr, Mii|Mdc);
6469a747e4fSDavid du Colombier 		csr9w(ctlr, Mii);
6479a747e4fSDavid du Colombier 	}
6489a747e4fSDavid du Colombier 	csr9w(ctlr, 0);
6499a747e4fSDavid du Colombier 
6509a747e4fSDavid du Colombier 	return data;
6519a747e4fSDavid du Colombier }
6529a747e4fSDavid du Colombier 
6539a747e4fSDavid du Colombier static void
miimdo(Ctlr * ctlr,int bits,int n)6549a747e4fSDavid du Colombier miimdo(Ctlr* ctlr, int bits, int n)
6559a747e4fSDavid du Colombier {
6569a747e4fSDavid du Colombier 	int i, mdo;
6579a747e4fSDavid du Colombier 
6589a747e4fSDavid du Colombier 	/*
6599a747e4fSDavid du Colombier 	 * Write n bits to the MII Management Register.
6609a747e4fSDavid du Colombier 	 */
6619a747e4fSDavid du Colombier 	for(i = n-1; i >= 0; i--){
6629a747e4fSDavid du Colombier 		if(bits & (1<<i))
6639a747e4fSDavid du Colombier 			mdo = Mdo;
6649a747e4fSDavid du Colombier 		else
6659a747e4fSDavid du Colombier 			mdo = 0;
6669a747e4fSDavid du Colombier 		csr9w(ctlr, mdo);
6679a747e4fSDavid du Colombier 		csr9w(ctlr, mdo|Mdc);
6689a747e4fSDavid du Colombier 		csr9w(ctlr, mdo);
6699a747e4fSDavid du Colombier 	}
6709a747e4fSDavid du Colombier }
6719a747e4fSDavid du Colombier 
6729a747e4fSDavid du Colombier static int
miir(Ctlr * ctlr,int phyad,int regad)6739a747e4fSDavid du Colombier miir(Ctlr* ctlr, int phyad, int regad)
6749a747e4fSDavid du Colombier {
6759a747e4fSDavid du Colombier 	int data, i;
6769a747e4fSDavid du Colombier 
6779a747e4fSDavid du Colombier 	if(ctlr->id == Pnic){
6789a747e4fSDavid du Colombier 		i = 1000;
6799a747e4fSDavid du Colombier 		csr32w(ctlr, 20, 0x60020000|(phyad<<23)|(regad<<18));
6809a747e4fSDavid du Colombier 		do{
6819a747e4fSDavid du Colombier 			microdelay(1);
6829a747e4fSDavid du Colombier 			data = csr32r(ctlr, 20);
6839a747e4fSDavid du Colombier 		}while((data & 0x80000000) && --i);
6849a747e4fSDavid du Colombier 
6859a747e4fSDavid du Colombier 		if(i == 0)
6869a747e4fSDavid du Colombier 			return -1;
6879a747e4fSDavid du Colombier 		return data & 0xFFFF;
6889a747e4fSDavid du Colombier 	}
6899a747e4fSDavid du Colombier 
6909a747e4fSDavid du Colombier 	/*
6919a747e4fSDavid du Colombier 	 * Preamble;
6929a747e4fSDavid du Colombier 	 * ST+OP+PHYAD+REGAD;
6939a747e4fSDavid du Colombier 	 * TA + 16 data bits.
6949a747e4fSDavid du Colombier 	 */
6959a747e4fSDavid du Colombier 	miimdo(ctlr, 0xFFFFFFFF, 32);
6969a747e4fSDavid du Colombier 	miimdo(ctlr, 0x1800|(phyad<<5)|regad, 14);
6979a747e4fSDavid du Colombier 	data = miimdi(ctlr, 18);
6989a747e4fSDavid du Colombier 
6999a747e4fSDavid du Colombier 	if(data & 0x10000)
7009a747e4fSDavid du Colombier 		return -1;
7019a747e4fSDavid du Colombier 
7029a747e4fSDavid du Colombier 	return data & 0xFFFF;
7039a747e4fSDavid du Colombier }
7049a747e4fSDavid du Colombier 
7059a747e4fSDavid du Colombier static void
miiw(Ctlr * ctlr,int phyad,int regad,int data)7069a747e4fSDavid du Colombier miiw(Ctlr* ctlr, int phyad, int regad, int data)
7079a747e4fSDavid du Colombier {
7089a747e4fSDavid du Colombier 	/*
7099a747e4fSDavid du Colombier 	 * Preamble;
7109a747e4fSDavid du Colombier 	 * ST+OP+PHYAD+REGAD+TA + 16 data bits;
7119a747e4fSDavid du Colombier 	 * Z.
7129a747e4fSDavid du Colombier 	 */
7139a747e4fSDavid du Colombier 	miimdo(ctlr, 0xFFFFFFFF, 32);
7149a747e4fSDavid du Colombier 	data &= 0xFFFF;
7159a747e4fSDavid du Colombier 	data |= (0x05<<(5+5+2+16))|(phyad<<(5+2+16))|(regad<<(2+16))|(0x02<<16);
7169a747e4fSDavid du Colombier 	miimdo(ctlr, data, 32);
7179a747e4fSDavid du Colombier 	csr9w(ctlr, Mdc);
7189a747e4fSDavid du Colombier 	csr9w(ctlr, 0);
7199a747e4fSDavid du Colombier }
7209a747e4fSDavid du Colombier 
7219a747e4fSDavid du Colombier static int
sromr(Ctlr * ctlr,int r)7229a747e4fSDavid du Colombier sromr(Ctlr* ctlr, int r)
7239a747e4fSDavid du Colombier {
7249a747e4fSDavid du Colombier 	int i, op, data, size;
7259a747e4fSDavid du Colombier 
7269a747e4fSDavid du Colombier 	if(ctlr->id == Pnic){
7279a747e4fSDavid du Colombier 		i = 1000;
7289a747e4fSDavid du Colombier 		csr32w(ctlr, 19, 0x600|r);
7299a747e4fSDavid du Colombier 		do{
7309a747e4fSDavid du Colombier 			microdelay(1);
7319a747e4fSDavid du Colombier 			data = csr32r(ctlr, 19);
7329a747e4fSDavid du Colombier 		}while((data & 0x80000000) && --i);
7339a747e4fSDavid du Colombier 
7349a747e4fSDavid du Colombier 		if(ctlr->sromsz == 0)
7359a747e4fSDavid du Colombier 			ctlr->sromsz = 6;
7369a747e4fSDavid du Colombier 
7379a747e4fSDavid du Colombier 		return csr32r(ctlr, 9) & 0xFFFF;
7389a747e4fSDavid du Colombier 	}
7399a747e4fSDavid du Colombier 
7409a747e4fSDavid du Colombier 	/*
7419a747e4fSDavid du Colombier 	 * This sequence for reading a 16-bit register 'r'
7429a747e4fSDavid du Colombier 	 * in the EEPROM is taken straight from Section
7439a747e4fSDavid du Colombier 	 * 7.4 of the 21140 Hardware Reference Manual.
7449a747e4fSDavid du Colombier 	 */
7459a747e4fSDavid du Colombier reread:
7469a747e4fSDavid du Colombier 	csr9w(ctlr, Rd|Ss);
7479a747e4fSDavid du Colombier 	csr9w(ctlr, Rd|Ss|Scs);
7489a747e4fSDavid du Colombier 	csr9w(ctlr, Rd|Ss|Sclk|Scs);
7499a747e4fSDavid du Colombier 	csr9w(ctlr, Rd|Ss);
7509a747e4fSDavid du Colombier 
7519a747e4fSDavid du Colombier 	op = 0x06;
7529a747e4fSDavid du Colombier 	for(i = 3-1; i >= 0; i--){
7539a747e4fSDavid du Colombier 		data = Rd|Ss|(((op>>i) & 0x01)<<2)|Scs;
7549a747e4fSDavid du Colombier 		csr9w(ctlr, data);
7559a747e4fSDavid du Colombier 		csr9w(ctlr, data|Sclk);
7569a747e4fSDavid du Colombier 		csr9w(ctlr, data);
7579a747e4fSDavid du Colombier 	}
7589a747e4fSDavid du Colombier 
7599a747e4fSDavid du Colombier 	/*
7609a747e4fSDavid du Colombier 	 * First time through must work out the EEPROM size.
7619a747e4fSDavid du Colombier 	 */
7629a747e4fSDavid du Colombier 	if((size = ctlr->sromsz) == 0)
7639a747e4fSDavid du Colombier 		size = 8;
7649a747e4fSDavid du Colombier 
7659a747e4fSDavid du Colombier 	for(size = size-1; size >= 0; size--){
7669a747e4fSDavid du Colombier 		data = Rd|Ss|(((r>>size) & 0x01)<<2)|Scs;
7679a747e4fSDavid du Colombier 		csr9w(ctlr, data);
7689a747e4fSDavid du Colombier 		csr9w(ctlr, data|Sclk);
7699a747e4fSDavid du Colombier 		csr9w(ctlr, data);
7709a747e4fSDavid du Colombier 		microdelay(1);
7719a747e4fSDavid du Colombier 		if(!(csr32r(ctlr, 9) & Sdo))
7729a747e4fSDavid du Colombier 			break;
7739a747e4fSDavid du Colombier 	}
7749a747e4fSDavid du Colombier 
7759a747e4fSDavid du Colombier 	data = 0;
7769a747e4fSDavid du Colombier 	for(i = 16-1; i >= 0; i--){
7779a747e4fSDavid du Colombier 		csr9w(ctlr, Rd|Ss|Sclk|Scs);
7789a747e4fSDavid du Colombier 		if(csr32r(ctlr, 9) & Sdo)
7799a747e4fSDavid du Colombier 			data |= (1<<i);
7809a747e4fSDavid du Colombier 		csr9w(ctlr, Rd|Ss|Scs);
7819a747e4fSDavid du Colombier 	}
7829a747e4fSDavid du Colombier 
7839a747e4fSDavid du Colombier 	csr9w(ctlr, 0);
7849a747e4fSDavid du Colombier 
7859a747e4fSDavid du Colombier 	if(ctlr->sromsz == 0){
7869a747e4fSDavid du Colombier 		ctlr->sromsz = 8-size;
7879a747e4fSDavid du Colombier 		goto reread;
7889a747e4fSDavid du Colombier 	}
7899a747e4fSDavid du Colombier 
7909a747e4fSDavid du Colombier 	return data & 0xFFFF;
7919a747e4fSDavid du Colombier }
7929a747e4fSDavid du Colombier 
7939a747e4fSDavid du Colombier static void
softreset(Ctlr * ctlr)7949a747e4fSDavid du Colombier softreset(Ctlr* ctlr)
7959a747e4fSDavid du Colombier {
7969a747e4fSDavid du Colombier 	/*
7979a747e4fSDavid du Colombier 	 * Soft-reset the controller and initialise bus mode.
7989a747e4fSDavid du Colombier 	 * Delay should be >= 50 PCI cycles (2×S @ 25MHz).
7999a747e4fSDavid du Colombier 	 */
8009a747e4fSDavid du Colombier 	csr32w(ctlr, 0, Swr);
8019a747e4fSDavid du Colombier 	microdelay(10);
8029a747e4fSDavid du Colombier 	csr32w(ctlr, 0, Rml|Cal16|Dbo);
8039a747e4fSDavid du Colombier 	delay(1);
8049a747e4fSDavid du Colombier }
8059a747e4fSDavid du Colombier 
8069a747e4fSDavid du Colombier static int
type5block(Ctlr * ctlr,uchar * block)8079a747e4fSDavid du Colombier type5block(Ctlr* ctlr, uchar* block)
8089a747e4fSDavid du Colombier {
8099a747e4fSDavid du Colombier 	int csr15, i, len;
8109a747e4fSDavid du Colombier 
8119a747e4fSDavid du Colombier 	/*
8129a747e4fSDavid du Colombier 	 * Reset or GPR sequence. Reset should be once only,
8139a747e4fSDavid du Colombier 	 * before the GPR sequence.
8149a747e4fSDavid du Colombier 	 * Note 'block' is not a pointer to the block head but
8159a747e4fSDavid du Colombier 	 * a pointer to the data in the block starting at the
8169a747e4fSDavid du Colombier 	 * reset length value so type5block can be used for the
8179a747e4fSDavid du Colombier 	 * sequences contained in type 1 and type 3 blocks.
8189a747e4fSDavid du Colombier 	 * The SROM docs state the 21140 type 5 block is the
8199a747e4fSDavid du Colombier 	 * same as that for the 21143, but the two controllers
8209a747e4fSDavid du Colombier 	 * use different registers and sequence-element lengths
8219a747e4fSDavid du Colombier 	 * so the 21140 code here is a guess for a real type 5
8229a747e4fSDavid du Colombier 	 * sequence.
8239a747e4fSDavid du Colombier 	 */
8249a747e4fSDavid du Colombier 	len = *block++;
8259a747e4fSDavid du Colombier 	if(ctlr->id != Tulip3){
8269a747e4fSDavid du Colombier 		for(i = 0; i < len; i++){
8279a747e4fSDavid du Colombier 			csr32w(ctlr, 12, *block);
8289a747e4fSDavid du Colombier 			block++;
8299a747e4fSDavid du Colombier 		}
8309a747e4fSDavid du Colombier 		return len;
8319a747e4fSDavid du Colombier 	}
8329a747e4fSDavid du Colombier 
8339a747e4fSDavid du Colombier 	for(i = 0; i < len; i++){
8349a747e4fSDavid du Colombier 		csr15 = *block++<<16;
8359a747e4fSDavid du Colombier 		csr15 |= *block++<<24;
8369a747e4fSDavid du Colombier 		csr32w(ctlr, 15, csr15);
8379a747e4fSDavid du Colombier 		debug("%8.8uX ", csr15);
8389a747e4fSDavid du Colombier 	}
8399a747e4fSDavid du Colombier 	return 2*len;
8409a747e4fSDavid du Colombier }
8419a747e4fSDavid du Colombier 
8429a747e4fSDavid du Colombier static int
typephylink(Ctlr * ctlr,uchar *)8439a747e4fSDavid du Colombier typephylink(Ctlr* ctlr, uchar*)
8449a747e4fSDavid du Colombier {
8459a747e4fSDavid du Colombier 	int an, bmcr, bmsr, csr6, x;
8469a747e4fSDavid du Colombier 
8479a747e4fSDavid du Colombier 	/*
8489a747e4fSDavid du Colombier 	 * Fail if
8499a747e4fSDavid du Colombier 	 *	auto-negotiataion enabled but not complete;
8509a747e4fSDavid du Colombier 	 *	no valid link established.
8519a747e4fSDavid du Colombier 	 */
8529a747e4fSDavid du Colombier 	bmcr = miir(ctlr, ctlr->curphyad, Bmcr);
8539a747e4fSDavid du Colombier 	miir(ctlr, ctlr->curphyad, Bmsr);
8549a747e4fSDavid du Colombier 	bmsr = miir(ctlr, ctlr->curphyad, Bmsr);
8559a747e4fSDavid du Colombier 	debug("bmcr 0x%2.2uX bmsr 0x%2.2uX\n", bmcr, bmsr);
8569a747e4fSDavid du Colombier 	if(((bmcr & 0x1000) && !(bmsr & 0x0020)) || !(bmsr & 0x0004))
8579a747e4fSDavid du Colombier 		return 0;
8589a747e4fSDavid du Colombier 
8599a747e4fSDavid du Colombier 	if(bmcr & 0x1000){
8609a747e4fSDavid du Colombier 		an = miir(ctlr, ctlr->curphyad, Anar);
8619a747e4fSDavid du Colombier 		an &= miir(ctlr, ctlr->curphyad, Anlpar) & 0x3E0;
8629a747e4fSDavid du Colombier 		debug("an 0x%2.uX 0x%2.2uX 0x%2.2uX\n",
8639a747e4fSDavid du Colombier 	    		miir(ctlr, ctlr->curphyad, Anar),
8649a747e4fSDavid du Colombier 			miir(ctlr, ctlr->curphyad, Anlpar),
8659a747e4fSDavid du Colombier 			an);
8669a747e4fSDavid du Colombier 
8679a747e4fSDavid du Colombier 		if(an & 0x0100)
8689a747e4fSDavid du Colombier 			x = 0x4000;
8699a747e4fSDavid du Colombier 		else if(an & 0x0080)
8709a747e4fSDavid du Colombier 			x = 0x2000;
8719a747e4fSDavid du Colombier 		else if(an & 0x0040)
8729a747e4fSDavid du Colombier 			x = 0x1000;
8739a747e4fSDavid du Colombier 		else if(an & 0x0020)
8749a747e4fSDavid du Colombier 			x = 0x0800;
8759a747e4fSDavid du Colombier 		else
8769a747e4fSDavid du Colombier 			x = 0;
8779a747e4fSDavid du Colombier 	}
8789a747e4fSDavid du Colombier 	else if((bmcr & 0x2100) == 0x2100)
8799a747e4fSDavid du Colombier 		x = 0x4000;
8809a747e4fSDavid du Colombier 	else if(bmcr & 0x2000){
8819a747e4fSDavid du Colombier 		/*
8829a747e4fSDavid du Colombier 		 * If FD capable, force it if necessary.
8839a747e4fSDavid du Colombier 		 */
8849a747e4fSDavid du Colombier 		if((bmsr & 0x4000) && ctlr->fd){
8859a747e4fSDavid du Colombier 			miiw(ctlr, ctlr->curphyad, Bmcr, 0x2100);
8869a747e4fSDavid du Colombier 			x = 0x4000;
8879a747e4fSDavid du Colombier 		}
8889a747e4fSDavid du Colombier 		else
8899a747e4fSDavid du Colombier 			x = 0x2000;
8909a747e4fSDavid du Colombier 	}
8919a747e4fSDavid du Colombier 	else if(bmcr & 0x0100)
8929a747e4fSDavid du Colombier 		x = 0x1000;
8939a747e4fSDavid du Colombier 	else
8949a747e4fSDavid du Colombier 		x = 0x0800;
8959a747e4fSDavid du Colombier 
8969a747e4fSDavid du Colombier 	csr6 = Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE;
8979a747e4fSDavid du Colombier 	if(ctlr->fdx & x)
8989a747e4fSDavid du Colombier 		csr6 |= Fd;
8999a747e4fSDavid du Colombier 	if(ctlr->ttm & x)
9009a747e4fSDavid du Colombier 		csr6 |= Ttm;
9019a747e4fSDavid du Colombier 	debug("csr6 0x%8.8uX 0x%8.8uX 0x%8.8luX\n",
9029a747e4fSDavid du Colombier 		csr6, ctlr->csr6, csr32r(ctlr, 6));
9039a747e4fSDavid du Colombier 	if(csr6 != ctlr->csr6){
9049a747e4fSDavid du Colombier 		ctlr->csr6 = csr6;
9059a747e4fSDavid du Colombier 		csr32w(ctlr, 6, csr6);
9069a747e4fSDavid du Colombier 	}
9079a747e4fSDavid du Colombier 
9089a747e4fSDavid du Colombier 	return 1;
9099a747e4fSDavid du Colombier }
9109a747e4fSDavid du Colombier 
9119a747e4fSDavid du Colombier static int
typephymode(Ctlr * ctlr,uchar * block,int wait)9129a747e4fSDavid du Colombier typephymode(Ctlr* ctlr, uchar* block, int wait)
9139a747e4fSDavid du Colombier {
9149a747e4fSDavid du Colombier 	uchar *p;
9159a747e4fSDavid du Colombier 	int len, mc, nway, phyx, timeo;
9169a747e4fSDavid du Colombier 
9179a747e4fSDavid du Colombier 	if(DEBUG){
9189a747e4fSDavid du Colombier 		int i;
9199a747e4fSDavid du Colombier 
9209a747e4fSDavid du Colombier 		len = (block[0] & ~0x80)+1;
9219a747e4fSDavid du Colombier 		for(i = 0; i < len; i++)
9229a747e4fSDavid du Colombier 			debug("%2.2uX ", block[i]);
9239a747e4fSDavid du Colombier 		debug("\n");
9249a747e4fSDavid du Colombier 	}
9259a747e4fSDavid du Colombier 
9269a747e4fSDavid du Colombier 	if(block[1] == 1)
9279a747e4fSDavid du Colombier 		len = 1;
9289a747e4fSDavid du Colombier 	else if(block[1] == 3)
9299a747e4fSDavid du Colombier 		len = 2;
9309a747e4fSDavid du Colombier 	else
9319a747e4fSDavid du Colombier 		return -1;
9329a747e4fSDavid du Colombier 
9339a747e4fSDavid du Colombier 	/*
9349a747e4fSDavid du Colombier 	 * Snarf the media capabilities, nway advertisment,
9359a747e4fSDavid du Colombier 	 * FDX and TTM bitmaps.
9369a747e4fSDavid du Colombier 	 */
9379a747e4fSDavid du Colombier 	p = &block[5+len*block[3]+len*block[4+len*block[3]]];
9389a747e4fSDavid du Colombier 	mc = *p++;
9399a747e4fSDavid du Colombier 	mc |= *p++<<8;
9409a747e4fSDavid du Colombier 	nway = *p++;
9419a747e4fSDavid du Colombier 	nway |= *p++<<8;
9429a747e4fSDavid du Colombier 	ctlr->fdx = *p++;
9439a747e4fSDavid du Colombier 	ctlr->fdx |= *p++<<8;
9449a747e4fSDavid du Colombier 	ctlr->ttm = *p++;
9459a747e4fSDavid du Colombier 	ctlr->ttm |= *p<<8;
9469a747e4fSDavid du Colombier 	debug("mc %4.4uX nway %4.4uX fdx %4.4uX ttm %4.4uX\n",
9479a747e4fSDavid du Colombier 		mc, nway, ctlr->fdx, ctlr->ttm);
9489a747e4fSDavid du Colombier 	USED(mc);
9499a747e4fSDavid du Colombier 
9509a747e4fSDavid du Colombier 	phyx = block[2];
9519a747e4fSDavid du Colombier 	ctlr->curphyad = ctlr->phy[phyx];
9529a747e4fSDavid du Colombier 
9539a747e4fSDavid du Colombier 	ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE;
9549a747e4fSDavid du Colombier 	//csr32w(ctlr, 6, ctlr->csr6);
9559a747e4fSDavid du Colombier 	if(typephylink(ctlr, block))
9569a747e4fSDavid du Colombier 		return 0;
9579a747e4fSDavid du Colombier 
9589a747e4fSDavid du Colombier 	if(!(ctlr->phyreset & (1<<phyx))){
9599a747e4fSDavid du Colombier 		debug("reset seq: len %d: ", block[3]);
9609a747e4fSDavid du Colombier 		if(ctlr->type5block)
9619a747e4fSDavid du Colombier 			type5block(ctlr, &ctlr->type5block[2]);
9629a747e4fSDavid du Colombier 		else
9639a747e4fSDavid du Colombier 			type5block(ctlr, &block[4+len*block[3]]);
9649a747e4fSDavid du Colombier 		debug("\n");
9659a747e4fSDavid du Colombier 		ctlr->phyreset |= (1<<phyx);
9669a747e4fSDavid du Colombier 	}
9679a747e4fSDavid du Colombier 
9689a747e4fSDavid du Colombier 	/*
9699a747e4fSDavid du Colombier 	 * GPR sequence.
9709a747e4fSDavid du Colombier 	 */
9719a747e4fSDavid du Colombier 	debug("gpr seq: len %d: ", block[3]);
9729a747e4fSDavid du Colombier 	type5block(ctlr, &block[3]);
9739a747e4fSDavid du Colombier 	debug("\n");
9749a747e4fSDavid du Colombier 
9759a747e4fSDavid du Colombier 	ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE;
9769a747e4fSDavid du Colombier 	//csr32w(ctlr, 6, ctlr->csr6);
9779a747e4fSDavid du Colombier 	if(typephylink(ctlr, block))
9789a747e4fSDavid du Colombier 		return 0;
9799a747e4fSDavid du Colombier 
9809a747e4fSDavid du Colombier 	/*
9819a747e4fSDavid du Colombier 	 * Turn off auto-negotiation, set the auto-negotiation
9829a747e4fSDavid du Colombier 	 * advertisment register then start the auto-negotiation
9839a747e4fSDavid du Colombier 	 * process again.
9849a747e4fSDavid du Colombier 	 */
9859a747e4fSDavid du Colombier 	miiw(ctlr, ctlr->curphyad, Bmcr, 0);
9869a747e4fSDavid du Colombier 	miiw(ctlr, ctlr->curphyad, Anar, nway|1);
9879a747e4fSDavid du Colombier 	miiw(ctlr, ctlr->curphyad, Bmcr, 0x1000);
9889a747e4fSDavid du Colombier 
9899a747e4fSDavid du Colombier 	if(!wait)
9909a747e4fSDavid du Colombier 		return 0;
9919a747e4fSDavid du Colombier 
9929a747e4fSDavid du Colombier 	for(timeo = 0; timeo < 30; timeo++){
9939a747e4fSDavid du Colombier 		if(typephylink(ctlr, block))
9949a747e4fSDavid du Colombier 			return 0;
9959a747e4fSDavid du Colombier 		delay(100);
9969a747e4fSDavid du Colombier 	}
9979a747e4fSDavid du Colombier 
9989a747e4fSDavid du Colombier 	return -1;
9999a747e4fSDavid du Colombier }
10009a747e4fSDavid du Colombier 
10019a747e4fSDavid du Colombier static int
typesymmode(Ctlr * ctlr,uchar * block,int wait)10029a747e4fSDavid du Colombier typesymmode(Ctlr *ctlr, uchar *block, int wait)
10039a747e4fSDavid du Colombier {
10049a747e4fSDavid du Colombier 	uint gpmode, gpdata, command;
10059a747e4fSDavid du Colombier 
10069a747e4fSDavid du Colombier 	USED(wait);
10079a747e4fSDavid du Colombier 	gpmode = block[3] | ((uint) block[4] << 8);
10089a747e4fSDavid du Colombier 	gpdata = block[5] | ((uint) block[6] << 8);
10099a747e4fSDavid du Colombier 	command = (block[7] | ((uint) block[8] << 8)) & 0x71;
10109a747e4fSDavid du Colombier 	if (command & 0x8000) {
10119a747e4fSDavid du Colombier 		print("ether2114x.c: FIXME: handle type 4 mode blocks where cmd.active_invalid != 0\n");
10129a747e4fSDavid du Colombier 		return -1;
10139a747e4fSDavid du Colombier 	}
10149a747e4fSDavid du Colombier 	csr32w(ctlr, 15, gpmode);
10159a747e4fSDavid du Colombier 	csr32w(ctlr, 15, gpdata);
10169a747e4fSDavid du Colombier 	ctlr->csr6 = (command & 0x71) << 18;
10179a747e4fSDavid du Colombier 	csr32w(ctlr, 6, ctlr->csr6);
10189a747e4fSDavid du Colombier 	return 0;
10199a747e4fSDavid du Colombier }
10209a747e4fSDavid du Colombier 
10219a747e4fSDavid du Colombier static int
type2mode(Ctlr * ctlr,uchar * block,int)10229a747e4fSDavid du Colombier type2mode(Ctlr* ctlr, uchar* block, int)
10239a747e4fSDavid du Colombier {
10249a747e4fSDavid du Colombier 	uchar *p;
10259a747e4fSDavid du Colombier 	int csr6, csr13, csr14, csr15, gpc, gpd;
10269a747e4fSDavid du Colombier 
10279a747e4fSDavid du Colombier 	csr6 = Sc|Mbo|Ca|Sb|TrMODE;
10289a747e4fSDavid du Colombier 	debug("type2mode: medium 0x%2.2uX\n", block[2]);
10299a747e4fSDavid du Colombier 
10309a747e4fSDavid du Colombier 	/*
10319a747e4fSDavid du Colombier 	 * Don't attempt full-duplex
10329a747e4fSDavid du Colombier 	 * unless explicitly requested.
10339a747e4fSDavid du Colombier 	 */
10349a747e4fSDavid du Colombier 	if((block[2] & 0x3F) == 0x04){	/* 10BASE-TFD */
10359a747e4fSDavid du Colombier 		if(!ctlr->fd)
10369a747e4fSDavid du Colombier 			return -1;
10379a747e4fSDavid du Colombier 		csr6 |= Fd;
10389a747e4fSDavid du Colombier 	}
10399a747e4fSDavid du Colombier 
10409a747e4fSDavid du Colombier 	/*
10419a747e4fSDavid du Colombier 	 * Operating mode programming values from the datasheet
10429a747e4fSDavid du Colombier 	 * unless media specific data is explicitly given.
10439a747e4fSDavid du Colombier 	 */
10449a747e4fSDavid du Colombier 	p = &block[3];
10459a747e4fSDavid du Colombier 	if(block[2] & 0x40){
10469a747e4fSDavid du Colombier 		csr13 = (block[4]<<8)|block[3];
10479a747e4fSDavid du Colombier 		csr14 = (block[6]<<8)|block[5];
10489a747e4fSDavid du Colombier 		csr15 = (block[8]<<8)|block[7];
10499a747e4fSDavid du Colombier 		p += 6;
10509a747e4fSDavid du Colombier 	}
10519a747e4fSDavid du Colombier 	else switch(block[2] & 0x3F){
10529a747e4fSDavid du Colombier 	default:
10539a747e4fSDavid du Colombier 		return -1;
10549a747e4fSDavid du Colombier 	case 0x00:			/* 10BASE-T */
10559a747e4fSDavid du Colombier 		csr13 = 0x00000001;
10569a747e4fSDavid du Colombier 		csr14 = 0x00007F3F;
10579a747e4fSDavid du Colombier 		csr15 = 0x00000008;
10589a747e4fSDavid du Colombier 		break;
10599a747e4fSDavid du Colombier 	case 0x01:			/* 10BASE-2 */
10609a747e4fSDavid du Colombier 		csr13 = 0x00000009;
10619a747e4fSDavid du Colombier 		csr14 = 0x00000705;
10629a747e4fSDavid du Colombier 		csr15 = 0x00000006;
10639a747e4fSDavid du Colombier 		break;
10649a747e4fSDavid du Colombier 	case 0x02:			/* 10BASE-5 (AUI) */
10659a747e4fSDavid du Colombier 		csr13 = 0x00000009;
10669a747e4fSDavid du Colombier 		csr14 = 0x00000705;
10679a747e4fSDavid du Colombier 		csr15 = 0x0000000E;
10689a747e4fSDavid du Colombier 		break;
10699a747e4fSDavid du Colombier 	case 0x04:			/* 10BASE-TFD */
10709a747e4fSDavid du Colombier 		csr13 = 0x00000001;
10719a747e4fSDavid du Colombier 		csr14 = 0x00007F3D;
10729a747e4fSDavid du Colombier 		csr15 = 0x00000008;
10739a747e4fSDavid du Colombier 		break;
10749a747e4fSDavid du Colombier 	}
10759a747e4fSDavid du Colombier 	gpc = *p++<<16;
10769a747e4fSDavid du Colombier 	gpc |= *p++<<24;
10779a747e4fSDavid du Colombier 	gpd = *p++<<16;
10789a747e4fSDavid du Colombier 	gpd |= *p<<24;
10799a747e4fSDavid du Colombier 
10809a747e4fSDavid du Colombier 	csr32w(ctlr, 13, 0);
10819a747e4fSDavid du Colombier 	csr32w(ctlr, 14, csr14);
10829a747e4fSDavid du Colombier 	csr32w(ctlr, 15, gpc|csr15);
10839a747e4fSDavid du Colombier 	delay(10);
10849a747e4fSDavid du Colombier 	csr32w(ctlr, 15, gpd|csr15);
10859a747e4fSDavid du Colombier 	csr32w(ctlr, 13, csr13);
10869a747e4fSDavid du Colombier 
10879a747e4fSDavid du Colombier 	ctlr->csr6 = csr6;
10889a747e4fSDavid du Colombier 	csr32w(ctlr, 6, ctlr->csr6);
10899a747e4fSDavid du Colombier 
10909a747e4fSDavid du Colombier 	debug("type2mode: csr13 %8.8uX csr14 %8.8uX csr15 %8.8uX\n",
10919a747e4fSDavid du Colombier 		csr13, csr14, csr15);
10929a747e4fSDavid du Colombier 	debug("type2mode: gpc %8.8uX gpd %8.8uX csr6 %8.8uX\n",
10939a747e4fSDavid du Colombier 		gpc, gpd, csr6);
10949a747e4fSDavid du Colombier 
10959a747e4fSDavid du Colombier 	return 0;
10969a747e4fSDavid du Colombier }
10979a747e4fSDavid du Colombier 
10989a747e4fSDavid du Colombier static int
type0link(Ctlr * ctlr,uchar * block)10999a747e4fSDavid du Colombier type0link(Ctlr* ctlr, uchar* block)
11009a747e4fSDavid du Colombier {
11019a747e4fSDavid du Colombier 	int m, polarity, sense;
11029a747e4fSDavid du Colombier 
11039a747e4fSDavid du Colombier 	m = (block[3]<<8)|block[2];
11049a747e4fSDavid du Colombier 	sense = 1<<((m & 0x000E)>>1);
11059a747e4fSDavid du Colombier 	if(m & 0x0080)
11069a747e4fSDavid du Colombier 		polarity = sense;
11079a747e4fSDavid du Colombier 	else
11089a747e4fSDavid du Colombier 		polarity = 0;
11099a747e4fSDavid du Colombier 
11109a747e4fSDavid du Colombier 	return (csr32r(ctlr, 12) & sense)^polarity;
11119a747e4fSDavid du Colombier }
11129a747e4fSDavid du Colombier 
11139a747e4fSDavid du Colombier static int
type0mode(Ctlr * ctlr,uchar * block,int wait)11149a747e4fSDavid du Colombier type0mode(Ctlr* ctlr, uchar* block, int wait)
11159a747e4fSDavid du Colombier {
11169a747e4fSDavid du Colombier 	int csr6, m, timeo;
11179a747e4fSDavid du Colombier 
11189a747e4fSDavid du Colombier 	csr6 = Sc|Mbo|Hbd|Ca|Sb|TrMODE;
11199a747e4fSDavid du Colombier debug("type0: medium 0x%uX, fd %d: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n",
11209a747e4fSDavid du Colombier     ctlr->medium, ctlr->fd, block[0], block[1], block[2], block[3]);
11219a747e4fSDavid du Colombier 	switch(block[0]){
11229a747e4fSDavid du Colombier 	default:
11239a747e4fSDavid du Colombier 		break;
11249a747e4fSDavid du Colombier 
11259a747e4fSDavid du Colombier 	case 0x04:			/* 10BASE-TFD */
11269a747e4fSDavid du Colombier 	case 0x05:			/* 100BASE-TXFD */
11279a747e4fSDavid du Colombier 	case 0x08:			/* 100BASE-FXFD */
11289a747e4fSDavid du Colombier 		/*
11299a747e4fSDavid du Colombier 		 * Don't attempt full-duplex
11309a747e4fSDavid du Colombier 		 * unless explicitly requested.
11319a747e4fSDavid du Colombier 		 */
11329a747e4fSDavid du Colombier 		if(!ctlr->fd)
11339a747e4fSDavid du Colombier 			return -1;
11349a747e4fSDavid du Colombier 		csr6 |= Fd;
11359a747e4fSDavid du Colombier 		break;
11369a747e4fSDavid du Colombier 	}
11379a747e4fSDavid du Colombier 
11389a747e4fSDavid du Colombier 	m = (block[3]<<8)|block[2];
11399a747e4fSDavid du Colombier 	if(m & 0x0001)
11409a747e4fSDavid du Colombier 		csr6 |= Ps;
11419a747e4fSDavid du Colombier 	if(m & 0x0010)
11429a747e4fSDavid du Colombier 		csr6 |= Ttm;
11439a747e4fSDavid du Colombier 	if(m & 0x0020)
11449a747e4fSDavid du Colombier 		csr6 |= Pcs;
11459a747e4fSDavid du Colombier 	if(m & 0x0040)
11469a747e4fSDavid du Colombier 		csr6 |= Scr;
11479a747e4fSDavid du Colombier 
11489a747e4fSDavid du Colombier 	csr32w(ctlr, 12, block[1]);
11499a747e4fSDavid du Colombier 	microdelay(10);
11509a747e4fSDavid du Colombier 	csr32w(ctlr, 6, csr6);
11519a747e4fSDavid du Colombier 	ctlr->csr6 = csr6;
11529a747e4fSDavid du Colombier 
11539a747e4fSDavid du Colombier 	if(!wait)
11549a747e4fSDavid du Colombier 		return 0;
11559a747e4fSDavid du Colombier 
11569a747e4fSDavid du Colombier 	for(timeo = 0; timeo < 30; timeo++){
11579a747e4fSDavid du Colombier 		if(type0link(ctlr, block))
11589a747e4fSDavid du Colombier 			return 0;
11599a747e4fSDavid du Colombier 		delay(100);
11609a747e4fSDavid du Colombier 	}
11619a747e4fSDavid du Colombier 
11629a747e4fSDavid du Colombier 	return -1;
11639a747e4fSDavid du Colombier }
11649a747e4fSDavid du Colombier 
11659a747e4fSDavid du Colombier static int
mediaxx(Ether * ether,int wait)11669a747e4fSDavid du Colombier mediaxx(Ether* ether, int wait)
11679a747e4fSDavid du Colombier {
11689a747e4fSDavid du Colombier 	Ctlr* ctlr;
11699a747e4fSDavid du Colombier 	uchar *block;
11709a747e4fSDavid du Colombier 
11719a747e4fSDavid du Colombier 	ctlr = ether->ctlr;
11729a747e4fSDavid du Colombier 	block = ctlr->infoblock[ctlr->curk];
11739a747e4fSDavid du Colombier 	if(block[0] & 0x80){
11749a747e4fSDavid du Colombier 		switch(block[1]){
11759a747e4fSDavid du Colombier 		default:
11769a747e4fSDavid du Colombier 			return -1;
11779a747e4fSDavid du Colombier 		case 0:
11789a747e4fSDavid du Colombier 			if(ctlr->medium >= 0 && block[2] != ctlr->medium)
11799a747e4fSDavid du Colombier 				return 0;
11809a747e4fSDavid du Colombier /* need this test? */	if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[2])
11819a747e4fSDavid du Colombier 				return 0;
11829a747e4fSDavid du Colombier 			if(type0mode(ctlr, block+2, wait))
11839a747e4fSDavid du Colombier 				return 0;
11849a747e4fSDavid du Colombier 			break;
11859a747e4fSDavid du Colombier 		case 1:
11869a747e4fSDavid du Colombier 			if(typephymode(ctlr, block, wait))
11879a747e4fSDavid du Colombier 				return 0;
11889a747e4fSDavid du Colombier 			break;
11899a747e4fSDavid du Colombier 		case 2:
11909a747e4fSDavid du Colombier 			debug("type2: medium %d block[2] %d\n",
11919a747e4fSDavid du Colombier 				ctlr->medium, block[2]);
11929a747e4fSDavid du Colombier 			if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium))
11939a747e4fSDavid du Colombier 				return 0;
11949a747e4fSDavid du Colombier 			if(type2mode(ctlr, block, wait))
11959a747e4fSDavid du Colombier 				return 0;
11969a747e4fSDavid du Colombier 			break;
11979a747e4fSDavid du Colombier 		case 3:
11989a747e4fSDavid du Colombier 			if(typephymode(ctlr, block, wait))
11999a747e4fSDavid du Colombier 				return 0;
12009a747e4fSDavid du Colombier 			break;
12019a747e4fSDavid du Colombier 		case 4:
12029a747e4fSDavid du Colombier 			debug("type4: medium %d block[2] %d\n",
12039a747e4fSDavid du Colombier 				ctlr->medium, block[2]);
12049a747e4fSDavid du Colombier 			if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium))
12059a747e4fSDavid du Colombier 				return 0;
12069a747e4fSDavid du Colombier 			if(typesymmode(ctlr, block, wait))
12079a747e4fSDavid du Colombier 				return 0;
12089a747e4fSDavid du Colombier 			break;
12099a747e4fSDavid du Colombier 		}
12109a747e4fSDavid du Colombier 	}
12119a747e4fSDavid du Colombier 	else{
12129a747e4fSDavid du Colombier 		if(ctlr->medium >= 0 && block[0] != ctlr->medium)
12139a747e4fSDavid du Colombier 			return 0;
12149a747e4fSDavid du Colombier /* need this test? */if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[0])
12159a747e4fSDavid du Colombier 			return 0;
12169a747e4fSDavid du Colombier 		if(type0mode(ctlr, block, wait))
12179a747e4fSDavid du Colombier 			return 0;
12189a747e4fSDavid du Colombier 	}
12199a747e4fSDavid du Colombier 
12209a747e4fSDavid du Colombier 	if(ctlr->csr6){
12219a747e4fSDavid du Colombier 		if(!(ctlr->csr6 & Ps) || (ctlr->csr6 & Ttm))
12229a747e4fSDavid du Colombier 			return 10;
12239a747e4fSDavid du Colombier 		return 100;
12249a747e4fSDavid du Colombier 	}
12259a747e4fSDavid du Colombier 
12269a747e4fSDavid du Colombier 	return 0;
12279a747e4fSDavid du Colombier }
12289a747e4fSDavid du Colombier 
12299a747e4fSDavid du Colombier static int
media(Ether * ether,int wait)12309a747e4fSDavid du Colombier media(Ether* ether, int wait)
12319a747e4fSDavid du Colombier {
12329a747e4fSDavid du Colombier 	Ctlr* ctlr;
12339a747e4fSDavid du Colombier 	int k, mbps;
12349a747e4fSDavid du Colombier 
12359a747e4fSDavid du Colombier 	ctlr = ether->ctlr;
12369a747e4fSDavid du Colombier 	for(k = 0; k < ctlr->k; k++){
12379a747e4fSDavid du Colombier 		mbps = mediaxx(ether, wait);
12389a747e4fSDavid du Colombier 		if(mbps > 0)
12399a747e4fSDavid du Colombier 			return mbps;
12409a747e4fSDavid du Colombier 		if(ctlr->curk == 0)
12419a747e4fSDavid du Colombier 			ctlr->curk = ctlr->k-1;
12429a747e4fSDavid du Colombier 		else
12439a747e4fSDavid du Colombier 			ctlr->curk--;
12449a747e4fSDavid du Colombier 	}
12459a747e4fSDavid du Colombier 
12469a747e4fSDavid du Colombier 	return 0;
12479a747e4fSDavid du Colombier }
12489a747e4fSDavid du Colombier 
12499a747e4fSDavid du Colombier static char* mediatable[9] = {
12509a747e4fSDavid du Colombier 	"10BASE-T",				/* TP */
12519a747e4fSDavid du Colombier 	"10BASE-2",				/* BNC */
12529a747e4fSDavid du Colombier 	"10BASE-5",				/* AUI */
12539a747e4fSDavid du Colombier 	"100BASE-TX",
12549a747e4fSDavid du Colombier 	"10BASE-TFD",
12559a747e4fSDavid du Colombier 	"100BASE-TXFD",
12569a747e4fSDavid du Colombier 	"100BASE-T4",
12579a747e4fSDavid du Colombier 	"100BASE-FX",
12589a747e4fSDavid du Colombier 	"100BASE-FXFD",
12599a747e4fSDavid du Colombier };
12609a747e4fSDavid du Colombier 
12619a747e4fSDavid du Colombier static uchar en1207[] = {		/* Accton EN1207-COMBO */
12629a747e4fSDavid du Colombier 	0x00, 0x00, 0xE8,		/* [0]  vendor ethernet code */
12639a747e4fSDavid du Colombier 	0x00,				/* [3]  spare */
12649a747e4fSDavid du Colombier 
12659a747e4fSDavid du Colombier 	0x00, 0x08,			/* [4]  connection (LSB+MSB = 0x0800) */
12669a747e4fSDavid du Colombier 	0x1F,				/* [6]  general purpose control */
12679a747e4fSDavid du Colombier 	2,				/* [7]  block count */
12689a747e4fSDavid du Colombier 
12699a747e4fSDavid du Colombier 	0x00,				/* [8]  media code (10BASE-TX) */
12709a747e4fSDavid du Colombier 	0x0B,				/* [9]  general purpose port data */
12719a747e4fSDavid du Colombier 	0x9E, 0x00,			/* [10] command (LSB+MSB = 0x009E) */
12729a747e4fSDavid du Colombier 
12739a747e4fSDavid du Colombier 	0x03,				/* [8]  media code (100BASE-TX) */
12749a747e4fSDavid du Colombier 	0x1B,				/* [9]  general purpose port data */
12759a747e4fSDavid du Colombier 	0x6D, 0x00,			/* [10] command (LSB+MSB = 0x006D) */
12769a747e4fSDavid du Colombier 
12779a747e4fSDavid du Colombier 					/* There is 10BASE-2 as well, but... */
12789a747e4fSDavid du Colombier };
12799a747e4fSDavid du Colombier 
12809a747e4fSDavid du Colombier static uchar ana6910fx[] = {		/* Adaptec (Cogent) ANA-6910FX */
12819a747e4fSDavid du Colombier 	0x00, 0x00, 0x92,		/* [0]  vendor ethernet code */
12829a747e4fSDavid du Colombier 	0x00,				/* [3]  spare */
12839a747e4fSDavid du Colombier 
12849a747e4fSDavid du Colombier 	0x00, 0x08,			/* [4]  connection (LSB+MSB = 0x0800) */
12859a747e4fSDavid du Colombier 	0x3F,				/* [6]  general purpose control */
12869a747e4fSDavid du Colombier 	1,				/* [7]  block count */
12879a747e4fSDavid du Colombier 
12889a747e4fSDavid du Colombier 	0x07,				/* [8]  media code (100BASE-FX) */
12899a747e4fSDavid du Colombier 	0x03,				/* [9]  general purpose port data */
12909a747e4fSDavid du Colombier 	0x2D, 0x00			/* [10] command (LSB+MSB = 0x000D) */
12919a747e4fSDavid du Colombier };
12929a747e4fSDavid du Colombier 
12939a747e4fSDavid du Colombier static uchar smc9332[] = {		/* SMC 9332 */
12949a747e4fSDavid du Colombier 	0x00, 0x00, 0xC0,		/* [0]  vendor ethernet code */
12959a747e4fSDavid du Colombier 	0x00,				/* [3]  spare */
12969a747e4fSDavid du Colombier 
12979a747e4fSDavid du Colombier 	0x00, 0x08,			/* [4]  connection (LSB+MSB = 0x0800) */
12989a747e4fSDavid du Colombier 	0x1F,				/* [6]  general purpose control */
12999a747e4fSDavid du Colombier 	2,				/* [7]  block count */
13009a747e4fSDavid du Colombier 
13019a747e4fSDavid du Colombier 	0x00,				/* [8]  media code (10BASE-TX) */
13029a747e4fSDavid du Colombier 	0x00,				/* [9]  general purpose port data */
13039a747e4fSDavid du Colombier 	0x9E, 0x00,			/* [10] command (LSB+MSB = 0x009E) */
13049a747e4fSDavid du Colombier 
13059a747e4fSDavid du Colombier 	0x03,				/* [8]  media code (100BASE-TX) */
13069a747e4fSDavid du Colombier 	0x09,				/* [9]  general purpose port data */
13079a747e4fSDavid du Colombier 	0x6D, 0x00,			/* [10] command (LSB+MSB = 0x006D) */
13089a747e4fSDavid du Colombier };
13099a747e4fSDavid du Colombier 
13109a747e4fSDavid du Colombier static uchar* leaf21140[] = {
13119a747e4fSDavid du Colombier 	en1207,				/* Accton EN1207-COMBO */
13129a747e4fSDavid du Colombier 	ana6910fx,			/* Adaptec (Cogent) ANA-6910FX */
13139a747e4fSDavid du Colombier 	smc9332,			/* SMC 9332 */
13149a747e4fSDavid du Colombier 	nil,
13159a747e4fSDavid du Colombier };
13169a747e4fSDavid du Colombier 
13179a747e4fSDavid du Colombier /*
13189a747e4fSDavid du Colombier  * Copied to ctlr->srom at offset 20.
13199a747e4fSDavid du Colombier  */
13209a747e4fSDavid du Colombier static uchar leafpnic[] = {
13219a747e4fSDavid du Colombier 	0x00, 0x00, 0x00, 0x00,		/* MAC address */
13229a747e4fSDavid du Colombier 	0x00, 0x00,
13239a747e4fSDavid du Colombier 	0x00,				/* controller 0 device number */
13249a747e4fSDavid du Colombier 	0x1E, 0x00,			/* controller 0 info leaf offset */
13259a747e4fSDavid du Colombier 	0x00,				/* reserved */
13269a747e4fSDavid du Colombier 	0x00, 0x08,			/* selected connection type */
13279a747e4fSDavid du Colombier 	0x00,				/* general purpose control */
13289a747e4fSDavid du Colombier 	0x01,				/* block count */
13299a747e4fSDavid du Colombier 
13309a747e4fSDavid du Colombier 	0x8C,				/* format indicator and count */
13319a747e4fSDavid du Colombier 	0x01,				/* block type */
13329a747e4fSDavid du Colombier 	0x00,				/* PHY number */
13339a747e4fSDavid du Colombier 	0x00,				/* GPR sequence length */
13349a747e4fSDavid du Colombier 	0x00,				/* reset sequence length */
13359a747e4fSDavid du Colombier 	0x00, 0x78,			/* media capabilities */
13369a747e4fSDavid du Colombier 	0xE0, 0x01,			/* Nway advertisment */
13379a747e4fSDavid du Colombier 	0x00, 0x50,			/* FDX bitmap */
13389a747e4fSDavid du Colombier 	0x00, 0x18,			/* TTM bitmap */
13399a747e4fSDavid du Colombier };
13409a747e4fSDavid du Colombier 
13419a747e4fSDavid du Colombier static int
srom(Ctlr * ctlr)13429a747e4fSDavid du Colombier srom(Ctlr* ctlr)
13439a747e4fSDavid du Colombier {
13449a747e4fSDavid du Colombier 	int i, k, oui, phy, x;
13459a747e4fSDavid du Colombier 	uchar *p;
13469a747e4fSDavid du Colombier 
13479a747e4fSDavid du Colombier 	/*
13489a747e4fSDavid du Colombier 	 * This is a partial decoding of the SROM format described in
13499a747e4fSDavid du Colombier 	 * 'Digital Semiconductor 21X4 Serial ROM Format, Version 4.05,
13509a747e4fSDavid du Colombier 	 * 2-Mar-98'. Only the 2114[03] are handled, support for other
13519a747e4fSDavid du Colombier 	 * controllers can be added as needed.
13529a747e4fSDavid du Colombier 	 * Do a dummy read first to get the size and allocate ctlr->srom.
13539a747e4fSDavid du Colombier 	 */
13549a747e4fSDavid du Colombier 	sromr(ctlr, 0);
13559a747e4fSDavid du Colombier 	if(ctlr->srom == nil)
13569a747e4fSDavid du Colombier 		ctlr->srom = malloc((1<<ctlr->sromsz)*sizeof(ushort));
13579a747e4fSDavid du Colombier 	for(i = 0; i < (1<<ctlr->sromsz); i++){
13589a747e4fSDavid du Colombier 		x = sromr(ctlr, i);
13599a747e4fSDavid du Colombier 		ctlr->srom[2*i] = x;
13609a747e4fSDavid du Colombier 		ctlr->srom[2*i+1] = x>>8;
13619a747e4fSDavid du Colombier 	}
13629a747e4fSDavid du Colombier 
13639a747e4fSDavid du Colombier 	/*
13649a747e4fSDavid du Colombier 	 * There are 2 SROM layouts:
13659a747e4fSDavid du Colombier 	 *	e.g. Digital EtherWORKS	station address at offset 20;
13669a747e4fSDavid du Colombier 	 *				this complies with the 21140A SROM
13679a747e4fSDavid du Colombier 	 *				application note from Digital;
13689a747e4fSDavid du Colombier 	 * 	e.g. SMC9332		station address at offset 0 followed by
13699a747e4fSDavid du Colombier 	 *				2 additional bytes, repeated at offset
13709a747e4fSDavid du Colombier 	 *				6; the 8 bytes are also repeated in
13719a747e4fSDavid du Colombier 	 *				reverse order at offset 8.
13729a747e4fSDavid du Colombier 	 * To check which it is, read the SROM and check for the repeating
13739a747e4fSDavid du Colombier 	 * patterns of the non-compliant cards; if that fails use the one at
13749a747e4fSDavid du Colombier 	 * offset 20.
13759a747e4fSDavid du Colombier 	 */
13769a747e4fSDavid du Colombier 	ctlr->sromea = ctlr->srom;
13779a747e4fSDavid du Colombier 	for(i = 0; i < 8; i++){
13789a747e4fSDavid du Colombier 		x = ctlr->srom[i];
13799a747e4fSDavid du Colombier 		if(x != ctlr->srom[15-i] || x != ctlr->srom[16+i]){
13809a747e4fSDavid du Colombier 			ctlr->sromea = &ctlr->srom[20];
13819a747e4fSDavid du Colombier 			break;
13829a747e4fSDavid du Colombier 		}
13839a747e4fSDavid du Colombier 	}
13849a747e4fSDavid du Colombier 
13859a747e4fSDavid du Colombier 	/*
13869a747e4fSDavid du Colombier 	 * Fake up the SROM for the PNIC.
13879a747e4fSDavid du Colombier 	 * It looks like a 21140 with a PHY.
13889a747e4fSDavid du Colombier 	 * The MAC address is byte-swapped in the orginal SROM data.
13899a747e4fSDavid du Colombier 	 */
13909a747e4fSDavid du Colombier 	if(ctlr->id == Pnic){
13919a747e4fSDavid du Colombier 		memmove(&ctlr->srom[20], leafpnic, sizeof(leafpnic));
13929a747e4fSDavid du Colombier 		for(i = 0; i < Eaddrlen; i += 2){
13939a747e4fSDavid du Colombier 			ctlr->srom[20+i] = ctlr->srom[i+1];
13949a747e4fSDavid du Colombier 			ctlr->srom[20+i+1] = ctlr->srom[i];
13959a747e4fSDavid du Colombier 		}
13969a747e4fSDavid du Colombier 	}
13979a747e4fSDavid du Colombier 
13989a747e4fSDavid du Colombier 	/*
13999a747e4fSDavid du Colombier 	 * Next, try to find the info leaf in the SROM for media detection.
14009a747e4fSDavid du Colombier 	 * If it's a non-conforming card try to match the vendor ethernet code
14019a747e4fSDavid du Colombier 	 * and point p at a fake info leaf with compact 21140 entries.
14029a747e4fSDavid du Colombier 	 */
14039a747e4fSDavid du Colombier 	if(ctlr->sromea == ctlr->srom){
14049a747e4fSDavid du Colombier 		p = nil;
14059a747e4fSDavid du Colombier 		for(i = 0; leaf21140[i] != nil; i++){
14069a747e4fSDavid du Colombier 			if(memcmp(leaf21140[i], ctlr->sromea, 3) == 0){
14079a747e4fSDavid du Colombier 				p = &leaf21140[i][4];
14089a747e4fSDavid du Colombier 				break;
14099a747e4fSDavid du Colombier 			}
14109a747e4fSDavid du Colombier 		}
14119a747e4fSDavid du Colombier 		if(p == nil)
14129a747e4fSDavid du Colombier 			return -1;
14139a747e4fSDavid du Colombier 	}
14149a747e4fSDavid du Colombier 	else
14159a747e4fSDavid du Colombier 		p = &ctlr->srom[(ctlr->srom[28]<<8)|ctlr->srom[27]];
14169a747e4fSDavid du Colombier 
14179a747e4fSDavid du Colombier 	/*
14189a747e4fSDavid du Colombier 	 * Set up the info needed for later media detection.
14199a747e4fSDavid du Colombier 	 * For the 21140, set the general-purpose mask in CSR12.
14209a747e4fSDavid du Colombier 	 * The info block entries are stored in order of increasing
14219a747e4fSDavid du Colombier 	 * precedence, so detection will work backwards through the
14229a747e4fSDavid du Colombier 	 * stored indexes into ctlr->srom.
14239a747e4fSDavid du Colombier 	 * If an entry is found which matches the selected connection
14249a747e4fSDavid du Colombier 	 * type, save the index. Otherwise, start at the last entry.
14259a747e4fSDavid du Colombier 	 * If any MII entries are found (type 1 and 3 blocks), scan
14269a747e4fSDavid du Colombier 	 * for PHYs.
14279a747e4fSDavid du Colombier 	 */
14289a747e4fSDavid du Colombier 	ctlr->leaf = p;
14299a747e4fSDavid du Colombier 	ctlr->sct = *p++;
14309a747e4fSDavid du Colombier 	ctlr->sct |= *p++<<8;
14319a747e4fSDavid du Colombier 	if(ctlr->id != Tulip3){
14329a747e4fSDavid du Colombier 		csr32w(ctlr, 12, Gpc|*p++);
14339a747e4fSDavid du Colombier 		delay(200);
14349a747e4fSDavid du Colombier 	}
14359a747e4fSDavid du Colombier 	ctlr->k = *p++;
14369a747e4fSDavid du Colombier 	if(ctlr->k >= nelem(ctlr->infoblock))
14379a747e4fSDavid du Colombier 		ctlr->k = nelem(ctlr->infoblock)-1;
14389a747e4fSDavid du Colombier 	ctlr->sctk = ctlr->k-1;
14399a747e4fSDavid du Colombier 	phy = 0;
14409a747e4fSDavid du Colombier 	for(k = 0; k < ctlr->k; k++){
14419a747e4fSDavid du Colombier 		ctlr->infoblock[k] = p;
14429a747e4fSDavid du Colombier 		/*
14439a747e4fSDavid du Colombier 		 * The RAMIX PMC665 has a badly-coded SROM,
14449a747e4fSDavid du Colombier 		 * hence the test for 21143 and type 3.
14459a747e4fSDavid du Colombier 		 */
14469a747e4fSDavid du Colombier 		if((*p & 0x80) || (ctlr->id == Tulip3 && *(p+1) == 3)){
14479a747e4fSDavid du Colombier 			*p |= 0x80;
14489a747e4fSDavid du Colombier 			if(*(p+1) == 1 || *(p+1) == 3)
14499a747e4fSDavid du Colombier 				phy = 1;
14509a747e4fSDavid du Colombier 			if(*(p+1) == 5)
14519a747e4fSDavid du Colombier 				ctlr->type5block = p;
14529a747e4fSDavid du Colombier 			p += (*p & ~0x80)+1;
14539a747e4fSDavid du Colombier 		}
14549a747e4fSDavid du Colombier 		else{
14559a747e4fSDavid du Colombier 			debug("type0: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n",
14569a747e4fSDavid du Colombier 				p[0], p[1], p[2], p[3]);
14579a747e4fSDavid du Colombier 			if(ctlr->sct != 0x0800 && *p == (ctlr->sct & 0xFF))
14589a747e4fSDavid du Colombier 				ctlr->sctk = k;
14599a747e4fSDavid du Colombier 			p += 4;
14609a747e4fSDavid du Colombier 		}
14619a747e4fSDavid du Colombier 	}
14629a747e4fSDavid du Colombier 	ctlr->curk = ctlr->sctk;
14639a747e4fSDavid du Colombier 	debug("sct 0x%uX medium 0x%uX k %d curk %d phy %d\n",
14649a747e4fSDavid du Colombier 		ctlr->sct, ctlr->medium, ctlr->k, ctlr->curk, phy);
14659a747e4fSDavid du Colombier 
14669a747e4fSDavid du Colombier 	if(phy){
14679a747e4fSDavid du Colombier 		x = 0;
14689a747e4fSDavid du Colombier 		for(k = 0; k < nelem(ctlr->phy); k++){
14699a747e4fSDavid du Colombier 			if((oui = miir(ctlr, k, 2)) == -1 || oui == 0)
14709a747e4fSDavid du Colombier 				continue;
14719a747e4fSDavid du Colombier 			if(DEBUG){
14729a747e4fSDavid du Colombier 				oui = (oui & 0x3FF)<<6;
14739a747e4fSDavid du Colombier 				oui |= miir(ctlr, k, 3)>>10;
14749a747e4fSDavid du Colombier 				miir(ctlr, k, 1);
14759a747e4fSDavid du Colombier 				debug("phy%d: index %d oui %uX reg1 %uX\n",
14769a747e4fSDavid du Colombier 					x, k, oui, miir(ctlr, k, 1));
14779a747e4fSDavid du Colombier 				USED(oui);
14789a747e4fSDavid du Colombier 			}
14799a747e4fSDavid du Colombier 			ctlr->phy[x] = k;
14809a747e4fSDavid du Colombier 		}
14819a747e4fSDavid du Colombier 	}
14829a747e4fSDavid du Colombier 
14839a747e4fSDavid du Colombier 	ctlr->fd = 0;
14849a747e4fSDavid du Colombier 	ctlr->medium = -1;
14859a747e4fSDavid du Colombier 
14869a747e4fSDavid du Colombier 	return 0;
14879a747e4fSDavid du Colombier }
14889a747e4fSDavid du Colombier 
14899a747e4fSDavid du Colombier static void
dec2114xpci(void)14909a747e4fSDavid du Colombier dec2114xpci(void)
14919a747e4fSDavid du Colombier {
14929a747e4fSDavid du Colombier 	Ctlr *ctlr;
14939a747e4fSDavid du Colombier 	Pcidev *p;
14949a747e4fSDavid du Colombier 	int x;
14959a747e4fSDavid du Colombier 
14969a747e4fSDavid du Colombier 	p = nil;
14979a747e4fSDavid du Colombier 	while(p = pcimatch(p, 0, 0)){
14989a747e4fSDavid du Colombier 		if(p->ccrb != 0x02 || p->ccru != 0)
14999a747e4fSDavid du Colombier 			continue;
15009a747e4fSDavid du Colombier 		switch((p->did<<16)|p->vid){
15019a747e4fSDavid du Colombier 		default:
15029a747e4fSDavid du Colombier 			continue;
15039a747e4fSDavid du Colombier 
15049a747e4fSDavid du Colombier 		case Tulip3:			/* 21143 */
15059a747e4fSDavid du Colombier 			/*
15069a747e4fSDavid du Colombier 			 * Exit sleep mode.
15079a747e4fSDavid du Colombier 			 */
15089a747e4fSDavid du Colombier 			x = pcicfgr32(p, 0x40);
15099a747e4fSDavid du Colombier 			x &= ~0xc0000000;
15109a747e4fSDavid du Colombier 			pcicfgw32(p, 0x40, x);
15119a747e4fSDavid du Colombier 			/*FALLTHROUGH*/
15129a747e4fSDavid du Colombier 
15139a747e4fSDavid du Colombier 		case Pnic:			/* PNIC */
15149a747e4fSDavid du Colombier 		case Pnic2:			/* PNIC-II */
15159a747e4fSDavid du Colombier 		case Tulip0:			/* 21140 */
15169a747e4fSDavid du Colombier 			break;
15179a747e4fSDavid du Colombier 		}
15189a747e4fSDavid du Colombier 
15199a747e4fSDavid du Colombier 		/*
15209a747e4fSDavid du Colombier 		 * bar[0] is the I/O port register address and
15219a747e4fSDavid du Colombier 		 * bar[1] is the memory-mapped register address.
15229a747e4fSDavid du Colombier 		 */
15239a747e4fSDavid du Colombier 		ctlr = malloc(sizeof(Ctlr));
15249a747e4fSDavid du Colombier 		ctlr->port = p->mem[0].bar & ~0x01;
15259a747e4fSDavid du Colombier 		ctlr->pcidev = p;
15269a747e4fSDavid du Colombier 		ctlr->id = (p->did<<16)|p->vid;
15279a747e4fSDavid du Colombier 
15289a747e4fSDavid du Colombier 		if(ioalloc(ctlr->port, p->mem[0].size, 0, "dec2114x") < 0){
15299a747e4fSDavid du Colombier 			print("dec2114x: port 0x%uX in use\n", ctlr->port);
15309a747e4fSDavid du Colombier 			free(ctlr);
15319a747e4fSDavid du Colombier 			continue;
15329a747e4fSDavid du Colombier 		}
15339a747e4fSDavid du Colombier 
15349a747e4fSDavid du Colombier 		/*
15359a747e4fSDavid du Colombier 		 * Some cards (e.g. ANA-6910FX) seem to need the Ps bit
15369a747e4fSDavid du Colombier 		 * set or they don't always work right after a hardware
15379a747e4fSDavid du Colombier 		 * reset.
15389a747e4fSDavid du Colombier 		 */
15399a747e4fSDavid du Colombier 		csr32w(ctlr, 6, Mbo|Ps);
15409a747e4fSDavid du Colombier 		softreset(ctlr);
15419a747e4fSDavid du Colombier 
15429a747e4fSDavid du Colombier 		if(srom(ctlr)){
15439a747e4fSDavid du Colombier 			iofree(ctlr->port);
15449a747e4fSDavid du Colombier 			free(ctlr);
15459a747e4fSDavid du Colombier 			continue;
15469a747e4fSDavid du Colombier 		}
15479a747e4fSDavid du Colombier 
15489a747e4fSDavid du Colombier 		switch(ctlr->id){
15499a747e4fSDavid du Colombier 		default:
15509a747e4fSDavid du Colombier 			break;
15519a747e4fSDavid du Colombier 
15529a747e4fSDavid du Colombier 		case Pnic:			/* PNIC */
15539a747e4fSDavid du Colombier 			/*
15549a747e4fSDavid du Colombier 			 * Turn off the jabber timer.
15559a747e4fSDavid du Colombier 			 */
15569a747e4fSDavid du Colombier 			csr32w(ctlr, 15, 0x00000001);
15579a747e4fSDavid du Colombier 			break;
15589a747e4fSDavid du Colombier 		}
15599a747e4fSDavid du Colombier 
15609a747e4fSDavid du Colombier 		if(ctlrhead != nil)
15619a747e4fSDavid du Colombier 			ctlrtail->next = ctlr;
15629a747e4fSDavid du Colombier 		else
15639a747e4fSDavid du Colombier 			ctlrhead = ctlr;
15649a747e4fSDavid du Colombier 		ctlrtail = ctlr;
15659a747e4fSDavid du Colombier 	}
15669a747e4fSDavid du Colombier }
15679a747e4fSDavid du Colombier 
15689a747e4fSDavid du Colombier static int
reset(Ether * ether)15699a747e4fSDavid du Colombier reset(Ether* ether)
15709a747e4fSDavid du Colombier {
15719a747e4fSDavid du Colombier 	Ctlr *ctlr;
15729a747e4fSDavid du Colombier 	int i, x;
15739a747e4fSDavid du Colombier 	uchar ea[Eaddrlen];
15749a747e4fSDavid du Colombier 	static int scandone;
15759a747e4fSDavid du Colombier 
15769a747e4fSDavid du Colombier 	if(scandone == 0){
15779a747e4fSDavid du Colombier 		dec2114xpci();
15789a747e4fSDavid du Colombier 		scandone = 1;
15799a747e4fSDavid du Colombier 	}
15809a747e4fSDavid du Colombier 
15819a747e4fSDavid du Colombier 	/*
15829a747e4fSDavid du Colombier 	 * Any adapter matches if no ether->port is supplied,
15839a747e4fSDavid du Colombier 	 * otherwise the ports must match.
15849a747e4fSDavid du Colombier 	 */
15859a747e4fSDavid du Colombier 	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
15869a747e4fSDavid du Colombier 		if(ctlr->active)
15879a747e4fSDavid du Colombier 			continue;
15889a747e4fSDavid du Colombier 		if(ether->port == 0 || ether->port == ctlr->port){
15899a747e4fSDavid du Colombier 			ctlr->active = 1;
15909a747e4fSDavid du Colombier 			break;
15919a747e4fSDavid du Colombier 		}
15929a747e4fSDavid du Colombier 	}
15939a747e4fSDavid du Colombier 	if(ctlr == nil)
15949a747e4fSDavid du Colombier 		return -1;
15959a747e4fSDavid du Colombier 
15969a747e4fSDavid du Colombier 	ether->ctlr = ctlr;
15979a747e4fSDavid du Colombier 	ether->port = ctlr->port;
15989a747e4fSDavid du Colombier //	ether->irq = ctlr->pcidev->intl;
15999a747e4fSDavid du Colombier ether->irq = 2;		/* arrrrrgh */
16009a747e4fSDavid du Colombier 	ether->tbdf = ctlr->pcidev->tbdf;
16019a747e4fSDavid du Colombier 
16029a747e4fSDavid du Colombier 	/*
16039a747e4fSDavid du Colombier 	 * Check if the adapter's station address is to be overridden.
16049a747e4fSDavid du Colombier 	 * If not, read it from the EEPROM and set in ether->ea prior to
16059a747e4fSDavid du Colombier 	 * loading the station address in the hardware.
16069a747e4fSDavid du Colombier 	 */
16079a747e4fSDavid du Colombier 	memset(ea, 0, Eaddrlen);
16089a747e4fSDavid du Colombier 	if(memcmp(ea, ether->ea, Eaddrlen) == 0)
16099a747e4fSDavid du Colombier 		memmove(ether->ea, ctlr->sromea, Eaddrlen);
16109a747e4fSDavid du Colombier 
16119a747e4fSDavid du Colombier 	/*
16129a747e4fSDavid du Colombier 	 * Look for a medium override in case there's no autonegotiation
16139a747e4fSDavid du Colombier 	 * (no MII) or the autonegotiation fails.
16149a747e4fSDavid du Colombier 	 */
16159a747e4fSDavid du Colombier 	for(i = 0; i < ether->nopt; i++){
16169a747e4fSDavid du Colombier 		if(cistrcmp(ether->opt[i], "FD") == 0){
16179a747e4fSDavid du Colombier 			ctlr->fd = 1;
16189a747e4fSDavid du Colombier 			continue;
16199a747e4fSDavid du Colombier 		}
16209a747e4fSDavid du Colombier 		for(x = 0; x < nelem(mediatable); x++){
16219a747e4fSDavid du Colombier 			debug("compare <%s> <%s>\n", mediatable[x],
16229a747e4fSDavid du Colombier 				ether->opt[i]);
16239a747e4fSDavid du Colombier 			if(cistrcmp(mediatable[x], ether->opt[i]))
16249a747e4fSDavid du Colombier 				continue;
16259a747e4fSDavid du Colombier 			ctlr->medium = x;
16269a747e4fSDavid du Colombier 
16279a747e4fSDavid du Colombier 			switch(ctlr->medium){
16289a747e4fSDavid du Colombier 			default:
16299a747e4fSDavid du Colombier 				ctlr->fd = 0;
16309a747e4fSDavid du Colombier 				break;
16319a747e4fSDavid du Colombier 
16329a747e4fSDavid du Colombier 			case 0x04:		/* 10BASE-TFD */
16339a747e4fSDavid du Colombier 			case 0x05:		/* 100BASE-TXFD */
16349a747e4fSDavid du Colombier 			case 0x08:		/* 100BASE-FXFD */
16359a747e4fSDavid du Colombier 				ctlr->fd = 1;
16369a747e4fSDavid du Colombier 				break;
16379a747e4fSDavid du Colombier 			}
16389a747e4fSDavid du Colombier 			break;
16399a747e4fSDavid du Colombier 		}
16409a747e4fSDavid du Colombier 	}
16419a747e4fSDavid du Colombier 
16429a747e4fSDavid du Colombier 	ether->mbps = media(ether, 1);
16439a747e4fSDavid du Colombier 
16449a747e4fSDavid du Colombier 	/*
16459a747e4fSDavid du Colombier 	 * Initialise descriptor rings, ethernet address.
16469a747e4fSDavid du Colombier 	 */
16479a747e4fSDavid du Colombier 	ctlr->nrdr = Nrde;
16489a747e4fSDavid du Colombier 	ctlr->ntdr = Ntde;
16499a747e4fSDavid du Colombier 	pcisetbme(ctlr->pcidev);
16509a747e4fSDavid du Colombier 	ctlrinit(ether);
16519a747e4fSDavid du Colombier 
16529a747e4fSDavid du Colombier 	/*
16539a747e4fSDavid du Colombier 	 * Linkage to the generic ethernet driver.
16549a747e4fSDavid du Colombier 	 */
16559a747e4fSDavid du Colombier 	ether->attach = attach;
16569a747e4fSDavid du Colombier 	ether->transmit = transmit;
16579a747e4fSDavid du Colombier 	ether->interrupt = interrupt;
16589a747e4fSDavid du Colombier 	ether->ifstat = ifstat;
16599a747e4fSDavid du Colombier 
16609a747e4fSDavid du Colombier 	ether->arg = ether;
16619a747e4fSDavid du Colombier 	ether->promiscuous = promiscuous;
16629a747e4fSDavid du Colombier 
16639a747e4fSDavid du Colombier 	return 0;
16649a747e4fSDavid du Colombier }
16659a747e4fSDavid du Colombier 
16669a747e4fSDavid du Colombier void
ether2114xlink(void)16679a747e4fSDavid du Colombier ether2114xlink(void)
16689a747e4fSDavid du Colombier {
16699a747e4fSDavid du Colombier 	addethercard("21140",  reset);
16709a747e4fSDavid du Colombier 	addethercard("2114x",  reset);
16719a747e4fSDavid du Colombier }
1672