xref: /plan9/sys/src/9/pc/ethervt6105m.c (revision 5b79763299e9ee505816eaec895aa7ccdd6561ca)
172cfc098SDavid du Colombier /*
272cfc098SDavid du Colombier  * VIA VT6105M Fast Ethernet Controller (Rhine III).
372cfc098SDavid du Colombier  * To do:
472cfc098SDavid du Colombier  *	reorganise initialisation/shutdown/reset
572cfc098SDavid du Colombier  *	adjust Tx FIFO threshold on underflow - untested
672cfc098SDavid du Colombier  *	why does the link status never cause an interrupt?
772cfc098SDavid du Colombier  *	use the lproc as a periodic timer for stalls, etc.
872cfc098SDavid du Colombier  *	take non-HW stuff out of descriptor for 64-bit
972cfc098SDavid du Colombier  *	cleanliness
1072cfc098SDavid du Colombier  *	why does the receive buffer alloc have a +3?
1172cfc098SDavid du Colombier  */
1272cfc098SDavid du Colombier #include "u.h"
1372cfc098SDavid du Colombier #include "../port/lib.h"
1472cfc098SDavid du Colombier #include "mem.h"
1572cfc098SDavid du Colombier #include "dat.h"
1672cfc098SDavid du Colombier #include "fns.h"
1772cfc098SDavid du Colombier #include "io.h"
1872cfc098SDavid du Colombier #include "../port/error.h"
1972cfc098SDavid du Colombier #include "../port/netif.h"
2072cfc098SDavid du Colombier 
2172cfc098SDavid du Colombier #include "etherif.h"
2272cfc098SDavid du Colombier #include "ethermii.h"
2372cfc098SDavid du Colombier 
2472cfc098SDavid du Colombier enum {
2572cfc098SDavid du Colombier 	Par0		= 0x00,			/* Ethernet Address */
2672cfc098SDavid du Colombier 	Rcr		= 0x06,			/* Receive Configuration */
2772cfc098SDavid du Colombier 	Tcr		= 0x07,			/* Transmit Configuration */
2872cfc098SDavid du Colombier 	Cr		= 0x08,			/* Control */
2972cfc098SDavid du Colombier 	Tqw		= 0x0A,			/* Transmit Queue Wake */
3072cfc098SDavid du Colombier 	Isr		= 0x0C,			/* Interrupt Status */
3172cfc098SDavid du Colombier 	Imr		= 0x0E,			/* Interrupt Mask */
3272cfc098SDavid du Colombier 	Mcfilt0		= 0x10,			/* Multicast Filter 0 */
3372cfc098SDavid du Colombier 	Mcfilt1		= 0x14,			/* Multicast Filter 1 */
3472cfc098SDavid du Colombier 	Rxdaddr		= 0x18,			/* Current Rd Address */
3572cfc098SDavid du Colombier 	Txdaddr		= 0x1C,			/* Current Td Address */
3672cfc098SDavid du Colombier 	Phyadr		= 0x6C,			/* Phy Address */
3772cfc098SDavid du Colombier 	Miisr		= 0x6D,			/* MII Status */
3872cfc098SDavid du Colombier 	Bcr0		= 0x6E,			/* Bus Control */
3972cfc098SDavid du Colombier 	Bcr1		= 0x6F,
4072cfc098SDavid du Colombier 	Miicr		= 0x70,			/* MII Control */
4172cfc098SDavid du Colombier 	Miiadr		= 0x71,			/* MII Address */
4272cfc098SDavid du Colombier 	Miidata		= 0x72,			/* MII Data */
4372cfc098SDavid du Colombier 	Eecsr		= 0x74,			/* EEPROM Control and Status */
4472cfc098SDavid du Colombier 	CfgA		= 0x78,			/* Chip Configuration A */
4572cfc098SDavid du Colombier 	CfgB		= 0x79,
4672cfc098SDavid du Colombier 	CfgC		= 0x7A,
4772cfc098SDavid du Colombier 	CfgD		= 0x7B,
4872cfc098SDavid du Colombier 	Cr0		= 0x80,			/* Miscellaneous Control */
4972cfc098SDavid du Colombier 	Cr1		= 0x81,
5072cfc098SDavid du Colombier 	Pmcc		= 0x82,			/* Power Mgmt Capability Control */
5172cfc098SDavid du Colombier 	Stickhw		= 0x83,			/* Sticky Hardware Control */
5272cfc098SDavid du Colombier 	Misr		= 0x84,			/* MII Interrupt Control */
5372cfc098SDavid du Colombier 	Mimr		= 0x85,			/* MII Interrupt Mask */
5472cfc098SDavid du Colombier 	Wolcrclr	= 0xA4,
5572cfc098SDavid du Colombier 	Wolcgclr	= 0xA7,
5672cfc098SDavid du Colombier 	Pwrcsrclr	= 0xAC,
5772cfc098SDavid du Colombier };
5872cfc098SDavid du Colombier 
5972cfc098SDavid du Colombier enum {						/* Rcr */
6072cfc098SDavid du Colombier 	Sep		= 0x01,			/* Accept Error Packets */
6172cfc098SDavid du Colombier 	Ar		= 0x02,			/* Accept Small Packets */
6272cfc098SDavid du Colombier 	Am		= 0x04,			/* Accept Multicast */
6372cfc098SDavid du Colombier 	Ab		= 0x08,			/* Accept Broadcast */
6472cfc098SDavid du Colombier 	Prom		= 0x10,			/* Accept Physical Address Packets */
6572cfc098SDavid du Colombier 	RrftMASK	= 0xE0,			/* Receive FIFO Threshold */
6672cfc098SDavid du Colombier 	RrftSHIFT	= 5,
6772cfc098SDavid du Colombier 	Rrft64		= 0<<RrftSHIFT,
6872cfc098SDavid du Colombier 	Rrft32		= 1<<RrftSHIFT,
6972cfc098SDavid du Colombier 	Rrft128		= 2<<RrftSHIFT,
7072cfc098SDavid du Colombier 	Rrft256		= 3<<RrftSHIFT,
7172cfc098SDavid du Colombier 	Rrft512		= 4<<RrftSHIFT,
7272cfc098SDavid du Colombier 	Rrft768		= 5<<RrftSHIFT,
7372cfc098SDavid du Colombier 	Rrft1024	= 6<<RrftSHIFT,
7472cfc098SDavid du Colombier 	RrftSAF		= 7<<RrftSHIFT,
7572cfc098SDavid du Colombier };
7672cfc098SDavid du Colombier 
7772cfc098SDavid du Colombier enum {						/* Tcr */
7872cfc098SDavid du Colombier 	Lb0		= 0x02,			/* Loopback Mode */
7972cfc098SDavid du Colombier 	Lb1		= 0x04,
8072cfc098SDavid du Colombier 	Ofset		= 0x08,			/* Select Back-off Priority */
8172cfc098SDavid du Colombier 	RtsfMASK	= 0xE0,			/* Transmit FIFO Threshold */
8272cfc098SDavid du Colombier 	RtsfSHIFT	= 5,
8372cfc098SDavid du Colombier 	Rtsf128		= 0<<RtsfSHIFT,
8472cfc098SDavid du Colombier 	Rtsf256		= 1<<RtsfSHIFT,
8572cfc098SDavid du Colombier 	Rtsf512		= 2<<RtsfSHIFT,
8672cfc098SDavid du Colombier 	Rtsf1024	= 3<<RtsfSHIFT,
8772cfc098SDavid du Colombier 	RtsfSAF		= 7<<RtsfSHIFT,
8872cfc098SDavid du Colombier };
8972cfc098SDavid du Colombier 
9072cfc098SDavid du Colombier enum {						/* Cr */
9172cfc098SDavid du Colombier 	Init		= 0x0001,		/* INIT Process Begin */
9272cfc098SDavid du Colombier 	Strt		= 0x0002,		/* Start NIC */
9372cfc098SDavid du Colombier 	Stop		= 0x0004,		/* Stop NIC */
9472cfc098SDavid du Colombier 	Rxon		= 0x0008,		/* Turn on Receive Process */
9572cfc098SDavid du Colombier 	Txon		= 0x0010,		/* Turn on Transmit Process */
9672cfc098SDavid du Colombier 	Tdmd		= 0x0020,		/* Transmit Poll Demand */
9772cfc098SDavid du Colombier 	Rdmd		= 0x0040,		/* Receive Poll Demand */
9872cfc098SDavid du Colombier 	Eren		= 0x0100,		/* Early Receive Enable */
9972cfc098SDavid du Colombier 	Fdx		= 0x0400,		/* Set MAC to Full Duplex */
10072cfc098SDavid du Colombier 	Dpoll		= 0x0800,		/* Disable Td/Rd Auto Polling */
10172cfc098SDavid du Colombier 	Tdmd1		= 0x2000,		/* Transmit Poll Demand 1 */
10272cfc098SDavid du Colombier 	Rdmd1		= 0x4000,		/* Receive Poll Demand 1 */
10372cfc098SDavid du Colombier 	Sfrst		= 0x8000,		/* Software Reset */
10472cfc098SDavid du Colombier };
10572cfc098SDavid du Colombier 
10672cfc098SDavid du Colombier enum {						/* Isr/Imr */
10772cfc098SDavid du Colombier 	Prx		= 0x0001,		/* Packet Received OK */
10872cfc098SDavid du Colombier 	Ptx		= 0x0002,		/* Packet Transmitted OK */
10972cfc098SDavid du Colombier 	Rxe		= 0x0004,		/* Receive Error */
11072cfc098SDavid du Colombier 	Txe		= 0x0008,		/* Transmit Error */
11172cfc098SDavid du Colombier 	Tu		= 0x0010,		/* Transmit Buffer Underflow */
11272cfc098SDavid du Colombier 	Ru		= 0x0020,		/* Receive Buffer Link Error */
11372cfc098SDavid du Colombier 	Be		= 0x0040,		/* PCI Bus Error */
11472cfc098SDavid du Colombier 	Cnt		= 0x0080,		/* Counter Overflow */
11572cfc098SDavid du Colombier 	Eri		= 0x0100,		/* Early Receive Interrupt */
11672cfc098SDavid du Colombier 	Udfi		= 0x0200,		/* Tx FIFO Underflow */
11772cfc098SDavid du Colombier 	Ovfi		= 0x0400,		/* Receive FIFO Overflow */
11872cfc098SDavid du Colombier 	Pktrace		= 0x0800,		/* Hmmm... */
11972cfc098SDavid du Colombier 	Norbf		= 0x1000,		/* No Receive Buffers */
12072cfc098SDavid du Colombier 	Abti		= 0x2000,		/* Transmission Abort */
12172cfc098SDavid du Colombier 	Srci		= 0x4000,		/* Port State Change */
12272cfc098SDavid du Colombier 	Geni		= 0x8000,		/* General Purpose Interrupt */
12372cfc098SDavid du Colombier };
12472cfc098SDavid du Colombier 
12572cfc098SDavid du Colombier enum {						/* Phyadr */
12672cfc098SDavid du Colombier 	PhyadMASK	= 0x1F,			/* PHY Address */
12772cfc098SDavid du Colombier 	PhyadSHIFT	= 0,
12872cfc098SDavid du Colombier 	Mfdc		= 0x20,			/* Accelerate MDC Speed */
12972cfc098SDavid du Colombier 	Mpo0		= 0x40,			/* MII Polling Timer Interval */
13072cfc098SDavid du Colombier 	Mpo1		= 0x80,
13172cfc098SDavid du Colombier };
13272cfc098SDavid du Colombier 
13372cfc098SDavid du Colombier enum {						/* Bcr0 */
13472cfc098SDavid du Colombier 	DmaMASK		= 0x07,			/* DMA Length */
13572cfc098SDavid du Colombier 	DmaSHIFT	= 0,
13672cfc098SDavid du Colombier 	Dma32		= 0<<DmaSHIFT,
13772cfc098SDavid du Colombier 	Dma64		= 1<<DmaSHIFT,
13872cfc098SDavid du Colombier 	Dma128		= 2<<DmaSHIFT,
13972cfc098SDavid du Colombier 	Dma256		= 3<<DmaSHIFT,
14072cfc098SDavid du Colombier 	Dma512		= 4<<DmaSHIFT,
14172cfc098SDavid du Colombier 	Dma1024		= 5<<DmaSHIFT,
14272cfc098SDavid du Colombier 	DmaSAF		= 7<<DmaSHIFT,
14372cfc098SDavid du Colombier 	CrftMASK	= 0x38,			/* Rx FIFO Threshold */
14472cfc098SDavid du Colombier 	CrftSHIFT	= 3,
14572cfc098SDavid du Colombier 	Crft64		= 1<<CrftSHIFT,
14672cfc098SDavid du Colombier 	Crft128		= 2<<CrftSHIFT,
14772cfc098SDavid du Colombier 	Crft256		= 3<<CrftSHIFT,
14872cfc098SDavid du Colombier 	Crft512		= 4<<CrftSHIFT,
14972cfc098SDavid du Colombier 	Crft1024	= 5<<CrftSHIFT,
15072cfc098SDavid du Colombier 	CrftSAF		= 7<<CrftSHIFT,
15172cfc098SDavid du Colombier 	Extled		= 0x40,			/* Extra LED Support Control */
15272cfc098SDavid du Colombier 	Med2		= 0x80,			/* Medium Select Control */
15372cfc098SDavid du Colombier };
15472cfc098SDavid du Colombier 
15572cfc098SDavid du Colombier enum {						/* Bcr1 */
15672cfc098SDavid du Colombier 	PotMASK		= 0x07,			/* Polling Timer Interval */
15772cfc098SDavid du Colombier 	PotSHIFT	= 0,
15872cfc098SDavid du Colombier 	CtftMASK	= 0x38,			/* Tx FIFO Threshold */
15972cfc098SDavid du Colombier 	CtftSHIFT	= 3,
16072cfc098SDavid du Colombier 	Ctft64		= 1<<CtftSHIFT,
16172cfc098SDavid du Colombier 	Ctft128		= 2<<CtftSHIFT,
16272cfc098SDavid du Colombier 	Ctft256		= 3<<CtftSHIFT,
16372cfc098SDavid du Colombier 	Ctft512		= 4<<CtftSHIFT,
16472cfc098SDavid du Colombier 	Ctft1024	= 5<<CtftSHIFT,
16572cfc098SDavid du Colombier 	CtftSAF		= 7<<CtftSHIFT,
16672cfc098SDavid du Colombier };
16772cfc098SDavid du Colombier 
16872cfc098SDavid du Colombier enum {						/* Miicr */
16972cfc098SDavid du Colombier 	Mdc		= 0x01,			/* Clock */
17072cfc098SDavid du Colombier 	Mdi		= 0x02,			/* Data In */
17172cfc098SDavid du Colombier 	Mdo		= 0x04,			/* Data Out */
17272cfc098SDavid du Colombier 	Mout		= 0x08,			/* Output Enable */
17372cfc098SDavid du Colombier 	Mdpm		= 0x10,			/* Direct Program Mode Enable */
17472cfc098SDavid du Colombier 	Wcmd		= 0x20,			/* Write Enable */
17572cfc098SDavid du Colombier 	Rcmd		= 0x40,			/* Read Enable */
17672cfc098SDavid du Colombier 	Mauto		= 0x80,			/* Auto Polling Enable */
17772cfc098SDavid du Colombier };
17872cfc098SDavid du Colombier 
17972cfc098SDavid du Colombier enum {						/* Miiadr */
18072cfc098SDavid du Colombier 	MadMASK		= 0x1F,			/* MII Port Address */
18172cfc098SDavid du Colombier 	MadSHIFT	= 0,
18272cfc098SDavid du Colombier 	Mdone		= 0x20,			/* Accelerate MDC Speed */
18372cfc098SDavid du Colombier 	Msrcen		= 0x40,			/* MII Polling Timer Interval */
18472cfc098SDavid du Colombier 	Midle		= 0x80,
18572cfc098SDavid du Colombier };
18672cfc098SDavid du Colombier 
18772cfc098SDavid du Colombier enum {						/* Eecsr */
18872cfc098SDavid du Colombier 	Edo		= 0x01,			/* Data Out */
18972cfc098SDavid du Colombier 	Edi		= 0x02,			/* Data In */
19072cfc098SDavid du Colombier 	Eck		= 0x04,			/* Clock */
19172cfc098SDavid du Colombier 	Ecs		= 0x08,			/* Chip Select */
19272cfc098SDavid du Colombier 	Dpm		= 0x10,			/* Direct Program Mode Enable */
19372cfc098SDavid du Colombier 	Autold		= 0x20,			/* Dynamic Reload */
19472cfc098SDavid du Colombier 	Embp		= 0x40,			/* Embedded Program Enable */
19572cfc098SDavid du Colombier 	Eepr		= 0x80,			/* Programmed */
19672cfc098SDavid du Colombier };
19772cfc098SDavid du Colombier 
19872cfc098SDavid du Colombier /*
19972cfc098SDavid du Colombier  * Ring descriptor. The space allocated for each
20072cfc098SDavid du Colombier  * of these will be rounded up to a cache-line boundary.
20172cfc098SDavid du Colombier  * The first 4 elements are known to the hardware.
20272cfc098SDavid du Colombier  */
20372cfc098SDavid du Colombier typedef struct Ds Ds;
20472cfc098SDavid du Colombier typedef struct Ds {
20572cfc098SDavid du Colombier 	u32int	status;
20672cfc098SDavid du Colombier 	u32int	control;
20772cfc098SDavid du Colombier 	u32int	addr;
20872cfc098SDavid du Colombier 	u32int	branch;
20972cfc098SDavid du Colombier 
21072cfc098SDavid du Colombier 	Block*	bp;
21172cfc098SDavid du Colombier 	Ds*	next;
21272cfc098SDavid du Colombier 	Ds*	prev;
21372cfc098SDavid du Colombier } Ds;
21472cfc098SDavid du Colombier 
21572cfc098SDavid du Colombier enum {						/* Rx Ds status */
21672cfc098SDavid du Colombier 	Rerr		= 0x00000001,		/* Buff|Rxserr|Fov|Fae|Crc */
21772cfc098SDavid du Colombier 	Crc		= 0x00000002,		/* CRC Error */
21872cfc098SDavid du Colombier 	Fae		= 0x00000004,		/* Frame Alignment Error */
21972cfc098SDavid du Colombier 	Fov		= 0x00000008,		/* FIFO Overflow */
22072cfc098SDavid du Colombier 	Long		= 0x00000010,		/* A Long Packet */
22172cfc098SDavid du Colombier 	Runt		= 0x00000020,		/* A Runt Packet */
22272cfc098SDavid du Colombier 	Rxserr		= 0x00000040,		/* System Error */
22372cfc098SDavid du Colombier 	Buff		= 0x00000080,		/* Buffer Underflow Error */
22472cfc098SDavid du Colombier 	Rxedp		= 0x00000100,		/* End of Packet Buffer */
22572cfc098SDavid du Colombier 	Rxstp		= 0x00000200,		/* Packet Start */
22672cfc098SDavid du Colombier 	Chn		= 0x00000400,		/* Chain Buffer */
22772cfc098SDavid du Colombier 	Phy		= 0x00000800,		/* Physical Address Packet */
22872cfc098SDavid du Colombier 	Bar		= 0x00001000,		/* Broadcast Packet */
22972cfc098SDavid du Colombier 	Mar		= 0x00002000,		/* Multicast Packet */
23072cfc098SDavid du Colombier 	Rxok		= 0x00008000,		/* Packet Received OK */
23172cfc098SDavid du Colombier 	LengthMASK	= 0x07FF0000,		/* Received Packet Length */
23272cfc098SDavid du Colombier 	LengthSHIFT	= 16,
23372cfc098SDavid du Colombier 
23472cfc098SDavid du Colombier 	Own		= 0x80000000,		/* Descriptor Owned by NIC */
23572cfc098SDavid du Colombier };
23672cfc098SDavid du Colombier 
23772cfc098SDavid du Colombier enum {						/* Rx Ds control */
23872cfc098SDavid du Colombier 	RbsizeMASK	= 0x000007FF,		/* Receive Buffer Size */
23972cfc098SDavid du Colombier 	RbsizeSHIFT	= 0,
24072cfc098SDavid du Colombier 	Tag		= 0x00010000,		/* Receive a Tagged Packet */
24172cfc098SDavid du Colombier 	Udpkt		= 0x00020000,		/* Receive a UDP Packet */
24272cfc098SDavid du Colombier 	Tcpkt		= 0x00040000,		/* Receive a TCP Packet */
24372cfc098SDavid du Colombier 	Ipkt		= 0x00080000,		/* Receive an IP Packet */
24472cfc098SDavid du Colombier 	Tuok		= 0x00100000,		/* TCP/UDP Checksum OK */
24572cfc098SDavid du Colombier 	Ipok		= 0x00200000,		/* IP Checksum OK */
24672cfc098SDavid du Colombier 	Snaptag		= 0x00400000,		/* Snap Packet + 802.1q Tag */
24772cfc098SDavid du Colombier 	Rxlerr		= 0x00800000,		/* Receive Length Check Error */
24872cfc098SDavid du Colombier 	IpktMASK	= 0xff000000,		/* Interesting Packet */
24972cfc098SDavid du Colombier 	IpktSHIFT	= 24,
25072cfc098SDavid du Colombier };
25172cfc098SDavid du Colombier 
25272cfc098SDavid du Colombier enum {						/* Tx Ds status */
25372cfc098SDavid du Colombier 	NcrMASK		= 0x0000000F,		/* Collision Retry Count */
25472cfc098SDavid du Colombier 	NcrSHIFT	= 0,
25572cfc098SDavid du Colombier 	Cols		= 0x00000010,		/* Experienced Collisions */
25672cfc098SDavid du Colombier 	Cdh		= 0x00000080,		/* CD Heartbeat */
25772cfc098SDavid du Colombier 	Abt		= 0x00000100,		/* Aborted after Excessive Collisions */
25872cfc098SDavid du Colombier 	Owc		= 0x00000200,		/* Out of Window Collision */
25972cfc098SDavid du Colombier 	Crs		= 0x00000400,		/* Carrier Sense Lost */
26072cfc098SDavid du Colombier 	Udf		= 0x00000800,		/* FIFO Underflow */
26172cfc098SDavid du Colombier 	Tbuff		= 0x00001000,		/* Invalid Td */
26272cfc098SDavid du Colombier 	Txserr		= 0x00002000,		/* System Error */
26372cfc098SDavid du Colombier 	Terr		= 0x00008000,		/* Excessive Collisions */
26472cfc098SDavid du Colombier };
26572cfc098SDavid du Colombier 
26672cfc098SDavid du Colombier enum {						/* Tx Ds control */
26772cfc098SDavid du Colombier 	TbsMASK		= 0x000007FF,		/* Tx Buffer Size */
26872cfc098SDavid du Colombier 	TbsSHIFT	= 0,
26972cfc098SDavid du Colombier 	Chain		= 0x00008000,		/* Chain Buffer */
27072cfc098SDavid du Colombier 	Crcdisable	= 0x00010000,		/* Disable CRC generation */
27172cfc098SDavid du Colombier 	Stp		= 0x00200000,		/* Start of Packet */
27272cfc098SDavid du Colombier 	Edp		= 0x00400000,		/* End of Packet */
27372cfc098SDavid du Colombier 	Ic		= 0x00800000,		/* Interrupt Control */
27472cfc098SDavid du Colombier };
27572cfc098SDavid du Colombier 
27672cfc098SDavid du Colombier enum {						/* Tx Ds branch */
27772cfc098SDavid du Colombier 	Tdctl		= 0x00000001,		/* No Interrupt Generated */
27872cfc098SDavid du Colombier };
27972cfc098SDavid du Colombier 
28072cfc098SDavid du Colombier enum {
28172cfc098SDavid du Colombier 	Nrd		= 196,
2826520663fSDavid du Colombier 	Ntd		= 64,
28372cfc098SDavid du Colombier 	Crcsz		= 4,
28472cfc098SDavid du Colombier 	Bslop		= 48,
28572cfc098SDavid du Colombier 	Rdbsz		= ETHERMAXTU+Crcsz+Bslop,
286*5b797632SDavid du Colombier 	Maxus		= 1000000,  /* Soekris 5501s take a while to reset */
28772cfc098SDavid du Colombier 
28872cfc098SDavid du Colombier 	Nrxstats	= 8,
28972cfc098SDavid du Colombier 	Ntxstats	= 9,
29072cfc098SDavid du Colombier };
29172cfc098SDavid du Colombier 
29272cfc098SDavid du Colombier typedef struct Ctlr Ctlr;
29372cfc098SDavid du Colombier typedef struct Ctlr {
29472cfc098SDavid du Colombier 	int	port;
29572cfc098SDavid du Colombier 	Pcidev*	pcidev;
29672cfc098SDavid du Colombier 	Ctlr*	next;
29772cfc098SDavid du Colombier 	int	active;
29872cfc098SDavid du Colombier 	int	id;
29972cfc098SDavid du Colombier 	uchar	par[Eaddrlen];
30072cfc098SDavid du Colombier 
30172cfc098SDavid du Colombier 	QLock	alock;				/* attach */
30272cfc098SDavid du Colombier 	void*	alloc;				/* descriptors, etc. */
30372cfc098SDavid du Colombier 	int	cls;				/* alignment */
30472cfc098SDavid du Colombier 	int	nrd;
30572cfc098SDavid du Colombier 	int	ntd;
30672cfc098SDavid du Colombier 
30772cfc098SDavid du Colombier 	Ds*	rd;
30872cfc098SDavid du Colombier 	Ds*	rdh;
30972cfc098SDavid du Colombier 
31072cfc098SDavid du Colombier 	Lock	tlock;
31172cfc098SDavid du Colombier 	Ds*	td;
31272cfc098SDavid du Colombier 	Ds*	tdh;
31372cfc098SDavid du Colombier 	Ds*	tdt;
31472cfc098SDavid du Colombier 	int	tdused;
31572cfc098SDavid du Colombier 
31672cfc098SDavid du Colombier 	Lock	clock;				/*  */
31772cfc098SDavid du Colombier 	int	cr;
31872cfc098SDavid du Colombier 	int	imr;
31972cfc098SDavid du Colombier 	int	tft;				/* Tx threshold */
32072cfc098SDavid du Colombier 
32172cfc098SDavid du Colombier 	Mii*	mii;
32272cfc098SDavid du Colombier 	Rendez	lrendez;
32372cfc098SDavid du Colombier 	int	lwakeup;
32472cfc098SDavid du Colombier 
32572cfc098SDavid du Colombier 	uint	rxstats[Nrxstats];		/* statistics */
32672cfc098SDavid du Colombier 	uint	txstats[Ntxstats];
32772cfc098SDavid du Colombier 	ulong	totalt;
32872cfc098SDavid du Colombier 	uint	intr;
32972cfc098SDavid du Colombier 	uint	lintr;
33072cfc098SDavid du Colombier 	uint	lsleep;
33172cfc098SDavid du Colombier 	uint	rintr;
33272cfc098SDavid du Colombier 	uint	tintr;
33372cfc098SDavid du Colombier 	uint	txdw;
33472cfc098SDavid du Colombier 	int	tdumax;
33572cfc098SDavid du Colombier 
33672cfc098SDavid du Colombier 	uint	abt;
33772cfc098SDavid du Colombier 	uint	tbuff;
33872cfc098SDavid du Colombier 	uint	udf;
33972cfc098SDavid du Colombier 
34072cfc098SDavid du Colombier 	uint	abti;
34172cfc098SDavid du Colombier 	uint	udfi;
34272cfc098SDavid du Colombier 	uint	tu;
34372cfc098SDavid du Colombier 
34472cfc098SDavid du Colombier 	uint	tuok;
34572cfc098SDavid du Colombier 	uint	ipok;
34672cfc098SDavid du Colombier } Ctlr;
34772cfc098SDavid du Colombier 
34872cfc098SDavid du Colombier static Ctlr* vt6105Mctlrhead;
34972cfc098SDavid du Colombier static Ctlr* vt6105Mctlrtail;
35072cfc098SDavid du Colombier 
35172cfc098SDavid du Colombier #define csr8r(c, r)	(inb((c)->port+(r)))
35272cfc098SDavid du Colombier #define csr16r(c, r)	(ins((c)->port+(r)))
35372cfc098SDavid du Colombier #define csr32r(c, r)	(inl((c)->port+(r)))
35472cfc098SDavid du Colombier #define csr8w(c, r, b)	(outb((c)->port+(r), (int)(b)))
35572cfc098SDavid du Colombier #define csr16w(c, r, w)	(outs((c)->port+(r), (ushort)(w)))
35672cfc098SDavid du Colombier #define csr32w(c, r, w)	(outl((c)->port+(r), (ulong)(w)))
35772cfc098SDavid du Colombier 
35872cfc098SDavid du Colombier static Lock vt6105Mrblock;			/* receive Block freelist */
35972cfc098SDavid du Colombier static Block* vt6105Mrbpool;
36072cfc098SDavid du Colombier static uint vt6105Mrbpoolsz;
36172cfc098SDavid du Colombier 
36272cfc098SDavid du Colombier typedef struct Regs Regs;
36372cfc098SDavid du Colombier typedef struct Regs {
36472cfc098SDavid du Colombier 	char*	name;
36572cfc098SDavid du Colombier 	int	offset;
36672cfc098SDavid du Colombier 	int	size;
36772cfc098SDavid du Colombier } Regs;
36872cfc098SDavid du Colombier 
36972cfc098SDavid du Colombier static Regs regs[] = {
37072cfc098SDavid du Colombier //	"Par0",		Par0,	1,
37172cfc098SDavid du Colombier //	"Par1",		Par0+1,	1,
37272cfc098SDavid du Colombier //	"Par2",		Par0+2,	1,
37372cfc098SDavid du Colombier //	"Par3",		Par0+3,	1,
37472cfc098SDavid du Colombier //	"Par4",		Par0+4,	1,
37572cfc098SDavid du Colombier //	"Par5",		Par0+5,	1,
37672cfc098SDavid du Colombier 	"Rcr",		Rcr,	1,
37772cfc098SDavid du Colombier 	"Tcr",		Tcr,	1,
37872cfc098SDavid du Colombier 	"Cr0",		Cr,	1,
37972cfc098SDavid du Colombier 	"Cr1",		Cr+1,	1,
38072cfc098SDavid du Colombier 	"Isr0",		Isr,	1,
38172cfc098SDavid du Colombier 	"Isr1",		Isr+1,	1,
38272cfc098SDavid du Colombier 	"Imr0",		Imr,	1,
38372cfc098SDavid du Colombier 	"Imr1",		Imr+1,	1,
38472cfc098SDavid du Colombier //	"Mcfilt0",	Mcfilt0,4,
38572cfc098SDavid du Colombier //	"Mcfilt1",	Mcfilt1,4,
38672cfc098SDavid du Colombier //	"Rxdaddr",	Rxdaddr,4,
38772cfc098SDavid du Colombier //	"Txdaddr",	Txdaddr,4,
38872cfc098SDavid du Colombier 	"Phyadr",	Phyadr,	1,
38972cfc098SDavid du Colombier 	"Miisr",	Miisr,	1,
39072cfc098SDavid du Colombier 	"Bcr0",		Bcr0,	1,
39172cfc098SDavid du Colombier 	"Bcr1",		Bcr1,	1,
39272cfc098SDavid du Colombier 	"Miicr",	Miicr,	1,
39372cfc098SDavid du Colombier 	"Miiadr",	Miiadr,	1,
39472cfc098SDavid du Colombier //	"Miidata",	Miidata,2,
39572cfc098SDavid du Colombier 	"Eecsr",	Eecsr,	1,
39672cfc098SDavid du Colombier 	"CfgA",		CfgA,	1,
39772cfc098SDavid du Colombier 	"CfgB",		CfgB,	1,
39872cfc098SDavid du Colombier 	"CfgC",		CfgC,	1,
39972cfc098SDavid du Colombier 	"CfgD",		CfgD,	1,
40072cfc098SDavid du Colombier 	"Cr0",		Cr0,	1,
40172cfc098SDavid du Colombier 	"Cr1",		Cr1,	1,
40272cfc098SDavid du Colombier 	"Pmcc",		Pmcc,	1,
40372cfc098SDavid du Colombier 	"Stickhw",	Stickhw,1,
40472cfc098SDavid du Colombier 	"Misr",		Misr,	1,
40572cfc098SDavid du Colombier 	"Mimr",		Mimr,	1,
40672cfc098SDavid du Colombier 	nil,
40772cfc098SDavid du Colombier };
40872cfc098SDavid du Colombier 
40972cfc098SDavid du Colombier static char* rxstats[Nrxstats] = {
41072cfc098SDavid du Colombier 	"Receiver Error",
41172cfc098SDavid du Colombier 	"CRC Error",
41272cfc098SDavid du Colombier 	"Frame Alignment Error",
41372cfc098SDavid du Colombier 	"FIFO Overflow",
41472cfc098SDavid du Colombier 	"Long Packet",
41572cfc098SDavid du Colombier 	"Runt Packet",
41672cfc098SDavid du Colombier 	"System Error",
41772cfc098SDavid du Colombier 	"Buffer Underflow Error",
41872cfc098SDavid du Colombier };
41972cfc098SDavid du Colombier static char* txstats[Ntxstats] = {
42072cfc098SDavid du Colombier 	"Aborted after Excessive Collisions",
42172cfc098SDavid du Colombier 	"Out of Window Collision Seen",
42272cfc098SDavid du Colombier 	"Carrier Sense Lost",
42372cfc098SDavid du Colombier 	"FIFO Underflow",
42472cfc098SDavid du Colombier 	"Invalid Td",
42572cfc098SDavid du Colombier 	"System Error",
42672cfc098SDavid du Colombier 	nil,
42772cfc098SDavid du Colombier 	"Excessive Collisions",
42872cfc098SDavid du Colombier };
42972cfc098SDavid du Colombier 
43072cfc098SDavid du Colombier static long
vt6105Mifstat(Ether * edev,void * a,long n,ulong offset)43172cfc098SDavid du Colombier vt6105Mifstat(Ether* edev, void* a, long n, ulong offset)
43272cfc098SDavid du Colombier {
43372cfc098SDavid du Colombier 	int i, r;
43472cfc098SDavid du Colombier 	Ctlr *ctlr;
43572cfc098SDavid du Colombier 	char *alloc, *e, *p;
43672cfc098SDavid du Colombier 
43772cfc098SDavid du Colombier 	ctlr = edev->ctlr;
43872cfc098SDavid du Colombier 
43946136019SDavid du Colombier 	alloc = malloc(READSTR);
44072cfc098SDavid du Colombier 	p = alloc;
441aa72973aSDavid du Colombier 	if(p == nil)
442aa72973aSDavid du Colombier 		error(Enomem);
44346136019SDavid du Colombier 	e = p + READSTR;
44472cfc098SDavid du Colombier 	for(i = 0; i < Nrxstats; i++){
44572cfc098SDavid du Colombier 		p = seprint(p, e, "%s: %ud\n", rxstats[i], ctlr->rxstats[i]);
44672cfc098SDavid du Colombier 	}
44772cfc098SDavid du Colombier 	for(i = 0; i < Ntxstats; i++){
44872cfc098SDavid du Colombier 		if(txstats[i] == nil)
44972cfc098SDavid du Colombier 			continue;
45072cfc098SDavid du Colombier 		p = seprint(p, e, "%s: %ud\n", txstats[i], ctlr->txstats[i]);
45172cfc098SDavid du Colombier 	}
45272cfc098SDavid du Colombier 	p = seprint(p, e, "cls: %ud\n", ctlr->cls);
45372cfc098SDavid du Colombier 	p = seprint(p, e, "intr: %ud\n", ctlr->intr);
45472cfc098SDavid du Colombier 	p = seprint(p, e, "lintr: %ud\n", ctlr->lintr);
45572cfc098SDavid du Colombier 	p = seprint(p, e, "lsleep: %ud\n", ctlr->lsleep);
45672cfc098SDavid du Colombier 	p = seprint(p, e, "rintr: %ud\n", ctlr->rintr);
45772cfc098SDavid du Colombier 	p = seprint(p, e, "tintr: %ud\n", ctlr->tintr);
45872cfc098SDavid du Colombier 	p = seprint(p, e, "txdw: %ud\n", ctlr->txdw);
45972cfc098SDavid du Colombier 	p = seprint(p, e, "tdumax: %ud\n", ctlr->tdumax);
46072cfc098SDavid du Colombier 	p = seprint(p, e, "tft: %ud\n", ctlr->tft);
46172cfc098SDavid du Colombier 
46272cfc098SDavid du Colombier 	p = seprint(p, e, "abt: %ud\n", ctlr->abt);
46372cfc098SDavid du Colombier 	p = seprint(p, e, "tbuff: %ud\n", ctlr->tbuff);
46472cfc098SDavid du Colombier 	p = seprint(p, e, "udf: %ud\n", ctlr->udf);
46572cfc098SDavid du Colombier 	p = seprint(p, e, "abti: %ud\n", ctlr->abti);
46672cfc098SDavid du Colombier 	p = seprint(p, e, "udfi: %ud\n", ctlr->udfi);
46772cfc098SDavid du Colombier 	p = seprint(p, e, "tu: %ud\n", ctlr->tu);
46872cfc098SDavid du Colombier 
46972cfc098SDavid du Colombier 	p = seprint(p, e, "tuok: %ud\n", ctlr->tuok);
47072cfc098SDavid du Colombier 	p = seprint(p, e, "ipok: %ud\n", ctlr->ipok);
47172cfc098SDavid du Colombier 
47272cfc098SDavid du Colombier 	p = seprint(p, e, "rbpoolsz: %ud\n", vt6105Mrbpoolsz);
47372cfc098SDavid du Colombier 	p = seprint(p, e, "totalt: %uld\n", ctlr->totalt);
47472cfc098SDavid du Colombier 
47572cfc098SDavid du Colombier 	for(i = 0; regs[i].name != nil; i++){
47672cfc098SDavid du Colombier 		p = seprint(p, e, "%s: %2.2x\n",
47772cfc098SDavid du Colombier 			regs[i].name,  csr8r(ctlr, regs[i].offset));
47872cfc098SDavid du Colombier 	}
47972cfc098SDavid du Colombier 
48072cfc098SDavid du Colombier 	if(ctlr->mii != nil && ctlr->mii->curphy != nil){
48172cfc098SDavid du Colombier 		p = seprint(p, e, "phy:   ");
48272cfc098SDavid du Colombier 		for(i = 0; i < NMiiPhyr; i++){
48372cfc098SDavid du Colombier 			if(i && ((i & 0x07) == 0))
48472cfc098SDavid du Colombier 				p = seprint(p, e, "\n       ");
48572cfc098SDavid du Colombier 			r = miimir(ctlr->mii, i);
48672cfc098SDavid du Colombier 			p = seprint(p, e, " %4.4uX", r);
48772cfc098SDavid du Colombier 		}
48872cfc098SDavid du Colombier 		seprint(p, e, "\n");
48972cfc098SDavid du Colombier 	}
49072cfc098SDavid du Colombier 
49172cfc098SDavid du Colombier 	n = readstr(offset, a, n, alloc);
49272cfc098SDavid du Colombier 	free(alloc);
49372cfc098SDavid du Colombier 
49472cfc098SDavid du Colombier 	return n;
49572cfc098SDavid du Colombier }
49672cfc098SDavid du Colombier 
49772cfc098SDavid du Colombier static void
vt6105Mpromiscuous(void * arg,int on)49872cfc098SDavid du Colombier vt6105Mpromiscuous(void* arg, int on)
49972cfc098SDavid du Colombier {
50072cfc098SDavid du Colombier 	int rcr;
50172cfc098SDavid du Colombier 	Ctlr *ctlr;
50272cfc098SDavid du Colombier 	Ether *edev;
50372cfc098SDavid du Colombier 
50472cfc098SDavid du Colombier 	edev = arg;
50572cfc098SDavid du Colombier 	ctlr = edev->ctlr;
50672cfc098SDavid du Colombier 	rcr = csr8r(ctlr, Rcr);
50772cfc098SDavid du Colombier 	if(on)
50872cfc098SDavid du Colombier 		rcr |= Prom;
50972cfc098SDavid du Colombier 	else
51072cfc098SDavid du Colombier 		rcr &= ~Prom;
51172cfc098SDavid du Colombier 	csr8w(ctlr, Rcr, rcr);
51272cfc098SDavid du Colombier }
51372cfc098SDavid du Colombier 
51472cfc098SDavid du Colombier static void
vt6105Mmulticast(void * arg,uchar * addr,int on)51572cfc098SDavid du Colombier vt6105Mmulticast(void* arg, uchar* addr, int on)
51672cfc098SDavid du Colombier {
51772cfc098SDavid du Colombier 	/*
51872cfc098SDavid du Colombier 	 * For now Am is set in Rcr.
51972cfc098SDavid du Colombier 	 * Will need to interlock with promiscuous
52072cfc098SDavid du Colombier 	 * when this gets filled in.
52172cfc098SDavid du Colombier 	 */
52272cfc098SDavid du Colombier 	USED(arg, addr, on);
52372cfc098SDavid du Colombier }
52472cfc098SDavid du Colombier 
52572cfc098SDavid du Colombier static int
vt6105Mwakeup(void * v)52672cfc098SDavid du Colombier vt6105Mwakeup(void* v)
52772cfc098SDavid du Colombier {
52872cfc098SDavid du Colombier 	return *((int*)v) != 0;
52972cfc098SDavid du Colombier }
53072cfc098SDavid du Colombier 
53172cfc098SDavid du Colombier static void
vt6105Mimr(Ctlr * ctlr,int imr)53272cfc098SDavid du Colombier vt6105Mimr(Ctlr* ctlr, int imr)
53372cfc098SDavid du Colombier {
53472cfc098SDavid du Colombier 	ilock(&ctlr->clock);
53572cfc098SDavid du Colombier 	ctlr->imr |= imr;
53672cfc098SDavid du Colombier 	csr16w(ctlr, Imr, ctlr->imr);
53772cfc098SDavid du Colombier 	iunlock(&ctlr->clock);
53872cfc098SDavid du Colombier }
53972cfc098SDavid du Colombier 
54072cfc098SDavid du Colombier static void
vt6105Mlproc(void * arg)54172cfc098SDavid du Colombier vt6105Mlproc(void* arg)
54272cfc098SDavid du Colombier {
54372cfc098SDavid du Colombier 	Ctlr *ctlr;
54472cfc098SDavid du Colombier 	Ether *edev;
54572cfc098SDavid du Colombier 	MiiPhy *phy;
54672cfc098SDavid du Colombier 
54772cfc098SDavid du Colombier 	edev = arg;
54872cfc098SDavid du Colombier 	ctlr = edev->ctlr;
54972cfc098SDavid du Colombier 	for(;;){
55072cfc098SDavid du Colombier 		if(ctlr->mii == nil || ctlr->mii->curphy == nil)
55172cfc098SDavid du Colombier 			break;
55272cfc098SDavid du Colombier 		if(miistatus(ctlr->mii) < 0)
55372cfc098SDavid du Colombier 			goto enable;
55472cfc098SDavid du Colombier 
55572cfc098SDavid du Colombier 		phy = ctlr->mii->curphy;
55672cfc098SDavid du Colombier 		ilock(&ctlr->clock);
55772cfc098SDavid du Colombier 		csr16w(ctlr, Cr, ctlr->cr & ~(Txon|Rxon));
55872cfc098SDavid du Colombier 		if(phy->fd)
55972cfc098SDavid du Colombier 			ctlr->cr |= Fdx;
56072cfc098SDavid du Colombier 		else
56172cfc098SDavid du Colombier 			ctlr->cr &= ~Fdx;
56272cfc098SDavid du Colombier 		csr16w(ctlr, Cr, ctlr->cr);
56372cfc098SDavid du Colombier 		iunlock(&ctlr->clock);
56472cfc098SDavid du Colombier enable:
56572cfc098SDavid du Colombier 		ctlr->lwakeup = 0;
56672cfc098SDavid du Colombier 		vt6105Mimr(ctlr, Srci);
56772cfc098SDavid du Colombier 
56872cfc098SDavid du Colombier 		ctlr->lsleep++;
56972cfc098SDavid du Colombier 		sleep(&ctlr->lrendez, vt6105Mwakeup, &ctlr->lwakeup);
57072cfc098SDavid du Colombier 
57172cfc098SDavid du Colombier 	}
57272cfc098SDavid du Colombier 	pexit("vt6105Mlproc: done", 1);
57372cfc098SDavid du Colombier }
57472cfc098SDavid du Colombier 
57572cfc098SDavid du Colombier static void
vt6105Mrbfree(Block * bp)57672cfc098SDavid du Colombier vt6105Mrbfree(Block* bp)
57772cfc098SDavid du Colombier {
57872cfc098SDavid du Colombier 	bp->rp = bp->lim - (Rdbsz+3);
57972cfc098SDavid du Colombier 	bp->wp = bp->rp;
580bfb6eab9SDavid du Colombier  	bp->flag &= ~(Bipck | Budpck | Btcpck | Bpktck);
58172cfc098SDavid du Colombier 
58272cfc098SDavid du Colombier 	ilock(&vt6105Mrblock);
58372cfc098SDavid du Colombier 	bp->next = vt6105Mrbpool;
58472cfc098SDavid du Colombier 	vt6105Mrbpool = bp;
58572cfc098SDavid du Colombier 	iunlock(&vt6105Mrblock);
58672cfc098SDavid du Colombier }
58772cfc098SDavid du Colombier 
58872cfc098SDavid du Colombier static Block*
vt6105Mrballoc(void)58972cfc098SDavid du Colombier vt6105Mrballoc(void)
59072cfc098SDavid du Colombier {
59172cfc098SDavid du Colombier 	Block *bp;
59272cfc098SDavid du Colombier 
59372cfc098SDavid du Colombier 	ilock(&vt6105Mrblock);
59472cfc098SDavid du Colombier 	if((bp = vt6105Mrbpool) != nil){
59572cfc098SDavid du Colombier 		vt6105Mrbpool = bp->next;
59672cfc098SDavid du Colombier 		bp->next = nil;
59772cfc098SDavid du Colombier 		_xinc(&bp->ref);	/* prevent bp from being freed */
59872cfc098SDavid du Colombier 	}
59972cfc098SDavid du Colombier 	iunlock(&vt6105Mrblock);
60072cfc098SDavid du Colombier 
60172cfc098SDavid du Colombier 	if(bp == nil && (bp = iallocb(Rdbsz+3)) != nil){
60272cfc098SDavid du Colombier 		bp->free = vt6105Mrbfree;
60372cfc098SDavid du Colombier 		vt6105Mrbpoolsz++;
60472cfc098SDavid du Colombier 	}
60572cfc098SDavid du Colombier 	return bp;
60672cfc098SDavid du Colombier }
60772cfc098SDavid du Colombier 
60872cfc098SDavid du Colombier static void
vt6105Mattach(Ether * edev)60972cfc098SDavid du Colombier vt6105Mattach(Ether* edev)
61072cfc098SDavid du Colombier {
61172cfc098SDavid du Colombier 	Ctlr *ctlr;
61272cfc098SDavid du Colombier //	MiiPhy *phy;
61372cfc098SDavid du Colombier 	uchar *alloc;
61472cfc098SDavid du Colombier 	Ds *ds, *prev;
61572cfc098SDavid du Colombier 	int dsz, i, timeo;
61672cfc098SDavid du Colombier 	char name[KNAMELEN];
61772cfc098SDavid du Colombier 
61872cfc098SDavid du Colombier 	ctlr = edev->ctlr;
61972cfc098SDavid du Colombier 	qlock(&ctlr->alock);
62072cfc098SDavid du Colombier 	if(ctlr->alloc != nil){
62172cfc098SDavid du Colombier 		qunlock(&ctlr->alock);
62272cfc098SDavid du Colombier 		return;
62372cfc098SDavid du Colombier 	}
62472cfc098SDavid du Colombier 
62572cfc098SDavid du Colombier 	/*
62672cfc098SDavid du Colombier 	 * Descriptor space.
62772cfc098SDavid du Colombier 	 * Receive descriptors should all be aligned on a 4-byte boundary,
62872cfc098SDavid du Colombier 	 * but try to do cache-line alignment.
62972cfc098SDavid du Colombier 	 */
63072cfc098SDavid du Colombier 	ctlr->nrd = Nrd;
63172cfc098SDavid du Colombier 	ctlr->ntd = Ntd;
63272cfc098SDavid du Colombier 	dsz = ROUNDUP(sizeof(Ds), ctlr->cls);
63372cfc098SDavid du Colombier 	alloc = mallocalign((ctlr->nrd+ctlr->ntd)*dsz, dsz, 0, 0);
63472cfc098SDavid du Colombier 	if(alloc == nil){
63572cfc098SDavid du Colombier 		qunlock(&ctlr->alock);
6366fbfa2f3SDavid du Colombier 		error(Enomem);
63772cfc098SDavid du Colombier 	}
63872cfc098SDavid du Colombier 	ctlr->alloc = alloc;
63972cfc098SDavid du Colombier 
64072cfc098SDavid du Colombier 	ctlr->rd = (Ds*)alloc;
64172cfc098SDavid du Colombier 
64272cfc098SDavid du Colombier 	if(waserror()){
64372cfc098SDavid du Colombier 		ds = ctlr->rd;
64472cfc098SDavid du Colombier 		for(i = 0; i < ctlr->nrd; i++){
64572cfc098SDavid du Colombier 			if(ds->bp != nil){
64672cfc098SDavid du Colombier 				freeb(ds->bp);
64772cfc098SDavid du Colombier 				ds->bp = nil;
64872cfc098SDavid du Colombier 			}
64972cfc098SDavid du Colombier 			if((ds = ds->next) == nil)
65072cfc098SDavid du Colombier 				break;
65172cfc098SDavid du Colombier 		}
65272cfc098SDavid du Colombier 		free(ctlr->alloc);
65372cfc098SDavid du Colombier 		ctlr->alloc = nil;
65472cfc098SDavid du Colombier 		qunlock(&ctlr->alock);
65572cfc098SDavid du Colombier 		nexterror();
65672cfc098SDavid du Colombier 	}
65772cfc098SDavid du Colombier 
65872cfc098SDavid du Colombier 	prev = (Ds*)(alloc + (ctlr->nrd-1)*dsz);
65972cfc098SDavid du Colombier 	for(i = 0; i < ctlr->nrd; i++){
66072cfc098SDavid du Colombier 		ds = (Ds*)alloc;
66172cfc098SDavid du Colombier 		alloc += dsz;
66272cfc098SDavid du Colombier 
66372cfc098SDavid du Colombier 		ds->control = Ipkt|Tcpkt|Udpkt|Rdbsz;
66472cfc098SDavid du Colombier 		ds->branch = PCIWADDR(alloc);
66572cfc098SDavid du Colombier 
66672cfc098SDavid du Colombier 		ds->bp = vt6105Mrballoc();
66772cfc098SDavid du Colombier 		if(ds->bp == nil)
66872cfc098SDavid du Colombier 			error("vt6105M: can't allocate receive ring\n");
66972cfc098SDavid du Colombier 		ds->bp->rp = (uchar*)ROUNDUP((ulong)ds->bp->rp, 4);
67072cfc098SDavid du Colombier 		ds->addr = PCIWADDR(ds->bp->rp);
67172cfc098SDavid du Colombier 
67272cfc098SDavid du Colombier 		ds->next = (Ds*)alloc;
67372cfc098SDavid du Colombier 		ds->prev = prev;
67472cfc098SDavid du Colombier 		prev = ds;
67572cfc098SDavid du Colombier 
67672cfc098SDavid du Colombier 		ds->status = Own;
67772cfc098SDavid du Colombier 	}
67872cfc098SDavid du Colombier 	prev->branch = 0;
67972cfc098SDavid du Colombier 	prev->next = ctlr->rd;
68072cfc098SDavid du Colombier 	prev->status = 0;
68172cfc098SDavid du Colombier 	ctlr->rdh = ctlr->rd;
68272cfc098SDavid du Colombier 
68372cfc098SDavid du Colombier 	ctlr->td = (Ds*)alloc;
68472cfc098SDavid du Colombier 	prev = (Ds*)(alloc + (ctlr->ntd-1)*dsz);
68572cfc098SDavid du Colombier 	for(i = 0; i < ctlr->ntd; i++){
68672cfc098SDavid du Colombier 		ds = (Ds*)alloc;
68772cfc098SDavid du Colombier 		alloc += dsz;
68872cfc098SDavid du Colombier 
68972cfc098SDavid du Colombier 		ds->next = (Ds*)alloc;
69072cfc098SDavid du Colombier 		ds->prev = prev;
69172cfc098SDavid du Colombier 		prev = ds;
69272cfc098SDavid du Colombier 	}
69372cfc098SDavid du Colombier 	prev->next = ctlr->td;
69472cfc098SDavid du Colombier 	ctlr->tdh = ctlr->tdt = ctlr->td;
69572cfc098SDavid du Colombier 	ctlr->tdused = 0;
69672cfc098SDavid du Colombier 
69772cfc098SDavid du Colombier 	ctlr->cr = Dpoll|Rdmd/*|Txon|Rxon*/|Strt;
69872cfc098SDavid du Colombier 	/*Srci|Abti|Norbf|Pktrace|Ovfi|Udfi|Be|Ru|Tu|Txe|Rxe|Ptx|Prx*/
69972cfc098SDavid du Colombier 	ctlr->imr = Abti|Norbf|Pktrace|Ovfi|Udfi|Be|Ru|Tu|Txe|Rxe|Ptx|Prx;
70072cfc098SDavid du Colombier 
70172cfc098SDavid du Colombier 	ilock(&ctlr->clock);
70272cfc098SDavid du Colombier 	csr32w(ctlr, Rxdaddr, PCIWADDR(ctlr->rd));
70372cfc098SDavid du Colombier 	csr32w(ctlr, Txdaddr, PCIWADDR(ctlr->td));
70472cfc098SDavid du Colombier 	csr16w(ctlr, Isr, ~0);
70572cfc098SDavid du Colombier 	csr16w(ctlr, Imr, ctlr->imr);
70672cfc098SDavid du Colombier 	csr16w(ctlr, Cr, ctlr->cr);
70772cfc098SDavid du Colombier 	iunlock(&ctlr->clock);
70872cfc098SDavid du Colombier 
70972cfc098SDavid du Colombier 	/*
71072cfc098SDavid du Colombier 	 * Wait for link to be ready.
71172cfc098SDavid du Colombier 	 */
71272cfc098SDavid du Colombier 	for(timeo = 0; timeo < 350; timeo++){
71372cfc098SDavid du Colombier 		if(miistatus(ctlr->mii) == 0)
71472cfc098SDavid du Colombier 			break;
71572cfc098SDavid du Colombier 		tsleep(&up->sleep, return0, 0, 10);
71672cfc098SDavid du Colombier 	}
71772cfc098SDavid du Colombier //	phy = ctlr->mii->curphy;
71872cfc098SDavid du Colombier //	print("%s: speed %d fd %d link %d rfc %d tfc %d\n",
71972cfc098SDavid du Colombier //		edev->name, phy->speed, phy->fd, phy->link, phy->rfc, phy->tfc);
72072cfc098SDavid du Colombier 
72172cfc098SDavid du Colombier 	ilock(&ctlr->clock);
72272cfc098SDavid du Colombier 	ctlr->cr |= Txon|Rxon;
72372cfc098SDavid du Colombier 	csr16w(ctlr, Cr, ctlr->cr);
72472cfc098SDavid du Colombier 	iunlock(&ctlr->clock);
72572cfc098SDavid du Colombier 
72672cfc098SDavid du Colombier 	snprint(name, KNAMELEN, "#l%dlproc", edev->ctlrno);
72772cfc098SDavid du Colombier 	kproc(name, vt6105Mlproc, edev);
72872cfc098SDavid du Colombier 
72972cfc098SDavid du Colombier 	qunlock(&ctlr->alock);
73072cfc098SDavid du Colombier 	poperror();
73172cfc098SDavid du Colombier }
73272cfc098SDavid du Colombier 
73372cfc098SDavid du Colombier static void
vt6105Mtransmit(Ether * edev)73472cfc098SDavid du Colombier vt6105Mtransmit(Ether* edev)
73572cfc098SDavid du Colombier {
73672cfc098SDavid du Colombier 	Block *bp;
73772cfc098SDavid du Colombier 	Ctlr *ctlr;
73872cfc098SDavid du Colombier 	Ds *ds, *next;
73972cfc098SDavid du Colombier 	int control, i, size, tdused, timeo;
74072cfc098SDavid du Colombier 	long t;
74172cfc098SDavid du Colombier 
74272cfc098SDavid du Colombier 	ctlr = edev->ctlr;
74372cfc098SDavid du Colombier 
74472cfc098SDavid du Colombier 	ilock(&ctlr->tlock);
74572cfc098SDavid du Colombier 	t = lcycles();
74672cfc098SDavid du Colombier 
74772cfc098SDavid du Colombier 	/*
74872cfc098SDavid du Colombier 	 * Free any completed packets
74972cfc098SDavid du Colombier 	 */
75072cfc098SDavid du Colombier 	ds = ctlr->tdh;
75172cfc098SDavid du Colombier 	for(tdused = ctlr->tdused; tdused > 0; tdused--){
75272cfc098SDavid du Colombier 		/*
75372cfc098SDavid du Colombier 		 * For some errors the chip will turn the Tx engine
75472cfc098SDavid du Colombier 		 * off. Wait for that to happen.
75572cfc098SDavid du Colombier 		 * Could reset and re-init the chip here if it doesn't
75672cfc098SDavid du Colombier 		 * play fair.
75772cfc098SDavid du Colombier 		 * To do: adjust Tx FIFO threshold on underflow.
75872cfc098SDavid du Colombier 		 */
75972cfc098SDavid du Colombier 		if(ds->status & (Abt|Tbuff|Udf)){
76072cfc098SDavid du Colombier 			if(ds->status & Abt)
76172cfc098SDavid du Colombier 				ctlr->abt++;
76272cfc098SDavid du Colombier 			if(ds->status & Tbuff)
76372cfc098SDavid du Colombier 				ctlr->tbuff++;
76472cfc098SDavid du Colombier 			if(ds->status & Udf)
76572cfc098SDavid du Colombier 				ctlr->udf++;
76672cfc098SDavid du Colombier 			for(timeo = 0; timeo < 1000; timeo++){
76772cfc098SDavid du Colombier 				if(!(csr16r(ctlr, Cr) & Txon))
76872cfc098SDavid du Colombier 					break;
76972cfc098SDavid du Colombier 				microdelay(1);
77072cfc098SDavid du Colombier 			}
77172cfc098SDavid du Colombier 			ds->status = Own;
77272cfc098SDavid du Colombier 			csr32w(ctlr, Txdaddr, PCIWADDR(ds));
77372cfc098SDavid du Colombier 		}
77472cfc098SDavid du Colombier 
77572cfc098SDavid du Colombier 		if(ds->status & Own)
77672cfc098SDavid du Colombier 			break;
77772cfc098SDavid du Colombier 		ds->addr = 0;
77872cfc098SDavid du Colombier 		ds->branch = 0;
77972cfc098SDavid du Colombier 
78072cfc098SDavid du Colombier 		if(ds->bp != nil){
78172cfc098SDavid du Colombier 			freeb(ds->bp);
78272cfc098SDavid du Colombier 			ds->bp = nil;
78372cfc098SDavid du Colombier 		}
78472cfc098SDavid du Colombier 		for(i = 0; i < Ntxstats-1; i++){
78572cfc098SDavid du Colombier 			if(ds->status & (1<<i))
78672cfc098SDavid du Colombier 				ctlr->txstats[i]++;
78772cfc098SDavid du Colombier 		}
78872cfc098SDavid du Colombier 		ctlr->txstats[i] += (ds->status & NcrMASK)>>NcrSHIFT;
78972cfc098SDavid du Colombier 
79072cfc098SDavid du Colombier 		ds = ds->next;
79172cfc098SDavid du Colombier 	}
79272cfc098SDavid du Colombier 	ctlr->tdh = ds;
79372cfc098SDavid du Colombier 
79472cfc098SDavid du Colombier 	/*
79572cfc098SDavid du Colombier 	 * Try to fill the ring back up.
79672cfc098SDavid du Colombier 	 */
79772cfc098SDavid du Colombier 	ds = ctlr->tdt;
79872cfc098SDavid du Colombier 	while(tdused < ctlr->ntd-2){
79972cfc098SDavid du Colombier 		if((bp = qget(edev->oq)) == nil)
80072cfc098SDavid du Colombier 			break;
80172cfc098SDavid du Colombier 		tdused++;
80272cfc098SDavid du Colombier 
80372cfc098SDavid du Colombier 		size = BLEN(bp);
80472cfc098SDavid du Colombier 
80572cfc098SDavid du Colombier 		next = ds->next;
80672cfc098SDavid du Colombier 		ds->branch = PCIWADDR(ds->next)|Tdctl;
80772cfc098SDavid du Colombier 
80872cfc098SDavid du Colombier 		ds->bp = bp;
80972cfc098SDavid du Colombier 		ds->addr = PCIWADDR(bp->rp);
81072cfc098SDavid du Colombier 		control = Edp|Stp|((size<<TbsSHIFT) & TbsMASK);
81172cfc098SDavid du Colombier 
81272cfc098SDavid du Colombier 		ds->control = control;
81372cfc098SDavid du Colombier 		if(tdused >= ctlr->ntd-2){
81472cfc098SDavid du Colombier 			ctlr->txdw++;
81572cfc098SDavid du Colombier 			ds->branch &= ~Tdctl;
81672cfc098SDavid du Colombier 		}
81772cfc098SDavid du Colombier 		coherence();
81872cfc098SDavid du Colombier 		ds->status = Own;
81972cfc098SDavid du Colombier 
82072cfc098SDavid du Colombier 		ds = next;
82172cfc098SDavid du Colombier 	}
82272cfc098SDavid du Colombier 	ctlr->tdt = ds;
82372cfc098SDavid du Colombier 	ctlr->tdused = tdused;
82472cfc098SDavid du Colombier 	if(ctlr->tdused){
82572cfc098SDavid du Colombier 		csr16w(ctlr, Cr, Tdmd|ctlr->cr);
82672cfc098SDavid du Colombier 		if(tdused > ctlr->tdumax)
82772cfc098SDavid du Colombier 			ctlr->tdumax = tdused;
82872cfc098SDavid du Colombier 	}
82972cfc098SDavid du Colombier 
83072cfc098SDavid du Colombier 	ctlr->totalt += lcycles() - t;
83172cfc098SDavid du Colombier 	iunlock(&ctlr->tlock);
83272cfc098SDavid du Colombier }
83372cfc098SDavid du Colombier 
83472cfc098SDavid du Colombier static void
vt6105Mreceive(Ether * edev)83572cfc098SDavid du Colombier vt6105Mreceive(Ether* edev)
83672cfc098SDavid du Colombier {
83772cfc098SDavid du Colombier 	Ds *ds;
83872cfc098SDavid du Colombier 	Block *bp;
83972cfc098SDavid du Colombier 	Ctlr *ctlr;
84072cfc098SDavid du Colombier 	int i, len;
84172cfc098SDavid du Colombier 
84272cfc098SDavid du Colombier 	ctlr = edev->ctlr;
84372cfc098SDavid du Colombier 
84472cfc098SDavid du Colombier 	ds = ctlr->rdh;
84572cfc098SDavid du Colombier 	while(!(ds->status & Own) && ds->status != 0){
84672cfc098SDavid du Colombier 		/*
84772cfc098SDavid du Colombier 		 * Can Long packets be received OK?
84872cfc098SDavid du Colombier 		 * What happens to the Rxok bit?
84972cfc098SDavid du Colombier 		 */
85072cfc098SDavid du Colombier 		if(ds->status & Rerr){
85172cfc098SDavid du Colombier 			for(i = 0; i < Nrxstats; i++){
85272cfc098SDavid du Colombier 				if(ds->status & (1<<i))
85372cfc098SDavid du Colombier 					ctlr->rxstats[i]++;
85472cfc098SDavid du Colombier 			}
85572cfc098SDavid du Colombier 		}
85672cfc098SDavid du Colombier 		else if(bp = vt6105Mrballoc()){
85772cfc098SDavid du Colombier 			if(ds->control & Tuok){
85872cfc098SDavid du Colombier 				ds->bp->flag |= Btcpck|Budpck;
85972cfc098SDavid du Colombier 				ctlr->tuok++;
86072cfc098SDavid du Colombier 			}
86172cfc098SDavid du Colombier 			if(ds->control & Ipok){
86272cfc098SDavid du Colombier 				ds->bp->flag |= Bipck;
86372cfc098SDavid du Colombier 				ctlr->ipok++;
86472cfc098SDavid du Colombier 			}
86572cfc098SDavid du Colombier 			len = ((ds->status & LengthMASK)>>LengthSHIFT)-4;
86672cfc098SDavid du Colombier 			ds->bp->wp = ds->bp->rp+len;
86772cfc098SDavid du Colombier 			etheriq(edev, ds->bp, 1);
86872cfc098SDavid du Colombier 			bp->rp = (uchar*)ROUNDUP((ulong)bp->rp, 4);
86972cfc098SDavid du Colombier 			ds->addr = PCIWADDR(bp->rp);
87072cfc098SDavid du Colombier 			ds->bp = bp;
87172cfc098SDavid du Colombier 		}
87272cfc098SDavid du Colombier 		ds->control = Ipkt|Tcpkt|Udpkt|Rdbsz;
87372cfc098SDavid du Colombier 		ds->branch = 0;
87472cfc098SDavid du Colombier 		ds->status = 0;
87572cfc098SDavid du Colombier 
87672cfc098SDavid du Colombier 		ds->prev->branch = PCIWADDR(ds);
87772cfc098SDavid du Colombier 		coherence();
87872cfc098SDavid du Colombier 		ds->prev->status = Own;
87972cfc098SDavid du Colombier 
88072cfc098SDavid du Colombier 		ds = ds->next;
88172cfc098SDavid du Colombier 	}
88272cfc098SDavid du Colombier 	ctlr->rdh = ds;
88372cfc098SDavid du Colombier 
88472cfc098SDavid du Colombier 	csr16w(ctlr, Cr, ctlr->cr);
88572cfc098SDavid du Colombier }
88672cfc098SDavid du Colombier 
88772cfc098SDavid du Colombier static void
vt6105Minterrupt(Ureg *,void * arg)88872cfc098SDavid du Colombier vt6105Minterrupt(Ureg*, void* arg)
88972cfc098SDavid du Colombier {
89072cfc098SDavid du Colombier 	Ctlr *ctlr;
89172cfc098SDavid du Colombier 	Ether *edev;
89272cfc098SDavid du Colombier 	int imr, isr, r, timeo;
89372cfc098SDavid du Colombier 	long t;
89472cfc098SDavid du Colombier 
89572cfc098SDavid du Colombier 	edev = arg;
89672cfc098SDavid du Colombier 	ctlr = edev->ctlr;
89772cfc098SDavid du Colombier 
89872cfc098SDavid du Colombier 	ilock(&ctlr->clock);
89972cfc098SDavid du Colombier 	t = lcycles();
90072cfc098SDavid du Colombier 
90172cfc098SDavid du Colombier 	csr16w(ctlr, Imr, 0);
90272cfc098SDavid du Colombier 	imr = ctlr->imr;
90372cfc098SDavid du Colombier 	ctlr->intr++;
90472cfc098SDavid du Colombier 	for(;;){
90572cfc098SDavid du Colombier 		if((isr = csr16r(ctlr, Isr)) != 0)
90672cfc098SDavid du Colombier 			csr16w(ctlr, Isr, isr);
90772cfc098SDavid du Colombier 		if((isr & ctlr->imr) == 0)
90872cfc098SDavid du Colombier 			break;
90972cfc098SDavid du Colombier 
91072cfc098SDavid du Colombier 		if(isr & Srci){
91172cfc098SDavid du Colombier 			imr &= ~Srci;
91272cfc098SDavid du Colombier 			ctlr->lwakeup = isr & Srci;
91372cfc098SDavid du Colombier 			wakeup(&ctlr->lrendez);
91472cfc098SDavid du Colombier 			isr &= ~Srci;
91572cfc098SDavid du Colombier 			ctlr->lintr++;
91672cfc098SDavid du Colombier 		}
91772cfc098SDavid du Colombier 		if(isr & (Norbf|Pktrace|Ovfi|Ru|Rxe|Prx)){
91872cfc098SDavid du Colombier 			vt6105Mreceive(edev);
91972cfc098SDavid du Colombier 			isr &= ~(Norbf|Pktrace|Ovfi|Ru|Rxe|Prx);
92072cfc098SDavid du Colombier 			ctlr->rintr++;
92172cfc098SDavid du Colombier 		}
92272cfc098SDavid du Colombier 		if(isr & (Abti|Udfi|Tu|Txe|Ptx)){
92372cfc098SDavid du Colombier 			if(isr & (Abti|Udfi|Tu)){
92472cfc098SDavid du Colombier 				if(isr & Abti)
92572cfc098SDavid du Colombier 					ctlr->abti++;
92672cfc098SDavid du Colombier 				if(isr & Udfi)
92772cfc098SDavid du Colombier 					ctlr->udfi++;
92872cfc098SDavid du Colombier 				if(isr & Tu)
92972cfc098SDavid du Colombier 					ctlr->tu++;
93072cfc098SDavid du Colombier 				for(timeo = 0; timeo < 1000; timeo++){
93172cfc098SDavid du Colombier 					if(!(csr16r(ctlr, Cr) & Txon))
93272cfc098SDavid du Colombier 						break;
93372cfc098SDavid du Colombier 					microdelay(1);
93472cfc098SDavid du Colombier 				}
93572cfc098SDavid du Colombier 
93672cfc098SDavid du Colombier 				if((isr & Udfi) && ctlr->tft < CtftSAF){
93772cfc098SDavid du Colombier 					ctlr->tft += 1<<CtftSHIFT;
93872cfc098SDavid du Colombier 					r = csr8r(ctlr, Bcr1) & ~CtftMASK;
93972cfc098SDavid du Colombier 					csr8w(ctlr, Bcr1, r|ctlr->tft);
94072cfc098SDavid du Colombier 				}
94172cfc098SDavid du Colombier 			}
94272cfc098SDavid du Colombier 
94372cfc098SDavid du Colombier 
94472cfc098SDavid du Colombier 			ctlr->totalt += lcycles() - t;
94572cfc098SDavid du Colombier 			vt6105Mtransmit(edev);
94672cfc098SDavid du Colombier 			t = lcycles();
94772cfc098SDavid du Colombier 			isr &= ~(Abti|Udfi|Tu|Txe|Ptx);
94872cfc098SDavid du Colombier 			ctlr->tintr++;
94972cfc098SDavid du Colombier 		}
95072cfc098SDavid du Colombier 		if(isr)
95172cfc098SDavid du Colombier 			panic("vt6105M: isr %4.4uX\n", isr);
95272cfc098SDavid du Colombier 	}
95372cfc098SDavid du Colombier 	ctlr->imr = imr;
95472cfc098SDavid du Colombier 	csr16w(ctlr, Imr, ctlr->imr);
95572cfc098SDavid du Colombier 
95672cfc098SDavid du Colombier 	ctlr->totalt += lcycles() - t;
95772cfc098SDavid du Colombier 	iunlock(&ctlr->clock);
95872cfc098SDavid du Colombier }
95972cfc098SDavid du Colombier 
96072cfc098SDavid du Colombier static int
vt6105Mmiimicmd(Mii * mii,int pa,int ra,int cmd,int data)96172cfc098SDavid du Colombier vt6105Mmiimicmd(Mii* mii, int pa, int ra, int cmd, int data)
96272cfc098SDavid du Colombier {
96372cfc098SDavid du Colombier 	Ctlr *ctlr;
96472cfc098SDavid du Colombier 	int r, timeo;
96572cfc098SDavid du Colombier 
96672cfc098SDavid du Colombier 	ctlr = mii->ctlr;
96772cfc098SDavid du Colombier 
96872cfc098SDavid du Colombier 	csr8w(ctlr, Miicr, 0);
96972cfc098SDavid du Colombier 	r = csr8r(ctlr, Phyadr);
97072cfc098SDavid du Colombier 	csr8w(ctlr, Phyadr, (r & ~PhyadMASK)|pa);
97172cfc098SDavid du Colombier 	csr8w(ctlr, Phyadr, pa);
97272cfc098SDavid du Colombier 	csr8w(ctlr, Miiadr, ra);
97372cfc098SDavid du Colombier 	if(cmd == Wcmd)
97472cfc098SDavid du Colombier 		csr16w(ctlr, Miidata, data);
97572cfc098SDavid du Colombier 	csr8w(ctlr, Miicr, cmd);
97672cfc098SDavid du Colombier 
977*5b797632SDavid du Colombier 	for(timeo = 0; timeo < Maxus; timeo++){
97872cfc098SDavid du Colombier 		if(!(csr8r(ctlr, Miicr) & cmd))
97972cfc098SDavid du Colombier 			break;
98072cfc098SDavid du Colombier 		microdelay(1);
98172cfc098SDavid du Colombier 	}
982*5b797632SDavid du Colombier 	if(timeo >= Maxus)
98372cfc098SDavid du Colombier 		return -1;
98472cfc098SDavid du Colombier 
98572cfc098SDavid du Colombier 	if(cmd == Wcmd)
98672cfc098SDavid du Colombier 		return 0;
98772cfc098SDavid du Colombier 	return csr16r(ctlr, Miidata);
98872cfc098SDavid du Colombier }
98972cfc098SDavid du Colombier 
99072cfc098SDavid du Colombier static int
vt6105Mmiimir(Mii * mii,int pa,int ra)99172cfc098SDavid du Colombier vt6105Mmiimir(Mii* mii, int pa, int ra)
99272cfc098SDavid du Colombier {
99372cfc098SDavid du Colombier 	return vt6105Mmiimicmd(mii, pa, ra, Rcmd, 0);
99472cfc098SDavid du Colombier }
99572cfc098SDavid du Colombier 
99672cfc098SDavid du Colombier static int
vt6105Mmiimiw(Mii * mii,int pa,int ra,int data)99772cfc098SDavid du Colombier vt6105Mmiimiw(Mii* mii, int pa, int ra, int data)
99872cfc098SDavid du Colombier {
99972cfc098SDavid du Colombier 	return vt6105Mmiimicmd(mii, pa, ra, Wcmd, data);
100072cfc098SDavid du Colombier }
100172cfc098SDavid du Colombier 
100272cfc098SDavid du Colombier static int
vt6105Mdetach(Ctlr * ctlr)100372cfc098SDavid du Colombier vt6105Mdetach(Ctlr* ctlr)
100472cfc098SDavid du Colombier {
100572cfc098SDavid du Colombier 	int revid, timeo;
100672cfc098SDavid du Colombier 
100772cfc098SDavid du Colombier 	/*
100872cfc098SDavid du Colombier 	 * Reset power management registers.
100972cfc098SDavid du Colombier 	 */
101072cfc098SDavid du Colombier 	revid = pcicfgr8(ctlr->pcidev, PciRID);
101172cfc098SDavid du Colombier 	if(revid >= 0x40){
101272cfc098SDavid du Colombier 		/* Set power state D0. */
101372cfc098SDavid du Colombier 		csr8w(ctlr, Stickhw, csr8r(ctlr, Stickhw) & 0xFC);
101472cfc098SDavid du Colombier 
101572cfc098SDavid du Colombier 		/* Disable force PME-enable. */
101672cfc098SDavid du Colombier 		csr8w(ctlr, Wolcgclr, 0x80);
101772cfc098SDavid du Colombier 
101872cfc098SDavid du Colombier 		/* Clear WOL config and status bits. */
101972cfc098SDavid du Colombier 		csr8w(ctlr, Wolcrclr, 0xFF);
102072cfc098SDavid du Colombier 		csr8w(ctlr, Pwrcsrclr, 0xFF);
102172cfc098SDavid du Colombier 	}
102272cfc098SDavid du Colombier 
102372cfc098SDavid du Colombier 	/*
102472cfc098SDavid du Colombier 	 * Soft reset the controller.
102572cfc098SDavid du Colombier 	 */
102672cfc098SDavid du Colombier 	csr16w(ctlr, Cr, Stop);
102772cfc098SDavid du Colombier 	csr16w(ctlr, Cr, Stop|Sfrst);
1028*5b797632SDavid du Colombier 	for(timeo = 0; timeo < Maxus; timeo++){
102972cfc098SDavid du Colombier 		if(!(csr16r(ctlr, Cr) & Sfrst))
103072cfc098SDavid du Colombier 			break;
103172cfc098SDavid du Colombier 		microdelay(1);
103272cfc098SDavid du Colombier 	}
1033*5b797632SDavid du Colombier 	if(timeo >= Maxus)
103472cfc098SDavid du Colombier 		return -1;
103572cfc098SDavid du Colombier 
103672cfc098SDavid du Colombier 	return 0;
103772cfc098SDavid du Colombier }
103872cfc098SDavid du Colombier 
10390a2a9dafSDavid du Colombier static void
vt6105Mshutdown(Ether * ether)10400a2a9dafSDavid du Colombier vt6105Mshutdown(Ether *ether)
10410a2a9dafSDavid du Colombier {
10420a2a9dafSDavid du Colombier 	Ctlr *ctlr = ether->ctlr;
10430a2a9dafSDavid du Colombier 
10440a2a9dafSDavid du Colombier 	vt6105Mdetach(ctlr);
10450a2a9dafSDavid du Colombier }
10460a2a9dafSDavid du Colombier 
104772cfc098SDavid du Colombier static int
vt6105Mreset(Ctlr * ctlr)104872cfc098SDavid du Colombier vt6105Mreset(Ctlr* ctlr)
104972cfc098SDavid du Colombier {
105072cfc098SDavid du Colombier 	MiiPhy *phy;
105172cfc098SDavid du Colombier 	int i, r, timeo;
105272cfc098SDavid du Colombier 
105372cfc098SDavid du Colombier 	if(vt6105Mdetach(ctlr) < 0)
105472cfc098SDavid du Colombier 		return -1;
105572cfc098SDavid du Colombier 
105672cfc098SDavid du Colombier 	/*
105772cfc098SDavid du Colombier 	 * Load the MAC address into the PAR[01]
105872cfc098SDavid du Colombier 	 * registers.
105972cfc098SDavid du Colombier 	 */
106072cfc098SDavid du Colombier 	r = csr8r(ctlr, Eecsr);
106172cfc098SDavid du Colombier 	csr8w(ctlr, Eecsr, Autold|r);
1062*5b797632SDavid du Colombier 	for(timeo = 0; timeo < Maxus; timeo++){
106372cfc098SDavid du Colombier 		if(!(csr8r(ctlr, Cr) & Autold))
106472cfc098SDavid du Colombier 			break;
106572cfc098SDavid du Colombier 		microdelay(1);
106672cfc098SDavid du Colombier 	}
1067*5b797632SDavid du Colombier 	if(timeo >= Maxus)
106872cfc098SDavid du Colombier 		return -1;
106972cfc098SDavid du Colombier 
107072cfc098SDavid du Colombier 	for(i = 0; i < Eaddrlen; i++)
107172cfc098SDavid du Colombier 		ctlr->par[i] = csr8r(ctlr, Par0+i);
107272cfc098SDavid du Colombier 
107372cfc098SDavid du Colombier 	/*
107472cfc098SDavid du Colombier 	 * Configure DMA and Rx/Tx thresholds.
107572cfc098SDavid du Colombier 	 * If the Rx/Tx threshold bits in Bcr[01] are 0 then
107672cfc098SDavid du Colombier 	 * the thresholds are determined by Rcr/Tcr.
107772cfc098SDavid du Colombier 	 */
107872cfc098SDavid du Colombier 	r = csr8r(ctlr, Bcr0) & ~(CrftMASK|DmaMASK);
107972cfc098SDavid du Colombier 	csr8w(ctlr, Bcr0, r|Crft128|DmaSAF);
108072cfc098SDavid du Colombier 	r = csr8r(ctlr, Bcr1) & ~CtftMASK;
108172cfc098SDavid du Colombier 	csr8w(ctlr, Bcr1, r|ctlr->tft);
108272cfc098SDavid du Colombier 
108372cfc098SDavid du Colombier 	r = csr8r(ctlr, Rcr) & ~(RrftMASK|Prom|Ar|Sep);
108472cfc098SDavid du Colombier 	csr8w(ctlr, Rcr, r|Ab|Am);
108572cfc098SDavid du Colombier 	csr32w(ctlr, Mcfilt0, ~0UL);	/* accept all multicast */
108672cfc098SDavid du Colombier 	csr32w(ctlr, Mcfilt1, ~0UL);
108772cfc098SDavid du Colombier 
108872cfc098SDavid du Colombier 	r = csr8r(ctlr, Tcr) & ~(RtsfMASK|Ofset|Lb1|Lb0);
108972cfc098SDavid du Colombier 	csr8w(ctlr, Tcr, r);
109072cfc098SDavid du Colombier 
109172cfc098SDavid du Colombier 	/*
109272cfc098SDavid du Colombier 	 * Link management.
109372cfc098SDavid du Colombier 	 */
109472cfc098SDavid du Colombier 	if((ctlr->mii = malloc(sizeof(Mii))) == nil)
109572cfc098SDavid du Colombier 		return -1;
109672cfc098SDavid du Colombier 	ctlr->mii->mir = vt6105Mmiimir;
109772cfc098SDavid du Colombier 	ctlr->mii->miw = vt6105Mmiimiw;
109872cfc098SDavid du Colombier 	ctlr->mii->ctlr = ctlr;
109972cfc098SDavid du Colombier 
110072cfc098SDavid du Colombier 	if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){
110172cfc098SDavid du Colombier 		free(ctlr->mii);
110272cfc098SDavid du Colombier 		ctlr->mii = nil;
110372cfc098SDavid du Colombier 		return -1;
110472cfc098SDavid du Colombier 	}
110572cfc098SDavid du Colombier //	print("oui %X phyno %d\n", phy->oui, phy->phyno);
110672cfc098SDavid du Colombier 	USED(phy);
110772cfc098SDavid du Colombier 
110872cfc098SDavid du Colombier 	if(miistatus(ctlr->mii) < 0){
110972cfc098SDavid du Colombier //		miireset(ctlr->mii);
111072cfc098SDavid du Colombier 		miiane(ctlr->mii, ~0, ~0, ~0);
111172cfc098SDavid du Colombier 	}
111272cfc098SDavid du Colombier 
111372cfc098SDavid du Colombier 	return 0;
111472cfc098SDavid du Colombier }
111572cfc098SDavid du Colombier 
111672cfc098SDavid du Colombier static void
vt6105Mpci(void)111772cfc098SDavid du Colombier vt6105Mpci(void)
111872cfc098SDavid du Colombier {
111972cfc098SDavid du Colombier 	Pcidev *p;
112072cfc098SDavid du Colombier 	Ctlr *ctlr;
112172cfc098SDavid du Colombier 	int cls, port;
112272cfc098SDavid du Colombier 
112372cfc098SDavid du Colombier 	p = nil;
112472cfc098SDavid du Colombier 	while(p = pcimatch(p, 0, 0)){
112572cfc098SDavid du Colombier 		if(p->ccrb != Pcibcnet || p->ccru != Pciscether)
112672cfc098SDavid du Colombier 			continue;
112772cfc098SDavid du Colombier 
112872cfc098SDavid du Colombier 		switch((p->did<<16)|p->vid){
112972cfc098SDavid du Colombier 		default:
113072cfc098SDavid du Colombier 			continue;
113172cfc098SDavid du Colombier 		case (0x3053<<16)|0x1106:	/* Rhine III-M vt6105M */
113272cfc098SDavid du Colombier 			break;
113372cfc098SDavid du Colombier 		}
113472cfc098SDavid du Colombier 
113572cfc098SDavid du Colombier 		port = p->mem[0].bar & ~0x01;
113672cfc098SDavid du Colombier 		if(ioalloc(port, p->mem[0].size, 0, "vt6105M") < 0){
113772cfc098SDavid du Colombier 			print("vt6105M: port 0x%uX in use\n", port);
113872cfc098SDavid du Colombier 			continue;
113972cfc098SDavid du Colombier 		}
114072cfc098SDavid du Colombier 		ctlr = malloc(sizeof(Ctlr));
1141aa72973aSDavid du Colombier 		if(ctlr == nil) {
1142aa72973aSDavid du Colombier 			iofree(port);
1143aa72973aSDavid du Colombier 			error(Enomem);
1144aa72973aSDavid du Colombier 		}
114572cfc098SDavid du Colombier 		ctlr->port = port;
114672cfc098SDavid du Colombier 		ctlr->pcidev = p;
114772cfc098SDavid du Colombier 		ctlr->id = (p->did<<16)|p->vid;
114872cfc098SDavid du Colombier 		if((cls = pcicfgr8(p, PciCLS)) == 0 || cls == 0xFF)
114972cfc098SDavid du Colombier 			cls = 0x10;
115072cfc098SDavid du Colombier 		ctlr->cls = cls*4;
115172cfc098SDavid du Colombier 		if(ctlr->cls < sizeof(Ds)){
115272cfc098SDavid du Colombier 			print("vt6105M: cls %d < sizeof(Ds)\n", ctlr->cls);
115372cfc098SDavid du Colombier 			iofree(port);
115472cfc098SDavid du Colombier 			free(ctlr);
115572cfc098SDavid du Colombier 			continue;
115672cfc098SDavid du Colombier 		}
115772cfc098SDavid du Colombier 		ctlr->tft = CtftSAF;
115872cfc098SDavid du Colombier 
115972cfc098SDavid du Colombier 		if(vt6105Mreset(ctlr)){
116072cfc098SDavid du Colombier 			iofree(port);
116172cfc098SDavid du Colombier 			free(ctlr);
116272cfc098SDavid du Colombier 			continue;
116372cfc098SDavid du Colombier 		}
116472cfc098SDavid du Colombier 		pcisetbme(p);
116572cfc098SDavid du Colombier 
116672cfc098SDavid du Colombier 		if(vt6105Mctlrhead != nil)
116772cfc098SDavid du Colombier 			vt6105Mctlrtail->next = ctlr;
116872cfc098SDavid du Colombier 		else
116972cfc098SDavid du Colombier 			vt6105Mctlrhead = ctlr;
117072cfc098SDavid du Colombier 		vt6105Mctlrtail = ctlr;
117172cfc098SDavid du Colombier 	}
117272cfc098SDavid du Colombier }
117372cfc098SDavid du Colombier 
117472cfc098SDavid du Colombier static int
vt6105Mpnp(Ether * edev)117572cfc098SDavid du Colombier vt6105Mpnp(Ether* edev)
117672cfc098SDavid du Colombier {
117772cfc098SDavid du Colombier 	Ctlr *ctlr;
117872cfc098SDavid du Colombier 
117972cfc098SDavid du Colombier 	if(vt6105Mctlrhead == nil)
118072cfc098SDavid du Colombier 		vt6105Mpci();
118172cfc098SDavid du Colombier 
118272cfc098SDavid du Colombier 	/*
118372cfc098SDavid du Colombier 	 * Any adapter matches if no edev->port is supplied,
118472cfc098SDavid du Colombier 	 * otherwise the ports must match.
118572cfc098SDavid du Colombier 	 */
118672cfc098SDavid du Colombier 	for(ctlr = vt6105Mctlrhead; ctlr != nil; ctlr = ctlr->next){
118772cfc098SDavid du Colombier 		if(ctlr->active)
118872cfc098SDavid du Colombier 			continue;
118972cfc098SDavid du Colombier 		if(edev->port == 0 || edev->port == ctlr->port){
119072cfc098SDavid du Colombier 			ctlr->active = 1;
119172cfc098SDavid du Colombier 			break;
119272cfc098SDavid du Colombier 		}
119372cfc098SDavid du Colombier 	}
119472cfc098SDavid du Colombier 	if(ctlr == nil)
119572cfc098SDavid du Colombier 		return -1;
119672cfc098SDavid du Colombier 
119772cfc098SDavid du Colombier 	edev->ctlr = ctlr;
119872cfc098SDavid du Colombier 	edev->port = ctlr->port;
119972cfc098SDavid du Colombier 	edev->irq = ctlr->pcidev->intl;
120072cfc098SDavid du Colombier 	edev->tbdf = ctlr->pcidev->tbdf;
120172cfc098SDavid du Colombier 	/*
120272cfc098SDavid du Colombier 	 * Set to 1000Mb/s to fool the bsz calculation.  We need
120372cfc098SDavid du Colombier 	 * something better, though.
120472cfc098SDavid du Colombier 	 */
120572cfc098SDavid du Colombier 	edev->mbps = 1000;
120672cfc098SDavid du Colombier 	memmove(edev->ea, ctlr->par, Eaddrlen);
120772cfc098SDavid du Colombier 
120872cfc098SDavid du Colombier 	/*
120972cfc098SDavid du Colombier 	 * Linkage to the generic ethernet driver.
121072cfc098SDavid du Colombier 	 */
121172cfc098SDavid du Colombier 	edev->attach = vt6105Mattach;
121272cfc098SDavid du Colombier 	edev->transmit = vt6105Mtransmit;
121372cfc098SDavid du Colombier 	edev->interrupt = vt6105Minterrupt;
121472cfc098SDavid du Colombier 	edev->ifstat = vt6105Mifstat;
12150a2a9dafSDavid du Colombier 	edev->shutdown = vt6105Mshutdown;
121672cfc098SDavid du Colombier 	edev->ctl = nil;
121772cfc098SDavid du Colombier 
121872cfc098SDavid du Colombier 	edev->arg = edev;
121972cfc098SDavid du Colombier 	edev->promiscuous = vt6105Mpromiscuous;
122072cfc098SDavid du Colombier 	edev->multicast = vt6105Mmulticast;
122172cfc098SDavid du Colombier 
122272cfc098SDavid du Colombier 	edev->maxmtu = ETHERMAXTU+Bslop;
122372cfc098SDavid du Colombier 
122472cfc098SDavid du Colombier 	return 0;
122572cfc098SDavid du Colombier }
122672cfc098SDavid du Colombier 
122772cfc098SDavid du Colombier void
ethervt6105mlink(void)122872cfc098SDavid du Colombier ethervt6105mlink(void)
122972cfc098SDavid du Colombier {
123072cfc098SDavid du Colombier 	addethercard("vt6105M", vt6105Mpnp);
123172cfc098SDavid du Colombier }
1232