xref: /plan9-contrib/sys/src/9/pc/ether2114x.c (revision 6520663fb7ebac8d17e4a1dbc55d10bf525f7b14)
17dd7cddfSDavid du Colombier /*
27dd7cddfSDavid du Colombier  * Digital Semiconductor DECchip 2114x PCI Fast Ethernet LAN Controller.
37dd7cddfSDavid du Colombier  * To do:
47dd7cddfSDavid du Colombier  *	thresholds;
57dd7cddfSDavid du Colombier  *	ring sizing;
67dd7cddfSDavid du Colombier  *	handle more error conditions;
77dd7cddfSDavid du Colombier  *	tidy setup packet mess;
87dd7cddfSDavid du Colombier  *	push initialisation back to attach;
97dd7cddfSDavid du Colombier  *	full SROM decoding.
107dd7cddfSDavid du Colombier  */
117dd7cddfSDavid du Colombier #include "u.h"
127dd7cddfSDavid du Colombier #include "../port/lib.h"
137dd7cddfSDavid du Colombier #include "mem.h"
147dd7cddfSDavid du Colombier #include "dat.h"
157dd7cddfSDavid du Colombier #include "fns.h"
167dd7cddfSDavid du Colombier #include "io.h"
177dd7cddfSDavid du Colombier #include "../port/error.h"
187dd7cddfSDavid du Colombier #include "../port/netif.h"
197dd7cddfSDavid du Colombier 
207dd7cddfSDavid du Colombier #include "etherif.h"
217dd7cddfSDavid du Colombier 
2259cc4ca5SDavid du Colombier #define DEBUG		(0)
237dd7cddfSDavid du Colombier #define debug		if(DEBUG)print
247dd7cddfSDavid du Colombier 
257dd7cddfSDavid du Colombier enum {
267dd7cddfSDavid du Colombier 	Nrde		= 64,
277dd7cddfSDavid du Colombier 	Ntde		= 64,
287dd7cddfSDavid du Colombier };
297dd7cddfSDavid du Colombier 
3059cc4ca5SDavid du Colombier #define Rbsz		ROUNDUP(sizeof(Etherpkt)+4, 4)
3159cc4ca5SDavid du Colombier 
327dd7cddfSDavid du Colombier enum {					/* CRS0 - Bus Mode */
337dd7cddfSDavid du Colombier 	Swr		= 0x00000001,	/* Software Reset */
347dd7cddfSDavid du Colombier 	Bar		= 0x00000002,	/* Bus Arbitration */
357dd7cddfSDavid du Colombier 	Dsl		= 0x0000007C,	/* Descriptor Skip Length (field) */
367dd7cddfSDavid du Colombier 	Ble		= 0x00000080,	/* Big/Little Endian */
377dd7cddfSDavid du Colombier 	Pbl		= 0x00003F00,	/* Programmable Burst Length (field) */
387dd7cddfSDavid du Colombier 	Cal		= 0x0000C000,	/* Cache Alignment (field) */
397dd7cddfSDavid du Colombier 	Cal8		= 0x00004000,	/* 8 longword boundary alignment */
407dd7cddfSDavid du Colombier 	Cal16		= 0x00008000,	/* 16 longword boundary alignment */
417dd7cddfSDavid du Colombier 	Cal32		= 0x0000C000,	/* 32 longword boundary alignment */
427dd7cddfSDavid du Colombier 	Tap		= 0x000E0000,	/* Transmit Automatic Polling (field) */
437dd7cddfSDavid du Colombier 	Dbo		= 0x00100000,	/* Descriptor Byte Ordering Mode */
447dd7cddfSDavid du Colombier 	Rml		= 0x00200000,	/* Read Multiple */
457dd7cddfSDavid du Colombier };
467dd7cddfSDavid du Colombier 
477dd7cddfSDavid du Colombier enum {					/* CSR[57] - Status and Interrupt Enable */
487dd7cddfSDavid du Colombier 	Ti		= 0x00000001,	/* Transmit Interrupt */
497dd7cddfSDavid du Colombier 	Tps		= 0x00000002,	/* Transmit Process Stopped */
507dd7cddfSDavid du Colombier 	Tu		= 0x00000004,	/* Transmit buffer Unavailable */
517dd7cddfSDavid du Colombier 	Tjt		= 0x00000008,	/* Transmit Jabber Timeout */
527dd7cddfSDavid du Colombier 	Unf		= 0x00000020,	/* transmit UNderFlow */
537dd7cddfSDavid du Colombier 	Ri		= 0x00000040,	/* Receive Interrupt */
547dd7cddfSDavid du Colombier 	Ru		= 0x00000080,	/* Receive buffer Unavailable */
557dd7cddfSDavid du Colombier 	Rps		= 0x00000100,	/* Receive Process Stopped */
567dd7cddfSDavid du Colombier 	Rwt		= 0x00000200,	/* Receive Watchdog Timeout */
577dd7cddfSDavid du Colombier 	Eti		= 0x00000400,	/* Early Transmit Interrupt */
587dd7cddfSDavid du Colombier 	Gte		= 0x00000800,	/* General purpose Timer Expired */
597dd7cddfSDavid du Colombier 	Fbe		= 0x00002000,	/* Fatal Bit Error */
607dd7cddfSDavid du Colombier 	Ais		= 0x00008000,	/* Abnormal Interrupt Summary */
617dd7cddfSDavid du Colombier 	Nis		= 0x00010000,	/* Normal Interrupt Summary */
627dd7cddfSDavid du Colombier 	Rs		= 0x000E0000,	/* Receive process State (field) */
637dd7cddfSDavid du Colombier 	Ts		= 0x00700000,	/* Transmit process State (field) */
647dd7cddfSDavid du Colombier 	Eb		= 0x03800000,	/* Error bits */
657dd7cddfSDavid du Colombier };
667dd7cddfSDavid du Colombier 
677dd7cddfSDavid du Colombier enum {					/* CSR6 - Operating Mode */
687dd7cddfSDavid du Colombier 	Hp		= 0x00000001,	/* Hash/Perfect receive filtering mode */
697dd7cddfSDavid du Colombier 	Sr		= 0x00000002,	/* Start/stop Receive */
707dd7cddfSDavid du Colombier 	Ho		= 0x00000004,	/* Hash-Only filtering mode */
717dd7cddfSDavid du Colombier 	Pb		= 0x00000008,	/* Pass Bad frames */
727dd7cddfSDavid du Colombier 	If		= 0x00000010,	/* Inverse Filtering */
737dd7cddfSDavid du Colombier 	Sb		= 0x00000020,	/* Start/stop Backoff counter */
747dd7cddfSDavid du Colombier 	Pr		= 0x00000040,	/* Promiscuous Mode */
757dd7cddfSDavid du Colombier 	Pm		= 0x00000080,	/* Pass all Multicast */
767dd7cddfSDavid du Colombier 	Fd		= 0x00000200,	/* Full Duplex mode */
777dd7cddfSDavid du Colombier 	Om		= 0x00000C00,	/* Operating Mode (field) */
787dd7cddfSDavid du Colombier 	Fc		= 0x00001000,	/* Force Collision */
797dd7cddfSDavid du Colombier 	St		= 0x00002000,	/* Start/stop Transmission Command */
807dd7cddfSDavid du Colombier 	Tr		= 0x0000C000,	/* ThReshold control bits (field) */
817dd7cddfSDavid du Colombier 	Tr128		= 0x00000000,
827dd7cddfSDavid du Colombier 	Tr256		= 0x00004000,
837dd7cddfSDavid du Colombier 	Tr512		= 0x00008000,
847dd7cddfSDavid du Colombier 	Tr1024		= 0x0000C000,
857dd7cddfSDavid du Colombier 	Ca		= 0x00020000,	/* CApture effect enable */
867dd7cddfSDavid du Colombier 	Ps		= 0x00040000,	/* Port Select */
877dd7cddfSDavid du Colombier 	Hbd		= 0x00080000,	/* HeartBeat Disable */
887dd7cddfSDavid du Colombier 	Imm		= 0x00100000,	/* IMMediate mode */
897dd7cddfSDavid du Colombier 	Sf		= 0x00200000,	/* Store and Forward */
907dd7cddfSDavid du Colombier 	Ttm		= 0x00400000,	/* Transmit Threshold Mode */
917dd7cddfSDavid du Colombier 	Pcs		= 0x00800000,	/* PCS function */
927dd7cddfSDavid du Colombier 	Scr		= 0x01000000,	/* SCRambler mode */
937dd7cddfSDavid du Colombier 	Mbo		= 0x02000000,	/* Must Be One */
947dd7cddfSDavid du Colombier 	Ra		= 0x40000000,	/* Receive All */
957dd7cddfSDavid du Colombier 	Sc		= 0x80000000,	/* Special Capture effect enable */
967dd7cddfSDavid du Colombier 
977dd7cddfSDavid du Colombier 	TrMODE		= Tr512,	/* default transmission threshold */
987dd7cddfSDavid du Colombier };
997dd7cddfSDavid du Colombier 
1007dd7cddfSDavid du Colombier enum {					/* CSR9 - ROM and MII Management */
1017dd7cddfSDavid du Colombier 	Scs		= 0x00000001,	/* serial ROM chip select */
1027dd7cddfSDavid du Colombier 	Sclk		= 0x00000002,	/* serial ROM clock */
1037dd7cddfSDavid du Colombier 	Sdi		= 0x00000004,	/* serial ROM data in */
1047dd7cddfSDavid du Colombier 	Sdo		= 0x00000008,	/* serial ROM data out */
1057dd7cddfSDavid du Colombier 	Ss		= 0x00000800,	/* serial ROM select */
1067dd7cddfSDavid du Colombier 	Wr		= 0x00002000,	/* write */
1077dd7cddfSDavid du Colombier 	Rd		= 0x00004000,	/* read */
1087dd7cddfSDavid du Colombier 
1097dd7cddfSDavid du Colombier 	Mdc		= 0x00010000,	/* MII management clock */
1107dd7cddfSDavid du Colombier 	Mdo		= 0x00020000,	/* MII management write data */
1117dd7cddfSDavid du Colombier 	Mii		= 0x00040000,	/* MII management operation mode (W) */
1127dd7cddfSDavid du Colombier 	Mdi		= 0x00080000,	/* MII management data in */
1137dd7cddfSDavid du Colombier };
1147dd7cddfSDavid du Colombier 
1157dd7cddfSDavid du Colombier enum {					/* CSR12 - General-Purpose Port */
1167dd7cddfSDavid du Colombier 	Gpc		= 0x00000100,	/* General Purpose Control */
1177dd7cddfSDavid du Colombier };
1187dd7cddfSDavid du Colombier 
1197dd7cddfSDavid du Colombier typedef struct Des {
1207dd7cddfSDavid du Colombier 	int	status;
1217dd7cddfSDavid du Colombier 	int	control;
1227dd7cddfSDavid du Colombier 	ulong	addr;
1237dd7cddfSDavid du Colombier 	Block*	bp;
1247dd7cddfSDavid du Colombier } Des;
1257dd7cddfSDavid du Colombier 
1267dd7cddfSDavid du Colombier enum {					/* status */
1277dd7cddfSDavid du Colombier 	Of		= 0x00000001,	/* Rx: OverFlow */
1287dd7cddfSDavid du Colombier 	Ce		= 0x00000002,	/* Rx: CRC Error */
1297dd7cddfSDavid du Colombier 	Db		= 0x00000004,	/* Rx: Dribbling Bit */
1307dd7cddfSDavid du Colombier 	Re		= 0x00000008,	/* Rx: Report on MII Error */
1317dd7cddfSDavid du Colombier 	Rw		= 0x00000010,	/* Rx: Receive Watchdog */
1327dd7cddfSDavid du Colombier 	Ft		= 0x00000020,	/* Rx: Frame Type */
1337dd7cddfSDavid du Colombier 	Cs		= 0x00000040,	/* Rx: Collision Seen */
1347dd7cddfSDavid du Colombier 	Tl		= 0x00000080,	/* Rx: Frame too Long */
1357dd7cddfSDavid du Colombier 	Ls		= 0x00000100,	/* Rx: Last deScriptor */
1367dd7cddfSDavid du Colombier 	Fs		= 0x00000200,	/* Rx: First deScriptor */
1377dd7cddfSDavid du Colombier 	Mf		= 0x00000400,	/* Rx: Multicast Frame */
1387dd7cddfSDavid du Colombier 	Rf		= 0x00000800,	/* Rx: Runt Frame */
1397dd7cddfSDavid du Colombier 	Dt		= 0x00003000,	/* Rx: Data Type (field) */
1407dd7cddfSDavid du Colombier 	De		= 0x00004000,	/* Rx: Descriptor Error */
1417dd7cddfSDavid du Colombier 	Fl		= 0x3FFF0000,	/* Rx: Frame Length (field) */
1427dd7cddfSDavid du Colombier 	Ff		= 0x40000000,	/* Rx: Filtering Fail */
1437dd7cddfSDavid du Colombier 
1447dd7cddfSDavid du Colombier 	Def		= 0x00000001,	/* Tx: DEFerred */
1457dd7cddfSDavid du Colombier 	Uf		= 0x00000002,	/* Tx: UnderFlow error */
1467dd7cddfSDavid du Colombier 	Lf		= 0x00000004,	/* Tx: Link Fail report */
1477dd7cddfSDavid du Colombier 	Cc		= 0x00000078,	/* Tx: Collision Count (field) */
1487dd7cddfSDavid du Colombier 	Hf		= 0x00000080,	/* Tx: Heartbeat Fail */
1497dd7cddfSDavid du Colombier 	Ec		= 0x00000100,	/* Tx: Excessive Collisions */
1507dd7cddfSDavid du Colombier 	Lc		= 0x00000200,	/* Tx: Late Collision */
1517dd7cddfSDavid du Colombier 	Nc		= 0x00000400,	/* Tx: No Carrier */
1527dd7cddfSDavid du Colombier 	Lo		= 0x00000800,	/* Tx: LOss of carrier */
1537dd7cddfSDavid du Colombier 	To		= 0x00004000,	/* Tx: Transmission jabber timeOut */
1547dd7cddfSDavid du Colombier 
1557dd7cddfSDavid du Colombier 	Es		= 0x00008000,	/* [RT]x: Error Summary */
1567dd7cddfSDavid du Colombier 	Own		= 0x80000000,	/* [RT]x: OWN bit */
1577dd7cddfSDavid du Colombier };
1587dd7cddfSDavid du Colombier 
1597dd7cddfSDavid du Colombier enum {					/* control */
1607dd7cddfSDavid du Colombier 	Bs1		= 0x000007FF,	/* [RT]x: Buffer 1 Size */
1617dd7cddfSDavid du Colombier 	Bs2		= 0x003FF800,	/* [RT]x: Buffer 2 Size */
1627dd7cddfSDavid du Colombier 
1637dd7cddfSDavid du Colombier 	Ch		= 0x01000000,	/* [RT]x: second address CHained */
1647dd7cddfSDavid du Colombier 	Er		= 0x02000000,	/* [RT]x: End of Ring */
1657dd7cddfSDavid du Colombier 
1667dd7cddfSDavid du Colombier 	Ft0		= 0x00400000,	/* Tx: Filtering Type 0 */
1677dd7cddfSDavid du Colombier 	Dpd		= 0x00800000,	/* Tx: Disabled PaDding */
1687dd7cddfSDavid du Colombier 	Ac		= 0x04000000,	/* Tx: Add CRC disable */
1697dd7cddfSDavid du Colombier 	Set		= 0x08000000,	/* Tx: SETup packet */
1707dd7cddfSDavid du Colombier 	Ft1		= 0x10000000,	/* Tx: Filtering Type 1 */
1717dd7cddfSDavid du Colombier 	Fseg		= 0x20000000,	/* Tx: First SEGment */
1727dd7cddfSDavid du Colombier 	Lseg		= 0x40000000,	/* Tx: Last SEGment */
1737dd7cddfSDavid du Colombier 	Ic		= 0x80000000,	/* Tx: Interrupt on Completion */
1747dd7cddfSDavid du Colombier };
1757dd7cddfSDavid du Colombier 
1767dd7cddfSDavid du Colombier enum {					/* PHY registers */
1777dd7cddfSDavid du Colombier 	Bmcr		= 0,		/* Basic Mode Control */
1787dd7cddfSDavid du Colombier 	Bmsr		= 1,		/* Basic Mode Status */
1797dd7cddfSDavid du Colombier 	Phyidr1		= 2,		/* PHY Identifier #1 */
1807dd7cddfSDavid du Colombier 	Phyidr2		= 3,		/* PHY Identifier #2 */
1817dd7cddfSDavid du Colombier 	Anar		= 4,		/* Auto-Negotiation Advertisment */
1827dd7cddfSDavid du Colombier 	Anlpar		= 5,		/* Auto-Negotiation Link Partner Ability */
1837dd7cddfSDavid du Colombier 	Aner		= 6,		/* Auto-Negotiation Expansion */
1847dd7cddfSDavid du Colombier };
1857dd7cddfSDavid du Colombier 
18659cc4ca5SDavid du Colombier enum {					/* Variants */
18759cc4ca5SDavid du Colombier 	Tulip0		= (0x0009<<16)|0x1011,
188f43e6a25SDavid du Colombier 	Tulip1		= (0x0014<<16)|0x1011,
18959cc4ca5SDavid du Colombier 	Tulip3		= (0x0019<<16)|0x1011,
19059cc4ca5SDavid du Colombier 	Pnic		= (0x0002<<16)|0x11AD,
19159cc4ca5SDavid du Colombier 	Pnic2		= (0xC115<<16)|0x11AD,
192d9306527SDavid du Colombier 	CentaurP	= (0x0985<<16)|0x1317,
193b4b9fc2fSDavid du Colombier 	CentaurPcb	= (0x1985<<16)|0x1317,
19459cc4ca5SDavid du Colombier };
19559cc4ca5SDavid du Colombier 
1967dd7cddfSDavid du Colombier typedef struct Ctlr Ctlr;
1977dd7cddfSDavid du Colombier typedef struct Ctlr {
1987dd7cddfSDavid du Colombier 	int	port;
1997dd7cddfSDavid du Colombier 	Pcidev*	pcidev;
2007dd7cddfSDavid du Colombier 	Ctlr*	next;
2017dd7cddfSDavid du Colombier 	int	active;
20259cc4ca5SDavid du Colombier 	int	id;			/* (pcidev->did<<16)|pcidev->vid */
2037dd7cddfSDavid du Colombier 
2049a747e4fSDavid du Colombier 	uchar*	srom;
2059a747e4fSDavid du Colombier 	int	sromsz;			/* address size in bits */
2067dd7cddfSDavid du Colombier 	uchar*	sromea;			/* MAC address */
2077dd7cddfSDavid du Colombier 	uchar*	leaf;
2087dd7cddfSDavid du Colombier 	int	sct;			/* selected connection type */
2097dd7cddfSDavid du Colombier 	int	k;			/* info block count */
2107dd7cddfSDavid du Colombier 	uchar*	infoblock[16];
2117dd7cddfSDavid du Colombier 	int	sctk;			/* sct block index */
2127dd7cddfSDavid du Colombier 	int	curk;			/* current block index */
2137dd7cddfSDavid du Colombier 	uchar*	type5block;
2147dd7cddfSDavid du Colombier 
2157dd7cddfSDavid du Colombier 	int	phy[32];		/* logical to physical map */
2167dd7cddfSDavid du Colombier 	int	phyreset;		/* reset bitmap */
2177dd7cddfSDavid du Colombier 	int	curphyad;
2187dd7cddfSDavid du Colombier 	int	fdx;
2197dd7cddfSDavid du Colombier 	int	ttm;
2207dd7cddfSDavid du Colombier 
2217dd7cddfSDavid du Colombier 	uchar	fd;			/* option */
2227dd7cddfSDavid du Colombier 	int	medium;			/* option */
2237dd7cddfSDavid du Colombier 
2247dd7cddfSDavid du Colombier 	int	csr6;			/* CSR6 - operating mode */
2257dd7cddfSDavid du Colombier 	int	mask;			/* CSR[57] - interrupt mask */
2267dd7cddfSDavid du Colombier 	int	mbps;
2277dd7cddfSDavid du Colombier 
2287dd7cddfSDavid du Colombier 	Lock	lock;
2297dd7cddfSDavid du Colombier 
2307dd7cddfSDavid du Colombier 	Des*	rdr;			/* receive descriptor ring */
2317dd7cddfSDavid du Colombier 	int	nrdr;			/* size of rdr */
2327dd7cddfSDavid du Colombier 	int	rdrx;			/* index into rdr */
2337dd7cddfSDavid du Colombier 
2347dd7cddfSDavid du Colombier 	Lock	tlock;
2357dd7cddfSDavid du Colombier 	Des*	tdr;			/* transmit descriptor ring */
2367dd7cddfSDavid du Colombier 	int	ntdr;			/* size of tdr */
2377dd7cddfSDavid du Colombier 	int	tdrh;			/* host index into tdr */
2387dd7cddfSDavid du Colombier 	int	tdri;			/* interface index into tdr */
2397dd7cddfSDavid du Colombier 	int	ntq;			/* descriptors active */
2407dd7cddfSDavid du Colombier 	int	ntqmax;
2417dd7cddfSDavid du Colombier 	Block*	setupbp;
2427dd7cddfSDavid du Colombier 
2437dd7cddfSDavid du Colombier 	ulong	of;			/* receive statistics */
2447dd7cddfSDavid du Colombier 	ulong	ce;
2457dd7cddfSDavid du Colombier 	ulong	cs;
2467dd7cddfSDavid du Colombier 	ulong	tl;
2477dd7cddfSDavid du Colombier 	ulong	rf;
2487dd7cddfSDavid du Colombier 	ulong	de;
2497dd7cddfSDavid du Colombier 
2507dd7cddfSDavid du Colombier 	ulong	ru;
2517dd7cddfSDavid du Colombier 	ulong	rps;
2527dd7cddfSDavid du Colombier 	ulong	rwt;
2537dd7cddfSDavid du Colombier 
2547dd7cddfSDavid du Colombier 	ulong	uf;			/* transmit statistics */
2557dd7cddfSDavid du Colombier 	ulong	ec;
2567dd7cddfSDavid du Colombier 	ulong	lc;
2577dd7cddfSDavid du Colombier 	ulong	nc;
2587dd7cddfSDavid du Colombier 	ulong	lo;
2597dd7cddfSDavid du Colombier 	ulong	to;
2607dd7cddfSDavid du Colombier 
2617dd7cddfSDavid du Colombier 	ulong	tps;
2627dd7cddfSDavid du Colombier 	ulong	tu;
2637dd7cddfSDavid du Colombier 	ulong	tjt;
2647dd7cddfSDavid du Colombier 	ulong	unf;
2657dd7cddfSDavid du Colombier } Ctlr;
2667dd7cddfSDavid du Colombier 
2677dd7cddfSDavid du Colombier static Ctlr* ctlrhead;
2687dd7cddfSDavid du Colombier static Ctlr* ctlrtail;
2697dd7cddfSDavid du Colombier 
2707dd7cddfSDavid du Colombier #define csr32r(c, r)	(inl((c)->port+((r)*8)))
2717dd7cddfSDavid du Colombier #define csr32w(c, r, l)	(outl((c)->port+((r)*8), (ulong)(l)))
2727dd7cddfSDavid du Colombier 
2737dd7cddfSDavid du Colombier static void
promiscuous(void * arg,int on)2747dd7cddfSDavid du Colombier promiscuous(void* arg, int on)
2757dd7cddfSDavid du Colombier {
2767dd7cddfSDavid du Colombier 	Ctlr *ctlr;
2777dd7cddfSDavid du Colombier 
2787dd7cddfSDavid du Colombier 	ctlr = ((Ether*)arg)->ctlr;
2797dd7cddfSDavid du Colombier 	ilock(&ctlr->lock);
2807dd7cddfSDavid du Colombier 	if(on)
2817dd7cddfSDavid du Colombier 		ctlr->csr6 |= Pr;
2827dd7cddfSDavid du Colombier 	else
2837dd7cddfSDavid du Colombier 		ctlr->csr6 &= ~Pr;
2847dd7cddfSDavid du Colombier 	csr32w(ctlr, 6, ctlr->csr6);
2857dd7cddfSDavid du Colombier 	iunlock(&ctlr->lock);
2867dd7cddfSDavid du Colombier }
2877dd7cddfSDavid du Colombier 
2880809e9a7SDavid du Colombier /* multicast already on, don't need to do anything */
2890809e9a7SDavid du Colombier static void
multicast(void *,uchar *,int)2900809e9a7SDavid du Colombier multicast(void*, uchar*, int)
2910809e9a7SDavid du Colombier {
2920809e9a7SDavid du Colombier }
2930809e9a7SDavid du Colombier 
2947dd7cddfSDavid du Colombier static void
attach(Ether * ether)2957dd7cddfSDavid du Colombier attach(Ether* ether)
2967dd7cddfSDavid du Colombier {
2977dd7cddfSDavid du Colombier 	Ctlr *ctlr;
2987dd7cddfSDavid du Colombier 
2997dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
3007dd7cddfSDavid du Colombier 	ilock(&ctlr->lock);
3017dd7cddfSDavid du Colombier 	if(!(ctlr->csr6 & Sr)){
3027dd7cddfSDavid du Colombier 		ctlr->csr6 |= Sr;
3037dd7cddfSDavid du Colombier 		csr32w(ctlr, 6, ctlr->csr6);
3047dd7cddfSDavid du Colombier 	}
3057dd7cddfSDavid du Colombier 	iunlock(&ctlr->lock);
3067dd7cddfSDavid du Colombier }
3077dd7cddfSDavid du Colombier 
3087dd7cddfSDavid du Colombier static long
ifstat(Ether * ether,void * a,long n,ulong offset)3097dd7cddfSDavid du Colombier ifstat(Ether* ether, void* a, long n, ulong offset)
3107dd7cddfSDavid du Colombier {
3117dd7cddfSDavid du Colombier 	Ctlr *ctlr;
3127dd7cddfSDavid du Colombier 	char *buf, *p;
3137dd7cddfSDavid du Colombier 	int i, l, len;
3147dd7cddfSDavid du Colombier 
3157dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
3167dd7cddfSDavid du Colombier 
3177dd7cddfSDavid du Colombier 	ether->crcs = ctlr->ce;
3187dd7cddfSDavid du Colombier 	ether->frames = ctlr->rf+ctlr->cs;
3197dd7cddfSDavid du Colombier 	ether->buffs = ctlr->de+ctlr->tl;
3207dd7cddfSDavid du Colombier 	ether->overflows = ctlr->of;
3217dd7cddfSDavid du Colombier 
3227dd7cddfSDavid du Colombier 	if(n == 0)
3237dd7cddfSDavid du Colombier 		return 0;
3247dd7cddfSDavid du Colombier 
3257dd7cddfSDavid du Colombier 	p = malloc(READSTR);
326aa72973aSDavid du Colombier 	if(p == nil)
327aa72973aSDavid du Colombier 		error(Enomem);
3287dd7cddfSDavid du Colombier 	l = snprint(p, READSTR, "Overflow: %lud\n", ctlr->of);
3297dd7cddfSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Ru: %lud\n", ctlr->ru);
3307dd7cddfSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Rps: %lud\n", ctlr->rps);
3317dd7cddfSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Rwt: %lud\n", ctlr->rwt);
3327dd7cddfSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Tps: %lud\n", ctlr->tps);
3337dd7cddfSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Tu: %lud\n", ctlr->tu);
3347dd7cddfSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Tjt: %lud\n", ctlr->tjt);
3357dd7cddfSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Unf: %lud\n", ctlr->unf);
3367dd7cddfSDavid du Colombier 	l += snprint(p+l, READSTR-l, "CRC Error: %lud\n", ctlr->ce);
3377dd7cddfSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Collision Seen: %lud\n", ctlr->cs);
3387dd7cddfSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Frame Too Long: %lud\n", ctlr->tl);
3397dd7cddfSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Runt Frame: %lud\n", ctlr->rf);
3407dd7cddfSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Descriptor Error: %lud\n", ctlr->de);
3417dd7cddfSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Underflow Error: %lud\n", ctlr->uf);
3427dd7cddfSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Excessive Collisions: %lud\n", ctlr->ec);
3437dd7cddfSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Late Collision: %lud\n", ctlr->lc);
3447dd7cddfSDavid du Colombier 	l += snprint(p+l, READSTR-l, "No Carrier: %lud\n", ctlr->nc);
3457dd7cddfSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Loss of Carrier: %lud\n", ctlr->lo);
3467dd7cddfSDavid du Colombier 	l += snprint(p+l, READSTR-l, "Transmit Jabber Timeout: %lud\n",
3477dd7cddfSDavid du Colombier 		ctlr->to);
3487dd7cddfSDavid du Colombier 	l += snprint(p+l, READSTR-l, "csr6: %luX %uX\n", csr32r(ctlr, 6),
3497dd7cddfSDavid du Colombier 		ctlr->csr6);
3507dd7cddfSDavid du Colombier 	snprint(p+l, READSTR-l, "ntqmax: %d\n", ctlr->ntqmax);
3517dd7cddfSDavid du Colombier 	ctlr->ntqmax = 0;
3527dd7cddfSDavid du Colombier 	buf = a;
3537dd7cddfSDavid du Colombier 	len = readstr(offset, buf, n, p);
3547dd7cddfSDavid du Colombier 	if(offset > l)
3557dd7cddfSDavid du Colombier 		offset -= l;
3567dd7cddfSDavid du Colombier 	else
3577dd7cddfSDavid du Colombier 		offset = 0;
3587dd7cddfSDavid du Colombier 	buf += len;
3597dd7cddfSDavid du Colombier 	n -= len;
3607dd7cddfSDavid du Colombier 
3617dd7cddfSDavid du Colombier 	l = snprint(p, READSTR, "srom:");
3629a747e4fSDavid du Colombier 	for(i = 0; i < (1<<(ctlr->sromsz)*sizeof(ushort)); i++){
3637dd7cddfSDavid du Colombier 		if(i && ((i & 0x0F) == 0))
3647dd7cddfSDavid du Colombier 			l += snprint(p+l, READSTR-l, "\n     ");
3657dd7cddfSDavid du Colombier 		l += snprint(p+l, READSTR-l, " %2.2uX", ctlr->srom[i]);
3667dd7cddfSDavid du Colombier 	}
3677dd7cddfSDavid du Colombier 
3687dd7cddfSDavid du Colombier 	snprint(p+l, READSTR-l, "\n");
3697dd7cddfSDavid du Colombier 	len += readstr(offset, buf, n, p);
3707dd7cddfSDavid du Colombier 	free(p);
3717dd7cddfSDavid du Colombier 
3727dd7cddfSDavid du Colombier 	return len;
3737dd7cddfSDavid du Colombier }
3747dd7cddfSDavid du Colombier 
3757dd7cddfSDavid du Colombier static void
txstart(Ether * ether)3767dd7cddfSDavid du Colombier txstart(Ether* ether)
3777dd7cddfSDavid du Colombier {
3787dd7cddfSDavid du Colombier 	Ctlr *ctlr;
3797dd7cddfSDavid du Colombier 	Block *bp;
3807dd7cddfSDavid du Colombier 	Des *des;
3817dd7cddfSDavid du Colombier 	int control;
3827dd7cddfSDavid du Colombier 
3837dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
3847dd7cddfSDavid du Colombier 	while(ctlr->ntq < (ctlr->ntdr-1)){
3857dd7cddfSDavid du Colombier 		if(ctlr->setupbp){
3867dd7cddfSDavid du Colombier 			bp = ctlr->setupbp;
3877dd7cddfSDavid du Colombier 			ctlr->setupbp = 0;
3887dd7cddfSDavid du Colombier 			control = Ic|Set|BLEN(bp);
3897dd7cddfSDavid du Colombier 		}
3907dd7cddfSDavid du Colombier 		else{
3917dd7cddfSDavid du Colombier 			bp = qget(ether->oq);
3927dd7cddfSDavid du Colombier 			if(bp == nil)
3937dd7cddfSDavid du Colombier 				break;
3947dd7cddfSDavid du Colombier 			control = Ic|Lseg|Fseg|BLEN(bp);
3957dd7cddfSDavid du Colombier 		}
3967dd7cddfSDavid du Colombier 
3977dd7cddfSDavid du Colombier 		ctlr->tdr[PREV(ctlr->tdrh, ctlr->ntdr)].control &= ~Ic;
3987dd7cddfSDavid du Colombier 		des = &ctlr->tdr[ctlr->tdrh];
3997dd7cddfSDavid du Colombier 		des->bp = bp;
4009a747e4fSDavid du Colombier 		des->addr = PCIWADDR(bp->rp);
4017dd7cddfSDavid du Colombier 		des->control |= control;
4027dd7cddfSDavid du Colombier 		ctlr->ntq++;
4037dd7cddfSDavid du Colombier 		coherence();
4047dd7cddfSDavid du Colombier 		des->status = Own;
4057dd7cddfSDavid du Colombier 		csr32w(ctlr, 1, 0);
4067dd7cddfSDavid du Colombier 		ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr);
4077dd7cddfSDavid du Colombier 	}
4087dd7cddfSDavid du Colombier 
4097dd7cddfSDavid du Colombier 	if(ctlr->ntq > ctlr->ntqmax)
4107dd7cddfSDavid du Colombier 		ctlr->ntqmax = ctlr->ntq;
4117dd7cddfSDavid du Colombier }
4127dd7cddfSDavid du Colombier 
4137dd7cddfSDavid du Colombier static void
transmit(Ether * ether)4147dd7cddfSDavid du Colombier transmit(Ether* ether)
4157dd7cddfSDavid du Colombier {
4167dd7cddfSDavid du Colombier 	Ctlr *ctlr;
4177dd7cddfSDavid du Colombier 
4187dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
4197dd7cddfSDavid du Colombier 	ilock(&ctlr->tlock);
4207dd7cddfSDavid du Colombier 	txstart(ether);
4217dd7cddfSDavid du Colombier 	iunlock(&ctlr->tlock);
4227dd7cddfSDavid du Colombier }
4237dd7cddfSDavid du Colombier 
4247dd7cddfSDavid du Colombier static void
interrupt(Ureg *,void * arg)4257dd7cddfSDavid du Colombier interrupt(Ureg*, void* arg)
4267dd7cddfSDavid du Colombier {
4277dd7cddfSDavid du Colombier 	Ctlr *ctlr;
4287dd7cddfSDavid du Colombier 	Ether *ether;
4297dd7cddfSDavid du Colombier 	int len, status;
4307dd7cddfSDavid du Colombier 	Des *des;
4317dd7cddfSDavid du Colombier 	Block *bp;
4327dd7cddfSDavid du Colombier 
4337dd7cddfSDavid du Colombier 	ether = arg;
4347dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
4357dd7cddfSDavid du Colombier 
4367dd7cddfSDavid du Colombier 	while((status = csr32r(ctlr, 5)) & (Nis|Ais)){
4377dd7cddfSDavid du Colombier 		/*
4387dd7cddfSDavid du Colombier 		 * Acknowledge the interrupts and mask-out
4397dd7cddfSDavid du Colombier 		 * the ones that are implicitly handled.
4407dd7cddfSDavid du Colombier 		 */
4417dd7cddfSDavid du Colombier 		csr32w(ctlr, 5, status);
4427dd7cddfSDavid du Colombier 		status &= (ctlr->mask & ~(Nis|Ti));
4437dd7cddfSDavid du Colombier 
4447dd7cddfSDavid du Colombier 		if(status & Ais){
4457dd7cddfSDavid du Colombier 			if(status & Tps)
4467dd7cddfSDavid du Colombier 				ctlr->tps++;
4477dd7cddfSDavid du Colombier 			if(status & Tu)
4487dd7cddfSDavid du Colombier 				ctlr->tu++;
4497dd7cddfSDavid du Colombier 			if(status & Tjt)
4507dd7cddfSDavid du Colombier 				ctlr->tjt++;
4517dd7cddfSDavid du Colombier 			if(status & Ru)
4527dd7cddfSDavid du Colombier 				ctlr->ru++;
4537dd7cddfSDavid du Colombier 			if(status & Rps)
4547dd7cddfSDavid du Colombier 				ctlr->rps++;
4557dd7cddfSDavid du Colombier 			if(status & Rwt)
4567dd7cddfSDavid du Colombier 				ctlr->rwt++;
4577dd7cddfSDavid du Colombier 			status &= ~(Ais|Rwt|Rps|Ru|Tjt|Tu|Tps);
4587dd7cddfSDavid du Colombier 		}
4597dd7cddfSDavid du Colombier 
4607dd7cddfSDavid du Colombier 		/*
4617dd7cddfSDavid du Colombier 		 * Received packets.
4627dd7cddfSDavid du Colombier 		 */
4637dd7cddfSDavid du Colombier 		if(status & Ri){
4647dd7cddfSDavid du Colombier 			des = &ctlr->rdr[ctlr->rdrx];
4657dd7cddfSDavid du Colombier 			while(!(des->status & Own)){
4667dd7cddfSDavid du Colombier 				if(des->status & Es){
4677dd7cddfSDavid du Colombier 					if(des->status & Of)
4687dd7cddfSDavid du Colombier 						ctlr->of++;
4697dd7cddfSDavid du Colombier 					if(des->status & Ce)
4707dd7cddfSDavid du Colombier 						ctlr->ce++;
4717dd7cddfSDavid du Colombier 					if(des->status & Cs)
4727dd7cddfSDavid du Colombier 						ctlr->cs++;
4737dd7cddfSDavid du Colombier 					if(des->status & Tl)
4747dd7cddfSDavid du Colombier 						ctlr->tl++;
4757dd7cddfSDavid du Colombier 					if(des->status & Rf)
4767dd7cddfSDavid du Colombier 						ctlr->rf++;
4777dd7cddfSDavid du Colombier 					if(des->status & De)
4787dd7cddfSDavid du Colombier 						ctlr->de++;
4797dd7cddfSDavid du Colombier 				}
48059cc4ca5SDavid du Colombier 				else if(bp = iallocb(Rbsz)){
4817dd7cddfSDavid du Colombier 					len = ((des->status & Fl)>>16)-4;
4827dd7cddfSDavid du Colombier 					des->bp->wp = des->bp->rp+len;
4837dd7cddfSDavid du Colombier 					etheriq(ether, des->bp, 1);
4847dd7cddfSDavid du Colombier 					des->bp = bp;
4859a747e4fSDavid du Colombier 					des->addr = PCIWADDR(bp->rp);
4867dd7cddfSDavid du Colombier 				}
4877dd7cddfSDavid du Colombier 
4887dd7cddfSDavid du Colombier 				des->control &= Er;
48959cc4ca5SDavid du Colombier 				des->control |= Rbsz;
4907dd7cddfSDavid du Colombier 				coherence();
4917dd7cddfSDavid du Colombier 				des->status = Own;
4927dd7cddfSDavid du Colombier 
4937dd7cddfSDavid du Colombier 				ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr);
4947dd7cddfSDavid du Colombier 				des = &ctlr->rdr[ctlr->rdrx];
4957dd7cddfSDavid du Colombier 			}
4967dd7cddfSDavid du Colombier 			status &= ~Ri;
4977dd7cddfSDavid du Colombier 		}
4987dd7cddfSDavid du Colombier 
4997dd7cddfSDavid du Colombier 		/*
5007dd7cddfSDavid du Colombier 		 * Check the transmit side:
5017dd7cddfSDavid du Colombier 		 *	check for Transmit Underflow and Adjust
5027dd7cddfSDavid du Colombier 		 *	the threshold upwards;
5037dd7cddfSDavid du Colombier 		 *	free any transmitted buffers and try to
5047dd7cddfSDavid du Colombier 		 *	top-up the ring.
5057dd7cddfSDavid du Colombier 		 */
5067dd7cddfSDavid du Colombier 		if(status & Unf){
5077dd7cddfSDavid du Colombier 			ctlr->unf++;
5087dd7cddfSDavid du Colombier 			ilock(&ctlr->lock);
5097dd7cddfSDavid du Colombier 			csr32w(ctlr, 6, ctlr->csr6 & ~St);
5107dd7cddfSDavid du Colombier 			switch(ctlr->csr6 & Tr){
5117dd7cddfSDavid du Colombier 			case Tr128:
5127dd7cddfSDavid du Colombier 				len = Tr256;
5137dd7cddfSDavid du Colombier 				break;
5147dd7cddfSDavid du Colombier 			case Tr256:
5157dd7cddfSDavid du Colombier 				len = Tr512;
5167dd7cddfSDavid du Colombier 				break;
5177dd7cddfSDavid du Colombier 			case Tr512:
5187dd7cddfSDavid du Colombier 				len = Tr1024;
5197dd7cddfSDavid du Colombier 				break;
5207dd7cddfSDavid du Colombier 			default:
5217dd7cddfSDavid du Colombier 			case Tr1024:
5227dd7cddfSDavid du Colombier 				len = Sf;
5237dd7cddfSDavid du Colombier 				break;
5247dd7cddfSDavid du Colombier 			}
5257dd7cddfSDavid du Colombier 			ctlr->csr6 = (ctlr->csr6 & ~Tr)|len;
5267dd7cddfSDavid du Colombier 			csr32w(ctlr, 6, ctlr->csr6);
5277dd7cddfSDavid du Colombier 			iunlock(&ctlr->lock);
5287dd7cddfSDavid du Colombier 			csr32w(ctlr, 5, Tps);
5297dd7cddfSDavid du Colombier 			status &= ~(Unf|Tps);
5307dd7cddfSDavid du Colombier 		}
5317dd7cddfSDavid du Colombier 
5327dd7cddfSDavid du Colombier 		ilock(&ctlr->tlock);
5337dd7cddfSDavid du Colombier 		while(ctlr->ntq){
5347dd7cddfSDavid du Colombier 			des = &ctlr->tdr[ctlr->tdri];
5357dd7cddfSDavid du Colombier 			if(des->status & Own)
5367dd7cddfSDavid du Colombier 				break;
5377dd7cddfSDavid du Colombier 
5387dd7cddfSDavid du Colombier 			if(des->status & Es){
5397dd7cddfSDavid du Colombier 				if(des->status & Uf)
5407dd7cddfSDavid du Colombier 					ctlr->uf++;
5417dd7cddfSDavid du Colombier 				if(des->status & Ec)
5427dd7cddfSDavid du Colombier 					ctlr->ec++;
5437dd7cddfSDavid du Colombier 				if(des->status & Lc)
5447dd7cddfSDavid du Colombier 					ctlr->lc++;
5457dd7cddfSDavid du Colombier 				if(des->status & Nc)
5467dd7cddfSDavid du Colombier 					ctlr->nc++;
5477dd7cddfSDavid du Colombier 				if(des->status & Lo)
5487dd7cddfSDavid du Colombier 					ctlr->lo++;
5497dd7cddfSDavid du Colombier 				if(des->status & To)
5507dd7cddfSDavid du Colombier 					ctlr->to++;
5517dd7cddfSDavid du Colombier 				ether->oerrs++;
5527dd7cddfSDavid du Colombier 			}
5537dd7cddfSDavid du Colombier 
5547dd7cddfSDavid du Colombier 			freeb(des->bp);
5557dd7cddfSDavid du Colombier 			des->control &= Er;
5567dd7cddfSDavid du Colombier 
5577dd7cddfSDavid du Colombier 			ctlr->ntq--;
5587dd7cddfSDavid du Colombier 			ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr);
5597dd7cddfSDavid du Colombier 		}
5607dd7cddfSDavid du Colombier 		txstart(ether);
5617dd7cddfSDavid du Colombier 		iunlock(&ctlr->tlock);
5627dd7cddfSDavid du Colombier 
5637dd7cddfSDavid du Colombier 		/*
5647dd7cddfSDavid du Colombier 		 * Anything left not catered for?
5657dd7cddfSDavid du Colombier 		 */
5667dd7cddfSDavid du Colombier 		if(status)
5677dd7cddfSDavid du Colombier 			panic("#l%d: status %8.8uX\n", ether->ctlrno, status);
5687dd7cddfSDavid du Colombier 	}
5697dd7cddfSDavid du Colombier }
5707dd7cddfSDavid du Colombier 
5717dd7cddfSDavid du Colombier static void
ctlrinit(Ether * ether)5727dd7cddfSDavid du Colombier ctlrinit(Ether* ether)
5737dd7cddfSDavid du Colombier {
5747dd7cddfSDavid du Colombier 	Ctlr *ctlr;
5757dd7cddfSDavid du Colombier 	Des *des;
5767dd7cddfSDavid du Colombier 	Block *bp;
5777dd7cddfSDavid du Colombier 	int i;
5787dd7cddfSDavid du Colombier 	uchar bi[Eaddrlen*2];
5797dd7cddfSDavid du Colombier 
5807dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
5817dd7cddfSDavid du Colombier 
5827dd7cddfSDavid du Colombier 	/*
5837dd7cddfSDavid du Colombier 	 * Allocate and initialise the receive ring;
5847dd7cddfSDavid du Colombier 	 * allocate and initialise the transmit ring;
5857dd7cddfSDavid du Colombier 	 * unmask interrupts and start the transmit side;
5867dd7cddfSDavid du Colombier 	 * create and post a setup packet to initialise
5877dd7cddfSDavid du Colombier 	 * the physical ethernet address.
5887dd7cddfSDavid du Colombier 	 */
5899a747e4fSDavid du Colombier 	ctlr->rdr = xspanalloc(ctlr->nrdr*sizeof(Des), 8*sizeof(ulong), 0);
5907dd7cddfSDavid du Colombier 	for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){
5919a747e4fSDavid du Colombier 		des->bp = iallocb(Rbsz);
5929a747e4fSDavid du Colombier 		if(des->bp == nil)
5939a747e4fSDavid du Colombier 			panic("can't allocate ethernet receive ring\n");
5947dd7cddfSDavid du Colombier 		des->status = Own;
59559cc4ca5SDavid du Colombier 		des->control = Rbsz;
5969a747e4fSDavid du Colombier 		des->addr = PCIWADDR(des->bp->rp);
5977dd7cddfSDavid du Colombier 	}
5987dd7cddfSDavid du Colombier 	ctlr->rdr[ctlr->nrdr-1].control |= Er;
5997dd7cddfSDavid du Colombier 	ctlr->rdrx = 0;
6009a747e4fSDavid du Colombier 	csr32w(ctlr, 3, PCIWADDR(ctlr->rdr));
6017dd7cddfSDavid du Colombier 
6027dd7cddfSDavid du Colombier 	ctlr->tdr = xspanalloc(ctlr->ntdr*sizeof(Des), 8*sizeof(ulong), 0);
6037dd7cddfSDavid du Colombier 	ctlr->tdr[ctlr->ntdr-1].control |= Er;
6047dd7cddfSDavid du Colombier 	ctlr->tdrh = 0;
6057dd7cddfSDavid du Colombier 	ctlr->tdri = 0;
6069a747e4fSDavid du Colombier 	csr32w(ctlr, 4, PCIWADDR(ctlr->tdr));
6077dd7cddfSDavid du Colombier 
60859cc4ca5SDavid du Colombier 	/*
60959cc4ca5SDavid du Colombier 	 * Clear any bits in the Status Register (CSR5) as
61059cc4ca5SDavid du Colombier 	 * the PNIC has a different reset value from a true 2114x.
61159cc4ca5SDavid du Colombier 	 */
6127dd7cddfSDavid du Colombier 	ctlr->mask = Nis|Ais|Fbe|Rwt|Rps|Ru|Ri|Unf|Tjt|Tps|Ti;
61359cc4ca5SDavid du Colombier 	csr32w(ctlr, 5, ctlr->mask);
6147dd7cddfSDavid du Colombier 	csr32w(ctlr, 7, ctlr->mask);
6150809e9a7SDavid du Colombier 	ctlr->csr6 |= St|Pm;
6167dd7cddfSDavid du Colombier 	csr32w(ctlr, 6, ctlr->csr6);
6177dd7cddfSDavid du Colombier 
6187dd7cddfSDavid du Colombier 	for(i = 0; i < Eaddrlen/2; i++){
6197dd7cddfSDavid du Colombier 		bi[i*4] = ether->ea[i*2];
6207dd7cddfSDavid du Colombier 		bi[i*4+1] = ether->ea[i*2+1];
6217dd7cddfSDavid du Colombier 		bi[i*4+2] = ether->ea[i*2+1];
6227dd7cddfSDavid du Colombier 		bi[i*4+3] = ether->ea[i*2];
6237dd7cddfSDavid du Colombier 	}
6249a747e4fSDavid du Colombier 	bp = iallocb(Eaddrlen*2*16);
6259a747e4fSDavid du Colombier 	if(bp == nil)
6269a747e4fSDavid du Colombier 		panic("can't allocate ethernet setup buffer\n");
6277dd7cddfSDavid du Colombier 	memset(bp->rp, 0xFF, sizeof(bi));
6287dd7cddfSDavid du Colombier 	for(i = sizeof(bi); i < sizeof(bi)*16; i += sizeof(bi))
6297dd7cddfSDavid du Colombier 		memmove(bp->rp+i, bi, sizeof(bi));
6307dd7cddfSDavid du Colombier 	bp->wp += sizeof(bi)*16;
6317dd7cddfSDavid du Colombier 
6327dd7cddfSDavid du Colombier 	ctlr->setupbp = bp;
633*6520663fSDavid du Colombier 	ether->oq = qopen(64*1024, Qmsg, 0, 0);
6347dd7cddfSDavid du Colombier 	transmit(ether);
6357dd7cddfSDavid du Colombier }
6367dd7cddfSDavid du Colombier 
6377dd7cddfSDavid du Colombier static void
csr9w(Ctlr * ctlr,int data)6387dd7cddfSDavid du Colombier csr9w(Ctlr* ctlr, int data)
6397dd7cddfSDavid du Colombier {
6407dd7cddfSDavid du Colombier 	csr32w(ctlr, 9, data);
6417dd7cddfSDavid du Colombier 	microdelay(1);
6427dd7cddfSDavid du Colombier }
6437dd7cddfSDavid du Colombier 
6447dd7cddfSDavid du Colombier static int
miimdi(Ctlr * ctlr,int n)6457dd7cddfSDavid du Colombier miimdi(Ctlr* ctlr, int n)
6467dd7cddfSDavid du Colombier {
6477dd7cddfSDavid du Colombier 	int data, i;
6487dd7cddfSDavid du Colombier 
6497dd7cddfSDavid du Colombier 	/*
6507dd7cddfSDavid du Colombier 	 * Read n bits from the MII Management Register.
6517dd7cddfSDavid du Colombier 	 */
6527dd7cddfSDavid du Colombier 	data = 0;
6537dd7cddfSDavid du Colombier 	for(i = n-1; i >= 0; i--){
6547dd7cddfSDavid du Colombier 		if(csr32r(ctlr, 9) & Mdi)
6557dd7cddfSDavid du Colombier 			data |= (1<<i);
6567dd7cddfSDavid du Colombier 		csr9w(ctlr, Mii|Mdc);
6577dd7cddfSDavid du Colombier 		csr9w(ctlr, Mii);
6587dd7cddfSDavid du Colombier 	}
6597dd7cddfSDavid du Colombier 	csr9w(ctlr, 0);
6607dd7cddfSDavid du Colombier 
6617dd7cddfSDavid du Colombier 	return data;
6627dd7cddfSDavid du Colombier }
6637dd7cddfSDavid du Colombier 
6647dd7cddfSDavid du Colombier static void
miimdo(Ctlr * ctlr,int bits,int n)6657dd7cddfSDavid du Colombier miimdo(Ctlr* ctlr, int bits, int n)
6667dd7cddfSDavid du Colombier {
6677dd7cddfSDavid du Colombier 	int i, mdo;
6687dd7cddfSDavid du Colombier 
6697dd7cddfSDavid du Colombier 	/*
6707dd7cddfSDavid du Colombier 	 * Write n bits to the MII Management Register.
6717dd7cddfSDavid du Colombier 	 */
6727dd7cddfSDavid du Colombier 	for(i = n-1; i >= 0; i--){
6737dd7cddfSDavid du Colombier 		if(bits & (1<<i))
6747dd7cddfSDavid du Colombier 			mdo = Mdo;
6757dd7cddfSDavid du Colombier 		else
6767dd7cddfSDavid du Colombier 			mdo = 0;
6777dd7cddfSDavid du Colombier 		csr9w(ctlr, mdo);
6787dd7cddfSDavid du Colombier 		csr9w(ctlr, mdo|Mdc);
6797dd7cddfSDavid du Colombier 		csr9w(ctlr, mdo);
6807dd7cddfSDavid du Colombier 	}
6817dd7cddfSDavid du Colombier }
6827dd7cddfSDavid du Colombier 
6837dd7cddfSDavid du Colombier static int
miir(Ctlr * ctlr,int phyad,int regad)6847dd7cddfSDavid du Colombier miir(Ctlr* ctlr, int phyad, int regad)
6857dd7cddfSDavid du Colombier {
68659cc4ca5SDavid du Colombier 	int data, i;
68759cc4ca5SDavid du Colombier 
68859cc4ca5SDavid du Colombier 	if(ctlr->id == Pnic){
68959cc4ca5SDavid du Colombier 		i = 1000;
69059cc4ca5SDavid du Colombier 		csr32w(ctlr, 20, 0x60020000|(phyad<<23)|(regad<<18));
69159cc4ca5SDavid du Colombier 		do{
69259cc4ca5SDavid du Colombier 			microdelay(1);
69359cc4ca5SDavid du Colombier 			data = csr32r(ctlr, 20);
69459cc4ca5SDavid du Colombier 		}while((data & 0x80000000) && --i);
69559cc4ca5SDavid du Colombier 
69659cc4ca5SDavid du Colombier 		if(i == 0)
69759cc4ca5SDavid du Colombier 			return -1;
69859cc4ca5SDavid du Colombier 		return data & 0xFFFF;
69959cc4ca5SDavid du Colombier 	}
7007dd7cddfSDavid du Colombier 
7017dd7cddfSDavid du Colombier 	/*
7027dd7cddfSDavid du Colombier 	 * Preamble;
7037dd7cddfSDavid du Colombier 	 * ST+OP+PHYAD+REGAD;
7047dd7cddfSDavid du Colombier 	 * TA + 16 data bits.
7057dd7cddfSDavid du Colombier 	 */
7067dd7cddfSDavid du Colombier 	miimdo(ctlr, 0xFFFFFFFF, 32);
7077dd7cddfSDavid du Colombier 	miimdo(ctlr, 0x1800|(phyad<<5)|regad, 14);
7087dd7cddfSDavid du Colombier 	data = miimdi(ctlr, 18);
7097dd7cddfSDavid du Colombier 
7107dd7cddfSDavid du Colombier 	if(data & 0x10000)
7117dd7cddfSDavid du Colombier 		return -1;
7127dd7cddfSDavid du Colombier 
7137dd7cddfSDavid du Colombier 	return data & 0xFFFF;
7147dd7cddfSDavid du Colombier }
7157dd7cddfSDavid du Colombier 
7167dd7cddfSDavid du Colombier static void
miiw(Ctlr * ctlr,int phyad,int regad,int data)7177dd7cddfSDavid du Colombier miiw(Ctlr* ctlr, int phyad, int regad, int data)
7187dd7cddfSDavid du Colombier {
7197dd7cddfSDavid du Colombier 	/*
7207dd7cddfSDavid du Colombier 	 * Preamble;
7217dd7cddfSDavid du Colombier 	 * ST+OP+PHYAD+REGAD+TA + 16 data bits;
7227dd7cddfSDavid du Colombier 	 * Z.
7237dd7cddfSDavid du Colombier 	 */
7247dd7cddfSDavid du Colombier 	miimdo(ctlr, 0xFFFFFFFF, 32);
7257dd7cddfSDavid du Colombier 	data &= 0xFFFF;
7267dd7cddfSDavid du Colombier 	data |= (0x05<<(5+5+2+16))|(phyad<<(5+2+16))|(regad<<(2+16))|(0x02<<16);
7277dd7cddfSDavid du Colombier 	miimdo(ctlr, data, 32);
7287dd7cddfSDavid du Colombier 	csr9w(ctlr, Mdc);
7297dd7cddfSDavid du Colombier 	csr9w(ctlr, 0);
7307dd7cddfSDavid du Colombier }
7317dd7cddfSDavid du Colombier 
7327dd7cddfSDavid du Colombier static int
sromr(Ctlr * ctlr,int r)7337dd7cddfSDavid du Colombier sromr(Ctlr* ctlr, int r)
7347dd7cddfSDavid du Colombier {
7359a747e4fSDavid du Colombier 	int i, op, data, size;
7367dd7cddfSDavid du Colombier 
73759cc4ca5SDavid du Colombier 	if(ctlr->id == Pnic){
73859cc4ca5SDavid du Colombier 		i = 1000;
73959cc4ca5SDavid du Colombier 		csr32w(ctlr, 19, 0x600|r);
74059cc4ca5SDavid du Colombier 		do{
74159cc4ca5SDavid du Colombier 			microdelay(1);
74259cc4ca5SDavid du Colombier 			data = csr32r(ctlr, 19);
74359cc4ca5SDavid du Colombier 		}while((data & 0x80000000) && --i);
74459cc4ca5SDavid du Colombier 
7459a747e4fSDavid du Colombier 		if(ctlr->sromsz == 0)
7469a747e4fSDavid du Colombier 			ctlr->sromsz = 6;
7479a747e4fSDavid du Colombier 
74859cc4ca5SDavid du Colombier 		return csr32r(ctlr, 9) & 0xFFFF;
74959cc4ca5SDavid du Colombier 	}
75059cc4ca5SDavid du Colombier 
7517dd7cddfSDavid du Colombier 	/*
7527dd7cddfSDavid du Colombier 	 * This sequence for reading a 16-bit register 'r'
753f43e6a25SDavid du Colombier 	 * in the EEPROM is taken (pretty much) straight from Section
7547dd7cddfSDavid du Colombier 	 * 7.4 of the 21140 Hardware Reference Manual.
7557dd7cddfSDavid du Colombier 	 */
7569a747e4fSDavid du Colombier reread:
7577dd7cddfSDavid du Colombier 	csr9w(ctlr, Rd|Ss);
7587dd7cddfSDavid du Colombier 	csr9w(ctlr, Rd|Ss|Scs);
7597dd7cddfSDavid du Colombier 	csr9w(ctlr, Rd|Ss|Sclk|Scs);
7607dd7cddfSDavid du Colombier 	csr9w(ctlr, Rd|Ss);
7617dd7cddfSDavid du Colombier 
7627dd7cddfSDavid du Colombier 	op = 0x06;
7637dd7cddfSDavid du Colombier 	for(i = 3-1; i >= 0; i--){
7647dd7cddfSDavid du Colombier 		data = Rd|Ss|(((op>>i) & 0x01)<<2)|Scs;
7657dd7cddfSDavid du Colombier 		csr9w(ctlr, data);
7667dd7cddfSDavid du Colombier 		csr9w(ctlr, data|Sclk);
7677dd7cddfSDavid du Colombier 		csr9w(ctlr, data);
7687dd7cddfSDavid du Colombier 	}
7697dd7cddfSDavid du Colombier 
7709a747e4fSDavid du Colombier 	/*
7719a747e4fSDavid du Colombier 	 * First time through must work out the EEPROM size.
772f43e6a25SDavid du Colombier 	 * This doesn't seem to work on the 21041 as implemented
773f43e6a25SDavid du Colombier 	 * in Virtual PC for the Mac, so wire any 21041 to 6,
774f43e6a25SDavid du Colombier 	 * it's the only 21041 this code will ever likely see.
7759a747e4fSDavid du Colombier 	 */
776f43e6a25SDavid du Colombier 	if((size = ctlr->sromsz) == 0){
777f43e6a25SDavid du Colombier 		if(ctlr->id == Tulip1)
778f43e6a25SDavid du Colombier 			ctlr->sromsz = size = 6;
779f43e6a25SDavid du Colombier 		else
7809a747e4fSDavid du Colombier 			size = 8;
781f43e6a25SDavid du Colombier 	}
7829a747e4fSDavid du Colombier 
7839a747e4fSDavid du Colombier 	for(size = size-1; size >= 0; size--){
7849a747e4fSDavid du Colombier 		data = Rd|Ss|(((r>>size) & 0x01)<<2)|Scs;
7857dd7cddfSDavid du Colombier 		csr9w(ctlr, data);
7867dd7cddfSDavid du Colombier 		csr9w(ctlr, data|Sclk);
7877dd7cddfSDavid du Colombier 		csr9w(ctlr, data);
7889a747e4fSDavid du Colombier 		microdelay(1);
789f43e6a25SDavid du Colombier 		if(ctlr->sromsz == 0 && !(csr32r(ctlr, 9) & Sdo))
7909a747e4fSDavid du Colombier 			break;
7917dd7cddfSDavid du Colombier 	}
7927dd7cddfSDavid du Colombier 
7937dd7cddfSDavid du Colombier 	data = 0;
7947dd7cddfSDavid du Colombier 	for(i = 16-1; i >= 0; i--){
7957dd7cddfSDavid du Colombier 		csr9w(ctlr, Rd|Ss|Sclk|Scs);
7967dd7cddfSDavid du Colombier 		if(csr32r(ctlr, 9) & Sdo)
7977dd7cddfSDavid du Colombier 			data |= (1<<i);
7987dd7cddfSDavid du Colombier 		csr9w(ctlr, Rd|Ss|Scs);
7997dd7cddfSDavid du Colombier 	}
8007dd7cddfSDavid du Colombier 
8017dd7cddfSDavid du Colombier 	csr9w(ctlr, 0);
8027dd7cddfSDavid du Colombier 
8039a747e4fSDavid du Colombier 	if(ctlr->sromsz == 0){
8049a747e4fSDavid du Colombier 		ctlr->sromsz = 8-size;
8059a747e4fSDavid du Colombier 		goto reread;
8069a747e4fSDavid du Colombier 	}
8079a747e4fSDavid du Colombier 
8087dd7cddfSDavid du Colombier 	return data & 0xFFFF;
8097dd7cddfSDavid du Colombier }
8107dd7cddfSDavid du Colombier 
8117dd7cddfSDavid du Colombier static void
shutdown(Ether * ether)8120809e9a7SDavid du Colombier shutdown(Ether* ether)
8130809e9a7SDavid du Colombier {
8140809e9a7SDavid du Colombier 	Ctlr *ctlr = ether->ctlr;
8150809e9a7SDavid du Colombier 
8160809e9a7SDavid du Colombier print("ether2114x shutting down\n");
8170809e9a7SDavid du Colombier 	csr32w(ctlr, 0, Swr);
8180809e9a7SDavid du Colombier }
8190809e9a7SDavid du Colombier 
8200809e9a7SDavid du Colombier static void
softreset(Ctlr * ctlr)8217dd7cddfSDavid du Colombier softreset(Ctlr* ctlr)
8227dd7cddfSDavid du Colombier {
8237dd7cddfSDavid du Colombier 	/*
8247dd7cddfSDavid du Colombier 	 * Soft-reset the controller and initialise bus mode.
8257dd7cddfSDavid du Colombier 	 * Delay should be >= 50 PCI cycles (2×S @ 25MHz).
8267dd7cddfSDavid du Colombier 	 */
8277dd7cddfSDavid du Colombier 	csr32w(ctlr, 0, Swr);
8287dd7cddfSDavid du Colombier 	microdelay(10);
8297dd7cddfSDavid du Colombier 	csr32w(ctlr, 0, Rml|Cal16);
8307dd7cddfSDavid du Colombier 	delay(1);
8317dd7cddfSDavid du Colombier }
8327dd7cddfSDavid du Colombier 
8337dd7cddfSDavid du Colombier static int
type5block(Ctlr * ctlr,uchar * block)8347dd7cddfSDavid du Colombier type5block(Ctlr* ctlr, uchar* block)
8357dd7cddfSDavid du Colombier {
8367dd7cddfSDavid du Colombier 	int csr15, i, len;
8377dd7cddfSDavid du Colombier 
8387dd7cddfSDavid du Colombier 	/*
8397dd7cddfSDavid du Colombier 	 * Reset or GPR sequence. Reset should be once only,
8407dd7cddfSDavid du Colombier 	 * before the GPR sequence.
8417dd7cddfSDavid du Colombier 	 * Note 'block' is not a pointer to the block head but
8427dd7cddfSDavid du Colombier 	 * a pointer to the data in the block starting at the
8437dd7cddfSDavid du Colombier 	 * reset length value so type5block can be used for the
8447dd7cddfSDavid du Colombier 	 * sequences contained in type 1 and type 3 blocks.
8457dd7cddfSDavid du Colombier 	 * The SROM docs state the 21140 type 5 block is the
8467dd7cddfSDavid du Colombier 	 * same as that for the 21143, but the two controllers
8477dd7cddfSDavid du Colombier 	 * use different registers and sequence-element lengths
8487dd7cddfSDavid du Colombier 	 * so the 21140 code here is a guess for a real type 5
8497dd7cddfSDavid du Colombier 	 * sequence.
8507dd7cddfSDavid du Colombier 	 */
8517dd7cddfSDavid du Colombier 	len = *block++;
85259cc4ca5SDavid du Colombier 	if(ctlr->id != Tulip3){
8537dd7cddfSDavid du Colombier 		for(i = 0; i < len; i++){
8547dd7cddfSDavid du Colombier 			csr32w(ctlr, 12, *block);
8557dd7cddfSDavid du Colombier 			block++;
8567dd7cddfSDavid du Colombier 		}
8577dd7cddfSDavid du Colombier 		return len;
8587dd7cddfSDavid du Colombier 	}
8597dd7cddfSDavid du Colombier 
8607dd7cddfSDavid du Colombier 	for(i = 0; i < len; i++){
8617dd7cddfSDavid du Colombier 		csr15 = *block++<<16;
8627dd7cddfSDavid du Colombier 		csr15 |= *block++<<24;
8637dd7cddfSDavid du Colombier 		csr32w(ctlr, 15, csr15);
8647dd7cddfSDavid du Colombier 		debug("%8.8uX ", csr15);
8657dd7cddfSDavid du Colombier 	}
8667dd7cddfSDavid du Colombier 	return 2*len;
8677dd7cddfSDavid du Colombier }
8687dd7cddfSDavid du Colombier 
8697dd7cddfSDavid du Colombier static int
typephylink(Ctlr * ctlr,uchar *)8707dd7cddfSDavid du Colombier typephylink(Ctlr* ctlr, uchar*)
8717dd7cddfSDavid du Colombier {
8727dd7cddfSDavid du Colombier 	int an, bmcr, bmsr, csr6, x;
8737dd7cddfSDavid du Colombier 
8747dd7cddfSDavid du Colombier 	/*
8757dd7cddfSDavid du Colombier 	 * Fail if
8767dd7cddfSDavid du Colombier 	 *	auto-negotiataion enabled but not complete;
8777dd7cddfSDavid du Colombier 	 *	no valid link established.
8787dd7cddfSDavid du Colombier 	 */
8797dd7cddfSDavid du Colombier 	bmcr = miir(ctlr, ctlr->curphyad, Bmcr);
8807dd7cddfSDavid du Colombier 	miir(ctlr, ctlr->curphyad, Bmsr);
8817dd7cddfSDavid du Colombier 	bmsr = miir(ctlr, ctlr->curphyad, Bmsr);
8827dd7cddfSDavid du Colombier 	debug("bmcr 0x%2.2uX bmsr 0x%2.2uX\n", bmcr, bmsr);
8837dd7cddfSDavid du Colombier 	if(((bmcr & 0x1000) && !(bmsr & 0x0020)) || !(bmsr & 0x0004))
8847dd7cddfSDavid du Colombier 		return 0;
8857dd7cddfSDavid du Colombier 
8867dd7cddfSDavid du Colombier 	if(bmcr & 0x1000){
8877dd7cddfSDavid du Colombier 		an = miir(ctlr, ctlr->curphyad, Anar);
8887dd7cddfSDavid du Colombier 		an &= miir(ctlr, ctlr->curphyad, Anlpar) & 0x3E0;
8897dd7cddfSDavid du Colombier 		debug("an 0x%2.uX 0x%2.2uX 0x%2.2uX\n",
8907dd7cddfSDavid du Colombier 	    		miir(ctlr, ctlr->curphyad, Anar),
8917dd7cddfSDavid du Colombier 			miir(ctlr, ctlr->curphyad, Anlpar),
8927dd7cddfSDavid du Colombier 			an);
8937dd7cddfSDavid du Colombier 
8947dd7cddfSDavid du Colombier 		if(an & 0x0100)
8957dd7cddfSDavid du Colombier 			x = 0x4000;
8967dd7cddfSDavid du Colombier 		else if(an & 0x0080)
8977dd7cddfSDavid du Colombier 			x = 0x2000;
8987dd7cddfSDavid du Colombier 		else if(an & 0x0040)
8997dd7cddfSDavid du Colombier 			x = 0x1000;
9007dd7cddfSDavid du Colombier 		else if(an & 0x0020)
9017dd7cddfSDavid du Colombier 			x = 0x0800;
9027dd7cddfSDavid du Colombier 		else
9037dd7cddfSDavid du Colombier 			x = 0;
9047dd7cddfSDavid du Colombier 	}
9057dd7cddfSDavid du Colombier 	else if((bmcr & 0x2100) == 0x2100)
9067dd7cddfSDavid du Colombier 		x = 0x4000;
9077dd7cddfSDavid du Colombier 	else if(bmcr & 0x2000){
9087dd7cddfSDavid du Colombier 		/*
9097dd7cddfSDavid du Colombier 		 * If FD capable, force it if necessary.
9107dd7cddfSDavid du Colombier 		 */
9117dd7cddfSDavid du Colombier 		if((bmsr & 0x4000) && ctlr->fd){
9127dd7cddfSDavid du Colombier 			miiw(ctlr, ctlr->curphyad, Bmcr, 0x2100);
9137dd7cddfSDavid du Colombier 			x = 0x4000;
9147dd7cddfSDavid du Colombier 		}
9157dd7cddfSDavid du Colombier 		else
9167dd7cddfSDavid du Colombier 			x = 0x2000;
9177dd7cddfSDavid du Colombier 	}
9187dd7cddfSDavid du Colombier 	else if(bmcr & 0x0100)
9197dd7cddfSDavid du Colombier 		x = 0x1000;
9207dd7cddfSDavid du Colombier 	else
9217dd7cddfSDavid du Colombier 		x = 0x0800;
9227dd7cddfSDavid du Colombier 
923f43e6a25SDavid du Colombier 	csr6 = Sc|Mbo|Hbd|Ps|Ca|TrMODE|Sb;
9247dd7cddfSDavid du Colombier 	if(ctlr->fdx & x)
9257dd7cddfSDavid du Colombier 		csr6 |= Fd;
9267dd7cddfSDavid du Colombier 	if(ctlr->ttm & x)
9277dd7cddfSDavid du Colombier 		csr6 |= Ttm;
9287dd7cddfSDavid du Colombier 	debug("csr6 0x%8.8uX 0x%8.8uX 0x%8.8luX\n",
9297dd7cddfSDavid du Colombier 		csr6, ctlr->csr6, csr32r(ctlr, 6));
9307dd7cddfSDavid du Colombier 	if(csr6 != ctlr->csr6){
9317dd7cddfSDavid du Colombier 		ctlr->csr6 = csr6;
9327dd7cddfSDavid du Colombier 		csr32w(ctlr, 6, csr6);
9337dd7cddfSDavid du Colombier 	}
9347dd7cddfSDavid du Colombier 
9357dd7cddfSDavid du Colombier 	return 1;
9367dd7cddfSDavid du Colombier }
9377dd7cddfSDavid du Colombier 
9387dd7cddfSDavid du Colombier static int
typephymode(Ctlr * ctlr,uchar * block,int wait)9397dd7cddfSDavid du Colombier typephymode(Ctlr* ctlr, uchar* block, int wait)
9407dd7cddfSDavid du Colombier {
9417dd7cddfSDavid du Colombier 	uchar *p;
9427dd7cddfSDavid du Colombier 	int len, mc, nway, phyx, timeo;
9437dd7cddfSDavid du Colombier 
9447dd7cddfSDavid du Colombier 	if(DEBUG){
9457dd7cddfSDavid du Colombier 		int i;
9467dd7cddfSDavid du Colombier 
9477dd7cddfSDavid du Colombier 		len = (block[0] & ~0x80)+1;
9487dd7cddfSDavid du Colombier 		for(i = 0; i < len; i++)
9497dd7cddfSDavid du Colombier 			debug("%2.2uX ", block[i]);
9507dd7cddfSDavid du Colombier 		debug("\n");
9517dd7cddfSDavid du Colombier 	}
9527dd7cddfSDavid du Colombier 
9537dd7cddfSDavid du Colombier 	if(block[1] == 1)
9547dd7cddfSDavid du Colombier 		len = 1;
9557dd7cddfSDavid du Colombier 	else if(block[1] == 3)
9567dd7cddfSDavid du Colombier 		len = 2;
9577dd7cddfSDavid du Colombier 	else
9587dd7cddfSDavid du Colombier 		return -1;
9597dd7cddfSDavid du Colombier 
9607dd7cddfSDavid du Colombier 	/*
9617dd7cddfSDavid du Colombier 	 * Snarf the media capabilities, nway advertisment,
9627dd7cddfSDavid du Colombier 	 * FDX and TTM bitmaps.
9637dd7cddfSDavid du Colombier 	 */
9647dd7cddfSDavid du Colombier 	p = &block[5+len*block[3]+len*block[4+len*block[3]]];
9657dd7cddfSDavid du Colombier 	mc = *p++;
9667dd7cddfSDavid du Colombier 	mc |= *p++<<8;
9677dd7cddfSDavid du Colombier 	nway = *p++;
9687dd7cddfSDavid du Colombier 	nway |= *p++<<8;
9697dd7cddfSDavid du Colombier 	ctlr->fdx = *p++;
9707dd7cddfSDavid du Colombier 	ctlr->fdx |= *p++<<8;
9717dd7cddfSDavid du Colombier 	ctlr->ttm = *p++;
9727dd7cddfSDavid du Colombier 	ctlr->ttm |= *p<<8;
9737dd7cddfSDavid du Colombier 	debug("mc %4.4uX nway %4.4uX fdx %4.4uX ttm %4.4uX\n",
9747dd7cddfSDavid du Colombier 		mc, nway, ctlr->fdx, ctlr->ttm);
9757dd7cddfSDavid du Colombier 	USED(mc);
9767dd7cddfSDavid du Colombier 
9777dd7cddfSDavid du Colombier 	phyx = block[2];
9787dd7cddfSDavid du Colombier 	ctlr->curphyad = ctlr->phy[phyx];
9797dd7cddfSDavid du Colombier 
98041dd6b47SDavid du Colombier 	ctlr->csr6 = 0;		/* Sc|Mbo|Hbd|Ps|Ca|TrMODE|Sb; */
9817dd7cddfSDavid du Colombier 	// csr32w(ctlr, 6, ctlr->csr6);
9827dd7cddfSDavid du Colombier 	if(typephylink(ctlr, block))
9837dd7cddfSDavid du Colombier 		return 0;
9847dd7cddfSDavid du Colombier 
9857dd7cddfSDavid du Colombier 	if(!(ctlr->phyreset & (1<<phyx))){
9867dd7cddfSDavid du Colombier 		debug("reset seq: len %d: ", block[3]);
9877dd7cddfSDavid du Colombier 		if(ctlr->type5block)
9887dd7cddfSDavid du Colombier 			type5block(ctlr, &ctlr->type5block[2]);
9897dd7cddfSDavid du Colombier 		else
9907dd7cddfSDavid du Colombier 			type5block(ctlr, &block[4+len*block[3]]);
9917dd7cddfSDavid du Colombier 		debug("\n");
9927dd7cddfSDavid du Colombier 		ctlr->phyreset |= (1<<phyx);
9937dd7cddfSDavid du Colombier 	}
9947dd7cddfSDavid du Colombier 
9957dd7cddfSDavid du Colombier 	/*
9967dd7cddfSDavid du Colombier 	 * GPR sequence.
9977dd7cddfSDavid du Colombier 	 */
9987dd7cddfSDavid du Colombier 	debug("gpr seq: len %d: ", block[3]);
9997dd7cddfSDavid du Colombier 	type5block(ctlr, &block[3]);
10007dd7cddfSDavid du Colombier 	debug("\n");
10017dd7cddfSDavid du Colombier 
100241dd6b47SDavid du Colombier 	ctlr->csr6 = 0;		/* Sc|Mbo|Hbd|Ps|Ca|TrMODE|Sb; */
10037dd7cddfSDavid du Colombier 	// csr32w(ctlr, 6, ctlr->csr6);
10047dd7cddfSDavid du Colombier 	if(typephylink(ctlr, block))
10057dd7cddfSDavid du Colombier 		return 0;
10067dd7cddfSDavid du Colombier 
10077dd7cddfSDavid du Colombier 	/*
10087dd7cddfSDavid du Colombier 	 * Turn off auto-negotiation, set the auto-negotiation
10097dd7cddfSDavid du Colombier 	 * advertisment register then start the auto-negotiation
10107dd7cddfSDavid du Colombier 	 * process again.
10117dd7cddfSDavid du Colombier 	 */
10127dd7cddfSDavid du Colombier 	miiw(ctlr, ctlr->curphyad, Bmcr, 0);
10137dd7cddfSDavid du Colombier 	miiw(ctlr, ctlr->curphyad, Anar, nway|1);
10147dd7cddfSDavid du Colombier 	miiw(ctlr, ctlr->curphyad, Bmcr, 0x1000);
10157dd7cddfSDavid du Colombier 
10167dd7cddfSDavid du Colombier 	if(!wait)
10177dd7cddfSDavid du Colombier 		return 0;
10187dd7cddfSDavid du Colombier 
1019d9306527SDavid du Colombier 	for(timeo = 0; timeo < 45; timeo++){
10207dd7cddfSDavid du Colombier 		if(typephylink(ctlr, block))
10217dd7cddfSDavid du Colombier 			return 0;
10227dd7cddfSDavid du Colombier 		delay(100);
10237dd7cddfSDavid du Colombier 	}
10247dd7cddfSDavid du Colombier 
10257dd7cddfSDavid du Colombier 	return -1;
10267dd7cddfSDavid du Colombier }
10277dd7cddfSDavid du Colombier 
10287dd7cddfSDavid du Colombier static int
typesymmode(Ctlr * ctlr,uchar * block,int wait)10299a747e4fSDavid du Colombier typesymmode(Ctlr *ctlr, uchar *block, int wait)
10309a747e4fSDavid du Colombier {
10319a747e4fSDavid du Colombier 	uint gpmode, gpdata, command;
10329a747e4fSDavid du Colombier 
10339a747e4fSDavid du Colombier 	USED(wait);
10349a747e4fSDavid du Colombier 	gpmode = block[3] | ((uint) block[4] << 8);
10359a747e4fSDavid du Colombier 	gpdata = block[5] | ((uint) block[6] << 8);
10369a747e4fSDavid du Colombier 	command = (block[7] | ((uint) block[8] << 8)) & 0x71;
10379a747e4fSDavid du Colombier 	if (command & 0x8000) {
10389a747e4fSDavid du Colombier 		print("ether2114x.c: FIXME: handle type 4 mode blocks where cmd.active_invalid != 0\n");
10399a747e4fSDavid du Colombier 		return -1;
10409a747e4fSDavid du Colombier 	}
10419a747e4fSDavid du Colombier 	csr32w(ctlr, 15, gpmode);
10429a747e4fSDavid du Colombier 	csr32w(ctlr, 15, gpdata);
10439a747e4fSDavid du Colombier 	ctlr->csr6 = (command & 0x71) << 18;
10449a747e4fSDavid du Colombier 	csr32w(ctlr, 6, ctlr->csr6);
10459a747e4fSDavid du Colombier 	return 0;
10469a747e4fSDavid du Colombier }
10479a747e4fSDavid du Colombier 
10489a747e4fSDavid du Colombier static int
type2mode(Ctlr * ctlr,uchar * block,int)10499a747e4fSDavid du Colombier type2mode(Ctlr* ctlr, uchar* block, int)
10509a747e4fSDavid du Colombier {
10519a747e4fSDavid du Colombier 	uchar *p;
10529a747e4fSDavid du Colombier 	int csr6, csr13, csr14, csr15, gpc, gpd;
10539a747e4fSDavid du Colombier 
1054f43e6a25SDavid du Colombier 	csr6 = Sc|Mbo|Ca|TrMODE|Sb;
10559a747e4fSDavid du Colombier 	debug("type2mode: medium 0x%2.2uX\n", block[2]);
10569a747e4fSDavid du Colombier 
10579a747e4fSDavid du Colombier 	/*
10589a747e4fSDavid du Colombier 	 * Don't attempt full-duplex
10599a747e4fSDavid du Colombier 	 * unless explicitly requested.
10609a747e4fSDavid du Colombier 	 */
10619a747e4fSDavid du Colombier 	if((block[2] & 0x3F) == 0x04){	/* 10BASE-TFD */
10629a747e4fSDavid du Colombier 		if(!ctlr->fd)
10639a747e4fSDavid du Colombier 			return -1;
10649a747e4fSDavid du Colombier 		csr6 |= Fd;
10659a747e4fSDavid du Colombier 	}
10669a747e4fSDavid du Colombier 
10679a747e4fSDavid du Colombier 	/*
10689a747e4fSDavid du Colombier 	 * Operating mode programming values from the datasheet
10699a747e4fSDavid du Colombier 	 * unless media specific data is explicitly given.
10709a747e4fSDavid du Colombier 	 */
10719a747e4fSDavid du Colombier 	p = &block[3];
10729a747e4fSDavid du Colombier 	if(block[2] & 0x40){
10739a747e4fSDavid du Colombier 		csr13 = (block[4]<<8)|block[3];
10749a747e4fSDavid du Colombier 		csr14 = (block[6]<<8)|block[5];
10759a747e4fSDavid du Colombier 		csr15 = (block[8]<<8)|block[7];
10769a747e4fSDavid du Colombier 		p += 6;
10779a747e4fSDavid du Colombier 	}
10789a747e4fSDavid du Colombier 	else switch(block[2] & 0x3F){
10799a747e4fSDavid du Colombier 	default:
10809a747e4fSDavid du Colombier 		return -1;
10819a747e4fSDavid du Colombier 	case 0x00:			/* 10BASE-T */
10829a747e4fSDavid du Colombier 		csr13 = 0x00000001;
10839a747e4fSDavid du Colombier 		csr14 = 0x00007F3F;
10849a747e4fSDavid du Colombier 		csr15 = 0x00000008;
10859a747e4fSDavid du Colombier 		break;
10869a747e4fSDavid du Colombier 	case 0x01:			/* 10BASE-2 */
10879a747e4fSDavid du Colombier 		csr13 = 0x00000009;
10889a747e4fSDavid du Colombier 		csr14 = 0x00000705;
10899a747e4fSDavid du Colombier 		csr15 = 0x00000006;
10909a747e4fSDavid du Colombier 		break;
10919a747e4fSDavid du Colombier 	case 0x02:			/* 10BASE-5 (AUI) */
10929a747e4fSDavid du Colombier 		csr13 = 0x00000009;
10939a747e4fSDavid du Colombier 		csr14 = 0x00000705;
10949a747e4fSDavid du Colombier 		csr15 = 0x0000000E;
10959a747e4fSDavid du Colombier 		break;
10969a747e4fSDavid du Colombier 	case 0x04:			/* 10BASE-TFD */
10979a747e4fSDavid du Colombier 		csr13 = 0x00000001;
10989a747e4fSDavid du Colombier 		csr14 = 0x00007F3D;
10999a747e4fSDavid du Colombier 		csr15 = 0x00000008;
11009a747e4fSDavid du Colombier 		break;
11019a747e4fSDavid du Colombier 	}
11029a747e4fSDavid du Colombier 	gpc = *p++<<16;
11039a747e4fSDavid du Colombier 	gpc |= *p++<<24;
11049a747e4fSDavid du Colombier 	gpd = *p++<<16;
11059a747e4fSDavid du Colombier 	gpd |= *p<<24;
11069a747e4fSDavid du Colombier 
11079a747e4fSDavid du Colombier 	csr32w(ctlr, 13, 0);
11089a747e4fSDavid du Colombier 	csr32w(ctlr, 14, csr14);
11099a747e4fSDavid du Colombier 	csr32w(ctlr, 15, gpc|csr15);
11109a747e4fSDavid du Colombier 	delay(10);
11119a747e4fSDavid du Colombier 	csr32w(ctlr, 15, gpd|csr15);
11129a747e4fSDavid du Colombier 	csr32w(ctlr, 13, csr13);
11139a747e4fSDavid du Colombier 
11149a747e4fSDavid du Colombier 	ctlr->csr6 = csr6;
11159a747e4fSDavid du Colombier 	csr32w(ctlr, 6, ctlr->csr6);
11169a747e4fSDavid du Colombier 
11179a747e4fSDavid du Colombier 	debug("type2mode: csr13 %8.8uX csr14 %8.8uX csr15 %8.8uX\n",
11189a747e4fSDavid du Colombier 		csr13, csr14, csr15);
11199a747e4fSDavid du Colombier 	debug("type2mode: gpc %8.8uX gpd %8.8uX csr6 %8.8uX\n",
11209a747e4fSDavid du Colombier 		gpc, gpd, csr6);
11219a747e4fSDavid du Colombier 
11229a747e4fSDavid du Colombier 	return 0;
11239a747e4fSDavid du Colombier }
11249a747e4fSDavid du Colombier 
11259a747e4fSDavid du Colombier static int
type0link(Ctlr * ctlr,uchar * block)11267dd7cddfSDavid du Colombier type0link(Ctlr* ctlr, uchar* block)
11277dd7cddfSDavid du Colombier {
11287dd7cddfSDavid du Colombier 	int m, polarity, sense;
11297dd7cddfSDavid du Colombier 
11307dd7cddfSDavid du Colombier 	m = (block[3]<<8)|block[2];
11317dd7cddfSDavid du Colombier 	sense = 1<<((m & 0x000E)>>1);
11327dd7cddfSDavid du Colombier 	if(m & 0x0080)
11337dd7cddfSDavid du Colombier 		polarity = sense;
11347dd7cddfSDavid du Colombier 	else
11357dd7cddfSDavid du Colombier 		polarity = 0;
11367dd7cddfSDavid du Colombier 
11377dd7cddfSDavid du Colombier 	return (csr32r(ctlr, 12) & sense)^polarity;
11387dd7cddfSDavid du Colombier }
11397dd7cddfSDavid du Colombier 
11407dd7cddfSDavid du Colombier static int
type0mode(Ctlr * ctlr,uchar * block,int wait)11417dd7cddfSDavid du Colombier type0mode(Ctlr* ctlr, uchar* block, int wait)
11427dd7cddfSDavid du Colombier {
11437dd7cddfSDavid du Colombier 	int csr6, m, timeo;
11447dd7cddfSDavid du Colombier 
1145f43e6a25SDavid du Colombier 	csr6 = Sc|Mbo|Hbd|Ca|TrMODE|Sb;
11467dd7cddfSDavid du Colombier debug("type0: medium 0x%uX, fd %d: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n",
11477dd7cddfSDavid du Colombier     ctlr->medium, ctlr->fd, block[0], block[1], block[2], block[3]);
11487dd7cddfSDavid du Colombier 	switch(block[0]){
11497dd7cddfSDavid du Colombier 	default:
11507dd7cddfSDavid du Colombier 		break;
11517dd7cddfSDavid du Colombier 
11527dd7cddfSDavid du Colombier 	case 0x04:			/* 10BASE-TFD */
11537dd7cddfSDavid du Colombier 	case 0x05:			/* 100BASE-TXFD */
11547dd7cddfSDavid du Colombier 	case 0x08:			/* 100BASE-FXFD */
11557dd7cddfSDavid du Colombier 		/*
11567dd7cddfSDavid du Colombier 		 * Don't attempt full-duplex
11577dd7cddfSDavid du Colombier 		 * unless explicitly requested.
11587dd7cddfSDavid du Colombier 		 */
11597dd7cddfSDavid du Colombier 		if(!ctlr->fd)
11607dd7cddfSDavid du Colombier 			return -1;
11617dd7cddfSDavid du Colombier 		csr6 |= Fd;
11627dd7cddfSDavid du Colombier 		break;
11637dd7cddfSDavid du Colombier 	}
11647dd7cddfSDavid du Colombier 
11657dd7cddfSDavid du Colombier 	m = (block[3]<<8)|block[2];
11667dd7cddfSDavid du Colombier 	if(m & 0x0001)
11677dd7cddfSDavid du Colombier 		csr6 |= Ps;
11687dd7cddfSDavid du Colombier 	if(m & 0x0010)
11697dd7cddfSDavid du Colombier 		csr6 |= Ttm;
11707dd7cddfSDavid du Colombier 	if(m & 0x0020)
11717dd7cddfSDavid du Colombier 		csr6 |= Pcs;
11727dd7cddfSDavid du Colombier 	if(m & 0x0040)
11737dd7cddfSDavid du Colombier 		csr6 |= Scr;
11747dd7cddfSDavid du Colombier 
11757dd7cddfSDavid du Colombier 	csr32w(ctlr, 12, block[1]);
11767dd7cddfSDavid du Colombier 	microdelay(10);
11777dd7cddfSDavid du Colombier 	csr32w(ctlr, 6, csr6);
11787dd7cddfSDavid du Colombier 	ctlr->csr6 = csr6;
11797dd7cddfSDavid du Colombier 
11807dd7cddfSDavid du Colombier 	if(!wait)
11817dd7cddfSDavid du Colombier 		return 0;
11827dd7cddfSDavid du Colombier 
11837dd7cddfSDavid du Colombier 	for(timeo = 0; timeo < 30; timeo++){
11847dd7cddfSDavid du Colombier 		if(type0link(ctlr, block))
11857dd7cddfSDavid du Colombier 			return 0;
11867dd7cddfSDavid du Colombier 		delay(100);
11877dd7cddfSDavid du Colombier 	}
11887dd7cddfSDavid du Colombier 
11897dd7cddfSDavid du Colombier 	return -1;
11907dd7cddfSDavid du Colombier }
11917dd7cddfSDavid du Colombier 
11927dd7cddfSDavid du Colombier static int
media21041(Ether * ether,int wait)1193f43e6a25SDavid du Colombier media21041(Ether* ether, int wait)
1194f43e6a25SDavid du Colombier {
1195f43e6a25SDavid du Colombier 	Ctlr* ctlr;
1196f43e6a25SDavid du Colombier 	uchar *block;
1197f43e6a25SDavid du Colombier 	int csr6, csr13, csr14, csr15, medium, timeo;
1198f43e6a25SDavid du Colombier 
1199f43e6a25SDavid du Colombier 	ctlr = ether->ctlr;
1200f43e6a25SDavid du Colombier 	block = ctlr->infoblock[ctlr->curk];
1201f43e6a25SDavid du Colombier 	debug("media21041: block[0] %2.2uX, medium %4.4uX sct %4.4uX\n",
1202f43e6a25SDavid du Colombier 		block[0], ctlr->medium, ctlr->sct);
1203f43e6a25SDavid du Colombier 
1204f43e6a25SDavid du Colombier 	medium = block[0] & 0x3F;
1205f43e6a25SDavid du Colombier 	if(ctlr->medium >= 0 && medium != ctlr->medium)
1206f43e6a25SDavid du Colombier 		return 0;
1207f43e6a25SDavid du Colombier 	if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != medium)
1208f43e6a25SDavid du Colombier 		return 0;
1209f43e6a25SDavid du Colombier 
1210f43e6a25SDavid du Colombier 	csr6 = Sc|Mbo|Ca|TrMODE|Sb;
1211f43e6a25SDavid du Colombier 	if(block[0] & 0x40){
1212f43e6a25SDavid du Colombier 		csr13 = (block[2]<<8)|block[1];
1213f43e6a25SDavid du Colombier 		csr14 = (block[4]<<8)|block[3];
1214f43e6a25SDavid du Colombier 		csr15 = (block[6]<<8)|block[5];
1215f43e6a25SDavid du Colombier 	}
1216f43e6a25SDavid du Colombier 	else switch(medium){
1217f43e6a25SDavid du Colombier 	default:
1218f43e6a25SDavid du Colombier 		return -1;
1219f43e6a25SDavid du Colombier 	case 0x00:		/* 10BASE-T */
1220f43e6a25SDavid du Colombier 		csr13 = 0xEF01;
1221f43e6a25SDavid du Colombier 		csr14 = 0xFF3F;
1222f43e6a25SDavid du Colombier 		csr15 = 0x0008;
1223f43e6a25SDavid du Colombier 		break;
1224f43e6a25SDavid du Colombier 	case 0x01:		/* 10BASE-2 */
1225f43e6a25SDavid du Colombier 		csr13 = 0xEF09;
1226f43e6a25SDavid du Colombier 		csr14 = 0xF73D;
1227f43e6a25SDavid du Colombier 		csr15 = 0x0006;
1228f43e6a25SDavid du Colombier 		break;
1229f43e6a25SDavid du Colombier 	case 0x02:		/* 10BASE-5 */
1230f43e6a25SDavid du Colombier 		csr13 = 0xEF09;
1231f43e6a25SDavid du Colombier 		csr14 = 0xF73D;
1232f43e6a25SDavid du Colombier 		csr15 = 0x000E;
1233f43e6a25SDavid du Colombier 		break;
1234f43e6a25SDavid du Colombier 	case 0x04:		/* 10BASE-TFD */
1235f43e6a25SDavid du Colombier 		csr13 = 0xEF01;
1236f43e6a25SDavid du Colombier 		csr14 = 0xFF3D;
1237f43e6a25SDavid du Colombier 		csr15 = 0x0008;
1238f43e6a25SDavid du Colombier 		break;
1239f43e6a25SDavid du Colombier 	}
1240f43e6a25SDavid du Colombier 
1241f43e6a25SDavid du Colombier 	csr32w(ctlr, 13, 0);
1242f43e6a25SDavid du Colombier 	csr32w(ctlr, 14, csr14);
1243f43e6a25SDavid du Colombier 	csr32w(ctlr, 15, csr15);
1244f43e6a25SDavid du Colombier 	csr32w(ctlr, 13, csr13);
1245f43e6a25SDavid du Colombier 	delay(10);
1246f43e6a25SDavid du Colombier 
1247f43e6a25SDavid du Colombier 	if(medium == 0x04)
1248f43e6a25SDavid du Colombier 		csr6 |= Fd;
1249f43e6a25SDavid du Colombier 	ctlr->csr6 = csr6;
1250f43e6a25SDavid du Colombier 	csr32w(ctlr, 6, ctlr->csr6);
1251f43e6a25SDavid du Colombier 
1252f43e6a25SDavid du Colombier 	debug("media21041: csr6 %8.8uX csr13 %4.4uX csr14 %4.4uX csr15 %4.4uX\n",
1253f43e6a25SDavid du Colombier 		csr6, csr13, csr14, csr15);
1254f43e6a25SDavid du Colombier 
1255f43e6a25SDavid du Colombier 	if(!wait)
1256f43e6a25SDavid du Colombier 		return 0;
1257f43e6a25SDavid du Colombier 
1258f43e6a25SDavid du Colombier 	for(timeo = 0; timeo < 30; timeo++){
1259f43e6a25SDavid du Colombier 		if(!(csr32r(ctlr, 12) & 0x0002)){
1260f43e6a25SDavid du Colombier 			debug("media21041: ok: csr12 %4.4luX timeo %d\n",
1261f43e6a25SDavid du Colombier 				csr32r(ctlr, 12), timeo);
1262f43e6a25SDavid du Colombier 			return 10;
1263f43e6a25SDavid du Colombier 		}
1264f43e6a25SDavid du Colombier 		delay(100);
1265f43e6a25SDavid du Colombier 	}
1266f43e6a25SDavid du Colombier 	debug("media21041: !ok: csr12 %4.4luX\n", csr32r(ctlr, 12));
1267f43e6a25SDavid du Colombier 
1268f43e6a25SDavid du Colombier 	return -1;
1269f43e6a25SDavid du Colombier }
1270f43e6a25SDavid du Colombier 
1271f43e6a25SDavid du Colombier static int
mediaxx(Ether * ether,int wait)12727dd7cddfSDavid du Colombier mediaxx(Ether* ether, int wait)
12737dd7cddfSDavid du Colombier {
12747dd7cddfSDavid du Colombier 	Ctlr* ctlr;
12757dd7cddfSDavid du Colombier 	uchar *block;
12767dd7cddfSDavid du Colombier 
12777dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
12787dd7cddfSDavid du Colombier 	block = ctlr->infoblock[ctlr->curk];
12797dd7cddfSDavid du Colombier 	if(block[0] & 0x80){
12807dd7cddfSDavid du Colombier 		switch(block[1]){
12817dd7cddfSDavid du Colombier 		default:
12827dd7cddfSDavid du Colombier 			return -1;
12837dd7cddfSDavid du Colombier 		case 0:
12847dd7cddfSDavid du Colombier 			if(ctlr->medium >= 0 && block[2] != ctlr->medium)
12857dd7cddfSDavid du Colombier 				return 0;
12867dd7cddfSDavid du Colombier /* need this test? */	if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[2])
12877dd7cddfSDavid du Colombier 				return 0;
12887dd7cddfSDavid du Colombier 			if(type0mode(ctlr, block+2, wait))
12897dd7cddfSDavid du Colombier 				return 0;
12907dd7cddfSDavid du Colombier 			break;
12917dd7cddfSDavid du Colombier 		case 1:
12927dd7cddfSDavid du Colombier 			if(typephymode(ctlr, block, wait))
12937dd7cddfSDavid du Colombier 				return 0;
12947dd7cddfSDavid du Colombier 			break;
12959a747e4fSDavid du Colombier 		case 2:
12969a747e4fSDavid du Colombier 			debug("type2: medium %d block[2] %d\n",
12979a747e4fSDavid du Colombier 				ctlr->medium, block[2]);
12989a747e4fSDavid du Colombier 			if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium))
12999a747e4fSDavid du Colombier 				return 0;
13009a747e4fSDavid du Colombier 			if(type2mode(ctlr, block, wait))
13019a747e4fSDavid du Colombier 				return 0;
13029a747e4fSDavid du Colombier 			break;
13037dd7cddfSDavid du Colombier 		case 3:
13047dd7cddfSDavid du Colombier 			if(typephymode(ctlr, block, wait))
13057dd7cddfSDavid du Colombier 				return 0;
13067dd7cddfSDavid du Colombier 			break;
13079a747e4fSDavid du Colombier 		case 4:
13089a747e4fSDavid du Colombier 			debug("type4: medium %d block[2] %d\n",
13099a747e4fSDavid du Colombier 				ctlr->medium, block[2]);
13109a747e4fSDavid du Colombier 			if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium))
13119a747e4fSDavid du Colombier 				return 0;
13129a747e4fSDavid du Colombier 			if(typesymmode(ctlr, block, wait))
13139a747e4fSDavid du Colombier 				return 0;
13149a747e4fSDavid du Colombier 			break;
13157dd7cddfSDavid du Colombier 		}
13167dd7cddfSDavid du Colombier 	}
13177dd7cddfSDavid du Colombier 	else{
13187dd7cddfSDavid du Colombier 		if(ctlr->medium >= 0 && block[0] != ctlr->medium)
13197dd7cddfSDavid du Colombier 			return 0;
13207dd7cddfSDavid du Colombier /* need this test? */if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[0])
13217dd7cddfSDavid du Colombier 			return 0;
13227dd7cddfSDavid du Colombier 		if(type0mode(ctlr, block, wait))
13237dd7cddfSDavid du Colombier 			return 0;
13247dd7cddfSDavid du Colombier 	}
13257dd7cddfSDavid du Colombier 
13267dd7cddfSDavid du Colombier 	if(ctlr->csr6){
13277dd7cddfSDavid du Colombier 		if(!(ctlr->csr6 & Ps) || (ctlr->csr6 & Ttm))
13287dd7cddfSDavid du Colombier 			return 10;
13297dd7cddfSDavid du Colombier 		return 100;
13307dd7cddfSDavid du Colombier 	}
13317dd7cddfSDavid du Colombier 
13327dd7cddfSDavid du Colombier 	return 0;
13337dd7cddfSDavid du Colombier }
13347dd7cddfSDavid du Colombier 
13357dd7cddfSDavid du Colombier static int
media(Ether * ether,int wait)13367dd7cddfSDavid du Colombier media(Ether* ether, int wait)
13377dd7cddfSDavid du Colombier {
13387dd7cddfSDavid du Colombier 	Ctlr* ctlr;
13397dd7cddfSDavid du Colombier 	int k, mbps;
13407dd7cddfSDavid du Colombier 
13417dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
13427dd7cddfSDavid du Colombier 	for(k = 0; k < ctlr->k; k++){
1343f43e6a25SDavid du Colombier 		switch(ctlr->id){
1344f43e6a25SDavid du Colombier 		default:
13457dd7cddfSDavid du Colombier 			mbps = mediaxx(ether, wait);
1346f43e6a25SDavid du Colombier 			break;
1347f43e6a25SDavid du Colombier 		case Tulip1:			/* 21041 */
1348f43e6a25SDavid du Colombier 			mbps = media21041(ether, wait);
1349f43e6a25SDavid du Colombier 			break;
1350f43e6a25SDavid du Colombier 		}
13517dd7cddfSDavid du Colombier 		if(mbps > 0)
13527dd7cddfSDavid du Colombier 			return mbps;
13537dd7cddfSDavid du Colombier 		if(ctlr->curk == 0)
13547dd7cddfSDavid du Colombier 			ctlr->curk = ctlr->k-1;
13557dd7cddfSDavid du Colombier 		else
13567dd7cddfSDavid du Colombier 			ctlr->curk--;
13577dd7cddfSDavid du Colombier 	}
13587dd7cddfSDavid du Colombier 
13597dd7cddfSDavid du Colombier 	return 0;
13607dd7cddfSDavid du Colombier }
13617dd7cddfSDavid du Colombier 
13627dd7cddfSDavid du Colombier static char* mediatable[9] = {
13637dd7cddfSDavid du Colombier 	"10BASE-T",				/* TP */
13647dd7cddfSDavid du Colombier 	"10BASE-2",				/* BNC */
13657dd7cddfSDavid du Colombier 	"10BASE-5",				/* AUI */
13667dd7cddfSDavid du Colombier 	"100BASE-TX",
13677dd7cddfSDavid du Colombier 	"10BASE-TFD",
13687dd7cddfSDavid du Colombier 	"100BASE-TXFD",
13697dd7cddfSDavid du Colombier 	"100BASE-T4",
13707dd7cddfSDavid du Colombier 	"100BASE-FX",
13717dd7cddfSDavid du Colombier 	"100BASE-FXFD",
13727dd7cddfSDavid du Colombier };
13737dd7cddfSDavid du Colombier 
13747dd7cddfSDavid du Colombier static uchar en1207[] = {		/* Accton EN1207-COMBO */
13757dd7cddfSDavid du Colombier 	0x00, 0x00, 0xE8,		/* [0]  vendor ethernet code */
13767dd7cddfSDavid du Colombier 	0x00,				/* [3]  spare */
13777dd7cddfSDavid du Colombier 
13787dd7cddfSDavid du Colombier 	0x00, 0x08,			/* [4]  connection (LSB+MSB = 0x0800) */
13797dd7cddfSDavid du Colombier 	0x1F,				/* [6]  general purpose control */
13807dd7cddfSDavid du Colombier 	2,				/* [7]  block count */
13817dd7cddfSDavid du Colombier 
13827dd7cddfSDavid du Colombier 	0x00,				/* [8]  media code (10BASE-TX) */
13837dd7cddfSDavid du Colombier 	0x0B,				/* [9]  general purpose port data */
13847dd7cddfSDavid du Colombier 	0x9E, 0x00,			/* [10] command (LSB+MSB = 0x009E) */
13857dd7cddfSDavid du Colombier 
13867dd7cddfSDavid du Colombier 	0x03,				/* [8]  media code (100BASE-TX) */
13877dd7cddfSDavid du Colombier 	0x1B,				/* [9]  general purpose port data */
13887dd7cddfSDavid du Colombier 	0x6D, 0x00,			/* [10] command (LSB+MSB = 0x006D) */
13897dd7cddfSDavid du Colombier 
13907dd7cddfSDavid du Colombier 					/* There is 10BASE-2 as well, but... */
13917dd7cddfSDavid du Colombier };
13927dd7cddfSDavid du Colombier 
13937dd7cddfSDavid du Colombier static uchar ana6910fx[] = {		/* Adaptec (Cogent) ANA-6910FX */
13947dd7cddfSDavid du Colombier 	0x00, 0x00, 0x92,		/* [0]  vendor ethernet code */
13957dd7cddfSDavid du Colombier 	0x00,				/* [3]  spare */
13967dd7cddfSDavid du Colombier 
13977dd7cddfSDavid du Colombier 	0x00, 0x08,			/* [4]  connection (LSB+MSB = 0x0800) */
13987dd7cddfSDavid du Colombier 	0x3F,				/* [6]  general purpose control */
13997dd7cddfSDavid du Colombier 	1,				/* [7]  block count */
14007dd7cddfSDavid du Colombier 
14017dd7cddfSDavid du Colombier 	0x07,				/* [8]  media code (100BASE-FX) */
14027dd7cddfSDavid du Colombier 	0x03,				/* [9]  general purpose port data */
14037dd7cddfSDavid du Colombier 	0x2D, 0x00			/* [10] command (LSB+MSB = 0x000D) */
14047dd7cddfSDavid du Colombier };
14057dd7cddfSDavid du Colombier 
14067dd7cddfSDavid du Colombier static uchar smc9332[] = {		/* SMC 9332 */
14077dd7cddfSDavid du Colombier 	0x00, 0x00, 0xC0,		/* [0]  vendor ethernet code */
14087dd7cddfSDavid du Colombier 	0x00,				/* [3]  spare */
14097dd7cddfSDavid du Colombier 
14107dd7cddfSDavid du Colombier 	0x00, 0x08,			/* [4]  connection (LSB+MSB = 0x0800) */
14117dd7cddfSDavid du Colombier 	0x1F,				/* [6]  general purpose control */
14127dd7cddfSDavid du Colombier 	2,				/* [7]  block count */
14137dd7cddfSDavid du Colombier 
14147dd7cddfSDavid du Colombier 	0x00,				/* [8]  media code (10BASE-TX) */
14157dd7cddfSDavid du Colombier 	0x00,				/* [9]  general purpose port data */
14167dd7cddfSDavid du Colombier 	0x9E, 0x00,			/* [10] command (LSB+MSB = 0x009E) */
14177dd7cddfSDavid du Colombier 
14187dd7cddfSDavid du Colombier 	0x03,				/* [8]  media code (100BASE-TX) */
14197dd7cddfSDavid du Colombier 	0x09,				/* [9]  general purpose port data */
14207dd7cddfSDavid du Colombier 	0x6D, 0x00,			/* [10] command (LSB+MSB = 0x006D) */
14217dd7cddfSDavid du Colombier };
14227dd7cddfSDavid du Colombier 
14237dd7cddfSDavid du Colombier static uchar* leaf21140[] = {
14247dd7cddfSDavid du Colombier 	en1207,				/* Accton EN1207-COMBO */
14257dd7cddfSDavid du Colombier 	ana6910fx,			/* Adaptec (Cogent) ANA-6910FX */
14267dd7cddfSDavid du Colombier 	smc9332,			/* SMC 9332 */
14277dd7cddfSDavid du Colombier 	nil,
14287dd7cddfSDavid du Colombier };
14297dd7cddfSDavid du Colombier 
143059cc4ca5SDavid du Colombier /*
143159cc4ca5SDavid du Colombier  * Copied to ctlr->srom at offset 20.
143259cc4ca5SDavid du Colombier  */
143359cc4ca5SDavid du Colombier static uchar leafpnic[] = {
143459cc4ca5SDavid du Colombier 	0x00, 0x00, 0x00, 0x00,		/* MAC address */
143559cc4ca5SDavid du Colombier 	0x00, 0x00,
143659cc4ca5SDavid du Colombier 	0x00,				/* controller 0 device number */
143759cc4ca5SDavid du Colombier 	0x1E, 0x00,			/* controller 0 info leaf offset */
143859cc4ca5SDavid du Colombier 	0x00,				/* reserved */
143959cc4ca5SDavid du Colombier 	0x00, 0x08,			/* selected connection type */
144059cc4ca5SDavid du Colombier 	0x00,				/* general purpose control */
144159cc4ca5SDavid du Colombier 	0x01,				/* block count */
144259cc4ca5SDavid du Colombier 
144359cc4ca5SDavid du Colombier 	0x8C,				/* format indicator and count */
144459cc4ca5SDavid du Colombier 	0x01,				/* block type */
144559cc4ca5SDavid du Colombier 	0x00,				/* PHY number */
144659cc4ca5SDavid du Colombier 	0x00,				/* GPR sequence length */
144759cc4ca5SDavid du Colombier 	0x00,				/* reset sequence length */
144859cc4ca5SDavid du Colombier 	0x00, 0x78,			/* media capabilities */
144959cc4ca5SDavid du Colombier 	0xE0, 0x01,			/* Nway advertisment */
145059cc4ca5SDavid du Colombier 	0x00, 0x50,			/* FDX bitmap */
145159cc4ca5SDavid du Colombier 	0x00, 0x18,			/* TTM bitmap */
145259cc4ca5SDavid du Colombier };
145359cc4ca5SDavid du Colombier 
14547dd7cddfSDavid du Colombier static int
srom(Ctlr * ctlr)14557dd7cddfSDavid du Colombier srom(Ctlr* ctlr)
14567dd7cddfSDavid du Colombier {
14577dd7cddfSDavid du Colombier 	int i, k, oui, phy, x;
14587dd7cddfSDavid du Colombier 	uchar *p;
14597dd7cddfSDavid du Colombier 
14607dd7cddfSDavid du Colombier 	/*
14617dd7cddfSDavid du Colombier 	 * This is a partial decoding of the SROM format described in
14627dd7cddfSDavid du Colombier 	 * 'Digital Semiconductor 21X4 Serial ROM Format, Version 4.05,
14637dd7cddfSDavid du Colombier 	 * 2-Mar-98'. Only the 2114[03] are handled, support for other
14647dd7cddfSDavid du Colombier 	 * controllers can be added as needed.
14659a747e4fSDavid du Colombier 	 * Do a dummy read first to get the size and allocate ctlr->srom.
14667dd7cddfSDavid du Colombier 	 */
14679a747e4fSDavid du Colombier 	sromr(ctlr, 0);
14689a747e4fSDavid du Colombier 	if(ctlr->srom == nil)
14699a747e4fSDavid du Colombier 		ctlr->srom = malloc((1<<ctlr->sromsz)*sizeof(ushort));
1470aa72973aSDavid du Colombier 	if(ctlr->srom == nil)
1471aa72973aSDavid du Colombier 		error(Enomem);
14729a747e4fSDavid du Colombier 	for(i = 0; i < (1<<ctlr->sromsz); i++){
14737dd7cddfSDavid du Colombier 		x = sromr(ctlr, i);
14747dd7cddfSDavid du Colombier 		ctlr->srom[2*i] = x;
14757dd7cddfSDavid du Colombier 		ctlr->srom[2*i+1] = x>>8;
14767dd7cddfSDavid du Colombier 	}
14777dd7cddfSDavid du Colombier 
1478f43e6a25SDavid du Colombier 	if(DEBUG){
1479f43e6a25SDavid du Colombier 		print("srom:");
1480f43e6a25SDavid du Colombier 		for(i = 0; i < ((1<<ctlr->sromsz)*sizeof(ushort)); i++){
1481f43e6a25SDavid du Colombier 			if(i && ((i & 0x0F) == 0))
1482f43e6a25SDavid du Colombier 				print("\n     ");
1483f43e6a25SDavid du Colombier 			print(" %2.2uX", ctlr->srom[i]);
1484f43e6a25SDavid du Colombier 		}
1485f43e6a25SDavid du Colombier 		print("\n");
1486f43e6a25SDavid du Colombier 	}
1487f43e6a25SDavid du Colombier 
14887dd7cddfSDavid du Colombier 	/*
1489d9306527SDavid du Colombier 	 * There are at least 2 SROM layouts:
14907dd7cddfSDavid du Colombier 	 *	e.g. Digital EtherWORKS	station address at offset 20;
14917dd7cddfSDavid du Colombier 	 *				this complies with the 21140A SROM
14927dd7cddfSDavid du Colombier 	 *				application note from Digital;
14937dd7cddfSDavid du Colombier 	 * 	e.g. SMC9332		station address at offset 0 followed by
14947dd7cddfSDavid du Colombier 	 *				2 additional bytes, repeated at offset
14957dd7cddfSDavid du Colombier 	 *				6; the 8 bytes are also repeated in
14967dd7cddfSDavid du Colombier 	 *				reverse order at offset 8.
14977dd7cddfSDavid du Colombier 	 * To check which it is, read the SROM and check for the repeating
14987dd7cddfSDavid du Colombier 	 * patterns of the non-compliant cards; if that fails use the one at
14997dd7cddfSDavid du Colombier 	 * offset 20.
15007dd7cddfSDavid du Colombier 	 */
15017dd7cddfSDavid du Colombier 	ctlr->sromea = ctlr->srom;
15027dd7cddfSDavid du Colombier 	for(i = 0; i < 8; i++){
15037dd7cddfSDavid du Colombier 		x = ctlr->srom[i];
15047dd7cddfSDavid du Colombier 		if(x != ctlr->srom[15-i] || x != ctlr->srom[16+i]){
15057dd7cddfSDavid du Colombier 			ctlr->sromea = &ctlr->srom[20];
15067dd7cddfSDavid du Colombier 			break;
15077dd7cddfSDavid du Colombier 		}
15087dd7cddfSDavid du Colombier 	}
15097dd7cddfSDavid du Colombier 
15107dd7cddfSDavid du Colombier 	/*
1511d9306527SDavid du Colombier 	 * Fake up the SROM for the PNIC and AMDtek.
1512d9306527SDavid du Colombier 	 * They look like a 21140 with a PHY.
1513d9306527SDavid du Colombier 	 * The MAC address is byte-swapped in the orginal
1514d9306527SDavid du Colombier 	 * PNIC SROM data.
151559cc4ca5SDavid du Colombier 	 */
151659cc4ca5SDavid du Colombier 	if(ctlr->id == Pnic){
151759cc4ca5SDavid du Colombier 		memmove(&ctlr->srom[20], leafpnic, sizeof(leafpnic));
151859cc4ca5SDavid du Colombier 		for(i = 0; i < Eaddrlen; i += 2){
151959cc4ca5SDavid du Colombier 			ctlr->srom[20+i] = ctlr->srom[i+1];
152059cc4ca5SDavid du Colombier 			ctlr->srom[20+i+1] = ctlr->srom[i];
152159cc4ca5SDavid du Colombier 		}
152259cc4ca5SDavid du Colombier 	}
1523b4b9fc2fSDavid du Colombier 	if(ctlr->id == CentaurP || ctlr->id == CentaurPcb){
1524d9306527SDavid du Colombier 		memmove(&ctlr->srom[20], leafpnic, sizeof(leafpnic));
1525d9306527SDavid du Colombier 		for(i = 0; i < Eaddrlen; i += 2){
1526d9306527SDavid du Colombier 			ctlr->srom[20+i] = ctlr->srom[8+i];
1527d9306527SDavid du Colombier 			ctlr->srom[20+i+1] = ctlr->srom[8+i+1];
1528d9306527SDavid du Colombier 		}
1529d9306527SDavid du Colombier 	}
153059cc4ca5SDavid du Colombier 
153159cc4ca5SDavid du Colombier 	/*
15327dd7cddfSDavid du Colombier 	 * Next, try to find the info leaf in the SROM for media detection.
15337dd7cddfSDavid du Colombier 	 * If it's a non-conforming card try to match the vendor ethernet code
15347dd7cddfSDavid du Colombier 	 * and point p at a fake info leaf with compact 21140 entries.
15357dd7cddfSDavid du Colombier 	 */
15367dd7cddfSDavid du Colombier 	if(ctlr->sromea == ctlr->srom){
15377dd7cddfSDavid du Colombier 		p = nil;
15387dd7cddfSDavid du Colombier 		for(i = 0; leaf21140[i] != nil; i++){
15397dd7cddfSDavid du Colombier 			if(memcmp(leaf21140[i], ctlr->sromea, 3) == 0){
15407dd7cddfSDavid du Colombier 				p = &leaf21140[i][4];
15417dd7cddfSDavid du Colombier 				break;
15427dd7cddfSDavid du Colombier 			}
15437dd7cddfSDavid du Colombier 		}
15447dd7cddfSDavid du Colombier 		if(p == nil)
15457dd7cddfSDavid du Colombier 			return -1;
15467dd7cddfSDavid du Colombier 	}
15477dd7cddfSDavid du Colombier 	else
15487dd7cddfSDavid du Colombier 		p = &ctlr->srom[(ctlr->srom[28]<<8)|ctlr->srom[27]];
15497dd7cddfSDavid du Colombier 
15507dd7cddfSDavid du Colombier 	/*
15517dd7cddfSDavid du Colombier 	 * Set up the info needed for later media detection.
15527dd7cddfSDavid du Colombier 	 * For the 21140, set the general-purpose mask in CSR12.
15537dd7cddfSDavid du Colombier 	 * The info block entries are stored in order of increasing
15547dd7cddfSDavid du Colombier 	 * precedence, so detection will work backwards through the
15557dd7cddfSDavid du Colombier 	 * stored indexes into ctlr->srom.
15567dd7cddfSDavid du Colombier 	 * If an entry is found which matches the selected connection
15577dd7cddfSDavid du Colombier 	 * type, save the index. Otherwise, start at the last entry.
15587dd7cddfSDavid du Colombier 	 * If any MII entries are found (type 1 and 3 blocks), scan
15597dd7cddfSDavid du Colombier 	 * for PHYs.
15607dd7cddfSDavid du Colombier 	 */
15617dd7cddfSDavid du Colombier 	ctlr->leaf = p;
15627dd7cddfSDavid du Colombier 	ctlr->sct = *p++;
15637dd7cddfSDavid du Colombier 	ctlr->sct |= *p++<<8;
1564f43e6a25SDavid du Colombier 	if(ctlr->id != Tulip3 && ctlr->id != Tulip1){
15657dd7cddfSDavid du Colombier 		csr32w(ctlr, 12, Gpc|*p++);
15667dd7cddfSDavid du Colombier 		delay(200);
15677dd7cddfSDavid du Colombier 	}
15687dd7cddfSDavid du Colombier 	ctlr->k = *p++;
15697dd7cddfSDavid du Colombier 	if(ctlr->k >= nelem(ctlr->infoblock))
15707dd7cddfSDavid du Colombier 		ctlr->k = nelem(ctlr->infoblock)-1;
15717dd7cddfSDavid du Colombier 	ctlr->sctk = ctlr->k-1;
15727dd7cddfSDavid du Colombier 	phy = 0;
15737dd7cddfSDavid du Colombier 	for(k = 0; k < ctlr->k; k++){
15747dd7cddfSDavid du Colombier 		ctlr->infoblock[k] = p;
1575f43e6a25SDavid du Colombier 		if(ctlr->id == Tulip1){
1576f43e6a25SDavid du Colombier 			debug("type21041: 0x%2.2uX\n", p[0]);
1577f43e6a25SDavid du Colombier 			if(ctlr->sct != 0x0800 && *p == (ctlr->sct & 0xFF))
1578f43e6a25SDavid du Colombier 				ctlr->sctk = k;
1579f43e6a25SDavid du Colombier 			if(*p & 0x40)
1580f43e6a25SDavid du Colombier 				p += 7;
1581f43e6a25SDavid du Colombier 			else
1582f43e6a25SDavid du Colombier 				p += 1;
1583f43e6a25SDavid du Colombier 		}
15847dd7cddfSDavid du Colombier 		/*
15857dd7cddfSDavid du Colombier 		 * The RAMIX PMC665 has a badly-coded SROM,
15867dd7cddfSDavid du Colombier 		 * hence the test for 21143 and type 3.
15877dd7cddfSDavid du Colombier 		 */
1588f43e6a25SDavid du Colombier 		else if((*p & 0x80) || (ctlr->id == Tulip3 && *(p+1) == 3)){
15897dd7cddfSDavid du Colombier 			*p |= 0x80;
15907dd7cddfSDavid du Colombier 			if(*(p+1) == 1 || *(p+1) == 3)
15917dd7cddfSDavid du Colombier 				phy = 1;
15927dd7cddfSDavid du Colombier 			if(*(p+1) == 5)
15937dd7cddfSDavid du Colombier 				ctlr->type5block = p;
15947dd7cddfSDavid du Colombier 			p += (*p & ~0x80)+1;
15957dd7cddfSDavid du Colombier 		}
15967dd7cddfSDavid du Colombier 		else{
15977dd7cddfSDavid du Colombier 			debug("type0: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n",
15987dd7cddfSDavid du Colombier 				p[0], p[1], p[2], p[3]);
15997dd7cddfSDavid du Colombier 			if(ctlr->sct != 0x0800 && *p == (ctlr->sct & 0xFF))
16007dd7cddfSDavid du Colombier 				ctlr->sctk = k;
16017dd7cddfSDavid du Colombier 			p += 4;
16027dd7cddfSDavid du Colombier 		}
16037dd7cddfSDavid du Colombier 	}
16047dd7cddfSDavid du Colombier 	ctlr->curk = ctlr->sctk;
16057dd7cddfSDavid du Colombier 	debug("sct 0x%uX medium 0x%uX k %d curk %d phy %d\n",
16067dd7cddfSDavid du Colombier 		ctlr->sct, ctlr->medium, ctlr->k, ctlr->curk, phy);
16077dd7cddfSDavid du Colombier 
16087dd7cddfSDavid du Colombier 	if(phy){
16097dd7cddfSDavid du Colombier 		x = 0;
16107dd7cddfSDavid du Colombier 		for(k = 0; k < nelem(ctlr->phy); k++){
1611b4b9fc2fSDavid du Colombier 			if((ctlr->id == CentaurP || ctlr->id == CentaurPcb) && k != 1)
1612d9306527SDavid du Colombier 				continue;
16137dd7cddfSDavid du Colombier 			if((oui = miir(ctlr, k, 2)) == -1 || oui == 0)
16147dd7cddfSDavid du Colombier 				continue;
1615d9306527SDavid du Colombier 			debug("phy reg 2 %4.4uX\n", oui);
16167dd7cddfSDavid du Colombier 			if(DEBUG){
16177dd7cddfSDavid du Colombier 				oui = (oui & 0x3FF)<<6;
16187dd7cddfSDavid du Colombier 				oui |= miir(ctlr, k, 3)>>10;
16197dd7cddfSDavid du Colombier 				miir(ctlr, k, 1);
16207dd7cddfSDavid du Colombier 				debug("phy%d: index %d oui %uX reg1 %uX\n",
16217dd7cddfSDavid du Colombier 					x, k, oui, miir(ctlr, k, 1));
16227dd7cddfSDavid du Colombier 				USED(oui);
16237dd7cddfSDavid du Colombier 			}
16247dd7cddfSDavid du Colombier 			ctlr->phy[x] = k;
16257dd7cddfSDavid du Colombier 		}
16267dd7cddfSDavid du Colombier 	}
16277dd7cddfSDavid du Colombier 
16287dd7cddfSDavid du Colombier 	ctlr->fd = 0;
16297dd7cddfSDavid du Colombier 	ctlr->medium = -1;
16307dd7cddfSDavid du Colombier 
16317dd7cddfSDavid du Colombier 	return 0;
16327dd7cddfSDavid du Colombier }
16337dd7cddfSDavid du Colombier 
16347dd7cddfSDavid du Colombier static void
dec2114xpci(void)16357dd7cddfSDavid du Colombier dec2114xpci(void)
16367dd7cddfSDavid du Colombier {
16377dd7cddfSDavid du Colombier 	Ctlr *ctlr;
16387dd7cddfSDavid du Colombier 	Pcidev *p;
16397dd7cddfSDavid du Colombier 	int x;
16407dd7cddfSDavid du Colombier 
16417dd7cddfSDavid du Colombier 	p = nil;
164259cc4ca5SDavid du Colombier 	while(p = pcimatch(p, 0, 0)){
164359cc4ca5SDavid du Colombier 		if(p->ccrb != 0x02 || p->ccru != 0)
164459cc4ca5SDavid du Colombier 			continue;
164559cc4ca5SDavid du Colombier 		switch((p->did<<16)|p->vid){
16467dd7cddfSDavid du Colombier 		default:
16477dd7cddfSDavid du Colombier 			continue;
16487dd7cddfSDavid du Colombier 
164959cc4ca5SDavid du Colombier 		case Tulip3:			/* 21143 */
16507dd7cddfSDavid du Colombier 			/*
16517dd7cddfSDavid du Colombier 			 * Exit sleep mode.
16527dd7cddfSDavid du Colombier 			 */
16537dd7cddfSDavid du Colombier 			x = pcicfgr32(p, 0x40);
1654f43e6a25SDavid du Colombier 			x &= ~0xC0000000;
16557dd7cddfSDavid du Colombier 			pcicfgw32(p, 0x40, x);
16567dd7cddfSDavid du Colombier 			/*FALLTHROUGH*/
16577dd7cddfSDavid du Colombier 
1658d9306527SDavid du Colombier 		case Tulip0:			/* 21140 */
1659f43e6a25SDavid du Colombier 		case Tulip1:			/* 21041 */
1660c957ad6aSDavid du Colombier 		case Pnic:			/* PNIC */
166159cc4ca5SDavid du Colombier 		case Pnic2:			/* PNIC-II */
1662d9306527SDavid du Colombier 		case CentaurP:			/* ADMtek */
1663b4b9fc2fSDavid du Colombier 		case CentaurPcb:		/* ADMtek CardBus */
16647dd7cddfSDavid du Colombier 			break;
16657dd7cddfSDavid du Colombier 		}
16667dd7cddfSDavid du Colombier 
16677dd7cddfSDavid du Colombier 		/*
16687dd7cddfSDavid du Colombier 		 * bar[0] is the I/O port register address and
16697dd7cddfSDavid du Colombier 		 * bar[1] is the memory-mapped register address.
16707dd7cddfSDavid du Colombier 		 */
16717dd7cddfSDavid du Colombier 		ctlr = malloc(sizeof(Ctlr));
1672aa72973aSDavid du Colombier 		if(ctlr == nil)
1673aa72973aSDavid du Colombier 			error(Enomem);
16747dd7cddfSDavid du Colombier 		ctlr->port = p->mem[0].bar & ~0x01;
16757dd7cddfSDavid du Colombier 		ctlr->pcidev = p;
167659cc4ca5SDavid du Colombier 		ctlr->id = (p->did<<16)|p->vid;
16777dd7cddfSDavid du Colombier 
16787dd7cddfSDavid du Colombier 		if(ioalloc(ctlr->port, p->mem[0].size, 0, "dec2114x") < 0){
167959cc4ca5SDavid du Colombier 			print("dec2114x: port 0x%uX in use\n", ctlr->port);
16807dd7cddfSDavid du Colombier 			free(ctlr);
16817dd7cddfSDavid du Colombier 			continue;
16827dd7cddfSDavid du Colombier 		}
16837dd7cddfSDavid du Colombier 
16847dd7cddfSDavid du Colombier 		/*
16857dd7cddfSDavid du Colombier 		 * Some cards (e.g. ANA-6910FX) seem to need the Ps bit
16867dd7cddfSDavid du Colombier 		 * set or they don't always work right after a hardware
16877dd7cddfSDavid du Colombier 		 * reset.
16887dd7cddfSDavid du Colombier 		 */
16897dd7cddfSDavid du Colombier 		csr32w(ctlr, 6, Mbo|Ps);
16907dd7cddfSDavid du Colombier 		softreset(ctlr);
16917dd7cddfSDavid du Colombier 
16927dd7cddfSDavid du Colombier 		if(srom(ctlr)){
169359cc4ca5SDavid du Colombier 			iofree(ctlr->port);
16947dd7cddfSDavid du Colombier 			free(ctlr);
169559cc4ca5SDavid du Colombier 			continue;
169659cc4ca5SDavid du Colombier 		}
169759cc4ca5SDavid du Colombier 
169859cc4ca5SDavid du Colombier 		switch(ctlr->id){
169959cc4ca5SDavid du Colombier 		default:
170059cc4ca5SDavid du Colombier 			break;
170159cc4ca5SDavid du Colombier 		case Pnic:			/* PNIC */
170259cc4ca5SDavid du Colombier 			/*
170359cc4ca5SDavid du Colombier 			 * Turn off the jabber timer.
170459cc4ca5SDavid du Colombier 			 */
170559cc4ca5SDavid du Colombier 			csr32w(ctlr, 15, 0x00000001);
17067dd7cddfSDavid du Colombier 			break;
1707d9306527SDavid du Colombier 		case CentaurP:
1708b4b9fc2fSDavid du Colombier 		case CentaurPcb:
1709d9306527SDavid du Colombier 			/*
1710d9306527SDavid du Colombier 			 * Nice - the register offsets change from *8 to *4
1711d9306527SDavid du Colombier 			 * for CSR16 and up...
1712d9306527SDavid du Colombier 			 * CSR25/26 give the MAC address read from the SROM.
1713d9306527SDavid du Colombier 			 * Don't really need to use this other than as a check,
1714d9306527SDavid du Colombier 			 * the SROM will be read in anyway so the value there
1715d9306527SDavid du Colombier 			 * can be used directly.
1716d9306527SDavid du Colombier 			 */
1717d9306527SDavid du Colombier 			debug("csr25 %8.8luX csr26 %8.8luX\n",
1718d9306527SDavid du Colombier 				inl(ctlr->port+0xA4), inl(ctlr->port+0xA8));
1719d9306527SDavid du Colombier 			debug("phyidr1 %4.4luX phyidr2 %4.4luX\n",
1720d9306527SDavid du Colombier 				inl(ctlr->port+0xBC), inl(ctlr->port+0xC0));
1721d9306527SDavid du Colombier 			break;
17227dd7cddfSDavid du Colombier 		}
17237dd7cddfSDavid du Colombier 
17247dd7cddfSDavid du Colombier 		if(ctlrhead != nil)
17257dd7cddfSDavid du Colombier 			ctlrtail->next = ctlr;
17267dd7cddfSDavid du Colombier 		else
17277dd7cddfSDavid du Colombier 			ctlrhead = ctlr;
17287dd7cddfSDavid du Colombier 		ctlrtail = ctlr;
17297dd7cddfSDavid du Colombier 	}
17307dd7cddfSDavid du Colombier }
17317dd7cddfSDavid du Colombier 
17327dd7cddfSDavid du Colombier static int
reset(Ether * ether)17337dd7cddfSDavid du Colombier reset(Ether* ether)
17347dd7cddfSDavid du Colombier {
17357dd7cddfSDavid du Colombier 	Ctlr *ctlr;
17367dd7cddfSDavid du Colombier 	int i, x;
17377dd7cddfSDavid du Colombier 	uchar ea[Eaddrlen];
17387dd7cddfSDavid du Colombier 	static int scandone;
17397dd7cddfSDavid du Colombier 
17407dd7cddfSDavid du Colombier 	if(scandone == 0){
17417dd7cddfSDavid du Colombier 		dec2114xpci();
17427dd7cddfSDavid du Colombier 		scandone = 1;
17437dd7cddfSDavid du Colombier 	}
17447dd7cddfSDavid du Colombier 
17457dd7cddfSDavid du Colombier 	/*
17467dd7cddfSDavid du Colombier 	 * Any adapter matches if no ether->port is supplied,
17477dd7cddfSDavid du Colombier 	 * otherwise the ports must match.
17487dd7cddfSDavid du Colombier 	 */
17497dd7cddfSDavid du Colombier 	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
17507dd7cddfSDavid du Colombier 		if(ctlr->active)
17517dd7cddfSDavid du Colombier 			continue;
17527dd7cddfSDavid du Colombier 		if(ether->port == 0 || ether->port == ctlr->port){
17537dd7cddfSDavid du Colombier 			ctlr->active = 1;
17547dd7cddfSDavid du Colombier 			break;
17557dd7cddfSDavid du Colombier 		}
17567dd7cddfSDavid du Colombier 	}
17577dd7cddfSDavid du Colombier 	if(ctlr == nil)
17587dd7cddfSDavid du Colombier 		return -1;
17597dd7cddfSDavid du Colombier 
17607dd7cddfSDavid du Colombier 	ether->ctlr = ctlr;
17617dd7cddfSDavid du Colombier 	ether->port = ctlr->port;
17627dd7cddfSDavid du Colombier 	ether->irq = ctlr->pcidev->intl;
17637dd7cddfSDavid du Colombier 	ether->tbdf = ctlr->pcidev->tbdf;
17647dd7cddfSDavid du Colombier 
17657dd7cddfSDavid du Colombier 	/*
17667dd7cddfSDavid du Colombier 	 * Check if the adapter's station address is to be overridden.
17677dd7cddfSDavid du Colombier 	 * If not, read it from the EEPROM and set in ether->ea prior to
17687dd7cddfSDavid du Colombier 	 * loading the station address in the hardware.
17697dd7cddfSDavid du Colombier 	 */
17707dd7cddfSDavid du Colombier 	memset(ea, 0, Eaddrlen);
17717dd7cddfSDavid du Colombier 	if(memcmp(ea, ether->ea, Eaddrlen) == 0)
17727dd7cddfSDavid du Colombier 		memmove(ether->ea, ctlr->sromea, Eaddrlen);
17737dd7cddfSDavid du Colombier 
17747dd7cddfSDavid du Colombier 	/*
17757dd7cddfSDavid du Colombier 	 * Look for a medium override in case there's no autonegotiation
17767dd7cddfSDavid du Colombier 	 * (no MII) or the autonegotiation fails.
17777dd7cddfSDavid du Colombier 	 */
17787dd7cddfSDavid du Colombier 	for(i = 0; i < ether->nopt; i++){
17797dd7cddfSDavid du Colombier 		if(cistrcmp(ether->opt[i], "FD") == 0){
17807dd7cddfSDavid du Colombier 			ctlr->fd = 1;
17817dd7cddfSDavid du Colombier 			continue;
17827dd7cddfSDavid du Colombier 		}
17837dd7cddfSDavid du Colombier 		for(x = 0; x < nelem(mediatable); x++){
17847dd7cddfSDavid du Colombier 			debug("compare <%s> <%s>\n", mediatable[x],
17857dd7cddfSDavid du Colombier 				ether->opt[i]);
17867dd7cddfSDavid du Colombier 			if(cistrcmp(mediatable[x], ether->opt[i]))
17877dd7cddfSDavid du Colombier 				continue;
17887dd7cddfSDavid du Colombier 			ctlr->medium = x;
17897dd7cddfSDavid du Colombier 
17907dd7cddfSDavid du Colombier 			switch(ctlr->medium){
17917dd7cddfSDavid du Colombier 			default:
17927dd7cddfSDavid du Colombier 				ctlr->fd = 0;
17937dd7cddfSDavid du Colombier 				break;
17947dd7cddfSDavid du Colombier 
17957dd7cddfSDavid du Colombier 			case 0x04:		/* 10BASE-TFD */
17967dd7cddfSDavid du Colombier 			case 0x05:		/* 100BASE-TXFD */
17977dd7cddfSDavid du Colombier 			case 0x08:		/* 100BASE-FXFD */
17987dd7cddfSDavid du Colombier 				ctlr->fd = 1;
17997dd7cddfSDavid du Colombier 				break;
18007dd7cddfSDavid du Colombier 			}
18017dd7cddfSDavid du Colombier 			break;
18027dd7cddfSDavid du Colombier 		}
18037dd7cddfSDavid du Colombier 	}
18047dd7cddfSDavid du Colombier 
18057dd7cddfSDavid du Colombier 	ether->mbps = media(ether, 1);
18067dd7cddfSDavid du Colombier 
18077dd7cddfSDavid du Colombier 	/*
18087dd7cddfSDavid du Colombier 	 * Initialise descriptor rings, ethernet address.
18097dd7cddfSDavid du Colombier 	 */
18107dd7cddfSDavid du Colombier 	ctlr->nrdr = Nrde;
18117dd7cddfSDavid du Colombier 	ctlr->ntdr = Ntde;
18127dd7cddfSDavid du Colombier 	pcisetbme(ctlr->pcidev);
18137dd7cddfSDavid du Colombier 	ctlrinit(ether);
18147dd7cddfSDavid du Colombier 
18157dd7cddfSDavid du Colombier 	/*
18167dd7cddfSDavid du Colombier 	 * Linkage to the generic ethernet driver.
18177dd7cddfSDavid du Colombier 	 */
18187dd7cddfSDavid du Colombier 	ether->attach = attach;
18197dd7cddfSDavid du Colombier 	ether->transmit = transmit;
18207dd7cddfSDavid du Colombier 	ether->interrupt = interrupt;
18217dd7cddfSDavid du Colombier 	ether->ifstat = ifstat;
18227dd7cddfSDavid du Colombier 
18237dd7cddfSDavid du Colombier 	ether->arg = ether;
18240809e9a7SDavid du Colombier 	ether->shutdown = shutdown;
18250809e9a7SDavid du Colombier 	ether->multicast = multicast;
18267dd7cddfSDavid du Colombier 	ether->promiscuous = promiscuous;
18277dd7cddfSDavid du Colombier 
18287dd7cddfSDavid du Colombier 	return 0;
18297dd7cddfSDavid du Colombier }
18307dd7cddfSDavid du Colombier 
18317dd7cddfSDavid du Colombier void
ether2114xlink(void)18327dd7cddfSDavid du Colombier ether2114xlink(void)
18337dd7cddfSDavid du Colombier {
18347dd7cddfSDavid du Colombier 	addethercard("2114x",  reset);
1835b4b9fc2fSDavid du Colombier 	addethercard("21140",  reset);
18367dd7cddfSDavid du Colombier }
1837