xref: /plan9/sys/src/9/pc/ethervt6102.c (revision 6fbfa2f3a4bd5daf3b27fc86a6c02b39e9bc7b86)
19b12e100SDavid du Colombier /*
29b12e100SDavid du Colombier  * VIA VT6102 Fast Ethernet Controller (Rhine II).
39b12e100SDavid du Colombier  * To do:
49b12e100SDavid du Colombier  *	cache-line size alignments - done
59b12e100SDavid du Colombier  *	reduce tx interrupts
69b12e100SDavid du Colombier  *	use 2 descriptors on tx for alignment - done
79b12e100SDavid du Colombier  *	reorganise initialisation/shutdown/reset
89b12e100SDavid du Colombier  *	adjust Tx FIFO threshold on underflow - untested
99b12e100SDavid du Colombier  *	why does the link status never cause an interrupt?
109b12e100SDavid du Colombier  *	use the lproc as a periodic timer for stalls, etc.
119b12e100SDavid du Colombier  */
129b12e100SDavid du Colombier #include "u.h"
139b12e100SDavid du Colombier #include "../port/lib.h"
149b12e100SDavid du Colombier #include "mem.h"
159b12e100SDavid du Colombier #include "dat.h"
169b12e100SDavid du Colombier #include "fns.h"
179b12e100SDavid du Colombier #include "io.h"
189b12e100SDavid du Colombier #include "../port/error.h"
199b12e100SDavid du Colombier #include "../port/netif.h"
209b12e100SDavid du Colombier 
219b12e100SDavid du Colombier #include "etherif.h"
229b12e100SDavid du Colombier #include "ethermii.h"
239b12e100SDavid du Colombier 
249b12e100SDavid du Colombier enum {
259b12e100SDavid du Colombier 	Par0		= 0x00,		/* Ethernet Address */
269b12e100SDavid du Colombier 	Rcr		= 0x06,		/* Receive Configuration */
279b12e100SDavid du Colombier 	Tcr		= 0x07,		/* Transmit Configuration */
289b12e100SDavid du Colombier 	Cr		= 0x08,		/* Control */
299b12e100SDavid du Colombier 	Isr		= 0x0C,		/* Interrupt Status */
309b12e100SDavid du Colombier 	Imr		= 0x0E,		/* Interrupt Mask */
317e254d1cSDavid du Colombier 	Mcfilt0		= 0x10,		/* Multicast Filter 0 */
327e254d1cSDavid du Colombier 	Mcfilt1		= 0x14,		/* Multicast Filter 1 */
339b12e100SDavid du Colombier 	Rxdaddr		= 0x18,		/* Current Rx Descriptor Address */
349b12e100SDavid du Colombier 	Txdaddr		= 0x1C,		/* Current Tx Descriptor Address */
359b12e100SDavid du Colombier 	Phyadr		= 0x6C,		/* Phy Address */
369b12e100SDavid du Colombier 	Miisr		= 0x6D,		/* MII Status */
379b12e100SDavid du Colombier 	Bcr0		= 0x6E,		/* Bus Control */
389b12e100SDavid du Colombier 	Bcr1		= 0x6F,
399b12e100SDavid du Colombier 	Miicr		= 0x70,		/* MII Control */
409b12e100SDavid du Colombier 	Miiadr		= 0x71,		/* MII Address */
419b12e100SDavid du Colombier 	Miidata		= 0x72,		/* MII Data */
429b12e100SDavid du Colombier 	Eecsr		= 0x74,		/* EEPROM Control and Status */
4376c9a8f8SDavid du Colombier 	Stickhw		= 0x83,		/* Sticky Hardware Control */
4476c9a8f8SDavid du Colombier 	Wolcrclr	= 0xA4,
4576c9a8f8SDavid du Colombier 	Wolcgclr	= 0xA7,
4676c9a8f8SDavid du Colombier 	Pwrcsrclr	= 0xAC,
479b12e100SDavid du Colombier };
489b12e100SDavid du Colombier 
499b12e100SDavid du Colombier enum {					/* Rcr */
509b12e100SDavid du Colombier 	Sep		= 0x01,		/* Accept Error Packets */
519b12e100SDavid du Colombier 	Ar		= 0x02,		/* Accept Small Packets */
529b12e100SDavid du Colombier 	Am		= 0x04,		/* Accept Multicast */
539b12e100SDavid du Colombier 	Ab		= 0x08,		/* Accept Broadcast */
549b12e100SDavid du Colombier 	Prom		= 0x10,		/* Accept Physical Address Packets */
559b12e100SDavid du Colombier 	RrftMASK	= 0xE0,		/* Receive FIFO Threshold */
569b12e100SDavid du Colombier 	RrftSHIFT	= 5,
579b12e100SDavid du Colombier 	Rrft64		= 0<<RrftSHIFT,
589b12e100SDavid du Colombier 	Rrft32		= 1<<RrftSHIFT,
599b12e100SDavid du Colombier 	Rrft128		= 2<<RrftSHIFT,
609b12e100SDavid du Colombier 	Rrft256		= 3<<RrftSHIFT,
619b12e100SDavid du Colombier 	Rrft512		= 4<<RrftSHIFT,
629b12e100SDavid du Colombier 	Rrft768		= 5<<RrftSHIFT,
639b12e100SDavid du Colombier 	Rrft1024	= 6<<RrftSHIFT,
649b12e100SDavid du Colombier 	RrftSAF		= 7<<RrftSHIFT,
659b12e100SDavid du Colombier };
669b12e100SDavid du Colombier 
679b12e100SDavid du Colombier enum {					/* Tcr */
689b12e100SDavid du Colombier 	Lb0		= 0x02,		/* Loopback Mode */
699b12e100SDavid du Colombier 	Lb1		= 0x04,
709b12e100SDavid du Colombier 	Ofset		= 0x08,		/* Back-off Priority Selection */
719b12e100SDavid du Colombier 	RtsfMASK	= 0xE0,		/* Transmit FIFO Threshold */
729b12e100SDavid du Colombier 	RtsfSHIFT	= 5,
739b12e100SDavid du Colombier 	Rtsf128		= 0<<RtsfSHIFT,
749b12e100SDavid du Colombier 	Rtsf256		= 1<<RtsfSHIFT,
759b12e100SDavid du Colombier 	Rtsf512		= 2<<RtsfSHIFT,
769b12e100SDavid du Colombier 	Rtsf1024	= 3<<RtsfSHIFT,
779b12e100SDavid du Colombier 	RtsfSAF		= 7<<RtsfSHIFT,
789b12e100SDavid du Colombier };
799b12e100SDavid du Colombier 
809b12e100SDavid du Colombier enum {					/* Cr */
819b12e100SDavid du Colombier 	Init		= 0x0001,	/* INIT Process Begin */
829b12e100SDavid du Colombier 	Strt		= 0x0002,	/* Start NIC */
839b12e100SDavid du Colombier 	Stop		= 0x0004,	/* Stop NIC */
849b12e100SDavid du Colombier 	Rxon		= 0x0008,	/* Turn on Receive Process */
859b12e100SDavid du Colombier 	Txon		= 0x0010,	/* Turn on Transmit Process */
869b12e100SDavid du Colombier 	Tdmd		= 0x0020,	/* Transmit Poll Demand */
879b12e100SDavid du Colombier 	Rdmd		= 0x0040,	/* Receive Poll Demand */
889b12e100SDavid du Colombier 	Eren		= 0x0100,	/* Early Receive Enable */
899b12e100SDavid du Colombier 	Fdx		= 0x0400,	/* Set MAC to Full Duplex Mode */
909b12e100SDavid du Colombier 	Dpoll		= 0x0800,	/* Disable Td/Rd Auto Polling */
919b12e100SDavid du Colombier 	Tdmd1		= 0x2000,	/* Transmit Poll Demand 1 */
929b12e100SDavid du Colombier 	Rdmd1		= 0x4000,	/* Receive Poll Demand 1 */
939b12e100SDavid du Colombier 	Sfrst		= 0x8000,	/* Software Reset */
949b12e100SDavid du Colombier };
959b12e100SDavid du Colombier 
969b12e100SDavid du Colombier enum {					/* Isr/Imr */
979b12e100SDavid du Colombier 	Prx		= 0x0001,	/* Received Packet Successfully */
989b12e100SDavid du Colombier 	Ptx		= 0x0002,	/* Transmitted Packet Successfully */
999b12e100SDavid du Colombier 	Rxe		= 0x0004,	/* Receive Error */
1009b12e100SDavid du Colombier 	Txe		= 0x0008,	/* Transmit Error */
1019b12e100SDavid du Colombier 	Tu		= 0x0010,	/* Transmit Buffer Underflow */
1029b12e100SDavid du Colombier 	Ru		= 0x0020,	/* Receive Buffer Link Error */
1039b12e100SDavid du Colombier 	Be		= 0x0040,	/* PCI Bus Error */
1049b12e100SDavid du Colombier 	Cnt		= 0x0080,	/* Counter Overflow */
1059b12e100SDavid du Colombier 	Eri		= 0x0100,	/* Early Receive Interrupt */
1069b12e100SDavid du Colombier 	Udfi		= 0x0200,	/* Tx FIFO Underflow */
1079b12e100SDavid du Colombier 	Ovfi		= 0x0400,	/* Receive FIFO Overflow */
1089b12e100SDavid du Colombier 	Pktrace		= 0x0800,	/* Hmmm... */
1099b12e100SDavid du Colombier 	Norbf		= 0x1000,	/* No Receive Buffers */
1109b12e100SDavid du Colombier 	Abti		= 0x2000,	/* Transmission Abort */
1119b12e100SDavid du Colombier 	Srci		= 0x4000,	/* Port State Change */
1129b12e100SDavid du Colombier 	Geni		= 0x8000,	/* General Purpose Interrupt */
1139b12e100SDavid du Colombier };
1149b12e100SDavid du Colombier 
1159b12e100SDavid du Colombier enum {					/* Phyadr */
1169b12e100SDavid du Colombier 	PhyadMASK	= 0x1F,		/* PHY Address */
1179b12e100SDavid du Colombier 	PhyadSHIFT	= 0,
1189b12e100SDavid du Colombier 	Mfdc		= 0x20,		/* Accelerate MDC Speed */
1199b12e100SDavid du Colombier 	Mpo0		= 0x40,		/* MII Polling Timer Interval */
1209b12e100SDavid du Colombier 	Mpo1		= 0x80,
1219b12e100SDavid du Colombier };
1229b12e100SDavid du Colombier 
1239b12e100SDavid du Colombier enum {					/* Bcr0 */
1249b12e100SDavid du Colombier 	DmaMASK		= 0x07,		/* DMA Length */
1259b12e100SDavid du Colombier 	DmaSHIFT	= 0,
1269b12e100SDavid du Colombier 	Dma32		= 0<<DmaSHIFT,
1279b12e100SDavid du Colombier 	Dma64		= 1<<DmaSHIFT,
1289b12e100SDavid du Colombier 	Dma128		= 2<<DmaSHIFT,
1299b12e100SDavid du Colombier 	Dma256		= 3<<DmaSHIFT,
1309b12e100SDavid du Colombier 	Dma512		= 4<<DmaSHIFT,
1319b12e100SDavid du Colombier 	Dma1024		= 5<<DmaSHIFT,
1329b12e100SDavid du Colombier 	DmaSAF		= 7<<DmaSHIFT,
1339b12e100SDavid du Colombier 	CrftMASK	= 0x38,		/* Rx FIFO Threshold */
1349b12e100SDavid du Colombier 	CrftSHIFT	= 3,
1359b12e100SDavid du Colombier 	Crft64		= 1<<CrftSHIFT,
1369b12e100SDavid du Colombier 	Crft128		= 2<<CrftSHIFT,
1379b12e100SDavid du Colombier 	Crft256		= 3<<CrftSHIFT,
1389b12e100SDavid du Colombier 	Crft512		= 4<<CrftSHIFT,
1399b12e100SDavid du Colombier 	Crft1024	= 5<<CrftSHIFT,
1409b12e100SDavid du Colombier 	CrftSAF		= 7<<CrftSHIFT,
1419b12e100SDavid du Colombier 	Extled		= 0x40,		/* Extra LED Support Control */
1429b12e100SDavid du Colombier 	Med2		= 0x80,		/* Medium Select Control */
1439b12e100SDavid du Colombier };
1449b12e100SDavid du Colombier 
1459b12e100SDavid du Colombier enum {					/* Bcr1 */
1469b12e100SDavid du Colombier 	PotMASK		= 0x07,		/* Polling Timer Interval */
1479b12e100SDavid du Colombier 	PotSHIFT	= 0,
1489b12e100SDavid du Colombier 	CtftMASK	= 0x38,		/* Tx FIFO Threshold */
1499b12e100SDavid du Colombier 	CtftSHIFT	= 3,
1509b12e100SDavid du Colombier 	Ctft64		= 1<<CtftSHIFT,
1519b12e100SDavid du Colombier 	Ctft128		= 2<<CtftSHIFT,
1529b12e100SDavid du Colombier 	Ctft256		= 3<<CtftSHIFT,
1539b12e100SDavid du Colombier 	Ctft512		= 4<<CtftSHIFT,
1549b12e100SDavid du Colombier 	Ctft1024	= 5<<CtftSHIFT,
1559b12e100SDavid du Colombier 	CtftSAF		= 7<<CtftSHIFT,
1569b12e100SDavid du Colombier };
1579b12e100SDavid du Colombier 
1589b12e100SDavid du Colombier enum {					/* Miicr */
1599b12e100SDavid du Colombier 	Mdc		= 0x01,		/* Clock */
1609b12e100SDavid du Colombier 	Mdi		= 0x02,		/* Data In */
1619b12e100SDavid du Colombier 	Mdo		= 0x04,		/* Data Out */
1629b12e100SDavid du Colombier 	Mout		= 0x08,		/* Output Enable */
1639b12e100SDavid du Colombier 	Mdpm		= 0x10,		/* Direct Program Mode Enable */
1649b12e100SDavid du Colombier 	Wcmd		= 0x20,		/* Write Enable */
1659b12e100SDavid du Colombier 	Rcmd		= 0x40,		/* Read Enable */
1669b12e100SDavid du Colombier 	Mauto		= 0x80,		/* Auto Polling Enable */
1679b12e100SDavid du Colombier };
1689b12e100SDavid du Colombier 
1699b12e100SDavid du Colombier enum {					/* Miiadr */
1709b12e100SDavid du Colombier 	MadMASK		= 0x1F,		/* MII Port Address */
1719b12e100SDavid du Colombier 	MadSHIFT	= 0,
1729b12e100SDavid du Colombier 	Mdone		= 0x20,		/* Accelerate MDC Speed */
1739b12e100SDavid du Colombier 	Msrcen		= 0x40,		/* MII Polling Timer Interval */
1749b12e100SDavid du Colombier 	Midle		= 0x80,
1759b12e100SDavid du Colombier };
1769b12e100SDavid du Colombier 
1779b12e100SDavid du Colombier enum {					/* Eecsr */
1789b12e100SDavid du Colombier 	Edo		= 0x01,		/* Data Out */
1799b12e100SDavid du Colombier 	Edi		= 0x02,		/* Data In */
1809b12e100SDavid du Colombier 	Eck		= 0x04,		/* Clock */
1819b12e100SDavid du Colombier 	Ecs		= 0x08,		/* Chip Select */
1829b12e100SDavid du Colombier 	Dpm		= 0x10,		/* Direct Program Mode Enable */
1839b12e100SDavid du Colombier 	Autold		= 0x20,		/* Dynamic Reload */
1849b12e100SDavid du Colombier 	Embp		= 0x40,		/* Embedded Program Enable */
1859b12e100SDavid du Colombier 	Eepr		= 0x80,		/* Programmed */
1869b12e100SDavid du Colombier };
1879b12e100SDavid du Colombier 
1889b12e100SDavid du Colombier /*
1899b12e100SDavid du Colombier  * Ring descriptor. The space allocated for each
1909b12e100SDavid du Colombier  * of these will be rounded up to a cache-line boundary.
1919b12e100SDavid du Colombier  * The first 4 elements are known to the hardware.
1929b12e100SDavid du Colombier  */
1939b12e100SDavid du Colombier typedef struct Ds Ds;
1949b12e100SDavid du Colombier typedef struct Ds {
1959b12e100SDavid du Colombier 	uint	status;
1969b12e100SDavid du Colombier 	uint	control;
1979b12e100SDavid du Colombier 	uint	addr;
1989b12e100SDavid du Colombier 	uint	branch;
1999b12e100SDavid du Colombier 
2009b12e100SDavid du Colombier 	Block*	bp;
2019b12e100SDavid du Colombier 	void*	bounce;
2029b12e100SDavid du Colombier 	Ds*	next;
2039b12e100SDavid du Colombier 	Ds*	prev;
2049b12e100SDavid du Colombier } Ds;
2059b12e100SDavid du Colombier 
2069b12e100SDavid du Colombier enum {					/* Rx Ds status */
2079b12e100SDavid du Colombier 	Rerr		= 0x00000001,	/* Receiver Error */
2089b12e100SDavid du Colombier 	Crc		= 0x00000002,	/* CRC Error */
2099b12e100SDavid du Colombier 	Fae		= 0x00000004,	/* Frame Alignment Error */
2109b12e100SDavid du Colombier 	Fov		= 0x00000008,	/* FIFO Overflow */
2119b12e100SDavid du Colombier 	Long		= 0x00000010,	/* A Long Packet */
2129b12e100SDavid du Colombier 	Runt		= 0x00000020,	/* A Runt Packet */
2139b12e100SDavid du Colombier 	Rxserr		= 0x00000040,	/* System Error */
2149b12e100SDavid du Colombier 	Buff		= 0x00000080,	/* Buffer Underflow Error */
2159b12e100SDavid du Colombier 	Rxedp		= 0x00000100,	/* End of Packet Buffer */
2169b12e100SDavid du Colombier 	Rxstp		= 0x00000200,	/* Packet Start */
2179b12e100SDavid du Colombier 	Chn		= 0x00000400,	/* Chain Buffer */
2189b12e100SDavid du Colombier 	Phy		= 0x00000800,	/* Physical Address Packet */
2199b12e100SDavid du Colombier 	Bar		= 0x00001000,	/* Broadcast Packet */
2209b12e100SDavid du Colombier 	Mar		= 0x00002000,	/* Multicast Packet */
2219b12e100SDavid du Colombier 	Rxok		= 0x00008000,	/* Packet Received Successfully */
2229b12e100SDavid du Colombier 	LengthMASK	= 0x07FF0000,	/* Received Packet Length */
2239b12e100SDavid du Colombier 	LengthSHIFT	= 16,
2249b12e100SDavid du Colombier 
2259b12e100SDavid du Colombier 	Own		= 0x80000000,	/* Descriptor Owned by NIC */
2269b12e100SDavid du Colombier };
2279b12e100SDavid du Colombier 
2289b12e100SDavid du Colombier enum {					/* Tx Ds status */
2299b12e100SDavid du Colombier 	NcrMASK		= 0x0000000F,	/* Collision Retry Count */
2309b12e100SDavid du Colombier 	NcrSHIFT	= 0,
2319b12e100SDavid du Colombier 	Cols		= 0x00000010,	/* Experienced Collisions */
2329b12e100SDavid du Colombier 	Cdh		= 0x00000080,	/* CD Heartbeat */
2339b12e100SDavid du Colombier 	Abt		= 0x00000100,	/* Aborted after Excessive Collisions */
2349b12e100SDavid du Colombier 	Owc		= 0x00000200,	/* Out of Window Collision Seen */
2359b12e100SDavid du Colombier 	Crs		= 0x00000400,	/* Carrier Sense Lost */
2369b12e100SDavid du Colombier 	Udf		= 0x00000800,	/* FIFO Underflow */
2379b12e100SDavid du Colombier 	Tbuff		= 0x00001000,	/* Invalid Td */
2389b12e100SDavid du Colombier 	Txserr		= 0x00002000,	/* System Error */
2399b12e100SDavid du Colombier 	Terr		= 0x00008000,	/* Excessive Collisions */
2409b12e100SDavid du Colombier };
2419b12e100SDavid du Colombier 
2429b12e100SDavid du Colombier enum {					/* Tx Ds control */
2439b12e100SDavid du Colombier 	TbsMASK		= 0x000007FF,	/* Tx Buffer Size */
2449b12e100SDavid du Colombier 	TbsSHIFT	= 0,
2459b12e100SDavid du Colombier 	Chain		= 0x00008000,	/* Chain Buffer */
2469b12e100SDavid du Colombier 	Crcdisable	= 0x00010000,	/* Disable CRC generation */
2479b12e100SDavid du Colombier 	Stp		= 0x00200000,	/* Start of Packet */
2489b12e100SDavid du Colombier 	Edp		= 0x00400000,	/* End of Packet */
2499b12e100SDavid du Colombier 	Ic		= 0x00800000,	/* Assert Interrupt Immediately */
2509b12e100SDavid du Colombier };
2519b12e100SDavid du Colombier 
2529b12e100SDavid du Colombier enum {
2539b12e100SDavid du Colombier 	Nrd		= 64,
2549b12e100SDavid du Colombier 	Ntd		= 64,
2559b12e100SDavid du Colombier 	Rdbsz		= ROUNDUP(ETHERMAXTU+4, 4),
2569b12e100SDavid du Colombier 
2579b12e100SDavid du Colombier 	Nrxstats	= 8,
2589b12e100SDavid du Colombier 	Ntxstats	= 9,
2599b12e100SDavid du Colombier 
2609b12e100SDavid du Colombier 	Txcopy		= 128,
2619b12e100SDavid du Colombier };
2629b12e100SDavid du Colombier 
2639b12e100SDavid du Colombier typedef struct Ctlr Ctlr;
2649b12e100SDavid du Colombier typedef struct Ctlr {
2659b12e100SDavid du Colombier 	int	port;
2669b12e100SDavid du Colombier 	Pcidev*	pcidev;
2679b12e100SDavid du Colombier 	Ctlr*	next;
2689b12e100SDavid du Colombier 	int	active;
2699b12e100SDavid du Colombier 	int	id;
2709b12e100SDavid du Colombier 	uchar	par[Eaddrlen];
2719b12e100SDavid du Colombier 
2729b12e100SDavid du Colombier 	QLock	alock;			/* attach */
2739b12e100SDavid du Colombier 	void*	alloc;			/* receive/transmit descriptors */
2749b12e100SDavid du Colombier 	int	cls;			/* alignment */
2759b12e100SDavid du Colombier 	int	nrd;
2769b12e100SDavid du Colombier 	int	ntd;
2779b12e100SDavid du Colombier 
2789b12e100SDavid du Colombier 	Ds*	rd;
2799b12e100SDavid du Colombier 	Ds*	rdh;
2809b12e100SDavid du Colombier 
2819b12e100SDavid du Colombier 	Lock	tlock;
2829b12e100SDavid du Colombier 	Ds*	td;
2839b12e100SDavid du Colombier 	Ds*	tdh;
2849b12e100SDavid du Colombier 	Ds*	tdt;
2859b12e100SDavid du Colombier 	int	tdused;
2869b12e100SDavid du Colombier 
2879b12e100SDavid du Colombier 	Lock	clock;			/*  */
2889b12e100SDavid du Colombier 	int	cr;
2899b12e100SDavid du Colombier 	int	imr;
2909b12e100SDavid du Colombier 	int	tft;			/* Tx threshold */
2919b12e100SDavid du Colombier 
2929b12e100SDavid du Colombier 	Mii*	mii;
2939b12e100SDavid du Colombier 	Rendez	lrendez;
2949b12e100SDavid du Colombier 	int	lwakeup;
2959b12e100SDavid du Colombier 
2969b12e100SDavid du Colombier 	uint	rxstats[Nrxstats];	/* statistics */
2979b12e100SDavid du Colombier 	uint	txstats[Ntxstats];
2989b12e100SDavid du Colombier 	uint	intr;
2999b12e100SDavid du Colombier 	uint	lintr;
3009b12e100SDavid du Colombier 	uint	lsleep;
3019b12e100SDavid du Colombier 	uint	rintr;
3029b12e100SDavid du Colombier 	uint	tintr;
3039b12e100SDavid du Colombier 	uint	taligned;
3049b12e100SDavid du Colombier 	uint	tsplit;
3059b12e100SDavid du Colombier 	uint	tcopied;
3069b12e100SDavid du Colombier 	uint	txdw;
3079b12e100SDavid du Colombier } Ctlr;
3089b12e100SDavid du Colombier 
3099b12e100SDavid du Colombier static Ctlr* vt6102ctlrhead;
3109b12e100SDavid du Colombier static Ctlr* vt6102ctlrtail;
3119b12e100SDavid du Colombier 
3129b12e100SDavid du Colombier #define csr8r(c, r)	(inb((c)->port+(r)))
3139b12e100SDavid du Colombier #define csr16r(c, r)	(ins((c)->port+(r)))
3149b12e100SDavid du Colombier #define csr32r(c, r)	(inl((c)->port+(r)))
3159b12e100SDavid du Colombier #define csr8w(c, r, b)	(outb((c)->port+(r), (int)(b)))
3169b12e100SDavid du Colombier #define csr16w(c, r, w)	(outs((c)->port+(r), (ushort)(w)))
3179b12e100SDavid du Colombier #define csr32w(c, r, w)	(outl((c)->port+(r), (ulong)(w)))
3189b12e100SDavid du Colombier 
3199b12e100SDavid du Colombier static char* rxstats[Nrxstats] = {
3209b12e100SDavid du Colombier 	"Receiver Error",
3219b12e100SDavid du Colombier 	"CRC Error",
3229b12e100SDavid du Colombier 	"Frame Alignment Error",
3239b12e100SDavid du Colombier 	"FIFO Overflow",
3249b12e100SDavid du Colombier 	"Long Packet",
3259b12e100SDavid du Colombier 	"Runt Packet",
3269b12e100SDavid du Colombier 	"System Error",
3279b12e100SDavid du Colombier 	"Buffer Underflow Error",
3289b12e100SDavid du Colombier };
3299b12e100SDavid du Colombier static char* txstats[Ntxstats] = {
3309b12e100SDavid du Colombier 	"Aborted after Excessive Collisions",
3319b12e100SDavid du Colombier 	"Out of Window Collision Seen",
3329b12e100SDavid du Colombier 	"Carrier Sense Lost",
3339b12e100SDavid du Colombier 	"FIFO Underflow",
3349b12e100SDavid du Colombier 	"Invalid Td",
3359b12e100SDavid du Colombier 	"System Error",
3369b12e100SDavid du Colombier 	nil,
3379b12e100SDavid du Colombier 	"Excessive Collisions",
3389b12e100SDavid du Colombier };
3399b12e100SDavid du Colombier 
3409b12e100SDavid du Colombier static long
vt6102ifstat(Ether * edev,void * a,long n,ulong offset)3419b12e100SDavid du Colombier vt6102ifstat(Ether* edev, void* a, long n, ulong offset)
3429b12e100SDavid du Colombier {
3439b12e100SDavid du Colombier 	char *p;
3449b12e100SDavid du Colombier 	Ctlr *ctlr;
3459b12e100SDavid du Colombier 	int i, l, r;
3469b12e100SDavid du Colombier 
3479b12e100SDavid du Colombier 	ctlr = edev->ctlr;
3489b12e100SDavid du Colombier 
34946136019SDavid du Colombier 	p = malloc(READSTR);
350aa72973aSDavid du Colombier 	if(p == nil)
351aa72973aSDavid du Colombier 		error(Enomem);
3529b12e100SDavid du Colombier 	l = 0;
3539b12e100SDavid du Colombier 	for(i = 0; i < Nrxstats; i++){
35446136019SDavid du Colombier 		l += snprint(p+l, READSTR-l, "%s: %ud\n",
3559b12e100SDavid du Colombier 			rxstats[i], ctlr->rxstats[i]);
3569b12e100SDavid du Colombier 	}
3579b12e100SDavid du Colombier 	for(i = 0; i < Ntxstats; i++){
3589b12e100SDavid du Colombier 		if(txstats[i] == nil)
3599b12e100SDavid du Colombier 			continue;
36046136019SDavid du Colombier 		l += snprint(p+l, READSTR-l, "%s: %ud\n",
3619b12e100SDavid du Colombier 			txstats[i], ctlr->txstats[i]);
3629b12e100SDavid du Colombier 	}
36346136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "cls: %ud\n", ctlr->cls);
36446136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "intr: %ud\n", ctlr->intr);
36546136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "lintr: %ud\n", ctlr->lintr);
36646136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "lsleep: %ud\n", ctlr->lsleep);
36746136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "rintr: %ud\n", ctlr->rintr);
36846136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "tintr: %ud\n", ctlr->tintr);
36946136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "taligned: %ud\n", ctlr->taligned);
37046136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "tsplit: %ud\n", ctlr->tsplit);
37146136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "tcopied: %ud\n", ctlr->tcopied);
37246136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "txdw: %ud\n", ctlr->txdw);
37346136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "tft: %ud\n", ctlr->tft);
3749b12e100SDavid du Colombier 
3759b12e100SDavid du Colombier 	if(ctlr->mii != nil && ctlr->mii->curphy != nil){
37646136019SDavid du Colombier 		l += snprint(p+l, READSTR, "phy:   ");
3779b12e100SDavid du Colombier 		for(i = 0; i < NMiiPhyr; i++){
3789b12e100SDavid du Colombier 			if(i && ((i & 0x07) == 0))
37946136019SDavid du Colombier 				l += snprint(p+l, READSTR-l, "\n       ");
3809b12e100SDavid du Colombier 			r = miimir(ctlr->mii, i);
38146136019SDavid du Colombier 			l += snprint(p+l, READSTR-l, " %4.4uX", r);
3829b12e100SDavid du Colombier 		}
38346136019SDavid du Colombier 		snprint(p+l, READSTR-l, "\n");
3849b12e100SDavid du Colombier 	}
38546136019SDavid du Colombier 	snprint(p+l, READSTR-l, "\n");
3869b12e100SDavid du Colombier 
3879b12e100SDavid du Colombier 	n = readstr(offset, a, n, p);
3889b12e100SDavid du Colombier 	free(p);
3899b12e100SDavid du Colombier 
3909b12e100SDavid du Colombier 	return n;
3919b12e100SDavid du Colombier }
3929b12e100SDavid du Colombier 
3939b12e100SDavid du Colombier static void
vt6102promiscuous(void * arg,int on)3949b12e100SDavid du Colombier vt6102promiscuous(void* arg, int on)
3959b12e100SDavid du Colombier {
3969b12e100SDavid du Colombier 	int rcr;
3979b12e100SDavid du Colombier 	Ctlr *ctlr;
3989b12e100SDavid du Colombier 	Ether *edev;
3999b12e100SDavid du Colombier 
4009b12e100SDavid du Colombier 	edev = arg;
4019b12e100SDavid du Colombier 	ctlr = edev->ctlr;
4029b12e100SDavid du Colombier 	rcr = csr8r(ctlr, Rcr);
4039b12e100SDavid du Colombier 	if(on)
4049b12e100SDavid du Colombier 		rcr |= Prom;
4059b12e100SDavid du Colombier 	else
4069b12e100SDavid du Colombier 		rcr &= ~Prom;
4079b12e100SDavid du Colombier 	csr8w(ctlr, Rcr, rcr);
4089b12e100SDavid du Colombier }
4099b12e100SDavid du Colombier 
4109b12e100SDavid du Colombier static void
vt6102multicast(void * arg,uchar * addr,int on)4119b12e100SDavid du Colombier vt6102multicast(void* arg, uchar* addr, int on)
4129b12e100SDavid du Colombier {
4139b12e100SDavid du Colombier 	/*
4149b12e100SDavid du Colombier 	 * For now Am is set in Rcr.
4159b12e100SDavid du Colombier 	 * Will need to interlock with promiscuous
4169b12e100SDavid du Colombier 	 * when this gets filled in.
4179b12e100SDavid du Colombier 	 */
4189b12e100SDavid du Colombier 	USED(arg, addr, on);
4199b12e100SDavid du Colombier }
4209b12e100SDavid du Colombier 
4219b12e100SDavid du Colombier static int
vt6102wakeup(void * v)4229b12e100SDavid du Colombier vt6102wakeup(void* v)
4239b12e100SDavid du Colombier {
4249b12e100SDavid du Colombier 	return *((int*)v) != 0;
4259b12e100SDavid du Colombier }
4269b12e100SDavid du Colombier 
4279b12e100SDavid du Colombier static void
vt6102imr(Ctlr * ctlr,int imr)4289b12e100SDavid du Colombier vt6102imr(Ctlr* ctlr, int imr)
4299b12e100SDavid du Colombier {
4309b12e100SDavid du Colombier 	ilock(&ctlr->clock);
4319b12e100SDavid du Colombier 	ctlr->imr |= imr;
4329b12e100SDavid du Colombier 	csr16w(ctlr, Imr, ctlr->imr);
4339b12e100SDavid du Colombier 	iunlock(&ctlr->clock);
4349b12e100SDavid du Colombier }
4359b12e100SDavid du Colombier 
4369b12e100SDavid du Colombier static void
vt6102lproc(void * arg)4379b12e100SDavid du Colombier vt6102lproc(void* arg)
4389b12e100SDavid du Colombier {
4399b12e100SDavid du Colombier 	Ctlr *ctlr;
4409b12e100SDavid du Colombier 	Ether *edev;
4419b12e100SDavid du Colombier 	MiiPhy *phy;
4429b12e100SDavid du Colombier 
4439b12e100SDavid du Colombier 	edev = arg;
4449b12e100SDavid du Colombier 	ctlr = edev->ctlr;
4459b12e100SDavid du Colombier 	for(;;){
4469b12e100SDavid du Colombier 		if(ctlr->mii == nil || ctlr->mii->curphy == nil)
4479b12e100SDavid du Colombier 			break;
4489b12e100SDavid du Colombier 		if(miistatus(ctlr->mii) < 0)
4499b12e100SDavid du Colombier 			goto enable;
4509b12e100SDavid du Colombier 
4519b12e100SDavid du Colombier 		phy = ctlr->mii->curphy;
4529b12e100SDavid du Colombier 		ilock(&ctlr->clock);
4539b12e100SDavid du Colombier 		if(phy->fd)
4549b12e100SDavid du Colombier 			ctlr->cr |= Fdx;
4559b12e100SDavid du Colombier 		else
4569b12e100SDavid du Colombier 			ctlr->cr &= ~Fdx;
4579b12e100SDavid du Colombier 		csr16w(ctlr, Cr, ctlr->cr);
4589b12e100SDavid du Colombier 		iunlock(&ctlr->clock);
4599b12e100SDavid du Colombier enable:
4609b12e100SDavid du Colombier 		ctlr->lwakeup = 0;
4619b12e100SDavid du Colombier 		vt6102imr(ctlr, Srci);
4629b12e100SDavid du Colombier 
4639b12e100SDavid du Colombier 		ctlr->lsleep++;
4649b12e100SDavid du Colombier 		sleep(&ctlr->lrendez, vt6102wakeup, &ctlr->lwakeup);
4659b12e100SDavid du Colombier 
4669b12e100SDavid du Colombier 	}
4679b12e100SDavid du Colombier 	pexit("vt6102lproc: done", 1);
4689b12e100SDavid du Colombier }
4699b12e100SDavid du Colombier 
4709b12e100SDavid du Colombier static void
vt6102attach(Ether * edev)4719b12e100SDavid du Colombier vt6102attach(Ether* edev)
4729b12e100SDavid du Colombier {
4739b12e100SDavid du Colombier 	int i;
4749b12e100SDavid du Colombier 	Ctlr *ctlr;
4759b12e100SDavid du Colombier 	Ds *ds, *prev;
4769b12e100SDavid du Colombier 	uchar *alloc, *bounce;
4779b12e100SDavid du Colombier 	char name[KNAMELEN];
4789b12e100SDavid du Colombier 
4799b12e100SDavid du Colombier 	ctlr = edev->ctlr;
4809b12e100SDavid du Colombier 	qlock(&ctlr->alock);
4819b12e100SDavid du Colombier 	if(ctlr->alloc != nil){
4829b12e100SDavid du Colombier 		qunlock(&ctlr->alock);
4839b12e100SDavid du Colombier 		return;
4849b12e100SDavid du Colombier 	}
4859b12e100SDavid du Colombier 
4869b12e100SDavid du Colombier 	/*
4879b12e100SDavid du Colombier 	 * Descriptor and bounce-buffer space.
4889b12e100SDavid du Colombier 	 * Must all be aligned on a 4-byte boundary,
4899b12e100SDavid du Colombier 	 * but try to align on cache-lines.
4909b12e100SDavid du Colombier 	 */
4919b12e100SDavid du Colombier 	ctlr->nrd = Nrd;
4929b12e100SDavid du Colombier 	ctlr->ntd = Ntd;
4939b12e100SDavid du Colombier 	alloc = malloc((ctlr->nrd+ctlr->ntd)*ctlr->cls + ctlr->ntd*Txcopy + ctlr->cls-1);
4949b12e100SDavid du Colombier 	if(alloc == nil){
4959b12e100SDavid du Colombier 		qunlock(&ctlr->alock);
496*6fbfa2f3SDavid du Colombier 		error(Enomem);
4979b12e100SDavid du Colombier 	}
4989b12e100SDavid du Colombier 	ctlr->alloc = alloc;
4999b12e100SDavid du Colombier 	alloc = (uchar*)ROUNDUP((ulong)alloc, ctlr->cls);
5009b12e100SDavid du Colombier 
5019b12e100SDavid du Colombier 	ctlr->rd = (Ds*)alloc;
5029b12e100SDavid du Colombier 
5039b12e100SDavid du Colombier 	if(waserror()){
5049b12e100SDavid du Colombier 		ds = ctlr->rd;
5059b12e100SDavid du Colombier 		for(i = 0; i < ctlr->nrd; i++){
5069b12e100SDavid du Colombier 			if(ds->bp != nil){
5079b12e100SDavid du Colombier 				freeb(ds->bp);
5089b12e100SDavid du Colombier 				ds->bp = nil;
5099b12e100SDavid du Colombier 			}
5109b12e100SDavid du Colombier 			if((ds = ds->next) == nil)
5119b12e100SDavid du Colombier 				break;
5129b12e100SDavid du Colombier 		}
5139b12e100SDavid du Colombier 		free(ctlr->alloc);
5149b12e100SDavid du Colombier 		ctlr->alloc = nil;
5159b12e100SDavid du Colombier 		qunlock(&ctlr->alock);
5169b12e100SDavid du Colombier 		nexterror();
5179b12e100SDavid du Colombier 	}
5189b12e100SDavid du Colombier 
5199b12e100SDavid du Colombier 	prev = ctlr->rd + ctlr->nrd-1;
5209b12e100SDavid du Colombier 	for(i = 0; i < ctlr->nrd; i++){
5219b12e100SDavid du Colombier 		ds = (Ds*)alloc;
5229b12e100SDavid du Colombier 		alloc += ctlr->cls;
5239b12e100SDavid du Colombier 
5249b12e100SDavid du Colombier 		ds->control = Rdbsz;
5259b12e100SDavid du Colombier 		ds->branch = PCIWADDR(alloc);
5269b12e100SDavid du Colombier 
5279b12e100SDavid du Colombier 		ds->bp = iallocb(Rdbsz+3);
5289b12e100SDavid du Colombier 		if(ds->bp == nil)
5299b12e100SDavid du Colombier 			error("vt6102: can't allocate receive ring\n");
5309b12e100SDavid du Colombier 		ds->bp->rp = (uchar*)ROUNDUP((ulong)ds->bp->rp, 4);
5319b12e100SDavid du Colombier 		ds->addr = PCIWADDR(ds->bp->rp);
5329b12e100SDavid du Colombier 
5339b12e100SDavid du Colombier 		ds->next = (Ds*)alloc;
5349b12e100SDavid du Colombier 		ds->prev = prev;
5359b12e100SDavid du Colombier 		prev = ds;
5369b12e100SDavid du Colombier 
5379b12e100SDavid du Colombier 		ds->status = Own;
5389b12e100SDavid du Colombier 	}
5399b12e100SDavid du Colombier 	prev->branch = 0;
5409b12e100SDavid du Colombier 	prev->next = ctlr->rd;
5419b12e100SDavid du Colombier 	prev->status = 0;
5429b12e100SDavid du Colombier 	ctlr->rdh = ctlr->rd;
5439b12e100SDavid du Colombier 
5449b12e100SDavid du Colombier 	ctlr->td = (Ds*)alloc;
5459b12e100SDavid du Colombier 	prev = ctlr->td + ctlr->ntd-1;
5469b12e100SDavid du Colombier 	bounce = alloc + ctlr->ntd*ctlr->cls;
5479b12e100SDavid du Colombier 	for(i = 0; i < ctlr->ntd; i++){
5489b12e100SDavid du Colombier 		ds = (Ds*)alloc;
5499b12e100SDavid du Colombier 		alloc += ctlr->cls;
5509b12e100SDavid du Colombier 
5519b12e100SDavid du Colombier 		ds->bounce = bounce;
5529b12e100SDavid du Colombier 		bounce += Txcopy;
5539b12e100SDavid du Colombier 		ds->next = (Ds*)alloc;
5549b12e100SDavid du Colombier 		ds->prev = prev;
5559b12e100SDavid du Colombier 		prev = ds;
5569b12e100SDavid du Colombier 	}
5579b12e100SDavid du Colombier 	prev->next = ctlr->td;
5589b12e100SDavid du Colombier 	ctlr->tdh = ctlr->tdt = ctlr->td;
5599b12e100SDavid du Colombier 	ctlr->tdused = 0;
5609b12e100SDavid du Colombier 
5619b12e100SDavid du Colombier 	ctlr->cr = Dpoll|Rdmd|Txon|Rxon|Strt;
5629b12e100SDavid du Colombier 	/*Srci|Abti|Norbf|Pktrace|Ovfi|Udfi|Be|Ru|Tu|Txe|Rxe|Ptx|Prx*/
5639b12e100SDavid du Colombier 	ctlr->imr = Abti|Norbf|Pktrace|Ovfi|Udfi|Be|Ru|Tu|Txe|Rxe|Ptx|Prx;
5649b12e100SDavid du Colombier 
5659b12e100SDavid du Colombier 	ilock(&ctlr->clock);
5669b12e100SDavid du Colombier 	csr32w(ctlr, Rxdaddr, PCIWADDR(ctlr->rd));
5679b12e100SDavid du Colombier 	csr32w(ctlr, Txdaddr, PCIWADDR(ctlr->td));
5689b12e100SDavid du Colombier 	csr16w(ctlr, Isr, ~0);
5699b12e100SDavid du Colombier 	csr16w(ctlr, Imr, ctlr->imr);
5709b12e100SDavid du Colombier 	csr16w(ctlr, Cr, ctlr->cr);
5719b12e100SDavid du Colombier 	iunlock(&ctlr->clock);
5729b12e100SDavid du Colombier 
5739b12e100SDavid du Colombier 	snprint(name, KNAMELEN, "#l%dlproc", edev->ctlrno);
5749b12e100SDavid du Colombier 	kproc(name, vt6102lproc, edev);
5759b12e100SDavid du Colombier 
5769b12e100SDavid du Colombier 	qunlock(&ctlr->alock);
5779b12e100SDavid du Colombier 	poperror();
5789b12e100SDavid du Colombier }
5799b12e100SDavid du Colombier 
5809b12e100SDavid du Colombier static void
vt6102transmit(Ether * edev)5819b12e100SDavid du Colombier vt6102transmit(Ether* edev)
5829b12e100SDavid du Colombier {
5839b12e100SDavid du Colombier 	Block *bp;
5849b12e100SDavid du Colombier 	Ctlr *ctlr;
5859b12e100SDavid du Colombier 	Ds *ds, *next;
5869b12e100SDavid du Colombier 	int control, i, o, prefix, size, tdused, timeo;
5879b12e100SDavid du Colombier 
5889b12e100SDavid du Colombier 	ctlr = edev->ctlr;
5899b12e100SDavid du Colombier 
5909b12e100SDavid du Colombier 	ilock(&ctlr->tlock);
5919b12e100SDavid du Colombier 
5929b12e100SDavid du Colombier 	/*
5939b12e100SDavid du Colombier 	 * Free any completed packets
5949b12e100SDavid du Colombier 	 */
5959b12e100SDavid du Colombier 	ds = ctlr->tdh;
5969b12e100SDavid du Colombier 	for(tdused = ctlr->tdused; tdused > 0; tdused--){
5979b12e100SDavid du Colombier 		/*
5989b12e100SDavid du Colombier 		 * For some errors the chip will turn the Tx engine
5999b12e100SDavid du Colombier 		 * off. Wait for that to happen.
6009b12e100SDavid du Colombier 		 * Could reset and re-init the chip here if it doesn't
6019b12e100SDavid du Colombier 		 * play fair.
6029b12e100SDavid du Colombier 		 * To do: adjust Tx FIFO threshold on underflow.
6039b12e100SDavid du Colombier 		 */
6049b12e100SDavid du Colombier 		if(ds->status & (Abt|Tbuff|Udf)){
6059b12e100SDavid du Colombier 			for(timeo = 0; timeo < 1000; timeo++){
6069b12e100SDavid du Colombier 				if(!(csr16r(ctlr, Cr) & Txon))
6079b12e100SDavid du Colombier 					break;
6089b12e100SDavid du Colombier 				microdelay(1);
6099b12e100SDavid du Colombier 			}
6109b12e100SDavid du Colombier 			ds->status = Own;
6119b12e100SDavid du Colombier 			csr32w(ctlr, Txdaddr, PCIWADDR(ds));
6129b12e100SDavid du Colombier 		}
6139b12e100SDavid du Colombier 
6149b12e100SDavid du Colombier 		if(ds->status & Own)
6159b12e100SDavid du Colombier 			break;
6169b12e100SDavid du Colombier 		ds->addr = 0;
6179b12e100SDavid du Colombier 		ds->branch = 0;
6189b12e100SDavid du Colombier 
6199b12e100SDavid du Colombier 		if(ds->bp != nil){
6209b12e100SDavid du Colombier 			freeb(ds->bp);
6219b12e100SDavid du Colombier 			ds->bp = nil;
6229b12e100SDavid du Colombier 		}
6239b12e100SDavid du Colombier 		for(i = 0; i < Ntxstats-1; i++){
6249b12e100SDavid du Colombier 			if(ds->status & (1<<i))
6259b12e100SDavid du Colombier 				ctlr->txstats[i]++;
6269b12e100SDavid du Colombier 		}
6279b12e100SDavid du Colombier 		ctlr->txstats[i] += (ds->status & NcrMASK)>>NcrSHIFT;
6289b12e100SDavid du Colombier 
6299b12e100SDavid du Colombier 		ds = ds->next;
6309b12e100SDavid du Colombier 	}
6319b12e100SDavid du Colombier 	ctlr->tdh = ds;
6329b12e100SDavid du Colombier 
6339b12e100SDavid du Colombier 	/*
6349b12e100SDavid du Colombier 	 * Try to fill the ring back up.
6359b12e100SDavid du Colombier 	 */
6369b12e100SDavid du Colombier 	ds = ctlr->tdt;
6379b12e100SDavid du Colombier 	while(tdused < ctlr->ntd-2){
6389b12e100SDavid du Colombier 		if((bp = qget(edev->oq)) == nil)
6399b12e100SDavid du Colombier 			break;
6409b12e100SDavid du Colombier 		tdused++;
6419b12e100SDavid du Colombier 
6429b12e100SDavid du Colombier 		size = BLEN(bp);
6439b12e100SDavid du Colombier 		prefix = 0;
6449b12e100SDavid du Colombier 
6459b12e100SDavid du Colombier 		if(o = (((int)bp->rp) & 0x03)){
6469b12e100SDavid du Colombier 			prefix = Txcopy-o;
6479b12e100SDavid du Colombier 			if(prefix > size)
6489b12e100SDavid du Colombier 				prefix = size;
6499b12e100SDavid du Colombier 			memmove(ds->bounce, bp->rp, prefix);
6509b12e100SDavid du Colombier 			ds->addr = PCIWADDR(ds->bounce);
6519b12e100SDavid du Colombier 			bp->rp += prefix;
6529b12e100SDavid du Colombier 			size -= prefix;
6539b12e100SDavid du Colombier 		}
6549b12e100SDavid du Colombier 
6559b12e100SDavid du Colombier 		next = ds->next;
6569b12e100SDavid du Colombier 		ds->branch = PCIWADDR(ds->next);
6579b12e100SDavid du Colombier 
6589b12e100SDavid du Colombier 		if(size){
6599b12e100SDavid du Colombier 			if(prefix){
6609b12e100SDavid du Colombier 				next->bp = bp;
6619b12e100SDavid du Colombier 				next->addr = PCIWADDR(bp->rp);
6629b12e100SDavid du Colombier 				next->branch = PCIWADDR(next->next);
6639b12e100SDavid du Colombier 				next->control = Edp|Chain|((size<<TbsSHIFT) & TbsMASK);
6649b12e100SDavid du Colombier 
6659b12e100SDavid du Colombier 				control = Stp|Chain|((prefix<<TbsSHIFT) & TbsMASK);
6669b12e100SDavid du Colombier 
6679b12e100SDavid du Colombier 				next = next->next;
6689b12e100SDavid du Colombier 				tdused++;
6699b12e100SDavid du Colombier 				ctlr->tsplit++;
6709b12e100SDavid du Colombier 			}
6719b12e100SDavid du Colombier 			else{
6729b12e100SDavid du Colombier 				ds->bp = bp;
6739b12e100SDavid du Colombier 				ds->addr = PCIWADDR(bp->rp);
6749b12e100SDavid du Colombier 				control = Edp|Stp|((size<<TbsSHIFT) & TbsMASK);
6759b12e100SDavid du Colombier 				ctlr->taligned++;
6769b12e100SDavid du Colombier 			}
6779b12e100SDavid du Colombier 		}
6789b12e100SDavid du Colombier 		else{
6799b12e100SDavid du Colombier 			freeb(bp);
6809b12e100SDavid du Colombier 			control = Edp|Stp|((prefix<<TbsSHIFT) & TbsMASK);
6819b12e100SDavid du Colombier 			ctlr->tcopied++;
6829b12e100SDavid du Colombier 		}
6839b12e100SDavid du Colombier 
6849b12e100SDavid du Colombier 		ds->control = control;
6859b12e100SDavid du Colombier 		if(tdused >= ctlr->ntd-2){
6869b12e100SDavid du Colombier 			ds->control |= Ic;
6879b12e100SDavid du Colombier 			ctlr->txdw++;
6889b12e100SDavid du Colombier 		}
6899b12e100SDavid du Colombier 		coherence();
6909b12e100SDavid du Colombier 		ds->status = Own;
6919b12e100SDavid du Colombier 
6929b12e100SDavid du Colombier 		ds = next;
6939b12e100SDavid du Colombier 	}
6949b12e100SDavid du Colombier 	ctlr->tdt = ds;
6959b12e100SDavid du Colombier 	ctlr->tdused = tdused;
6969b12e100SDavid du Colombier 	if(ctlr->tdused)
6979b12e100SDavid du Colombier 		csr16w(ctlr, Cr, Tdmd|ctlr->cr);
6989b12e100SDavid du Colombier 
6999b12e100SDavid du Colombier 	iunlock(&ctlr->tlock);
7009b12e100SDavid du Colombier }
7019b12e100SDavid du Colombier 
7029b12e100SDavid du Colombier static void
vt6102receive(Ether * edev)7039b12e100SDavid du Colombier vt6102receive(Ether* edev)
7049b12e100SDavid du Colombier {
7059b12e100SDavid du Colombier 	Ds *ds;
7069b12e100SDavid du Colombier 	Block *bp;
7079b12e100SDavid du Colombier 	Ctlr *ctlr;
7089b12e100SDavid du Colombier 	int i, len;
7099b12e100SDavid du Colombier 
7109b12e100SDavid du Colombier 	ctlr = edev->ctlr;
7119b12e100SDavid du Colombier 
7129b12e100SDavid du Colombier 	ds = ctlr->rdh;
7139b12e100SDavid du Colombier 	while(!(ds->status & Own) && ds->status != 0){
7149b12e100SDavid du Colombier 		if(ds->status & Rerr){
7159b12e100SDavid du Colombier 			for(i = 0; i < Nrxstats; i++){
7169b12e100SDavid du Colombier 				if(ds->status & (1<<i))
7179b12e100SDavid du Colombier 					ctlr->rxstats[i]++;
7189b12e100SDavid du Colombier 			}
7199b12e100SDavid du Colombier 		}
7209b12e100SDavid du Colombier 		else if(bp = iallocb(Rdbsz+3)){
7219b12e100SDavid du Colombier 			len = ((ds->status & LengthMASK)>>LengthSHIFT)-4;
7229b12e100SDavid du Colombier 			ds->bp->wp = ds->bp->rp+len;
7239b12e100SDavid du Colombier 			etheriq(edev, ds->bp, 1);
7249b12e100SDavid du Colombier 			bp->rp = (uchar*)ROUNDUP((ulong)bp->rp, 4);
7259b12e100SDavid du Colombier 			ds->addr = PCIWADDR(bp->rp);
7269b12e100SDavid du Colombier 			ds->bp = bp;
7279b12e100SDavid du Colombier 		}
7289b12e100SDavid du Colombier 		ds->control = Rdbsz;
7299b12e100SDavid du Colombier 		ds->branch = 0;
7309b12e100SDavid du Colombier 		ds->status = 0;
7319b12e100SDavid du Colombier 
7329b12e100SDavid du Colombier 		ds->prev->branch = PCIWADDR(ds);
7339b12e100SDavid du Colombier 		coherence();
7349b12e100SDavid du Colombier 		ds->prev->status = Own;
7359b12e100SDavid du Colombier 
7369b12e100SDavid du Colombier 		ds = ds->next;
7379b12e100SDavid du Colombier 	}
7389b12e100SDavid du Colombier 	ctlr->rdh = ds;
7399b12e100SDavid du Colombier 
7409b12e100SDavid du Colombier 	csr16w(ctlr, Cr, ctlr->cr);
7419b12e100SDavid du Colombier }
7429b12e100SDavid du Colombier 
7439b12e100SDavid du Colombier static void
vt6102interrupt(Ureg *,void * arg)7449b12e100SDavid du Colombier vt6102interrupt(Ureg*, void* arg)
7459b12e100SDavid du Colombier {
7469b12e100SDavid du Colombier 	Ctlr *ctlr;
7479b12e100SDavid du Colombier 	Ether *edev;
7489b12e100SDavid du Colombier 	int imr, isr, r, timeo;
7499b12e100SDavid du Colombier 
7509b12e100SDavid du Colombier 	edev = arg;
7519b12e100SDavid du Colombier 	ctlr = edev->ctlr;
7529b12e100SDavid du Colombier 
7539b12e100SDavid du Colombier 	ilock(&ctlr->clock);
7549b12e100SDavid du Colombier 	csr16w(ctlr, Imr, 0);
7559b12e100SDavid du Colombier 	imr = ctlr->imr;
7569b12e100SDavid du Colombier 	ctlr->intr++;
7579b12e100SDavid du Colombier 	for(;;){
7589b12e100SDavid du Colombier 		if((isr = csr16r(ctlr, Isr)) != 0)
7599b12e100SDavid du Colombier 			csr16w(ctlr, Isr, isr);
7609b12e100SDavid du Colombier 		if((isr & ctlr->imr) == 0)
7619b12e100SDavid du Colombier 			break;
7629b12e100SDavid du Colombier 
7639b12e100SDavid du Colombier 		if(isr & Srci){
7649b12e100SDavid du Colombier 			imr &= ~Srci;
7659b12e100SDavid du Colombier 			ctlr->lwakeup = isr & Srci;
7669b12e100SDavid du Colombier 			wakeup(&ctlr->lrendez);
7679b12e100SDavid du Colombier 			isr &= ~Srci;
7689b12e100SDavid du Colombier 			ctlr->lintr++;
7699b12e100SDavid du Colombier 		}
7709b12e100SDavid du Colombier 		if(isr & (Norbf|Pktrace|Ovfi|Ru|Rxe|Prx)){
7719b12e100SDavid du Colombier 			vt6102receive(edev);
7729b12e100SDavid du Colombier 			isr &= ~(Norbf|Pktrace|Ovfi|Ru|Rxe|Prx);
7739b12e100SDavid du Colombier 			ctlr->rintr++;
7749b12e100SDavid du Colombier 		}
7759b12e100SDavid du Colombier 		if(isr & (Abti|Udfi|Tu|Txe|Ptx)){
7769b12e100SDavid du Colombier 			if(isr & (Abti|Udfi|Tu)){
7779b12e100SDavid du Colombier 				for(timeo = 0; timeo < 1000; timeo++){
7789b12e100SDavid du Colombier 					if(!(csr16r(ctlr, Cr) & Txon))
7799b12e100SDavid du Colombier 						break;
7809b12e100SDavid du Colombier 					microdelay(1);
7819b12e100SDavid du Colombier 				}
7829b12e100SDavid du Colombier 
7839b12e100SDavid du Colombier 				if((isr & Udfi) && ctlr->tft < CtftSAF){
7849b12e100SDavid du Colombier 					ctlr->tft += 1<<CtftSHIFT;
7859b12e100SDavid du Colombier 					r = csr8r(ctlr, Bcr1) & ~CtftMASK;
7869b12e100SDavid du Colombier 					csr8w(ctlr, Bcr1, r|ctlr->tft);
7879b12e100SDavid du Colombier 				}
7889b12e100SDavid du Colombier 			}
7899b12e100SDavid du Colombier 			vt6102transmit(edev);
7909b12e100SDavid du Colombier 			isr &= ~(Abti|Udfi|Tu|Txe|Ptx);
7919b12e100SDavid du Colombier 			ctlr->tintr++;
7929b12e100SDavid du Colombier 		}
79326ad7229SDavid du Colombier 		if(isr)
7949b12e100SDavid du Colombier 			panic("vt6102: isr %4.4uX\n", isr);
7959b12e100SDavid du Colombier 	}
7969b12e100SDavid du Colombier 	ctlr->imr = imr;
7979b12e100SDavid du Colombier 	csr16w(ctlr, Imr, ctlr->imr);
7989b12e100SDavid du Colombier 	iunlock(&ctlr->clock);
7999b12e100SDavid du Colombier }
8009b12e100SDavid du Colombier 
8019b12e100SDavid du Colombier static int
vt6102miimicmd(Mii * mii,int pa,int ra,int cmd,int data)8029b12e100SDavid du Colombier vt6102miimicmd(Mii* mii, int pa, int ra, int cmd, int data)
8039b12e100SDavid du Colombier {
8049b12e100SDavid du Colombier 	Ctlr *ctlr;
8059b12e100SDavid du Colombier 	int r, timeo;
8069b12e100SDavid du Colombier 
8079b12e100SDavid du Colombier 	ctlr = mii->ctlr;
8089b12e100SDavid du Colombier 
8099b12e100SDavid du Colombier 	csr8w(ctlr, Miicr, 0);
8109b12e100SDavid du Colombier 	r = csr8r(ctlr, Phyadr);
8119b12e100SDavid du Colombier 	csr8w(ctlr, Phyadr, (r & ~PhyadMASK)|pa);
8129b12e100SDavid du Colombier 	csr8w(ctlr, Phyadr, pa);
8139b12e100SDavid du Colombier 	csr8w(ctlr, Miiadr, ra);
8149b12e100SDavid du Colombier 	if(cmd == Wcmd)
8159b12e100SDavid du Colombier 		csr16w(ctlr, Miidata, data);
8169b12e100SDavid du Colombier 	csr8w(ctlr, Miicr, cmd);
8179b12e100SDavid du Colombier 
8189b12e100SDavid du Colombier 	for(timeo = 0; timeo < 10000; timeo++){
8199b12e100SDavid du Colombier 		if(!(csr8r(ctlr, Miicr) & cmd))
8209b12e100SDavid du Colombier 			break;
8219b12e100SDavid du Colombier 		microdelay(1);
8229b12e100SDavid du Colombier 	}
8239b12e100SDavid du Colombier 	if(timeo >= 10000)
8249b12e100SDavid du Colombier 		return -1;
8259b12e100SDavid du Colombier 
8269b12e100SDavid du Colombier 	if(cmd == Wcmd)
8279b12e100SDavid du Colombier 		return 0;
8289b12e100SDavid du Colombier 	return csr16r(ctlr, Miidata);
8299b12e100SDavid du Colombier }
8309b12e100SDavid du Colombier 
8319b12e100SDavid du Colombier static int
vt6102miimir(Mii * mii,int pa,int ra)8329b12e100SDavid du Colombier vt6102miimir(Mii* mii, int pa, int ra)
8339b12e100SDavid du Colombier {
8349b12e100SDavid du Colombier 	return vt6102miimicmd(mii, pa, ra, Rcmd, 0);
8359b12e100SDavid du Colombier }
8369b12e100SDavid du Colombier 
8379b12e100SDavid du Colombier static int
vt6102miimiw(Mii * mii,int pa,int ra,int data)8389b12e100SDavid du Colombier vt6102miimiw(Mii* mii, int pa, int ra, int data)
8399b12e100SDavid du Colombier {
8409b12e100SDavid du Colombier 	return vt6102miimicmd(mii, pa, ra, Wcmd, data);
8419b12e100SDavid du Colombier }
8429b12e100SDavid du Colombier 
8439b12e100SDavid du Colombier static int
vt6102detach(Ctlr * ctlr)8449b12e100SDavid du Colombier vt6102detach(Ctlr* ctlr)
8459b12e100SDavid du Colombier {
84676c9a8f8SDavid du Colombier 	int revid, timeo;
84776c9a8f8SDavid du Colombier 
84876c9a8f8SDavid du Colombier 	/*
84976c9a8f8SDavid du Colombier 	 * Reset power management registers.
85076c9a8f8SDavid du Colombier 	 */
85176c9a8f8SDavid du Colombier 	revid = pcicfgr8(ctlr->pcidev, PciRID);
85276c9a8f8SDavid du Colombier 	if(revid >= 0x40){
85376c9a8f8SDavid du Colombier 		/* Set power state D0. */
85476c9a8f8SDavid du Colombier 		csr8w(ctlr, Stickhw, csr8r(ctlr, Stickhw) & 0xFC);
85576c9a8f8SDavid du Colombier 
85676c9a8f8SDavid du Colombier 		/* Disable force PME-enable. */
85776c9a8f8SDavid du Colombier 		csr8w(ctlr, Wolcgclr, 0x80);
85876c9a8f8SDavid du Colombier 
85976c9a8f8SDavid du Colombier 		/* Clear WOL config and status bits. */
86076c9a8f8SDavid du Colombier 		csr8w(ctlr, Wolcrclr, 0xFF);
86176c9a8f8SDavid du Colombier 		csr8w(ctlr, Pwrcsrclr, 0xFF);
86276c9a8f8SDavid du Colombier 	}
8639b12e100SDavid du Colombier 
8649b12e100SDavid du Colombier 	/*
8659b12e100SDavid du Colombier 	 * Soft reset the controller.
8669b12e100SDavid du Colombier 	 */
8677e254d1cSDavid du Colombier 	csr16w(ctlr, Cr, Stop);
8687e254d1cSDavid du Colombier 	csr16w(ctlr, Cr, Stop|Sfrst);
8699b12e100SDavid du Colombier 	for(timeo = 0; timeo < 10000; timeo++){
8709b12e100SDavid du Colombier 		if(!(csr16r(ctlr, Cr) & Sfrst))
8719b12e100SDavid du Colombier 			break;
8729b12e100SDavid du Colombier 		microdelay(1);
8739b12e100SDavid du Colombier 	}
8749b12e100SDavid du Colombier 	if(timeo >= 1000)
8759b12e100SDavid du Colombier 		return -1;
8769b12e100SDavid du Colombier 
8779b12e100SDavid du Colombier 	return 0;
8789b12e100SDavid du Colombier }
8799b12e100SDavid du Colombier 
8800a2a9dafSDavid du Colombier static void
vt6102shutdown(Ether * ether)8810a2a9dafSDavid du Colombier vt6102shutdown(Ether *ether)
8820a2a9dafSDavid du Colombier {
8830a2a9dafSDavid du Colombier 	Ctlr *ctlr = ether->ctlr;
8840a2a9dafSDavid du Colombier 
8850a2a9dafSDavid du Colombier 	vt6102detach(ctlr);
8860a2a9dafSDavid du Colombier }
8870a2a9dafSDavid du Colombier 
8889b12e100SDavid du Colombier static int
vt6102reset(Ctlr * ctlr)8899b12e100SDavid du Colombier vt6102reset(Ctlr* ctlr)
8909b12e100SDavid du Colombier {
8919b12e100SDavid du Colombier 	MiiPhy *phy;
8929b12e100SDavid du Colombier 	int i, r, timeo;
8939b12e100SDavid du Colombier 
8949b12e100SDavid du Colombier 	if(vt6102detach(ctlr) < 0)
8959b12e100SDavid du Colombier 		return -1;
8969b12e100SDavid du Colombier 
8979b12e100SDavid du Colombier 	/*
8989b12e100SDavid du Colombier 	 * Load the MAC address into the PAR[01]
8999b12e100SDavid du Colombier 	 * registers.
9009b12e100SDavid du Colombier 	 */
9019b12e100SDavid du Colombier 	r = csr8r(ctlr, Eecsr);
9029b12e100SDavid du Colombier 	csr8w(ctlr, Eecsr, Autold|r);
9039b12e100SDavid du Colombier 	for(timeo = 0; timeo < 100; timeo++){
9049b12e100SDavid du Colombier 		if(!(csr8r(ctlr, Cr) & Autold))
9059b12e100SDavid du Colombier 			break;
9069b12e100SDavid du Colombier 		microdelay(1);
9079b12e100SDavid du Colombier 	}
9089b12e100SDavid du Colombier 	if(timeo >= 100)
9099b12e100SDavid du Colombier 		return -1;
9109b12e100SDavid du Colombier 
9119b12e100SDavid du Colombier 	for(i = 0; i < Eaddrlen; i++)
9129b12e100SDavid du Colombier 		ctlr->par[i] = csr8r(ctlr, Par0+i);
9139b12e100SDavid du Colombier 
9149b12e100SDavid du Colombier 	/*
9159b12e100SDavid du Colombier 	 * Configure DMA and Rx/Tx thresholds.
9169b12e100SDavid du Colombier 	 * If the Rx/Tx threshold bits in Bcr[01] are 0 then
9179b12e100SDavid du Colombier 	 * the thresholds are determined by Rcr/Tcr.
9189b12e100SDavid du Colombier 	 */
9199b12e100SDavid du Colombier 	r = csr8r(ctlr, Bcr0) & ~(CrftMASK|DmaMASK);
9209b12e100SDavid du Colombier 	csr8w(ctlr, Bcr0, r|Crft64|Dma64);
9219b12e100SDavid du Colombier 	r = csr8r(ctlr, Bcr1) & ~CtftMASK;
9229b12e100SDavid du Colombier 	csr8w(ctlr, Bcr1, r|ctlr->tft);
9239b12e100SDavid du Colombier 
9249b12e100SDavid du Colombier 	r = csr8r(ctlr, Rcr) & ~(RrftMASK|Prom|Ar|Sep);
9259b12e100SDavid du Colombier 	csr8w(ctlr, Rcr, r|Ab|Am);
9267e254d1cSDavid du Colombier 	csr32w(ctlr, Mcfilt0, ~0UL);	/* accept all multicast */
9277e254d1cSDavid du Colombier 	csr32w(ctlr, Mcfilt1, ~0UL);
9289b12e100SDavid du Colombier 
9299b12e100SDavid du Colombier 	r = csr8r(ctlr, Tcr) & ~(RtsfMASK|Ofset|Lb1|Lb0);
9309b12e100SDavid du Colombier 	csr8w(ctlr, Tcr, r);
9319b12e100SDavid du Colombier 
9329b12e100SDavid du Colombier 	/*
9339b12e100SDavid du Colombier 	 * Link management.
9349b12e100SDavid du Colombier 	 */
9359b12e100SDavid du Colombier 	if((ctlr->mii = malloc(sizeof(Mii))) == nil)
9369b12e100SDavid du Colombier 		return -1;
9379b12e100SDavid du Colombier 	ctlr->mii->mir = vt6102miimir;
9389b12e100SDavid du Colombier 	ctlr->mii->miw = vt6102miimiw;
9399b12e100SDavid du Colombier 	ctlr->mii->ctlr = ctlr;
9409b12e100SDavid du Colombier 
9419b12e100SDavid du Colombier 	if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){
9429b12e100SDavid du Colombier 		free(ctlr->mii);
9439b12e100SDavid du Colombier 		ctlr->mii = nil;
9449b12e100SDavid du Colombier 		return -1;
9459b12e100SDavid du Colombier 	}
9464de34a7eSDavid du Colombier 	// print("oui %X phyno %d\n", phy->oui, phy->phyno);
94726ad7229SDavid du Colombier 	USED(phy);
9489b12e100SDavid du Colombier 
9499b12e100SDavid du Colombier 	//miiane(ctlr->mii, ~0, ~0, ~0);
9509b12e100SDavid du Colombier 
9519b12e100SDavid du Colombier 	return 0;
9529b12e100SDavid du Colombier }
9539b12e100SDavid du Colombier 
9549b12e100SDavid du Colombier static void
vt6102pci(void)9559b12e100SDavid du Colombier vt6102pci(void)
9569b12e100SDavid du Colombier {
9579b12e100SDavid du Colombier 	Pcidev *p;
9589b12e100SDavid du Colombier 	Ctlr *ctlr;
9599b12e100SDavid du Colombier 	int cls, port;
9609b12e100SDavid du Colombier 
9619b12e100SDavid du Colombier 	p = nil;
9629b12e100SDavid du Colombier 	while(p = pcimatch(p, 0, 0)){
963cbc16779SDavid du Colombier 		if(p->ccrb != Pcibcnet || p->ccru != Pciscether)
9649b12e100SDavid du Colombier 			continue;
9659b12e100SDavid du Colombier 
9669b12e100SDavid du Colombier 		switch((p->did<<16)|p->vid){
9679b12e100SDavid du Colombier 		default:
9689b12e100SDavid du Colombier 			continue;
9699b12e100SDavid du Colombier 		case (0x3065<<16)|0x1106:	/* Rhine II */
970ea15f0ccSDavid du Colombier 		case (0x3106<<16)|0x1106:	/* Rhine III */
9719b12e100SDavid du Colombier 			break;
9729b12e100SDavid du Colombier 		}
9739b12e100SDavid du Colombier 
9749b12e100SDavid du Colombier 		port = p->mem[0].bar & ~0x01;
9759b12e100SDavid du Colombier 		if(ioalloc(port, p->mem[0].size, 0, "vt6102") < 0){
9769b12e100SDavid du Colombier 			print("vt6102: port 0x%uX in use\n", port);
9779b12e100SDavid du Colombier 			continue;
9789b12e100SDavid du Colombier 		}
9799b12e100SDavid du Colombier 		ctlr = malloc(sizeof(Ctlr));
980aa72973aSDavid du Colombier 		if(ctlr == nil) {
981aa72973aSDavid du Colombier 			iofree(port);
982aa72973aSDavid du Colombier 			error(Enomem);
983aa72973aSDavid du Colombier 		}
9849b12e100SDavid du Colombier 		ctlr->port = port;
9859b12e100SDavid du Colombier 		ctlr->pcidev = p;
9869b12e100SDavid du Colombier 		ctlr->id = (p->did<<16)|p->vid;
9879b12e100SDavid du Colombier 		if((cls = pcicfgr8(p, PciCLS)) == 0 || cls == 0xFF)
9889b12e100SDavid du Colombier 			cls = 0x10;
9899b12e100SDavid du Colombier 		ctlr->cls = cls*4;
9909b12e100SDavid du Colombier 		if(ctlr->cls < sizeof(Ds)){
9919b12e100SDavid du Colombier 			print("vt6102: cls %d < sizeof(Ds)\n", ctlr->cls);
992cbc16779SDavid du Colombier 			iofree(port);
9939b12e100SDavid du Colombier 			free(ctlr);
9949b12e100SDavid du Colombier 			continue;
9959b12e100SDavid du Colombier 		}
9969b12e100SDavid du Colombier 		ctlr->tft = Ctft64;
9979b12e100SDavid du Colombier 
9989b12e100SDavid du Colombier 		if(vt6102reset(ctlr)){
999cbc16779SDavid du Colombier 			iofree(port);
10009b12e100SDavid du Colombier 			free(ctlr);
10019b12e100SDavid du Colombier 			continue;
10029b12e100SDavid du Colombier 		}
10039b12e100SDavid du Colombier 		pcisetbme(p);
10049b12e100SDavid du Colombier 
10059b12e100SDavid du Colombier 		if(vt6102ctlrhead != nil)
10069b12e100SDavid du Colombier 			vt6102ctlrtail->next = ctlr;
10079b12e100SDavid du Colombier 		else
10089b12e100SDavid du Colombier 			vt6102ctlrhead = ctlr;
10099b12e100SDavid du Colombier 		vt6102ctlrtail = ctlr;
10109b12e100SDavid du Colombier 	}
10119b12e100SDavid du Colombier }
10129b12e100SDavid du Colombier 
10139b12e100SDavid du Colombier static int
vt6102pnp(Ether * edev)10149b12e100SDavid du Colombier vt6102pnp(Ether* edev)
10159b12e100SDavid du Colombier {
10169b12e100SDavid du Colombier 	Ctlr *ctlr;
10179b12e100SDavid du Colombier 
10189b12e100SDavid du Colombier 	if(vt6102ctlrhead == nil)
10199b12e100SDavid du Colombier 		vt6102pci();
10209b12e100SDavid du Colombier 
10219b12e100SDavid du Colombier 	/*
10229b12e100SDavid du Colombier 	 * Any adapter matches if no edev->port is supplied,
10239b12e100SDavid du Colombier 	 * otherwise the ports must match.
10249b12e100SDavid du Colombier 	 */
10259b12e100SDavid du Colombier 	for(ctlr = vt6102ctlrhead; ctlr != nil; ctlr = ctlr->next){
10269b12e100SDavid du Colombier 		if(ctlr->active)
10279b12e100SDavid du Colombier 			continue;
10289b12e100SDavid du Colombier 		if(edev->port == 0 || edev->port == ctlr->port){
10299b12e100SDavid du Colombier 			ctlr->active = 1;
10309b12e100SDavid du Colombier 			break;
10319b12e100SDavid du Colombier 		}
10329b12e100SDavid du Colombier 	}
10339b12e100SDavid du Colombier 	if(ctlr == nil)
10349b12e100SDavid du Colombier 		return -1;
10359b12e100SDavid du Colombier 
10369b12e100SDavid du Colombier 	edev->ctlr = ctlr;
10379b12e100SDavid du Colombier 	edev->port = ctlr->port;
10389b12e100SDavid du Colombier 	edev->irq = ctlr->pcidev->intl;
10399b12e100SDavid du Colombier 	edev->tbdf = ctlr->pcidev->tbdf;
10409b12e100SDavid du Colombier 	edev->mbps = 100;
10419b12e100SDavid du Colombier 	memmove(edev->ea, ctlr->par, Eaddrlen);
10429b12e100SDavid du Colombier 
10439b12e100SDavid du Colombier 	/*
10449b12e100SDavid du Colombier 	 * Linkage to the generic ethernet driver.
10459b12e100SDavid du Colombier 	 */
10469b12e100SDavid du Colombier 	edev->attach = vt6102attach;
10479b12e100SDavid du Colombier 	edev->transmit = vt6102transmit;
10489b12e100SDavid du Colombier 	edev->interrupt = vt6102interrupt;
10499b12e100SDavid du Colombier 	edev->ifstat = vt6102ifstat;
10500a2a9dafSDavid du Colombier 	edev->shutdown = vt6102shutdown;
10519b12e100SDavid du Colombier 	edev->ctl = nil;
10529b12e100SDavid du Colombier 
10539b12e100SDavid du Colombier 	edev->arg = edev;
10549b12e100SDavid du Colombier 	edev->promiscuous = vt6102promiscuous;
10559b12e100SDavid du Colombier 	edev->multicast = vt6102multicast;
10569b12e100SDavid du Colombier 
10579b12e100SDavid du Colombier 	return 0;
10589b12e100SDavid du Colombier }
10599b12e100SDavid du Colombier 
10609b12e100SDavid du Colombier void
ethervt6102link(void)10619b12e100SDavid du Colombier ethervt6102link(void)
10629b12e100SDavid du Colombier {
10639b12e100SDavid du Colombier 	addethercard("vt6102", vt6102pnp);
10649b12e100SDavid du Colombier 	addethercard("rhine", vt6102pnp);
10659b12e100SDavid du Colombier }
1066