xref: /plan9/sys/src/9/pc/ether82563.c (revision cebd3b46303b6884206200c08c0d8b3fb8dc989e)
147ad9175SDavid du Colombier /*
208cb4641SDavid du Colombier  * Intel Gigabit Ethernet PCI-Express Controllers.
39b7bf7dfSDavid du Colombier  *	8256[36], 8257[1-79]
408cb4641SDavid du Colombier  * Pretty basic, does not use many of the chip smarts.
508cb4641SDavid du Colombier  * The interrupt mitigation tuning for each chip variant
608cb4641SDavid du Colombier  * is probably different. The reset/initialisation
791157df7SDavid du Colombier  * sequence needs straightened out. Doubt the PHY code
808cb4641SDavid du Colombier  * for the 82575eb is right.
947ad9175SDavid du Colombier  */
1047ad9175SDavid du Colombier #include "u.h"
1147ad9175SDavid du Colombier #include "../port/lib.h"
1247ad9175SDavid du Colombier #include "mem.h"
1347ad9175SDavid du Colombier #include "dat.h"
1447ad9175SDavid du Colombier #include "fns.h"
1547ad9175SDavid du Colombier #include "io.h"
1647ad9175SDavid du Colombier #include "../port/error.h"
1747ad9175SDavid du Colombier #include "../port/netif.h"
1847ad9175SDavid du Colombier 
1947ad9175SDavid du Colombier #include "etherif.h"
2047ad9175SDavid du Colombier 
2147ad9175SDavid du Colombier /*
2247ad9175SDavid du Colombier  * these are in the order they appear in the manual, not numeric order.
2347ad9175SDavid du Colombier  * It was too hard to find them in the book. Ref 21489, rev 2.6
2447ad9175SDavid du Colombier  */
2547ad9175SDavid du Colombier 
2647ad9175SDavid du Colombier enum {
2747ad9175SDavid du Colombier 	/* General */
2847ad9175SDavid du Colombier 
2937a6523bSDavid du Colombier 	Ctrl		= 0x0000,	/* Device Control */
3037a6523bSDavid du Colombier 	Status		= 0x0008,	/* Device Status */
3137a6523bSDavid du Colombier 	Eec		= 0x0010,	/* EEPROM/Flash Control/Data */
3237a6523bSDavid du Colombier 	Eerd		= 0x0014,	/* EEPROM Read */
3337a6523bSDavid du Colombier 	Ctrlext		= 0x0018,	/* Extended Device Control */
3437a6523bSDavid du Colombier 	Fla		= 0x001c,	/* Flash Access */
3537a6523bSDavid du Colombier 	Mdic		= 0x0020,	/* MDI Control */
3637a6523bSDavid du Colombier 	Seresctl	= 0x0024,	/* Serdes ana */
3737a6523bSDavid du Colombier 	Fcal		= 0x0028,	/* Flow Control Address Low */
3837a6523bSDavid du Colombier 	Fcah		= 0x002C,	/* Flow Control Address High */
3937a6523bSDavid du Colombier 	Fct		= 0x0030,	/* Flow Control Type */
4008cb4641SDavid du Colombier 	Kumctrlsta	= 0x0034,	/* MAC-PHY Interface */
4137a6523bSDavid du Colombier 	Vet		= 0x0038,	/* VLAN EtherType */
4237a6523bSDavid du Colombier 	Fcttv		= 0x0170,	/* Flow Control Transmit Timer Value */
4337a6523bSDavid du Colombier 	Txcw		= 0x0178,	/* Transmit Configuration Word */
4437a6523bSDavid du Colombier 	Rxcw		= 0x0180,	/* Receive Configuration Word */
4537a6523bSDavid du Colombier 	Ledctl		= 0x0E00,	/* LED control */
4637a6523bSDavid du Colombier 	Pba		= 0x1000,	/* Packet Buffer Allocation */
4737a6523bSDavid du Colombier 	Pbs		= 0x1008,	/* Packet Buffer Size */
4847ad9175SDavid du Colombier 
4947ad9175SDavid du Colombier 	/* Interrupt */
5047ad9175SDavid du Colombier 
5137a6523bSDavid du Colombier 	Icr		= 0x00C0,	/* Interrupt Cause Read */
5237a6523bSDavid du Colombier 	Itr		= 0x00c4,	/* Interrupt Throttling Rate */
5337a6523bSDavid du Colombier 	Ics		= 0x00C8,	/* Interrupt Cause Set */
5437a6523bSDavid du Colombier 	Ims		= 0x00D0,	/* Interrupt Mask Set/Read */
5537a6523bSDavid du Colombier 	Imc		= 0x00D8,	/* Interrupt mask Clear */
5637a6523bSDavid du Colombier 	Iam		= 0x00E0,	/* Interrupt acknowledge Auto Mask */
5747ad9175SDavid du Colombier 
5847ad9175SDavid du Colombier 	/* Receive */
5947ad9175SDavid du Colombier 
6037a6523bSDavid du Colombier 	Rctl		= 0x0100,	/* Control */
619b7bf7dfSDavid du Colombier 	Ert		= 0x2008,	/* Early Receive Threshold (573[EVL], 579 only) */
6237a6523bSDavid du Colombier 	Fcrtl		= 0x2160,	/* Flow Control RX Threshold Low */
6337a6523bSDavid du Colombier 	Fcrth		= 0x2168,	/* Flow Control Rx Threshold High */
6437a6523bSDavid du Colombier 	Psrctl		= 0x2170,	/* Packet Split Receive Control */
6537a6523bSDavid du Colombier 	Rdbal		= 0x2800,	/* Rdesc Base Address Low Queue 0 */
6637a6523bSDavid du Colombier 	Rdbah		= 0x2804,	/* Rdesc Base Address High Queue 0 */
6737a6523bSDavid du Colombier 	Rdlen		= 0x2808,	/* Descriptor Length Queue 0 */
6837a6523bSDavid du Colombier 	Rdh		= 0x2810,	/* Descriptor Head Queue 0 */
6937a6523bSDavid du Colombier 	Rdt		= 0x2818,	/* Descriptor Tail Queue 0 */
7037a6523bSDavid du Colombier 	Rdtr		= 0x2820,	/* Descriptor Timer Ring */
7137a6523bSDavid du Colombier 	Rxdctl		= 0x2828,	/* Descriptor Control */
7237a6523bSDavid du Colombier 	Radv		= 0x282C,	/* Interrupt Absolute Delay Timer */
7337a6523bSDavid du Colombier 	Rdbal1		= 0x2900,	/* Rdesc Base Address Low Queue 1 */
7437a6523bSDavid du Colombier 	Rdbah1		= 0x2804,	/* Rdesc Base Address High Queue 1 */
7537a6523bSDavid du Colombier 	Rdlen1		= 0x2908,	/* Descriptor Length Queue 1 */
7637a6523bSDavid du Colombier 	Rdh1		= 0x2910,	/* Descriptor Head Queue 1 */
7737a6523bSDavid du Colombier 	Rdt1		= 0x2918,	/* Descriptor Tail Queue 1 */
7837a6523bSDavid du Colombier 	Rxdctl1		= 0x2928,	/* Descriptor Control Queue 1 */
7937a6523bSDavid du Colombier 	Rsrpd		= 0x2c00,	/* Small Packet Detect */
8037a6523bSDavid du Colombier 	Raid		= 0x2c08,	/* ACK interrupt delay */
8137a6523bSDavid du Colombier 	Cpuvec		= 0x2c10,	/* CPU Vector */
8237a6523bSDavid du Colombier 	Rxcsum		= 0x5000,	/* Checksum Control */
8337a6523bSDavid du Colombier 	Rfctl		= 0x5008,	/* Filter Control */
8437a6523bSDavid du Colombier 	Mta		= 0x5200,	/* Multicast Table Array */
8537a6523bSDavid du Colombier 	Ral		= 0x5400,	/* Receive Address Low */
8637a6523bSDavid du Colombier 	Rah		= 0x5404,	/* Receive Address High */
8737a6523bSDavid du Colombier 	Vfta		= 0x5600,	/* VLAN Filter Table Array */
8837a6523bSDavid du Colombier 	Mrqc		= 0x5818,	/* Multiple Receive Queues Command */
8937a6523bSDavid du Colombier 	Rssim		= 0x5864,	/* RSS Interrupt Mask */
9037a6523bSDavid du Colombier 	Rssir		= 0x5868,	/* RSS Interrupt Request */
9137a6523bSDavid du Colombier 	Reta		= 0x5c00,	/* Redirection Table */
9237a6523bSDavid du Colombier 	Rssrk		= 0x5c80,	/* RSS Random Key */
9347ad9175SDavid du Colombier 
9447ad9175SDavid du Colombier 	/* Transmit */
9547ad9175SDavid du Colombier 
9637a6523bSDavid du Colombier 	Tctl		= 0x0400,	/* Transmit Control */
9737a6523bSDavid du Colombier 	Tipg		= 0x0410,	/* Transmit IPG */
9837a6523bSDavid du Colombier 	Tkabgtxd	= 0x3004,	/* glci afe band gap transmit ref data, or something */
9937a6523bSDavid du Colombier 	Tdbal		= 0x3800,	/* Tdesc Base Address Low */
10037a6523bSDavid du Colombier 	Tdbah		= 0x3804,	/* Tdesc Base Address High */
10137a6523bSDavid du Colombier 	Tdlen		= 0x3808,	/* Descriptor Length */
10237a6523bSDavid du Colombier 	Tdh		= 0x3810,	/* Descriptor Head */
10337a6523bSDavid du Colombier 	Tdt		= 0x3818,	/* Descriptor Tail */
10437a6523bSDavid du Colombier 	Tidv		= 0x3820,	/* Interrupt Delay Value */
10537a6523bSDavid du Colombier 	Txdctl		= 0x3828,	/* Descriptor Control */
10637a6523bSDavid du Colombier 	Tadv		= 0x382C,	/* Interrupt Absolute Delay Timer */
10737a6523bSDavid du Colombier 	Tarc0		= 0x3840,	/* Arbitration Counter Queue 0 */
10837a6523bSDavid du Colombier 	Tdbal1		= 0x3900,	/* Descriptor Base Low Queue 1 */
10937a6523bSDavid du Colombier 	Tdbah1		= 0x3904,	/* Descriptor Base High Queue 1 */
11037a6523bSDavid du Colombier 	Tdlen1		= 0x3908,	/* Descriptor Length Queue 1 */
11137a6523bSDavid du Colombier 	Tdh1		= 0x3910,	/* Descriptor Head Queue 1 */
11237a6523bSDavid du Colombier 	Tdt1		= 0x3918,	/* Descriptor Tail Queue 1 */
11337a6523bSDavid du Colombier 	Txdctl1		= 0x3928,	/* Descriptor Control 1 */
11437a6523bSDavid du Colombier 	Tarc1		= 0x3940,	/* Arbitration Counter Queue 1 */
11547ad9175SDavid du Colombier 
11647ad9175SDavid du Colombier 	/* Statistics */
11747ad9175SDavid du Colombier 
11837a6523bSDavid du Colombier 	Statistics	= 0x4000,	/* Start of Statistics Area */
11947ad9175SDavid du Colombier 	Gorcl		= 0x88/4,	/* Good Octets Received Count */
12047ad9175SDavid du Colombier 	Gotcl		= 0x90/4,	/* Good Octets Transmitted Count */
12147ad9175SDavid du Colombier 	Torl		= 0xC0/4,	/* Total Octets Received */
12247ad9175SDavid du Colombier 	Totl		= 0xC8/4,	/* Total Octets Transmitted */
12337a6523bSDavid du Colombier 	Nstatistics	= 0x124/4,
12447ad9175SDavid du Colombier };
12547ad9175SDavid du Colombier 
12647ad9175SDavid du Colombier enum {					/* Ctrl */
12737a6523bSDavid du Colombier 	GIOmd		= 1<<2,		/* BIO master disable */
12837a6523bSDavid du Colombier 	Lrst		= 1<<3,		/* link reset */
12937a6523bSDavid du Colombier 	Slu		= 1<<6,		/* Set Link Up */
13037a6523bSDavid du Colombier 	SspeedMASK	= 3<<8,		/* Speed Selection */
13147ad9175SDavid du Colombier 	SspeedSHIFT	= 8,
1320343ea0dSDavid du Colombier 	Sspeed10	= 0x00000000,	/* 10Mb/s */
1330343ea0dSDavid du Colombier 	Sspeed100	= 0x00000100,	/* 100Mb/s */
1340343ea0dSDavid du Colombier 	Sspeed1000	= 0x00000200,	/* 1000Mb/s */
13537a6523bSDavid du Colombier 	Frcspd		= 1<<11,	/* Force Speed */
13637a6523bSDavid du Colombier 	Frcdplx		= 1<<12,	/* Force Duplex */
13747ad9175SDavid du Colombier 	SwdpinsloMASK	= 0x003C0000,	/* Software Defined Pins - lo nibble */
13847ad9175SDavid du Colombier 	SwdpinsloSHIFT	= 18,
13947ad9175SDavid du Colombier 	SwdpioloMASK	= 0x03C00000,	/* Software Defined Pins - I or O */
14047ad9175SDavid du Colombier 	SwdpioloSHIFT	= 22,
14137a6523bSDavid du Colombier 	Devrst		= 1<<26,	/* Device Reset */
14237a6523bSDavid du Colombier 	Rfce		= 1<<27,	/* Receive Flow Control Enable */
14337a6523bSDavid du Colombier 	Tfce		= 1<<28,	/* Transmit Flow Control Enable */
14437a6523bSDavid du Colombier 	Vme		= 1<<30,	/* VLAN Mode Enable */
14537a6523bSDavid du Colombier 	Phyrst		= 1<<31,	/* Phy Reset */
14647ad9175SDavid du Colombier };
14747ad9175SDavid du Colombier 
14847ad9175SDavid du Colombier enum {					/* Status */
14937a6523bSDavid du Colombier 	Lu		= 1<<1,		/* Link Up */
15022f9ff60SDavid du Colombier 	Lanid		= 3<<2,		/* mask for Lan ID. */
15137a6523bSDavid du Colombier 	Txoff		= 1<<4,		/* Transmission Paused */
15237a6523bSDavid du Colombier 	Tbimode		= 1<<5,		/* TBI Mode Indication */
15337a6523bSDavid du Colombier 	Phyra		= 1<<10,	/* PHY Reset Asserted */
15437a6523bSDavid du Colombier 	GIOme		= 1<<19,	/* GIO Master Enable Status */
15547ad9175SDavid du Colombier };
15647ad9175SDavid du Colombier 
15747ad9175SDavid du Colombier enum {					/* Eerd */
1580343ea0dSDavid du Colombier 	EEstart		= 1<<0,		/* Start Read */
1590343ea0dSDavid du Colombier 	EEdone		= 1<<1,		/* Read done */
16047ad9175SDavid du Colombier };
16147ad9175SDavid du Colombier 
16247ad9175SDavid du Colombier enum {					/* Ctrlext */
16337a6523bSDavid du Colombier 	Asdchk		= 1<<12,	/* ASD Check */
16437a6523bSDavid du Colombier 	Eerst		= 1<<13,	/* EEPROM Reset */
16537a6523bSDavid du Colombier 	Spdbyps		= 1<<15,	/* Speed Select Bypass */
16647ad9175SDavid du Colombier };
16747ad9175SDavid du Colombier 
16847ad9175SDavid du Colombier enum {					/* EEPROM content offsets */
16947ad9175SDavid du Colombier 	Ea		= 0x00,		/* Ethernet Address */
17047ad9175SDavid du Colombier 	Cf		= 0x03,		/* Compatibility Field */
17147ad9175SDavid du Colombier 	Icw1		= 0x0A,		/* Initialization Control Word 1 */
17247ad9175SDavid du Colombier 	Sid		= 0x0B,		/* Subsystem ID */
17347ad9175SDavid du Colombier 	Svid		= 0x0C,		/* Subsystem Vendor ID */
17447ad9175SDavid du Colombier 	Did		= 0x0D,		/* Device ID */
17547ad9175SDavid du Colombier 	Vid		= 0x0E,		/* Vendor ID */
17647ad9175SDavid du Colombier 	Icw2		= 0x0F,		/* Initialization Control Word 2 */
17747ad9175SDavid du Colombier };
17847ad9175SDavid du Colombier 
17947ad9175SDavid du Colombier enum {					/* Mdic */
1800343ea0dSDavid du Colombier 	MDIdMASK	= 0x0000FFFF,	/* Data */
18147ad9175SDavid du Colombier 	MDIdSHIFT	= 0,
18247ad9175SDavid du Colombier 	MDIrMASK	= 0x001F0000,	/* PHY Register Address */
18347ad9175SDavid du Colombier 	MDIrSHIFT	= 16,
18447ad9175SDavid du Colombier 	MDIpMASK	= 0x03E00000,	/* PHY Address */
18547ad9175SDavid du Colombier 	MDIpSHIFT	= 21,
18647ad9175SDavid du Colombier 	MDIwop		= 0x04000000,	/* Write Operation */
18747ad9175SDavid du Colombier 	MDIrop		= 0x08000000,	/* Read Operation */
18847ad9175SDavid du Colombier 	MDIready	= 0x10000000,	/* End of Transaction */
18947ad9175SDavid du Colombier 	MDIie		= 0x20000000,	/* Interrupt Enable */
19047ad9175SDavid du Colombier 	MDIe		= 0x40000000,	/* Error */
19147ad9175SDavid du Colombier };
19247ad9175SDavid du Colombier 
19331748cd5SDavid du Colombier enum {					/* phy interface registers */
19431748cd5SDavid du Colombier 	Phyctl		= 0,		/* phy ctl */
19531748cd5SDavid du Colombier 	Physsr		= 17,		/* phy secondary status */
19631748cd5SDavid du Colombier 	Phyier		= 18,		/* 82573 phy interrupt enable */
19731748cd5SDavid du Colombier 	Phyisr		= 19,		/* 82563 phy interrupt status */
19831748cd5SDavid du Colombier 	Phylhr		= 19,		/* 8257[12] link health */
19931748cd5SDavid du Colombier 
20037a6523bSDavid du Colombier 	Rtlink		= 1<<10,	/* realtime link status */
20131748cd5SDavid du Colombier 	Phyan		= 1<<11,	/* phy has auto-negotiated */
20231748cd5SDavid du Colombier 
20331748cd5SDavid du Colombier 	/* Phyctl bits */
20431748cd5SDavid du Colombier 	Ran		= 1<<9,		/* restart auto-negotiation */
20531748cd5SDavid du Colombier 	Ean		= 1<<12,	/* enable auto-negotiation */
20631748cd5SDavid du Colombier 
20731748cd5SDavid du Colombier 	/* 82573 Phyier bits */
20831748cd5SDavid du Colombier 	Lscie		= 1<<10,	/* link status changed ie */
20931748cd5SDavid du Colombier 	Ancie		= 1<<11,	/* auto-negotiation complete ie */
21031748cd5SDavid du Colombier 	Spdie		= 1<<14,	/* speed changed ie */
21131748cd5SDavid du Colombier 	Panie		= 1<<15,	/* phy auto-negotiation error ie */
21231748cd5SDavid du Colombier 
21331748cd5SDavid du Colombier 	/* Phylhr/Phyisr bits */
21431748cd5SDavid du Colombier 	Anf		= 1<<6,		/* lhr: auto-negotiation fault */
21531748cd5SDavid du Colombier 	Ane		= 1<<15,	/* isr: auto-negotiation error */
21637a6523bSDavid du Colombier };
21737a6523bSDavid du Colombier 
21847ad9175SDavid du Colombier enum {					/* Icr, Ics, Ims, Imc */
2190343ea0dSDavid du Colombier 	Txdw		= 0x00000001,	/* Transmit Descriptor Written Back */
2200343ea0dSDavid du Colombier 	Txqe		= 0x00000002,	/* Transmit Queue Empty */
2210343ea0dSDavid du Colombier 	Lsc		= 0x00000004,	/* Link Status Change */
2220343ea0dSDavid du Colombier 	Rxseq		= 0x00000008,	/* Receive Sequence Error */
2230343ea0dSDavid du Colombier 	Rxdmt0		= 0x00000010,	/* Rdesc Minimum Threshold Reached */
2240343ea0dSDavid du Colombier 	Rxo		= 0x00000040,	/* Receiver Overrun */
2250343ea0dSDavid du Colombier 	Rxt0		= 0x00000080,	/* Receiver Timer Interrupt */
2260343ea0dSDavid du Colombier 	Mdac		= 0x00000200,	/* MDIO Access Completed */
2270343ea0dSDavid du Colombier 	Rxcfg		= 0x00000400,	/* Receiving /C/ ordered sets */
2280343ea0dSDavid du Colombier 	Gpi0		= 0x00000800,	/* General Purpose Interrupts */
2290343ea0dSDavid du Colombier 	Gpi1		= 0x00001000,
2300343ea0dSDavid du Colombier 	Gpi2		= 0x00002000,
2310343ea0dSDavid du Colombier 	Gpi3		= 0x00004000,
23237a6523bSDavid du Colombier 	Ack		= 0x00020000,	/* Receive ACK frame */
23347ad9175SDavid du Colombier };
23447ad9175SDavid du Colombier 
23547ad9175SDavid du Colombier enum {					/* Txcw */
2360343ea0dSDavid du Colombier 	TxcwFd		= 0x00000020,	/* Full Duplex */
2370343ea0dSDavid du Colombier 	TxcwHd		= 0x00000040,	/* Half Duplex */
2380343ea0dSDavid du Colombier 	TxcwPauseMASK	= 0x00000180,	/* Pause */
23947ad9175SDavid du Colombier 	TxcwPauseSHIFT	= 7,
24037a6523bSDavid du Colombier 	TxcwPs		= 1<<TxcwPauseSHIFT,	/* Pause Supported */
24137a6523bSDavid du Colombier 	TxcwAs		= 2<<TxcwPauseSHIFT,	/* Asymmetric FC desired */
2420343ea0dSDavid du Colombier 	TxcwRfiMASK	= 0x00003000,	/* Remote Fault Indication */
24347ad9175SDavid du Colombier 	TxcwRfiSHIFT	= 12,
2440343ea0dSDavid du Colombier 	TxcwNpr		= 0x00008000,	/* Next Page Request */
24508cb4641SDavid du Colombier 	TxcwConfig	= 0x40000000,	/* Transmit Config Control */
24647ad9175SDavid du Colombier 	TxcwAne		= 0x80000000,	/* Auto-Negotiation Enable */
24747ad9175SDavid du Colombier };
24847ad9175SDavid du Colombier 
24947ad9175SDavid du Colombier enum {					/* Rctl */
2500343ea0dSDavid du Colombier 	Rrst		= 0x00000001,	/* Receiver Software Reset */
2510343ea0dSDavid du Colombier 	Ren		= 0x00000002,	/* Receiver Enable */
2520343ea0dSDavid du Colombier 	Sbp		= 0x00000004,	/* Store Bad Packets */
2530343ea0dSDavid du Colombier 	Upe		= 0x00000008,	/* Unicast Promiscuous Enable */
2540343ea0dSDavid du Colombier 	Mpe		= 0x00000010,	/* Multicast Promiscuous Enable */
2550343ea0dSDavid du Colombier 	Lpe		= 0x00000020,	/* Long Packet Reception Enable */
2560343ea0dSDavid du Colombier 	LbmMASK		= 0x000000C0,	/* Loopback Mode */
2570343ea0dSDavid du Colombier 	LbmOFF		= 0x00000000,	/* No Loopback */
2580343ea0dSDavid du Colombier 	LbmTBI		= 0x00000040,	/* TBI Loopback */
2590343ea0dSDavid du Colombier 	LbmMII		= 0x00000080,	/* GMII/MII Loopback */
2600343ea0dSDavid du Colombier 	LbmXCVR		= 0x000000C0,	/* Transceiver Loopback */
2610343ea0dSDavid du Colombier 	RdtmsMASK	= 0x00000300,	/* Rdesc Minimum Threshold Size */
2620343ea0dSDavid du Colombier 	RdtmsHALF	= 0x00000000,	/* Threshold is 1/2 Rdlen */
2630343ea0dSDavid du Colombier 	RdtmsQUARTER	= 0x00000100,	/* Threshold is 1/4 Rdlen */
2640343ea0dSDavid du Colombier 	RdtmsEIGHTH	= 0x00000200,	/* Threshold is 1/8 Rdlen */
2650343ea0dSDavid du Colombier 	MoMASK		= 0x00003000,	/* Multicast Offset */
2660343ea0dSDavid du Colombier 	Bam		= 0x00008000,	/* Broadcast Accept Mode */
26747ad9175SDavid du Colombier 	BsizeMASK	= 0x00030000,	/* Receive Buffer Size */
26837a6523bSDavid du Colombier 	Bsize16384	= 0x00010000,	/* Bsex = 1 */
26947ad9175SDavid du Colombier 	Bsize8192	= 0x00020000, 	/* Bsex = 1 */
2700343ea0dSDavid du Colombier 	Bsize2048	= 0x00000000,
27147ad9175SDavid du Colombier 	Bsize1024	= 0x00010000,
27247ad9175SDavid du Colombier 	Bsize512	= 0x00020000,
27347ad9175SDavid du Colombier 	Bsize256	= 0x00030000,
27408cb4641SDavid du Colombier 	BsizeFlex	= 0x08000000,	/* Flexible Bsize in 1KB increments */
27547ad9175SDavid du Colombier 	Vfe		= 0x00040000,	/* VLAN Filter Enable */
27647ad9175SDavid du Colombier 	Cfien		= 0x00080000,	/* Canonical Form Indicator Enable */
27747ad9175SDavid du Colombier 	Cfi		= 0x00100000,	/* Canonical Form Indicator value */
27847ad9175SDavid du Colombier 	Dpf		= 0x00400000,	/* Discard Pause Frames */
27947ad9175SDavid du Colombier 	Pmcf		= 0x00800000,	/* Pass MAC Control Frames */
28047ad9175SDavid du Colombier 	Bsex		= 0x02000000,	/* Buffer Size Extension */
28147ad9175SDavid du Colombier 	Secrc		= 0x04000000,	/* Strip CRC from incoming packet */
28247ad9175SDavid du Colombier };
28347ad9175SDavid du Colombier 
28447ad9175SDavid du Colombier enum {					/* Tctl */
2850343ea0dSDavid du Colombier 	Trst		= 0x00000001,	/* Transmitter Software Reset */
2860343ea0dSDavid du Colombier 	Ten		= 0x00000002,	/* Transmit Enable */
2870343ea0dSDavid du Colombier 	Psp		= 0x00000008,	/* Pad Short Packets */
28837a6523bSDavid du Colombier 	Mulr		= 0x10000000,	/* Allow multiple concurrent requests */
2890343ea0dSDavid du Colombier 	CtMASK		= 0x00000FF0,	/* Collision Threshold */
29047ad9175SDavid du Colombier 	CtSHIFT		= 4,
29147ad9175SDavid du Colombier 	ColdMASK	= 0x003FF000,	/* Collision Distance */
29247ad9175SDavid du Colombier 	ColdSHIFT	= 12,
29347ad9175SDavid du Colombier 	Swxoff		= 0x00400000,	/* Sofware XOFF Transmission */
29447ad9175SDavid du Colombier 	Pbe		= 0x00800000,	/* Packet Burst Enable */
29547ad9175SDavid du Colombier 	Rtlc		= 0x01000000,	/* Re-transmit on Late Collision */
29647ad9175SDavid du Colombier 	Nrtu		= 0x02000000,	/* No Re-transmit on Underrrun */
29747ad9175SDavid du Colombier };
29847ad9175SDavid du Colombier 
29947ad9175SDavid du Colombier enum {					/* [RT]xdctl */
3000343ea0dSDavid du Colombier 	PthreshMASK	= 0x0000003F,	/* Prefetch Threshold */
30147ad9175SDavid du Colombier 	PthreshSHIFT	= 0,
3020343ea0dSDavid du Colombier 	HthreshMASK	= 0x00003F00,	/* Host Threshold */
30347ad9175SDavid du Colombier 	HthreshSHIFT	= 8,
30437a6523bSDavid du Colombier 	WthreshMASK	= 0x003F0000,	/* Writeback Threshold */
30547ad9175SDavid du Colombier 	WthreshSHIFT	= 16,
30647ad9175SDavid du Colombier 	Gran		= 0x01000000,	/* Granularity */
30708cb4641SDavid du Colombier 	Qenable		= 0x02000000,	/* Queue Enable (82575) */
30847ad9175SDavid du Colombier };
30947ad9175SDavid du Colombier 
31047ad9175SDavid du Colombier enum {					/* Rxcsum */
31137a6523bSDavid du Colombier 	PcssMASK	= 0x00FF,	/* Packet Checksum Start */
31247ad9175SDavid du Colombier 	PcssSHIFT	= 0,
31337a6523bSDavid du Colombier 	Ipofl		= 0x0100,	/* IP Checksum Off-load Enable */
31437a6523bSDavid du Colombier 	Tuofl		= 0x0200,	/* TCP/UDP Checksum Off-load Enable */
31547ad9175SDavid du Colombier };
31647ad9175SDavid du Colombier 
31747ad9175SDavid du Colombier enum {					/* Receive Delay Timer Ring */
31837a6523bSDavid du Colombier 	DelayMASK	= 0xFFFF,	/* delay timer in 1.024nS increments */
31947ad9175SDavid du Colombier 	DelaySHIFT	= 0,
32047ad9175SDavid du Colombier 	Fpd		= 0x80000000,	/* Flush partial Descriptor Block */
32147ad9175SDavid du Colombier };
32247ad9175SDavid du Colombier 
323c02f0a41SDavid du Colombier typedef struct Ctlr Ctlr;
324c02f0a41SDavid du Colombier typedef struct Rd Rd;
325c02f0a41SDavid du Colombier typedef struct Td Td;
326c02f0a41SDavid du Colombier 
327c02f0a41SDavid du Colombier struct Rd {				/* Receive Descriptor */
32808cb4641SDavid du Colombier 	u32int	addr[2];
32908cb4641SDavid du Colombier 	u16int	length;
33008cb4641SDavid du Colombier 	u16int	checksum;
33108cb4641SDavid du Colombier 	u8int	status;
33208cb4641SDavid du Colombier 	u8int	errors;
33308cb4641SDavid du Colombier 	u16int	special;
334c02f0a41SDavid du Colombier };
33547ad9175SDavid du Colombier 
33647ad9175SDavid du Colombier enum {					/* Rd status */
33747ad9175SDavid du Colombier 	Rdd		= 0x01,		/* Descriptor Done */
33847ad9175SDavid du Colombier 	Reop		= 0x02,		/* End of Packet */
33947ad9175SDavid du Colombier 	Ixsm		= 0x04,		/* Ignore Checksum Indication */
34047ad9175SDavid du Colombier 	Vp		= 0x08,		/* Packet is 802.1Q (matched VET) */
34147ad9175SDavid du Colombier 	Tcpcs		= 0x20,		/* TCP Checksum Calculated on Packet */
34247ad9175SDavid du Colombier 	Ipcs		= 0x40,		/* IP Checksum Calculated on Packet */
34347ad9175SDavid du Colombier 	Pif		= 0x80,		/* Passed in-exact filter */
34447ad9175SDavid du Colombier };
34547ad9175SDavid du Colombier 
34647ad9175SDavid du Colombier enum {					/* Rd errors */
34747ad9175SDavid du Colombier 	Ce		= 0x01,		/* CRC Error or Alignment Error */
34847ad9175SDavid du Colombier 	Se		= 0x02,		/* Symbol Error */
34947ad9175SDavid du Colombier 	Seq		= 0x04,		/* Sequence Error */
35047ad9175SDavid du Colombier 	Cxe		= 0x10,		/* Carrier Extension Error */
35147ad9175SDavid du Colombier 	Tcpe		= 0x20,		/* TCP/UDP Checksum Error */
35247ad9175SDavid du Colombier 	Ipe		= 0x40,		/* IP Checksum Error */
35347ad9175SDavid du Colombier 	Rxe		= 0x80,		/* RX Data Error */
35447ad9175SDavid du Colombier };
35547ad9175SDavid du Colombier 
356c02f0a41SDavid du Colombier struct Td {				/* Transmit Descriptor */
35708cb4641SDavid du Colombier 	u32int	addr[2];		/* Data */
35808cb4641SDavid du Colombier 	u32int	control;
35908cb4641SDavid du Colombier 	u32int	status;
360c02f0a41SDavid du Colombier };
36147ad9175SDavid du Colombier 
36247ad9175SDavid du Colombier enum {					/* Tdesc control */
36347ad9175SDavid du Colombier 	LenMASK		= 0x000FFFFF,	/* Data/Packet Length Field */
36447ad9175SDavid du Colombier 	LenSHIFT	= 0,
3650343ea0dSDavid du Colombier 	DtypeCD		= 0x00000000,	/* Data Type 'Context Descriptor' */
36647ad9175SDavid du Colombier 	DtypeDD		= 0x00100000,	/* Data Type 'Data Descriptor' */
36747ad9175SDavid du Colombier 	PtypeTCP	= 0x01000000,	/* TCP/UDP Packet Type (CD) */
36847ad9175SDavid du Colombier 	Teop		= 0x01000000,	/* End of Packet (DD) */
36947ad9175SDavid du Colombier 	PtypeIP		= 0x02000000,	/* IP Packet Type (CD) */
37047ad9175SDavid du Colombier 	Ifcs		= 0x02000000,	/* Insert FCS (DD) */
37147ad9175SDavid du Colombier 	Tse		= 0x04000000,	/* TCP Segmentation Enable */
37247ad9175SDavid du Colombier 	Rs		= 0x08000000,	/* Report Status */
37347ad9175SDavid du Colombier 	Rps		= 0x10000000,	/* Report Status Sent */
37447ad9175SDavid du Colombier 	Dext		= 0x20000000,	/* Descriptor Extension */
37547ad9175SDavid du Colombier 	Vle		= 0x40000000,	/* VLAN Packet Enable */
37647ad9175SDavid du Colombier 	Ide		= 0x80000000,	/* Interrupt Delay Enable */
37747ad9175SDavid du Colombier };
37847ad9175SDavid du Colombier 
37947ad9175SDavid du Colombier enum {					/* Tdesc status */
38037a6523bSDavid du Colombier 	Tdd		= 0x0001,	/* Descriptor Done */
38137a6523bSDavid du Colombier 	Ec		= 0x0002,	/* Excess Collisions */
38237a6523bSDavid du Colombier 	Lc		= 0x0004,	/* Late Collision */
38337a6523bSDavid du Colombier 	Tu		= 0x0008,	/* Transmit Underrun */
38437a6523bSDavid du Colombier 	CssMASK		= 0xFF00,	/* Checksum Start Field */
38547ad9175SDavid du Colombier 	CssSHIFT	= 8,
38647ad9175SDavid du Colombier };
38747ad9175SDavid du Colombier 
38891b330d9SDavid du Colombier typedef struct {
38908cb4641SDavid du Colombier 	u16int	*reg;
39008cb4641SDavid du Colombier 	u32int	*reg32;
3918c242bd4SDavid du Colombier 	u16int	base;
3928c242bd4SDavid du Colombier 	u16int	lim;
39391b330d9SDavid du Colombier } Flash;
39491b330d9SDavid du Colombier 
39591b330d9SDavid du Colombier enum {
39691b330d9SDavid du Colombier 	/* 16 and 32-bit flash registers for ich flash parts */
39791b330d9SDavid du Colombier 	Bfpr	= 0x00/4,		/* flash base 0:12; lim 16:28 */
3983bf825d6SDavid du Colombier 	Fsts	= 0x04/2,		/* flash status;  Hsfsts */
3993bf825d6SDavid du Colombier 	Fctl	= 0x06/2,		/* flash control; Hsfctl */
40091b330d9SDavid du Colombier 	Faddr	= 0x08/4,		/* flash address to r/w */
40191b330d9SDavid du Colombier 	Fdata	= 0x10/4,		/* data @ address */
40291b330d9SDavid du Colombier 
40391b330d9SDavid du Colombier 	/* status register */
40491b330d9SDavid du Colombier 	Fdone	= 1<<0,			/* flash cycle done */
40591b330d9SDavid du Colombier 	Fcerr	= 1<<1,			/* cycle error; write 1 to clear */
40691b330d9SDavid du Colombier 	Ael	= 1<<2,			/* direct access error log; 1 to clear */
40791b330d9SDavid du Colombier 	Scip	= 1<<5,			/* spi cycle in progress */
40891b330d9SDavid du Colombier 	Fvalid	= 1<<14,		/* flash descriptor valid */
40991b330d9SDavid du Colombier 
41091b330d9SDavid du Colombier 	/* control register */
41191b330d9SDavid du Colombier 	Fgo	= 1<<0,			/* start cycle */
41291b330d9SDavid du Colombier 	Flcycle	= 1<<1,			/* two bits: r=0; w=2 */
41391b330d9SDavid du Colombier 	Fdbc	= 1<<8,			/* bytes to read; 5 bits */
41491b330d9SDavid du Colombier };
41591b330d9SDavid du Colombier 
41647ad9175SDavid du Colombier enum {
417588d0145SDavid du Colombier 	/* were 512, 1024 & 64, but 52, 253 and 9 are ample. */
418588d0145SDavid du Colombier 	Nrd		= 128,		/* power of two */
419*cebd3b46SDavid du Colombier 	Nrb		= 512,		/* private receive buffers per Ctlr */
420588d0145SDavid du Colombier 	Ntd		= 32,		/* power of two */
42147ad9175SDavid du Colombier };
42247ad9175SDavid du Colombier 
42337a6523bSDavid du Colombier enum {
42491b330d9SDavid du Colombier 	Iany,
42537a6523bSDavid du Colombier 	i82563,
42637a6523bSDavid du Colombier 	i82566,
427e4ac449cSDavid du Colombier 	i82567,
42837a6523bSDavid du Colombier 	i82571,
42937a6523bSDavid du Colombier 	i82572,
43037a6523bSDavid du Colombier 	i82573,
43188bfb1f3SDavid du Colombier 	i82574,
43208cb4641SDavid du Colombier 	i82575,
433c39c2eb3SDavid du Colombier 	i82576,
434d8cd2beaSDavid du Colombier 	i82577,
4359b7bf7dfSDavid du Colombier 	i82579,
43637a6523bSDavid du Colombier };
43737a6523bSDavid du Colombier 
43837a6523bSDavid du Colombier static int rbtab[] = {
43991b330d9SDavid du Colombier 	0,
44037a6523bSDavid du Colombier 	9014,
441a587111cSDavid du Colombier 	ETHERMAXTU,
442a587111cSDavid du Colombier 	ETHERMAXTU,
44337a6523bSDavid du Colombier 	9234,
44437a6523bSDavid du Colombier 	9234,
44537a6523bSDavid du Colombier 	8192,				/* terrible performance above 8k */
446a587111cSDavid du Colombier 	ETHERMAXTU,
447a587111cSDavid du Colombier 	ETHERMAXTU,
448a587111cSDavid du Colombier 	ETHERMAXTU,
449a587111cSDavid du Colombier 	ETHERMAXTU,
4509b7bf7dfSDavid du Colombier 	9018,
45137a6523bSDavid du Colombier };
45237a6523bSDavid du Colombier 
45337a6523bSDavid du Colombier static char *tname[] = {
45491b330d9SDavid du Colombier 	"any",
45537a6523bSDavid du Colombier 	"i82563",
45637a6523bSDavid du Colombier 	"i82566",
457e4ac449cSDavid du Colombier 	"i82567",
45837a6523bSDavid du Colombier 	"i82571",
45937a6523bSDavid du Colombier 	"i82572",
46037a6523bSDavid du Colombier 	"i82573",
46188bfb1f3SDavid du Colombier 	"i82574",
46208cb4641SDavid du Colombier 	"i82575",
463c39c2eb3SDavid du Colombier 	"i82576",
464d8cd2beaSDavid du Colombier 	"i82577",
4659b7bf7dfSDavid du Colombier 	"i82579",
46637a6523bSDavid du Colombier };
46737a6523bSDavid du Colombier 
46847ad9175SDavid du Colombier struct Ctlr {
46947ad9175SDavid du Colombier 	int	port;
47047ad9175SDavid du Colombier 	Pcidev	*pcidev;
47147ad9175SDavid du Colombier 	Ctlr	*next;
47246136019SDavid du Colombier 	Ether	*edev;
47347ad9175SDavid du Colombier 	int	active;
47437a6523bSDavid du Colombier 	int	type;
47547ad9175SDavid du Colombier 	ushort	eeprom[0x40];
47647ad9175SDavid du Colombier 
47747ad9175SDavid du Colombier 	QLock	alock;			/* attach */
47808cb4641SDavid du Colombier 	int	attached;
47947ad9175SDavid du Colombier 	int	nrd;
48047ad9175SDavid du Colombier 	int	ntd;
4816083aa43SDavid du Colombier 	int	nrb;			/* # rcv bufs this Ctlr has in the pool */
4820343ea0dSDavid du Colombier 	unsigned rbsz;			/* unsigned for % and / by 1024 */
48347ad9175SDavid du Colombier 
48447ad9175SDavid du Colombier 	int	*nic;
48547ad9175SDavid du Colombier 	Lock	imlock;
48647ad9175SDavid du Colombier 	int	im;			/* interrupt mask */
48747ad9175SDavid du Colombier 
48847ad9175SDavid du Colombier 	Rendez	lrendez;
48947ad9175SDavid du Colombier 	int	lim;
49047ad9175SDavid du Colombier 
491217e9e83SDavid du Colombier 	Watermark wmrb;
492217e9e83SDavid du Colombier 	Watermark wmrd;
493217e9e83SDavid du Colombier 	Watermark wmtd;
494217e9e83SDavid du Colombier 
49547ad9175SDavid du Colombier 	QLock	slock;
49647ad9175SDavid du Colombier 	uint	statistics[Nstatistics];
49747ad9175SDavid du Colombier 	uint	lsleep;
49847ad9175SDavid du Colombier 	uint	lintr;
49947ad9175SDavid du Colombier 	uint	rsleep;
50047ad9175SDavid du Colombier 	uint	rintr;
50147ad9175SDavid du Colombier 	uint	txdw;
50247ad9175SDavid du Colombier 	uint	tintr;
50347ad9175SDavid du Colombier 	uint	ixsm;
50447ad9175SDavid du Colombier 	uint	ipcs;
50547ad9175SDavid du Colombier 	uint	tcpcs;
50637a6523bSDavid du Colombier 	uint	speeds[4];
50747ad9175SDavid du Colombier 
50847ad9175SDavid du Colombier 	uchar	ra[Eaddrlen];		/* receive address */
50947ad9175SDavid du Colombier 	ulong	mta[128];		/* multicast table array */
51047ad9175SDavid du Colombier 
51147ad9175SDavid du Colombier 	Rendez	rrendez;
51247ad9175SDavid du Colombier 	int	rim;
513c02f0a41SDavid du Colombier 	int	rdfree;			/* rx descriptors awaiting packets */
51447ad9175SDavid du Colombier 	Rd	*rdba;			/* receive descriptor base address */
51547ad9175SDavid du Colombier 	Block	**rb;			/* receive buffers */
51647ad9175SDavid du Colombier 	int	rdh;			/* receive descriptor head */
51747ad9175SDavid du Colombier 	int	rdt;			/* receive descriptor tail */
51847ad9175SDavid du Colombier 	int	rdtr;			/* receive delay timer ring value */
51947ad9175SDavid du Colombier 	int	radv;			/* receive interrupt absolute delay timer */
52047ad9175SDavid du Colombier 
52137a6523bSDavid du Colombier 	Rendez	trendez;
52237a6523bSDavid du Colombier 	QLock	tlock;
52347ad9175SDavid du Colombier 	Td	*tdba;			/* transmit descriptor base address */
52447ad9175SDavid du Colombier 	Block	**tb;			/* transmit buffers */
52547ad9175SDavid du Colombier 	int	tdh;			/* transmit descriptor head */
52647ad9175SDavid du Colombier 	int	tdt;			/* transmit descriptor tail */
52747ad9175SDavid du Colombier 
52847ad9175SDavid du Colombier 	int	fcrtl;
52947ad9175SDavid du Colombier 	int	fcrth;
53047ad9175SDavid du Colombier 
53137a6523bSDavid du Colombier 	uint	pba;			/* packet buffer allocation */
53247ad9175SDavid du Colombier };
53347ad9175SDavid du Colombier 
53447ad9175SDavid du Colombier #define csr32r(c, r)	(*((c)->nic+((r)/4)))
53547ad9175SDavid du Colombier #define csr32w(c, r, v)	(*((c)->nic+((r)/4)) = (v))
53647ad9175SDavid du Colombier 
53747ad9175SDavid du Colombier static Ctlr* i82563ctlrhead;
53847ad9175SDavid du Colombier static Ctlr* i82563ctlrtail;
53947ad9175SDavid du Colombier 
54047ad9175SDavid du Colombier static Lock i82563rblock;		/* free receive Blocks */
54147ad9175SDavid du Colombier static Block* i82563rbpool;
5426083aa43SDavid du Colombier static int nrbfull;	/* # of rcv Blocks with data awaiting processing */
54347ad9175SDavid du Colombier 
54437a6523bSDavid du Colombier static char* statistics[] = {
54547ad9175SDavid du Colombier 	"CRC Error",
54647ad9175SDavid du Colombier 	"Alignment Error",
54747ad9175SDavid du Colombier 	"Symbol Error",
54847ad9175SDavid du Colombier 	"RX Error",
54947ad9175SDavid du Colombier 	"Missed Packets",
55047ad9175SDavid du Colombier 	"Single Collision",
55147ad9175SDavid du Colombier 	"Excessive Collisions",
55247ad9175SDavid du Colombier 	"Multiple Collision",
55347ad9175SDavid du Colombier 	"Late Collisions",
55447ad9175SDavid du Colombier 	nil,
55547ad9175SDavid du Colombier 	"Collision",
55647ad9175SDavid du Colombier 	"Transmit Underrun",
55747ad9175SDavid du Colombier 	"Defer",
55847ad9175SDavid du Colombier 	"Transmit - No CRS",
55947ad9175SDavid du Colombier 	"Sequence Error",
56047ad9175SDavid du Colombier 	"Carrier Extension Error",
56147ad9175SDavid du Colombier 	"Receive Error Length",
56247ad9175SDavid du Colombier 	nil,
56347ad9175SDavid du Colombier 	"XON Received",
56447ad9175SDavid du Colombier 	"XON Transmitted",
56547ad9175SDavid du Colombier 	"XOFF Received",
56647ad9175SDavid du Colombier 	"XOFF Transmitted",
56747ad9175SDavid du Colombier 	"FC Received Unsupported",
56847ad9175SDavid du Colombier 	"Packets Received (64 Bytes)",
56947ad9175SDavid du Colombier 	"Packets Received (65-127 Bytes)",
57047ad9175SDavid du Colombier 	"Packets Received (128-255 Bytes)",
57147ad9175SDavid du Colombier 	"Packets Received (256-511 Bytes)",
57247ad9175SDavid du Colombier 	"Packets Received (512-1023 Bytes)",
57337a6523bSDavid du Colombier 	"Packets Received (1024-mtu Bytes)",
57447ad9175SDavid du Colombier 	"Good Packets Received",
57547ad9175SDavid du Colombier 	"Broadcast Packets Received",
57647ad9175SDavid du Colombier 	"Multicast Packets Received",
57747ad9175SDavid du Colombier 	"Good Packets Transmitted",
57847ad9175SDavid du Colombier 	nil,
57947ad9175SDavid du Colombier 	"Good Octets Received",
58047ad9175SDavid du Colombier 	nil,
58147ad9175SDavid du Colombier 	"Good Octets Transmitted",
58247ad9175SDavid du Colombier 	nil,
58347ad9175SDavid du Colombier 	nil,
58447ad9175SDavid du Colombier 	nil,
58547ad9175SDavid du Colombier 	"Receive No Buffers",
58647ad9175SDavid du Colombier 	"Receive Undersize",
58747ad9175SDavid du Colombier 	"Receive Fragment",
58847ad9175SDavid du Colombier 	"Receive Oversize",
58947ad9175SDavid du Colombier 	"Receive Jabber",
59037a6523bSDavid du Colombier 	"Management Packets Rx",
59137a6523bSDavid du Colombier 	"Management Packets Drop",
59237a6523bSDavid du Colombier 	"Management Packets Tx",
59347ad9175SDavid du Colombier 	"Total Octets Received",
59447ad9175SDavid du Colombier 	nil,
59547ad9175SDavid du Colombier 	"Total Octets Transmitted",
59647ad9175SDavid du Colombier 	nil,
59747ad9175SDavid du Colombier 	"Total Packets Received",
59847ad9175SDavid du Colombier 	"Total Packets Transmitted",
59947ad9175SDavid du Colombier 	"Packets Transmitted (64 Bytes)",
60047ad9175SDavid du Colombier 	"Packets Transmitted (65-127 Bytes)",
60147ad9175SDavid du Colombier 	"Packets Transmitted (128-255 Bytes)",
60247ad9175SDavid du Colombier 	"Packets Transmitted (256-511 Bytes)",
60347ad9175SDavid du Colombier 	"Packets Transmitted (512-1023 Bytes)",
60437a6523bSDavid du Colombier 	"Packets Transmitted (1024-mtu Bytes)",
60547ad9175SDavid du Colombier 	"Multicast Packets Transmitted",
60647ad9175SDavid du Colombier 	"Broadcast Packets Transmitted",
60747ad9175SDavid du Colombier 	"TCP Segmentation Context Transmitted",
60847ad9175SDavid du Colombier 	"TCP Segmentation Context Fail",
60937a6523bSDavid du Colombier 	"Interrupt Assertion",
61037a6523bSDavid du Colombier 	"Interrupt Rx Pkt Timer",
61137a6523bSDavid du Colombier 	"Interrupt Rx Abs Timer",
61237a6523bSDavid du Colombier 	"Interrupt Tx Pkt Timer",
61337a6523bSDavid du Colombier 	"Interrupt Tx Abs Timer",
61437a6523bSDavid du Colombier 	"Interrupt Tx Queue Empty",
61537a6523bSDavid du Colombier 	"Interrupt Tx Desc Low",
61637a6523bSDavid du Colombier 	"Interrupt Rx Min",
61737a6523bSDavid du Colombier 	"Interrupt Rx Overrun",
61847ad9175SDavid du Colombier };
61947ad9175SDavid du Colombier 
62047ad9175SDavid du Colombier static long
i82563ifstat(Ether * edev,void * a,long n,ulong offset)62147ad9175SDavid du Colombier i82563ifstat(Ether* edev, void* a, long n, ulong offset)
62247ad9175SDavid du Colombier {
62347ad9175SDavid du Colombier 	Ctlr *ctlr;
62437a6523bSDavid du Colombier 	char *s, *p, *e, *stat;
62537a6523bSDavid du Colombier 	int i, r;
62647ad9175SDavid du Colombier 	uvlong tuvl, ruvl;
62747ad9175SDavid du Colombier 
62847ad9175SDavid du Colombier 	ctlr = edev->ctlr;
62947ad9175SDavid du Colombier 	qlock(&ctlr->slock);
63046136019SDavid du Colombier 	p = s = malloc(READSTR);
631aa72973aSDavid du Colombier 	if(p == nil) {
632aa72973aSDavid du Colombier 		qunlock(&ctlr->slock);
633aa72973aSDavid du Colombier 		error(Enomem);
634aa72973aSDavid du Colombier 	}
63546136019SDavid du Colombier 	e = p + READSTR;
63637a6523bSDavid du Colombier 
63747ad9175SDavid du Colombier 	for(i = 0; i < Nstatistics; i++){
63847ad9175SDavid du Colombier 		r = csr32r(ctlr, Statistics + i*4);
63937a6523bSDavid du Colombier 		if((stat = statistics[i]) == nil)
64047ad9175SDavid du Colombier 			continue;
64147ad9175SDavid du Colombier 		switch(i){
64247ad9175SDavid du Colombier 		case Gorcl:
64347ad9175SDavid du Colombier 		case Gotcl:
64447ad9175SDavid du Colombier 		case Torl:
64547ad9175SDavid du Colombier 		case Totl:
64647ad9175SDavid du Colombier 			ruvl = r;
64737a6523bSDavid du Colombier 			ruvl += (uvlong)csr32r(ctlr, Statistics+(i+1)*4) << 32;
64847ad9175SDavid du Colombier 			tuvl = ruvl;
64947ad9175SDavid du Colombier 			tuvl += ctlr->statistics[i];
65037a6523bSDavid du Colombier 			tuvl += (uvlong)ctlr->statistics[i+1] << 32;
65147ad9175SDavid du Colombier 			if(tuvl == 0)
65247ad9175SDavid du Colombier 				continue;
65347ad9175SDavid du Colombier 			ctlr->statistics[i] = tuvl;
65447ad9175SDavid du Colombier 			ctlr->statistics[i+1] = tuvl >> 32;
65537a6523bSDavid du Colombier 			p = seprint(p, e, "%s: %llud %llud\n", stat, tuvl, ruvl);
65647ad9175SDavid du Colombier 			i++;
65747ad9175SDavid du Colombier 			break;
65847ad9175SDavid du Colombier 
65947ad9175SDavid du Colombier 		default:
66047ad9175SDavid du Colombier 			ctlr->statistics[i] += r;
66147ad9175SDavid du Colombier 			if(ctlr->statistics[i] == 0)
66247ad9175SDavid du Colombier 				continue;
66337a6523bSDavid du Colombier 			p = seprint(p, e, "%s: %ud %ud\n", stat,
66437a6523bSDavid du Colombier 				ctlr->statistics[i], r);
66547ad9175SDavid du Colombier 			break;
66647ad9175SDavid du Colombier 		}
66747ad9175SDavid du Colombier 	}
66847ad9175SDavid du Colombier 
66937a6523bSDavid du Colombier 	p = seprint(p, e, "lintr: %ud %ud\n", ctlr->lintr, ctlr->lsleep);
67037a6523bSDavid du Colombier 	p = seprint(p, e, "rintr: %ud %ud\n", ctlr->rintr, ctlr->rsleep);
67137a6523bSDavid du Colombier 	p = seprint(p, e, "tintr: %ud %ud\n", ctlr->tintr, ctlr->txdw);
67237a6523bSDavid du Colombier 	p = seprint(p, e, "ixcs: %ud %ud %ud\n", ctlr->ixsm, ctlr->ipcs, ctlr->tcpcs);
67337a6523bSDavid du Colombier 	p = seprint(p, e, "rdtr: %ud\n", ctlr->rdtr);
67437a6523bSDavid du Colombier 	p = seprint(p, e, "radv: %ud\n", ctlr->radv);
67537a6523bSDavid du Colombier 	p = seprint(p, e, "ctrl: %.8ux\n", csr32r(ctlr, Ctrl));
67637a6523bSDavid du Colombier 	p = seprint(p, e, "ctrlext: %.8ux\n", csr32r(ctlr, Ctrlext));
67737a6523bSDavid du Colombier 	p = seprint(p, e, "status: %.8ux\n", csr32r(ctlr, Status));
67837a6523bSDavid du Colombier 	p = seprint(p, e, "txcw: %.8ux\n", csr32r(ctlr, Txcw));
67937a6523bSDavid du Colombier 	p = seprint(p, e, "txdctl: %.8ux\n", csr32r(ctlr, Txdctl));
68037a6523bSDavid du Colombier 	p = seprint(p, e, "pba: %.8ux\n", ctlr->pba);
68147ad9175SDavid du Colombier 
68237a6523bSDavid du Colombier 	p = seprint(p, e, "speeds: 10:%ud 100:%ud 1000:%ud ?:%ud\n",
68337a6523bSDavid du Colombier 		ctlr->speeds[0], ctlr->speeds[1], ctlr->speeds[2], ctlr->speeds[3]);
68491b330d9SDavid du Colombier 	p = seprint(p, e, "type: %s\n", tname[ctlr->type]);
6856083aa43SDavid du Colombier 	p = seprint(p, e, "nrbfull (rcv blocks outstanding): %d\n", nrbfull);
68637a6523bSDavid du Colombier 
68737a6523bSDavid du Colombier //	p = seprint(p, e, "eeprom:");
68837a6523bSDavid du Colombier //	for(i = 0; i < 0x40; i++){
68937a6523bSDavid du Colombier //		if(i && ((i & 7) == 0))
69037a6523bSDavid du Colombier //			p = seprint(p, e, "\n       ");
69137a6523bSDavid du Colombier //		p = seprint(p, e, " %4.4ux", ctlr->eeprom[i]);
69237a6523bSDavid du Colombier //	}
69337a6523bSDavid du Colombier //	p = seprint(p, e, "\n");
69437a6523bSDavid du Colombier 
695217e9e83SDavid du Colombier 	p = seprintmark(p, e, &ctlr->wmrb);
696217e9e83SDavid du Colombier 	p = seprintmark(p, e, &ctlr->wmrd);
697217e9e83SDavid du Colombier 	p = seprintmark(p, e, &ctlr->wmtd);
698217e9e83SDavid du Colombier 
69937a6523bSDavid du Colombier 	USED(p);
70037a6523bSDavid du Colombier 	n = readstr(offset, a, n, s);
70137a6523bSDavid du Colombier 	free(s);
70247ad9175SDavid du Colombier 	qunlock(&ctlr->slock);
70347ad9175SDavid du Colombier 
70447ad9175SDavid du Colombier 	return n;
70547ad9175SDavid du Colombier }
70647ad9175SDavid du Colombier 
70747ad9175SDavid du Colombier enum {
70847ad9175SDavid du Colombier 	CMrdtr,
70947ad9175SDavid du Colombier 	CMradv,
71047ad9175SDavid du Colombier };
71147ad9175SDavid du Colombier 
71247ad9175SDavid du Colombier static Cmdtab i82563ctlmsg[] = {
71347ad9175SDavid du Colombier 	CMrdtr,	"rdtr",	2,
71447ad9175SDavid du Colombier 	CMradv,	"radv",	2,
71547ad9175SDavid du Colombier };
71647ad9175SDavid du Colombier 
71747ad9175SDavid du Colombier static long
i82563ctl(Ether * edev,void * buf,long n)71847ad9175SDavid du Colombier i82563ctl(Ether* edev, void* buf, long n)
71947ad9175SDavid du Colombier {
72031748cd5SDavid du Colombier 	ulong v;
72147ad9175SDavid du Colombier 	char *p;
72247ad9175SDavid du Colombier 	Ctlr *ctlr;
72347ad9175SDavid du Colombier 	Cmdbuf *cb;
72447ad9175SDavid du Colombier 	Cmdtab *ct;
72547ad9175SDavid du Colombier 
72647ad9175SDavid du Colombier 	if((ctlr = edev->ctlr) == nil)
72747ad9175SDavid du Colombier 		error(Enonexist);
72847ad9175SDavid du Colombier 
72947ad9175SDavid du Colombier 	cb = parsecmd(buf, n);
73047ad9175SDavid du Colombier 	if(waserror()){
73147ad9175SDavid du Colombier 		free(cb);
73247ad9175SDavid du Colombier 		nexterror();
73347ad9175SDavid du Colombier 	}
73447ad9175SDavid du Colombier 
73547ad9175SDavid du Colombier 	ct = lookupcmd(cb, i82563ctlmsg, nelem(i82563ctlmsg));
73647ad9175SDavid du Colombier 	switch(ct->index){
73747ad9175SDavid du Colombier 	case CMrdtr:
73831748cd5SDavid du Colombier 		v = strtoul(cb->f[1], &p, 0);
73931748cd5SDavid du Colombier 		if(p == cb->f[1] || v > 0xFFFF)
74047ad9175SDavid du Colombier 			error(Ebadarg);
74147ad9175SDavid du Colombier 		ctlr->rdtr = v;
74237a6523bSDavid du Colombier 		csr32w(ctlr, Rdtr, v);
74347ad9175SDavid du Colombier 		break;
74447ad9175SDavid du Colombier 	case CMradv:
74531748cd5SDavid du Colombier 		v = strtoul(cb->f[1], &p, 0);
74631748cd5SDavid du Colombier 		if(p == cb->f[1] || v > 0xFFFF)
74747ad9175SDavid du Colombier 			error(Ebadarg);
74847ad9175SDavid du Colombier 		ctlr->radv = v;
74947ad9175SDavid du Colombier 		csr32w(ctlr, Radv, v);
75047ad9175SDavid du Colombier 	}
75147ad9175SDavid du Colombier 	free(cb);
75247ad9175SDavid du Colombier 	poperror();
75347ad9175SDavid du Colombier 
75447ad9175SDavid du Colombier 	return n;
75547ad9175SDavid du Colombier }
75647ad9175SDavid du Colombier 
75747ad9175SDavid du Colombier static void
i82563promiscuous(void * arg,int on)75847ad9175SDavid du Colombier i82563promiscuous(void* arg, int on)
75947ad9175SDavid du Colombier {
76047ad9175SDavid du Colombier 	int rctl;
76147ad9175SDavid du Colombier 	Ctlr *ctlr;
76247ad9175SDavid du Colombier 	Ether *edev;
76347ad9175SDavid du Colombier 
76447ad9175SDavid du Colombier 	edev = arg;
76547ad9175SDavid du Colombier 	ctlr = edev->ctlr;
76647ad9175SDavid du Colombier 
76747ad9175SDavid du Colombier 	rctl = csr32r(ctlr, Rctl);
76847ad9175SDavid du Colombier 	rctl &= ~MoMASK;
76947ad9175SDavid du Colombier 	if(on)
77047ad9175SDavid du Colombier 		rctl |= Upe|Mpe;
77147ad9175SDavid du Colombier 	else
77247ad9175SDavid du Colombier 		rctl &= ~(Upe|Mpe);
77347ad9175SDavid du Colombier 	csr32w(ctlr, Rctl, rctl);
77447ad9175SDavid du Colombier }
77547ad9175SDavid du Colombier 
77647ad9175SDavid du Colombier static void
i82563multicast(void * arg,uchar * addr,int on)77747ad9175SDavid du Colombier i82563multicast(void* arg, uchar* addr, int on)
77847ad9175SDavid du Colombier {
77947ad9175SDavid du Colombier 	int bit, x;
78047ad9175SDavid du Colombier 	Ctlr *ctlr;
78147ad9175SDavid du Colombier 	Ether *edev;
78247ad9175SDavid du Colombier 
78347ad9175SDavid du Colombier 	edev = arg;
78447ad9175SDavid du Colombier 	ctlr = edev->ctlr;
78547ad9175SDavid du Colombier 
78647ad9175SDavid du Colombier 	x = addr[5]>>1;
787e4ac449cSDavid du Colombier 	if(ctlr->type == i82566 || ctlr->type == i82567)
78837a6523bSDavid du Colombier 		x &= 31;
78947ad9175SDavid du Colombier 	bit = ((addr[5] & 1)<<4)|(addr[4]>>4);
7903bf825d6SDavid du Colombier 	/*
7913bf825d6SDavid du Colombier 	 * multiple ether addresses can hash to the same filter bit,
7923bf825d6SDavid du Colombier 	 * so it's never safe to clear a filter bit.
7933bf825d6SDavid du Colombier 	 * if we want to clear filter bits, we need to keep track of
7943bf825d6SDavid du Colombier 	 * all the multicast addresses in use, clear all the filter bits,
7953bf825d6SDavid du Colombier 	 * then set the ones corresponding to in-use addresses.
7963bf825d6SDavid du Colombier 	 */
79747ad9175SDavid du Colombier 	if(on)
79847ad9175SDavid du Colombier 		ctlr->mta[x] |= 1<<bit;
7993bf825d6SDavid du Colombier //	else
8003bf825d6SDavid du Colombier //		ctlr->mta[x] &= ~(1<<bit);
80147ad9175SDavid du Colombier 
80247ad9175SDavid du Colombier 	csr32w(ctlr, Mta+x*4, ctlr->mta[x]);
80347ad9175SDavid du Colombier }
80447ad9175SDavid du Colombier 
80547ad9175SDavid du Colombier static Block*
i82563rballoc(void)80647ad9175SDavid du Colombier i82563rballoc(void)
80747ad9175SDavid du Colombier {
80847ad9175SDavid du Colombier 	Block *bp;
80947ad9175SDavid du Colombier 
81047ad9175SDavid du Colombier 	ilock(&i82563rblock);
81147ad9175SDavid du Colombier 	if((bp = i82563rbpool) != nil){
81247ad9175SDavid du Colombier 		i82563rbpool = bp->next;
81347ad9175SDavid du Colombier 		bp->next = nil;
81408cb4641SDavid du Colombier 		_xinc(&bp->ref);	/* prevent bp from being freed */
81547ad9175SDavid du Colombier 	}
81647ad9175SDavid du Colombier 	iunlock(&i82563rblock);
81747ad9175SDavid du Colombier 
81847ad9175SDavid du Colombier 	return bp;
81947ad9175SDavid du Colombier }
82047ad9175SDavid du Colombier 
82147ad9175SDavid du Colombier static void
i82563rbfree(Block * b)82237a6523bSDavid du Colombier i82563rbfree(Block* b)
82347ad9175SDavid du Colombier {
82437a6523bSDavid du Colombier 	b->rp = b->wp = (uchar*)PGROUND((uintptr)b->base);
825bfb6eab9SDavid du Colombier  	b->flag &= ~(Bipck | Budpck | Btcpck | Bpktck);
82647ad9175SDavid du Colombier 	ilock(&i82563rblock);
82737a6523bSDavid du Colombier 	b->next = i82563rbpool;
82837a6523bSDavid du Colombier 	i82563rbpool = b;
8296083aa43SDavid du Colombier 	nrbfull--;
83047ad9175SDavid du Colombier 	iunlock(&i82563rblock);
83147ad9175SDavid du Colombier }
83247ad9175SDavid du Colombier 
83347ad9175SDavid du Colombier static void
i82563im(Ctlr * ctlr,int im)83447ad9175SDavid du Colombier i82563im(Ctlr* ctlr, int im)
83547ad9175SDavid du Colombier {
83647ad9175SDavid du Colombier 	ilock(&ctlr->imlock);
83747ad9175SDavid du Colombier 	ctlr->im |= im;
83847ad9175SDavid du Colombier 	csr32w(ctlr, Ims, ctlr->im);
83947ad9175SDavid du Colombier 	iunlock(&ctlr->imlock);
84047ad9175SDavid du Colombier }
84147ad9175SDavid du Colombier 
84247ad9175SDavid du Colombier static void
i82563txinit(Ctlr * ctlr)84347ad9175SDavid du Colombier i82563txinit(Ctlr* ctlr)
84447ad9175SDavid du Colombier {
84547ad9175SDavid du Colombier 	int i, r;
84647ad9175SDavid du Colombier 	Block *bp;
84747ad9175SDavid du Colombier 
84837a6523bSDavid du Colombier 	csr32w(ctlr, Tctl, 0x0F<<CtSHIFT | Psp | 66<<ColdSHIFT | Mulr);
84937a6523bSDavid du Colombier 	csr32w(ctlr, Tipg, 6<<20 | 8<<10 | 8);		/* yb sez: 0x702008 */
85047ad9175SDavid du Colombier 	csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba));
85147ad9175SDavid du Colombier 	csr32w(ctlr, Tdbah, 0);
85247ad9175SDavid du Colombier 	csr32w(ctlr, Tdlen, ctlr->ntd * sizeof(Td));
85347ad9175SDavid du Colombier 	ctlr->tdh = PREV(0, ctlr->ntd);
85447ad9175SDavid du Colombier 	csr32w(ctlr, Tdh, 0);
85547ad9175SDavid du Colombier 	ctlr->tdt = 0;
85647ad9175SDavid du Colombier 	csr32w(ctlr, Tdt, 0);
85747ad9175SDavid du Colombier 	for(i = 0; i < ctlr->ntd; i++){
85847ad9175SDavid du Colombier 		if((bp = ctlr->tb[i]) != nil){
85947ad9175SDavid du Colombier 			ctlr->tb[i] = nil;
86047ad9175SDavid du Colombier 			freeb(bp);
86147ad9175SDavid du Colombier 		}
86247ad9175SDavid du Colombier 		memset(&ctlr->tdba[i], 0, sizeof(Td));
86347ad9175SDavid du Colombier 	}
86447ad9175SDavid du Colombier 	csr32w(ctlr, Tidv, 128);
86547ad9175SDavid du Colombier 	r = csr32r(ctlr, Txdctl);
866c39c2eb3SDavid du Colombier 	r &= ~(WthreshMASK|PthreshMASK);
86737a6523bSDavid du Colombier 	r |= 4<<WthreshSHIFT | 4<<PthreshSHIFT;
868c39c2eb3SDavid du Colombier 	if(ctlr->type == i82575 || ctlr->type == i82576)
86908cb4641SDavid du Colombier 		r |= Qenable;
87047ad9175SDavid du Colombier 	csr32w(ctlr, Tadv, 64);
87147ad9175SDavid du Colombier 	csr32w(ctlr, Txdctl, r);
87247ad9175SDavid du Colombier 	r = csr32r(ctlr, Tctl);
87347ad9175SDavid du Colombier 	r |= Ten;
87447ad9175SDavid du Colombier 	csr32w(ctlr, Tctl, r);
87537a6523bSDavid du Colombier //	if(ctlr->type == i82671)
87637a6523bSDavid du Colombier //		csr32w(ctlr, Tarc0, csr32r(ctlr, Tarc0) | 7<<24); /* yb sez? */
87737a6523bSDavid du Colombier }
87837a6523bSDavid du Colombier 
87937a6523bSDavid du Colombier #define Next(x, m)	(((x)+1) & (m))
88037a6523bSDavid du Colombier 
88137a6523bSDavid du Colombier static int
i82563cleanup(Ctlr * ctlr)882217e9e83SDavid du Colombier i82563cleanup(Ctlr *ctlr)
88337a6523bSDavid du Colombier {
88437a6523bSDavid du Colombier 	Block *b;
88537a6523bSDavid du Colombier 	int tdh, m, n;
88637a6523bSDavid du Colombier 
887217e9e83SDavid du Colombier 	tdh = ctlr->tdh;
888217e9e83SDavid du Colombier 	m = ctlr->ntd-1;
889217e9e83SDavid du Colombier 	while(ctlr->tdba[n = Next(tdh, m)].status & Tdd){
89037a6523bSDavid du Colombier 		tdh = n;
891217e9e83SDavid du Colombier 		if((b = ctlr->tb[tdh]) != nil){
892217e9e83SDavid du Colombier 			ctlr->tb[tdh] = nil;
89337a6523bSDavid du Colombier 			freeb(b);
89437a6523bSDavid du Colombier 		}else
89537a6523bSDavid du Colombier 			iprint("82563 tx underrun!\n");
896217e9e83SDavid du Colombier 		ctlr->tdba[tdh].status = 0;
89737a6523bSDavid du Colombier 	}
89837a6523bSDavid du Colombier 
899217e9e83SDavid du Colombier 	return ctlr->tdh = tdh;
90047ad9175SDavid du Colombier }
90147ad9175SDavid du Colombier 
90247ad9175SDavid du Colombier static void
i82563transmit(Ether * edev)90347ad9175SDavid du Colombier i82563transmit(Ether* edev)
90447ad9175SDavid du Colombier {
90547ad9175SDavid du Colombier 	Td *td;
90647ad9175SDavid du Colombier 	Block *bp;
90747ad9175SDavid du Colombier 	Ctlr *ctlr;
90837a6523bSDavid du Colombier 	int tdh, tdt, m;
90947ad9175SDavid du Colombier 
91047ad9175SDavid du Colombier 	ctlr = edev->ctlr;
91147ad9175SDavid du Colombier 
91237a6523bSDavid du Colombier 	qlock(&ctlr->tlock);
91347ad9175SDavid du Colombier 
91447ad9175SDavid du Colombier 	/*
91547ad9175SDavid du Colombier 	 * Free any completed packets
91647ad9175SDavid du Colombier 	 */
91737a6523bSDavid du Colombier 	tdh = i82563cleanup(ctlr);
91847ad9175SDavid du Colombier 
91947ad9175SDavid du Colombier 	/*
92047ad9175SDavid du Colombier 	 * Try to fill the ring back up.
92147ad9175SDavid du Colombier 	 */
92247ad9175SDavid du Colombier 	tdt = ctlr->tdt;
92337a6523bSDavid du Colombier 	m = ctlr->ntd-1;
92437a6523bSDavid du Colombier 	for(;;){
92537a6523bSDavid du Colombier 		if(Next(tdt, m) == tdh){
92647ad9175SDavid du Colombier 			ctlr->txdw++;
92747ad9175SDavid du Colombier 			i82563im(ctlr, Txdw);
92847ad9175SDavid du Colombier 			break;
92947ad9175SDavid du Colombier 		}
93037a6523bSDavid du Colombier 		if((bp = qget(edev->oq)) == nil)
93137a6523bSDavid du Colombier 			break;
93237a6523bSDavid du Colombier 		td = &ctlr->tdba[tdt];
93337a6523bSDavid du Colombier 		td->addr[0] = PCIWADDR(bp->rp);
93437a6523bSDavid du Colombier 		td->control = Ide|Rs|Ifcs|Teop|BLEN(bp);
93537a6523bSDavid du Colombier 		ctlr->tb[tdt] = bp;
936217e9e83SDavid du Colombier 		/* note size of queue of tds awaiting transmission */
937217e9e83SDavid du Colombier 		notemark(&ctlr->wmtd, (tdt + Ntd - tdh) % Ntd);
93837a6523bSDavid du Colombier 		tdt = Next(tdt, m);
93947ad9175SDavid du Colombier 	}
94037a6523bSDavid du Colombier 	if(ctlr->tdt != tdt){
94137a6523bSDavid du Colombier 		ctlr->tdt = tdt;
94247ad9175SDavid du Colombier 		csr32w(ctlr, Tdt, tdt);
94337a6523bSDavid du Colombier 	}
94437a6523bSDavid du Colombier 	qunlock(&ctlr->tlock);
94547ad9175SDavid du Colombier }
94647ad9175SDavid du Colombier 
94747ad9175SDavid du Colombier static void
i82563replenish(Ctlr * ctlr)94847ad9175SDavid du Colombier i82563replenish(Ctlr* ctlr)
94947ad9175SDavid du Colombier {
95047ad9175SDavid du Colombier 	Rd *rd;
95137a6523bSDavid du Colombier 	int rdt, m;
95247ad9175SDavid du Colombier 	Block *bp;
95347ad9175SDavid du Colombier 
95447ad9175SDavid du Colombier 	rdt = ctlr->rdt;
95537a6523bSDavid du Colombier 	m = ctlr->nrd-1;
95637a6523bSDavid du Colombier 	while(Next(rdt, m) != ctlr->rdh){
95747ad9175SDavid du Colombier 		rd = &ctlr->rdba[rdt];
95837a6523bSDavid du Colombier 		if(ctlr->rb[rdt] != nil){
9596083aa43SDavid du Colombier 			print("#l%d: 82563: rx overrun\n", ctlr->edev->ctlrno);
96037a6523bSDavid du Colombier 			break;
96137a6523bSDavid du Colombier 		}
96247ad9175SDavid du Colombier 		bp = i82563rballoc();
9636083aa43SDavid du Colombier 		if(bp == nil)
9646083aa43SDavid du Colombier 			/*
9656083aa43SDavid du Colombier 			 * this almost never gets better.  likely there's a bug
9666083aa43SDavid du Colombier 			 * elsewhere in the kernel that is failing to free a
9676083aa43SDavid du Colombier 			 * receive Block.
9686083aa43SDavid du Colombier 			 */
9696083aa43SDavid du Colombier 			panic("#l%d: 82563: all %d rx buffers in use, nrbfull %d",
9706083aa43SDavid du Colombier 				ctlr->edev->ctlrno, ctlr->nrb, nrbfull);
97147ad9175SDavid du Colombier 		ctlr->rb[rdt] = bp;
97247ad9175SDavid du Colombier 		rd->addr[0] = PCIWADDR(bp->rp);
97337a6523bSDavid du Colombier //		rd->addr[1] = 0;
97447ad9175SDavid du Colombier 		rd->status = 0;
97547ad9175SDavid du Colombier 		ctlr->rdfree++;
97637a6523bSDavid du Colombier 		rdt = Next(rdt, m);
97747ad9175SDavid du Colombier 	}
97847ad9175SDavid du Colombier 	ctlr->rdt = rdt;
97947ad9175SDavid du Colombier 	csr32w(ctlr, Rdt, rdt);
98047ad9175SDavid du Colombier }
98147ad9175SDavid du Colombier 
98247ad9175SDavid du Colombier static void
i82563rxinit(Ctlr * ctlr)98347ad9175SDavid du Colombier i82563rxinit(Ctlr* ctlr)
98447ad9175SDavid du Colombier {
98547ad9175SDavid du Colombier 	Block *bp;
98608cb4641SDavid du Colombier 	int i, r, rctl;
98747ad9175SDavid du Colombier 
98837a6523bSDavid du Colombier 	if(ctlr->rbsz <= 2048)
98908cb4641SDavid du Colombier 		rctl = Dpf|Bsize2048|Bam|RdtmsHALF;
99037a6523bSDavid du Colombier 	else if(ctlr->rbsz <= 8192)
99108cb4641SDavid du Colombier 		rctl = Lpe|Dpf|Bsize8192|Bsex|Bam|RdtmsHALF|Secrc;
99237a6523bSDavid du Colombier 	else if(ctlr->rbsz <= 12*1024){
99337a6523bSDavid du Colombier 		i = ctlr->rbsz / 1024;
99437a6523bSDavid du Colombier 		if(ctlr->rbsz % 1024)
99537a6523bSDavid du Colombier 			i++;
99608cb4641SDavid du Colombier 		rctl = Lpe|Dpf|BsizeFlex*i|Bam|RdtmsHALF|Secrc;
99708cb4641SDavid du Colombier 	}
99808cb4641SDavid du Colombier 	else
99908cb4641SDavid du Colombier 		rctl = Lpe|Dpf|Bsize16384|Bsex|Bam|RdtmsHALF|Secrc;
100008cb4641SDavid du Colombier 
1001c39c2eb3SDavid du Colombier 	if(ctlr->type == i82575 || ctlr->type == i82576){
100208cb4641SDavid du Colombier 		/*
100308cb4641SDavid du Colombier 		 * Setting Qenable in Rxdctl does not
100408cb4641SDavid du Colombier 		 * appear to stick unless Ren is on.
100508cb4641SDavid du Colombier 		 */
100608cb4641SDavid du Colombier 		csr32w(ctlr, Rctl, Ren|rctl);
100708cb4641SDavid du Colombier 		r = csr32r(ctlr, Rxdctl);
100808cb4641SDavid du Colombier 		r |= Qenable;
100908cb4641SDavid du Colombier 		csr32w(ctlr, Rxdctl, r);
101008cb4641SDavid du Colombier 	}
101108cb4641SDavid du Colombier 	csr32w(ctlr, Rctl, rctl);
101237a6523bSDavid du Colombier 
10139b7bf7dfSDavid du Colombier 	if(ctlr->type == i82573 || ctlr->type == i82577 || ctlr->type == i82579)
101437a6523bSDavid du Colombier 		csr32w(ctlr, Ert, 1024/8);
10150343ea0dSDavid du Colombier 
1016e4ac449cSDavid du Colombier 	if(ctlr->type == i82566 || ctlr->type == i82567)
101737a6523bSDavid du Colombier 		csr32w(ctlr, Pbs, 16);
101847ad9175SDavid du Colombier 
101947ad9175SDavid du Colombier 	csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba));
102047ad9175SDavid du Colombier 	csr32w(ctlr, Rdbah, 0);
102147ad9175SDavid du Colombier 	csr32w(ctlr, Rdlen, ctlr->nrd * sizeof(Rd));
102247ad9175SDavid du Colombier 	ctlr->rdh = 0;
102347ad9175SDavid du Colombier 	csr32w(ctlr, Rdh, 0);
102447ad9175SDavid du Colombier 	ctlr->rdt = 0;
102547ad9175SDavid du Colombier 	csr32w(ctlr, Rdt, 0);
10266f746b77SDavid du Colombier 	/* to hell with interrupt moderation, we've got fast cpus */
10276f746b77SDavid du Colombier //	ctlr->rdtr = 25;		/* µs units? */
10286f746b77SDavid du Colombier //	ctlr->radv = 500;		/* µs units? */
102991157df7SDavid du Colombier 	ctlr->radv = ctlr->rdtr = 0;
103037a6523bSDavid du Colombier 	csr32w(ctlr, Rdtr, ctlr->rdtr);
103137a6523bSDavid du Colombier 	csr32w(ctlr, Radv, ctlr->radv);
103247ad9175SDavid du Colombier 
103308cb4641SDavid du Colombier 	for(i = 0; i < ctlr->nrd; i++){
103447ad9175SDavid du Colombier 		if((bp = ctlr->rb[i]) != nil){
103547ad9175SDavid du Colombier 			ctlr->rb[i] = nil;
103647ad9175SDavid du Colombier 			freeb(bp);
103747ad9175SDavid du Colombier 		}
103808cb4641SDavid du Colombier 	}
103947ad9175SDavid du Colombier 	i82563replenish(ctlr);
104008cb4641SDavid du Colombier 
1041c39c2eb3SDavid du Colombier 	if(ctlr->type != i82575 || ctlr->type == i82576){
104208cb4641SDavid du Colombier 		/*
104308cb4641SDavid du Colombier 		 * See comment above for Qenable.
104408cb4641SDavid du Colombier 		 * Could shuffle the code?
104508cb4641SDavid du Colombier 		 */
104608cb4641SDavid du Colombier 		r = csr32r(ctlr, Rxdctl);
1047c39c2eb3SDavid du Colombier 		r &= ~(WthreshMASK|PthreshMASK);
104808cb4641SDavid du Colombier 		r |= (2<<WthreshSHIFT)|(2<<PthreshSHIFT);
104908cb4641SDavid du Colombier 		csr32w(ctlr, Rxdctl, r);
105008cb4641SDavid du Colombier 	}
105147ad9175SDavid du Colombier 
105247ad9175SDavid du Colombier 	/*
10536f746b77SDavid du Colombier 	 * Don't enable checksum offload.  In practice, it interferes with
10546f746b77SDavid du Colombier 	 * tftp booting on at least the 82575.
105547ad9175SDavid du Colombier 	 */
10566f746b77SDavid du Colombier //	csr32w(ctlr, Rxcsum, Tuofl | Ipofl | ETHERHDRSIZE<<PcssSHIFT);
10576f746b77SDavid du Colombier 	csr32w(ctlr, Rxcsum, 0);
105847ad9175SDavid du Colombier }
105947ad9175SDavid du Colombier 
106047ad9175SDavid du Colombier static int
i82563rim(void * ctlr)106147ad9175SDavid du Colombier i82563rim(void* ctlr)
106247ad9175SDavid du Colombier {
106347ad9175SDavid du Colombier 	return ((Ctlr*)ctlr)->rim != 0;
106447ad9175SDavid du Colombier }
106547ad9175SDavid du Colombier 
106647ad9175SDavid du Colombier static void
i82563rproc(void * arg)106747ad9175SDavid du Colombier i82563rproc(void* arg)
106847ad9175SDavid du Colombier {
106947ad9175SDavid du Colombier 	Rd *rd;
107047ad9175SDavid du Colombier 	Block *bp;
107147ad9175SDavid du Colombier 	Ctlr *ctlr;
1072217e9e83SDavid du Colombier 	int r, m, rdh, rim, passed;
107347ad9175SDavid du Colombier 	Ether *edev;
107447ad9175SDavid du Colombier 
107547ad9175SDavid du Colombier 	edev = arg;
107647ad9175SDavid du Colombier 	ctlr = edev->ctlr;
107747ad9175SDavid du Colombier 
107847ad9175SDavid du Colombier 	i82563rxinit(ctlr);
107947ad9175SDavid du Colombier 	r = csr32r(ctlr, Rctl);
108047ad9175SDavid du Colombier 	r |= Ren;
108147ad9175SDavid du Colombier 	csr32w(ctlr, Rctl, r);
108237a6523bSDavid du Colombier 	m = ctlr->nrd-1;
108347ad9175SDavid du Colombier 
108447ad9175SDavid du Colombier 	for(;;){
10856083aa43SDavid du Colombier 		i82563replenish(ctlr);
108637a6523bSDavid du Colombier 		i82563im(ctlr, Rxt0|Rxo|Rxdmt0|Rxseq|Ack);
108747ad9175SDavid du Colombier 		ctlr->rsleep++;
108847ad9175SDavid du Colombier 		sleep(&ctlr->rrendez, i82563rim, ctlr);
108947ad9175SDavid du Colombier 
109047ad9175SDavid du Colombier 		rdh = ctlr->rdh;
1091217e9e83SDavid du Colombier 		passed = 0;
109247ad9175SDavid du Colombier 		for(;;){
109347ad9175SDavid du Colombier 			rim = ctlr->rim;
109447ad9175SDavid du Colombier 			ctlr->rim = 0;
10956083aa43SDavid du Colombier 			rd = &ctlr->rdba[rdh];
109647ad9175SDavid du Colombier 			if(!(rd->status & Rdd))
109747ad9175SDavid du Colombier 				break;
109847ad9175SDavid du Colombier 
109947ad9175SDavid du Colombier 			/*
110047ad9175SDavid du Colombier 			 * Accept eop packets with no errors.
110147ad9175SDavid du Colombier 			 * With no errors and the Ixsm bit set,
110247ad9175SDavid du Colombier 			 * the descriptor status Tpcs and Ipcs bits give
110347ad9175SDavid du Colombier 			 * an indication of whether the checksums were
110447ad9175SDavid du Colombier 			 * calculated and valid.
110547ad9175SDavid du Colombier 			 */
110631748cd5SDavid du Colombier 			bp = ctlr->rb[rdh];
110747ad9175SDavid du Colombier 			if((rd->status & Reop) && rd->errors == 0){
110847ad9175SDavid du Colombier 				bp->wp += rd->length;
11090343ea0dSDavid du Colombier 				bp->lim = bp->wp;	/* lie like a dog. */
111047ad9175SDavid du Colombier 				if(!(rd->status & Ixsm)){
111147ad9175SDavid du Colombier 					ctlr->ixsm++;
111247ad9175SDavid du Colombier 					if(rd->status & Ipcs){
111347ad9175SDavid du Colombier 						/*
111447ad9175SDavid du Colombier 						 * IP checksum calculated
111547ad9175SDavid du Colombier 						 * (and valid as errors == 0).
111647ad9175SDavid du Colombier 						 */
111747ad9175SDavid du Colombier 						ctlr->ipcs++;
111847ad9175SDavid du Colombier 						bp->flag |= Bipck;
111947ad9175SDavid du Colombier 					}
112047ad9175SDavid du Colombier 					if(rd->status & Tcpcs){
112147ad9175SDavid du Colombier 						/*
112247ad9175SDavid du Colombier 						 * TCP/UDP checksum calculated
112347ad9175SDavid du Colombier 						 * (and valid as errors == 0).
112447ad9175SDavid du Colombier 						 */
112547ad9175SDavid du Colombier 						ctlr->tcpcs++;
112647ad9175SDavid du Colombier 						bp->flag |= Btcpck|Budpck;
112747ad9175SDavid du Colombier 					}
112847ad9175SDavid du Colombier 					bp->checksum = rd->checksum;
112947ad9175SDavid du Colombier 					bp->flag |= Bpktck;
113047ad9175SDavid du Colombier 				}
11316083aa43SDavid du Colombier 				ilock(&i82563rblock);
11326083aa43SDavid du Colombier 				nrbfull++;
11336083aa43SDavid du Colombier 				iunlock(&i82563rblock);
1134217e9e83SDavid du Colombier 				notemark(&ctlr->wmrb, nrbfull);
113547ad9175SDavid du Colombier 				etheriq(edev, bp, 1);
1136217e9e83SDavid du Colombier 				passed++;
113759f7772cSDavid du Colombier 			} else {
113859f7772cSDavid du Colombier 				if (rd->status & Reop && rd->errors)
11396f746b77SDavid du Colombier 					print("%s: input packet error %#ux\n",
11406f746b77SDavid du Colombier 						tname[ctlr->type], rd->errors);
114147ad9175SDavid du Colombier 				freeb(bp);
114259f7772cSDavid du Colombier 			}
114347ad9175SDavid du Colombier 			ctlr->rb[rdh] = nil;
114431748cd5SDavid du Colombier 
1145c02f0a41SDavid du Colombier 			/* rd needs to be replenished to accept another pkt */
114637a6523bSDavid du Colombier 			rd->status = 0;
114747ad9175SDavid du Colombier 			ctlr->rdfree--;
114837a6523bSDavid du Colombier 			ctlr->rdh = rdh = Next(rdh, m);
1149c02f0a41SDavid du Colombier 			/*
1150c02f0a41SDavid du Colombier 			 * if number of rds ready for packets is too low,
1151c02f0a41SDavid du Colombier 			 * set up the unready ones.
1152c02f0a41SDavid du Colombier 			 */
1153c02f0a41SDavid du Colombier 			if(ctlr->rdfree <= ctlr->nrd - 32 || (rim & Rxdmt0))
115447ad9175SDavid du Colombier 				i82563replenish(ctlr);
115547ad9175SDavid du Colombier 		}
1156217e9e83SDavid du Colombier 		/* note how many rds had full buffers */
1157217e9e83SDavid du Colombier 		notemark(&ctlr->wmrd, passed);
115847ad9175SDavid du Colombier 	}
115947ad9175SDavid du Colombier }
116047ad9175SDavid du Colombier 
116137a6523bSDavid du Colombier static int
i82563lim(void * ctlr)1162217e9e83SDavid du Colombier i82563lim(void* ctlr)
116337a6523bSDavid du Colombier {
1164217e9e83SDavid du Colombier 	return ((Ctlr*)ctlr)->lim != 0;
116537a6523bSDavid du Colombier }
116637a6523bSDavid du Colombier 
116737a6523bSDavid du Colombier static int speedtab[] = {
116837a6523bSDavid du Colombier 	10, 100, 1000, 0
116937a6523bSDavid du Colombier };
117037a6523bSDavid du Colombier 
117137a6523bSDavid du Colombier static uint
phyread(Ctlr * ctlr,int reg)1172217e9e83SDavid du Colombier phyread(Ctlr *ctlr, int reg)
117337a6523bSDavid du Colombier {
117437a6523bSDavid du Colombier 	uint phy, i;
117537a6523bSDavid du Colombier 
1176217e9e83SDavid du Colombier 	csr32w(ctlr, Mdic, MDIrop | 1<<MDIpSHIFT | reg<<MDIrSHIFT);
117737a6523bSDavid du Colombier 	phy = 0;
117837a6523bSDavid du Colombier 	for(i = 0; i < 64; i++){
1179217e9e83SDavid du Colombier 		phy = csr32r(ctlr, Mdic);
118037a6523bSDavid du Colombier 		if(phy & (MDIe|MDIready))
118137a6523bSDavid du Colombier 			break;
118237a6523bSDavid du Colombier 		microdelay(1);
118337a6523bSDavid du Colombier 	}
118437a6523bSDavid du Colombier 	if((phy & (MDIe|MDIready)) != MDIready)
118537a6523bSDavid du Colombier 		return ~0;
11860343ea0dSDavid du Colombier 	return phy & 0xffff;
118737a6523bSDavid du Colombier }
118837a6523bSDavid du Colombier 
118937a6523bSDavid du Colombier static uint
phywrite(Ctlr * ctlr,int reg,ushort val)1190217e9e83SDavid du Colombier phywrite(Ctlr *ctlr, int reg, ushort val)
119137a6523bSDavid du Colombier {
119237a6523bSDavid du Colombier 	uint phy, i;
119337a6523bSDavid du Colombier 
1194217e9e83SDavid du Colombier 	csr32w(ctlr, Mdic, MDIwop | 1<<MDIpSHIFT | reg<<MDIrSHIFT | val);
119537a6523bSDavid du Colombier 	phy = 0;
119637a6523bSDavid du Colombier 	for(i = 0; i < 64; i++){
1197217e9e83SDavid du Colombier 		phy = csr32r(ctlr, Mdic);
119837a6523bSDavid du Colombier 		if(phy & (MDIe|MDIready))
119937a6523bSDavid du Colombier 			break;
120037a6523bSDavid du Colombier 		microdelay(1);
120137a6523bSDavid du Colombier 	}
120237a6523bSDavid du Colombier 	if((phy & (MDIe|MDIready)) != MDIready)
120337a6523bSDavid du Colombier 		return ~0;
120437a6523bSDavid du Colombier 	return 0;
120537a6523bSDavid du Colombier }
120637a6523bSDavid du Colombier 
120731748cd5SDavid du Colombier /*
120831748cd5SDavid du Colombier  * watch for changes of link state
120931748cd5SDavid du Colombier  */
121037a6523bSDavid du Colombier static void
i82563lproc(void * v)121137a6523bSDavid du Colombier i82563lproc(void *v)
121237a6523bSDavid du Colombier {
121331748cd5SDavid du Colombier 	uint phy, i, a;
1214217e9e83SDavid du Colombier 	Ctlr *ctlr;
121531748cd5SDavid du Colombier 	Ether *e;
121637a6523bSDavid du Colombier 
121737a6523bSDavid du Colombier 	e = v;
1218217e9e83SDavid du Colombier 	ctlr = e->ctlr;
121937a6523bSDavid du Colombier 
1220217e9e83SDavid du Colombier 	if(ctlr->type == i82573 && (phy = phyread(ctlr, Phyier)) != ~0)
1221217e9e83SDavid du Colombier 		phywrite(ctlr, Phyier, phy | Lscie | Ancie | Spdie | Panie);
122237a6523bSDavid du Colombier 	for(;;){
1223217e9e83SDavid du Colombier 		phy = phyread(ctlr, Physsr);
122437a6523bSDavid du Colombier 		if(phy == ~0)
122537a6523bSDavid du Colombier 			goto next;
122637a6523bSDavid du Colombier 		i = (phy>>14) & 3;
122737a6523bSDavid du Colombier 
1228217e9e83SDavid du Colombier 		switch(ctlr->type){
122937a6523bSDavid du Colombier 		case i82563:
1230217e9e83SDavid du Colombier 			a = phyread(ctlr, Phyisr) & Ane;
123137a6523bSDavid du Colombier 			break;
123237a6523bSDavid du Colombier 		case i82571:
123337a6523bSDavid du Colombier 		case i82572:
1234d8cd2beaSDavid du Colombier 		case i82575:
1235d8cd2beaSDavid du Colombier 		case i82576:
1236217e9e83SDavid du Colombier 			a = phyread(ctlr, Phylhr) & Anf;
123737a6523bSDavid du Colombier 			i = (i-1) & 3;
123837a6523bSDavid du Colombier 			break;
123931748cd5SDavid du Colombier 		default:
124031748cd5SDavid du Colombier 			a = 0;
124131748cd5SDavid du Colombier 			break;
124237a6523bSDavid du Colombier 		}
124331748cd5SDavid du Colombier 		if(a)
1244217e9e83SDavid du Colombier 			phywrite(ctlr, Phyctl, phyread(ctlr, Phyctl) | Ran | Ean);
124531748cd5SDavid du Colombier 		e->link = (phy & Rtlink) != 0;
124631748cd5SDavid du Colombier 		if(e->link){
1247217e9e83SDavid du Colombier 			ctlr->speeds[i]++;
12486f746b77SDavid du Colombier 			if (speedtab[i])
124937a6523bSDavid du Colombier 				e->mbps = speedtab[i];
125031748cd5SDavid du Colombier 		}
125137a6523bSDavid du Colombier next:
1252217e9e83SDavid du Colombier 		ctlr->lim = 0;
1253217e9e83SDavid du Colombier 		i82563im(ctlr, Lsc);
1254217e9e83SDavid du Colombier 		ctlr->lsleep++;
1255217e9e83SDavid du Colombier 		sleep(&ctlr->lrendez, i82563lim, ctlr);
125637a6523bSDavid du Colombier 	}
125737a6523bSDavid du Colombier }
125837a6523bSDavid du Colombier 
125937a6523bSDavid du Colombier static void
i82563tproc(void * v)126037a6523bSDavid du Colombier i82563tproc(void *v)
126137a6523bSDavid du Colombier {
126237a6523bSDavid du Colombier 	Ether *e;
1263217e9e83SDavid du Colombier 	Ctlr *ctlr;
126437a6523bSDavid du Colombier 
126537a6523bSDavid du Colombier 	e = v;
1266217e9e83SDavid du Colombier 	ctlr = e->ctlr;
126737a6523bSDavid du Colombier 	for(;;){
1268217e9e83SDavid du Colombier 		sleep(&ctlr->trendez, return0, 0);
126937a6523bSDavid du Colombier 		i82563transmit(e);
127037a6523bSDavid du Colombier 	}
127137a6523bSDavid du Colombier }
127237a6523bSDavid du Colombier 
127347ad9175SDavid du Colombier static void
i82563attach(Ether * edev)127447ad9175SDavid du Colombier i82563attach(Ether* edev)
127547ad9175SDavid du Colombier {
127637a6523bSDavid du Colombier 	Block *bp;
127747ad9175SDavid du Colombier 	Ctlr *ctlr;
127847ad9175SDavid du Colombier 	char name[KNAMELEN];
127947ad9175SDavid du Colombier 
128047ad9175SDavid du Colombier 	ctlr = edev->ctlr;
128147ad9175SDavid du Colombier 	qlock(&ctlr->alock);
128208cb4641SDavid du Colombier 	if(ctlr->attached){
128347ad9175SDavid du Colombier 		qunlock(&ctlr->alock);
128447ad9175SDavid du Colombier 		return;
128547ad9175SDavid du Colombier 	}
128647ad9175SDavid du Colombier 
128737a6523bSDavid du Colombier 	ctlr->nrd = Nrd;
128837a6523bSDavid du Colombier 	ctlr->ntd = Ntd;
128947ad9175SDavid du Colombier 
129047ad9175SDavid du Colombier 	if(waserror()){
129191157df7SDavid du Colombier 		while(ctlr->nrb > 0){
129247ad9175SDavid du Colombier 			bp = i82563rballoc();
129347ad9175SDavid du Colombier 			bp->free = nil;
129447ad9175SDavid du Colombier 			freeb(bp);
129591157df7SDavid du Colombier 			ctlr->nrb--;
129647ad9175SDavid du Colombier 		}
129747ad9175SDavid du Colombier 		free(ctlr->tb);
1298bfb6eab9SDavid du Colombier 		ctlr->tb = nil;
129991157df7SDavid du Colombier 		free(ctlr->rb);
130047ad9175SDavid du Colombier 		ctlr->rb = nil;
130108cb4641SDavid du Colombier 		free(ctlr->tdba);
1302bfb6eab9SDavid du Colombier 		ctlr->tdba = nil;
130391157df7SDavid du Colombier 		free(ctlr->rdba);
130408cb4641SDavid du Colombier 		ctlr->rdba = nil;
130547ad9175SDavid du Colombier 		qunlock(&ctlr->alock);
130647ad9175SDavid du Colombier 		nexterror();
130747ad9175SDavid du Colombier 	}
130847ad9175SDavid du Colombier 
1309bfb6eab9SDavid du Colombier 	if((ctlr->rdba = mallocalign(ctlr->nrd*sizeof(Rd), 128, 0, 0)) == nil ||
1310bfb6eab9SDavid du Colombier 	   (ctlr->tdba = mallocalign(ctlr->ntd*sizeof(Td), 128, 0, 0)) == nil ||
1311bfb6eab9SDavid du Colombier 	   (ctlr->rb = malloc(ctlr->nrd*sizeof(Block*))) == nil ||
1312bfb6eab9SDavid du Colombier 	   (ctlr->tb = malloc(ctlr->ntd*sizeof(Block*))) == nil)
131308cb4641SDavid du Colombier 		error(Enomem);
131408cb4641SDavid du Colombier 
131537a6523bSDavid du Colombier 	for(ctlr->nrb = 0; ctlr->nrb < Nrb; ctlr->nrb++){
131637a6523bSDavid du Colombier 		if((bp = allocb(ctlr->rbsz + BY2PG)) == nil)
13176fbfa2f3SDavid du Colombier 			error(Enomem);
131847ad9175SDavid du Colombier 		bp->free = i82563rbfree;
131947ad9175SDavid du Colombier 		freeb(bp);
132047ad9175SDavid du Colombier 	}
13216083aa43SDavid du Colombier 	nrbfull = 0;
132237a6523bSDavid du Colombier 
132330ce352cSDavid du Colombier 	ctlr->edev = edev;			/* point back to Ether* */
132408cb4641SDavid du Colombier 	ctlr->attached = 1;
1325217e9e83SDavid du Colombier 	initmark(&ctlr->wmrb, Nrb, "rcv bufs unprocessed");
1326217e9e83SDavid du Colombier 	initmark(&ctlr->wmrd, Nrd-1, "rcv descrs processed at once");
1327217e9e83SDavid du Colombier 	initmark(&ctlr->wmtd, Ntd-1, "xmit descr queue len");
132808cb4641SDavid du Colombier 
13290343ea0dSDavid du Colombier 	snprint(name, sizeof name, "#l%dl", edev->ctlrno);
133037a6523bSDavid du Colombier 	kproc(name, i82563lproc, edev);
133147ad9175SDavid du Colombier 
13320343ea0dSDavid du Colombier 	snprint(name, sizeof name, "#l%dr", edev->ctlrno);
133347ad9175SDavid du Colombier 	kproc(name, i82563rproc, edev);
133447ad9175SDavid du Colombier 
13350343ea0dSDavid du Colombier 	snprint(name, sizeof name, "#l%dt", edev->ctlrno);
133637a6523bSDavid du Colombier 	kproc(name, i82563tproc, edev);
133737a6523bSDavid du Colombier 
133847ad9175SDavid du Colombier 	i82563txinit(ctlr);
133947ad9175SDavid du Colombier 
134047ad9175SDavid du Colombier 	qunlock(&ctlr->alock);
134147ad9175SDavid du Colombier 	poperror();
134247ad9175SDavid du Colombier }
134347ad9175SDavid du Colombier 
134447ad9175SDavid du Colombier static void
i82563interrupt(Ureg *,void * arg)134547ad9175SDavid du Colombier i82563interrupt(Ureg*, void* arg)
134647ad9175SDavid du Colombier {
134747ad9175SDavid du Colombier 	Ctlr *ctlr;
134847ad9175SDavid du Colombier 	Ether *edev;
13496fbfa2f3SDavid du Colombier 	int icr, im, i;
135047ad9175SDavid du Colombier 
135147ad9175SDavid du Colombier 	edev = arg;
135247ad9175SDavid du Colombier 	ctlr = edev->ctlr;
135347ad9175SDavid du Colombier 
135447ad9175SDavid du Colombier 	ilock(&ctlr->imlock);
135547ad9175SDavid du Colombier 	csr32w(ctlr, Imc, ~0);
135647ad9175SDavid du Colombier 	im = ctlr->im;
13576083aa43SDavid du Colombier 	i = Nrd;			/* don't livelock */
13586fbfa2f3SDavid du Colombier 	for(icr = csr32r(ctlr, Icr); icr & ctlr->im && i-- > 0;
13596fbfa2f3SDavid du Colombier 	    icr = csr32r(ctlr, Icr)){
136047ad9175SDavid du Colombier 		if(icr & Lsc){
136147ad9175SDavid du Colombier 			im &= ~Lsc;
136247ad9175SDavid du Colombier 			ctlr->lim = icr & Lsc;
136347ad9175SDavid du Colombier 			wakeup(&ctlr->lrendez);
136447ad9175SDavid du Colombier 			ctlr->lintr++;
136547ad9175SDavid du Colombier 		}
136637a6523bSDavid du Colombier 		if(icr & (Rxt0|Rxo|Rxdmt0|Rxseq|Ack)){
136737a6523bSDavid du Colombier 			ctlr->rim = icr & (Rxt0|Rxo|Rxdmt0|Rxseq|Ack);
136837a6523bSDavid du Colombier 			im &= ~(Rxt0|Rxo|Rxdmt0|Rxseq|Ack);
136947ad9175SDavid du Colombier 			wakeup(&ctlr->rrendez);
137047ad9175SDavid du Colombier 			ctlr->rintr++;
137147ad9175SDavid du Colombier 		}
137247ad9175SDavid du Colombier 		if(icr & Txdw){
137347ad9175SDavid du Colombier 			im &= ~Txdw;
137447ad9175SDavid du Colombier 			ctlr->tintr++;
137537a6523bSDavid du Colombier 			wakeup(&ctlr->trendez);
137647ad9175SDavid du Colombier 		}
137747ad9175SDavid du Colombier 	}
137847ad9175SDavid du Colombier 	ctlr->im = im;
137947ad9175SDavid du Colombier 	csr32w(ctlr, Ims, im);
138047ad9175SDavid du Colombier 	iunlock(&ctlr->imlock);
138147ad9175SDavid du Colombier }
138247ad9175SDavid du Colombier 
13836f746b77SDavid du Colombier /* assume misrouted interrupts and check all controllers */
13846f746b77SDavid du Colombier static void
i82575interrupt(Ureg *,void *)13856f746b77SDavid du Colombier i82575interrupt(Ureg*, void *)
13866f746b77SDavid du Colombier {
13876f746b77SDavid du Colombier 	Ctlr *ctlr;
13886f746b77SDavid du Colombier 
138930ce352cSDavid du Colombier 	for (ctlr = i82563ctlrhead; ctlr != nil && ctlr->edev != nil;
139030ce352cSDavid du Colombier 	     ctlr = ctlr->next)
13916f746b77SDavid du Colombier 		i82563interrupt(nil, ctlr->edev);
13926f746b77SDavid du Colombier }
13936f746b77SDavid du Colombier 
139447ad9175SDavid du Colombier static int
i82563detach0(Ctlr * ctlr)13958ccc32efSDavid du Colombier i82563detach0(Ctlr* ctlr)
139647ad9175SDavid du Colombier {
139747ad9175SDavid du Colombier 	int r, timeo;
139847ad9175SDavid du Colombier 
139947ad9175SDavid du Colombier 	/*
140047ad9175SDavid du Colombier 	 * Perform a device reset to get the chip back to the
140147ad9175SDavid du Colombier 	 * power-on state, followed by an EEPROM reset to read
140247ad9175SDavid du Colombier 	 * the defaults for some internal registers.
140347ad9175SDavid du Colombier 	 */
140447ad9175SDavid du Colombier 	csr32w(ctlr, Imc, ~0);
140547ad9175SDavid du Colombier 	csr32w(ctlr, Rctl, 0);
140647ad9175SDavid du Colombier 	csr32w(ctlr, Tctl, 0);
140747ad9175SDavid du Colombier 
140847ad9175SDavid du Colombier 	delay(10);
140947ad9175SDavid du Colombier 
141037a6523bSDavid du Colombier 	r = csr32r(ctlr, Ctrl);
14119b7bf7dfSDavid du Colombier 	if(ctlr->type == i82566 || ctlr->type == i82567 || ctlr->type == i82579)
141237a6523bSDavid du Colombier 		r |= Phyrst;
141337a6523bSDavid du Colombier 	csr32w(ctlr, Ctrl, Devrst | r);
141447ad9175SDavid du Colombier 	delay(1);
141547ad9175SDavid du Colombier 	for(timeo = 0; timeo < 1000; timeo++){
141647ad9175SDavid du Colombier 		if(!(csr32r(ctlr, Ctrl) & Devrst))
141747ad9175SDavid du Colombier 			break;
141847ad9175SDavid du Colombier 		delay(1);
141947ad9175SDavid du Colombier 	}
142047ad9175SDavid du Colombier 	if(csr32r(ctlr, Ctrl) & Devrst)
142147ad9175SDavid du Colombier 		return -1;
142237a6523bSDavid du Colombier 
142347ad9175SDavid du Colombier 	r = csr32r(ctlr, Ctrlext);
142447ad9175SDavid du Colombier 	csr32w(ctlr, Ctrlext, r|Eerst);
142547ad9175SDavid du Colombier 	delay(1);
142647ad9175SDavid du Colombier 	for(timeo = 0; timeo < 1000; timeo++){
142747ad9175SDavid du Colombier 		if(!(csr32r(ctlr, Ctrlext) & Eerst))
142847ad9175SDavid du Colombier 			break;
142947ad9175SDavid du Colombier 		delay(1);
143047ad9175SDavid du Colombier 	}
143147ad9175SDavid du Colombier 	if(csr32r(ctlr, Ctrlext) & Eerst)
143247ad9175SDavid du Colombier 		return -1;
143347ad9175SDavid du Colombier 
143447ad9175SDavid du Colombier 	csr32w(ctlr, Imc, ~0);
143547ad9175SDavid du Colombier 	delay(1);
143647ad9175SDavid du Colombier 	for(timeo = 0; timeo < 1000; timeo++){
143747ad9175SDavid du Colombier 		if(!csr32r(ctlr, Icr))
143847ad9175SDavid du Colombier 			break;
143947ad9175SDavid du Colombier 		delay(1);
144047ad9175SDavid du Colombier 	}
144147ad9175SDavid du Colombier 	if(csr32r(ctlr, Icr))
144247ad9175SDavid du Colombier 		return -1;
144347ad9175SDavid du Colombier 
144408cb4641SDavid du Colombier 	/*
144508cb4641SDavid du Colombier 	 * Balance Rx/Tx packet buffer.
144608cb4641SDavid du Colombier 	 * No need to set PBA register unless using jumbo, defaults to 32KB
144708cb4641SDavid du Colombier 	 * for receive. If it is changed, then have to do a MAC reset,
144808cb4641SDavid du Colombier 	 * and need to do that at the the right time as it will wipe stuff.
144908cb4641SDavid du Colombier 	 */
145008cb4641SDavid du Colombier 	if(ctlr->rbsz > 8192 && (ctlr->type == i82563 || ctlr->type == i82571 ||
145108cb4641SDavid du Colombier 	    ctlr->type == i82572)){
145208cb4641SDavid du Colombier 		ctlr->pba = csr32r(ctlr, Pba);
145308cb4641SDavid du Colombier 		r = ctlr->pba >> 16;
145408cb4641SDavid du Colombier 		r += ctlr->pba & 0xffff;
145508cb4641SDavid du Colombier 		r >>= 1;
145608cb4641SDavid du Colombier 		csr32w(ctlr, Pba, r);
1457a587111cSDavid du Colombier 	} else if(ctlr->type == i82573 && ctlr->rbsz > ETHERMAXTU)
145808cb4641SDavid du Colombier 		csr32w(ctlr, Pba, 14);
145908cb4641SDavid du Colombier 	ctlr->pba = csr32r(ctlr, Pba);
146008cb4641SDavid du Colombier 
146108cb4641SDavid du Colombier 	r = csr32r(ctlr, Ctrl);
146208cb4641SDavid du Colombier 	csr32w(ctlr, Ctrl, Slu|r);
146308cb4641SDavid du Colombier 
146447ad9175SDavid du Colombier 	return 0;
146547ad9175SDavid du Colombier }
146647ad9175SDavid du Colombier 
14678ccc32efSDavid du Colombier static int
i82563detach(Ctlr * ctlr)14688ccc32efSDavid du Colombier i82563detach(Ctlr* ctlr)
14698ccc32efSDavid du Colombier {
14708ccc32efSDavid du Colombier 	int r;
14718ccc32efSDavid du Colombier 	static Lock detlck;
14728ccc32efSDavid du Colombier 
14738ccc32efSDavid du Colombier 	ilock(&detlck);
14748ccc32efSDavid du Colombier 	r = i82563detach0(ctlr);
14758ccc32efSDavid du Colombier 	iunlock(&detlck);
14768ccc32efSDavid du Colombier 	return r;
14778ccc32efSDavid du Colombier }
14788ccc32efSDavid du Colombier 
147947ad9175SDavid du Colombier static void
i82563shutdown(Ether * ether)148047ad9175SDavid du Colombier i82563shutdown(Ether* ether)
148147ad9175SDavid du Colombier {
148247ad9175SDavid du Colombier 	i82563detach(ether->ctlr);
148347ad9175SDavid du Colombier }
148447ad9175SDavid du Colombier 
148547ad9175SDavid du Colombier static ushort
eeread(Ctlr * ctlr,int adr)148647ad9175SDavid du Colombier eeread(Ctlr *ctlr, int adr)
148747ad9175SDavid du Colombier {
14888ccc32efSDavid du Colombier 	ulong n;
14898ccc32efSDavid du Colombier 
14900343ea0dSDavid du Colombier 	csr32w(ctlr, Eerd, EEstart | adr << 2);
14918ccc32efSDavid du Colombier 	for (n = 1000000; (csr32r(ctlr, Eerd) & EEdone) == 0 && n-- > 0; )
149247ad9175SDavid du Colombier 		;
14938ccc32efSDavid du Colombier 	if (n == 0)
14948ccc32efSDavid du Colombier 		panic("i82563: eeread stuck");
149547ad9175SDavid du Colombier 	return csr32r(ctlr, Eerd) >> 16;
149647ad9175SDavid du Colombier }
149747ad9175SDavid du Colombier 
149847ad9175SDavid du Colombier static int
eeload(Ctlr * ctlr)149947ad9175SDavid du Colombier eeload(Ctlr *ctlr)
150047ad9175SDavid du Colombier {
150147ad9175SDavid du Colombier 	ushort sum;
150247ad9175SDavid du Colombier 	int data, adr;
150347ad9175SDavid du Colombier 
150447ad9175SDavid du Colombier 	sum = 0;
150547ad9175SDavid du Colombier 	for (adr = 0; adr < 0x40; adr++) {
150647ad9175SDavid du Colombier 		data = eeread(ctlr, adr);
150747ad9175SDavid du Colombier 		ctlr->eeprom[adr] = data;
150847ad9175SDavid du Colombier 		sum += data;
150947ad9175SDavid du Colombier 	}
151047ad9175SDavid du Colombier 	return sum;
151147ad9175SDavid du Colombier }
151247ad9175SDavid du Colombier 
151347ad9175SDavid du Colombier static int
fcycle(Ctlr *,Flash * f)151491b330d9SDavid du Colombier fcycle(Ctlr *, Flash *f)
151591b330d9SDavid du Colombier {
151691b330d9SDavid du Colombier 	ushort s, i;
151791b330d9SDavid du Colombier 
151891b330d9SDavid du Colombier 	s = f->reg[Fsts];
151991b330d9SDavid du Colombier 	if((s&Fvalid) == 0)
152091b330d9SDavid du Colombier 		return -1;
152191b330d9SDavid du Colombier 	f->reg[Fsts] |= Fcerr | Ael;
152291b330d9SDavid du Colombier 	for(i = 0; i < 10; i++){
152391b330d9SDavid du Colombier 		if((s&Scip) == 0)
152491b330d9SDavid du Colombier 			return 0;
152591b330d9SDavid du Colombier 		delay(1);
152691b330d9SDavid du Colombier 		s = f->reg[Fsts];
152791b330d9SDavid du Colombier 	}
152891b330d9SDavid du Colombier 	return -1;
152991b330d9SDavid du Colombier }
153091b330d9SDavid du Colombier 
153191b330d9SDavid du Colombier static int
fread(Ctlr * ctlr,Flash * f,int ladr)1532217e9e83SDavid du Colombier fread(Ctlr *ctlr, Flash *f, int ladr)
153391b330d9SDavid du Colombier {
153491b330d9SDavid du Colombier 	ushort s;
15358ccc32efSDavid du Colombier 	ulong n;
153691b330d9SDavid du Colombier 
153791b330d9SDavid du Colombier 	delay(1);
1538217e9e83SDavid du Colombier 	if(fcycle(ctlr, f) == -1)
153991b330d9SDavid du Colombier 		return -1;
154091b330d9SDavid du Colombier 	f->reg[Fsts] |= Fdone;
154191b330d9SDavid du Colombier 	f->reg32[Faddr] = ladr;
154291b330d9SDavid du Colombier 
154391b330d9SDavid du Colombier 	/* setup flash control register */
154491b330d9SDavid du Colombier 	s = f->reg[Fctl];
154591b330d9SDavid du Colombier 	s &= ~(0x1f << 8);
154691b330d9SDavid du Colombier 	s |= (2-1) << 8;		/* 2 bytes */
154791b330d9SDavid du Colombier 	s &= ~(2*Flcycle);		/* read */
154891b330d9SDavid du Colombier 	f->reg[Fctl] = s | Fgo;
154991b330d9SDavid du Colombier 
15508ccc32efSDavid du Colombier 	for (n = 1000000; (f->reg[Fsts] & Fdone) == 0 && n-- > 0; )
155191b330d9SDavid du Colombier 		;
15528ccc32efSDavid du Colombier 	if (n == 0)
15538ccc32efSDavid du Colombier 		panic("i82563: fread stuck");
155491b330d9SDavid du Colombier 	if(f->reg[Fsts] & (Fcerr|Ael))
155591b330d9SDavid du Colombier 		return -1;
155691b330d9SDavid du Colombier 	return f->reg32[Fdata] & 0xffff;
155791b330d9SDavid du Colombier }
155891b330d9SDavid du Colombier 
155991b330d9SDavid du Colombier static int
fload(Ctlr * ctlr)1560217e9e83SDavid du Colombier fload(Ctlr *ctlr)
156191b330d9SDavid du Colombier {
156291b330d9SDavid du Colombier 	ulong data, io, r, adr;
156391b330d9SDavid du Colombier 	ushort sum;
156491b330d9SDavid du Colombier 	Flash f;
156591b330d9SDavid du Colombier 
1566217e9e83SDavid du Colombier 	io = ctlr->pcidev->mem[1].bar & ~0x0f;
1567217e9e83SDavid du Colombier 	f.reg = vmap(io, ctlr->pcidev->mem[1].size);
156891b330d9SDavid du Colombier 	if(f.reg == nil)
156991b330d9SDavid du Colombier 		return -1;
157008cb4641SDavid du Colombier 	f.reg32 = (void*)f.reg;
15718c242bd4SDavid du Colombier 	f.base = f.reg32[Bfpr] & FMASK(0, 13);
15728c242bd4SDavid du Colombier 	f.lim = (f.reg32[Bfpr]>>16) & FMASK(0, 13);
1573217e9e83SDavid du Colombier 	if(csr32r(ctlr, Eec) & (1<<22))
15748c242bd4SDavid du Colombier 		f.base += (f.lim + 1 - f.base) >> 1;
15758c242bd4SDavid du Colombier 	r = f.base << 12;
157691b330d9SDavid du Colombier 
157791b330d9SDavid du Colombier 	sum = 0;
157891b330d9SDavid du Colombier 	for (adr = 0; adr < 0x40; adr++) {
1579217e9e83SDavid du Colombier 		data = fread(ctlr, &f, r + adr*2);
158091b330d9SDavid du Colombier 		if(data == -1)
158191b330d9SDavid du Colombier 			break;
1582217e9e83SDavid du Colombier 		ctlr->eeprom[adr] = data;
158391b330d9SDavid du Colombier 		sum += data;
158491b330d9SDavid du Colombier 	}
1585217e9e83SDavid du Colombier 	vunmap(f.reg, ctlr->pcidev->mem[1].size);
158691b330d9SDavid du Colombier 	return sum;
158791b330d9SDavid du Colombier }
158891b330d9SDavid du Colombier 
158991b330d9SDavid du Colombier static int
i82563reset(Ctlr * ctlr)159047ad9175SDavid du Colombier i82563reset(Ctlr *ctlr)
159147ad9175SDavid du Colombier {
159247ad9175SDavid du Colombier 	int i, r;
159347ad9175SDavid du Colombier 
159447ad9175SDavid du Colombier 	if(i82563detach(ctlr))
159547ad9175SDavid du Colombier 		return -1;
15969b7bf7dfSDavid du Colombier 	if(ctlr->type == i82566 || ctlr->type == i82567 ||
15979b7bf7dfSDavid du Colombier 	   ctlr->type == i82577 || ctlr->type == i82579)
159891b330d9SDavid du Colombier 		r = fload(ctlr);
159991b330d9SDavid du Colombier 	else
160047ad9175SDavid du Colombier 		r = eeload(ctlr);
160147ad9175SDavid du Colombier 	if (r != 0 && r != 0xBABA){
160291b330d9SDavid du Colombier 		print("%s: bad EEPROM checksum - %#.4ux\n",
160391b330d9SDavid du Colombier 			tname[ctlr->type], r);
160447ad9175SDavid du Colombier 		return -1;
160547ad9175SDavid du Colombier 	}
160647ad9175SDavid du Colombier 
160731748cd5SDavid du Colombier 	for(i = 0; i < Eaddrlen/2; i++){
160831748cd5SDavid du Colombier 		ctlr->ra[2*i]   = ctlr->eeprom[Ea+i];
160931748cd5SDavid du Colombier 		ctlr->ra[2*i+1] = ctlr->eeprom[Ea+i] >> 8;
161047ad9175SDavid du Colombier 	}
161122f9ff60SDavid du Colombier 	r = (csr32r(ctlr, Status) & Lanid) >> 2;
161222f9ff60SDavid du Colombier 	ctlr->ra[5] += r;		/* ea ctlr[1] = ea ctlr[0]+1 */
161322f9ff60SDavid du Colombier 
161437a6523bSDavid du Colombier 	r = ctlr->ra[3]<<24 | ctlr->ra[2]<<16 | ctlr->ra[1]<<8 | ctlr->ra[0];
161547ad9175SDavid du Colombier 	csr32w(ctlr, Ral, r);
161637a6523bSDavid du Colombier 	r = 0x80000000 | ctlr->ra[5]<<8 | ctlr->ra[4];
161747ad9175SDavid du Colombier 	csr32w(ctlr, Rah, r);
161847ad9175SDavid du Colombier 	for(i = 1; i < 16; i++){
161947ad9175SDavid du Colombier 		csr32w(ctlr, Ral+i*8, 0);
162047ad9175SDavid du Colombier 		csr32w(ctlr, Rah+i*8, 0);
162147ad9175SDavid du Colombier 	}
162247ad9175SDavid du Colombier 	memset(ctlr->mta, 0, sizeof(ctlr->mta));
162347ad9175SDavid du Colombier 	for(i = 0; i < 128; i++)
162447ad9175SDavid du Colombier 		csr32w(ctlr, Mta + i*4, 0);
162508cb4641SDavid du Colombier 
162608cb4641SDavid du Colombier 	/*
162708cb4641SDavid du Colombier 	 * Does autonegotiation affect this manual setting?
162808cb4641SDavid du Colombier 	 * The correct values here should depend on the PBA value
162908cb4641SDavid du Colombier 	 * and maximum frame length, no?
163008cb4641SDavid du Colombier 	 * ctlr->fcrt[lh] are never set, so default to 0.
163108cb4641SDavid du Colombier 	 */
163247ad9175SDavid du Colombier 	csr32w(ctlr, Fcal, 0x00C28001);
163337a6523bSDavid du Colombier 	csr32w(ctlr, Fcah, 0x0100);
16349b7bf7dfSDavid du Colombier 	if(ctlr->type != i82579)
163537a6523bSDavid du Colombier 		csr32w(ctlr, Fct, 0x8808);
163637a6523bSDavid du Colombier 	csr32w(ctlr, Fcttv, 0x0100);
163708cb4641SDavid du Colombier 
163846136019SDavid du Colombier 	ctlr->fcrtl = ctlr->fcrth = 0;
163946136019SDavid du Colombier 	// ctlr->fcrtl = 0x00002000;
164046136019SDavid du Colombier 	// ctlr->fcrth = 0x00004000;
164147ad9175SDavid du Colombier 	csr32w(ctlr, Fcrtl, ctlr->fcrtl);
164247ad9175SDavid du Colombier 	csr32w(ctlr, Fcrth, ctlr->fcrth);
164337a6523bSDavid du Colombier 
164447ad9175SDavid du Colombier 	return 0;
164547ad9175SDavid du Colombier }
164647ad9175SDavid du Colombier 
164747ad9175SDavid du Colombier static void
i82563pci(void)164847ad9175SDavid du Colombier i82563pci(void)
164947ad9175SDavid du Colombier {
16500343ea0dSDavid du Colombier 	int type;
165147ad9175SDavid du Colombier 	ulong io;
165247ad9175SDavid du Colombier 	void *mem;
165337a6523bSDavid du Colombier 	Pcidev *p;
165437a6523bSDavid du Colombier 	Ctlr *ctlr;
165547ad9175SDavid du Colombier 
165647ad9175SDavid du Colombier 	p = nil;
165737a6523bSDavid du Colombier 	while(p = pcimatch(p, 0x8086, 0)){
165837a6523bSDavid du Colombier 		switch(p->did){
165937a6523bSDavid du Colombier 		default:
166047ad9175SDavid du Colombier 			continue;
166137a6523bSDavid du Colombier 		case 0x1096:
166237a6523bSDavid du Colombier 		case 0x10ba:
166337a6523bSDavid du Colombier 			type = i82563;
166437a6523bSDavid du Colombier 			break;
166537a6523bSDavid du Colombier 		case 0x1049:		/* mm */
166637a6523bSDavid du Colombier 		case 0x104a:		/* dm */
1667f881a1ebSDavid du Colombier 		case 0x104b:		/* dc */
1668f881a1ebSDavid du Colombier 		case 0x104d:		/* mc */
166931748cd5SDavid du Colombier 		case 0x10bd:		/* dm */
1670f881a1ebSDavid du Colombier 		case 0x294c:		/* dc-2 */
167137a6523bSDavid du Colombier 			type = i82566;
167237a6523bSDavid du Colombier 			break;
1673e4ac449cSDavid du Colombier 		case 0x10cd:		/* lf */
1674f881a1ebSDavid du Colombier 		case 0x10ce:		/* v-2 */
1675f881a1ebSDavid du Colombier 		case 0x10de:		/* lm-3 */
1676f881a1ebSDavid du Colombier 		case 0x10f5:		/* lm-2 */
1677e4ac449cSDavid du Colombier 			type = i82567;
1678e4ac449cSDavid du Colombier 			break;
167937a6523bSDavid du Colombier 		case 0x10a4:
168037a6523bSDavid du Colombier 		case 0x105e:
168137a6523bSDavid du Colombier 			type = i82571;
168237a6523bSDavid du Colombier 			break;
16834faf3cb1SDavid du Colombier 		case 0x107d:		/* eb copper */
16844faf3cb1SDavid du Colombier 		case 0x107e:		/* ei fiber */
16854faf3cb1SDavid du Colombier 		case 0x107f:		/* ei */
1686f881a1ebSDavid du Colombier 		case 0x10b9:		/* sic, 82572gi */
168737a6523bSDavid du Colombier 			type = i82572;
168837a6523bSDavid du Colombier 			break;
1689f881a1ebSDavid du Colombier 		case 0x108b:		/*  v */
169037a6523bSDavid du Colombier 		case 0x108c:		/*  e (iamt) */
169137a6523bSDavid du Colombier 		case 0x109a:		/*  l */
169237a6523bSDavid du Colombier 			type = i82573;
169337a6523bSDavid du Colombier 			break;
169488bfb1f3SDavid du Colombier 		case 0x10d3:		/* l */
169588bfb1f3SDavid du Colombier 			type = i82574;
169688bfb1f3SDavid du Colombier 			break;
16976f746b77SDavid du Colombier 		case 0x10a7:	/* 82575eb: one of a pair of controllers */
169808cb4641SDavid du Colombier 			type = i82575;
169908cb4641SDavid du Colombier 			break;
1700c39c2eb3SDavid du Colombier 		case 0x10c9:		/* 82576 copper */
1701c39c2eb3SDavid du Colombier 		case 0x10e6:		/* 82576 fiber */
1702c39c2eb3SDavid du Colombier 		case 0x10e7:		/* 82576 serdes */
1703c39c2eb3SDavid du Colombier 			type = i82576;
1704c39c2eb3SDavid du Colombier 			break;
1705d8cd2beaSDavid du Colombier 		case 0x10ea:		/* 82577lm */
1706d8cd2beaSDavid du Colombier 			type = i82577;
1707d8cd2beaSDavid du Colombier 			break;
17089b7bf7dfSDavid du Colombier 		case 0x1502:		/* 82579lm */
17099b7bf7dfSDavid du Colombier 		case 0x1503:		/* 82579v */
17109b7bf7dfSDavid du Colombier 			type = i82579;
17119b7bf7dfSDavid du Colombier 			break;
171237a6523bSDavid du Colombier 		}
171347ad9175SDavid du Colombier 
171447ad9175SDavid du Colombier 		io = p->mem[0].bar & ~0x0F;
171547ad9175SDavid du Colombier 		mem = vmap(io, p->mem[0].size);
171647ad9175SDavid du Colombier 		if(mem == nil){
171731748cd5SDavid du Colombier 			print("%s: can't map %.8lux\n", tname[type], io);
171847ad9175SDavid du Colombier 			continue;
171947ad9175SDavid du Colombier 		}
172047ad9175SDavid du Colombier 		ctlr = malloc(sizeof(Ctlr));
1721aa72973aSDavid du Colombier 		if(ctlr == nil) {
1722aa72973aSDavid du Colombier 			vunmap(mem, p->mem[0].size);
1723aa72973aSDavid du Colombier 			error(Enomem);
1724aa72973aSDavid du Colombier 		}
172547ad9175SDavid du Colombier 		ctlr->port = io;
172647ad9175SDavid du Colombier 		ctlr->pcidev = p;
172737a6523bSDavid du Colombier 		ctlr->type = type;
1728a587111cSDavid du Colombier 		/*
1729a587111cSDavid du Colombier 		 * on the assumption that allowing jumbo packets makes
1730a587111cSDavid du Colombier 		 * the controller much slower (as is true of the 82579),
1731a587111cSDavid du Colombier 		 * never allow jumbos.
1732a587111cSDavid du Colombier 		 */
1733a587111cSDavid du Colombier 		// ctlr->rbsz = rbtab[type];
1734a587111cSDavid du Colombier 		ctlr->rbsz = ETHERMAXTU;
173547ad9175SDavid du Colombier 		ctlr->nic = mem;
173647ad9175SDavid du Colombier 
173747ad9175SDavid du Colombier 		if(i82563reset(ctlr)){
173831748cd5SDavid du Colombier 			vunmap(mem, p->mem[0].size);
173947ad9175SDavid du Colombier 			free(ctlr);
174047ad9175SDavid du Colombier 			continue;
174147ad9175SDavid du Colombier 		}
174247ad9175SDavid du Colombier 		pcisetbme(p);
174347ad9175SDavid du Colombier 
174447ad9175SDavid du Colombier 		if(i82563ctlrhead != nil)
174547ad9175SDavid du Colombier 			i82563ctlrtail->next = ctlr;
174647ad9175SDavid du Colombier 		else
174747ad9175SDavid du Colombier 			i82563ctlrhead = ctlr;
174847ad9175SDavid du Colombier 		i82563ctlrtail = ctlr;
174947ad9175SDavid du Colombier 	}
175047ad9175SDavid du Colombier }
175147ad9175SDavid du Colombier 
175247ad9175SDavid du Colombier static int
pnp(Ether * edev,int type)175337a6523bSDavid du Colombier pnp(Ether* edev, int type)
175447ad9175SDavid du Colombier {
175547ad9175SDavid du Colombier 	Ctlr *ctlr;
175691b330d9SDavid du Colombier 	static int done;
175747ad9175SDavid du Colombier 
175891b330d9SDavid du Colombier 	if(!done) {
175947ad9175SDavid du Colombier 		i82563pci();
176091b330d9SDavid du Colombier 		done = 1;
176191b330d9SDavid du Colombier 	}
176247ad9175SDavid du Colombier 
176347ad9175SDavid du Colombier 	/*
176447ad9175SDavid du Colombier 	 * Any adapter matches if no edev->port is supplied,
176547ad9175SDavid du Colombier 	 * otherwise the ports must match.
176647ad9175SDavid du Colombier 	 */
176737a6523bSDavid du Colombier 	for(ctlr = i82563ctlrhead; ctlr != nil; ctlr = ctlr->next){
176847ad9175SDavid du Colombier 		if(ctlr->active)
176947ad9175SDavid du Colombier 			continue;
177091b330d9SDavid du Colombier 		if(type != Iany && ctlr->type != type)
177137a6523bSDavid du Colombier 			continue;
177237a6523bSDavid du Colombier 		if(edev->port == 0 || edev->port == ctlr->port){
177347ad9175SDavid du Colombier 			ctlr->active = 1;
177447ad9175SDavid du Colombier 			break;
177547ad9175SDavid du Colombier 		}
177637a6523bSDavid du Colombier 	}
177747ad9175SDavid du Colombier 	if(ctlr == nil)
177847ad9175SDavid du Colombier 		return -1;
177947ad9175SDavid du Colombier 
178047ad9175SDavid du Colombier 	edev->ctlr = ctlr;
17816f746b77SDavid du Colombier 	ctlr->edev = edev;			/* point back to Ether* */
178247ad9175SDavid du Colombier 	edev->port = ctlr->port;
178347ad9175SDavid du Colombier 	edev->irq = ctlr->pcidev->intl;
178447ad9175SDavid du Colombier 	edev->tbdf = ctlr->pcidev->tbdf;
178547ad9175SDavid du Colombier 	edev->mbps = 1000;
178637a6523bSDavid du Colombier 	edev->maxmtu = ctlr->rbsz;
178747ad9175SDavid du Colombier 	memmove(edev->ea, ctlr->ra, Eaddrlen);
178847ad9175SDavid du Colombier 
178947ad9175SDavid du Colombier 	/*
179047ad9175SDavid du Colombier 	 * Linkage to the generic ethernet driver.
179147ad9175SDavid du Colombier 	 */
179247ad9175SDavid du Colombier 	edev->attach = i82563attach;
179347ad9175SDavid du Colombier 	edev->transmit = i82563transmit;
17946f746b77SDavid du Colombier 	edev->interrupt = (ctlr->type == i82575?
17956f746b77SDavid du Colombier 		i82575interrupt: i82563interrupt);
179647ad9175SDavid du Colombier 	edev->ifstat = i82563ifstat;
179747ad9175SDavid du Colombier 	edev->ctl = i82563ctl;
179847ad9175SDavid du Colombier 
179947ad9175SDavid du Colombier 	edev->arg = edev;
180047ad9175SDavid du Colombier 	edev->promiscuous = i82563promiscuous;
180147ad9175SDavid du Colombier 	edev->shutdown = i82563shutdown;
180247ad9175SDavid du Colombier 	edev->multicast = i82563multicast;
180347ad9175SDavid du Colombier 
180447ad9175SDavid du Colombier 	return 0;
180547ad9175SDavid du Colombier }
180647ad9175SDavid du Colombier 
180737a6523bSDavid du Colombier static int
anypnp(Ether * e)180837a6523bSDavid du Colombier anypnp(Ether *e)
180937a6523bSDavid du Colombier {
181091b330d9SDavid du Colombier 	return pnp(e, Iany);
181137a6523bSDavid du Colombier }
181237a6523bSDavid du Colombier 
181337a6523bSDavid du Colombier static int
i82563pnp(Ether * e)181437a6523bSDavid du Colombier i82563pnp(Ether *e)
181537a6523bSDavid du Colombier {
181637a6523bSDavid du Colombier 	return pnp(e, i82563);
181737a6523bSDavid du Colombier }
181837a6523bSDavid du Colombier 
181937a6523bSDavid du Colombier static int
i82566pnp(Ether * e)182037a6523bSDavid du Colombier i82566pnp(Ether *e)
182137a6523bSDavid du Colombier {
182237a6523bSDavid du Colombier 	return pnp(e, i82566);
182337a6523bSDavid du Colombier }
182437a6523bSDavid du Colombier 
182537a6523bSDavid du Colombier static int
i82571pnp(Ether * e)182637a6523bSDavid du Colombier i82571pnp(Ether *e)
182737a6523bSDavid du Colombier {
182837a6523bSDavid du Colombier 	return pnp(e, i82571);
182937a6523bSDavid du Colombier }
183037a6523bSDavid du Colombier 
183137a6523bSDavid du Colombier static int
i82572pnp(Ether * e)183237a6523bSDavid du Colombier i82572pnp(Ether *e)
183337a6523bSDavid du Colombier {
183437a6523bSDavid du Colombier 	return pnp(e, i82572);
183537a6523bSDavid du Colombier }
183637a6523bSDavid du Colombier 
183737a6523bSDavid du Colombier static int
i82573pnp(Ether * e)183837a6523bSDavid du Colombier i82573pnp(Ether *e)
183937a6523bSDavid du Colombier {
184037a6523bSDavid du Colombier 	return pnp(e, i82573);
184137a6523bSDavid du Colombier }
184237a6523bSDavid du Colombier 
184308cb4641SDavid du Colombier static int
i82575pnp(Ether * e)184408cb4641SDavid du Colombier i82575pnp(Ether *e)
184508cb4641SDavid du Colombier {
184608cb4641SDavid du Colombier 	return pnp(e, i82575);
184708cb4641SDavid du Colombier }
184808cb4641SDavid du Colombier 
18499b7bf7dfSDavid du Colombier static int
i82579pnp(Ether * e)18509b7bf7dfSDavid du Colombier i82579pnp(Ether *e)
18519b7bf7dfSDavid du Colombier {
18529b7bf7dfSDavid du Colombier 	return pnp(e, i82579);
18539b7bf7dfSDavid du Colombier }
18549b7bf7dfSDavid du Colombier 
185547ad9175SDavid du Colombier void
ether82563link(void)185647ad9175SDavid du Colombier ether82563link(void)
185747ad9175SDavid du Colombier {
185837a6523bSDavid du Colombier 	/* recognise lots of model numbers for debugging assistance */
185947ad9175SDavid du Colombier 	addethercard("i82563", i82563pnp);
186037a6523bSDavid du Colombier 	addethercard("i82566", i82566pnp);
186137a6523bSDavid du Colombier 	addethercard("i82571", i82571pnp);
186237a6523bSDavid du Colombier 	addethercard("i82572", i82572pnp);
186337a6523bSDavid du Colombier 	addethercard("i82573", i82573pnp);
186408cb4641SDavid du Colombier 	addethercard("i82575", i82575pnp);
18659b7bf7dfSDavid du Colombier 	addethercard("i82579", i82579pnp);
18660343ea0dSDavid du Colombier 	addethercard("igbepcie", anypnp);
186747ad9175SDavid du Colombier }
1868