xref: /plan9-contrib/sys/src/9/pc/etherelnk3.c (revision aa72973a2891ccbd3fb042462446761159389e19)
17dd7cddfSDavid du Colombier /*
27dd7cddfSDavid du Colombier  * Etherlink III, Fast EtherLink and Fast EtherLink XL adapters.
37dd7cddfSDavid du Colombier  * To do:
47dd7cddfSDavid du Colombier  *	check robustness in the face of errors (e.g. busmaster & rxUnderrun);
57dd7cddfSDavid du Colombier  *	RxEarly and busmaster;
67dd7cddfSDavid du Colombier  *	autoSelect;
77dd7cddfSDavid du Colombier  *	PCI latency timer and master enable;
87dd7cddfSDavid du Colombier  *	errata list;
99a747e4fSDavid du Colombier  *	rewrite all initialisation.
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 
227dd7cddfSDavid du Colombier #define XCVRDEBUG		if(0)print
237dd7cddfSDavid du Colombier 
247dd7cddfSDavid du Colombier enum {
257dd7cddfSDavid du Colombier 	IDport			= 0x0110,	/* anywhere between 0x0100 and 0x01F0 */
267dd7cddfSDavid du Colombier };
277dd7cddfSDavid du Colombier 
287dd7cddfSDavid du Colombier enum {						/* all windows */
297dd7cddfSDavid du Colombier 	CommandR		= 0x000E,
307dd7cddfSDavid du Colombier 	IntStatusR		= 0x000E,
317dd7cddfSDavid du Colombier };
327dd7cddfSDavid du Colombier 
337dd7cddfSDavid du Colombier enum {						/* Commands */
347dd7cddfSDavid du Colombier 	GlobalReset		= 0x0000,
357dd7cddfSDavid du Colombier 	SelectRegisterWindow	= 0x0001,
367dd7cddfSDavid du Colombier 	EnableDcConverter	= 0x0002,
377dd7cddfSDavid du Colombier 	RxDisable		= 0x0003,
387dd7cddfSDavid du Colombier 	RxEnable		= 0x0004,
397dd7cddfSDavid du Colombier 	RxReset			= 0x0005,
407dd7cddfSDavid du Colombier 	Stall			= 0x0006,	/* 3C90x */
417dd7cddfSDavid du Colombier 	TxDone			= 0x0007,
427dd7cddfSDavid du Colombier 	RxDiscard		= 0x0008,
437dd7cddfSDavid du Colombier 	TxEnable		= 0x0009,
447dd7cddfSDavid du Colombier 	TxDisable		= 0x000A,
457dd7cddfSDavid du Colombier 	TxReset			= 0x000B,
467dd7cddfSDavid du Colombier 	RequestInterrupt	= 0x000C,
477dd7cddfSDavid du Colombier 	AcknowledgeInterrupt	= 0x000D,
487dd7cddfSDavid du Colombier 	SetInterruptEnable	= 0x000E,
497dd7cddfSDavid du Colombier 	SetIndicationEnable	= 0x000F,	/* SetReadZeroMask */
507dd7cddfSDavid du Colombier 	SetRxFilter		= 0x0010,
517dd7cddfSDavid du Colombier 	SetRxEarlyThresh	= 0x0011,
527dd7cddfSDavid du Colombier 	SetTxAvailableThresh	= 0x0012,
537dd7cddfSDavid du Colombier 	SetTxStartThresh	= 0x0013,
547dd7cddfSDavid du Colombier 	StartDma		= 0x0014,	/* initiate busmaster operation */
557dd7cddfSDavid du Colombier 	StatisticsEnable	= 0x0015,
567dd7cddfSDavid du Colombier 	StatisticsDisable	= 0x0016,
577dd7cddfSDavid du Colombier 	DisableDcConverter	= 0x0017,
587dd7cddfSDavid du Colombier 	SetTxReclaimThresh	= 0x0018,	/* PIO-only adapters */
597dd7cddfSDavid du Colombier 	PowerUp			= 0x001B,	/* not all adapters */
607dd7cddfSDavid du Colombier 	PowerDownFull		= 0x001C,	/* not all adapters */
617dd7cddfSDavid du Colombier 	PowerAuto		= 0x001D,	/* not all adapters */
627dd7cddfSDavid du Colombier };
637dd7cddfSDavid du Colombier 
647dd7cddfSDavid du Colombier enum {						/* (Global|Rx|Tx)Reset command bits */
657dd7cddfSDavid du Colombier 	tpAuiReset		= 0x0001,	/* 10BaseT and AUI transceivers */
667dd7cddfSDavid du Colombier 	endecReset		= 0x0002,	/* internal Ethernet encoder/decoder */
677dd7cddfSDavid du Colombier 	networkReset		= 0x0004,	/* network interface logic */
687dd7cddfSDavid du Colombier 	fifoReset		= 0x0008,	/* FIFO control logic */
697dd7cddfSDavid du Colombier 	aismReset		= 0x0010,	/* autoinitialise state-machine logic */
707dd7cddfSDavid du Colombier 	hostReset		= 0x0020,	/* bus interface logic */
717dd7cddfSDavid du Colombier 	dmaReset		= 0x0040,	/* bus master logic */
727dd7cddfSDavid du Colombier 	vcoReset		= 0x0080,	/* on-board 10Mbps VCO */
737dd7cddfSDavid du Colombier 	updnReset		= 0x0100,	/* upload/download (Rx/TX) logic */
747dd7cddfSDavid du Colombier 
757dd7cddfSDavid du Colombier 	resetMask		= 0x01FF,
767dd7cddfSDavid du Colombier };
777dd7cddfSDavid du Colombier 
787dd7cddfSDavid du Colombier enum {						/* Stall command bits */
797dd7cddfSDavid du Colombier 	upStall			= 0x0000,
807dd7cddfSDavid du Colombier 	upUnStall		= 0x0001,
817dd7cddfSDavid du Colombier 	dnStall			= 0x0002,
827dd7cddfSDavid du Colombier 	dnUnStall		= 0x0003,
837dd7cddfSDavid du Colombier };
847dd7cddfSDavid du Colombier 
857dd7cddfSDavid du Colombier enum {						/* SetRxFilter command bits */
867dd7cddfSDavid du Colombier 	receiveIndividual	= 0x0001,	/* match station address */
877dd7cddfSDavid du Colombier 	receiveMulticast	= 0x0002,
887dd7cddfSDavid du Colombier 	receiveBroadcast	= 0x0004,
897dd7cddfSDavid du Colombier 	receiveAllFrames	= 0x0008,	/* promiscuous */
907dd7cddfSDavid du Colombier };
917dd7cddfSDavid du Colombier 
927dd7cddfSDavid du Colombier enum {						/* StartDma command bits */
937dd7cddfSDavid du Colombier 	Upload			= 0x0000,	/* transfer data from adapter to memory */
947dd7cddfSDavid du Colombier 	Download		= 0x0001,	/* transfer data from memory to adapter */
957dd7cddfSDavid du Colombier };
967dd7cddfSDavid du Colombier 
977dd7cddfSDavid du Colombier enum {						/* IntStatus bits */
987dd7cddfSDavid du Colombier 	interruptLatch		= 0x0001,
997dd7cddfSDavid du Colombier 	hostError		= 0x0002,	/* Adapter Failure */
1007dd7cddfSDavid du Colombier 	txComplete		= 0x0004,
1017dd7cddfSDavid du Colombier 	txAvailable		= 0x0008,
1027dd7cddfSDavid du Colombier 	rxComplete		= 0x0010,
1037dd7cddfSDavid du Colombier 	rxEarly			= 0x0020,
1047dd7cddfSDavid du Colombier 	intRequested		= 0x0040,
1057dd7cddfSDavid du Colombier 	updateStats		= 0x0080,
1067dd7cddfSDavid du Colombier 	transferInt		= 0x0100,	/* Bus Master Transfer Complete */
1077dd7cddfSDavid du Colombier 	dnComplete		= 0x0200,
1087dd7cddfSDavid du Colombier 	upComplete		= 0x0400,
1097dd7cddfSDavid du Colombier 	busMasterInProgress	= 0x0800,
1107dd7cddfSDavid du Colombier 	commandInProgress	= 0x1000,
1117dd7cddfSDavid du Colombier 
1127dd7cddfSDavid du Colombier 	interruptMask		= 0x07FE,
1137dd7cddfSDavid du Colombier };
1147dd7cddfSDavid du Colombier 
1157dd7cddfSDavid du Colombier #define COMMAND(port, cmd, a)	outs((port)+CommandR, ((cmd)<<11)|(a))
1167dd7cddfSDavid du Colombier #define STATUS(port)		ins((port)+IntStatusR)
1177dd7cddfSDavid du Colombier 
1187dd7cddfSDavid du Colombier enum {						/* Window 0 - setup */
1197dd7cddfSDavid du Colombier 	Wsetup			= 0x0000,
1207dd7cddfSDavid du Colombier 						/* registers */
1217dd7cddfSDavid du Colombier 	ManufacturerID		= 0x0000,	/* 3C5[08]*, 3C59[27] */
1227dd7cddfSDavid du Colombier 	ProductID		= 0x0002,	/* 3C5[08]*, 3C59[27] */
1237dd7cddfSDavid du Colombier 	ConfigControl		= 0x0004,	/* 3C5[08]*, 3C59[27] */
1247dd7cddfSDavid du Colombier 	AddressConfig		= 0x0006,	/* 3C5[08]*, 3C59[27] */
1257dd7cddfSDavid du Colombier 	ResourceConfig		= 0x0008,	/* 3C5[08]*, 3C59[27] */
1267dd7cddfSDavid du Colombier 	EepromCommand		= 0x000A,
1277dd7cddfSDavid du Colombier 	EepromData		= 0x000C,
1287dd7cddfSDavid du Colombier 						/* AddressConfig Bits */
1297dd7cddfSDavid du Colombier 	autoSelect9		= 0x0080,
1307dd7cddfSDavid du Colombier 	xcvrMask9		= 0xC000,
1317dd7cddfSDavid du Colombier 						/* ConfigControl bits */
1327dd7cddfSDavid du Colombier 	Ena			= 0x0001,
1337dd7cddfSDavid du Colombier 	base10TAvailable9	= 0x0200,
1347dd7cddfSDavid du Colombier 	coaxAvailable9		= 0x1000,
1357dd7cddfSDavid du Colombier 	auiAvailable9		= 0x2000,
1367dd7cddfSDavid du Colombier 						/* EepromCommand bits */
1377dd7cddfSDavid du Colombier 	EepromReadRegister	= 0x0080,
138e31d734fSDavid du Colombier 	EepromReadOffRegister	= 0x00B0,
1399a747e4fSDavid du Colombier 	EepromRead8bRegister	= 0x0230,
1407dd7cddfSDavid du Colombier 	EepromBusy		= 0x8000,
1417dd7cddfSDavid du Colombier };
1427dd7cddfSDavid du Colombier 
1437dd7cddfSDavid du Colombier #define EEPROMCMD(port, cmd, a)	outs((port)+EepromCommand, (cmd)|(a))
1447dd7cddfSDavid du Colombier #define EEPROMBUSY(port)	(ins((port)+EepromCommand) & EepromBusy)
1457dd7cddfSDavid du Colombier #define EEPROMDATA(port)	ins((port)+EepromData)
1467dd7cddfSDavid du Colombier 
1477dd7cddfSDavid du Colombier enum {						/* Window 1 - operating set */
1487dd7cddfSDavid du Colombier 	Wop			= 0x0001,
1497dd7cddfSDavid du Colombier 						/* registers */
1507dd7cddfSDavid du Colombier 	Fifo			= 0x0000,
1517dd7cddfSDavid du Colombier 	RxError			= 0x0004,	/* 3C59[0257] only */
1527dd7cddfSDavid du Colombier 	RxStatus		= 0x0008,
1533ff48bf5SDavid du Colombier 	TIMER			= 0x000A,
1547dd7cddfSDavid du Colombier 	TxStatus		= 0x000B,
1557dd7cddfSDavid du Colombier 	TxFree			= 0x000C,
1567dd7cddfSDavid du Colombier 						/* RxError bits */
1577dd7cddfSDavid du Colombier 	rxOverrun		= 0x0001,
1587dd7cddfSDavid du Colombier 	runtFrame		= 0x0002,
1597dd7cddfSDavid du Colombier 	alignmentError		= 0x0004,	/* Framing */
1607dd7cddfSDavid du Colombier 	crcError		= 0x0008,
1617dd7cddfSDavid du Colombier 	oversizedFrame		= 0x0010,
1627dd7cddfSDavid du Colombier 	dribbleBits		= 0x0080,
1637dd7cddfSDavid du Colombier 						/* RxStatus bits */
1647dd7cddfSDavid du Colombier 	rxBytes			= 0x1FFF,	/* 3C59[0257] mask */
1657dd7cddfSDavid du Colombier 	rxBytes9		= 0x07FF,	/* 3C5[078]9 mask */
1667dd7cddfSDavid du Colombier 	rxError9		= 0x3800,	/* 3C5[078]9 error mask */
1677dd7cddfSDavid du Colombier 	rxOverrun9		= 0x0000,
1687dd7cddfSDavid du Colombier 	oversizedFrame9		= 0x0800,
1697dd7cddfSDavid du Colombier 	dribbleBits9		= 0x1000,
1707dd7cddfSDavid du Colombier 	runtFrame9		= 0x1800,
1717dd7cddfSDavid du Colombier 	alignmentError9		= 0x2000,	/* Framing */
1727dd7cddfSDavid du Colombier 	crcError9		= 0x2800,
1737dd7cddfSDavid du Colombier 	rxError			= 0x4000,
1747dd7cddfSDavid du Colombier 	rxIncomplete		= 0x8000,
1757dd7cddfSDavid du Colombier 						/* TxStatus Bits */
1767dd7cddfSDavid du Colombier 	txStatusOverflow	= 0x0004,
1777dd7cddfSDavid du Colombier 	maxCollisions		= 0x0008,
1787dd7cddfSDavid du Colombier 	txUnderrun		= 0x0010,
1797dd7cddfSDavid du Colombier 	txJabber		= 0x0020,
1807dd7cddfSDavid du Colombier 	interruptRequested	= 0x0040,
1817dd7cddfSDavid du Colombier 	txStatusComplete	= 0x0080,
1827dd7cddfSDavid du Colombier };
1837dd7cddfSDavid du Colombier 
1847dd7cddfSDavid du Colombier enum {						/* Window 2 - station address */
1857dd7cddfSDavid du Colombier 	Wstation		= 0x0002,
1867dd7cddfSDavid du Colombier 
1877dd7cddfSDavid du Colombier 	ResetOp905B		= 0x000C,
1887dd7cddfSDavid du Colombier };
1897dd7cddfSDavid du Colombier 
1907dd7cddfSDavid du Colombier enum {						/* Window 3 - FIFO management */
1917dd7cddfSDavid du Colombier 	Wfifo			= 0x0003,
1927dd7cddfSDavid du Colombier 						/* registers */
1937dd7cddfSDavid du Colombier 	InternalConfig		= 0x0000,	/* 3C509B, 3C589, 3C59[0257] */
1947dd7cddfSDavid du Colombier 	OtherInt		= 0x0004,	/* 3C59[0257] */
1957dd7cddfSDavid du Colombier 	RomControl		= 0x0006,	/* 3C509B, 3C59[27] */
1967dd7cddfSDavid du Colombier 	MacControl		= 0x0006,	/* 3C59[0257] */
1977dd7cddfSDavid du Colombier 	ResetOptions		= 0x0008,	/* 3C59[0257] */
1987dd7cddfSDavid du Colombier 	MediaOptions		= 0x0008,	/* 3C905B */
1997dd7cddfSDavid du Colombier 	RxFree			= 0x000A,
2007dd7cddfSDavid du Colombier 						/* InternalConfig bits */
2017dd7cddfSDavid du Colombier 	disableBadSsdDetect	= 0x00000100,
2027dd7cddfSDavid du Colombier 	ramLocation		= 0x00000200,	/* 0 external, 1 internal */
2037dd7cddfSDavid du Colombier 	ramPartition5to3	= 0x00000000,
2047dd7cddfSDavid du Colombier 	ramPartition3to1	= 0x00010000,
2057dd7cddfSDavid du Colombier 	ramPartition1to1	= 0x00020000,
2067dd7cddfSDavid du Colombier 	ramPartition3to5	= 0x00030000,
2077dd7cddfSDavid du Colombier 	ramPartitionMask	= 0x00030000,
2087dd7cddfSDavid du Colombier 	xcvr10BaseT		= 0x00000000,
2097dd7cddfSDavid du Colombier 	xcvrAui			= 0x00100000,	/* 10BASE5 */
2107dd7cddfSDavid du Colombier 	xcvr10Base2		= 0x00300000,
2117dd7cddfSDavid du Colombier 	xcvr100BaseTX		= 0x00400000,
2127dd7cddfSDavid du Colombier 	xcvr100BaseFX		= 0x00500000,
2137dd7cddfSDavid du Colombier 	xcvrMii			= 0x00600000,
2147dd7cddfSDavid du Colombier 	xcvrMask		= 0x00700000,
2157dd7cddfSDavid du Colombier 	autoSelect		= 0x01000000,
2167dd7cddfSDavid du Colombier 						/* MacControl bits */
2177dd7cddfSDavid du Colombier 	deferExtendEnable	= 0x0001,
2183ff48bf5SDavid du Colombier 	deferTIMERSelect	= 0x001E,	/* mask */
2197dd7cddfSDavid du Colombier 	fullDuplexEnable	= 0x0020,
2207dd7cddfSDavid du Colombier 	allowLargePackets	= 0x0040,
2217dd7cddfSDavid du Colombier 	extendAfterCollision	= 0x0080,	/* 3C90xB */
2227dd7cddfSDavid du Colombier 	flowControlEnable	= 0x0100,	/* 3C90xB */
2237dd7cddfSDavid du Colombier 	vltEnable		= 0x0200,	/* 3C90xB */
2247dd7cddfSDavid du Colombier 						/* ResetOptions bits */
2257dd7cddfSDavid du Colombier 	baseT4Available		= 0x0001,
2267dd7cddfSDavid du Colombier 	baseTXAvailable		= 0x0002,
2277dd7cddfSDavid du Colombier 	baseFXAvailable		= 0x0004,
2287dd7cddfSDavid du Colombier 	base10TAvailable	= 0x0008,
2297dd7cddfSDavid du Colombier 	coaxAvailable		= 0x0010,
2307dd7cddfSDavid du Colombier 	auiAvailable		= 0x0020,
2317dd7cddfSDavid du Colombier 	miiConnector		= 0x0040,
2327dd7cddfSDavid du Colombier };
2337dd7cddfSDavid du Colombier 
2347dd7cddfSDavid du Colombier enum {						/* Window 4 - diagnostic */
2357dd7cddfSDavid du Colombier 	Wdiagnostic		= 0x0004,
2367dd7cddfSDavid du Colombier 						/* registers */
2377dd7cddfSDavid du Colombier 	VcoDiagnostic		= 0x0002,
2387dd7cddfSDavid du Colombier 	FifoDiagnostic		= 0x0004,
2397dd7cddfSDavid du Colombier 	NetworkDiagnostic	= 0x0006,
2407dd7cddfSDavid du Colombier 	PhysicalMgmt		= 0x0008,
2417dd7cddfSDavid du Colombier 	MediaStatus		= 0x000A,
2427dd7cddfSDavid du Colombier 	BadSSD			= 0x000C,
2437dd7cddfSDavid du Colombier 	UpperBytesOk		= 0x000D,
2447dd7cddfSDavid du Colombier 						/* FifoDiagnostic bits */
2457dd7cddfSDavid du Colombier 	txOverrun		= 0x0400,
2467dd7cddfSDavid du Colombier 	rxUnderrun		= 0x2000,
2477dd7cddfSDavid du Colombier 	receiving		= 0x8000,
2487dd7cddfSDavid du Colombier 						/* PhysicalMgmt bits */
2497dd7cddfSDavid du Colombier 	mgmtClk			= 0x0001,
2507dd7cddfSDavid du Colombier 	mgmtData		= 0x0002,
2517dd7cddfSDavid du Colombier 	mgmtDir			= 0x0004,
2527dd7cddfSDavid du Colombier 	cat5LinkTestDefeat	= 0x8000,
2537dd7cddfSDavid du Colombier 						/* MediaStatus bits */
2547dd7cddfSDavid du Colombier 	dataRate100		= 0x0002,
2557dd7cddfSDavid du Colombier 	crcStripDisable		= 0x0004,
2567dd7cddfSDavid du Colombier 	enableSqeStats		= 0x0008,
2577dd7cddfSDavid du Colombier 	collisionDetect		= 0x0010,
2587dd7cddfSDavid du Colombier 	carrierSense		= 0x0020,
2597dd7cddfSDavid du Colombier 	jabberGuardEnable	= 0x0040,
2607dd7cddfSDavid du Colombier 	linkBeatEnable		= 0x0080,
2617dd7cddfSDavid du Colombier 	jabberDetect		= 0x0200,
2627dd7cddfSDavid du Colombier 	polarityReversed	= 0x0400,
2637dd7cddfSDavid du Colombier 	linkBeatDetect		= 0x0800,
2647dd7cddfSDavid du Colombier 	txInProg		= 0x1000,
2657dd7cddfSDavid du Colombier 	dcConverterEnabled	= 0x4000,
2667dd7cddfSDavid du Colombier 	auiDisable		= 0x8000,	/* 10BaseT transceiver selected */
2677dd7cddfSDavid du Colombier };
2687dd7cddfSDavid du Colombier 
2697dd7cddfSDavid du Colombier enum {						/* Window 5 - internal state */
2707dd7cddfSDavid du Colombier 	Wstate			= 0x0005,
2717dd7cddfSDavid du Colombier 						/* registers */
2727dd7cddfSDavid du Colombier 	TxStartThresh		= 0x0000,
2737dd7cddfSDavid du Colombier 	TxAvailableThresh	= 0x0002,
2747dd7cddfSDavid du Colombier 	RxEarlyThresh		= 0x0006,
2757dd7cddfSDavid du Colombier 	RxFilter		= 0x0008,
2767dd7cddfSDavid du Colombier 	InterruptEnable		= 0x000A,
2777dd7cddfSDavid du Colombier 	IndicationEnable	= 0x000C,
2787dd7cddfSDavid du Colombier };
2797dd7cddfSDavid du Colombier 
2807dd7cddfSDavid du Colombier enum {						/* Window 6 - statistics */
2817dd7cddfSDavid du Colombier 	Wstatistics		= 0x0006,
2827dd7cddfSDavid du Colombier 						/* registers */
2837dd7cddfSDavid du Colombier 	CarrierLost		= 0x0000,
2847dd7cddfSDavid du Colombier 	SqeErrors		= 0x0001,
2857dd7cddfSDavid du Colombier 	MultipleColls		= 0x0002,
2867dd7cddfSDavid du Colombier 	SingleCollFrames	= 0x0003,
2877dd7cddfSDavid du Colombier 	LateCollisions		= 0x0004,
2887dd7cddfSDavid du Colombier 	RxOverruns		= 0x0005,
2897dd7cddfSDavid du Colombier 	FramesXmittedOk		= 0x0006,
2907dd7cddfSDavid du Colombier 	FramesRcvdOk		= 0x0007,
2917dd7cddfSDavid du Colombier 	FramesDeferred		= 0x0008,
2927dd7cddfSDavid du Colombier 	UpperFramesOk		= 0x0009,
2937dd7cddfSDavid du Colombier 	BytesRcvdOk		= 0x000A,
2947dd7cddfSDavid du Colombier 	BytesXmittedOk		= 0x000C,
2957dd7cddfSDavid du Colombier };
2967dd7cddfSDavid du Colombier 
2977dd7cddfSDavid du Colombier enum {						/* Window 7 - bus master operations */
2987dd7cddfSDavid du Colombier 	Wmaster			= 0x0007,
2997dd7cddfSDavid du Colombier 						/* registers */
3007dd7cddfSDavid du Colombier 	MasterAddress		= 0x0000,
3017dd7cddfSDavid du Colombier 	MasterLen		= 0x0006,
3027dd7cddfSDavid du Colombier 	MasterStatus		= 0x000C,
3037dd7cddfSDavid du Colombier 						/* MasterStatus bits */
3047dd7cddfSDavid du Colombier 	masterAbort		= 0x0001,
3057dd7cddfSDavid du Colombier 	targetAbort		= 0x0002,
3067dd7cddfSDavid du Colombier 	targetRetry		= 0x0004,
3077dd7cddfSDavid du Colombier 	targetDisc		= 0x0008,
3087dd7cddfSDavid du Colombier 	masterDownload		= 0x1000,
3097dd7cddfSDavid du Colombier 	masterUpload		= 0x4000,
3107dd7cddfSDavid du Colombier 	masterInProgress	= 0x8000,
3117dd7cddfSDavid du Colombier 
3127dd7cddfSDavid du Colombier 	masterMask		= 0xD00F,
3137dd7cddfSDavid du Colombier };
3147dd7cddfSDavid du Colombier 
3157dd7cddfSDavid du Colombier enum {						/* 3C90x extended register set */
3163ff48bf5SDavid du Colombier 	TIMER905		= 0x001A,	/* 8-bits */
31759cc4ca5SDavid du Colombier 	TxStatus905		= 0x001B,	/* 8-bits */
3187dd7cddfSDavid du Colombier 	PktStatus		= 0x0020,	/* 32-bits */
3197dd7cddfSDavid du Colombier 	DnListPtr		= 0x0024,	/* 32-bits, 8-byte aligned */
3207dd7cddfSDavid du Colombier 	FragAddr		= 0x0028,	/* 32-bits */
3217dd7cddfSDavid du Colombier 	FragLen			= 0x002C,	/* 16-bits */
3227dd7cddfSDavid du Colombier 	ListOffset		= 0x002E,	/* 8-bits */
3237dd7cddfSDavid du Colombier 	TxFreeThresh		= 0x002F,	/* 8-bits */
3247dd7cddfSDavid du Colombier 	UpPktStatus		= 0x0030,	/* 32-bits */
3253ff48bf5SDavid du Colombier 	FreeTIMER		= 0x0034,	/* 16-bits */
3267dd7cddfSDavid du Colombier 	UpListPtr		= 0x0038,	/* 32-bits, 8-byte aligned */
3277dd7cddfSDavid du Colombier 
3287dd7cddfSDavid du Colombier 						/* PktStatus bits */
3297dd7cddfSDavid du Colombier 	fragLast		= 0x00000001,
3307dd7cddfSDavid du Colombier 	dnCmplReq		= 0x00000002,
3317dd7cddfSDavid du Colombier 	dnStalled		= 0x00000004,
3327dd7cddfSDavid du Colombier 	upCompleteX		= 0x00000008,
3337dd7cddfSDavid du Colombier 	dnCompleteX		= 0x00000010,
3347dd7cddfSDavid du Colombier 	upRxEarlyEnable		= 0x00000020,
3357dd7cddfSDavid du Colombier 	armCountdown		= 0x00000040,
3367dd7cddfSDavid du Colombier 	dnInProg		= 0x00000080,
3377dd7cddfSDavid du Colombier 	counterSpeed		= 0x00000010,	/* 0 3.2uS, 1 320nS */
3387dd7cddfSDavid du Colombier 	countdownMode		= 0x00000020,
3397dd7cddfSDavid du Colombier 						/* UpPktStatus bits (dpd->control) */
3407dd7cddfSDavid du Colombier 	upPktLenMask		= 0x00001FFF,
3417dd7cddfSDavid du Colombier 	upStalled		= 0x00002000,
3427dd7cddfSDavid du Colombier 	upError			= 0x00004000,
3437dd7cddfSDavid du Colombier 	upPktComplete		= 0x00008000,
3447dd7cddfSDavid du Colombier 	upOverrun		= 0x00010000,	/* RxError<<16 */
3457dd7cddfSDavid du Colombier 	upRuntFrame		= 0x00020000,
3467dd7cddfSDavid du Colombier 	upAlignmentError	= 0x00040000,
3477dd7cddfSDavid du Colombier 	upCRCError		= 0x00080000,
3487dd7cddfSDavid du Colombier 	upOversizedFrame	= 0x00100000,
3497dd7cddfSDavid du Colombier 	upDribbleBits		= 0x00800000,
3507dd7cddfSDavid du Colombier 	upOverflow		= 0x01000000,
3517dd7cddfSDavid du Colombier 
3527dd7cddfSDavid du Colombier 	dnIndicate		= 0x80000000,	/* FrameStartHeader (dpd->control) */
3537dd7cddfSDavid du Colombier 
3547dd7cddfSDavid du Colombier 	updnLastFrag		= 0x80000000,	/* (dpd->len) */
3557dd7cddfSDavid du Colombier 
3567dd7cddfSDavid du Colombier 	Nup			= 32,
3577dd7cddfSDavid du Colombier 	Ndn			= 64,
3587dd7cddfSDavid du Colombier };
3597dd7cddfSDavid du Colombier 
3607dd7cddfSDavid du Colombier /*
3617dd7cddfSDavid du Colombier  * Up/Dn Packet Descriptors.
3627dd7cddfSDavid du Colombier  * The hardware info (np, control, addr, len) must be 8-byte aligned
3637dd7cddfSDavid du Colombier  * and this structure size must be a multiple of 8.
3647dd7cddfSDavid du Colombier  */
3657dd7cddfSDavid du Colombier typedef struct Pd Pd;
3667dd7cddfSDavid du Colombier typedef struct Pd {
3677dd7cddfSDavid du Colombier 	ulong	np;			/* next pointer */
3687dd7cddfSDavid du Colombier 	ulong	control;		/* FSH or UpPktStatus */
3697dd7cddfSDavid du Colombier 	ulong	addr;
3707dd7cddfSDavid du Colombier 	ulong	len;
3717dd7cddfSDavid du Colombier 
3727dd7cddfSDavid du Colombier 	Pd*	next;
3737dd7cddfSDavid du Colombier 	Block*	bp;
3747dd7cddfSDavid du Colombier } Pd;
3757dd7cddfSDavid du Colombier 
3763ff48bf5SDavid du Colombier typedef struct Ctlr Ctlr;
3773ff48bf5SDavid du Colombier typedef struct Ctlr {
3783ff48bf5SDavid du Colombier 	int	port;
3793ff48bf5SDavid du Colombier 	Pcidev*	pcidev;
3803ff48bf5SDavid du Colombier 	int	irq;
3813ff48bf5SDavid du Colombier 	Ctlr*	next;
3823ff48bf5SDavid du Colombier 	int	active;
3833ff48bf5SDavid du Colombier 	int	did;
3843ff48bf5SDavid du Colombier 
3857dd7cddfSDavid du Colombier 	Lock	wlock;			/* window access */
3867dd7cddfSDavid du Colombier 
3877dd7cddfSDavid du Colombier 	int	attached;
3887dd7cddfSDavid du Colombier 	int	busmaster;
3897dd7cddfSDavid du Colombier 	Block*	rbp;			/* receive buffer */
3907dd7cddfSDavid du Colombier 
3917dd7cddfSDavid du Colombier 	Block*	txbp;			/* FIFO -based transmission */
3927dd7cddfSDavid du Colombier 	int	txthreshold;
3937dd7cddfSDavid du Colombier 	int	txbusy;
3947dd7cddfSDavid du Colombier 
3957dd7cddfSDavid du Colombier 	int	nup;			/* full-busmaster -based reception */
3967dd7cddfSDavid du Colombier 	void*	upbase;
3977dd7cddfSDavid du Colombier 	Pd*	upr;
3987dd7cddfSDavid du Colombier 	Pd*	uphead;
3997dd7cddfSDavid du Colombier 
4007dd7cddfSDavid du Colombier 	int	ndn;			/* full-busmaster -based transmission */
4017dd7cddfSDavid du Colombier 	void*	dnbase;
4027dd7cddfSDavid du Colombier 	Pd*	dnr;
4037dd7cddfSDavid du Colombier 	Pd*	dnhead;
4047dd7cddfSDavid du Colombier 	Pd*	dntail;
4057dd7cddfSDavid du Colombier 	int	dnq;
4067dd7cddfSDavid du Colombier 
4077dd7cddfSDavid du Colombier 	long	interrupts;		/* statistics */
4083ff48bf5SDavid du Colombier 	long	bogusinterrupts;
40959cc4ca5SDavid du Colombier 	long	timer[2];
4107dd7cddfSDavid du Colombier 	long	stats[BytesRcvdOk+3];
4117dd7cddfSDavid du Colombier 
4127dd7cddfSDavid du Colombier 	int	upqmax;
4137dd7cddfSDavid du Colombier 	int	upqmaxhw;
4147dd7cddfSDavid du Colombier 	ulong	upinterrupts;
4157dd7cddfSDavid du Colombier 	ulong	upqueued;
4167dd7cddfSDavid du Colombier 	ulong	upstalls;
4177dd7cddfSDavid du Colombier 	int	dnqmax;
4187dd7cddfSDavid du Colombier 	int	dnqmaxhw;
4197dd7cddfSDavid du Colombier 	ulong	dninterrupts;
4207dd7cddfSDavid du Colombier 	ulong	dnqueued;
4217dd7cddfSDavid du Colombier 
4227dd7cddfSDavid du Colombier 	int	xcvr;			/* transceiver type */
423e31d734fSDavid du Colombier 	int	eepromcmd;		/* EEPROM read command */
4247dd7cddfSDavid du Colombier 	int	rxstatus9;		/* old-style RxStatus register */
4257dd7cddfSDavid du Colombier 	int	rxearly;		/* RxEarlyThreshold */
4267dd7cddfSDavid du Colombier 	int	ts;			/* threshold shift */
4277dd7cddfSDavid du Colombier 	int	upenabled;
4287dd7cddfSDavid du Colombier 	int	dnenabled;
429e31d734fSDavid du Colombier 	ulong	cbfnpa;			/* CardBus functions */
430e31d734fSDavid du Colombier 	ulong*	cbfn;
4317dd7cddfSDavid du Colombier } Ctlr;
4327dd7cddfSDavid du Colombier 
4333ff48bf5SDavid du Colombier static Ctlr* ctlrhead;
4343ff48bf5SDavid du Colombier static Ctlr* ctlrtail;
4353ff48bf5SDavid du Colombier 
4367dd7cddfSDavid du Colombier static void
init905(Ctlr * ctlr)4377dd7cddfSDavid du Colombier init905(Ctlr* ctlr)
4387dd7cddfSDavid du Colombier {
4397dd7cddfSDavid du Colombier 	Block *bp;
4407dd7cddfSDavid du Colombier 	Pd *pd, *prev;
4417dd7cddfSDavid du Colombier 
4427dd7cddfSDavid du Colombier 	/*
4437dd7cddfSDavid du Colombier 	 * Create rings for the receive and transmit sides.
4447dd7cddfSDavid du Colombier 	 * Take care with alignment:
4457dd7cddfSDavid du Colombier 	 *	make sure ring base is 8-byte aligned;
4467dd7cddfSDavid du Colombier 	 *	make sure each entry is 8-byte aligned.
4477dd7cddfSDavid du Colombier 	 */
4487dd7cddfSDavid du Colombier 	ctlr->upbase = malloc((ctlr->nup+1)*sizeof(Pd));
449*aa72973aSDavid du Colombier 	if(ctlr->upbase == nil)
450*aa72973aSDavid du Colombier 		error(Enomem);
4517dd7cddfSDavid du Colombier 	ctlr->upr = (Pd*)ROUNDUP((ulong)ctlr->upbase, 8);
4527dd7cddfSDavid du Colombier 
4537dd7cddfSDavid du Colombier 	prev = ctlr->upr;
4547dd7cddfSDavid du Colombier 	for(pd = &ctlr->upr[ctlr->nup-1]; pd >= ctlr->upr; pd--){
4557dd7cddfSDavid du Colombier 		pd->np = PADDR(&prev->np);
4567dd7cddfSDavid du Colombier 		pd->control = 0;
4579a747e4fSDavid du Colombier 		bp = iallocb(sizeof(Etherpkt));
4589a747e4fSDavid du Colombier 		if(bp == nil)
4599a747e4fSDavid du Colombier 			panic("can't allocate ethernet receive ring");
4607dd7cddfSDavid du Colombier 		pd->addr = PADDR(bp->rp);
4617dd7cddfSDavid du Colombier 		pd->len = updnLastFrag|sizeof(Etherpkt);
4627dd7cddfSDavid du Colombier 
4637dd7cddfSDavid du Colombier 		pd->next = prev;
4647dd7cddfSDavid du Colombier 		prev = pd;
4657dd7cddfSDavid du Colombier 		pd->bp = bp;
4667dd7cddfSDavid du Colombier 	}
4677dd7cddfSDavid du Colombier 	ctlr->uphead = ctlr->upr;
4687dd7cddfSDavid du Colombier 
4697dd7cddfSDavid du Colombier 	ctlr->dnbase = malloc((ctlr->ndn+1)*sizeof(Pd));
470*aa72973aSDavid du Colombier 	if(ctlr->dnbase == nil) {
471*aa72973aSDavid du Colombier 		free(ctlr->upbase);
472*aa72973aSDavid du Colombier 		error(Enomem);
473*aa72973aSDavid du Colombier 	}
4747dd7cddfSDavid du Colombier 	ctlr->dnr = (Pd*)ROUNDUP((ulong)ctlr->dnbase, 8);
4757dd7cddfSDavid du Colombier 
4767dd7cddfSDavid du Colombier 	prev = ctlr->dnr;
4777dd7cddfSDavid du Colombier 	for(pd = &ctlr->dnr[ctlr->ndn-1]; pd >= ctlr->dnr; pd--){
4787dd7cddfSDavid du Colombier 		pd->next = prev;
4797dd7cddfSDavid du Colombier 		prev = pd;
4807dd7cddfSDavid du Colombier 	}
4817dd7cddfSDavid du Colombier 	ctlr->dnhead = ctlr->dnr;
4827dd7cddfSDavid du Colombier 	ctlr->dntail = ctlr->dnr;
4837dd7cddfSDavid du Colombier 	ctlr->dnq = 0;
4847dd7cddfSDavid du Colombier }
4857dd7cddfSDavid du Colombier 
4867dd7cddfSDavid du Colombier static Block*
rbpalloc(Block * (* f)(int))4877dd7cddfSDavid du Colombier rbpalloc(Block* (*f)(int))
4887dd7cddfSDavid du Colombier {
4897dd7cddfSDavid du Colombier 	Block *bp;
4907dd7cddfSDavid du Colombier 	ulong addr;
4917dd7cddfSDavid du Colombier 
4927dd7cddfSDavid du Colombier 	/*
4937dd7cddfSDavid du Colombier 	 * The receive buffers must be on a 32-byte
4947dd7cddfSDavid du Colombier 	 * boundary for EISA busmastering.
4957dd7cddfSDavid du Colombier 	 */
4967dd7cddfSDavid du Colombier 	if(bp = f(ROUNDUP(sizeof(Etherpkt), 4) + 31)){
4977dd7cddfSDavid du Colombier 		addr = (ulong)bp->base;
4987dd7cddfSDavid du Colombier 		addr = ROUNDUP(addr, 32);
4997dd7cddfSDavid du Colombier 		bp->rp = (uchar*)addr;
5007dd7cddfSDavid du Colombier 	}
5017dd7cddfSDavid du Colombier 
5027dd7cddfSDavid du Colombier 	return bp;
5037dd7cddfSDavid du Colombier }
5047dd7cddfSDavid du Colombier 
5057dd7cddfSDavid du Colombier static uchar*
startdma(Ether * ether,ulong address)5067dd7cddfSDavid du Colombier startdma(Ether* ether, ulong address)
5077dd7cddfSDavid du Colombier {
5087dd7cddfSDavid du Colombier 	int port, status, w;
5097dd7cddfSDavid du Colombier 	uchar *wp;
5107dd7cddfSDavid du Colombier 
5117dd7cddfSDavid du Colombier 	port = ether->port;
5127dd7cddfSDavid du Colombier 
5137dd7cddfSDavid du Colombier 	w = (STATUS(port)>>13) & 0x07;
5147dd7cddfSDavid du Colombier 	COMMAND(port, SelectRegisterWindow, Wmaster);
5157dd7cddfSDavid du Colombier 
5167dd7cddfSDavid du Colombier 	wp = KADDR(inl(port+MasterAddress));
5177dd7cddfSDavid du Colombier 	status = ins(port+MasterStatus);
5187dd7cddfSDavid du Colombier 	if(status & (masterInProgress|targetAbort|masterAbort))
5197dd7cddfSDavid du Colombier 		print("#l%d: BM status 0x%uX\n", ether->ctlrno, status);
5207dd7cddfSDavid du Colombier 	outs(port+MasterStatus, masterMask);
5217dd7cddfSDavid du Colombier 	outl(port+MasterAddress, address);
5227dd7cddfSDavid du Colombier 	outs(port+MasterLen, sizeof(Etherpkt));
5237dd7cddfSDavid du Colombier 	COMMAND(port, StartDma, Upload);
5247dd7cddfSDavid du Colombier 
5257dd7cddfSDavid du Colombier 	COMMAND(port, SelectRegisterWindow, w);
5267dd7cddfSDavid du Colombier 	return wp;
5277dd7cddfSDavid du Colombier }
5287dd7cddfSDavid du Colombier 
5297dd7cddfSDavid du Colombier static void
promiscuous(void * arg,int on)5307dd7cddfSDavid du Colombier promiscuous(void* arg, int on)
5317dd7cddfSDavid du Colombier {
5327dd7cddfSDavid du Colombier 	int filter, port;
5337dd7cddfSDavid du Colombier 	Ether *ether;
5347dd7cddfSDavid du Colombier 
5357dd7cddfSDavid du Colombier 	ether = (Ether*)arg;
5367dd7cddfSDavid du Colombier 	port = ether->port;
5377dd7cddfSDavid du Colombier 
5387dd7cddfSDavid du Colombier 	filter = receiveBroadcast|receiveIndividual;
5397dd7cddfSDavid du Colombier 	if(ether->nmaddr)
5407dd7cddfSDavid du Colombier 		filter |= receiveMulticast;
5417dd7cddfSDavid du Colombier 	if(on)
5427dd7cddfSDavid du Colombier 		filter |= receiveAllFrames;
5437dd7cddfSDavid du Colombier 	COMMAND(port, SetRxFilter, filter);
5447dd7cddfSDavid du Colombier }
5457dd7cddfSDavid du Colombier 
5467dd7cddfSDavid du Colombier static void
multicast(void * arg,uchar * addr,int on)5477dd7cddfSDavid du Colombier multicast(void* arg, uchar *addr, int on)
5487dd7cddfSDavid du Colombier {
5497dd7cddfSDavid du Colombier 	int filter, port;
5507dd7cddfSDavid du Colombier 	Ether *ether;
5517dd7cddfSDavid du Colombier 
5527dd7cddfSDavid du Colombier 	USED(addr, on);
5537dd7cddfSDavid du Colombier 
5547dd7cddfSDavid du Colombier 	ether = (Ether*)arg;
5557dd7cddfSDavid du Colombier 	port = ether->port;
5567dd7cddfSDavid du Colombier 
5577dd7cddfSDavid du Colombier 	filter = receiveBroadcast|receiveIndividual;
5587dd7cddfSDavid du Colombier 	if(ether->nmaddr)
5597dd7cddfSDavid du Colombier 		filter |= receiveMulticast;
5607dd7cddfSDavid du Colombier 	if(ether->prom)
5617dd7cddfSDavid du Colombier 		filter |= receiveAllFrames;
5627dd7cddfSDavid du Colombier 	COMMAND(port, SetRxFilter, filter);
5637dd7cddfSDavid du Colombier }
5647dd7cddfSDavid du Colombier 
5659a747e4fSDavid du Colombier /* On the 575B and C, interrupts need to be acknowledged in CardBus memory space */
5669a747e4fSDavid du Colombier static void
intrackcb(ulong * cbfn)567e31d734fSDavid du Colombier intrackcb(ulong *cbfn)
5689a747e4fSDavid du Colombier {
569e31d734fSDavid du Colombier 	cbfn[1] = 0x8000;
5709a747e4fSDavid du Colombier }
5719a747e4fSDavid du Colombier 
5727dd7cddfSDavid du Colombier static void
attach(Ether * ether)5737dd7cddfSDavid du Colombier attach(Ether* ether)
5747dd7cddfSDavid du Colombier {
5757dd7cddfSDavid du Colombier 	int port, x;
5767dd7cddfSDavid du Colombier 	Ctlr *ctlr;
5777dd7cddfSDavid du Colombier 
5787dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
5797dd7cddfSDavid du Colombier 	ilock(&ctlr->wlock);
5807dd7cddfSDavid du Colombier 	if(ctlr->attached){
5817dd7cddfSDavid du Colombier 		iunlock(&ctlr->wlock);
5827dd7cddfSDavid du Colombier 		return;
5837dd7cddfSDavid du Colombier 	}
5847dd7cddfSDavid du Colombier 
5857dd7cddfSDavid du Colombier 	port = ether->port;
5867dd7cddfSDavid du Colombier 
5877dd7cddfSDavid du Colombier 	/*
5887dd7cddfSDavid du Colombier 	 * Set the receiver packet filter for this and broadcast addresses,
5897dd7cddfSDavid du Colombier 	 * set the interrupt masks for all interrupts, enable the receiver
5907dd7cddfSDavid du Colombier 	 * and transmitter.
5917dd7cddfSDavid du Colombier 	 */
5927dd7cddfSDavid du Colombier 	promiscuous(ether, ether->prom);
5937dd7cddfSDavid du Colombier 
5947dd7cddfSDavid du Colombier 	x = interruptMask;
5957dd7cddfSDavid du Colombier 	if(ctlr->busmaster == 1)
5967dd7cddfSDavid du Colombier 		x &= ~(rxEarly|rxComplete);
5977dd7cddfSDavid du Colombier 	else{
5987dd7cddfSDavid du Colombier 		if(ctlr->dnenabled)
5997dd7cddfSDavid du Colombier 			x &= ~transferInt;
6007dd7cddfSDavid du Colombier 		if(ctlr->upenabled)
6017dd7cddfSDavid du Colombier 			x &= ~(rxEarly|rxComplete);
6027dd7cddfSDavid du Colombier 	}
6037dd7cddfSDavid du Colombier 	COMMAND(port, SetIndicationEnable, x);
6047dd7cddfSDavid du Colombier 	COMMAND(port, SetInterruptEnable, x);
6057dd7cddfSDavid du Colombier 	COMMAND(port, RxEnable, 0);
6067dd7cddfSDavid du Colombier 	COMMAND(port, TxEnable, 0);
6077dd7cddfSDavid du Colombier 
6083ff48bf5SDavid du Colombier 	/*
6093ff48bf5SDavid du Colombier 	 * If this is a CardBus card, acknowledge any interrupts.
6103ff48bf5SDavid du Colombier 	 */
611e31d734fSDavid du Colombier 	if(ctlr->cbfn != nil)
612e31d734fSDavid du Colombier 		intrackcb(ctlr->cbfn);
6139a747e4fSDavid du Colombier 
6147dd7cddfSDavid du Colombier 	/*
6157dd7cddfSDavid du Colombier 	 * Prime the busmaster channel for receiving directly into a
6167dd7cddfSDavid du Colombier 	 * receive packet buffer if necessary.
6177dd7cddfSDavid du Colombier 	 */
6187dd7cddfSDavid du Colombier 	if(ctlr->busmaster == 1)
6197dd7cddfSDavid du Colombier 		startdma(ether, PADDR(ctlr->rbp->rp));
6207dd7cddfSDavid du Colombier 	else{
6217dd7cddfSDavid du Colombier 		if(ctlr->upenabled)
6227dd7cddfSDavid du Colombier 			outl(port+UpListPtr, PADDR(&ctlr->uphead->np));
6237dd7cddfSDavid du Colombier 	}
6247dd7cddfSDavid du Colombier 
6257dd7cddfSDavid du Colombier 	ctlr->attached = 1;
6267dd7cddfSDavid du Colombier 	iunlock(&ctlr->wlock);
6277dd7cddfSDavid du Colombier }
6287dd7cddfSDavid du Colombier 
6297dd7cddfSDavid du Colombier static void
statistics(Ether * ether)6307dd7cddfSDavid du Colombier statistics(Ether* ether)
6317dd7cddfSDavid du Colombier {
6327dd7cddfSDavid du Colombier 	int port, i, u, w;
6337dd7cddfSDavid du Colombier 	Ctlr *ctlr;
6347dd7cddfSDavid du Colombier 
6357dd7cddfSDavid du Colombier 	port = ether->port;
6367dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
6377dd7cddfSDavid du Colombier 
6387dd7cddfSDavid du Colombier 	/*
6397dd7cddfSDavid du Colombier 	 * 3C59[27] require a read between a PIO write and
6407dd7cddfSDavid du Colombier 	 * reading a statistics register.
6417dd7cddfSDavid du Colombier 	 */
6427dd7cddfSDavid du Colombier 	w = (STATUS(port)>>13) & 0x07;
6437dd7cddfSDavid du Colombier 	COMMAND(port, SelectRegisterWindow, Wstatistics);
6447dd7cddfSDavid du Colombier 	STATUS(port);
6457dd7cddfSDavid du Colombier 
6467dd7cddfSDavid du Colombier 	for(i = 0; i < UpperFramesOk; i++)
6477dd7cddfSDavid du Colombier 		ctlr->stats[i] += inb(port+i) & 0xFF;
6487dd7cddfSDavid du Colombier 	u = inb(port+UpperFramesOk) & 0xFF;
6497dd7cddfSDavid du Colombier 	ctlr->stats[FramesXmittedOk] += (u & 0x30)<<4;
6507dd7cddfSDavid du Colombier 	ctlr->stats[FramesRcvdOk] += (u & 0x03)<<8;
6517dd7cddfSDavid du Colombier 	ctlr->stats[BytesRcvdOk] += ins(port+BytesRcvdOk) & 0xFFFF;
6527dd7cddfSDavid du Colombier 	ctlr->stats[BytesRcvdOk+1] += ins(port+BytesXmittedOk) & 0xFFFF;
6537dd7cddfSDavid du Colombier 
6547dd7cddfSDavid du Colombier 	switch(ctlr->xcvr){
6557dd7cddfSDavid du Colombier 
6567dd7cddfSDavid du Colombier 	case xcvrMii:
6577dd7cddfSDavid du Colombier 	case xcvr100BaseTX:
6587dd7cddfSDavid du Colombier 	case xcvr100BaseFX:
6597dd7cddfSDavid du Colombier 		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
6607dd7cddfSDavid du Colombier 		STATUS(port);
6617dd7cddfSDavid du Colombier 		ctlr->stats[BytesRcvdOk+2] += inb(port+BadSSD);
6627dd7cddfSDavid du Colombier 		break;
6637dd7cddfSDavid du Colombier 	}
6647dd7cddfSDavid du Colombier 
6657dd7cddfSDavid du Colombier 	COMMAND(port, SelectRegisterWindow, w);
6667dd7cddfSDavid du Colombier }
6677dd7cddfSDavid du Colombier 
6687dd7cddfSDavid du Colombier static void
txstart(Ether * ether)6697dd7cddfSDavid du Colombier txstart(Ether* ether)
6707dd7cddfSDavid du Colombier {
6717dd7cddfSDavid du Colombier 	int port, len;
6727dd7cddfSDavid du Colombier 	Ctlr *ctlr;
6737dd7cddfSDavid du Colombier 	Block *bp;
6747dd7cddfSDavid du Colombier 
6757dd7cddfSDavid du Colombier 	port = ether->port;
6767dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
6777dd7cddfSDavid du Colombier 
6787dd7cddfSDavid du Colombier 	/*
6797dd7cddfSDavid du Colombier 	 * Attempt to top-up the transmit FIFO. If there's room simply
6807dd7cddfSDavid du Colombier 	 * stuff in the packet length (unpadded to a dword boundary), the
6817dd7cddfSDavid du Colombier 	 * packet data (padded) and remove the packet from the queue.
6827dd7cddfSDavid du Colombier 	 * If there's no room post an interrupt for when there is.
6837dd7cddfSDavid du Colombier 	 * This routine is called both from the top level and from interrupt
6847dd7cddfSDavid du Colombier 	 * level and expects to be called with ctlr->wlock already locked
6857dd7cddfSDavid du Colombier 	 * and the correct register window (Wop) in place.
6867dd7cddfSDavid du Colombier 	 */
6877dd7cddfSDavid du Colombier 	for(;;){
6887dd7cddfSDavid du Colombier 		if(ctlr->txbp){
6897dd7cddfSDavid du Colombier 			bp = ctlr->txbp;
6907dd7cddfSDavid du Colombier 			ctlr->txbp = 0;
6917dd7cddfSDavid du Colombier 		}
6927dd7cddfSDavid du Colombier 		else{
6937dd7cddfSDavid du Colombier 			bp = qget(ether->oq);
6947dd7cddfSDavid du Colombier 			if(bp == nil)
6957dd7cddfSDavid du Colombier 				break;
6967dd7cddfSDavid du Colombier 		}
6977dd7cddfSDavid du Colombier 
6987dd7cddfSDavid du Colombier 		len = ROUNDUP(BLEN(bp), 4);
6997dd7cddfSDavid du Colombier 		if(len+4 <= ins(port+TxFree)){
7007dd7cddfSDavid du Colombier 			outl(port+Fifo, BLEN(bp));
7017dd7cddfSDavid du Colombier 			outsl(port+Fifo, bp->rp, len/4);
7027dd7cddfSDavid du Colombier 
7037dd7cddfSDavid du Colombier 			freeb(bp);
7047dd7cddfSDavid du Colombier 
7057dd7cddfSDavid du Colombier 			ether->outpackets++;
7067dd7cddfSDavid du Colombier 		}
7077dd7cddfSDavid du Colombier 		else{
7087dd7cddfSDavid du Colombier 			ctlr->txbp = bp;
7097dd7cddfSDavid du Colombier 			if(ctlr->txbusy == 0){
7107dd7cddfSDavid du Colombier 				ctlr->txbusy = 1;
7117dd7cddfSDavid du Colombier 				COMMAND(port, SetTxAvailableThresh, len>>ctlr->ts);
7127dd7cddfSDavid du Colombier 			}
7137dd7cddfSDavid du Colombier 			break;
7147dd7cddfSDavid du Colombier 		}
7157dd7cddfSDavid du Colombier 	}
7167dd7cddfSDavid du Colombier }
7177dd7cddfSDavid du Colombier 
7187dd7cddfSDavid du Colombier static void
txstart905(Ether * ether)7197dd7cddfSDavid du Colombier txstart905(Ether* ether)
7207dd7cddfSDavid du Colombier {
7217dd7cddfSDavid du Colombier 	Ctlr *ctlr;
7227dd7cddfSDavid du Colombier 	int port, stalled, timeo;
7237dd7cddfSDavid du Colombier 	Block *bp;
7247dd7cddfSDavid du Colombier 	Pd *pd;
7257dd7cddfSDavid du Colombier 
7267dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
7277dd7cddfSDavid du Colombier 	port = ether->port;
7287dd7cddfSDavid du Colombier 
7297dd7cddfSDavid du Colombier 	/*
7307dd7cddfSDavid du Colombier 	 * Free any completed packets.
7317dd7cddfSDavid du Colombier 	 */
7327dd7cddfSDavid du Colombier 	pd = ctlr->dntail;
7337dd7cddfSDavid du Colombier 	while(ctlr->dnq){
7347dd7cddfSDavid du Colombier 		if(PADDR(&pd->np) == inl(port+DnListPtr))
7357dd7cddfSDavid du Colombier 			break;
7367dd7cddfSDavid du Colombier 		if(pd->bp){
7377dd7cddfSDavid du Colombier 			freeb(pd->bp);
7387dd7cddfSDavid du Colombier 			pd->bp = nil;
7397dd7cddfSDavid du Colombier 		}
7407dd7cddfSDavid du Colombier 		ctlr->dnq--;
7417dd7cddfSDavid du Colombier 		pd = pd->next;
7427dd7cddfSDavid du Colombier 	}
7437dd7cddfSDavid du Colombier 	ctlr->dntail = pd;
7447dd7cddfSDavid du Colombier 
7457dd7cddfSDavid du Colombier 	stalled = 0;
7467dd7cddfSDavid du Colombier 	while(ctlr->dnq < (ctlr->ndn-1)){
7477dd7cddfSDavid du Colombier 		bp = qget(ether->oq);
7487dd7cddfSDavid du Colombier 		if(bp == nil)
7497dd7cddfSDavid du Colombier 			break;
7507dd7cddfSDavid du Colombier 
7517dd7cddfSDavid du Colombier 		pd = ctlr->dnhead->next;
7527dd7cddfSDavid du Colombier 		pd->np = 0;
7537dd7cddfSDavid du Colombier 		pd->control = dnIndicate|BLEN(bp);
7547dd7cddfSDavid du Colombier 		pd->addr = PADDR(bp->rp);
7557dd7cddfSDavid du Colombier 		pd->len = updnLastFrag|BLEN(bp);
7567dd7cddfSDavid du Colombier 		pd->bp = bp;
7577dd7cddfSDavid du Colombier 
7587dd7cddfSDavid du Colombier 		if(stalled == 0 && ctlr->dnq && inl(port+DnListPtr)){
7597dd7cddfSDavid du Colombier 			COMMAND(port, Stall, dnStall);
7607dd7cddfSDavid du Colombier 			for(timeo = 100; (STATUS(port) & commandInProgress) && timeo; timeo--)
7617dd7cddfSDavid du Colombier 				;
7627dd7cddfSDavid du Colombier 			if(timeo == 0)
7637dd7cddfSDavid du Colombier 				print("#l%d: dnstall %d\n", ether->ctlrno, timeo);
7647dd7cddfSDavid du Colombier 			stalled = 1;
7657dd7cddfSDavid du Colombier 		}
7667dd7cddfSDavid du Colombier 
7677dd7cddfSDavid du Colombier 		coherence();
7687dd7cddfSDavid du Colombier 		ctlr->dnhead->np = PADDR(&pd->np);
7697dd7cddfSDavid du Colombier 		ctlr->dnhead->control &= ~dnIndicate;
7707dd7cddfSDavid du Colombier 		ctlr->dnhead = pd;
7717dd7cddfSDavid du Colombier 		if(ctlr->dnq == 0)
7727dd7cddfSDavid du Colombier 			ctlr->dntail = pd;
7737dd7cddfSDavid du Colombier 		ctlr->dnq++;
7747dd7cddfSDavid du Colombier 
7757dd7cddfSDavid du Colombier 		ctlr->dnqueued++;
7767dd7cddfSDavid du Colombier 	}
7777dd7cddfSDavid du Colombier 
7787dd7cddfSDavid du Colombier 	if(ctlr->dnq > ctlr->dnqmax)
7797dd7cddfSDavid du Colombier 		ctlr->dnqmax = ctlr->dnq;
7807dd7cddfSDavid du Colombier 
7817dd7cddfSDavid du Colombier 	/*
7827dd7cddfSDavid du Colombier 	 * If the adapter is not currently processing anything
7837dd7cddfSDavid du Colombier 	 * and there is something on the queue, start it processing.
7847dd7cddfSDavid du Colombier 	 */
7857dd7cddfSDavid du Colombier 	if(inl(port+DnListPtr) == 0 && ctlr->dnq)
7867dd7cddfSDavid du Colombier 		outl(port+DnListPtr, PADDR(&ctlr->dnhead->np));
7877dd7cddfSDavid du Colombier 	if(stalled)
7887dd7cddfSDavid du Colombier 		COMMAND(port, Stall, dnUnStall);
7897dd7cddfSDavid du Colombier }
7907dd7cddfSDavid du Colombier 
7917dd7cddfSDavid du Colombier static void
transmit(Ether * ether)7927dd7cddfSDavid du Colombier transmit(Ether* ether)
7937dd7cddfSDavid du Colombier {
7947dd7cddfSDavid du Colombier 	Ctlr *ctlr;
7957dd7cddfSDavid du Colombier 	int port, w;
7967dd7cddfSDavid du Colombier 
7977dd7cddfSDavid du Colombier 	port = ether->port;
7987dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
7997dd7cddfSDavid du Colombier 
8007dd7cddfSDavid du Colombier 	ilock(&ctlr->wlock);
8017dd7cddfSDavid du Colombier 	if(ctlr->dnenabled)
8027dd7cddfSDavid du Colombier 		txstart905(ether);
8037dd7cddfSDavid du Colombier 	else{
8047dd7cddfSDavid du Colombier 		w = (STATUS(port)>>13) & 0x07;
8057dd7cddfSDavid du Colombier 		COMMAND(port, SelectRegisterWindow, Wop);
8067dd7cddfSDavid du Colombier 		txstart(ether);
8077dd7cddfSDavid du Colombier 		COMMAND(port, SelectRegisterWindow, w);
8087dd7cddfSDavid du Colombier 	}
8097dd7cddfSDavid du Colombier 	iunlock(&ctlr->wlock);
8107dd7cddfSDavid du Colombier }
8117dd7cddfSDavid du Colombier 
8127dd7cddfSDavid du Colombier static void
receive905(Ether * ether)8137dd7cddfSDavid du Colombier receive905(Ether* ether)
8147dd7cddfSDavid du Colombier {
8157dd7cddfSDavid du Colombier 	Ctlr *ctlr;
8167dd7cddfSDavid du Colombier 	int len, port, q;
8177dd7cddfSDavid du Colombier 	Pd *pd;
8187dd7cddfSDavid du Colombier 	Block *bp;
8197dd7cddfSDavid du Colombier 
8207dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
8217dd7cddfSDavid du Colombier 	port = ether->port;
8227dd7cddfSDavid du Colombier 
8237dd7cddfSDavid du Colombier 	if(inl(port+UpPktStatus) & upStalled)
8247dd7cddfSDavid du Colombier 		ctlr->upstalls++;
8257dd7cddfSDavid du Colombier 	q = 0;
8267dd7cddfSDavid du Colombier 	for(pd = ctlr->uphead; pd->control & upPktComplete; pd = pd->next){
8277dd7cddfSDavid du Colombier 		if(pd->control & upError){
8287dd7cddfSDavid du Colombier 			if(pd->control & upOverrun)
8297dd7cddfSDavid du Colombier 				ether->overflows++;
8307dd7cddfSDavid du Colombier 			if(pd->control & (upOversizedFrame|upRuntFrame))
8317dd7cddfSDavid du Colombier 				ether->buffs++;
8327dd7cddfSDavid du Colombier 			if(pd->control & upAlignmentError)
8337dd7cddfSDavid du Colombier 				ether->frames++;
8347dd7cddfSDavid du Colombier 			if(pd->control & upCRCError)
8357dd7cddfSDavid du Colombier 				ether->crcs++;
8367dd7cddfSDavid du Colombier 		}
8377dd7cddfSDavid du Colombier 		else if(bp = iallocb(sizeof(Etherpkt)+4)){
8387dd7cddfSDavid du Colombier 			len = pd->control & rxBytes;
8397dd7cddfSDavid du Colombier 			pd->bp->wp = pd->bp->rp+len;
8407dd7cddfSDavid du Colombier 			etheriq(ether, pd->bp, 1);
8417dd7cddfSDavid du Colombier 			pd->bp = bp;
8427dd7cddfSDavid du Colombier 			pd->addr = PADDR(bp->rp);
8437dd7cddfSDavid du Colombier 			coherence();
8447dd7cddfSDavid du Colombier 		}
8457dd7cddfSDavid du Colombier 
8467dd7cddfSDavid du Colombier 		pd->control = 0;
8477dd7cddfSDavid du Colombier 		COMMAND(port, Stall, upUnStall);
8487dd7cddfSDavid du Colombier 
8497dd7cddfSDavid du Colombier 		q++;
8507dd7cddfSDavid du Colombier 	}
8517dd7cddfSDavid du Colombier 	ctlr->uphead = pd;
8527dd7cddfSDavid du Colombier 
8537dd7cddfSDavid du Colombier 	ctlr->upqueued += q;
8547dd7cddfSDavid du Colombier 	if(q > ctlr->upqmax)
8557dd7cddfSDavid du Colombier 		ctlr->upqmax = q;
8567dd7cddfSDavid du Colombier }
8577dd7cddfSDavid du Colombier 
8587dd7cddfSDavid du Colombier static void
receive(Ether * ether)8597dd7cddfSDavid du Colombier receive(Ether* ether)
8607dd7cddfSDavid du Colombier {
8617dd7cddfSDavid du Colombier 	int len, port, rxerror, rxstatus;
8627dd7cddfSDavid du Colombier 	Ctlr *ctlr;
8637dd7cddfSDavid du Colombier 	Block *bp;
8647dd7cddfSDavid du Colombier 
8657dd7cddfSDavid du Colombier 	port = ether->port;
8667dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
8677dd7cddfSDavid du Colombier 
8687dd7cddfSDavid du Colombier 	while(((rxstatus = ins(port+RxStatus)) & rxIncomplete) == 0){
8697dd7cddfSDavid du Colombier 		if(ctlr->busmaster == 1 && (STATUS(port) & busMasterInProgress))
8707dd7cddfSDavid du Colombier 			break;
8717dd7cddfSDavid du Colombier 
8727dd7cddfSDavid du Colombier 		/*
8737dd7cddfSDavid du Colombier 		 * If there was an error, log it and continue.
8747dd7cddfSDavid du Colombier 		 * Unfortunately the 3C5[078]9 has the error info in the status register
8757dd7cddfSDavid du Colombier 		 * and the 3C59[0257] implement a separate RxError register.
8767dd7cddfSDavid du Colombier 		 */
8777dd7cddfSDavid du Colombier 		if(rxstatus & rxError){
8787dd7cddfSDavid du Colombier 			if(ctlr->rxstatus9){
8797dd7cddfSDavid du Colombier 				switch(rxstatus & rxError9){
8807dd7cddfSDavid du Colombier 
8817dd7cddfSDavid du Colombier 				case rxOverrun9:
8827dd7cddfSDavid du Colombier 					ether->overflows++;
8837dd7cddfSDavid du Colombier 					break;
8847dd7cddfSDavid du Colombier 
8857dd7cddfSDavid du Colombier 				case oversizedFrame9:
8867dd7cddfSDavid du Colombier 				case runtFrame9:
8877dd7cddfSDavid du Colombier 					ether->buffs++;
8887dd7cddfSDavid du Colombier 					break;
8897dd7cddfSDavid du Colombier 
8907dd7cddfSDavid du Colombier 				case alignmentError9:
8917dd7cddfSDavid du Colombier 					ether->frames++;
8927dd7cddfSDavid du Colombier 					break;
8937dd7cddfSDavid du Colombier 
8947dd7cddfSDavid du Colombier 				case crcError9:
8957dd7cddfSDavid du Colombier 					ether->crcs++;
8967dd7cddfSDavid du Colombier 					break;
8977dd7cddfSDavid du Colombier 
8987dd7cddfSDavid du Colombier 				}
8997dd7cddfSDavid du Colombier 			}
9007dd7cddfSDavid du Colombier 			else{
9017dd7cddfSDavid du Colombier 				rxerror = inb(port+RxError);
9027dd7cddfSDavid du Colombier 				if(rxerror & rxOverrun)
9037dd7cddfSDavid du Colombier 					ether->overflows++;
9047dd7cddfSDavid du Colombier 				if(rxerror & (oversizedFrame|runtFrame))
9057dd7cddfSDavid du Colombier 					ether->buffs++;
9067dd7cddfSDavid du Colombier 				if(rxerror & alignmentError)
9077dd7cddfSDavid du Colombier 					ether->frames++;
9087dd7cddfSDavid du Colombier 				if(rxerror & crcError)
9097dd7cddfSDavid du Colombier 					ether->crcs++;
9107dd7cddfSDavid du Colombier 			}
9117dd7cddfSDavid du Colombier 		}
9127dd7cddfSDavid du Colombier 
9137dd7cddfSDavid du Colombier 		/*
9147dd7cddfSDavid du Colombier 		 * If there was an error or a new receive buffer can't be
9157dd7cddfSDavid du Colombier 		 * allocated, discard the packet and go on to the next.
9167dd7cddfSDavid du Colombier 		 */
9177dd7cddfSDavid du Colombier 		if((rxstatus & rxError) || (bp = rbpalloc(iallocb)) == 0){
9187dd7cddfSDavid du Colombier 			COMMAND(port, RxDiscard, 0);
9197dd7cddfSDavid du Colombier 			while(STATUS(port) & commandInProgress)
9207dd7cddfSDavid du Colombier 				;
9217dd7cddfSDavid du Colombier 
9227dd7cddfSDavid du Colombier 			if(ctlr->busmaster == 1)
9237dd7cddfSDavid du Colombier 				startdma(ether, PADDR(ctlr->rbp->rp));
9247dd7cddfSDavid du Colombier 
9257dd7cddfSDavid du Colombier 			continue;
9267dd7cddfSDavid du Colombier 		}
9277dd7cddfSDavid du Colombier 
9287dd7cddfSDavid du Colombier 		/*
9297dd7cddfSDavid du Colombier 		 * A valid receive packet awaits:
9307dd7cddfSDavid du Colombier 		 *	if using PIO, read it into the buffer;
9317dd7cddfSDavid du Colombier 		 *	discard the packet from the FIFO;
9327dd7cddfSDavid du Colombier 		 *	if using busmastering, start a new transfer for
9337dd7cddfSDavid du Colombier 		 *	  the next packet and as a side-effect get the
9347dd7cddfSDavid du Colombier 		 *	  end-pointer of the one just received;
9357dd7cddfSDavid du Colombier 		 *	pass the packet on to whoever wants it.
9367dd7cddfSDavid du Colombier 		 */
9377dd7cddfSDavid du Colombier 		if(ctlr->busmaster == 0 || ctlr->busmaster == 2){
9387dd7cddfSDavid du Colombier 			len = (rxstatus & rxBytes9);
9397dd7cddfSDavid du Colombier 			ctlr->rbp->wp = ctlr->rbp->rp + len;
9407dd7cddfSDavid du Colombier 			insl(port+Fifo, ctlr->rbp->rp, HOWMANY(len, 4));
9417dd7cddfSDavid du Colombier 		}
9427dd7cddfSDavid du Colombier 
9437dd7cddfSDavid du Colombier 		COMMAND(port, RxDiscard, 0);
9447dd7cddfSDavid du Colombier 		while(STATUS(port) & commandInProgress)
9457dd7cddfSDavid du Colombier 			;
9467dd7cddfSDavid du Colombier 
9477dd7cddfSDavid du Colombier 		if(ctlr->busmaster == 1)
9487dd7cddfSDavid du Colombier 			ctlr->rbp->wp = startdma(ether, PADDR(bp->rp));
9497dd7cddfSDavid du Colombier 
9507dd7cddfSDavid du Colombier 		etheriq(ether, ctlr->rbp, 1);
9517dd7cddfSDavid du Colombier 		ctlr->rbp = bp;
9527dd7cddfSDavid du Colombier 	}
9537dd7cddfSDavid du Colombier }
9547dd7cddfSDavid du Colombier 
9559a747e4fSDavid du Colombier static int
ejectable(int did)9569a747e4fSDavid du Colombier ejectable(int did)
9579a747e4fSDavid du Colombier {
9589a747e4fSDavid du Colombier 	switch (did) {
9599a747e4fSDavid du Colombier 	case 0x5157:
9609a747e4fSDavid du Colombier 		return 1;
9619a747e4fSDavid du Colombier 
9629a747e4fSDavid du Colombier 	default:
9639a747e4fSDavid du Colombier 		return 0;
9649a747e4fSDavid du Colombier 	}
9659a747e4fSDavid du Colombier }
9669a747e4fSDavid du Colombier 
9677dd7cddfSDavid du Colombier static void
interrupt(Ureg *,void * arg)9687dd7cddfSDavid du Colombier interrupt(Ureg*, void* arg)
9697dd7cddfSDavid du Colombier {
9707dd7cddfSDavid du Colombier 	Ether *ether;
97159cc4ca5SDavid du Colombier 	int port, status, s, txstatus, w, x;
9727dd7cddfSDavid du Colombier 	Ctlr *ctlr;
9737dd7cddfSDavid du Colombier 
9747dd7cddfSDavid du Colombier 	ether = arg;
9757dd7cddfSDavid du Colombier 	port = ether->port;
9767dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
9777dd7cddfSDavid du Colombier 
9787dd7cddfSDavid du Colombier 	ilock(&ctlr->wlock);
97959cc4ca5SDavid du Colombier 	status = STATUS(port);
98059cc4ca5SDavid du Colombier 	if(!(status & (interruptMask|interruptLatch))){
9813ff48bf5SDavid du Colombier 		ctlr->bogusinterrupts++;
98259cc4ca5SDavid du Colombier 		iunlock(&ctlr->wlock);
98359cc4ca5SDavid du Colombier 		return;
98459cc4ca5SDavid du Colombier 	}
98559cc4ca5SDavid du Colombier 	w = (status>>13) & 0x07;
9867dd7cddfSDavid du Colombier 	COMMAND(port, SelectRegisterWindow, Wop);
9877dd7cddfSDavid du Colombier 
9887dd7cddfSDavid du Colombier 	ctlr->interrupts++;
98959cc4ca5SDavid du Colombier 	if(ctlr->busmaster == 2)
9903ff48bf5SDavid du Colombier 		ctlr->timer[0] += inb(port+TIMER905) & 0xFF;
99159cc4ca5SDavid du Colombier 	else
9923ff48bf5SDavid du Colombier 		ctlr->timer[0] += inb(port+TIMER) & 0xFF;
99359cc4ca5SDavid du Colombier 
99459cc4ca5SDavid du Colombier 	do{
9957dd7cddfSDavid du Colombier 		if(status & hostError){
9967dd7cddfSDavid du Colombier 			/*
9977dd7cddfSDavid du Colombier 			 * Adapter failure, try to find out why, reset if
9987dd7cddfSDavid du Colombier 			 * necessary. What happens if Tx is active and a reset
9997dd7cddfSDavid du Colombier 			 * occurs, need to retransmit? This probably isn't right.
10007dd7cddfSDavid du Colombier 			 */
10017dd7cddfSDavid du Colombier 			COMMAND(port, SelectRegisterWindow, Wdiagnostic);
10027dd7cddfSDavid du Colombier 			x = ins(port+FifoDiagnostic);
10037dd7cddfSDavid du Colombier 			COMMAND(port, SelectRegisterWindow, Wop);
10049a747e4fSDavid du Colombier 
10059a747e4fSDavid du Colombier 			if (status == 0xFFFF && x == 0xFFFF && ejectable(ctlr->did)) {
10069a747e4fSDavid du Colombier 				print("#l%d: Card ejected?\n", ether->ctlrno);
10079a747e4fSDavid du Colombier 				iunlock(&ctlr->wlock);
10089a747e4fSDavid du Colombier 				return;
10099a747e4fSDavid du Colombier 			}
10109a747e4fSDavid du Colombier 
10117dd7cddfSDavid du Colombier 			print("#l%d: status 0x%uX, diag 0x%uX\n",
10127dd7cddfSDavid du Colombier 			    ether->ctlrno, status, x);
10137dd7cddfSDavid du Colombier 
10147dd7cddfSDavid du Colombier 			if(x & txOverrun){
10157dd7cddfSDavid du Colombier 				if(ctlr->busmaster == 0)
10167dd7cddfSDavid du Colombier 					COMMAND(port, TxReset, 0);
10177dd7cddfSDavid du Colombier 				else
10187dd7cddfSDavid du Colombier 					COMMAND(port, TxReset, (updnReset|dmaReset));
10197dd7cddfSDavid du Colombier 				COMMAND(port, TxEnable, 0);
10207dd7cddfSDavid du Colombier 			}
10217dd7cddfSDavid du Colombier 
10227dd7cddfSDavid du Colombier 			if(x & rxUnderrun){
10237dd7cddfSDavid du Colombier 				/*
10247dd7cddfSDavid du Colombier 				 * This shouldn't happen...
10257dd7cddfSDavid du Colombier 				 * Reset the receiver and restore the filter and RxEarly
10267dd7cddfSDavid du Colombier 				 * threshold before re-enabling.
10277dd7cddfSDavid du Colombier 				 * Need to restart any busmastering?
10287dd7cddfSDavid du Colombier 				 */
10297dd7cddfSDavid du Colombier 				COMMAND(port, SelectRegisterWindow, Wstate);
10307dd7cddfSDavid du Colombier 				s = (port+RxFilter) & 0x000F;
10317dd7cddfSDavid du Colombier 				COMMAND(port, SelectRegisterWindow, Wop);
10327dd7cddfSDavid du Colombier 				COMMAND(port, RxReset, 0);
10337dd7cddfSDavid du Colombier 				while(STATUS(port) & commandInProgress)
10347dd7cddfSDavid du Colombier 					;
10357dd7cddfSDavid du Colombier 				COMMAND(port, SetRxFilter, s);
10367dd7cddfSDavid du Colombier 				COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts);
10377dd7cddfSDavid du Colombier 				COMMAND(port, RxEnable, 0);
10387dd7cddfSDavid du Colombier 			}
10397dd7cddfSDavid du Colombier 
10407dd7cddfSDavid du Colombier 			status &= ~hostError;
10417dd7cddfSDavid du Colombier 		}
10427dd7cddfSDavid du Colombier 
10437dd7cddfSDavid du Colombier 		if(status & (transferInt|rxComplete)){
10447dd7cddfSDavid du Colombier 			receive(ether);
10457dd7cddfSDavid du Colombier 			status &= ~(transferInt|rxComplete);
10467dd7cddfSDavid du Colombier 		}
10477dd7cddfSDavid du Colombier 
10487dd7cddfSDavid du Colombier 		if(status & (upComplete)){
10497dd7cddfSDavid du Colombier 			COMMAND(port, AcknowledgeInterrupt, upComplete);
10507dd7cddfSDavid du Colombier 			receive905(ether);
10517dd7cddfSDavid du Colombier 			status &= ~upComplete;
10527dd7cddfSDavid du Colombier 			ctlr->upinterrupts++;
10537dd7cddfSDavid du Colombier 		}
10547dd7cddfSDavid du Colombier 
10557dd7cddfSDavid du Colombier 		if(status & txComplete){
10567dd7cddfSDavid du Colombier 			/*
10577dd7cddfSDavid du Colombier 			 * Pop the TxStatus stack, accumulating errors.
10587dd7cddfSDavid du Colombier 			 * Adjust the TX start threshold if there was an underrun.
10597dd7cddfSDavid du Colombier 			 * If there was a Jabber or Underrun error, reset
10607dd7cddfSDavid du Colombier 			 * the transmitter, taking care not to reset the dma logic
10617dd7cddfSDavid du Colombier 			 * as a busmaster receive may be in progress.
10627dd7cddfSDavid du Colombier 			 * For all conditions enable the transmitter.
10637dd7cddfSDavid du Colombier 			 */
106459cc4ca5SDavid du Colombier 			if(ctlr->busmaster == 2)
106559cc4ca5SDavid du Colombier 				txstatus = port+TxStatus905;
106659cc4ca5SDavid du Colombier 			else
106759cc4ca5SDavid du Colombier 				txstatus = port+TxStatus;
10687dd7cddfSDavid du Colombier 			s = 0;
10697dd7cddfSDavid du Colombier 			do{
107059cc4ca5SDavid du Colombier 				if(x = inb(txstatus))
107159cc4ca5SDavid du Colombier 					outb(txstatus, 0);
10727dd7cddfSDavid du Colombier 				s |= x;
10737dd7cddfSDavid du Colombier 			}while(STATUS(port) & txComplete);
10747dd7cddfSDavid du Colombier 
10757dd7cddfSDavid du Colombier 			if(s & txUnderrun){
10767dd7cddfSDavid du Colombier 				if(ctlr->dnenabled){
10777dd7cddfSDavid du Colombier 					while(inl(port+PktStatus) & dnInProg)
10787dd7cddfSDavid du Colombier 						;
10797dd7cddfSDavid du Colombier 				}
10807dd7cddfSDavid du Colombier 				COMMAND(port, SelectRegisterWindow, Wdiagnostic);
10817dd7cddfSDavid du Colombier 				while(ins(port+MediaStatus) & txInProg)
10827dd7cddfSDavid du Colombier 					;
10837dd7cddfSDavid du Colombier 				COMMAND(port, SelectRegisterWindow, Wop);
10847dd7cddfSDavid du Colombier 				if(ctlr->txthreshold < ETHERMAXTU)
10857dd7cddfSDavid du Colombier 					ctlr->txthreshold += ETHERMINTU;
10867dd7cddfSDavid du Colombier 			}
10877dd7cddfSDavid du Colombier 
10887dd7cddfSDavid du Colombier 			/*
10897dd7cddfSDavid du Colombier 			 * According to the manual, maxCollisions does not require
10907dd7cddfSDavid du Colombier 			 * a TxReset, merely a TxEnable. However, evidence points to
10917dd7cddfSDavid du Colombier 			 * it being necessary on the 3C905. The jury is still out.
10927dd7cddfSDavid du Colombier 			 * On busy or badly configured networks maxCollisions can
10937dd7cddfSDavid du Colombier 			 * happen frequently enough for messages to be annoying so
10947dd7cddfSDavid du Colombier 			 * keep quiet about them by popular request.
10957dd7cddfSDavid du Colombier 			 */
10967dd7cddfSDavid du Colombier 			if(s & (txJabber|txUnderrun|maxCollisions)){
10977dd7cddfSDavid du Colombier 				if(ctlr->busmaster == 0)
10987dd7cddfSDavid du Colombier 					COMMAND(port, TxReset, 0);
10997dd7cddfSDavid du Colombier 				else
11007dd7cddfSDavid du Colombier 					COMMAND(port, TxReset, (updnReset|dmaReset));
11017dd7cddfSDavid du Colombier 				while(STATUS(port) & commandInProgress)
11027dd7cddfSDavid du Colombier 					;
11037dd7cddfSDavid du Colombier 				COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts);
11047dd7cddfSDavid du Colombier 				if(ctlr->busmaster == 2)
11057dd7cddfSDavid du Colombier 					outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256));
11067dd7cddfSDavid du Colombier 				if(ctlr->dnenabled)
11077dd7cddfSDavid du Colombier 					status |= dnComplete;
11087dd7cddfSDavid du Colombier 			}
11097dd7cddfSDavid du Colombier 
11107dd7cddfSDavid du Colombier 			if(s & ~(txStatusComplete|maxCollisions))
11117dd7cddfSDavid du Colombier 				print("#l%d: txstatus 0x%uX, threshold %d\n",
11127dd7cddfSDavid du Colombier 			    		ether->ctlrno, s, ctlr->txthreshold);
11137dd7cddfSDavid du Colombier 			COMMAND(port, TxEnable, 0);
11147dd7cddfSDavid du Colombier 			ether->oerrs++;
11157dd7cddfSDavid du Colombier 			status &= ~txComplete;
11167dd7cddfSDavid du Colombier 			status |= txAvailable;
11177dd7cddfSDavid du Colombier 		}
11187dd7cddfSDavid du Colombier 
11197dd7cddfSDavid du Colombier 		if(status & txAvailable){
11207dd7cddfSDavid du Colombier 			COMMAND(port, AcknowledgeInterrupt, txAvailable);
11217dd7cddfSDavid du Colombier 			ctlr->txbusy = 0;
11227dd7cddfSDavid du Colombier 			txstart(ether);
11237dd7cddfSDavid du Colombier 			status &= ~txAvailable;
11247dd7cddfSDavid du Colombier 		}
11257dd7cddfSDavid du Colombier 
11267dd7cddfSDavid du Colombier 		if(status & dnComplete){
11277dd7cddfSDavid du Colombier 			COMMAND(port, AcknowledgeInterrupt, dnComplete);
11287dd7cddfSDavid du Colombier 			txstart905(ether);
11297dd7cddfSDavid du Colombier 			status &= ~dnComplete;
11307dd7cddfSDavid du Colombier 			ctlr->dninterrupts++;
11317dd7cddfSDavid du Colombier 		}
11327dd7cddfSDavid du Colombier 
11337dd7cddfSDavid du Colombier 		if(status & updateStats){
11347dd7cddfSDavid du Colombier 			statistics(ether);
11357dd7cddfSDavid du Colombier 			status &= ~updateStats;
11367dd7cddfSDavid du Colombier 		}
11377dd7cddfSDavid du Colombier 
11387dd7cddfSDavid du Colombier 		/*
11397dd7cddfSDavid du Colombier 		 * Currently, this shouldn't happen.
11407dd7cddfSDavid du Colombier 		 */
11417dd7cddfSDavid du Colombier 		if(status & rxEarly){
11427dd7cddfSDavid du Colombier 			COMMAND(port, AcknowledgeInterrupt, rxEarly);
11437dd7cddfSDavid du Colombier 			status &= ~rxEarly;
11447dd7cddfSDavid du Colombier 		}
11457dd7cddfSDavid du Colombier 
11467dd7cddfSDavid du Colombier 		/*
11477dd7cddfSDavid du Colombier 		 * Panic if there are any interrupts not dealt with.
11487dd7cddfSDavid du Colombier 		 */
11497dd7cddfSDavid du Colombier 		if(status & interruptMask)
11507dd7cddfSDavid du Colombier 			panic("#l%d: interrupt mask 0x%uX\n", ether->ctlrno, status);
11517dd7cddfSDavid du Colombier 
11527dd7cddfSDavid du Colombier 		COMMAND(port, AcknowledgeInterrupt, interruptLatch);
1153e31d734fSDavid du Colombier 		if(ctlr->cbfn != nil)
1154e31d734fSDavid du Colombier 			intrackcb(ctlr->cbfn);
11559a747e4fSDavid du Colombier 
115659cc4ca5SDavid du Colombier 	}while((status = STATUS(port)) & (interruptMask|interruptLatch));
115759cc4ca5SDavid du Colombier 
115859cc4ca5SDavid du Colombier 	if(ctlr->busmaster == 2)
11593ff48bf5SDavid du Colombier 		ctlr->timer[1] += inb(port+TIMER905) & 0xFF;
116059cc4ca5SDavid du Colombier 	else
11613ff48bf5SDavid du Colombier 		ctlr->timer[1] += inb(port+TIMER) & 0xFF;
11627dd7cddfSDavid du Colombier 
11637dd7cddfSDavid du Colombier 	COMMAND(port, SelectRegisterWindow, w);
11647dd7cddfSDavid du Colombier 	iunlock(&ctlr->wlock);
11657dd7cddfSDavid du Colombier }
11667dd7cddfSDavid du Colombier 
11677dd7cddfSDavid du Colombier static long
ifstat(Ether * ether,void * a,long n,ulong offset)11687dd7cddfSDavid du Colombier ifstat(Ether* ether, void* a, long n, ulong offset)
11697dd7cddfSDavid du Colombier {
11707dd7cddfSDavid du Colombier 	char *p;
11717dd7cddfSDavid du Colombier 	int len;
11727dd7cddfSDavid du Colombier 	Ctlr *ctlr;
11737dd7cddfSDavid du Colombier 
11747dd7cddfSDavid du Colombier 	if(n == 0)
11757dd7cddfSDavid du Colombier 		return 0;
11767dd7cddfSDavid du Colombier 
11777dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
11787dd7cddfSDavid du Colombier 
11797dd7cddfSDavid du Colombier 	ilock(&ctlr->wlock);
11807dd7cddfSDavid du Colombier 	statistics(ether);
11817dd7cddfSDavid du Colombier 	iunlock(&ctlr->wlock);
11827dd7cddfSDavid du Colombier 
11837dd7cddfSDavid du Colombier 	p = malloc(READSTR);
1184*aa72973aSDavid du Colombier 	if(p == nil)
1185*aa72973aSDavid du Colombier 		error(Enomem);
11867dd7cddfSDavid du Colombier 	len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts);
11873ff48bf5SDavid du Colombier 	len += snprint(p+len, READSTR-len, "bogusinterrupts: %lud\n", ctlr->bogusinterrupts);
118859cc4ca5SDavid du Colombier 	len += snprint(p+len, READSTR-len, "timer: %lud %lud\n",
118959cc4ca5SDavid du Colombier 		ctlr->timer[0], ctlr->timer[1]);
119059cc4ca5SDavid du Colombier 	len += snprint(p+len, READSTR-len, "carrierlost: %lud\n",
119159cc4ca5SDavid du Colombier 		ctlr->stats[CarrierLost]);
119259cc4ca5SDavid du Colombier 	len += snprint(p+len, READSTR-len, "sqeerrors: %lud\n",
119359cc4ca5SDavid du Colombier 		ctlr->stats[SqeErrors]);
119459cc4ca5SDavid du Colombier 	len += snprint(p+len, READSTR-len, "multiplecolls: %lud\n",
119559cc4ca5SDavid du Colombier 		ctlr->stats[MultipleColls]);
119659cc4ca5SDavid du Colombier 	len += snprint(p+len, READSTR-len, "singlecollframes: %lud\n",
119759cc4ca5SDavid du Colombier 		ctlr->stats[SingleCollFrames]);
119859cc4ca5SDavid du Colombier 	len += snprint(p+len, READSTR-len, "latecollisions: %lud\n",
119959cc4ca5SDavid du Colombier 		ctlr->stats[LateCollisions]);
120059cc4ca5SDavid du Colombier 	len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n",
120159cc4ca5SDavid du Colombier 		ctlr->stats[RxOverruns]);
120259cc4ca5SDavid du Colombier 	len += snprint(p+len, READSTR-len, "framesxmittedok: %lud\n",
120359cc4ca5SDavid du Colombier 		ctlr->stats[FramesXmittedOk]);
120459cc4ca5SDavid du Colombier 	len += snprint(p+len, READSTR-len, "framesrcvdok: %lud\n",
120559cc4ca5SDavid du Colombier 		ctlr->stats[FramesRcvdOk]);
120659cc4ca5SDavid du Colombier 	len += snprint(p+len, READSTR-len, "framesdeferred: %lud\n",
120759cc4ca5SDavid du Colombier 		ctlr->stats[FramesDeferred]);
120859cc4ca5SDavid du Colombier 	len += snprint(p+len, READSTR-len, "bytesrcvdok: %lud\n",
120959cc4ca5SDavid du Colombier 		ctlr->stats[BytesRcvdOk]);
121059cc4ca5SDavid du Colombier 	len += snprint(p+len, READSTR-len, "bytesxmittedok: %lud\n",
121159cc4ca5SDavid du Colombier 		ctlr->stats[BytesRcvdOk+1]);
12127dd7cddfSDavid du Colombier 
12137dd7cddfSDavid du Colombier 	if(ctlr->upenabled){
12147dd7cddfSDavid du Colombier 		if(ctlr->upqmax > ctlr->upqmaxhw)
12157dd7cddfSDavid du Colombier 			ctlr->upqmaxhw = ctlr->upqmax;
12167dd7cddfSDavid du Colombier 		len += snprint(p+len, READSTR-len, "up: q %lud i %lud m %d h %d s %lud\n",
12177dd7cddfSDavid du Colombier 			ctlr->upqueued, ctlr->upinterrupts,
12187dd7cddfSDavid du Colombier 			ctlr->upqmax, ctlr->upqmaxhw, ctlr->upstalls);
12197dd7cddfSDavid du Colombier 		ctlr->upqmax = 0;
12207dd7cddfSDavid du Colombier 	}
12217dd7cddfSDavid du Colombier 	if(ctlr->dnenabled){
12227dd7cddfSDavid du Colombier 		if(ctlr->dnqmax > ctlr->dnqmaxhw)
12237dd7cddfSDavid du Colombier 			ctlr->dnqmaxhw = ctlr->dnqmax;
12247dd7cddfSDavid du Colombier 		len += snprint(p+len, READSTR-len, "dn: q %lud i %lud m %d h %d\n",
12257dd7cddfSDavid du Colombier 			ctlr->dnqueued, ctlr->dninterrupts, ctlr->dnqmax, ctlr->dnqmaxhw);
12267dd7cddfSDavid du Colombier 		ctlr->dnqmax = 0;
12277dd7cddfSDavid du Colombier 	}
12287dd7cddfSDavid du Colombier 
12297dd7cddfSDavid du Colombier 	snprint(p+len, READSTR-len, "badssd: %lud\n", ctlr->stats[BytesRcvdOk+2]);
12307dd7cddfSDavid du Colombier 
12317dd7cddfSDavid du Colombier 	n = readstr(offset, a, n, p);
12327dd7cddfSDavid du Colombier 	free(p);
12337dd7cddfSDavid du Colombier 
12347dd7cddfSDavid du Colombier 	return n;
12357dd7cddfSDavid du Colombier }
12367dd7cddfSDavid du Colombier 
12377dd7cddfSDavid du Colombier static void
txrxreset(int port)12387dd7cddfSDavid du Colombier txrxreset(int port)
12397dd7cddfSDavid du Colombier {
12407dd7cddfSDavid du Colombier 	COMMAND(port, TxReset, 0);
12417dd7cddfSDavid du Colombier 	while(STATUS(port) & commandInProgress)
12427dd7cddfSDavid du Colombier 		;
12437dd7cddfSDavid du Colombier 	COMMAND(port, RxReset, 0);
12447dd7cddfSDavid du Colombier 	while(STATUS(port) & commandInProgress)
12457dd7cddfSDavid du Colombier 		;
12467dd7cddfSDavid du Colombier }
12477dd7cddfSDavid du Colombier 
12483ff48bf5SDavid du Colombier static Ctlr*
tcmadapter(int port,int irq,Pcidev * pcidev)12493ff48bf5SDavid du Colombier tcmadapter(int port, int irq, Pcidev* pcidev)
12507dd7cddfSDavid du Colombier {
12513ff48bf5SDavid du Colombier 	Ctlr *ctlr;
12527dd7cddfSDavid du Colombier 
12533ff48bf5SDavid du Colombier 	ctlr = malloc(sizeof(Ctlr));
1254*aa72973aSDavid du Colombier 	if(ctlr == nil)
1255*aa72973aSDavid du Colombier 		error(Enomem);
12563ff48bf5SDavid du Colombier 	ctlr->port = port;
12573ff48bf5SDavid du Colombier 	ctlr->irq = irq;
12583ff48bf5SDavid du Colombier 	ctlr->pcidev = pcidev;
1259e31d734fSDavid du Colombier 	ctlr->eepromcmd = EepromReadRegister;
12607dd7cddfSDavid du Colombier 
12613ff48bf5SDavid du Colombier 	if(ctlrhead != nil)
12623ff48bf5SDavid du Colombier 		ctlrtail->next = ctlr;
12633ff48bf5SDavid du Colombier 	else
12643ff48bf5SDavid du Colombier 		ctlrhead = ctlr;
12653ff48bf5SDavid du Colombier 	ctlrtail = ctlr;
12663ff48bf5SDavid du Colombier 
12673ff48bf5SDavid du Colombier 	return ctlr;
12687dd7cddfSDavid du Colombier }
12697dd7cddfSDavid du Colombier 
12707dd7cddfSDavid du Colombier /*
12717dd7cddfSDavid du Colombier  * Write two 0 bytes to identify the IDport and then reset the
12727dd7cddfSDavid du Colombier  * ID sequence. Then send the ID sequence to the card to get
12737dd7cddfSDavid du Colombier  * the card into command state.
12747dd7cddfSDavid du Colombier  */
12757dd7cddfSDavid du Colombier static void
idseq(void)12767dd7cddfSDavid du Colombier idseq(void)
12777dd7cddfSDavid du Colombier {
12787dd7cddfSDavid du Colombier 	int i;
12797dd7cddfSDavid du Colombier 	uchar al;
12807dd7cddfSDavid du Colombier 	static int reset, untag;
12817dd7cddfSDavid du Colombier 
12827dd7cddfSDavid du Colombier 	/*
12837dd7cddfSDavid du Colombier 	 * One time only:
12847dd7cddfSDavid du Colombier 	 *	reset any adapters listening
12857dd7cddfSDavid du Colombier 	 */
12867dd7cddfSDavid du Colombier 	if(reset == 0){
12877dd7cddfSDavid du Colombier 		outb(IDport, 0);
12887dd7cddfSDavid du Colombier 		outb(IDport, 0);
12897dd7cddfSDavid du Colombier 		outb(IDport, 0xC0);
12907dd7cddfSDavid du Colombier 		delay(20);
12917dd7cddfSDavid du Colombier 		reset = 1;
12927dd7cddfSDavid du Colombier 	}
12937dd7cddfSDavid du Colombier 
12947dd7cddfSDavid du Colombier 	outb(IDport, 0);
12957dd7cddfSDavid du Colombier 	outb(IDport, 0);
12967dd7cddfSDavid du Colombier 	for(al = 0xFF, i = 0; i < 255; i++){
12977dd7cddfSDavid du Colombier 		outb(IDport, al);
12987dd7cddfSDavid du Colombier 		if(al & 0x80){
12997dd7cddfSDavid du Colombier 			al <<= 1;
13007dd7cddfSDavid du Colombier 			al ^= 0xCF;
13017dd7cddfSDavid du Colombier 		}
13027dd7cddfSDavid du Colombier 		else
13037dd7cddfSDavid du Colombier 			al <<= 1;
13047dd7cddfSDavid du Colombier 	}
13057dd7cddfSDavid du Colombier 
13067dd7cddfSDavid du Colombier 	/*
13077dd7cddfSDavid du Colombier 	 * One time only:
13087dd7cddfSDavid du Colombier 	 *	write ID sequence to get the attention of all adapters;
13097dd7cddfSDavid du Colombier 	 *	untag all adapters.
13107dd7cddfSDavid du Colombier 	 * If a global reset is done here on all adapters it will confuse
13117dd7cddfSDavid du Colombier 	 * any ISA cards configured for EISA mode.
13127dd7cddfSDavid du Colombier 	 */
13137dd7cddfSDavid du Colombier 	if(untag == 0){
13147dd7cddfSDavid du Colombier 		outb(IDport, 0xD0);
13157dd7cddfSDavid du Colombier 		untag = 1;
13167dd7cddfSDavid du Colombier 	}
13177dd7cddfSDavid du Colombier }
13187dd7cddfSDavid du Colombier 
13197dd7cddfSDavid du Colombier static ulong
activate(void)13207dd7cddfSDavid du Colombier activate(void)
13217dd7cddfSDavid du Colombier {
13227dd7cddfSDavid du Colombier 	int i;
13237dd7cddfSDavid du Colombier 	ushort x, acr;
13247dd7cddfSDavid du Colombier 
13257dd7cddfSDavid du Colombier 	/*
13267dd7cddfSDavid du Colombier 	 * Do the little configuration dance:
13277dd7cddfSDavid du Colombier 	 *
13287dd7cddfSDavid du Colombier 	 * 2. write the ID sequence to get to command state.
13297dd7cddfSDavid du Colombier 	 */
13307dd7cddfSDavid du Colombier 	idseq();
13317dd7cddfSDavid du Colombier 
13327dd7cddfSDavid du Colombier 	/*
13337dd7cddfSDavid du Colombier 	 * 3. Read the Manufacturer ID from the EEPROM.
13347dd7cddfSDavid du Colombier 	 *    This is done by writing the IDPort with 0x87 (0x80
13357dd7cddfSDavid du Colombier 	 *    is the 'read EEPROM' command, 0x07 is the offset of
13367dd7cddfSDavid du Colombier 	 *    the Manufacturer ID field in the EEPROM).
13377dd7cddfSDavid du Colombier 	 *    The data comes back 1 bit at a time.
13387dd7cddfSDavid du Colombier 	 *    A delay seems necessary between reading the bits.
13397dd7cddfSDavid du Colombier 	 *
13407dd7cddfSDavid du Colombier 	 * If the ID doesn't match, there are no more adapters.
13417dd7cddfSDavid du Colombier 	 */
13427dd7cddfSDavid du Colombier 	outb(IDport, 0x87);
13437dd7cddfSDavid du Colombier 	delay(20);
13447dd7cddfSDavid du Colombier 	for(x = 0, i = 0; i < 16; i++){
13457dd7cddfSDavid du Colombier 		delay(20);
13467dd7cddfSDavid du Colombier 		x <<= 1;
13477dd7cddfSDavid du Colombier 		x |= inb(IDport) & 0x01;
13487dd7cddfSDavid du Colombier 	}
13497dd7cddfSDavid du Colombier 	if(x != 0x6D50)
13507dd7cddfSDavid du Colombier 		return 0;
13517dd7cddfSDavid du Colombier 
13527dd7cddfSDavid du Colombier 	/*
13537dd7cddfSDavid du Colombier 	 * 3. Read the Address Configuration from the EEPROM.
13547dd7cddfSDavid du Colombier 	 *    The Address Configuration field is at offset 0x08 in the EEPROM).
13557dd7cddfSDavid du Colombier 	 */
13567dd7cddfSDavid du Colombier 	outb(IDport, 0x88);
13577dd7cddfSDavid du Colombier 	for(acr = 0, i = 0; i < 16; i++){
13587dd7cddfSDavid du Colombier 		delay(20);
13597dd7cddfSDavid du Colombier 		acr <<= 1;
13607dd7cddfSDavid du Colombier 		acr |= inb(IDport) & 0x01;
13617dd7cddfSDavid du Colombier 	}
13627dd7cddfSDavid du Colombier 
13637dd7cddfSDavid du Colombier 	return (acr & 0x1F)*0x10 + 0x200;
13647dd7cddfSDavid du Colombier }
13657dd7cddfSDavid du Colombier 
13667dd7cddfSDavid du Colombier static void
tcm509isa(void)13677dd7cddfSDavid du Colombier tcm509isa(void)
13687dd7cddfSDavid du Colombier {
13697dd7cddfSDavid du Colombier 	int irq, port;
13707dd7cddfSDavid du Colombier 
13717dd7cddfSDavid du Colombier 	/*
13727dd7cddfSDavid du Colombier 	 * Attempt to activate all adapters. If adapter is set for
13737dd7cddfSDavid du Colombier 	 * EISA mode (0x3F0), tag it and ignore. Otherwise, activate
13747dd7cddfSDavid du Colombier 	 * it fully.
13757dd7cddfSDavid du Colombier 	 */
13767dd7cddfSDavid du Colombier 	while(port = activate()){
13773ff48bf5SDavid du Colombier 		if(ioalloc(port, 0x10, 0, "tcm509isa") < 0){
13783ff48bf5SDavid du Colombier 			print("tcm509isa: port 0x%uX in use\n", port);
13797dd7cddfSDavid du Colombier 			continue;
13803ff48bf5SDavid du Colombier 		}
13817dd7cddfSDavid du Colombier 
13827dd7cddfSDavid du Colombier 		/*
13837dd7cddfSDavid du Colombier 		 * 6. Tag the adapter so it won't respond in future.
13847dd7cddfSDavid du Colombier 		 */
13857dd7cddfSDavid du Colombier 		outb(IDport, 0xD1);
13867dd7cddfSDavid du Colombier 		if(port == 0x3F0){
13877dd7cddfSDavid du Colombier 			iofree(port);
13887dd7cddfSDavid du Colombier 			continue;
13897dd7cddfSDavid du Colombier 		}
13907dd7cddfSDavid du Colombier 
13917dd7cddfSDavid du Colombier 		/*
13927dd7cddfSDavid du Colombier 		 * 6. Activate the adapter by writing the Activate command
13937dd7cddfSDavid du Colombier 		 *    (0xFF).
13947dd7cddfSDavid du Colombier 		 */
13957dd7cddfSDavid du Colombier 		outb(IDport, 0xFF);
13967dd7cddfSDavid du Colombier 		delay(20);
13977dd7cddfSDavid du Colombier 
13987dd7cddfSDavid du Colombier 		/*
13997dd7cddfSDavid du Colombier 		 * 8. Can now talk to the adapter's I/O base addresses.
14007dd7cddfSDavid du Colombier 		 *    Use the I/O base address from the acr just read.
14017dd7cddfSDavid du Colombier 		 *
14027dd7cddfSDavid du Colombier 		 *    Enable the adapter and clear out any lingering status
14037dd7cddfSDavid du Colombier 		 *    and interrupts.
14047dd7cddfSDavid du Colombier 		 */
14057dd7cddfSDavid du Colombier 		while(STATUS(port) & commandInProgress)
14067dd7cddfSDavid du Colombier 			;
14077dd7cddfSDavid du Colombier 		COMMAND(port, SelectRegisterWindow, Wsetup);
14087dd7cddfSDavid du Colombier 		outs(port+ConfigControl, Ena);
14097dd7cddfSDavid du Colombier 
14107dd7cddfSDavid du Colombier 		txrxreset(port);
14117dd7cddfSDavid du Colombier 		COMMAND(port, AcknowledgeInterrupt, 0xFF);
14127dd7cddfSDavid du Colombier 
14137dd7cddfSDavid du Colombier 		irq = (ins(port+ResourceConfig)>>12) & 0x0F;
14143ff48bf5SDavid du Colombier 		tcmadapter(port, irq, nil);
14157dd7cddfSDavid du Colombier 	}
14167dd7cddfSDavid du Colombier }
14177dd7cddfSDavid du Colombier 
14187dd7cddfSDavid du Colombier static void
tcm5XXeisa(void)14197dd7cddfSDavid du Colombier tcm5XXeisa(void)
14207dd7cddfSDavid du Colombier {
14217dd7cddfSDavid du Colombier 	ushort x;
14227dd7cddfSDavid du Colombier 	int irq, port, slot;
14237dd7cddfSDavid du Colombier 
14247dd7cddfSDavid du Colombier 	/*
14257dd7cddfSDavid du Colombier 	 * Check if this is an EISA machine.
14267dd7cddfSDavid du Colombier 	 * If not, nothing to do.
14277dd7cddfSDavid du Colombier 	 */
14287dd7cddfSDavid du Colombier 	if(strncmp((char*)KADDR(0xFFFD9), "EISA", 4))
14297dd7cddfSDavid du Colombier 		return;
14307dd7cddfSDavid du Colombier 
14317dd7cddfSDavid du Colombier 	/*
14327dd7cddfSDavid du Colombier 	 * Continue through the EISA slots looking for a match on both
14337dd7cddfSDavid du Colombier 	 * 3COM as the manufacturer and 3C579-* or 3C59[27]-* as the product.
14347dd7cddfSDavid du Colombier 	 * If an adapter is found, select window 0, enable it and clear
14357dd7cddfSDavid du Colombier 	 * out any lingering status and interrupts.
14367dd7cddfSDavid du Colombier 	 */
14377dd7cddfSDavid du Colombier 	for(slot = 1; slot < MaxEISA; slot++){
14387dd7cddfSDavid du Colombier 		port = slot*0x1000;
14393ff48bf5SDavid du Colombier 		if(ioalloc(port, 0x1000, 0, "tcm5XXeisa") < 0){
14403ff48bf5SDavid du Colombier 			print("tcm5XXeisa: port 0x%uX in use\n", port);
14417dd7cddfSDavid du Colombier 			continue;
14423ff48bf5SDavid du Colombier 		}
14437dd7cddfSDavid du Colombier 		if(ins(port+0xC80+ManufacturerID) != 0x6D50){
14447dd7cddfSDavid du Colombier 			iofree(port);
14457dd7cddfSDavid du Colombier 			continue;
14467dd7cddfSDavid du Colombier 		}
14477dd7cddfSDavid du Colombier 		x = ins(port+0xC80+ProductID);
14487dd7cddfSDavid du Colombier 		if((x & 0xF0FF) != 0x9050 && (x & 0xFF00) != 0x5900){
14497dd7cddfSDavid du Colombier 			iofree(port);
14507dd7cddfSDavid du Colombier 			continue;
14517dd7cddfSDavid du Colombier 		}
14527dd7cddfSDavid du Colombier 
14537dd7cddfSDavid du Colombier 		COMMAND(port, SelectRegisterWindow, Wsetup);
14547dd7cddfSDavid du Colombier 		outs(port+ConfigControl, Ena);
14557dd7cddfSDavid du Colombier 
14567dd7cddfSDavid du Colombier 		txrxreset(port);
14577dd7cddfSDavid du Colombier 		COMMAND(port, AcknowledgeInterrupt, 0xFF);
14587dd7cddfSDavid du Colombier 
14597dd7cddfSDavid du Colombier 		irq = (ins(port+ResourceConfig)>>12) & 0x0F;
14603ff48bf5SDavid du Colombier 		tcmadapter(port, irq, nil);
14617dd7cddfSDavid du Colombier 	}
14627dd7cddfSDavid du Colombier }
14637dd7cddfSDavid du Colombier 
14647dd7cddfSDavid du Colombier static void
tcm59Xpci(void)14657dd7cddfSDavid du Colombier tcm59Xpci(void)
14667dd7cddfSDavid du Colombier {
14677dd7cddfSDavid du Colombier 	Pcidev *p;
1468e31d734fSDavid du Colombier 	Ctlr *ctlr;
14697dd7cddfSDavid du Colombier 	int irq, port;
14707dd7cddfSDavid du Colombier 
14717dd7cddfSDavid du Colombier 	p = nil;
14727dd7cddfSDavid du Colombier 	while(p = pcimatch(p, 0x10B7, 0)){
1473e31d734fSDavid du Colombier 		if(p->ccrb != 0x02 || p->ccru != 0)
1474e31d734fSDavid du Colombier 			continue;
14759a747e4fSDavid du Colombier 		/*
14769a747e4fSDavid du Colombier 		 * Not prepared to deal with memory-mapped
14779a747e4fSDavid du Colombier 		 * devices yet.
14789a747e4fSDavid du Colombier 		 */
14799a747e4fSDavid du Colombier 		if(!(p->mem[0].bar & 0x01))
14807dd7cddfSDavid du Colombier 			continue;
14819a747e4fSDavid du Colombier 		port = p->mem[0].bar & ~0x01;
14829a747e4fSDavid du Colombier 		if((port = ioalloc((port == 0)? -1: port,  p->mem[0].size,
14833ff48bf5SDavid du Colombier 					  0, "tcm59Xpci")) < 0){
14843ff48bf5SDavid du Colombier 			print("tcm59Xpci: port 0x%uX in use\n", port);
14859a747e4fSDavid du Colombier 			continue;
14863ff48bf5SDavid du Colombier 		}
14877dd7cddfSDavid du Colombier 		irq = p->intl;
14887dd7cddfSDavid du Colombier 
1489e31d734fSDavid du Colombier 		txrxreset(port);
1490e31d734fSDavid du Colombier 		COMMAND(port, AcknowledgeInterrupt, 0xFF);
1491e31d734fSDavid du Colombier 
1492e31d734fSDavid du Colombier 		ctlr = tcmadapter(port, irq, p);
1493e31d734fSDavid du Colombier 		switch(p->did){
1494e31d734fSDavid du Colombier 		default:
1495e31d734fSDavid du Colombier 			break;
1496e31d734fSDavid du Colombier 		case 0x5157:
1497e31d734fSDavid du Colombier 			ctlr->eepromcmd = EepromRead8bRegister;
14984de34a7eSDavid du Colombier 			ctlr->cbfnpa = p->mem[2].bar&~0x0F;
14994de34a7eSDavid du Colombier 			ctlr->cbfn = vmap(p->mem[2].bar&~0x0F, p->mem[2].size);
1500e31d734fSDavid du Colombier 			break;
1501e31d734fSDavid du Colombier 		case 0x6056:
1502e31d734fSDavid du Colombier 			ctlr->eepromcmd = EepromReadOffRegister;
15034de34a7eSDavid du Colombier 			ctlr->cbfnpa = p->mem[2].bar&~0x0F;
15044de34a7eSDavid du Colombier 			ctlr->cbfn = vmap(p->mem[2].bar&~0x0F, p->mem[2].size);
1505e31d734fSDavid du Colombier 			break;
1506e31d734fSDavid du Colombier 		}
15077dd7cddfSDavid du Colombier 		pcisetbme(p);
15087dd7cddfSDavid du Colombier 	}
15097dd7cddfSDavid du Colombier }
15107dd7cddfSDavid du Colombier 
15117dd7cddfSDavid du Colombier static char* tcmpcmcia[] = {
15127dd7cddfSDavid du Colombier 	"3C589",			/* 3COM 589[ABCD] */
15137dd7cddfSDavid du Colombier 	"3C562",			/* 3COM 562 */
15147dd7cddfSDavid du Colombier 	"589E",				/* 3COM Megahertz 589E */
15157dd7cddfSDavid du Colombier 	nil,
15167dd7cddfSDavid du Colombier };
15177dd7cddfSDavid du Colombier 
15183ff48bf5SDavid du Colombier static Ctlr*
tcm5XXpcmcia(Ether * ether)15197dd7cddfSDavid du Colombier tcm5XXpcmcia(Ether* ether)
15207dd7cddfSDavid du Colombier {
15217dd7cddfSDavid du Colombier 	int i;
15223ff48bf5SDavid du Colombier 	Ctlr *ctlr;
15233ff48bf5SDavid du Colombier 
15243ff48bf5SDavid du Colombier 	if(ether->type == nil)
15253ff48bf5SDavid du Colombier 		return nil;
15267dd7cddfSDavid du Colombier 
15277dd7cddfSDavid du Colombier 	for(i = 0; tcmpcmcia[i] != nil; i++){
15283ff48bf5SDavid du Colombier 		if(cistrcmp(ether->type, tcmpcmcia[i]))
15293ff48bf5SDavid du Colombier 			continue;
15303ff48bf5SDavid du Colombier 		ctlr = tcmadapter(ether->port, ether->irq, nil);
15313ff48bf5SDavid du Colombier 		ctlr->active = 1;
15323ff48bf5SDavid du Colombier 		return ctlr;
15337dd7cddfSDavid du Colombier 	}
1534b985bfb9SDavid du Colombier 
15353ff48bf5SDavid du Colombier 	return nil;
15367dd7cddfSDavid du Colombier }
15377dd7cddfSDavid du Colombier 
15387dd7cddfSDavid du Colombier static void
setxcvr(Ctlr * ctlr,int xcvr)15393ff48bf5SDavid du Colombier setxcvr(Ctlr* ctlr, int xcvr)
15407dd7cddfSDavid du Colombier {
15413ff48bf5SDavid du Colombier 	int port, x;
15427dd7cddfSDavid du Colombier 
15433ff48bf5SDavid du Colombier 	port = ctlr->port;
15443ff48bf5SDavid du Colombier 	if(ctlr->rxstatus9){
15457dd7cddfSDavid du Colombier 		COMMAND(port, SelectRegisterWindow, Wsetup);
15467dd7cddfSDavid du Colombier 		x = ins(port+AddressConfig) & ~xcvrMask9;
15477dd7cddfSDavid du Colombier 		x |= (xcvr>>20)<<14;
15487dd7cddfSDavid du Colombier 		outs(port+AddressConfig, x);
15497dd7cddfSDavid du Colombier 	}
15507dd7cddfSDavid du Colombier 	else{
15517dd7cddfSDavid du Colombier 		COMMAND(port, SelectRegisterWindow, Wfifo);
15527dd7cddfSDavid du Colombier 		x = inl(port+InternalConfig) & ~xcvrMask;
15537dd7cddfSDavid du Colombier 		x |= xcvr;
15547dd7cddfSDavid du Colombier 		outl(port+InternalConfig, x);
15557dd7cddfSDavid du Colombier 	}
15567dd7cddfSDavid du Colombier 
15577dd7cddfSDavid du Colombier 	txrxreset(port);
15587dd7cddfSDavid du Colombier }
15597dd7cddfSDavid du Colombier 
15607dd7cddfSDavid du Colombier static void
setfullduplex(int port)15617dd7cddfSDavid du Colombier setfullduplex(int port)
15627dd7cddfSDavid du Colombier {
15637dd7cddfSDavid du Colombier 	int x;
15647dd7cddfSDavid du Colombier 
15657dd7cddfSDavid du Colombier 	COMMAND(port, SelectRegisterWindow, Wfifo);
15667dd7cddfSDavid du Colombier 	x = ins(port+MacControl);
15677dd7cddfSDavid du Colombier 	outs(port+MacControl, fullDuplexEnable|x);
15687dd7cddfSDavid du Colombier 
15697dd7cddfSDavid du Colombier 	txrxreset(port);
15707dd7cddfSDavid du Colombier }
15717dd7cddfSDavid du Colombier 
15727dd7cddfSDavid du Colombier static int
miimdi(int port,int n)15737dd7cddfSDavid du Colombier miimdi(int port, int n)
15747dd7cddfSDavid du Colombier {
15757dd7cddfSDavid du Colombier 	int data, i;
15767dd7cddfSDavid du Colombier 
15777dd7cddfSDavid du Colombier 	/*
15787dd7cddfSDavid du Colombier 	 * Read n bits from the MII Management Register.
15797dd7cddfSDavid du Colombier 	 */
15807dd7cddfSDavid du Colombier 	data = 0;
15817dd7cddfSDavid du Colombier 	for(i = n-1; i >= 0; i--){
15827dd7cddfSDavid du Colombier 		if(ins(port) & mgmtData)
15837dd7cddfSDavid du Colombier 			data |= (1<<i);
15847dd7cddfSDavid du Colombier 		microdelay(1);
15857dd7cddfSDavid du Colombier 		outs(port, mgmtClk);
15867dd7cddfSDavid du Colombier 		microdelay(1);
15877dd7cddfSDavid du Colombier 		outs(port, 0);
15887dd7cddfSDavid du Colombier 		microdelay(1);
15897dd7cddfSDavid du Colombier 	}
15907dd7cddfSDavid du Colombier 
15917dd7cddfSDavid du Colombier 	return data;
15927dd7cddfSDavid du Colombier }
15937dd7cddfSDavid du Colombier 
15947dd7cddfSDavid du Colombier static void
miimdo(int port,int bits,int n)15957dd7cddfSDavid du Colombier miimdo(int port, int bits, int n)
15967dd7cddfSDavid du Colombier {
15977dd7cddfSDavid du Colombier 	int i, mdo;
15987dd7cddfSDavid du Colombier 
15997dd7cddfSDavid du Colombier 	/*
16007dd7cddfSDavid du Colombier 	 * Write n bits to the MII Management Register.
16017dd7cddfSDavid du Colombier 	 */
16027dd7cddfSDavid du Colombier 	for(i = n-1; i >= 0; i--){
16037dd7cddfSDavid du Colombier 		if(bits & (1<<i))
16047dd7cddfSDavid du Colombier 			mdo = mgmtDir|mgmtData;
16057dd7cddfSDavid du Colombier 		else
16067dd7cddfSDavid du Colombier 			mdo = mgmtDir;
16077dd7cddfSDavid du Colombier 		outs(port, mdo);
16087dd7cddfSDavid du Colombier 		microdelay(1);
16097dd7cddfSDavid du Colombier 		outs(port, mdo|mgmtClk);
16107dd7cddfSDavid du Colombier 		microdelay(1);
16117dd7cddfSDavid du Colombier 		outs(port, mdo);
16127dd7cddfSDavid du Colombier 		microdelay(1);
16137dd7cddfSDavid du Colombier 	}
16147dd7cddfSDavid du Colombier }
16157dd7cddfSDavid du Colombier 
16167dd7cddfSDavid du Colombier static int
miir(int port,int phyad,int regad)16177dd7cddfSDavid du Colombier miir(int port, int phyad, int regad)
16187dd7cddfSDavid du Colombier {
16197dd7cddfSDavid du Colombier 	int data, w;
16207dd7cddfSDavid du Colombier 
16217dd7cddfSDavid du Colombier 	w = (STATUS(port)>>13) & 0x07;
16227dd7cddfSDavid du Colombier 	COMMAND(port, SelectRegisterWindow, Wdiagnostic);
16237dd7cddfSDavid du Colombier 	port += PhysicalMgmt;
16247dd7cddfSDavid du Colombier 
16257dd7cddfSDavid du Colombier 	/*
16267dd7cddfSDavid du Colombier 	 * Preamble;
16277dd7cddfSDavid du Colombier 	 * ST+OP+PHYAD+REGAD;
16287dd7cddfSDavid du Colombier 	 * TA + 16 data bits.
16297dd7cddfSDavid du Colombier 	 */
16307dd7cddfSDavid du Colombier 	miimdo(port, 0xFFFFFFFF, 32);
16317dd7cddfSDavid du Colombier 	miimdo(port, 0x1800|(phyad<<5)|regad, 14);
16327dd7cddfSDavid du Colombier 	data = miimdi(port, 18);
16337dd7cddfSDavid du Colombier 
16347dd7cddfSDavid du Colombier 	port -= PhysicalMgmt;
16357dd7cddfSDavid du Colombier 	COMMAND(port, SelectRegisterWindow, w);
16367dd7cddfSDavid du Colombier 
16377dd7cddfSDavid du Colombier 	if(data & 0x10000)
16387dd7cddfSDavid du Colombier 		return -1;
16397dd7cddfSDavid du Colombier 
16407dd7cddfSDavid du Colombier 	return data & 0xFFFF;
16417dd7cddfSDavid du Colombier }
16427dd7cddfSDavid du Colombier 
1643e31d734fSDavid du Colombier static int
scanphy(int port)16447dd7cddfSDavid du Colombier scanphy(int port)
16457dd7cddfSDavid du Colombier {
16467dd7cddfSDavid du Colombier 	int i, x;
16477dd7cddfSDavid du Colombier 
16487dd7cddfSDavid du Colombier 	for(i = 0; i < 32; i++){
16497dd7cddfSDavid du Colombier 		if((x = miir(port, i, 2)) == -1 || x == 0)
16507dd7cddfSDavid du Colombier 			continue;
16517dd7cddfSDavid du Colombier 		x <<= 6;
16527dd7cddfSDavid du Colombier 		x |= miir(port, i, 3)>>10;
16537dd7cddfSDavid du Colombier 		XCVRDEBUG("phy%d: oui %uX reg1 %uX\n", i, x, miir(port, i, 1));
16547dd7cddfSDavid du Colombier 		USED(x);
1655e31d734fSDavid du Colombier 
1656e31d734fSDavid du Colombier 		return i;
16577dd7cddfSDavid du Colombier 	}
1658e31d734fSDavid du Colombier 	return 24;
16597dd7cddfSDavid du Colombier }
16607dd7cddfSDavid du Colombier 
16617dd7cddfSDavid du Colombier static struct {
16627dd7cddfSDavid du Colombier 	char *name;
16637dd7cddfSDavid du Colombier 	int avail;
16647dd7cddfSDavid du Colombier 	int xcvr;
16657dd7cddfSDavid du Colombier } media[] = {
16667dd7cddfSDavid du Colombier 	"10BaseT",	base10TAvailable,	xcvr10BaseT,
16677dd7cddfSDavid du Colombier 	"10Base2",	coaxAvailable,		xcvr10Base2,
16687dd7cddfSDavid du Colombier 	"100BaseTX",	baseTXAvailable,	xcvr100BaseTX,
16697dd7cddfSDavid du Colombier 	"100BaseFX",	baseFXAvailable,	xcvr100BaseFX,
16707dd7cddfSDavid du Colombier 	"aui",		auiAvailable,		xcvrAui,
16717dd7cddfSDavid du Colombier 	"mii",		miiConnector,		xcvrMii
16727dd7cddfSDavid du Colombier };
16737dd7cddfSDavid du Colombier 
16747dd7cddfSDavid du Colombier static int
autoselect(Ctlr * ctlr)16753ff48bf5SDavid du Colombier autoselect(Ctlr* ctlr)
16767dd7cddfSDavid du Colombier {
16773ff48bf5SDavid du Colombier 	int media, port, x;
16787dd7cddfSDavid du Colombier 
16797dd7cddfSDavid du Colombier 	/*
16807dd7cddfSDavid du Colombier 	 * Pathetic attempt at automatic media selection.
16817dd7cddfSDavid du Colombier 	 * Really just to get the Fast Etherlink 10BASE-T/100BASE-TX
16827dd7cddfSDavid du Colombier 	 * cards operational.
16837dd7cddfSDavid du Colombier 	 * It's a bonus if it works for anything else.
16847dd7cddfSDavid du Colombier 	 */
16853ff48bf5SDavid du Colombier 	port = ctlr->port;
16863ff48bf5SDavid du Colombier 	if(ctlr->rxstatus9){
16877dd7cddfSDavid du Colombier 		COMMAND(port, SelectRegisterWindow, Wsetup);
16887dd7cddfSDavid du Colombier 		x = ins(port+ConfigControl);
16897dd7cddfSDavid du Colombier 		media = 0;
16907dd7cddfSDavid du Colombier 		if(x & base10TAvailable9)
16917dd7cddfSDavid du Colombier 			media |= base10TAvailable;
16927dd7cddfSDavid du Colombier 		if(x & coaxAvailable9)
16937dd7cddfSDavid du Colombier 			media |= coaxAvailable;
16947dd7cddfSDavid du Colombier 		if(x & auiAvailable9)
16957dd7cddfSDavid du Colombier 			media |= auiAvailable;
16967dd7cddfSDavid du Colombier 	}
16977dd7cddfSDavid du Colombier 	else{
16987dd7cddfSDavid du Colombier 		COMMAND(port, SelectRegisterWindow, Wfifo);
16997dd7cddfSDavid du Colombier 		media = ins(port+ResetOptions);
17007dd7cddfSDavid du Colombier 	}
17017dd7cddfSDavid du Colombier 	XCVRDEBUG("autoselect: media %uX\n", media);
17027dd7cddfSDavid du Colombier 
17037dd7cddfSDavid du Colombier 	if(media & miiConnector)
17047dd7cddfSDavid du Colombier 		return xcvrMii;
17057dd7cddfSDavid du Colombier 
17067dd7cddfSDavid du Colombier 	COMMAND(port, SelectRegisterWindow, Wdiagnostic);
17077dd7cddfSDavid du Colombier 	XCVRDEBUG("autoselect: media status %uX\n", ins(port+MediaStatus));
17087dd7cddfSDavid du Colombier 
17097dd7cddfSDavid du Colombier 	if(media & baseTXAvailable){
17107dd7cddfSDavid du Colombier 		/*
17117dd7cddfSDavid du Colombier 		 * Must have InternalConfig register.
17127dd7cddfSDavid du Colombier 		 */
17133ff48bf5SDavid du Colombier 		setxcvr(ctlr, xcvr100BaseTX);
17147dd7cddfSDavid du Colombier 
17157dd7cddfSDavid du Colombier 		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
17167dd7cddfSDavid du Colombier 		x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable);
17177dd7cddfSDavid du Colombier 		outs(port+MediaStatus, linkBeatEnable|x);
17187dd7cddfSDavid du Colombier 		delay(10);
17197dd7cddfSDavid du Colombier 
17207dd7cddfSDavid du Colombier 		if(ins(port+MediaStatus) & linkBeatDetect)
17217dd7cddfSDavid du Colombier 			return xcvr100BaseTX;
17227dd7cddfSDavid du Colombier 		outs(port+MediaStatus, x);
17237dd7cddfSDavid du Colombier 	}
17247dd7cddfSDavid du Colombier 
17257dd7cddfSDavid du Colombier 	if(media & base10TAvailable){
17263ff48bf5SDavid du Colombier 		setxcvr(ctlr, xcvr10BaseT);
17277dd7cddfSDavid du Colombier 
17287dd7cddfSDavid du Colombier 		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
17297dd7cddfSDavid du Colombier 		x = ins(port+MediaStatus) & ~dcConverterEnabled;
17307dd7cddfSDavid du Colombier 		outs(port+MediaStatus, linkBeatEnable|jabberGuardEnable|x);
17317dd7cddfSDavid du Colombier 		delay(100);
17327dd7cddfSDavid du Colombier 
17337dd7cddfSDavid du Colombier 		XCVRDEBUG("autoselect: 10BaseT media status %uX\n", ins(port+MediaStatus));
17347dd7cddfSDavid du Colombier 		if(ins(port+MediaStatus) & linkBeatDetect)
17357dd7cddfSDavid du Colombier 			return xcvr10BaseT;
17367dd7cddfSDavid du Colombier 		outs(port+MediaStatus, x);
17377dd7cddfSDavid du Colombier 	}
17387dd7cddfSDavid du Colombier 
17397dd7cddfSDavid du Colombier 	/*
17407dd7cddfSDavid du Colombier 	 * Botch.
17417dd7cddfSDavid du Colombier 	 */
17427dd7cddfSDavid du Colombier 	return autoSelect;
17437dd7cddfSDavid du Colombier }
17447dd7cddfSDavid du Colombier 
17457dd7cddfSDavid du Colombier static int
eepromdata(Ctlr * ctlr,int offset)17463ff48bf5SDavid du Colombier eepromdata(Ctlr* ctlr, int offset)
17477dd7cddfSDavid du Colombier {
17483ff48bf5SDavid du Colombier 	int port;
17493ff48bf5SDavid du Colombier 
17503ff48bf5SDavid du Colombier 	port = ctlr->port;
17513ff48bf5SDavid du Colombier 
17527dd7cddfSDavid du Colombier 	COMMAND(port, SelectRegisterWindow, Wsetup);
17537dd7cddfSDavid du Colombier 	while(EEPROMBUSY(port))
17547dd7cddfSDavid du Colombier 		;
1755e31d734fSDavid du Colombier 	EEPROMCMD(port, ctlr->eepromcmd, offset);
17567dd7cddfSDavid du Colombier 	while(EEPROMBUSY(port))
17577dd7cddfSDavid du Colombier 		;
17587dd7cddfSDavid du Colombier 	return EEPROMDATA(port);
17597dd7cddfSDavid du Colombier }
17607dd7cddfSDavid du Colombier 
17610809e9a7SDavid du Colombier static void
resetctlr(Ctlr * ctlr)17620809e9a7SDavid du Colombier resetctlr(Ctlr *ctlr)
17630809e9a7SDavid du Colombier {
17640809e9a7SDavid du Colombier 	int x, port = ctlr->port;
17650809e9a7SDavid du Colombier 
17660809e9a7SDavid du Colombier 	txrxreset(port);
17670809e9a7SDavid du Colombier 	x = ins(port+ResetOp905B);
17680809e9a7SDavid du Colombier 	XCVRDEBUG("905[BC] reset ops 0x%uX\n", x);
17690809e9a7SDavid du Colombier 	x &= ~0x4010;
17700809e9a7SDavid du Colombier 	if(ctlr->did == 0x5157){
17710809e9a7SDavid du Colombier 		x |= 0x0010;			/* Invert LED */
17720809e9a7SDavid du Colombier 		outs(port+ResetOp905B, x);
17730809e9a7SDavid du Colombier 	}
17740809e9a7SDavid du Colombier 	if(ctlr->did == 0x6056){
17750809e9a7SDavid du Colombier 		x |= 0x4000;
17760809e9a7SDavid du Colombier 		outs(port+ResetOp905B, x);
17770809e9a7SDavid du Colombier 
17780809e9a7SDavid du Colombier 		COMMAND(port, SelectRegisterWindow, Wsetup);
17790809e9a7SDavid du Colombier 		outs(port, 0x0800);
17800809e9a7SDavid du Colombier 	}
17810809e9a7SDavid du Colombier }
17820809e9a7SDavid du Colombier 
17830809e9a7SDavid du Colombier static void
shutdown(Ether * ether)17840809e9a7SDavid du Colombier shutdown(Ether *ether)
17850809e9a7SDavid du Colombier {
17860809e9a7SDavid du Colombier print("etherelnk3 shutting down\n");
17870809e9a7SDavid du Colombier 	resetctlr(ether->ctlr);
17880809e9a7SDavid du Colombier }
17890809e9a7SDavid du Colombier 
17907dd7cddfSDavid du Colombier int
etherelnk3reset(Ether * ether)17917dd7cddfSDavid du Colombier etherelnk3reset(Ether* ether)
17927dd7cddfSDavid du Colombier {
17937dd7cddfSDavid du Colombier 	char *p;
17943ff48bf5SDavid du Colombier 	Ctlr *ctlr;
17953ff48bf5SDavid du Colombier 	uchar ea[Eaddrlen];
17963ff48bf5SDavid du Colombier 	static int scandone;
17973ff48bf5SDavid du Colombier 	int anar, anlpar, i, j, phyaddr, phystat, port, timeo, x;
17987dd7cddfSDavid du Colombier 
17997dd7cddfSDavid du Colombier 	/*
18007dd7cddfSDavid du Colombier 	 * Scan for adapter on PCI, EISA and finally
18017dd7cddfSDavid du Colombier 	 * using the little ISA configuration dance.
18027dd7cddfSDavid du Colombier 	 */
18037dd7cddfSDavid du Colombier 	if(scandone == 0){
18047dd7cddfSDavid du Colombier 		tcm59Xpci();
18057dd7cddfSDavid du Colombier 		tcm5XXeisa();
18067dd7cddfSDavid du Colombier 		tcm509isa();
18077dd7cddfSDavid du Colombier 		scandone = 1;
18087dd7cddfSDavid du Colombier 	}
18097dd7cddfSDavid du Colombier 
18107dd7cddfSDavid du Colombier 	/*
18117dd7cddfSDavid du Colombier 	 * Any adapter matches if no ether->port is supplied,
18127dd7cddfSDavid du Colombier 	 * otherwise the ports must match.
18137dd7cddfSDavid du Colombier 	 */
18143ff48bf5SDavid du Colombier 	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
18153ff48bf5SDavid du Colombier 		if(ctlr->active)
18169a747e4fSDavid du Colombier 			continue;
18173ff48bf5SDavid du Colombier 		if(ether->port == 0 || ether->port == ctlr->port){
18183ff48bf5SDavid du Colombier 			ctlr->active = 1;
18197dd7cddfSDavid du Colombier 			break;
18207dd7cddfSDavid du Colombier 		}
18217dd7cddfSDavid du Colombier 	}
18223ff48bf5SDavid du Colombier 	if(ctlr == nil && (ctlr = tcm5XXpcmcia(ether)) == 0)
18237dd7cddfSDavid du Colombier 		return -1;
18247dd7cddfSDavid du Colombier 
18253ff48bf5SDavid du Colombier 	ether->ctlr = ctlr;
18263ff48bf5SDavid du Colombier 	port = ctlr->port;
18273ff48bf5SDavid du Colombier 	ether->port = port;
18283ff48bf5SDavid du Colombier 	ether->irq = ctlr->irq;
18293ff48bf5SDavid du Colombier 	if(ctlr->pcidev != nil)
18303ff48bf5SDavid du Colombier 		ether->tbdf = ctlr->pcidev->tbdf;
18313ff48bf5SDavid du Colombier 	else
18323ff48bf5SDavid du Colombier 		ether->tbdf = BUSUNKNOWN;
18333ff48bf5SDavid du Colombier 
18347dd7cddfSDavid du Colombier 	/*
18357dd7cddfSDavid du Colombier 	 * Read the DeviceID from the EEPROM, it's at offset 0x03,
18367dd7cddfSDavid du Colombier 	 * and do something depending on capabilities.
18377dd7cddfSDavid du Colombier 	 */
18383ff48bf5SDavid du Colombier 	switch(ctlr->did = eepromdata(ctlr, 0x03)){
18399a747e4fSDavid du Colombier 	case 0x5157:		/* 3C575 Cyclone */
1840e31d734fSDavid du Colombier 	case 0x6056:
18413ff48bf5SDavid du Colombier 		/*FALLTHROUGH*/
18423ff48bf5SDavid du Colombier 	case 0x4500:		/* 3C450 HomePNA Tornado */
18433ff48bf5SDavid du Colombier 	case 0x7646:		/* 3CSOHO100-TX */
18443ff48bf5SDavid du Colombier 	case 0x9055:		/* 3C905B-TX */
18453ff48bf5SDavid du Colombier 	case 0x9200:		/* 3C905C-TX */
184639734e7eSDavid du Colombier 	case 0x9201:		/* 3C920 */
18471805eb57SDavid du Colombier 	case 0x9805:		/* 3C9805: 3C980-TX Python-T 10/100baseTX */
18483ff48bf5SDavid du Colombier 		/*FALLTHROUGH*/
18493ff48bf5SDavid du Colombier 	case 0x9000:		/* 3C900-TPO */
18503ff48bf5SDavid du Colombier 	case 0x9001:		/* 3C900-COMBO */
18513ff48bf5SDavid du Colombier 	case 0x9005:		/* 3C900B-COMBO */
18523ff48bf5SDavid du Colombier 	case 0x9050:		/* 3C905-TX */
18533ff48bf5SDavid du Colombier 	case 0x9051:		/* 3C905-T4 */
18547dd7cddfSDavid du Colombier 		if(BUSTYPE(ether->tbdf) != BusPCI)
18557dd7cddfSDavid du Colombier 			goto buggery;
18563ff48bf5SDavid du Colombier 		ctlr->busmaster = 2;
18577dd7cddfSDavid du Colombier 		goto vortex;
18583ff48bf5SDavid du Colombier 	case 0x5900:		/* 3C590-[TP|COMBO|TPO] */
18593ff48bf5SDavid du Colombier 	case 0x5920:		/* 3C592-[TP|COMBO|TPO] */
18603ff48bf5SDavid du Colombier 	case 0x5950:		/* 3C595-TX */
18613ff48bf5SDavid du Colombier 	case 0x5951:		/* 3C595-T4 */
18623ff48bf5SDavid du Colombier 	case 0x5952:		/* 3C595-MII */
18633ff48bf5SDavid du Colombier 	case 0x5970:		/* 3C597-TX */
18643ff48bf5SDavid du Colombier 	case 0x5971:		/* 3C597-T4 */
18653ff48bf5SDavid du Colombier 	case 0x5972:		/* 3C597-MII */
18663ff48bf5SDavid du Colombier 		ctlr->busmaster = 1;
18677dd7cddfSDavid du Colombier 	vortex:
18687dd7cddfSDavid du Colombier 		COMMAND(port, SelectRegisterWindow, Wfifo);
18693ff48bf5SDavid du Colombier 		ctlr->xcvr = inl(port+InternalConfig) & (autoSelect|xcvrMask);
18703ff48bf5SDavid du Colombier 		ctlr->rxearly = 8188;
18713ff48bf5SDavid du Colombier 		ctlr->rxstatus9 = 0;
18727dd7cddfSDavid du Colombier 		break;
18737dd7cddfSDavid du Colombier 	buggery:
18747dd7cddfSDavid du Colombier 	default:
18753ff48bf5SDavid du Colombier 		ctlr->busmaster = 0;
18767dd7cddfSDavid du Colombier 		COMMAND(port, SelectRegisterWindow, Wsetup);
18777dd7cddfSDavid du Colombier 		x = ins(port+AddressConfig);
18783ff48bf5SDavid du Colombier 		ctlr->xcvr = ((x & xcvrMask9)>>14)<<20;
18797dd7cddfSDavid du Colombier 		if(x & autoSelect9)
18803ff48bf5SDavid du Colombier 			ctlr->xcvr |= autoSelect;
18813ff48bf5SDavid du Colombier 		ctlr->rxearly = 2044;
18823ff48bf5SDavid du Colombier 		ctlr->rxstatus9 = 1;
18837dd7cddfSDavid du Colombier 		break;
18847dd7cddfSDavid du Colombier 	}
18853ff48bf5SDavid du Colombier 	if(ctlr->rxearly >= 2048)
18863ff48bf5SDavid du Colombier 		ctlr->ts = 2;
18877dd7cddfSDavid du Colombier 
18887dd7cddfSDavid du Colombier 	/*
18897dd7cddfSDavid du Colombier 	 * Check if the adapter's station address is to be overridden.
18909a747e4fSDavid du Colombier 	 * If not, read it from the EEPROM and set in ether->ea prior to
18919a747e4fSDavid du Colombier 	 * loading the station address in Wstation.
18929a747e4fSDavid du Colombier 	 * The EEPROM returns 16-bits at a time.
18937dd7cddfSDavid du Colombier 	 */
18947dd7cddfSDavid du Colombier 	memset(ea, 0, Eaddrlen);
18957dd7cddfSDavid du Colombier 	if(memcmp(ea, ether->ea, Eaddrlen) == 0){
18967dd7cddfSDavid du Colombier 		for(i = 0; i < Eaddrlen/2; i++){
18973ff48bf5SDavid du Colombier 			x = eepromdata(ctlr, i);
18987dd7cddfSDavid du Colombier 			ether->ea[2*i] = x>>8;
18997dd7cddfSDavid du Colombier 			ether->ea[2*i+1] = x;
19007dd7cddfSDavid du Colombier 		}
19017dd7cddfSDavid du Colombier 	}
19027dd7cddfSDavid du Colombier 
19037dd7cddfSDavid du Colombier 	COMMAND(port, SelectRegisterWindow, Wstation);
19047dd7cddfSDavid du Colombier 	for(i = 0; i < Eaddrlen; i++)
19057dd7cddfSDavid du Colombier 		outb(port+i, ether->ea[i]);
19067dd7cddfSDavid du Colombier 
19077dd7cddfSDavid du Colombier 	/*
19087dd7cddfSDavid du Colombier 	 * Enable the transceiver if necessary and determine whether
19097dd7cddfSDavid du Colombier 	 * busmastering can be used. Due to bugs in the first revision
19107dd7cddfSDavid du Colombier 	 * of the 3C59[05], don't use busmastering at 10Mbps.
19117dd7cddfSDavid du Colombier 	 */
19123ff48bf5SDavid du Colombier 	XCVRDEBUG("reset: xcvr %uX\n", ctlr->xcvr);
19137dd7cddfSDavid du Colombier 
19147dd7cddfSDavid du Colombier 	/*
19157dd7cddfSDavid du Colombier 	 * Allow user to specify desired media in plan9.ini
19167dd7cddfSDavid du Colombier 	 */
19177dd7cddfSDavid du Colombier 	for(i = 0; i < ether->nopt; i++){
19187dd7cddfSDavid du Colombier 		if(cistrncmp(ether->opt[i], "media=", 6) != 0)
19197dd7cddfSDavid du Colombier 			continue;
19207dd7cddfSDavid du Colombier 		p = ether->opt[i]+6;
19217dd7cddfSDavid du Colombier 		for(j = 0; j < nelem(media); j++)
19227dd7cddfSDavid du Colombier 			if(cistrcmp(p, media[j].name) == 0)
19233ff48bf5SDavid du Colombier 				ctlr->xcvr = media[j].xcvr;
19247dd7cddfSDavid du Colombier 	}
19257dd7cddfSDavid du Colombier 
19267dd7cddfSDavid du Colombier 	/*
19277dd7cddfSDavid du Colombier 	 * forgive me, but i am weak
19287dd7cddfSDavid du Colombier 	 */
19293ff48bf5SDavid du Colombier 	switch(ctlr->did){
19303ff48bf5SDavid du Colombier 	default:
19313ff48bf5SDavid du Colombier 		if(ctlr->xcvr & autoSelect)
19323ff48bf5SDavid du Colombier 			ctlr->xcvr = autoselect(ctlr);
19333ff48bf5SDavid du Colombier 		break;
19343ff48bf5SDavid du Colombier 	case 0x5157:
19353ff48bf5SDavid du Colombier 	case 0x6056:
1936e31d734fSDavid du Colombier 	case 0x4500:
19373ff48bf5SDavid du Colombier 	case 0x7646:
19383ff48bf5SDavid du Colombier 	case 0x9055:
19393ff48bf5SDavid du Colombier 	case 0x9200:
194039734e7eSDavid du Colombier 	case 0x9201:
19411805eb57SDavid du Colombier 	case 0x9805:
19423ff48bf5SDavid du Colombier 		ctlr->xcvr = xcvrMii;
19430809e9a7SDavid du Colombier 		resetctlr(ctlr);
19443ff48bf5SDavid du Colombier 		break;
19459a747e4fSDavid du Colombier 	}
19463ff48bf5SDavid du Colombier 	XCVRDEBUG("xcvr selected: %uX, did 0x%uX\n", ctlr->xcvr, ctlr->did);
19479a747e4fSDavid du Colombier 
19483ff48bf5SDavid du Colombier 	switch(ctlr->xcvr){
19497dd7cddfSDavid du Colombier 	case xcvrMii:
19507dd7cddfSDavid du Colombier 		/*
19517dd7cddfSDavid du Colombier 		 * Quick hack.
19527dd7cddfSDavid du Colombier 		 */
19533ff48bf5SDavid du Colombier 		if(ctlr->did == 0x5157)
19543ff48bf5SDavid du Colombier 			phyaddr = 0;
1955e31d734fSDavid du Colombier 		else if(ctlr->did == 0x6056)
1956e31d734fSDavid du Colombier 			phyaddr = scanphy(port);
19573ff48bf5SDavid du Colombier 		else
19583ff48bf5SDavid du Colombier 			phyaddr = 24;
19597dd7cddfSDavid du Colombier 		for(i = 0; i < 7; i++)
19607dd7cddfSDavid du Colombier 			XCVRDEBUG(" %2.2uX", miir(port, phyaddr, i));
19617dd7cddfSDavid du Colombier 			XCVRDEBUG("\n");
19627dd7cddfSDavid du Colombier 
19637dd7cddfSDavid du Colombier 		for(timeo = 0; timeo < 30; timeo++){
19647dd7cddfSDavid du Colombier 			phystat = miir(port, phyaddr, 0x01);
19657dd7cddfSDavid du Colombier 			if(phystat & 0x20)
19667dd7cddfSDavid du Colombier 				break;
19677dd7cddfSDavid du Colombier 			XCVRDEBUG(" %2.2uX", phystat);
19687dd7cddfSDavid du Colombier 			delay(100);
19697dd7cddfSDavid du Colombier 		}
19707dd7cddfSDavid du Colombier 		XCVRDEBUG(" %2.2uX", miir(port, phyaddr, 0x01));
19717dd7cddfSDavid du Colombier 		XCVRDEBUG("\n");
19727dd7cddfSDavid du Colombier 
19737dd7cddfSDavid du Colombier 		anar = miir(port, phyaddr, 0x04);
19747dd7cddfSDavid du Colombier 		anlpar = miir(port, phyaddr, 0x05) & 0x03E0;
19757dd7cddfSDavid du Colombier 		anar &= anlpar;
19767dd7cddfSDavid du Colombier 		miir(port, phyaddr, 0x00);
19777dd7cddfSDavid du Colombier 		XCVRDEBUG("mii an: %uX anlp: %uX r0:%uX r1:%uX\n",
19787dd7cddfSDavid du Colombier 			anar, anlpar, miir(port, phyaddr, 0x00),
19797dd7cddfSDavid du Colombier 			miir(port, phyaddr, 0x01));
19807dd7cddfSDavid du Colombier 		for(i = 0; i < ether->nopt; i++){
19817dd7cddfSDavid du Colombier 			if(cistrcmp(ether->opt[i], "fullduplex") == 0)
19827dd7cddfSDavid du Colombier 				anar |= 0x0100;
19837dd7cddfSDavid du Colombier 			else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0)
19847dd7cddfSDavid du Colombier 				anar |= 0x0100;
19857dd7cddfSDavid du Colombier 			else if(cistrcmp(ether->opt[i], "force100") == 0)
19867dd7cddfSDavid du Colombier 				anar |= 0x0080;
19877dd7cddfSDavid du Colombier 		}
19887dd7cddfSDavid du Colombier 		XCVRDEBUG("mii anar: %uX\n", anar);
19897dd7cddfSDavid du Colombier 		if(anar & 0x0100){		/* 100BASE-TXFD */
19907dd7cddfSDavid du Colombier 			ether->mbps = 100;
19917dd7cddfSDavid du Colombier 			setfullduplex(port);
19927dd7cddfSDavid du Colombier 		}
19939a747e4fSDavid du Colombier 		else if(anar & 0x0200){		/* 100BASE-T4 */
19949a747e4fSDavid du Colombier 			/* nothing to do */
19959a747e4fSDavid du Colombier 		}
19967dd7cddfSDavid du Colombier 		else if(anar & 0x0080)		/* 100BASE-TX */
19977dd7cddfSDavid du Colombier 			ether->mbps = 100;
19987dd7cddfSDavid du Colombier 		else if(anar & 0x0040)		/* 10BASE-TFD */
19997dd7cddfSDavid du Colombier 			setfullduplex(port);
20009a747e4fSDavid du Colombier 		else{				/* 10BASE-T */
20019a747e4fSDavid du Colombier 			/* nothing to do */
20029a747e4fSDavid du Colombier 		}
20037dd7cddfSDavid du Colombier 		break;
20047dd7cddfSDavid du Colombier 	case xcvr100BaseTX:
20057dd7cddfSDavid du Colombier 	case xcvr100BaseFX:
20067dd7cddfSDavid du Colombier 		COMMAND(port, SelectRegisterWindow, Wfifo);
20077dd7cddfSDavid du Colombier 		x = inl(port+InternalConfig) & ~ramPartitionMask;
20087dd7cddfSDavid du Colombier 		outl(port+InternalConfig, x|ramPartition1to1);
20097dd7cddfSDavid du Colombier 
20107dd7cddfSDavid du Colombier 		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
20117dd7cddfSDavid du Colombier 		x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable);
20127dd7cddfSDavid du Colombier 		x |= linkBeatEnable;
20137dd7cddfSDavid du Colombier 		outs(port+MediaStatus, x);
20147dd7cddfSDavid du Colombier 
20157dd7cddfSDavid du Colombier 		if(x & dataRate100)
20167dd7cddfSDavid du Colombier 			ether->mbps = 100;
20177dd7cddfSDavid du Colombier 		break;
20187dd7cddfSDavid du Colombier 	case xcvr10BaseT:
20197dd7cddfSDavid du Colombier 		/*
20207dd7cddfSDavid du Colombier 		 * Enable Link Beat and Jabber to start the
20217dd7cddfSDavid du Colombier 		 * transceiver.
20227dd7cddfSDavid du Colombier 		 */
20237dd7cddfSDavid du Colombier 		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
20247dd7cddfSDavid du Colombier 		x = ins(port+MediaStatus) & ~dcConverterEnabled;
20257dd7cddfSDavid du Colombier 		x |= linkBeatEnable|jabberGuardEnable;
20267dd7cddfSDavid du Colombier 		outs(port+MediaStatus, x);
20277dd7cddfSDavid du Colombier 
20283ff48bf5SDavid du Colombier 		if((ctlr->did & 0xFF00) == 0x5900)
20293ff48bf5SDavid du Colombier 			ctlr->busmaster = 0;
20307dd7cddfSDavid du Colombier 		break;
20317dd7cddfSDavid du Colombier 	case xcvr10Base2:
20327dd7cddfSDavid du Colombier 		COMMAND(port, SelectRegisterWindow, Wdiagnostic);
20337dd7cddfSDavid du Colombier 		x = ins(port+MediaStatus) & ~(linkBeatEnable|jabberGuardEnable);
20347dd7cddfSDavid du Colombier 		outs(port+MediaStatus, x);
20357dd7cddfSDavid du Colombier 
20367dd7cddfSDavid du Colombier 		/*
20377dd7cddfSDavid du Colombier 		 * Start the DC-DC converter.
20387dd7cddfSDavid du Colombier 		 * Wait > 800 microseconds.
20397dd7cddfSDavid du Colombier 		 */
20407dd7cddfSDavid du Colombier 		COMMAND(port, EnableDcConverter, 0);
20417dd7cddfSDavid du Colombier 		delay(1);
20427dd7cddfSDavid du Colombier 		break;
20437dd7cddfSDavid du Colombier 	}
20447dd7cddfSDavid du Colombier 
20457dd7cddfSDavid du Colombier 	/*
20467dd7cddfSDavid du Colombier 	 * Wop is the normal operating register set.
20477dd7cddfSDavid du Colombier 	 * The 3C59[0257] adapters allow access to more than one register window
20487dd7cddfSDavid du Colombier 	 * at a time, but there are situations where switching still needs to be
20497dd7cddfSDavid du Colombier 	 * done, so just do it.
20507dd7cddfSDavid du Colombier 	 * Clear out any lingering Tx status.
20517dd7cddfSDavid du Colombier 	 */
20527dd7cddfSDavid du Colombier 	COMMAND(port, SelectRegisterWindow, Wop);
20533ff48bf5SDavid du Colombier 	if(ctlr->busmaster == 2)
205459cc4ca5SDavid du Colombier 		x = port+TxStatus905;
205559cc4ca5SDavid du Colombier 	else
205659cc4ca5SDavid du Colombier 		x = port+TxStatus;
205759cc4ca5SDavid du Colombier 	while(inb(x))
205859cc4ca5SDavid du Colombier 		outb(x, 0);
20597dd7cddfSDavid du Colombier 
20607dd7cddfSDavid du Colombier 	/*
20613ff48bf5SDavid du Colombier 	 * Clear out the
20627dd7cddfSDavid du Colombier 	 * adapter statistics, clear the statistics logged into ctlr
20633ff48bf5SDavid du Colombier 	 * and enable statistics collection.
20647dd7cddfSDavid du Colombier 	 */
20657dd7cddfSDavid du Colombier 	ilock(&ctlr->wlock);
20667dd7cddfSDavid du Colombier 	statistics(ether);
20677dd7cddfSDavid du Colombier 	memset(ctlr->stats, 0, sizeof(ctlr->stats));
20687dd7cddfSDavid du Colombier 
20697dd7cddfSDavid du Colombier 	COMMAND(port, StatisticsEnable, 0);
20707dd7cddfSDavid du Colombier 
20717dd7cddfSDavid du Colombier 	/*
20727dd7cddfSDavid du Colombier 	 * Allocate any receive buffers.
20737dd7cddfSDavid du Colombier 	 */
20747dd7cddfSDavid du Colombier 	switch(ctlr->busmaster){
20757dd7cddfSDavid du Colombier 	case 2:
20767dd7cddfSDavid du Colombier 		ctlr->dnenabled = 1;
20777dd7cddfSDavid du Colombier 
20787dd7cddfSDavid du Colombier 		/*
20797dd7cddfSDavid du Colombier 		 * 10MUpldBug.
20807dd7cddfSDavid du Colombier 		 * Disabling is too severe, can use receive busmastering at
20817dd7cddfSDavid du Colombier 		 * 100Mbps OK, but how to tell which rate is actually being used -
20827dd7cddfSDavid du Colombier 		 * the 3c905 always seems to have dataRate100 set?
20837dd7cddfSDavid du Colombier 		 * Believe the bug doesn't apply if upRxEarlyEnable is set
20847dd7cddfSDavid du Colombier 		 * and the threshold is set such that uploads won't start
20857dd7cddfSDavid du Colombier 		 * until the whole packet has been received.
20867dd7cddfSDavid du Colombier 		 */
20877dd7cddfSDavid du Colombier 		ctlr->upenabled = 1;
20883ff48bf5SDavid du Colombier 		x = eepromdata(ctlr, 0x0F);
20897dd7cddfSDavid du Colombier 		if(!(x & 0x01))
20907dd7cddfSDavid du Colombier 			outl(port+PktStatus, upRxEarlyEnable);
20917dd7cddfSDavid du Colombier 
20927dd7cddfSDavid du Colombier 		if(ctlr->upenabled || ctlr->dnenabled){
20937dd7cddfSDavid du Colombier 			ctlr->nup = Nup;
20947dd7cddfSDavid du Colombier 			ctlr->ndn = Ndn;
20957dd7cddfSDavid du Colombier 			init905(ctlr);
20967dd7cddfSDavid du Colombier 		}
20979a747e4fSDavid du Colombier 		else {
20989a747e4fSDavid du Colombier 			ctlr->rbp = rbpalloc(iallocb);
20999a747e4fSDavid du Colombier 			if(ctlr->rbp == nil)
21009a747e4fSDavid du Colombier 				panic("can't reset ethernet: out of memory");
21019a747e4fSDavid du Colombier 		}
21027dd7cddfSDavid du Colombier 		outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256));
21037dd7cddfSDavid du Colombier 		break;
21047dd7cddfSDavid du Colombier 	default:
21059a747e4fSDavid du Colombier 		ctlr->rbp = rbpalloc(iallocb);
21069a747e4fSDavid du Colombier 		if(ctlr->rbp == nil)
21079a747e4fSDavid du Colombier 			panic("can't reset ethernet: out of memory");
21087dd7cddfSDavid du Colombier 		break;
21097dd7cddfSDavid du Colombier 	}
21107dd7cddfSDavid du Colombier 
21117dd7cddfSDavid du Colombier 	/*
21127dd7cddfSDavid du Colombier 	 * Set a base TxStartThresh which will be incremented
21137dd7cddfSDavid du Colombier 	 * if any txUnderrun errors occur and ensure no RxEarly
21147dd7cddfSDavid du Colombier 	 * interrupts happen.
21157dd7cddfSDavid du Colombier 	 */
21167dd7cddfSDavid du Colombier 	ctlr->txthreshold = ETHERMAXTU/2;
21177dd7cddfSDavid du Colombier 	COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts);
21183ff48bf5SDavid du Colombier 	COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts);
21197dd7cddfSDavid du Colombier 
21207dd7cddfSDavid du Colombier 	iunlock(&ctlr->wlock);
21217dd7cddfSDavid du Colombier 
21227dd7cddfSDavid du Colombier 	/*
21237dd7cddfSDavid du Colombier 	 * Linkage to the generic ethernet driver.
21247dd7cddfSDavid du Colombier 	 */
21257dd7cddfSDavid du Colombier 	ether->attach = attach;
21267dd7cddfSDavid du Colombier 	ether->transmit = transmit;
21277dd7cddfSDavid du Colombier 	ether->interrupt = interrupt;
21287dd7cddfSDavid du Colombier 	ether->ifstat = ifstat;
21297dd7cddfSDavid du Colombier 
21307dd7cddfSDavid du Colombier 	ether->promiscuous = promiscuous;
21317dd7cddfSDavid du Colombier 	ether->multicast = multicast;
21320809e9a7SDavid du Colombier 	ether->shutdown = shutdown;
21337dd7cddfSDavid du Colombier 	ether->arg = ether;
21347dd7cddfSDavid du Colombier 
21357dd7cddfSDavid du Colombier 	return 0;
21367dd7cddfSDavid du Colombier }
21377dd7cddfSDavid du Colombier 
21387dd7cddfSDavid du Colombier void
etherelnk3link(void)21397dd7cddfSDavid du Colombier etherelnk3link(void)
21407dd7cddfSDavid du Colombier {
21417dd7cddfSDavid du Colombier 	addethercard("elnk3", etherelnk3reset);
21427dd7cddfSDavid du Colombier 	addethercard("3C509", etherelnk3reset);
21439a747e4fSDavid du Colombier 	addethercard("3C575", etherelnk3reset);
21447dd7cddfSDavid du Colombier }
2145