xref: /plan9/sys/src/9/pc/etherigbe.c (revision cebd3b46303b6884206200c08c0d8b3fb8dc989e)
16a081dcdSDavid du Colombier /*
27e254d1cSDavid du Colombier  * Intel 8254[340]NN Gigabit Ethernet PCI Controllers
36a081dcdSDavid du Colombier  * as found on the Intel PRO/1000 series of adapters:
46a081dcdSDavid du Colombier  *	82543GC	Intel PRO/1000 T
56a081dcdSDavid du Colombier  *	82544EI Intel PRO/1000 XT
66a081dcdSDavid du Colombier  *	82540EM Intel PRO/1000 MT
7d717568cSDavid du Colombier  *	82541[GP]I
8aeb1c8a5SDavid du Colombier  *	82547GI
9aeb1c8a5SDavid du Colombier  *	82546GB
105b7d6169SDavid du Colombier  *	82546EB
116a081dcdSDavid du Colombier  * To Do:
126a081dcdSDavid du Colombier  *	finish autonegotiation code;
136a081dcdSDavid du Colombier  *	integrate fiber stuff back in (this ONLY handles
146a081dcdSDavid du Colombier  *	the CAT5 cards at the moment);
156a081dcdSDavid du Colombier  *	add tuning control via ctl file;
166a081dcdSDavid du Colombier  *	this driver is little-endian specific.
176a081dcdSDavid du Colombier  */
186a081dcdSDavid du Colombier #include "u.h"
196a081dcdSDavid du Colombier #include "../port/lib.h"
206a081dcdSDavid du Colombier #include "mem.h"
216a081dcdSDavid du Colombier #include "dat.h"
226a081dcdSDavid du Colombier #include "fns.h"
236a081dcdSDavid du Colombier #include "io.h"
246a081dcdSDavid du Colombier #include "../port/error.h"
256a081dcdSDavid du Colombier #include "../port/netif.h"
266a081dcdSDavid du Colombier 
276a081dcdSDavid du Colombier #include "etherif.h"
286a081dcdSDavid du Colombier #include "ethermii.h"
296a081dcdSDavid du Colombier 
306a081dcdSDavid du Colombier enum {
31aeb1c8a5SDavid du Colombier 	i82542		= (0x1000<<16)|0x8086,
32aeb1c8a5SDavid du Colombier 	i82543gc	= (0x1004<<16)|0x8086,
33aeb1c8a5SDavid du Colombier 	i82544ei	= (0x1008<<16)|0x8086,
3407c32395SDavid du Colombier 	i82544eif	= (0x1009<<16)|0x8086,
359a8968c3SDavid du Colombier 	i82544gc	= (0x100d<<16)|0x8086,
36aeb1c8a5SDavid du Colombier 	i82540em	= (0x100E<<16)|0x8086,
37aeb1c8a5SDavid du Colombier 	i82540eplp	= (0x101E<<16)|0x8086,
38c079eed3SDavid du Colombier 	i82545em	= (0x100F<<16)|0x8086,
39ab3dc52fSDavid du Colombier 	i82545gmc	= (0x1026<<16)|0x8086,
405efba406SDavid du Colombier 	i82547ei	= (0x1019<<16)|0x8086,
41aeb1c8a5SDavid du Colombier 	i82547gi	= (0x1075<<16)|0x8086,
425efba406SDavid du Colombier 	i82541ei	= (0x1013<<16)|0x8086,
43aeb1c8a5SDavid du Colombier 	i82541gi	= (0x1076<<16)|0x8086,
4426ad7229SDavid du Colombier 	i82541gi2	= (0x1077<<16)|0x8086,
45d717568cSDavid du Colombier 	i82541pi	= (0x107c<<16)|0x8086,
465efba406SDavid du Colombier 	i82546gb	= (0x1079<<16)|0x8086,
475b7d6169SDavid du Colombier 	i82546eb	= (0x1010<<16)|0x8086,
48aeb1c8a5SDavid du Colombier };
49aeb1c8a5SDavid du Colombier 
50aeb1c8a5SDavid du Colombier enum {
516a081dcdSDavid du Colombier 	Ctrl		= 0x00000000,	/* Device Control */
52ab3dc52fSDavid du Colombier 	Ctrldup		= 0x00000004,	/* Device Control Duplicate */
536a081dcdSDavid du Colombier 	Status		= 0x00000008,	/* Device Status */
546a081dcdSDavid du Colombier 	Eecd		= 0x00000010,	/* EEPROM/Flash Control/Data */
556a081dcdSDavid du Colombier 	Ctrlext		= 0x00000018,	/* Extended Device Control */
566a081dcdSDavid du Colombier 	Mdic		= 0x00000020,	/* MDI Control */
576a081dcdSDavid du Colombier 	Fcal		= 0x00000028,	/* Flow Control Address Low */
586a081dcdSDavid du Colombier 	Fcah		= 0x0000002C,	/* Flow Control Address High */
596a081dcdSDavid du Colombier 	Fct		= 0x00000030,	/* Flow Control Type */
606a081dcdSDavid du Colombier 	Icr		= 0x000000C0,	/* Interrupt Cause Read */
616a081dcdSDavid du Colombier 	Ics		= 0x000000C8,	/* Interrupt Cause Set */
626a081dcdSDavid du Colombier 	Ims		= 0x000000D0,	/* Interrupt Mask Set/Read */
636a081dcdSDavid du Colombier 	Imc		= 0x000000D8,	/* Interrupt mask Clear */
646a081dcdSDavid du Colombier 	Rctl		= 0x00000100,	/* Receive Control */
656a081dcdSDavid du Colombier 	Fcttv		= 0x00000170,	/* Flow Control Transmit Timer Value */
666a081dcdSDavid du Colombier 	Txcw		= 0x00000178,	/* Transmit Configuration Word */
676a081dcdSDavid du Colombier 	Rxcw		= 0x00000180,	/* Receive Configuration Word */
68b1707c5dSDavid du Colombier 	/* on the oldest cards (8254[23]), the Mta register is at 0x200 */
696a081dcdSDavid du Colombier 	Tctl		= 0x00000400,	/* Transmit Control */
706a081dcdSDavid du Colombier 	Tipg		= 0x00000410,	/* Transmit IPG */
716a081dcdSDavid du Colombier 	Tbt		= 0x00000448,	/* Transmit Burst Timer */
726a081dcdSDavid du Colombier 	Ait		= 0x00000458,	/* Adaptive IFS Throttle */
736a081dcdSDavid du Colombier 	Fcrtl		= 0x00002160,	/* Flow Control RX Threshold Low */
746a081dcdSDavid du Colombier 	Fcrth		= 0x00002168,	/* Flow Control Rx Threshold High */
756a081dcdSDavid du Colombier 	Rdfh		= 0x00002410,	/* Receive data fifo head */
766a081dcdSDavid du Colombier 	Rdft		= 0x00002418,	/* Receive data fifo tail */
776a081dcdSDavid du Colombier 	Rdfhs		= 0x00002420,	/* Receive data fifo head saved */
786a081dcdSDavid du Colombier 	Rdfts		= 0x00002428,	/* Receive data fifo tail saved */
796a081dcdSDavid du Colombier 	Rdfpc		= 0x00002430,	/* Receive data fifo packet count */
806a081dcdSDavid du Colombier 	Rdbal		= 0x00002800,	/* Rd Base Address Low */
816a081dcdSDavid du Colombier 	Rdbah		= 0x00002804,	/* Rd Base Address High */
826a081dcdSDavid du Colombier 	Rdlen		= 0x00002808,	/* Receive Descriptor Length */
836a081dcdSDavid du Colombier 	Rdh		= 0x00002810,	/* Receive Descriptor Head */
846a081dcdSDavid du Colombier 	Rdt		= 0x00002818,	/* Receive Descriptor Tail */
856a081dcdSDavid du Colombier 	Rdtr		= 0x00002820,	/* Receive Descriptor Timer Ring */
866a081dcdSDavid du Colombier 	Rxdctl		= 0x00002828,	/* Receive Descriptor Control */
876a081dcdSDavid du Colombier 	Radv		= 0x0000282C,	/* Receive Interrupt Absolute Delay Timer */
886a081dcdSDavid du Colombier 	Txdmac		= 0x00003000,	/* Transfer DMA Control */
896a081dcdSDavid du Colombier 	Ett		= 0x00003008,	/* Early Transmit Control */
906a081dcdSDavid du Colombier 	Tdfh		= 0x00003410,	/* Transmit data fifo head */
916a081dcdSDavid du Colombier 	Tdft		= 0x00003418,	/* Transmit data fifo tail */
926a081dcdSDavid du Colombier 	Tdfhs		= 0x00003420,	/* Transmit data Fifo Head saved */
936a081dcdSDavid du Colombier 	Tdfts		= 0x00003428,	/* Transmit data fifo tail saved */
946a081dcdSDavid du Colombier 	Tdfpc		= 0x00003430,	/* Trasnmit data Fifo packet count */
956a081dcdSDavid du Colombier 	Tdbal		= 0x00003800,	/* Td Base Address Low */
966a081dcdSDavid du Colombier 	Tdbah		= 0x00003804,	/* Td Base Address High */
976a081dcdSDavid du Colombier 	Tdlen		= 0x00003808,	/* Transmit Descriptor Length */
986a081dcdSDavid du Colombier 	Tdh		= 0x00003810,	/* Transmit Descriptor Head */
996a081dcdSDavid du Colombier 	Tdt		= 0x00003818,	/* Transmit Descriptor Tail */
1006a081dcdSDavid du Colombier 	Tidv		= 0x00003820,	/* Transmit Interrupt Delay Value */
1016a081dcdSDavid du Colombier 	Txdctl		= 0x00003828,	/* Transmit Descriptor Control */
1026a081dcdSDavid du Colombier 	Tadv		= 0x0000382C,	/* Transmit Interrupt Absolute Delay Timer */
1036a081dcdSDavid du Colombier 
1046a081dcdSDavid du Colombier 	Statistics	= 0x00004000,	/* Start of Statistics Area */
1056a081dcdSDavid du Colombier 	Gorcl		= 0x88/4,	/* Good Octets Received Count */
1066a081dcdSDavid du Colombier 	Gotcl		= 0x90/4,	/* Good Octets Transmitted Count */
1076a081dcdSDavid du Colombier 	Torl		= 0xC0/4,	/* Total Octets Received */
1086a081dcdSDavid du Colombier 	Totl		= 0xC8/4,	/* Total Octets Transmitted */
1096a081dcdSDavid du Colombier 	Nstatistics	= 64,
1106a081dcdSDavid du Colombier 
1116a081dcdSDavid du Colombier 	Rxcsum		= 0x00005000,	/* Receive Checksum Control */
1126a081dcdSDavid du Colombier 	Mta		= 0x00005200,	/* Multicast Table Array */
1136a081dcdSDavid du Colombier 	Ral		= 0x00005400,	/* Receive Address Low */
1146a081dcdSDavid du Colombier 	Rah		= 0x00005404,	/* Receive Address High */
1156a081dcdSDavid du Colombier 	Manc		= 0x00005820,	/* Management Control */
1166a081dcdSDavid du Colombier };
1176a081dcdSDavid du Colombier 
1186a081dcdSDavid du Colombier enum {					/* Ctrl */
1196a081dcdSDavid du Colombier 	Bem		= 0x00000002,	/* Big Endian Mode */
1206a081dcdSDavid du Colombier 	Prior		= 0x00000004,	/* Priority on the PCI bus */
1216a081dcdSDavid du Colombier 	Lrst		= 0x00000008,	/* Link Reset */
1226a081dcdSDavid du Colombier 	Asde		= 0x00000020,	/* Auto-Speed Detection Enable */
1236a081dcdSDavid du Colombier 	Slu		= 0x00000040,	/* Set Link Up */
1246a081dcdSDavid du Colombier 	Ilos		= 0x00000080,	/* Invert Loss of Signal (LOS) */
1256a081dcdSDavid du Colombier 	SspeedMASK	= 0x00000300,	/* Speed Selection */
1266a081dcdSDavid du Colombier 	SspeedSHIFT	= 8,
1276a081dcdSDavid du Colombier 	Sspeed10	= 0x00000000,	/* 10Mb/s */
1286a081dcdSDavid du Colombier 	Sspeed100	= 0x00000100,	/* 100Mb/s */
1296a081dcdSDavid du Colombier 	Sspeed1000	= 0x00000200,	/* 1000Mb/s */
1306a081dcdSDavid du Colombier 	Frcspd		= 0x00000800,	/* Force Speed */
1316a081dcdSDavid du Colombier 	Frcdplx		= 0x00001000,	/* Force Duplex */
1326a081dcdSDavid du Colombier 	SwdpinsloMASK	= 0x003C0000,	/* Software Defined Pins - lo nibble */
1336a081dcdSDavid du Colombier 	SwdpinsloSHIFT	= 18,
1346a081dcdSDavid du Colombier 	SwdpioloMASK	= 0x03C00000,	/* Software Defined Pins - I or O */
1356a081dcdSDavid du Colombier 	SwdpioloSHIFT	= 22,
1366a081dcdSDavid du Colombier 	Devrst		= 0x04000000,	/* Device Reset */
1376a081dcdSDavid du Colombier 	Rfce		= 0x08000000,	/* Receive Flow Control Enable */
1386a081dcdSDavid du Colombier 	Tfce		= 0x10000000,	/* Transmit Flow Control Enable */
1396a081dcdSDavid du Colombier 	Vme		= 0x40000000,	/* VLAN Mode Enable */
1406a081dcdSDavid du Colombier };
1416a081dcdSDavid du Colombier 
1427e254d1cSDavid du Colombier /*
1437e254d1cSDavid du Colombier  * can't find Tckok nor Rbcok in any Intel docs,
1447e254d1cSDavid du Colombier  * but even 82543gc docs define Lanid.
1457e254d1cSDavid du Colombier  */
1466a081dcdSDavid du Colombier enum {					/* Status */
1476a081dcdSDavid du Colombier 	Lu		= 0x00000002,	/* Link Up */
1487e254d1cSDavid du Colombier 	Lanid		= 0x0000000C,	/* mask for Lan ID. (function id) */
1497e254d1cSDavid du Colombier //	Tckok		= 0x00000004,	/* Transmit clock is running */
1507e254d1cSDavid du Colombier //	Rbcok		= 0x00000008,	/* Receive clock is running */
1516a081dcdSDavid du Colombier 	Txoff		= 0x00000010,	/* Transmission Paused */
1526a081dcdSDavid du Colombier 	Tbimode		= 0x00000020,	/* TBI Mode Indication */
1536a081dcdSDavid du Colombier 	LspeedMASK	= 0x000000C0,	/* Link Speed Setting */
1546a081dcdSDavid du Colombier 	LspeedSHIFT	= 6,
1556a081dcdSDavid du Colombier 	Lspeed10	= 0x00000000,	/* 10Mb/s */
1566a081dcdSDavid du Colombier 	Lspeed100	= 0x00000040,	/* 100Mb/s */
1576a081dcdSDavid du Colombier 	Lspeed1000	= 0x00000080,	/* 1000Mb/s */
1586a081dcdSDavid du Colombier 	Mtxckok		= 0x00000400,	/* MTX clock is running */
1596a081dcdSDavid du Colombier 	Pci66		= 0x00000800,	/* PCI Bus speed indication */
1606a081dcdSDavid du Colombier 	Bus64		= 0x00001000,	/* PCI Bus width indication */
1616a081dcdSDavid du Colombier 	Pcixmode	= 0x00002000,	/* PCI-X mode */
1626a081dcdSDavid du Colombier 	PcixspeedMASK	= 0x0000C000,	/* PCI-X bus speed */
1636a081dcdSDavid du Colombier 	PcixspeedSHIFT	= 14,
1646a081dcdSDavid du Colombier 	Pcix66		= 0x00000000,	/* 50-66MHz */
1656a081dcdSDavid du Colombier 	Pcix100		= 0x00004000,	/* 66-100MHz */
1666a081dcdSDavid du Colombier 	Pcix133		= 0x00008000,	/* 100-133MHz */
1676a081dcdSDavid du Colombier };
1686a081dcdSDavid du Colombier 
1696a081dcdSDavid du Colombier enum {					/* Ctrl and Status */
1706a081dcdSDavid du Colombier 	Fd		= 0x00000001,	/* Full-Duplex */
1716a081dcdSDavid du Colombier 	AsdvMASK	= 0x00000300,
1726a081dcdSDavid du Colombier 	AsdvSHIFT	= 8,
1736a081dcdSDavid du Colombier 	Asdv10		= 0x00000000,	/* 10Mb/s */
1746a081dcdSDavid du Colombier 	Asdv100		= 0x00000100,	/* 100Mb/s */
1756a081dcdSDavid du Colombier 	Asdv1000	= 0x00000200,	/* 1000Mb/s */
1766a081dcdSDavid du Colombier };
1776a081dcdSDavid du Colombier 
1786a081dcdSDavid du Colombier enum {					/* Eecd */
1796a081dcdSDavid du Colombier 	Sk		= 0x00000001,	/* Clock input to the EEPROM */
1806a081dcdSDavid du Colombier 	Cs		= 0x00000002,	/* Chip Select */
1816a081dcdSDavid du Colombier 	Di		= 0x00000004,	/* Data Input to the EEPROM */
1826a081dcdSDavid du Colombier 	Do		= 0x00000008,	/* Data Output from the EEPROM */
1836a081dcdSDavid du Colombier 	Areq		= 0x00000040,	/* EEPROM Access Request */
1846a081dcdSDavid du Colombier 	Agnt		= 0x00000080,	/* EEPROM Access Grant */
1858466d066SDavid du Colombier 	Eepresent	= 0x00000100,	/* EEPROM Present */
1866a081dcdSDavid du Colombier 	Eesz256		= 0x00000200,	/* EEPROM is 256 words not 64 */
1878466d066SDavid du Colombier 	Eeszaddr	= 0x00000400,	/* EEPROM size for 8254[17] */
1886a081dcdSDavid du Colombier 	Spi		= 0x00002000,	/* EEPROM is SPI not Microwire */
1896a081dcdSDavid du Colombier };
1906a081dcdSDavid du Colombier 
1916a081dcdSDavid du Colombier enum {					/* Ctrlext */
1926a081dcdSDavid du Colombier 	Gpien		= 0x0000000F,	/* General Purpose Interrupt Enables */
1936a081dcdSDavid du Colombier 	SwdpinshiMASK	= 0x000000F0,	/* Software Defined Pins - hi nibble */
1946a081dcdSDavid du Colombier 	SwdpinshiSHIFT	= 4,
1956a081dcdSDavid du Colombier 	SwdpiohiMASK	= 0x00000F00,	/* Software Defined Pins - I or O */
1966a081dcdSDavid du Colombier 	SwdpiohiSHIFT	= 8,
1976a081dcdSDavid du Colombier 	Asdchk		= 0x00001000,	/* ASD Check */
1986a081dcdSDavid du Colombier 	Eerst		= 0x00002000,	/* EEPROM Reset */
1996a081dcdSDavid du Colombier 	Ips		= 0x00004000,	/* Invert Power State */
2006a081dcdSDavid du Colombier 	Spdbyps		= 0x00008000,	/* Speed Select Bypass */
2016a081dcdSDavid du Colombier };
2026a081dcdSDavid du Colombier 
2036a081dcdSDavid du Colombier enum {					/* EEPROM content offsets */
2046a081dcdSDavid du Colombier 	Ea		= 0x00,		/* Ethernet Address */
2056a081dcdSDavid du Colombier 	Cf		= 0x03,		/* Compatibility Field */
2066a081dcdSDavid du Colombier 	Pba		= 0x08,		/* Printed Board Assembly number */
2076a081dcdSDavid du Colombier 	Icw1		= 0x0A,		/* Initialization Control Word 1 */
2086a081dcdSDavid du Colombier 	Sid		= 0x0B,		/* Subsystem ID */
2096a081dcdSDavid du Colombier 	Svid		= 0x0C,		/* Subsystem Vendor ID */
2106a081dcdSDavid du Colombier 	Did		= 0x0D,		/* Device ID */
2116a081dcdSDavid du Colombier 	Vid		= 0x0E,		/* Vendor ID */
2126a081dcdSDavid du Colombier 	Icw2		= 0x0F,		/* Initialization Control Word 2 */
2136a081dcdSDavid du Colombier };
2146a081dcdSDavid du Colombier 
2156a081dcdSDavid du Colombier enum {					/* Mdic */
2166a081dcdSDavid du Colombier 	MDIdMASK	= 0x0000FFFF,	/* Data */
2176a081dcdSDavid du Colombier 	MDIdSHIFT	= 0,
2186a081dcdSDavid du Colombier 	MDIrMASK	= 0x001F0000,	/* PHY Register Address */
2196a081dcdSDavid du Colombier 	MDIrSHIFT	= 16,
2206a081dcdSDavid du Colombier 	MDIpMASK	= 0x03E00000,	/* PHY Address */
2216a081dcdSDavid du Colombier 	MDIpSHIFT	= 21,
2226a081dcdSDavid du Colombier 	MDIwop		= 0x04000000,	/* Write Operation */
2236a081dcdSDavid du Colombier 	MDIrop		= 0x08000000,	/* Read Operation */
2246a081dcdSDavid du Colombier 	MDIready	= 0x10000000,	/* End of Transaction */
2256a081dcdSDavid du Colombier 	MDIie		= 0x20000000,	/* Interrupt Enable */
2266a081dcdSDavid du Colombier 	MDIe		= 0x40000000,	/* Error */
2276a081dcdSDavid du Colombier };
2286a081dcdSDavid du Colombier 
2296a081dcdSDavid du Colombier enum {					/* Icr, Ics, Ims, Imc */
2306a081dcdSDavid du Colombier 	Txdw		= 0x00000001,	/* Transmit Descriptor Written Back */
2316a081dcdSDavid du Colombier 	Txqe		= 0x00000002,	/* Transmit Queue Empty */
2326a081dcdSDavid du Colombier 	Lsc		= 0x00000004,	/* Link Status Change */
2336a081dcdSDavid du Colombier 	Rxseq		= 0x00000008,	/* Receive Sequence Error */
2346a081dcdSDavid du Colombier 	Rxdmt0		= 0x00000010,	/* Rd Minimum Threshold Reached */
2356a081dcdSDavid du Colombier 	Rxo		= 0x00000040,	/* Receiver Overrun */
2366a081dcdSDavid du Colombier 	Rxt0		= 0x00000080,	/* Receiver Timer Interrupt */
2376a081dcdSDavid du Colombier 	Mdac		= 0x00000200,	/* MDIO Access Completed */
2386a081dcdSDavid du Colombier 	Rxcfg		= 0x00000400,	/* Receiving /C/ ordered sets */
2396a081dcdSDavid du Colombier 	Gpi0		= 0x00000800,	/* General Purpose Interrupts */
2406a081dcdSDavid du Colombier 	Gpi1		= 0x00001000,
2416a081dcdSDavid du Colombier 	Gpi2		= 0x00002000,
2426a081dcdSDavid du Colombier 	Gpi3		= 0x00004000,
2436a081dcdSDavid du Colombier };
2446a081dcdSDavid du Colombier 
2456a081dcdSDavid du Colombier /*
2466a081dcdSDavid du Colombier  * The Mdic register isn't implemented on the 82543GC,
2476a081dcdSDavid du Colombier  * the software defined pins are used instead.
2486a081dcdSDavid du Colombier  * These definitions work for the Intel PRO/1000 T Server Adapter.
2496a081dcdSDavid du Colombier  * The direction pin bits are read from the EEPROM.
2506a081dcdSDavid du Colombier  */
2516a081dcdSDavid du Colombier enum {
2526a081dcdSDavid du Colombier 	Mdd		= ((1<<2)<<SwdpinsloSHIFT),	/* data */
2536a081dcdSDavid du Colombier 	Mddo		= ((1<<2)<<SwdpioloSHIFT),	/* pin direction */
2546a081dcdSDavid du Colombier 	Mdc		= ((1<<3)<<SwdpinsloSHIFT),	/* clock */
2556a081dcdSDavid du Colombier 	Mdco		= ((1<<3)<<SwdpioloSHIFT),	/* pin direction */
2566a081dcdSDavid du Colombier 	Mdr		= ((1<<0)<<SwdpinshiSHIFT),	/* reset */
2576a081dcdSDavid du Colombier 	Mdro		= ((1<<0)<<SwdpiohiSHIFT),	/* pin direction */
2586a081dcdSDavid du Colombier };
2596a081dcdSDavid du Colombier 
2606a081dcdSDavid du Colombier enum {					/* Txcw */
2616a081dcdSDavid du Colombier 	TxcwFd		= 0x00000020,	/* Full Duplex */
2626a081dcdSDavid du Colombier 	TxcwHd		= 0x00000040,	/* Half Duplex */
2636a081dcdSDavid du Colombier 	TxcwPauseMASK	= 0x00000180,	/* Pause */
2646a081dcdSDavid du Colombier 	TxcwPauseSHIFT	= 7,
2656a081dcdSDavid du Colombier 	TxcwPs		= (1<<TxcwPauseSHIFT),	/* Pause Supported */
2666a081dcdSDavid du Colombier 	TxcwAs		= (2<<TxcwPauseSHIFT),	/* Asymmetric FC desired */
2676a081dcdSDavid du Colombier 	TxcwRfiMASK	= 0x00003000,	/* Remote Fault Indication */
2686a081dcdSDavid du Colombier 	TxcwRfiSHIFT	= 12,
2696a081dcdSDavid du Colombier 	TxcwNpr		= 0x00008000,	/* Next Page Request */
2706a081dcdSDavid du Colombier 	TxcwConfig	= 0x40000000,	/* Transmit COnfig Control */
2716a081dcdSDavid du Colombier 	TxcwAne		= 0x80000000,	/* Auto-Negotiation Enable */
2726a081dcdSDavid du Colombier };
2736a081dcdSDavid du Colombier 
2746a081dcdSDavid du Colombier enum {					/* Rxcw */
2756a081dcdSDavid du Colombier 	Rxword		= 0x0000FFFF,	/* Data from auto-negotiation process */
2766a081dcdSDavid du Colombier 	Rxnocarrier	= 0x04000000,	/* Carrier Sense indication */
2776a081dcdSDavid du Colombier 	Rxinvalid	= 0x08000000,	/* Invalid Symbol during configuration */
2786a081dcdSDavid du Colombier 	Rxchange	= 0x10000000,	/* Change to the Rxword indication */
2796a081dcdSDavid du Colombier 	Rxconfig	= 0x20000000,	/* /C/ order set reception indication */
2806a081dcdSDavid du Colombier 	Rxsync		= 0x40000000,	/* Lost bit synchronization indication */
2816a081dcdSDavid du Colombier 	Anc		= 0x80000000,	/* Auto Negotiation Complete */
2826a081dcdSDavid du Colombier };
2836a081dcdSDavid du Colombier 
2846a081dcdSDavid du Colombier enum {					/* Rctl */
2856a081dcdSDavid du Colombier 	Rrst		= 0x00000001,	/* Receiver Software Reset */
2866a081dcdSDavid du Colombier 	Ren		= 0x00000002,	/* Receiver Enable */
2876a081dcdSDavid du Colombier 	Sbp		= 0x00000004,	/* Store Bad Packets */
2886a081dcdSDavid du Colombier 	Upe		= 0x00000008,	/* Unicast Promiscuous Enable */
2896a081dcdSDavid du Colombier 	Mpe		= 0x00000010,	/* Multicast Promiscuous Enable */
2906a081dcdSDavid du Colombier 	Lpe		= 0x00000020,	/* Long Packet Reception Enable */
2916a081dcdSDavid du Colombier 	LbmMASK		= 0x000000C0,	/* Loopback Mode */
2926a081dcdSDavid du Colombier 	LbmOFF		= 0x00000000,	/* No Loopback */
2936a081dcdSDavid du Colombier 	LbmTBI		= 0x00000040,	/* TBI Loopback */
2946a081dcdSDavid du Colombier 	LbmMII		= 0x00000080,	/* GMII/MII Loopback */
2956a081dcdSDavid du Colombier 	LbmXCVR		= 0x000000C0,	/* Transceiver Loopback */
2966a081dcdSDavid du Colombier 	RdtmsMASK	= 0x00000300,	/* Rd Minimum Threshold Size */
2976a081dcdSDavid du Colombier 	RdtmsHALF	= 0x00000000,	/* Threshold is 1/2 Rdlen */
2986a081dcdSDavid du Colombier 	RdtmsQUARTER	= 0x00000100,	/* Threshold is 1/4 Rdlen */
2996a081dcdSDavid du Colombier 	RdtmsEIGHTH	= 0x00000200,	/* Threshold is 1/8 Rdlen */
3006a081dcdSDavid du Colombier 	MoMASK		= 0x00003000,	/* Multicast Offset */
3016a081dcdSDavid du Colombier 	Mo47b36		= 0x00000000,	/* bits [47:36] of received address */
3026a081dcdSDavid du Colombier 	Mo46b35		= 0x00001000,	/* bits [46:35] of received address */
3036a081dcdSDavid du Colombier 	Mo45b34		= 0x00002000,	/* bits [45:34] of received address */
3046a081dcdSDavid du Colombier 	Mo43b32		= 0x00003000,	/* bits [43:32] of received address */
3056a081dcdSDavid du Colombier 	Bam		= 0x00008000,	/* Broadcast Accept Mode */
3066a081dcdSDavid du Colombier 	BsizeMASK	= 0x00030000,	/* Receive Buffer Size */
3076a081dcdSDavid du Colombier 	Bsize2048	= 0x00000000,	/* Bsex = 0 */
3086a081dcdSDavid du Colombier 	Bsize1024	= 0x00010000,	/* Bsex = 0 */
3096a081dcdSDavid du Colombier 	Bsize512	= 0x00020000,	/* Bsex = 0 */
3106a081dcdSDavid du Colombier 	Bsize256	= 0x00030000,	/* Bsex = 0 */
3116a081dcdSDavid du Colombier 	Bsize16384	= 0x00010000,	/* Bsex = 1 */
3126a081dcdSDavid du Colombier 	Vfe		= 0x00040000,	/* VLAN Filter Enable */
3136a081dcdSDavid du Colombier 	Cfien		= 0x00080000,	/* Canonical Form Indicator Enable */
3146a081dcdSDavid du Colombier 	Cfi		= 0x00100000,	/* Canonical Form Indicator value */
3156a081dcdSDavid du Colombier 	Dpf		= 0x00400000,	/* Discard Pause Frames */
3166a081dcdSDavid du Colombier 	Pmcf		= 0x00800000,	/* Pass MAC Control Frames */
3176a081dcdSDavid du Colombier 	Bsex		= 0x02000000,	/* Buffer Size Extension */
3186a081dcdSDavid du Colombier 	Secrc		= 0x04000000,	/* Strip CRC from incoming packet */
3196a081dcdSDavid du Colombier };
3206a081dcdSDavid du Colombier 
3216a081dcdSDavid du Colombier enum {					/* Tctl */
3226a081dcdSDavid du Colombier 	Trst		= 0x00000001,	/* Transmitter Software Reset */
3236a081dcdSDavid du Colombier 	Ten		= 0x00000002,	/* Transmit Enable */
3246a081dcdSDavid du Colombier 	Psp		= 0x00000008,	/* Pad Short Packets */
3256a081dcdSDavid du Colombier 	CtMASK		= 0x00000FF0,	/* Collision Threshold */
3266a081dcdSDavid du Colombier 	CtSHIFT		= 4,
3276a081dcdSDavid du Colombier 	ColdMASK	= 0x003FF000,	/* Collision Distance */
3286a081dcdSDavid du Colombier 	ColdSHIFT	= 12,
3296a081dcdSDavid du Colombier 	Swxoff		= 0x00400000,	/* Sofware XOFF Transmission */
3306a081dcdSDavid du Colombier 	Pbe		= 0x00800000,	/* Packet Burst Enable */
3316a081dcdSDavid du Colombier 	Rtlc		= 0x01000000,	/* Re-transmit on Late Collision */
3326a081dcdSDavid du Colombier 	Nrtu		= 0x02000000,	/* No Re-transmit on Underrrun */
3336a081dcdSDavid du Colombier };
3346a081dcdSDavid du Colombier 
3356a081dcdSDavid du Colombier enum {					/* [RT]xdctl */
3366a081dcdSDavid du Colombier 	PthreshMASK	= 0x0000003F,	/* Prefetch Threshold */
3376a081dcdSDavid du Colombier 	PthreshSHIFT	= 0,
3386a081dcdSDavid du Colombier 	HthreshMASK	= 0x00003F00,	/* Host Threshold */
3396a081dcdSDavid du Colombier 	HthreshSHIFT	= 8,
3406a081dcdSDavid du Colombier 	WthreshMASK	= 0x003F0000,	/* Writeback Threshold */
3416a081dcdSDavid du Colombier 	WthreshSHIFT	= 16,
3426a081dcdSDavid du Colombier 	Gran		= 0x01000000,	/* Granularity */
3436a081dcdSDavid du Colombier 	LthreshMASK	= 0xFE000000,	/* Low Threshold */
3446a081dcdSDavid du Colombier 	LthreshSHIFT	= 25,
3456a081dcdSDavid du Colombier };
3466a081dcdSDavid du Colombier 
3476a081dcdSDavid du Colombier enum {					/* Rxcsum */
3486a081dcdSDavid du Colombier 	PcssMASK	= 0x000000FF,	/* Packet Checksum Start */
3496a081dcdSDavid du Colombier 	PcssSHIFT	= 0,
3506a081dcdSDavid du Colombier 	Ipofl		= 0x00000100,	/* IP Checksum Off-load Enable */
3516a081dcdSDavid du Colombier 	Tuofl		= 0x00000200,	/* TCP/UDP Checksum Off-load Enable */
3526a081dcdSDavid du Colombier };
3536a081dcdSDavid du Colombier 
3546a081dcdSDavid du Colombier enum {					/* Manc */
3556a081dcdSDavid du Colombier 	Arpen		= 0x00002000,	/* Enable ARP Request Filtering */
3566a081dcdSDavid du Colombier };
3576a081dcdSDavid du Colombier 
3586a081dcdSDavid du Colombier enum {					/* Receive Delay Timer Ring */
3596a081dcdSDavid du Colombier 	DelayMASK	= 0x0000FFFF,	/* delay timer in 1.024nS increments */
3606a081dcdSDavid du Colombier 	DelaySHIFT	= 0,
3616a081dcdSDavid du Colombier 	Fpd		= 0x80000000,	/* Flush partial Descriptor Block */
3626a081dcdSDavid du Colombier };
3636a081dcdSDavid du Colombier 
3646a081dcdSDavid du Colombier typedef struct Rd {			/* Receive Descriptor */
3656a081dcdSDavid du Colombier 	uint	addr[2];
3666a081dcdSDavid du Colombier 	ushort	length;
3676a081dcdSDavid du Colombier 	ushort	checksum;
3686a081dcdSDavid du Colombier 	uchar	status;
3696a081dcdSDavid du Colombier 	uchar	errors;
3706a081dcdSDavid du Colombier 	ushort	special;
3716a081dcdSDavid du Colombier } Rd;
3726a081dcdSDavid du Colombier 
3736a081dcdSDavid du Colombier enum {					/* Rd status */
3746a081dcdSDavid du Colombier 	Rdd		= 0x01,		/* Descriptor Done */
3756a081dcdSDavid du Colombier 	Reop		= 0x02,		/* End of Packet */
3766a081dcdSDavid du Colombier 	Ixsm		= 0x04,		/* Ignore Checksum Indication */
3776a081dcdSDavid du Colombier 	Vp		= 0x08,		/* Packet is 802.1Q (matched VET) */
3786a081dcdSDavid du Colombier 	Tcpcs		= 0x20,		/* TCP Checksum Calculated on Packet */
3796a081dcdSDavid du Colombier 	Ipcs		= 0x40,		/* IP Checksum Calculated on Packet */
3806a081dcdSDavid du Colombier 	Pif		= 0x80,		/* Passed in-exact filter */
3816a081dcdSDavid du Colombier };
3826a081dcdSDavid du Colombier 
3836a081dcdSDavid du Colombier enum {					/* Rd errors */
3846a081dcdSDavid du Colombier 	Ce		= 0x01,		/* CRC Error or Alignment Error */
3856a081dcdSDavid du Colombier 	Se		= 0x02,		/* Symbol Error */
3866a081dcdSDavid du Colombier 	Seq		= 0x04,		/* Sequence Error */
3876a081dcdSDavid du Colombier 	Cxe		= 0x10,		/* Carrier Extension Error */
3886a081dcdSDavid du Colombier 	Tcpe		= 0x20,		/* TCP/UDP Checksum Error */
3896a081dcdSDavid du Colombier 	Ipe		= 0x40,		/* IP Checksum Error */
3906a081dcdSDavid du Colombier 	Rxe		= 0x80,		/* RX Data Error */
3916a081dcdSDavid du Colombier };
3926a081dcdSDavid du Colombier 
39359c21d95SDavid du Colombier typedef struct Td Td;
39459c21d95SDavid du Colombier struct Td {				/* Transmit Descriptor */
3956a081dcdSDavid du Colombier 	union {
3966a081dcdSDavid du Colombier 		uint	addr[2];	/* Data */
3976a081dcdSDavid du Colombier 		struct {		/* Context */
3986a081dcdSDavid du Colombier 			uchar	ipcss;
3996a081dcdSDavid du Colombier 			uchar	ipcso;
4006a081dcdSDavid du Colombier 			ushort	ipcse;
4016a081dcdSDavid du Colombier 			uchar	tucss;
4026a081dcdSDavid du Colombier 			uchar	tucso;
4036a081dcdSDavid du Colombier 			ushort	tucse;
4046a081dcdSDavid du Colombier 		};
4056a081dcdSDavid du Colombier 	};
4066a081dcdSDavid du Colombier 	uint	control;
4076a081dcdSDavid du Colombier 	uint	status;
40859c21d95SDavid du Colombier };
4096a081dcdSDavid du Colombier 
4106a081dcdSDavid du Colombier enum {					/* Td control */
4116a081dcdSDavid du Colombier 	LenMASK		= 0x000FFFFF,	/* Data/Packet Length Field */
4126a081dcdSDavid du Colombier 	LenSHIFT	= 0,
4136a081dcdSDavid du Colombier 	DtypeCD		= 0x00000000,	/* Data Type 'Context Descriptor' */
4146a081dcdSDavid du Colombier 	DtypeDD		= 0x00100000,	/* Data Type 'Data Descriptor' */
4156a081dcdSDavid du Colombier 	PtypeTCP	= 0x01000000,	/* TCP/UDP Packet Type (CD) */
4166a081dcdSDavid du Colombier 	Teop		= 0x01000000,	/* End of Packet (DD) */
4176a081dcdSDavid du Colombier 	PtypeIP		= 0x02000000,	/* IP Packet Type (CD) */
4186a081dcdSDavid du Colombier 	Ifcs		= 0x02000000,	/* Insert FCS (DD) */
4196a081dcdSDavid du Colombier 	Tse		= 0x04000000,	/* TCP Segmentation Enable */
4206a081dcdSDavid du Colombier 	Rs		= 0x08000000,	/* Report Status */
4216a081dcdSDavid du Colombier 	Rps		= 0x10000000,	/* Report Status Sent */
4226a081dcdSDavid du Colombier 	Dext		= 0x20000000,	/* Descriptor Extension */
4236a081dcdSDavid du Colombier 	Vle		= 0x40000000,	/* VLAN Packet Enable */
4246a081dcdSDavid du Colombier 	Ide		= 0x80000000,	/* Interrupt Delay Enable */
4256a081dcdSDavid du Colombier };
4266a081dcdSDavid du Colombier 
4276a081dcdSDavid du Colombier enum {					/* Td status */
4286a081dcdSDavid du Colombier 	Tdd		= 0x00000001,	/* Descriptor Done */
4296a081dcdSDavid du Colombier 	Ec		= 0x00000002,	/* Excess Collisions */
4306a081dcdSDavid du Colombier 	Lc		= 0x00000004,	/* Late Collision */
4316a081dcdSDavid du Colombier 	Tu		= 0x00000008,	/* Transmit Underrun */
4326a081dcdSDavid du Colombier 	Iixsm		= 0x00000100,	/* Insert IP Checksum */
4336a081dcdSDavid du Colombier 	Itxsm		= 0x00000200,	/* Insert TCP/UDP Checksum */
4346a081dcdSDavid du Colombier 	HdrlenMASK	= 0x0000FF00,	/* Header Length (Tse) */
4356a081dcdSDavid du Colombier 	HdrlenSHIFT	= 8,
4366a081dcdSDavid du Colombier 	VlanMASK	= 0x0FFF0000,	/* VLAN Identifier */
4376a081dcdSDavid du Colombier 	VlanSHIFT	= 16,
4386a081dcdSDavid du Colombier 	Tcfi		= 0x10000000,	/* Canonical Form Indicator */
4396a081dcdSDavid du Colombier 	PriMASK		= 0xE0000000,	/* User Priority */
4406a081dcdSDavid du Colombier 	PriSHIFT	= 29,
4416a081dcdSDavid du Colombier 	MssMASK		= 0xFFFF0000,	/* Maximum Segment Size (Tse) */
4426a081dcdSDavid du Colombier 	MssSHIFT	= 16,
4436a081dcdSDavid du Colombier };
4446a081dcdSDavid du Colombier 
4456a081dcdSDavid du Colombier enum {
4466a081dcdSDavid du Colombier 	Rbsz		= 2048,
447588d0145SDavid du Colombier 	/* were 256, 1024 & 64, but 52, 253 and 9 are ample. */
448588d0145SDavid du Colombier 	Nrd		= 128,		/* multiple of 8 */
449*cebd3b46SDavid du Colombier 	Nrb		= 512,		/* private receive buffers per Ctlr */
450588d0145SDavid du Colombier 	Ntd		= 32,		/* multiple of 8 */
4516a081dcdSDavid du Colombier };
4526a081dcdSDavid du Colombier 
4536a081dcdSDavid du Colombier typedef struct Ctlr Ctlr;
454c02f0a41SDavid du Colombier struct Ctlr {
4558466d066SDavid du Colombier 	int	port;
4566a081dcdSDavid du Colombier 	Pcidev*	pcidev;
4576a081dcdSDavid du Colombier 	Ctlr*	next;
458208510e1SDavid du Colombier 	Ether*	edev;
4596a081dcdSDavid du Colombier 	int	active;
4606a081dcdSDavid du Colombier 	int	started;
4616a081dcdSDavid du Colombier 	int	id;
4626a081dcdSDavid du Colombier 	int	cls;
4636a081dcdSDavid du Colombier 	ushort	eeprom[0x40];
4646a081dcdSDavid du Colombier 
4656a081dcdSDavid du Colombier 	QLock	alock;			/* attach */
4666a081dcdSDavid du Colombier 	void*	alloc;			/* receive/transmit descriptors */
4676a081dcdSDavid du Colombier 	int	nrd;
4686a081dcdSDavid du Colombier 	int	ntd;
469c02f0a41SDavid du Colombier 	int	nrb;			/* # bufs this Ctlr has in the pool */
4706a081dcdSDavid du Colombier 
4716a081dcdSDavid du Colombier 	int*	nic;
4726a081dcdSDavid du Colombier 	Lock	imlock;
4736a081dcdSDavid du Colombier 	int	im;			/* interrupt mask */
4746a081dcdSDavid du Colombier 
4756a081dcdSDavid du Colombier 	Mii*	mii;
4766a081dcdSDavid du Colombier 	Rendez	lrendez;
4776a081dcdSDavid du Colombier 	int	lim;
4786a081dcdSDavid du Colombier 
4796a081dcdSDavid du Colombier 	int	link;
4806a081dcdSDavid du Colombier 
481217e9e83SDavid du Colombier 	Watermark wmrb;
482217e9e83SDavid du Colombier 	Watermark wmrd;
483217e9e83SDavid du Colombier 	Watermark wmtd;
484217e9e83SDavid du Colombier 
4856a081dcdSDavid du Colombier 	QLock	slock;
4866a081dcdSDavid du Colombier 	uint	statistics[Nstatistics];
4876a081dcdSDavid du Colombier 	uint	lsleep;
4886a081dcdSDavid du Colombier 	uint	lintr;
4896a081dcdSDavid du Colombier 	uint	rsleep;
4906a081dcdSDavid du Colombier 	uint	rintr;
4916a081dcdSDavid du Colombier 	uint	txdw;
4926a081dcdSDavid du Colombier 	uint	tintr;
4936a081dcdSDavid du Colombier 	uint	ixsm;
4946a081dcdSDavid du Colombier 	uint	ipcs;
4956a081dcdSDavid du Colombier 	uint	tcpcs;
4966a081dcdSDavid du Colombier 
4976a081dcdSDavid du Colombier 	uchar	ra[Eaddrlen];		/* receive address */
4986a081dcdSDavid du Colombier 	ulong	mta[128];		/* multicast table array */
4996a081dcdSDavid du Colombier 
5006a081dcdSDavid du Colombier 	Rendez	rrendez;
5016a081dcdSDavid du Colombier 	int	rim;
502c02f0a41SDavid du Colombier 	int	rdfree;			/* rx descriptors awaiting packets */
5036a081dcdSDavid du Colombier 	Rd*	rdba;			/* receive descriptor base address */
5046a081dcdSDavid du Colombier 	Block**	rb;			/* receive buffers */
5056a081dcdSDavid du Colombier 	int	rdh;			/* receive descriptor head */
5066a081dcdSDavid du Colombier 	int	rdt;			/* receive descriptor tail */
5076a081dcdSDavid du Colombier 	int	rdtr;			/* receive delay timer ring value */
5086a081dcdSDavid du Colombier 
5096a081dcdSDavid du Colombier 	Lock	tlock;
5106a081dcdSDavid du Colombier 	int	tdfree;
5116a081dcdSDavid du Colombier 	Td*	tdba;			/* transmit descriptor base address */
5126a081dcdSDavid du Colombier 	Block**	tb;			/* transmit buffers */
5136a081dcdSDavid du Colombier 	int	tdh;			/* transmit descriptor head */
5146a081dcdSDavid du Colombier 	int	tdt;			/* transmit descriptor tail */
5156a081dcdSDavid du Colombier 
5166a081dcdSDavid du Colombier 	int	txcw;
5176a081dcdSDavid du Colombier 	int	fcrtl;
5186a081dcdSDavid du Colombier 	int	fcrth;
519c02f0a41SDavid du Colombier };
5206a081dcdSDavid du Colombier 
5216a081dcdSDavid du Colombier #define csr32r(c, r)	(*((c)->nic+((r)/4)))
5226a081dcdSDavid du Colombier #define csr32w(c, r, v)	(*((c)->nic+((r)/4)) = (v))
5236a081dcdSDavid du Colombier 
5246a081dcdSDavid du Colombier static Ctlr* igbectlrhead;
5256a081dcdSDavid du Colombier static Ctlr* igbectlrtail;
5266a081dcdSDavid du Colombier 
5276a081dcdSDavid du Colombier static Lock igberblock;		/* free receive Blocks */
528208510e1SDavid du Colombier static Block* igberbpool;	/* receive Blocks for all igbe controllers */
529217e9e83SDavid du Colombier static int nrbfull;	/* # of rcv Blocks with data awaiting processing */
5306a081dcdSDavid du Colombier 
5316a081dcdSDavid du Colombier static char* statistics[Nstatistics] = {
5326a081dcdSDavid du Colombier 	"CRC Error",
5336a081dcdSDavid du Colombier 	"Alignment Error",
5346a081dcdSDavid du Colombier 	"Symbol Error",
5356a081dcdSDavid du Colombier 	"RX Error",
5366a081dcdSDavid du Colombier 	"Missed Packets",
5376a081dcdSDavid du Colombier 	"Single Collision",
5386a081dcdSDavid du Colombier 	"Excessive Collisions",
5396a081dcdSDavid du Colombier 	"Multiple Collision",
5406a081dcdSDavid du Colombier 	"Late Collisions",
5416a081dcdSDavid du Colombier 	nil,
5426a081dcdSDavid du Colombier 	"Collision",
5436a081dcdSDavid du Colombier 	"Transmit Underrun",
5446a081dcdSDavid du Colombier 	"Defer",
5456a081dcdSDavid du Colombier 	"Transmit - No CRS",
5466a081dcdSDavid du Colombier 	"Sequence Error",
5476a081dcdSDavid du Colombier 	"Carrier Extension Error",
5486a081dcdSDavid du Colombier 	"Receive Error Length",
5496a081dcdSDavid du Colombier 	nil,
5506a081dcdSDavid du Colombier 	"XON Received",
5516a081dcdSDavid du Colombier 	"XON Transmitted",
5526a081dcdSDavid du Colombier 	"XOFF Received",
5536a081dcdSDavid du Colombier 	"XOFF Transmitted",
5546a081dcdSDavid du Colombier 	"FC Received Unsupported",
5556a081dcdSDavid du Colombier 	"Packets Received (64 Bytes)",
5566a081dcdSDavid du Colombier 	"Packets Received (65-127 Bytes)",
5576a081dcdSDavid du Colombier 	"Packets Received (128-255 Bytes)",
5586a081dcdSDavid du Colombier 	"Packets Received (256-511 Bytes)",
5596a081dcdSDavid du Colombier 	"Packets Received (512-1023 Bytes)",
5606a081dcdSDavid du Colombier 	"Packets Received (1024-1522 Bytes)",
5616a081dcdSDavid du Colombier 	"Good Packets Received",
5626a081dcdSDavid du Colombier 	"Broadcast Packets Received",
5636a081dcdSDavid du Colombier 	"Multicast Packets Received",
5646a081dcdSDavid du Colombier 	"Good Packets Transmitted",
5656a081dcdSDavid du Colombier 	nil,
5666a081dcdSDavid du Colombier 	"Good Octets Received",
5676a081dcdSDavid du Colombier 	nil,
5686a081dcdSDavid du Colombier 	"Good Octets Transmitted",
5696a081dcdSDavid du Colombier 	nil,
5706a081dcdSDavid du Colombier 	nil,
5716a081dcdSDavid du Colombier 	nil,
5726a081dcdSDavid du Colombier 	"Receive No Buffers",
5736a081dcdSDavid du Colombier 	"Receive Undersize",
5746a081dcdSDavid du Colombier 	"Receive Fragment",
5756a081dcdSDavid du Colombier 	"Receive Oversize",
5766a081dcdSDavid du Colombier 	"Receive Jabber",
5776a081dcdSDavid du Colombier 	nil,
5786a081dcdSDavid du Colombier 	nil,
5796a081dcdSDavid du Colombier 	nil,
5806a081dcdSDavid du Colombier 	"Total Octets Received",
5816a081dcdSDavid du Colombier 	nil,
5826a081dcdSDavid du Colombier 	"Total Octets Transmitted",
5836a081dcdSDavid du Colombier 	nil,
5846a081dcdSDavid du Colombier 	"Total Packets Received",
5856a081dcdSDavid du Colombier 	"Total Packets Transmitted",
5866a081dcdSDavid du Colombier 	"Packets Transmitted (64 Bytes)",
5876a081dcdSDavid du Colombier 	"Packets Transmitted (65-127 Bytes)",
5886a081dcdSDavid du Colombier 	"Packets Transmitted (128-255 Bytes)",
5896a081dcdSDavid du Colombier 	"Packets Transmitted (256-511 Bytes)",
5906a081dcdSDavid du Colombier 	"Packets Transmitted (512-1023 Bytes)",
5916a081dcdSDavid du Colombier 	"Packets Transmitted (1024-1522 Bytes)",
5926a081dcdSDavid du Colombier 	"Multicast Packets Transmitted",
5936a081dcdSDavid du Colombier 	"Broadcast Packets Transmitted",
5946a081dcdSDavid du Colombier 	"TCP Segmentation Context Transmitted",
5956a081dcdSDavid du Colombier 	"TCP Segmentation Context Fail",
5966a081dcdSDavid du Colombier };
5976a081dcdSDavid du Colombier 
5986a081dcdSDavid du Colombier static long
igbeifstat(Ether * edev,void * a,long n,ulong offset)5996a081dcdSDavid du Colombier igbeifstat(Ether* edev, void* a, long n, ulong offset)
6006a081dcdSDavid du Colombier {
6016a081dcdSDavid du Colombier 	Ctlr *ctlr;
602217e9e83SDavid du Colombier 	char *p, *s, *e;
6036a081dcdSDavid du Colombier 	int i, l, r;
6046a081dcdSDavid du Colombier 	uvlong tuvl, ruvl;
6056a081dcdSDavid du Colombier 
6066a081dcdSDavid du Colombier 	ctlr = edev->ctlr;
6076a081dcdSDavid du Colombier 	qlock(&ctlr->slock);
60846136019SDavid du Colombier 	p = malloc(READSTR);
609aa72973aSDavid du Colombier 	if(p == nil) {
610aa72973aSDavid du Colombier 		qunlock(&ctlr->slock);
611aa72973aSDavid du Colombier 		error(Enomem);
612aa72973aSDavid du Colombier 	}
6136a081dcdSDavid du Colombier 	l = 0;
6146a081dcdSDavid du Colombier 	for(i = 0; i < Nstatistics; i++){
6156a081dcdSDavid du Colombier 		r = csr32r(ctlr, Statistics+i*4);
6166a081dcdSDavid du Colombier 		if((s = statistics[i]) == nil)
6176a081dcdSDavid du Colombier 			continue;
6186a081dcdSDavid du Colombier 		switch(i){
6196a081dcdSDavid du Colombier 		case Gorcl:
6206a081dcdSDavid du Colombier 		case Gotcl:
6216a081dcdSDavid du Colombier 		case Torl:
6226a081dcdSDavid du Colombier 		case Totl:
6236a081dcdSDavid du Colombier 			ruvl = r;
6246a081dcdSDavid du Colombier 			ruvl += ((uvlong)csr32r(ctlr, Statistics+(i+1)*4))<<32;
6256a081dcdSDavid du Colombier 			tuvl = ruvl;
6266a081dcdSDavid du Colombier 			tuvl += ctlr->statistics[i];
6276a081dcdSDavid du Colombier 			tuvl += ((uvlong)ctlr->statistics[i+1])<<32;
6286a081dcdSDavid du Colombier 			if(tuvl == 0)
6296a081dcdSDavid du Colombier 				continue;
6306a081dcdSDavid du Colombier 			ctlr->statistics[i] = tuvl;
6316a081dcdSDavid du Colombier 			ctlr->statistics[i+1] = tuvl>>32;
63246136019SDavid du Colombier 			l += snprint(p+l, READSTR-l, "%s: %llud %llud\n",
6336a081dcdSDavid du Colombier 				s, tuvl, ruvl);
6346a081dcdSDavid du Colombier 			i++;
6356a081dcdSDavid du Colombier 			break;
6366a081dcdSDavid du Colombier 
6376a081dcdSDavid du Colombier 		default:
6386a081dcdSDavid du Colombier 			ctlr->statistics[i] += r;
6396a081dcdSDavid du Colombier 			if(ctlr->statistics[i] == 0)
6406a081dcdSDavid du Colombier 				continue;
64146136019SDavid du Colombier 			l += snprint(p+l, READSTR-l, "%s: %ud %ud\n",
6426a081dcdSDavid du Colombier 				s, ctlr->statistics[i], r);
6436a081dcdSDavid du Colombier 			break;
6446a081dcdSDavid du Colombier 		}
6456a081dcdSDavid du Colombier 	}
6466a081dcdSDavid du Colombier 
64746136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "lintr: %ud %ud\n",
6486a081dcdSDavid du Colombier 		ctlr->lintr, ctlr->lsleep);
64946136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "rintr: %ud %ud\n",
6506a081dcdSDavid du Colombier 		ctlr->rintr, ctlr->rsleep);
65146136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "tintr: %ud %ud\n",
6526a081dcdSDavid du Colombier 		ctlr->tintr, ctlr->txdw);
65346136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "ixcs: %ud %ud %ud\n",
6546a081dcdSDavid du Colombier 		ctlr->ixsm, ctlr->ipcs, ctlr->tcpcs);
65546136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "rdtr: %ud\n", ctlr->rdtr);
65646136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "Ctrlext: %08x\n", csr32r(ctlr, Ctrlext));
6576a081dcdSDavid du Colombier 
65846136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "eeprom:");
6596a081dcdSDavid du Colombier 	for(i = 0; i < 0x40; i++){
6606a081dcdSDavid du Colombier 		if(i && ((i & 0x07) == 0))
66146136019SDavid du Colombier 			l += snprint(p+l, READSTR-l, "\n       ");
66246136019SDavid du Colombier 		l += snprint(p+l, READSTR-l, " %4.4uX", ctlr->eeprom[i]);
6636a081dcdSDavid du Colombier 	}
66446136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "\n");
6656a081dcdSDavid du Colombier 
6666a081dcdSDavid du Colombier 	if(ctlr->mii != nil && ctlr->mii->curphy != nil){
667923c9daaSDavid du Colombier 		l += snprint(p+l, READSTR-l, "phy:   ");
6686a081dcdSDavid du Colombier 		for(i = 0; i < NMiiPhyr; i++){
6696a081dcdSDavid du Colombier 			if(i && ((i & 0x07) == 0))
67046136019SDavid du Colombier 				l += snprint(p+l, READSTR-l, "\n       ");
6716a081dcdSDavid du Colombier 			r = miimir(ctlr->mii, i);
67246136019SDavid du Colombier 			l += snprint(p+l, READSTR-l, " %4.4uX", r);
6736a081dcdSDavid du Colombier 		}
67446136019SDavid du Colombier 		snprint(p+l, READSTR-l, "\n");
6756a081dcdSDavid du Colombier 	}
676217e9e83SDavid du Colombier 	e = p + READSTR;
677217e9e83SDavid du Colombier 	s = p + l + 1;
678217e9e83SDavid du Colombier 	s = seprintmark(s, e, &ctlr->wmrb);
679217e9e83SDavid du Colombier 	s = seprintmark(s, e, &ctlr->wmrd);
680217e9e83SDavid du Colombier 	s = seprintmark(s, e, &ctlr->wmtd);
681217e9e83SDavid du Colombier 	USED(s);
682217e9e83SDavid du Colombier 
6836a081dcdSDavid du Colombier 	n = readstr(offset, a, n, p);
6846a081dcdSDavid du Colombier 	free(p);
6856a081dcdSDavid du Colombier 	qunlock(&ctlr->slock);
6866a081dcdSDavid du Colombier 
6876a081dcdSDavid du Colombier 	return n;
6886a081dcdSDavid du Colombier }
6896a081dcdSDavid du Colombier 
6906a081dcdSDavid du Colombier enum {
6916a081dcdSDavid du Colombier 	CMrdtr,
6926a081dcdSDavid du Colombier };
6936a081dcdSDavid du Colombier 
6946a081dcdSDavid du Colombier static Cmdtab igbectlmsg[] = {
6956a081dcdSDavid du Colombier 	CMrdtr,	"rdtr",	2,
6966a081dcdSDavid du Colombier };
6976a081dcdSDavid du Colombier 
6986a081dcdSDavid du Colombier static long
igbectl(Ether * edev,void * buf,long n)6996a081dcdSDavid du Colombier igbectl(Ether* edev, void* buf, long n)
7006a081dcdSDavid du Colombier {
7016a081dcdSDavid du Colombier 	int v;
7026a081dcdSDavid du Colombier 	char *p;
7036a081dcdSDavid du Colombier 	Ctlr *ctlr;
7046a081dcdSDavid du Colombier 	Cmdbuf *cb;
7056a081dcdSDavid du Colombier 	Cmdtab *ct;
7066a081dcdSDavid du Colombier 
7076a081dcdSDavid du Colombier 	if((ctlr = edev->ctlr) == nil)
7086a081dcdSDavid du Colombier 		error(Enonexist);
7096a081dcdSDavid du Colombier 
7106a081dcdSDavid du Colombier 	cb = parsecmd(buf, n);
7116a081dcdSDavid du Colombier 	if(waserror()){
7126a081dcdSDavid du Colombier 		free(cb);
7136a081dcdSDavid du Colombier 		nexterror();
7146a081dcdSDavid du Colombier 	}
7156a081dcdSDavid du Colombier 
7166a081dcdSDavid du Colombier 	ct = lookupcmd(cb, igbectlmsg, nelem(igbectlmsg));
7176a081dcdSDavid du Colombier 	switch(ct->index){
7186a081dcdSDavid du Colombier 	case CMrdtr:
7196a081dcdSDavid du Colombier 		v = strtol(cb->f[1], &p, 0);
7206a081dcdSDavid du Colombier 		if(v < 0 || p == cb->f[1] || v > 0xFFFF)
7216a081dcdSDavid du Colombier 			error(Ebadarg);
7226520663fSDavid du Colombier 		ctlr->rdtr = v;
7236a081dcdSDavid du Colombier 		csr32w(ctlr, Rdtr, Fpd|v);
7246a081dcdSDavid du Colombier 		break;
7256a081dcdSDavid du Colombier 	}
7266a081dcdSDavid du Colombier 	free(cb);
7276a081dcdSDavid du Colombier 	poperror();
7286a081dcdSDavid du Colombier 
7296a081dcdSDavid du Colombier 	return n;
7306a081dcdSDavid du Colombier }
7316a081dcdSDavid du Colombier 
7326a081dcdSDavid du Colombier static void
igbepromiscuous(void * arg,int on)7336a081dcdSDavid du Colombier igbepromiscuous(void* arg, int on)
7346a081dcdSDavid du Colombier {
7356a081dcdSDavid du Colombier 	int rctl;
7366a081dcdSDavid du Colombier 	Ctlr *ctlr;
7376a081dcdSDavid du Colombier 	Ether *edev;
7386a081dcdSDavid du Colombier 
7396a081dcdSDavid du Colombier 	edev = arg;
7406a081dcdSDavid du Colombier 	ctlr = edev->ctlr;
7416a081dcdSDavid du Colombier 
7426a081dcdSDavid du Colombier 	rctl = csr32r(ctlr, Rctl);
7436a081dcdSDavid du Colombier 	rctl &= ~MoMASK;
7446a081dcdSDavid du Colombier 	rctl |= Mo47b36;
7456a081dcdSDavid du Colombier 	if(on)
7466a081dcdSDavid du Colombier 		rctl |= Upe|Mpe;
7476a081dcdSDavid du Colombier 	else
7486a081dcdSDavid du Colombier 		rctl &= ~(Upe|Mpe);
749b1707c5dSDavid du Colombier 	csr32w(ctlr, Rctl, rctl|Mpe);	/* temporarily keep Mpe on */
7506a081dcdSDavid du Colombier }
7516a081dcdSDavid du Colombier 
752ecea9424SDavid du Colombier static void
igbemulticast(void * arg,uchar * addr,int add)753b1707c5dSDavid du Colombier igbemulticast(void* arg, uchar* addr, int add)
754ecea9424SDavid du Colombier {
755ecea9424SDavid du Colombier 	int bit, x;
756ecea9424SDavid du Colombier 	Ctlr *ctlr;
757ecea9424SDavid du Colombier 	Ether *edev;
758ecea9424SDavid du Colombier 
759ecea9424SDavid du Colombier 	edev = arg;
760ecea9424SDavid du Colombier 	ctlr = edev->ctlr;
761ecea9424SDavid du Colombier 
762ecea9424SDavid du Colombier 	x = addr[5]>>1;
763ecea9424SDavid du Colombier 	bit = ((addr[5] & 1)<<4)|(addr[4]>>4);
764b1707c5dSDavid du Colombier 	/*
765b1707c5dSDavid du Colombier 	 * multiple ether addresses can hash to the same filter bit,
766b1707c5dSDavid du Colombier 	 * so it's never safe to clear a filter bit.
767b1707c5dSDavid du Colombier 	 * if we want to clear filter bits, we need to keep track of
768b1707c5dSDavid du Colombier 	 * all the multicast addresses in use, clear all the filter bits,
769b1707c5dSDavid du Colombier 	 * then set the ones corresponding to in-use addresses.
770b1707c5dSDavid du Colombier 	 */
771b1707c5dSDavid du Colombier 	if(add)
772ecea9424SDavid du Colombier 		ctlr->mta[x] |= 1<<bit;
773b1707c5dSDavid du Colombier //	else
774b1707c5dSDavid du Colombier //		ctlr->mta[x] &= ~(1<<bit);
775ecea9424SDavid du Colombier 
776ecea9424SDavid du Colombier 	csr32w(ctlr, Mta+x*4, ctlr->mta[x]);
777ecea9424SDavid du Colombier }
778ecea9424SDavid du Colombier 
7796a081dcdSDavid du Colombier static Block*
igberballoc(void)7806a081dcdSDavid du Colombier igberballoc(void)
7816a081dcdSDavid du Colombier {
7826a081dcdSDavid du Colombier 	Block *bp;
7836a081dcdSDavid du Colombier 
7846a081dcdSDavid du Colombier 	ilock(&igberblock);
7856a081dcdSDavid du Colombier 	if((bp = igberbpool) != nil){
7866a081dcdSDavid du Colombier 		igberbpool = bp->next;
7876a081dcdSDavid du Colombier 		bp->next = nil;
788b8a11165SDavid du Colombier 		_xinc(&bp->ref);	/* prevent bp from being freed */
7896a081dcdSDavid du Colombier 	}
7906a081dcdSDavid du Colombier 	iunlock(&igberblock);
7916a081dcdSDavid du Colombier 
7926a081dcdSDavid du Colombier 	return bp;
7936a081dcdSDavid du Colombier }
7946a081dcdSDavid du Colombier 
7956a081dcdSDavid du Colombier static void
igberbfree(Block * bp)7966a081dcdSDavid du Colombier igberbfree(Block* bp)
7976a081dcdSDavid du Colombier {
798ff8c3af2SDavid du Colombier 	bp->rp = bp->lim - Rbsz;
799ff8c3af2SDavid du Colombier 	bp->wp = bp->rp;
800bfb6eab9SDavid du Colombier  	bp->flag &= ~(Bipck | Budpck | Btcpck | Bpktck);
801ff8c3af2SDavid du Colombier 
8026a081dcdSDavid du Colombier 	ilock(&igberblock);
8036a081dcdSDavid du Colombier 	bp->next = igberbpool;
8046a081dcdSDavid du Colombier 	igberbpool = bp;
805217e9e83SDavid du Colombier 	nrbfull--;
8066a081dcdSDavid du Colombier 	iunlock(&igberblock);
8076a081dcdSDavid du Colombier }
8086a081dcdSDavid du Colombier 
8096a081dcdSDavid du Colombier static void
igbeim(Ctlr * ctlr,int im)8106a081dcdSDavid du Colombier igbeim(Ctlr* ctlr, int im)
8116a081dcdSDavid du Colombier {
8126a081dcdSDavid du Colombier 	ilock(&ctlr->imlock);
8136a081dcdSDavid du Colombier 	ctlr->im |= im;
8146a081dcdSDavid du Colombier 	csr32w(ctlr, Ims, ctlr->im);
8156a081dcdSDavid du Colombier 	iunlock(&ctlr->imlock);
8166a081dcdSDavid du Colombier }
8176a081dcdSDavid du Colombier 
8186a081dcdSDavid du Colombier static int
igbelim(void * ctlr)8196a081dcdSDavid du Colombier igbelim(void* ctlr)
8206a081dcdSDavid du Colombier {
8216a081dcdSDavid du Colombier 	return ((Ctlr*)ctlr)->lim != 0;
8226a081dcdSDavid du Colombier }
8236a081dcdSDavid du Colombier 
8246a081dcdSDavid du Colombier static void
igbelproc(void * arg)825ff8c3af2SDavid du Colombier igbelproc(void* arg)
8266a081dcdSDavid du Colombier {
8276a081dcdSDavid du Colombier 	Ctlr *ctlr;
8286a081dcdSDavid du Colombier 	Ether *edev;
8296a081dcdSDavid du Colombier 	MiiPhy *phy;
8306a081dcdSDavid du Colombier 	int ctrl, r;
8316a081dcdSDavid du Colombier 
832ff8c3af2SDavid du Colombier 	edev = arg;
8336a081dcdSDavid du Colombier 	ctlr = edev->ctlr;
8346a081dcdSDavid du Colombier 	for(;;){
835bf3a53f8SDavid du Colombier 		if(ctlr->mii == nil || ctlr->mii->curphy == nil) {
836bf3a53f8SDavid du Colombier 			sched();
8376a081dcdSDavid du Colombier 			continue;
838bf3a53f8SDavid du Colombier 		}
8396a081dcdSDavid du Colombier 
8406a081dcdSDavid du Colombier 		/*
8416a081dcdSDavid du Colombier 		 * To do:
8426a081dcdSDavid du Colombier 		 *	logic to manage status change,
8436a081dcdSDavid du Colombier 		 *	this is incomplete but should work
8446a081dcdSDavid du Colombier 		 *	one time to set up the hardware.
8456a081dcdSDavid du Colombier 		 *
8466a081dcdSDavid du Colombier 		 *	MiiPhy.speed, etc. should be in Mii.
8476a081dcdSDavid du Colombier 		 */
8486a081dcdSDavid du Colombier 		if(miistatus(ctlr->mii) < 0)
8496a081dcdSDavid du Colombier 			//continue;
8506a081dcdSDavid du Colombier 			goto enable;
8516a081dcdSDavid du Colombier 
8526a081dcdSDavid du Colombier 		phy = ctlr->mii->curphy;
8536a081dcdSDavid du Colombier 		ctrl = csr32r(ctlr, Ctrl);
8546a081dcdSDavid du Colombier 
8556a081dcdSDavid du Colombier 		switch(ctlr->id){
856aeb1c8a5SDavid du Colombier 		case i82543gc:
857aeb1c8a5SDavid du Colombier 		case i82544ei:
85807c32395SDavid du Colombier 		case i82544eif:
8596a081dcdSDavid du Colombier 		default:
8606a081dcdSDavid du Colombier 			if(!(ctrl & Asde)){
8616a081dcdSDavid du Colombier 				ctrl &= ~(SspeedMASK|Ilos|Fd);
8626a081dcdSDavid du Colombier 				ctrl |= Frcdplx|Frcspd;
8636a081dcdSDavid du Colombier 				if(phy->speed == 1000)
8646a081dcdSDavid du Colombier 					ctrl |= Sspeed1000;
8656a081dcdSDavid du Colombier 				else if(phy->speed == 100)
8666a081dcdSDavid du Colombier 					ctrl |= Sspeed100;
8676a081dcdSDavid du Colombier 				if(phy->fd)
8686a081dcdSDavid du Colombier 					ctrl |= Fd;
8696a081dcdSDavid du Colombier 			}
8706a081dcdSDavid du Colombier 			break;
871aeb1c8a5SDavid du Colombier 
872aeb1c8a5SDavid du Colombier 		case i82540em:
873aeb1c8a5SDavid du Colombier 		case i82540eplp:
874aeb1c8a5SDavid du Colombier 		case i82547gi:
875aeb1c8a5SDavid du Colombier 		case i82541gi:
8768466d066SDavid du Colombier 		case i82541gi2:
877d717568cSDavid du Colombier 		case i82541pi:
8786a081dcdSDavid du Colombier 			break;
8796a081dcdSDavid du Colombier 		}
8806a081dcdSDavid du Colombier 
8816a081dcdSDavid du Colombier 		/*
8826a081dcdSDavid du Colombier 		 * Collision Distance.
8836a081dcdSDavid du Colombier 		 */
8846a081dcdSDavid du Colombier 		r = csr32r(ctlr, Tctl);
8856a081dcdSDavid du Colombier 		r &= ~ColdMASK;
8866a081dcdSDavid du Colombier 		if(phy->fd)
8876a081dcdSDavid du Colombier 			r |= 64<<ColdSHIFT;
8886a081dcdSDavid du Colombier 		else
8896a081dcdSDavid du Colombier 			r |= 512<<ColdSHIFT;
8906a081dcdSDavid du Colombier 		csr32w(ctlr, Tctl, r);
8916a081dcdSDavid du Colombier 
8926a081dcdSDavid du Colombier 		/*
8936a081dcdSDavid du Colombier 		 * Flow control.
8946a081dcdSDavid du Colombier 		 */
8956a081dcdSDavid du Colombier 		if(phy->rfc)
8966a081dcdSDavid du Colombier 			ctrl |= Rfce;
8976a081dcdSDavid du Colombier 		if(phy->tfc)
8986a081dcdSDavid du Colombier 			ctrl |= Tfce;
8996a081dcdSDavid du Colombier 		csr32w(ctlr, Ctrl, ctrl);
9006a081dcdSDavid du Colombier 
9016a081dcdSDavid du Colombier enable:
9026a081dcdSDavid du Colombier 		ctlr->lim = 0;
9036a081dcdSDavid du Colombier 		igbeim(ctlr, Lsc);
9046a081dcdSDavid du Colombier 
9056a081dcdSDavid du Colombier 		ctlr->lsleep++;
9066a081dcdSDavid du Colombier 		sleep(&ctlr->lrendez, igbelim, ctlr);
9076a081dcdSDavid du Colombier 	}
9086a081dcdSDavid du Colombier }
9096a081dcdSDavid du Colombier 
9106a081dcdSDavid du Colombier static void
igbetxinit(Ctlr * ctlr)9116a081dcdSDavid du Colombier igbetxinit(Ctlr* ctlr)
9126a081dcdSDavid du Colombier {
9136a081dcdSDavid du Colombier 	int i, r;
9146a081dcdSDavid du Colombier 	Block *bp;
9156a081dcdSDavid du Colombier 
9166a081dcdSDavid du Colombier 	csr32w(ctlr, Tctl, (0x0F<<CtSHIFT)|Psp|(66<<ColdSHIFT));
9176a081dcdSDavid du Colombier 	switch(ctlr->id){
9186a081dcdSDavid du Colombier 	default:
9196a081dcdSDavid du Colombier 		r = 6;
9206a081dcdSDavid du Colombier 		break;
921aeb1c8a5SDavid du Colombier 	case i82543gc:
922aeb1c8a5SDavid du Colombier 	case i82544ei:
92307c32395SDavid du Colombier 	case i82544eif:
9249a8968c3SDavid du Colombier 	case i82544gc:
925aeb1c8a5SDavid du Colombier 	case i82540em:
926aeb1c8a5SDavid du Colombier 	case i82540eplp:
9275efba406SDavid du Colombier 	case i82541ei:
928aeb1c8a5SDavid du Colombier 	case i82541gi:
9298466d066SDavid du Colombier 	case i82541gi2:
930d717568cSDavid du Colombier 	case i82541pi:
931c079eed3SDavid du Colombier 	case i82545em:
932ab3dc52fSDavid du Colombier 	case i82545gmc:
933aeb1c8a5SDavid du Colombier 	case i82546gb:
9345b7d6169SDavid du Colombier 	case i82546eb:
9355efba406SDavid du Colombier 	case i82547ei:
936aeb1c8a5SDavid du Colombier 	case i82547gi:
9376a081dcdSDavid du Colombier 		r = 8;
9386a081dcdSDavid du Colombier 		break;
9396a081dcdSDavid du Colombier 	}
9406a081dcdSDavid du Colombier 	csr32w(ctlr, Tipg, (6<<20)|(8<<10)|r);
9416a081dcdSDavid du Colombier 	csr32w(ctlr, Ait, 0);
9426a081dcdSDavid du Colombier 	csr32w(ctlr, Txdmac, 0);
9436a081dcdSDavid du Colombier 
9446a081dcdSDavid du Colombier 	csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba));
9456a081dcdSDavid du Colombier 	csr32w(ctlr, Tdbah, 0);
9466a081dcdSDavid du Colombier 	csr32w(ctlr, Tdlen, ctlr->ntd*sizeof(Td));
9476a081dcdSDavid du Colombier 	ctlr->tdh = PREV(0, ctlr->ntd);
9486a081dcdSDavid du Colombier 	csr32w(ctlr, Tdh, 0);
9496a081dcdSDavid du Colombier 	ctlr->tdt = 0;
9506a081dcdSDavid du Colombier 	csr32w(ctlr, Tdt, 0);
9516a081dcdSDavid du Colombier 
9526a081dcdSDavid du Colombier 	for(i = 0; i < ctlr->ntd; i++){
9536a081dcdSDavid du Colombier 		if((bp = ctlr->tb[i]) != nil){
9546a081dcdSDavid du Colombier 			ctlr->tb[i] = nil;
9556a081dcdSDavid du Colombier 			freeb(bp);
9566a081dcdSDavid du Colombier 		}
9576a081dcdSDavid du Colombier 		memset(&ctlr->tdba[i], 0, sizeof(Td));
9586a081dcdSDavid du Colombier 	}
9596a081dcdSDavid du Colombier 	ctlr->tdfree = ctlr->ntd;
9606a081dcdSDavid du Colombier 
9616a081dcdSDavid du Colombier 	csr32w(ctlr, Tidv, 128);
9626a081dcdSDavid du Colombier 	r = (4<<WthreshSHIFT)|(4<<HthreshSHIFT)|(8<<PthreshSHIFT);
9636a081dcdSDavid du Colombier 
9646a081dcdSDavid du Colombier 	switch(ctlr->id){
9656a081dcdSDavid du Colombier 	default:
9666a081dcdSDavid du Colombier 		break;
967aeb1c8a5SDavid du Colombier 	case i82540em:
968aeb1c8a5SDavid du Colombier 	case i82540eplp:
969aeb1c8a5SDavid du Colombier 	case i82547gi:
970c079eed3SDavid du Colombier 	case i82545em:
971ab3dc52fSDavid du Colombier 	case i82545gmc:
972aeb1c8a5SDavid du Colombier 	case i82546gb:
9735b7d6169SDavid du Colombier 	case i82546eb:
974aeb1c8a5SDavid du Colombier 	case i82541gi:
9758466d066SDavid du Colombier 	case i82541gi2:
976d717568cSDavid du Colombier 	case i82541pi:
9776a081dcdSDavid du Colombier 		r = csr32r(ctlr, Txdctl);
9786a081dcdSDavid du Colombier 		r &= ~WthreshMASK;
9796a081dcdSDavid du Colombier 		r |= Gran|(4<<WthreshSHIFT);
9806a081dcdSDavid du Colombier 
9816a081dcdSDavid du Colombier 		csr32w(ctlr, Tadv, 64);
9826a081dcdSDavid du Colombier 		break;
9836a081dcdSDavid du Colombier 	}
9846a081dcdSDavid du Colombier 
9856a081dcdSDavid du Colombier 	csr32w(ctlr, Txdctl, r);
9866a081dcdSDavid du Colombier 
9876a081dcdSDavid du Colombier 	r = csr32r(ctlr, Tctl);
9886a081dcdSDavid du Colombier 	r |= Ten;
9896a081dcdSDavid du Colombier 	csr32w(ctlr, Tctl, r);
9906a081dcdSDavid du Colombier }
9916a081dcdSDavid du Colombier 
9926a081dcdSDavid du Colombier static void
igbetransmit(Ether * edev)9936a081dcdSDavid du Colombier igbetransmit(Ether* edev)
9946a081dcdSDavid du Colombier {
9956a081dcdSDavid du Colombier 	Td *td;
9966a081dcdSDavid du Colombier 	Block *bp;
9976a081dcdSDavid du Colombier 	Ctlr *ctlr;
9986a081dcdSDavid du Colombier 	int tdh, tdt;
9996a081dcdSDavid du Colombier 
10006a081dcdSDavid du Colombier 	ctlr = edev->ctlr;
10016a081dcdSDavid du Colombier 
10026a081dcdSDavid du Colombier 	ilock(&ctlr->tlock);
10036a081dcdSDavid du Colombier 
10046a081dcdSDavid du Colombier 	/*
10056a081dcdSDavid du Colombier 	 * Free any completed packets
10066a081dcdSDavid du Colombier 	 */
10076a081dcdSDavid du Colombier 	tdh = ctlr->tdh;
10086a081dcdSDavid du Colombier 	while(NEXT(tdh, ctlr->ntd) != csr32r(ctlr, Tdh)){
10096a081dcdSDavid du Colombier 		if((bp = ctlr->tb[tdh]) != nil){
10106a081dcdSDavid du Colombier 			ctlr->tb[tdh] = nil;
10116a081dcdSDavid du Colombier 			freeb(bp);
10126a081dcdSDavid du Colombier 		}
10136a081dcdSDavid du Colombier 		memset(&ctlr->tdba[tdh], 0, sizeof(Td));
10146a081dcdSDavid du Colombier 		tdh = NEXT(tdh, ctlr->ntd);
10156a081dcdSDavid du Colombier 	}
10166a081dcdSDavid du Colombier 	ctlr->tdh = tdh;
10176a081dcdSDavid du Colombier 
10186a081dcdSDavid du Colombier 	/*
10196a081dcdSDavid du Colombier 	 * Try to fill the ring back up.
10206a081dcdSDavid du Colombier 	 */
10216a081dcdSDavid du Colombier 	tdt = ctlr->tdt;
10226a081dcdSDavid du Colombier 	while(NEXT(tdt, ctlr->ntd) != tdh){
1023ff8c3af2SDavid du Colombier 		if((bp = qget(edev->oq)) == nil)
10246a081dcdSDavid du Colombier 			break;
10256a081dcdSDavid du Colombier 		td = &ctlr->tdba[tdt];
10266a081dcdSDavid du Colombier 		td->addr[0] = PCIWADDR(bp->rp);
10276a081dcdSDavid du Colombier 		td->control = ((BLEN(bp) & LenMASK)<<LenSHIFT);
10286a081dcdSDavid du Colombier 		td->control |= Dext|Ifcs|Teop|DtypeDD;
10296a081dcdSDavid du Colombier 		ctlr->tb[tdt] = bp;
1030217e9e83SDavid du Colombier 		/* note size of queue of tds awaiting transmission */
1031217e9e83SDavid du Colombier 		notemark(&ctlr->wmtd, (tdt + Ntd - tdh) % Ntd);
10326a081dcdSDavid du Colombier 		tdt = NEXT(tdt, ctlr->ntd);
10336a081dcdSDavid du Colombier 		if(NEXT(tdt, ctlr->ntd) == tdh){
10346a081dcdSDavid du Colombier 			td->control |= Rs;
10356a081dcdSDavid du Colombier 			ctlr->txdw++;
10366a081dcdSDavid du Colombier 			ctlr->tdt = tdt;
10376a081dcdSDavid du Colombier 			csr32w(ctlr, Tdt, tdt);
10386a081dcdSDavid du Colombier 			igbeim(ctlr, Txdw);
10396a081dcdSDavid du Colombier 			break;
10406a081dcdSDavid du Colombier 		}
10416a081dcdSDavid du Colombier 		ctlr->tdt = tdt;
10426a081dcdSDavid du Colombier 		csr32w(ctlr, Tdt, tdt);
10436a081dcdSDavid du Colombier 	}
10446a081dcdSDavid du Colombier 
10456a081dcdSDavid du Colombier 	iunlock(&ctlr->tlock);
10466a081dcdSDavid du Colombier }
10476a081dcdSDavid du Colombier 
10486a081dcdSDavid du Colombier static void
igbereplenish(Ctlr * ctlr)10496a081dcdSDavid du Colombier igbereplenish(Ctlr* ctlr)
10506a081dcdSDavid du Colombier {
10516a081dcdSDavid du Colombier 	Rd *rd;
10526a081dcdSDavid du Colombier 	int rdt;
10536a081dcdSDavid du Colombier 	Block *bp;
10546a081dcdSDavid du Colombier 
10556a081dcdSDavid du Colombier 	rdt = ctlr->rdt;
10566a081dcdSDavid du Colombier 	while(NEXT(rdt, ctlr->nrd) != ctlr->rdh){
10576a081dcdSDavid du Colombier 		rd = &ctlr->rdba[rdt];
10586a081dcdSDavid du Colombier 		if(ctlr->rb[rdt] == nil){
10596a081dcdSDavid du Colombier 			bp = igberballoc();
10606a081dcdSDavid du Colombier 			if(bp == nil){
1061208510e1SDavid du Colombier 				iprint("#l%d: igbereplenish: no available buffers\n",
1062208510e1SDavid du Colombier 					ctlr->edev->ctlrno);
10636a081dcdSDavid du Colombier 				break;
10646a081dcdSDavid du Colombier 			}
10656a081dcdSDavid du Colombier 			ctlr->rb[rdt] = bp;
10666a081dcdSDavid du Colombier 			rd->addr[0] = PCIWADDR(bp->rp);
10676a081dcdSDavid du Colombier 			rd->addr[1] = 0;
10686a081dcdSDavid du Colombier 		}
10696a081dcdSDavid du Colombier 		coherence();
10706a081dcdSDavid du Colombier 		rd->status = 0;
10716a081dcdSDavid du Colombier 		rdt = NEXT(rdt, ctlr->nrd);
10726a081dcdSDavid du Colombier 		ctlr->rdfree++;
10736a081dcdSDavid du Colombier 	}
10746a081dcdSDavid du Colombier 	ctlr->rdt = rdt;
10756a081dcdSDavid du Colombier 	csr32w(ctlr, Rdt, rdt);
10766a081dcdSDavid du Colombier }
10776a081dcdSDavid du Colombier 
10786a081dcdSDavid du Colombier static void
igberxinit(Ctlr * ctlr)10796a081dcdSDavid du Colombier igberxinit(Ctlr* ctlr)
10806a081dcdSDavid du Colombier {
10816a081dcdSDavid du Colombier 	int i;
10826a081dcdSDavid du Colombier 	Block *bp;
10836a081dcdSDavid du Colombier 
1084b1707c5dSDavid du Colombier 	/* temporarily keep Mpe on */
1085b1707c5dSDavid du Colombier 	csr32w(ctlr, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF|Mpe);
10866a081dcdSDavid du Colombier 
10876a081dcdSDavid du Colombier 	csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba));
10886a081dcdSDavid du Colombier 	csr32w(ctlr, Rdbah, 0);
10896a081dcdSDavid du Colombier 	csr32w(ctlr, Rdlen, ctlr->nrd*sizeof(Rd));
10906a081dcdSDavid du Colombier 	ctlr->rdh = 0;
10916a081dcdSDavid du Colombier 	csr32w(ctlr, Rdh, 0);
10926a081dcdSDavid du Colombier 	ctlr->rdt = 0;
10936a081dcdSDavid du Colombier 	csr32w(ctlr, Rdt, 0);
10946a081dcdSDavid du Colombier 	ctlr->rdtr = 0;
10956a081dcdSDavid du Colombier 	csr32w(ctlr, Rdtr, Fpd|0);
10966a081dcdSDavid du Colombier 
10976a081dcdSDavid du Colombier 	for(i = 0; i < ctlr->nrd; i++){
10986a081dcdSDavid du Colombier 		if((bp = ctlr->rb[i]) != nil){
10996a081dcdSDavid du Colombier 			ctlr->rb[i] = nil;
11006a081dcdSDavid du Colombier 			freeb(bp);
11016a081dcdSDavid du Colombier 		}
11026a081dcdSDavid du Colombier 	}
11036a081dcdSDavid du Colombier 	igbereplenish(ctlr);
1104217e9e83SDavid du Colombier 	nrbfull = 0;
11056a081dcdSDavid du Colombier 
11066a081dcdSDavid du Colombier 	switch(ctlr->id){
1107aeb1c8a5SDavid du Colombier 	case i82540em:
1108aeb1c8a5SDavid du Colombier 	case i82540eplp:
1109aeb1c8a5SDavid du Colombier 	case i82541gi:
11108466d066SDavid du Colombier 	case i82541gi2:
1111d717568cSDavid du Colombier 	case i82541pi:
1112c079eed3SDavid du Colombier 	case i82545em:
1113ab3dc52fSDavid du Colombier 	case i82545gmc:
1114aeb1c8a5SDavid du Colombier 	case i82546gb:
11155b7d6169SDavid du Colombier 	case i82546eb:
1116aeb1c8a5SDavid du Colombier 	case i82547gi:
11176a081dcdSDavid du Colombier 		csr32w(ctlr, Radv, 64);
11186a081dcdSDavid du Colombier 		break;
11196a081dcdSDavid du Colombier 	}
11206a081dcdSDavid du Colombier 	csr32w(ctlr, Rxdctl, (8<<WthreshSHIFT)|(8<<HthreshSHIFT)|4);
11216a081dcdSDavid du Colombier 
11226a081dcdSDavid du Colombier 	/*
11230591a7c1SDavid du Colombier 	 * Disable checksum offload as it has known bugs.
11246a081dcdSDavid du Colombier 	 */
11250591a7c1SDavid du Colombier 	csr32w(ctlr, Rxcsum, ETHERHDRSIZE<<PcssSHIFT);
11266a081dcdSDavid du Colombier }
11276a081dcdSDavid du Colombier 
11286a081dcdSDavid du Colombier static int
igberim(void * ctlr)11296a081dcdSDavid du Colombier igberim(void* ctlr)
11306a081dcdSDavid du Colombier {
11316a081dcdSDavid du Colombier 	return ((Ctlr*)ctlr)->rim != 0;
11326a081dcdSDavid du Colombier }
11336a081dcdSDavid du Colombier 
11346a081dcdSDavid du Colombier static void
igberproc(void * arg)1135ff8c3af2SDavid du Colombier igberproc(void* arg)
11366a081dcdSDavid du Colombier {
11376a081dcdSDavid du Colombier 	Rd *rd;
11386a081dcdSDavid du Colombier 	Block *bp;
11396a081dcdSDavid du Colombier 	Ctlr *ctlr;
1140217e9e83SDavid du Colombier 	int r, rdh, passed;
11416a081dcdSDavid du Colombier 	Ether *edev;
11426a081dcdSDavid du Colombier 
1143ff8c3af2SDavid du Colombier 	edev = arg;
11446a081dcdSDavid du Colombier 	ctlr = edev->ctlr;
11456a081dcdSDavid du Colombier 
11466a081dcdSDavid du Colombier 	igberxinit(ctlr);
11476a081dcdSDavid du Colombier 	r = csr32r(ctlr, Rctl);
11486a081dcdSDavid du Colombier 	r |= Ren;
11496a081dcdSDavid du Colombier 	csr32w(ctlr, Rctl, r);
11506a081dcdSDavid du Colombier 	for(;;){
11516a081dcdSDavid du Colombier 		ctlr->rim = 0;
11526a081dcdSDavid du Colombier 		igbeim(ctlr, Rxt0|Rxo|Rxdmt0|Rxseq);
11536a081dcdSDavid du Colombier 		ctlr->rsleep++;
11546a081dcdSDavid du Colombier 		sleep(&ctlr->rrendez, igberim, ctlr);
11556a081dcdSDavid du Colombier 
11566a081dcdSDavid du Colombier 		rdh = ctlr->rdh;
1157217e9e83SDavid du Colombier 		passed = 0;
11586a081dcdSDavid du Colombier 		for(;;){
11596a081dcdSDavid du Colombier 			rd = &ctlr->rdba[rdh];
11606a081dcdSDavid du Colombier 
11616a081dcdSDavid du Colombier 			if(!(rd->status & Rdd))
11626a081dcdSDavid du Colombier 				break;
11636a081dcdSDavid du Colombier 
11646a081dcdSDavid du Colombier 			/*
11656a081dcdSDavid du Colombier 			 * Accept eop packets with no errors.
11666a081dcdSDavid du Colombier 			 * With no errors and the Ixsm bit set,
11676a081dcdSDavid du Colombier 			 * the descriptor status Tpcs and Ipcs bits give
11686a081dcdSDavid du Colombier 			 * an indication of whether the checksums were
11696a081dcdSDavid du Colombier 			 * calculated and valid.
11706a081dcdSDavid du Colombier 			 */
11718ccda836SDavid du Colombier 			/* ignore checksum offload as it has known bugs. */
11728ccda836SDavid du Colombier 			rd->errors &= ~(Ipe | Tcpe);
11736a081dcdSDavid du Colombier 			if((rd->status & Reop) && rd->errors == 0){
11746a081dcdSDavid du Colombier 				bp = ctlr->rb[rdh];
11756a081dcdSDavid du Colombier 				ctlr->rb[rdh] = nil;
1176ff8c3af2SDavid du Colombier 				bp->wp += rd->length;
11776a081dcdSDavid du Colombier 				bp->next = nil;
11788ccda836SDavid du Colombier 				/* ignore checksum offload as it has known bugs. */
11798ccda836SDavid du Colombier 				if(0 && !(rd->status & Ixsm)){
11806a081dcdSDavid du Colombier 					ctlr->ixsm++;
11816a081dcdSDavid du Colombier 					if(rd->status & Ipcs){
11826a081dcdSDavid du Colombier 						/*
11836a081dcdSDavid du Colombier 						 * IP checksum calculated
11846a081dcdSDavid du Colombier 						 * (and valid as errors == 0).
11856a081dcdSDavid du Colombier 						 */
11866a081dcdSDavid du Colombier 						ctlr->ipcs++;
11876a081dcdSDavid du Colombier 						bp->flag |= Bipck;
11886a081dcdSDavid du Colombier 					}
11896a081dcdSDavid du Colombier 					if(rd->status & Tcpcs){
11906a081dcdSDavid du Colombier 						/*
11916a081dcdSDavid du Colombier 						 * TCP/UDP checksum calculated
11926a081dcdSDavid du Colombier 						 * (and valid as errors == 0).
11936a081dcdSDavid du Colombier 						 */
11946a081dcdSDavid du Colombier 						ctlr->tcpcs++;
11956a081dcdSDavid du Colombier 						bp->flag |= Btcpck|Budpck;
11966a081dcdSDavid du Colombier 					}
11976a081dcdSDavid du Colombier 					bp->checksum = rd->checksum;
11986a081dcdSDavid du Colombier 					bp->flag |= Bpktck;
11996a081dcdSDavid du Colombier 				}
1200217e9e83SDavid du Colombier 				ilock(&igberblock);
1201217e9e83SDavid du Colombier 				nrbfull++;
1202217e9e83SDavid du Colombier 				iunlock(&igberblock);
1203217e9e83SDavid du Colombier 				notemark(&ctlr->wmrb, nrbfull);
1204ff8c3af2SDavid du Colombier 				etheriq(edev, bp, 1);
1205217e9e83SDavid du Colombier 				passed++;
12066a081dcdSDavid du Colombier 			}
12076a081dcdSDavid du Colombier 			else if(ctlr->rb[rdh] != nil){
12086a081dcdSDavid du Colombier 				freeb(ctlr->rb[rdh]);
12096a081dcdSDavid du Colombier 				ctlr->rb[rdh] = nil;
12106a081dcdSDavid du Colombier 			}
12116a081dcdSDavid du Colombier 
12126a081dcdSDavid du Colombier 			memset(rd, 0, sizeof(Rd));
12136a081dcdSDavid du Colombier 			coherence();
12146a081dcdSDavid du Colombier 			ctlr->rdfree--;
12156a081dcdSDavid du Colombier 			rdh = NEXT(rdh, ctlr->nrd);
12166a081dcdSDavid du Colombier 		}
12176a081dcdSDavid du Colombier 		ctlr->rdh = rdh;
12186a081dcdSDavid du Colombier 
12196a081dcdSDavid du Colombier 		if(ctlr->rdfree < ctlr->nrd/2 || (ctlr->rim & Rxdmt0))
12206a081dcdSDavid du Colombier 			igbereplenish(ctlr);
1221217e9e83SDavid du Colombier 		/* note how many rds had full buffers */
1222217e9e83SDavid du Colombier 		notemark(&ctlr->wmrd, passed);
12236a081dcdSDavid du Colombier 	}
12246a081dcdSDavid du Colombier }
12256a081dcdSDavid du Colombier 
12266a081dcdSDavid du Colombier static void
igbeattach(Ether * edev)12276a081dcdSDavid du Colombier igbeattach(Ether* edev)
12286a081dcdSDavid du Colombier {
12296a081dcdSDavid du Colombier 	Block *bp;
12306a081dcdSDavid du Colombier 	Ctlr *ctlr;
12316a081dcdSDavid du Colombier 	char name[KNAMELEN];
12326a081dcdSDavid du Colombier 
12336a081dcdSDavid du Colombier 	ctlr = edev->ctlr;
1234208510e1SDavid du Colombier 	ctlr->edev = edev;			/* point back to Ether* */
12356a081dcdSDavid du Colombier 	qlock(&ctlr->alock);
1236ea58ad6fSDavid du Colombier 	if(ctlr->alloc != nil){			/* already allocated? */
12376a081dcdSDavid du Colombier 		qunlock(&ctlr->alock);
12386a081dcdSDavid du Colombier 		return;
12396a081dcdSDavid du Colombier 	}
12406a081dcdSDavid du Colombier 
1241aa72973aSDavid du Colombier 	ctlr->tb = nil;
1242aa72973aSDavid du Colombier 	ctlr->rb = nil;
1243aa72973aSDavid du Colombier 	ctlr->alloc = nil;
1244aa72973aSDavid du Colombier 	ctlr->nrb = 0;
12456a081dcdSDavid du Colombier 	if(waserror()){
12466a081dcdSDavid du Colombier 		while(ctlr->nrb > 0){
12476a081dcdSDavid du Colombier 			bp = igberballoc();
12486a081dcdSDavid du Colombier 			bp->free = nil;
12496a081dcdSDavid du Colombier 			freeb(bp);
12506a081dcdSDavid du Colombier 			ctlr->nrb--;
12516a081dcdSDavid du Colombier 		}
12526a081dcdSDavid du Colombier 		free(ctlr->tb);
12536a081dcdSDavid du Colombier 		ctlr->tb = nil;
12546a081dcdSDavid du Colombier 		free(ctlr->rb);
12556a081dcdSDavid du Colombier 		ctlr->rb = nil;
12566a081dcdSDavid du Colombier 		free(ctlr->alloc);
12576a081dcdSDavid du Colombier 		ctlr->alloc = nil;
12586a081dcdSDavid du Colombier 		qunlock(&ctlr->alock);
12596a081dcdSDavid du Colombier 		nexterror();
12606a081dcdSDavid du Colombier 	}
12616a081dcdSDavid du Colombier 
1262aa72973aSDavid du Colombier 	ctlr->nrd = ROUND(Nrd, 8);
1263aa72973aSDavid du Colombier 	ctlr->ntd = ROUND(Ntd, 8);
1264aa72973aSDavid du Colombier 	ctlr->alloc = malloc(ctlr->nrd*sizeof(Rd)+ctlr->ntd*sizeof(Td) + 127);
1265aa72973aSDavid du Colombier 	if(ctlr->alloc == nil) {
1266aa72973aSDavid du Colombier 		print("igbe: can't allocate ctlr->alloc\n");
1267aa72973aSDavid du Colombier 		error(Enomem);
1268aa72973aSDavid du Colombier 	}
1269aa72973aSDavid du Colombier 	ctlr->rdba = (Rd*)ROUNDUP((uintptr)ctlr->alloc, 128);
1270aa72973aSDavid du Colombier 	ctlr->tdba = (Td*)(ctlr->rdba+ctlr->nrd);
1271aa72973aSDavid du Colombier 
1272aa72973aSDavid du Colombier 	ctlr->rb = malloc(ctlr->nrd*sizeof(Block*));
1273aa72973aSDavid du Colombier 	ctlr->tb = malloc(ctlr->ntd*sizeof(Block*));
1274aa72973aSDavid du Colombier 	if (ctlr->rb == nil || ctlr->tb == nil) {
1275aa72973aSDavid du Colombier 		print("igbe: can't allocate ctlr->rb or ctlr->tb\n");
1276aa72973aSDavid du Colombier 		error(Enomem);
1277aa72973aSDavid du Colombier 	}
1278aa72973aSDavid du Colombier 
12796a081dcdSDavid du Colombier 	for(ctlr->nrb = 0; ctlr->nrb < Nrb; ctlr->nrb++){
12806a081dcdSDavid du Colombier 		if((bp = allocb(Rbsz)) == nil)
12816a081dcdSDavid du Colombier 			break;
12826a081dcdSDavid du Colombier 		bp->free = igberbfree;
12836a081dcdSDavid du Colombier 		freeb(bp);
12846a081dcdSDavid du Colombier 	}
1285217e9e83SDavid du Colombier 	initmark(&ctlr->wmrb, Nrb, "rcv bufs unprocessed");
1286217e9e83SDavid du Colombier 	initmark(&ctlr->wmrd, Nrd-1, "rcv descrs processed at once");
1287217e9e83SDavid du Colombier 	initmark(&ctlr->wmtd, Ntd-1, "xmit descr queue len");
12886a081dcdSDavid du Colombier 
12896a081dcdSDavid du Colombier 	snprint(name, KNAMELEN, "#l%dlproc", edev->ctlrno);
12906a081dcdSDavid du Colombier 	kproc(name, igbelproc, edev);
12916a081dcdSDavid du Colombier 
12926a081dcdSDavid du Colombier 	snprint(name, KNAMELEN, "#l%drproc", edev->ctlrno);
12936a081dcdSDavid du Colombier 	kproc(name, igberproc, edev);
12946a081dcdSDavid du Colombier 
12956a081dcdSDavid du Colombier 	igbetxinit(ctlr);
12966a081dcdSDavid du Colombier 
12976a081dcdSDavid du Colombier 	qunlock(&ctlr->alock);
12986a081dcdSDavid du Colombier 	poperror();
12996a081dcdSDavid du Colombier }
13006a081dcdSDavid du Colombier 
13016a081dcdSDavid du Colombier static void
igbeinterrupt(Ureg *,void * arg)13026a081dcdSDavid du Colombier igbeinterrupt(Ureg*, void* arg)
13036a081dcdSDavid du Colombier {
13046a081dcdSDavid du Colombier 	Ctlr *ctlr;
13056a081dcdSDavid du Colombier 	Ether *edev;
13066a081dcdSDavid du Colombier 	int icr, im, txdw;
13076a081dcdSDavid du Colombier 
13086a081dcdSDavid du Colombier 	edev = arg;
13096a081dcdSDavid du Colombier 	ctlr = edev->ctlr;
13106a081dcdSDavid du Colombier 
13116a081dcdSDavid du Colombier 	ilock(&ctlr->imlock);
13126a081dcdSDavid du Colombier 	csr32w(ctlr, Imc, ~0);
13136a081dcdSDavid du Colombier 	im = ctlr->im;
13146a081dcdSDavid du Colombier 	txdw = 0;
13156a081dcdSDavid du Colombier 
13166a081dcdSDavid du Colombier 	while((icr = csr32r(ctlr, Icr) & ctlr->im) != 0){
13176a081dcdSDavid du Colombier 		if(icr & Lsc){
13186a081dcdSDavid du Colombier 			im &= ~Lsc;
13196a081dcdSDavid du Colombier 			ctlr->lim = icr & Lsc;
13206a081dcdSDavid du Colombier 			wakeup(&ctlr->lrendez);
13216a081dcdSDavid du Colombier 			ctlr->lintr++;
13226a081dcdSDavid du Colombier 		}
13236a081dcdSDavid du Colombier 		if(icr & (Rxt0|Rxo|Rxdmt0|Rxseq)){
13246a081dcdSDavid du Colombier 			im &= ~(Rxt0|Rxo|Rxdmt0|Rxseq);
13256a081dcdSDavid du Colombier 			ctlr->rim = icr & (Rxt0|Rxo|Rxdmt0|Rxseq);
13266a081dcdSDavid du Colombier 			wakeup(&ctlr->rrendez);
13276a081dcdSDavid du Colombier 			ctlr->rintr++;
13286a081dcdSDavid du Colombier 		}
13296a081dcdSDavid du Colombier 		if(icr & Txdw){
13306a081dcdSDavid du Colombier 			im &= ~Txdw;
13316a081dcdSDavid du Colombier 			txdw++;
13326a081dcdSDavid du Colombier 			ctlr->tintr++;
13336a081dcdSDavid du Colombier 		}
13346a081dcdSDavid du Colombier 	}
13356a081dcdSDavid du Colombier 
13366a081dcdSDavid du Colombier 	ctlr->im = im;
13376a081dcdSDavid du Colombier 	csr32w(ctlr, Ims, im);
13386a081dcdSDavid du Colombier 	iunlock(&ctlr->imlock);
13396a081dcdSDavid du Colombier 
13406a081dcdSDavid du Colombier 	if(txdw)
13416a081dcdSDavid du Colombier 		igbetransmit(edev);
13426a081dcdSDavid du Colombier }
13436a081dcdSDavid du Colombier 
13446a081dcdSDavid du Colombier static int
i82543mdior(Ctlr * ctlr,int n)13456a081dcdSDavid du Colombier i82543mdior(Ctlr* ctlr, int n)
13466a081dcdSDavid du Colombier {
13476a081dcdSDavid du Colombier 	int ctrl, data, i, r;
13486a081dcdSDavid du Colombier 
13496a081dcdSDavid du Colombier 	/*
13506a081dcdSDavid du Colombier 	 * Read n bits from the Management Data I/O Interface.
13516a081dcdSDavid du Colombier 	 */
13526a081dcdSDavid du Colombier 	ctrl = csr32r(ctlr, Ctrl);
13536a081dcdSDavid du Colombier 	r = (ctrl & ~Mddo)|Mdco;
13546a081dcdSDavid du Colombier 	data = 0;
13556a081dcdSDavid du Colombier 	for(i = n-1; i >= 0; i--){
13566a081dcdSDavid du Colombier 		if(csr32r(ctlr, Ctrl) & Mdd)
13576a081dcdSDavid du Colombier 			data |= (1<<i);
13586a081dcdSDavid du Colombier 		csr32w(ctlr, Ctrl, Mdc|r);
13596a081dcdSDavid du Colombier 		csr32w(ctlr, Ctrl, r);
13606a081dcdSDavid du Colombier 	}
13616a081dcdSDavid du Colombier 	csr32w(ctlr, Ctrl, ctrl);
13626a081dcdSDavid du Colombier 
13636a081dcdSDavid du Colombier 	return data;
13646a081dcdSDavid du Colombier }
13656a081dcdSDavid du Colombier 
13666a081dcdSDavid du Colombier static int
i82543mdiow(Ctlr * ctlr,int bits,int n)13676a081dcdSDavid du Colombier i82543mdiow(Ctlr* ctlr, int bits, int n)
13686a081dcdSDavid du Colombier {
13696a081dcdSDavid du Colombier 	int ctrl, i, r;
13706a081dcdSDavid du Colombier 
13716a081dcdSDavid du Colombier 	/*
13726a081dcdSDavid du Colombier 	 * Write n bits to the Management Data I/O Interface.
13736a081dcdSDavid du Colombier 	 */
13746a081dcdSDavid du Colombier 	ctrl = csr32r(ctlr, Ctrl);
13756a081dcdSDavid du Colombier 	r = Mdco|Mddo|ctrl;
13766a081dcdSDavid du Colombier 	for(i = n-1; i >= 0; i--){
13776a081dcdSDavid du Colombier 		if(bits & (1<<i))
13786a081dcdSDavid du Colombier 			r |= Mdd;
13796a081dcdSDavid du Colombier 		else
13806a081dcdSDavid du Colombier 			r &= ~Mdd;
13816a081dcdSDavid du Colombier 		csr32w(ctlr, Ctrl, Mdc|r);
13826a081dcdSDavid du Colombier 		csr32w(ctlr, Ctrl, r);
13836a081dcdSDavid du Colombier 	}
13846a081dcdSDavid du Colombier 	csr32w(ctlr, Ctrl, ctrl);
13856a081dcdSDavid du Colombier 
13866a081dcdSDavid du Colombier 	return 0;
13876a081dcdSDavid du Colombier }
13886a081dcdSDavid du Colombier 
13896a081dcdSDavid du Colombier static int
i82543miimir(Mii * mii,int pa,int ra)13906a081dcdSDavid du Colombier i82543miimir(Mii* mii, int pa, int ra)
13916a081dcdSDavid du Colombier {
13926a081dcdSDavid du Colombier 	int data;
13936a081dcdSDavid du Colombier 	Ctlr *ctlr;
13946a081dcdSDavid du Colombier 
13956a081dcdSDavid du Colombier 	ctlr = mii->ctlr;
13966a081dcdSDavid du Colombier 
13976a081dcdSDavid du Colombier 	/*
13986a081dcdSDavid du Colombier 	 * MII Management Interface Read.
13996a081dcdSDavid du Colombier 	 *
14006a081dcdSDavid du Colombier 	 * Preamble;
14016a081dcdSDavid du Colombier 	 * ST+OP+PHYAD+REGAD;
14026a081dcdSDavid du Colombier 	 * TA + 16 data bits.
14036a081dcdSDavid du Colombier 	 */
14046a081dcdSDavid du Colombier 	i82543mdiow(ctlr, 0xFFFFFFFF, 32);
14056a081dcdSDavid du Colombier 	i82543mdiow(ctlr, 0x1800|(pa<<5)|ra, 14);
14066a081dcdSDavid du Colombier 	data = i82543mdior(ctlr, 18);
14076a081dcdSDavid du Colombier 
14086a081dcdSDavid du Colombier 	if(data & 0x10000)
14096a081dcdSDavid du Colombier 		return -1;
14106a081dcdSDavid du Colombier 
14116a081dcdSDavid du Colombier 	return data & 0xFFFF;
14126a081dcdSDavid du Colombier }
14136a081dcdSDavid du Colombier 
14146a081dcdSDavid du Colombier static int
i82543miimiw(Mii * mii,int pa,int ra,int data)14156a081dcdSDavid du Colombier i82543miimiw(Mii* mii, int pa, int ra, int data)
14166a081dcdSDavid du Colombier {
14176a081dcdSDavid du Colombier 	Ctlr *ctlr;
14186a081dcdSDavid du Colombier 
14196a081dcdSDavid du Colombier 	ctlr = mii->ctlr;
14206a081dcdSDavid du Colombier 
14216a081dcdSDavid du Colombier 	/*
14226a081dcdSDavid du Colombier 	 * MII Management Interface Write.
14236a081dcdSDavid du Colombier 	 *
14246a081dcdSDavid du Colombier 	 * Preamble;
14256a081dcdSDavid du Colombier 	 * ST+OP+PHYAD+REGAD+TA + 16 data bits;
14266a081dcdSDavid du Colombier 	 * Z.
14276a081dcdSDavid du Colombier 	 */
14286a081dcdSDavid du Colombier 	i82543mdiow(ctlr, 0xFFFFFFFF, 32);
14296a081dcdSDavid du Colombier 	data &= 0xFFFF;
14306a081dcdSDavid du Colombier 	data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16);
14316a081dcdSDavid du Colombier 	i82543mdiow(ctlr, data, 32);
14326a081dcdSDavid du Colombier 
14336a081dcdSDavid du Colombier 	return 0;
14346a081dcdSDavid du Colombier }
14356a081dcdSDavid du Colombier 
14366a081dcdSDavid du Colombier static int
igbemiimir(Mii * mii,int pa,int ra)14376a081dcdSDavid du Colombier igbemiimir(Mii* mii, int pa, int ra)
14386a081dcdSDavid du Colombier {
14396a081dcdSDavid du Colombier 	Ctlr *ctlr;
14406a081dcdSDavid du Colombier 	int mdic, timo;
14416a081dcdSDavid du Colombier 
14426a081dcdSDavid du Colombier 	ctlr = mii->ctlr;
14436a081dcdSDavid du Colombier 
14446a081dcdSDavid du Colombier 	csr32w(ctlr, Mdic, MDIrop|(pa<<MDIpSHIFT)|(ra<<MDIrSHIFT));
14456a081dcdSDavid du Colombier 	mdic = 0;
14466a081dcdSDavid du Colombier 	for(timo = 64; timo; timo--){
14476a081dcdSDavid du Colombier 		mdic = csr32r(ctlr, Mdic);
14486a081dcdSDavid du Colombier 		if(mdic & (MDIe|MDIready))
14496a081dcdSDavid du Colombier 			break;
14506a081dcdSDavid du Colombier 		microdelay(1);
14516a081dcdSDavid du Colombier 	}
14526a081dcdSDavid du Colombier 
14536a081dcdSDavid du Colombier 	if((mdic & (MDIe|MDIready)) == MDIready)
14546a081dcdSDavid du Colombier 		return mdic & 0xFFFF;
14556a081dcdSDavid du Colombier 	return -1;
14566a081dcdSDavid du Colombier }
14576a081dcdSDavid du Colombier 
14586a081dcdSDavid du Colombier static int
igbemiimiw(Mii * mii,int pa,int ra,int data)14596a081dcdSDavid du Colombier igbemiimiw(Mii* mii, int pa, int ra, int data)
14606a081dcdSDavid du Colombier {
14616a081dcdSDavid du Colombier 	Ctlr *ctlr;
14626a081dcdSDavid du Colombier 	int mdic, timo;
14636a081dcdSDavid du Colombier 
14646a081dcdSDavid du Colombier 	ctlr = mii->ctlr;
14656a081dcdSDavid du Colombier 
14666a081dcdSDavid du Colombier 	data &= MDIdMASK;
14676a081dcdSDavid du Colombier 	csr32w(ctlr, Mdic, MDIwop|(pa<<MDIpSHIFT)|(ra<<MDIrSHIFT)|data);
14686a081dcdSDavid du Colombier 	mdic = 0;
14696a081dcdSDavid du Colombier 	for(timo = 64; timo; timo--){
14706a081dcdSDavid du Colombier 		mdic = csr32r(ctlr, Mdic);
14716a081dcdSDavid du Colombier 		if(mdic & (MDIe|MDIready))
14726a081dcdSDavid du Colombier 			break;
14736a081dcdSDavid du Colombier 		microdelay(1);
14746a081dcdSDavid du Colombier 	}
14756a081dcdSDavid du Colombier 	if((mdic & (MDIe|MDIready)) == MDIready)
14766a081dcdSDavid du Colombier 		return 0;
14776a081dcdSDavid du Colombier 	return -1;
14786a081dcdSDavid du Colombier }
14796a081dcdSDavid du Colombier 
14806a081dcdSDavid du Colombier static int
igbemii(Ctlr * ctlr)14816a081dcdSDavid du Colombier igbemii(Ctlr* ctlr)
14826a081dcdSDavid du Colombier {
14836a081dcdSDavid du Colombier 	MiiPhy *phy;
14846a081dcdSDavid du Colombier 	int ctrl, p, r;
14856a081dcdSDavid du Colombier 
14866a081dcdSDavid du Colombier 	r = csr32r(ctlr, Status);
14876a081dcdSDavid du Colombier 	if(r & Tbimode)
14886a081dcdSDavid du Colombier 		return -1;
14896a081dcdSDavid du Colombier 	if((ctlr->mii = malloc(sizeof(Mii))) == nil)
14906a081dcdSDavid du Colombier 		return -1;
14916a081dcdSDavid du Colombier 	ctlr->mii->ctlr = ctlr;
14926a081dcdSDavid du Colombier 
14936a081dcdSDavid du Colombier 	ctrl = csr32r(ctlr, Ctrl);
14946a081dcdSDavid du Colombier 	ctrl |= Slu;
14956a081dcdSDavid du Colombier 
14966a081dcdSDavid du Colombier 	switch(ctlr->id){
1497aeb1c8a5SDavid du Colombier 	case i82543gc:
14986a081dcdSDavid du Colombier 		ctrl |= Frcdplx|Frcspd;
14996a081dcdSDavid du Colombier 		csr32w(ctlr, Ctrl, ctrl);
15006a081dcdSDavid du Colombier 
15016a081dcdSDavid du Colombier 		/*
15026a081dcdSDavid du Colombier 		 * The reset pin direction (Mdro) should already
15036a081dcdSDavid du Colombier 		 * be set from the EEPROM load.
15046a081dcdSDavid du Colombier 		 * If it's not set this configuration is unexpected
15056a081dcdSDavid du Colombier 		 * so bail.
15066a081dcdSDavid du Colombier 		 */
15076a081dcdSDavid du Colombier 		r = csr32r(ctlr, Ctrlext);
15080591a7c1SDavid du Colombier 		if(!(r & Mdro)) {
15090591a7c1SDavid du Colombier 			print("igbe: 82543gc Mdro not set\n");
15106a081dcdSDavid du Colombier 			return -1;
15110591a7c1SDavid du Colombier 		}
15126a081dcdSDavid du Colombier 		csr32w(ctlr, Ctrlext, r);
15136a081dcdSDavid du Colombier 		delay(20);
15146a081dcdSDavid du Colombier 		r = csr32r(ctlr, Ctrlext);
15156a081dcdSDavid du Colombier 		r &= ~Mdr;
15166a081dcdSDavid du Colombier 		csr32w(ctlr, Ctrlext, r);
15176a081dcdSDavid du Colombier 		delay(20);
15186a081dcdSDavid du Colombier 		r = csr32r(ctlr, Ctrlext);
15196a081dcdSDavid du Colombier 		r |= Mdr;
15206a081dcdSDavid du Colombier 		csr32w(ctlr, Ctrlext, r);
15216a081dcdSDavid du Colombier 		delay(20);
15226a081dcdSDavid du Colombier 
15236a081dcdSDavid du Colombier 		ctlr->mii->mir = i82543miimir;
15246a081dcdSDavid du Colombier 		ctlr->mii->miw = i82543miimiw;
15256a081dcdSDavid du Colombier 		break;
1526aeb1c8a5SDavid du Colombier 	case i82544ei:
152707c32395SDavid du Colombier 	case i82544eif:
15289a8968c3SDavid du Colombier 	case i82544gc:
1529aeb1c8a5SDavid du Colombier 	case i82540em:
1530aeb1c8a5SDavid du Colombier 	case i82540eplp:
15315efba406SDavid du Colombier 	case i82547ei:
1532aeb1c8a5SDavid du Colombier 	case i82547gi:
15335efba406SDavid du Colombier 	case i82541ei:
1534aeb1c8a5SDavid du Colombier 	case i82541gi:
15358466d066SDavid du Colombier 	case i82541gi2:
1536d717568cSDavid du Colombier 	case i82541pi:
1537c079eed3SDavid du Colombier 	case i82545em:
1538ab3dc52fSDavid du Colombier 	case i82545gmc:
1539aeb1c8a5SDavid du Colombier 	case i82546gb:
15405b7d6169SDavid du Colombier 	case i82546eb:
15416a081dcdSDavid du Colombier 		ctrl &= ~(Frcdplx|Frcspd);
15426a081dcdSDavid du Colombier 		csr32w(ctlr, Ctrl, ctrl);
15436a081dcdSDavid du Colombier 		ctlr->mii->mir = igbemiimir;
15446a081dcdSDavid du Colombier 		ctlr->mii->miw = igbemiimiw;
15456a081dcdSDavid du Colombier 		break;
15466a081dcdSDavid du Colombier 	default:
15476a081dcdSDavid du Colombier 		free(ctlr->mii);
15486a081dcdSDavid du Colombier 		ctlr->mii = nil;
15496a081dcdSDavid du Colombier 		return -1;
15506a081dcdSDavid du Colombier 	}
15516a081dcdSDavid du Colombier 
15526a081dcdSDavid du Colombier 	if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){
15536a081dcdSDavid du Colombier 		free(ctlr->mii);
15546a081dcdSDavid du Colombier 		ctlr->mii = nil;
15556a081dcdSDavid du Colombier 		return -1;
15566a081dcdSDavid du Colombier 	}
15574de34a7eSDavid du Colombier 	USED(phy);
15584de34a7eSDavid du Colombier 	// print("oui %X phyno %d\n", phy->oui, phy->phyno);
15596a081dcdSDavid du Colombier 
15606a081dcdSDavid du Colombier 	/*
15616a081dcdSDavid du Colombier 	 * 8254X-specific PHY registers not in 802.3:
15626a081dcdSDavid du Colombier 	 *	0x10	PHY specific control
15636a081dcdSDavid du Colombier 	 *	0x14	extended PHY specific control
15646a081dcdSDavid du Colombier 	 * Set appropriate values then reset the PHY to have
15656a081dcdSDavid du Colombier 	 * changes noted.
15666a081dcdSDavid du Colombier 	 */
1567d717568cSDavid du Colombier 	switch(ctlr->id){
1568d717568cSDavid du Colombier 	case i82547gi:
1569d717568cSDavid du Colombier 	case i82541gi:
15708466d066SDavid du Colombier 	case i82541gi2:
1571d717568cSDavid du Colombier 	case i82541pi:
1572c079eed3SDavid du Colombier 	case i82545em:
1573ab3dc52fSDavid du Colombier 	case i82545gmc:
1574d717568cSDavid du Colombier 	case i82546gb:
1575d717568cSDavid du Colombier 	case i82546eb:
1576d717568cSDavid du Colombier 		break;
1577d717568cSDavid du Colombier 	default:
1578aeb1c8a5SDavid du Colombier 		r = miimir(ctlr->mii, 16);
15796a081dcdSDavid du Colombier 		r |= 0x0800;			/* assert CRS on Tx */
15806a081dcdSDavid du Colombier 		r |= 0x0060;			/* auto-crossover all speeds */
15816a081dcdSDavid du Colombier 		r |= 0x0002;			/* polarity reversal enabled */
1582aeb1c8a5SDavid du Colombier 		miimiw(ctlr->mii, 16, r);
15836a081dcdSDavid du Colombier 
1584aeb1c8a5SDavid du Colombier 		r = miimir(ctlr->mii, 20);
15856a081dcdSDavid du Colombier 		r |= 0x0070;			/* +25MHz clock */
15866a081dcdSDavid du Colombier 		r &= ~0x0F00;
15876a081dcdSDavid du Colombier 		r |= 0x0100;			/* 1x downshift */
1588aeb1c8a5SDavid du Colombier 		miimiw(ctlr->mii, 20, r);
15896a081dcdSDavid du Colombier 
15906a081dcdSDavid du Colombier 		miireset(ctlr->mii);
15916a081dcdSDavid du Colombier 		p = 0;
15926a081dcdSDavid du Colombier 		if(ctlr->txcw & TxcwPs)
15936a081dcdSDavid du Colombier 			p |= AnaP;
15946a081dcdSDavid du Colombier 		if(ctlr->txcw & TxcwAs)
15956a081dcdSDavid du Colombier 			p |= AnaAP;
15966a081dcdSDavid du Colombier 		miiane(ctlr->mii, ~0, p, ~0);
1597d717568cSDavid du Colombier 		break;
1598aeb1c8a5SDavid du Colombier 	}
15996a081dcdSDavid du Colombier 	return 0;
16006a081dcdSDavid du Colombier }
16016a081dcdSDavid du Colombier 
16026a081dcdSDavid du Colombier static int
at93c46io(Ctlr * ctlr,char * op,int data)16036a081dcdSDavid du Colombier at93c46io(Ctlr* ctlr, char* op, int data)
16046a081dcdSDavid du Colombier {
16056a081dcdSDavid du Colombier 	char *lp, *p;
16066a081dcdSDavid du Colombier 	int i, loop, eecd, r;
16076a081dcdSDavid du Colombier 
16086a081dcdSDavid du Colombier 	eecd = csr32r(ctlr, Eecd);
16096a081dcdSDavid du Colombier 
16106a081dcdSDavid du Colombier 	r = 0;
16116a081dcdSDavid du Colombier 	loop = -1;
16126a081dcdSDavid du Colombier 	lp = nil;
16136a081dcdSDavid du Colombier 	for(p = op; *p != '\0'; p++){
16146a081dcdSDavid du Colombier 		switch(*p){
16156a081dcdSDavid du Colombier 		default:
16166a081dcdSDavid du Colombier 			return -1;
16176a081dcdSDavid du Colombier 		case ' ':
16186a081dcdSDavid du Colombier 			continue;
16196a081dcdSDavid du Colombier 		case ':':			/* start of loop */
16206a081dcdSDavid du Colombier 			loop = strtol(p+1, &lp, 0)-1;
16216a081dcdSDavid du Colombier 			lp--;
16226a081dcdSDavid du Colombier 			if(p == lp)
16236a081dcdSDavid du Colombier 				loop = 7;
16246a081dcdSDavid du Colombier 			p = lp;
16256a081dcdSDavid du Colombier 			continue;
16266a081dcdSDavid du Colombier 		case ';':			/* end of loop */
16276a081dcdSDavid du Colombier 			if(lp == nil)
16286a081dcdSDavid du Colombier 				return -1;
16296a081dcdSDavid du Colombier 			loop--;
16306a081dcdSDavid du Colombier 			if(loop >= 0)
16316a081dcdSDavid du Colombier 				p = lp;
16326a081dcdSDavid du Colombier 			else
16336a081dcdSDavid du Colombier 				lp = nil;
16346a081dcdSDavid du Colombier 			continue;
16356a081dcdSDavid du Colombier 		case 'C':			/* assert clock */
16366a081dcdSDavid du Colombier 			eecd |= Sk;
16376a081dcdSDavid du Colombier 			break;
16386a081dcdSDavid du Colombier 		case 'c':			/* deassert clock */
16396a081dcdSDavid du Colombier 			eecd &= ~Sk;
16406a081dcdSDavid du Colombier 			break;
16416a081dcdSDavid du Colombier 		case 'D':			/* next bit in 'data' byte */
16426a081dcdSDavid du Colombier 			if(loop < 0)
16436a081dcdSDavid du Colombier 				return -1;
16446a081dcdSDavid du Colombier 			if(data & (1<<loop))
16456a081dcdSDavid du Colombier 				eecd |= Di;
16466a081dcdSDavid du Colombier 			else
16476a081dcdSDavid du Colombier 				eecd &= ~Di;
16486a081dcdSDavid du Colombier 			break;
16496a081dcdSDavid du Colombier 		case 'O':			/* collect data output */
16506a081dcdSDavid du Colombier 			i = (csr32r(ctlr, Eecd) & Do) != 0;
16516a081dcdSDavid du Colombier 			if(loop >= 0)
16526a081dcdSDavid du Colombier 				r |= (i<<loop);
16536a081dcdSDavid du Colombier 			else
16546a081dcdSDavid du Colombier 				r = i;
16556a081dcdSDavid du Colombier 			continue;
16566a081dcdSDavid du Colombier 		case 'I':			/* assert data input */
16576a081dcdSDavid du Colombier 			eecd |= Di;
16586a081dcdSDavid du Colombier 			break;
16596a081dcdSDavid du Colombier 		case 'i':			/* deassert data input */
16606a081dcdSDavid du Colombier 			eecd &= ~Di;
16616a081dcdSDavid du Colombier 			break;
16626a081dcdSDavid du Colombier 		case 'S':			/* enable chip select */
16636a081dcdSDavid du Colombier 			eecd |= Cs;
16646a081dcdSDavid du Colombier 			break;
16656a081dcdSDavid du Colombier 		case 's':			/* disable chip select */
16666a081dcdSDavid du Colombier 			eecd &= ~Cs;
16676a081dcdSDavid du Colombier 			break;
16686a081dcdSDavid du Colombier 		}
16696a081dcdSDavid du Colombier 		csr32w(ctlr, Eecd, eecd);
16708466d066SDavid du Colombier 		microdelay(50);
16716a081dcdSDavid du Colombier 	}
16726a081dcdSDavid du Colombier 	if(loop >= 0)
16736a081dcdSDavid du Colombier 		return -1;
16746a081dcdSDavid du Colombier 	return r;
16756a081dcdSDavid du Colombier }
16766a081dcdSDavid du Colombier 
16776a081dcdSDavid du Colombier static int
at93c46r(Ctlr * ctlr)16786a081dcdSDavid du Colombier at93c46r(Ctlr* ctlr)
16796a081dcdSDavid du Colombier {
16806a081dcdSDavid du Colombier 	ushort sum;
16816a081dcdSDavid du Colombier 	char rop[20];
16826a081dcdSDavid du Colombier 	int addr, areq, bits, data, eecd, i;
16836a081dcdSDavid du Colombier 
16846a081dcdSDavid du Colombier 	eecd = csr32r(ctlr, Eecd);
16856a081dcdSDavid du Colombier 	if(eecd & Spi){
16866a081dcdSDavid du Colombier 		print("igbe: SPI EEPROM access not implemented\n");
16876a081dcdSDavid du Colombier 		return 0;
16886a081dcdSDavid du Colombier 	}
16898466d066SDavid du Colombier 	if(eecd & (Eeszaddr|Eesz256))
16906a081dcdSDavid du Colombier 		bits = 8;
16916a081dcdSDavid du Colombier 	else
16926a081dcdSDavid du Colombier 		bits = 6;
16936a081dcdSDavid du Colombier 
16946a081dcdSDavid du Colombier 	sum = 0;
16956a081dcdSDavid du Colombier 
16966a081dcdSDavid du Colombier 	switch(ctlr->id){
16976a081dcdSDavid du Colombier 	default:
16986a081dcdSDavid du Colombier 		areq = 0;
16996a081dcdSDavid du Colombier 		break;
1700aeb1c8a5SDavid du Colombier 	case i82540em:
1701aeb1c8a5SDavid du Colombier 	case i82540eplp:
17025efba406SDavid du Colombier 	case i82541ei:
17035efba406SDavid du Colombier 	case i82541gi:
170426ad7229SDavid du Colombier 	case i82541gi2:
17055efba406SDavid du Colombier 	case i82541pi:
1706c079eed3SDavid du Colombier 	case i82545em:
1707ab3dc52fSDavid du Colombier 	case i82545gmc:
1708aeb1c8a5SDavid du Colombier 	case i82546gb:
17095b7d6169SDavid du Colombier 	case i82546eb:
17107e254d1cSDavid du Colombier 	case i82547ei:
17115efba406SDavid du Colombier 	case i82547gi:
17126a081dcdSDavid du Colombier 		areq = 1;
17136a081dcdSDavid du Colombier 		csr32w(ctlr, Eecd, eecd|Areq);
17146a081dcdSDavid du Colombier 		for(i = 0; i < 1000; i++){
17156a081dcdSDavid du Colombier 			if((eecd = csr32r(ctlr, Eecd)) & Agnt)
17166a081dcdSDavid du Colombier 				break;
17176a081dcdSDavid du Colombier 			microdelay(5);
17186a081dcdSDavid du Colombier 		}
17196a081dcdSDavid du Colombier 		if(!(eecd & Agnt)){
17206a081dcdSDavid du Colombier 			print("igbe: not granted EEPROM access\n");
17216a081dcdSDavid du Colombier 			goto release;
17226a081dcdSDavid du Colombier 		}
17236a081dcdSDavid du Colombier 		break;
17246a081dcdSDavid du Colombier 	}
17258466d066SDavid du Colombier 	snprint(rop, sizeof(rop), "S :%dDCc;", bits+3);
17266a081dcdSDavid du Colombier 
17276a081dcdSDavid du Colombier 	for(addr = 0; addr < 0x40; addr++){
17286a081dcdSDavid du Colombier 		/*
17296a081dcdSDavid du Colombier 		 * Read a word at address 'addr' from the Atmel AT93C46
17306a081dcdSDavid du Colombier 		 * 3-Wire Serial EEPROM or compatible. The EEPROM access is
17316a081dcdSDavid du Colombier 		 * controlled by 4 bits in Eecd. See the AT93C46 datasheet
17326a081dcdSDavid du Colombier 		 * for protocol details.
17336a081dcdSDavid du Colombier 		 */
17346a081dcdSDavid du Colombier 		if(at93c46io(ctlr, rop, (0x06<<bits)|addr) != 0){
17356a081dcdSDavid du Colombier 			print("igbe: can't set EEPROM address 0x%2.2X\n", addr);
17366a081dcdSDavid du Colombier 			goto release;
17376a081dcdSDavid du Colombier 		}
17386a081dcdSDavid du Colombier 		data = at93c46io(ctlr, ":16COc;", 0);
17396a081dcdSDavid du Colombier 		at93c46io(ctlr, "sic", 0);
17406a081dcdSDavid du Colombier 		ctlr->eeprom[addr] = data;
17416a081dcdSDavid du Colombier 		sum += data;
17426a081dcdSDavid du Colombier 	}
17436a081dcdSDavid du Colombier 
17446a081dcdSDavid du Colombier release:
17456a081dcdSDavid du Colombier 	if(areq)
17466a081dcdSDavid du Colombier 		csr32w(ctlr, Eecd, eecd & ~Areq);
17476a081dcdSDavid du Colombier 	return sum;
17486a081dcdSDavid du Colombier }
17496a081dcdSDavid du Colombier 
17505d9682faSDavid du Colombier static int
igbedetach(Ctlr * ctlr)17516a081dcdSDavid du Colombier igbedetach(Ctlr* ctlr)
17526a081dcdSDavid du Colombier {
17535d9682faSDavid du Colombier 	int r, timeo;
17546a081dcdSDavid du Colombier 
17556a081dcdSDavid du Colombier 	/*
17566a081dcdSDavid du Colombier 	 * Perform a device reset to get the chip back to the
17576a081dcdSDavid du Colombier 	 * power-on state, followed by an EEPROM reset to read
17586a081dcdSDavid du Colombier 	 * the defaults for some internal registers.
17596a081dcdSDavid du Colombier 	 */
17606a081dcdSDavid du Colombier 	csr32w(ctlr, Imc, ~0);
17616a081dcdSDavid du Colombier 	csr32w(ctlr, Rctl, 0);
17626a081dcdSDavid du Colombier 	csr32w(ctlr, Tctl, 0);
17636a081dcdSDavid du Colombier 
1764ff8c3af2SDavid du Colombier 	delay(10);
17656a081dcdSDavid du Colombier 
17666a081dcdSDavid du Colombier 	csr32w(ctlr, Ctrl, Devrst);
17675d9682faSDavid du Colombier 	delay(1);
17685d9682faSDavid du Colombier 	for(timeo = 0; timeo < 1000; timeo++){
17695d9682faSDavid du Colombier 		if(!(csr32r(ctlr, Ctrl) & Devrst))
17705d9682faSDavid du Colombier 			break;
17715d9682faSDavid du Colombier 		delay(1);
17725d9682faSDavid du Colombier 	}
17735d9682faSDavid du Colombier 	if(csr32r(ctlr, Ctrl) & Devrst)
17745d9682faSDavid du Colombier 		return -1;
1775aeb1c8a5SDavid du Colombier 	r = csr32r(ctlr, Ctrlext);
1776aeb1c8a5SDavid du Colombier 	csr32w(ctlr, Ctrlext, r|Eerst);
17775d9682faSDavid du Colombier 	delay(1);
17785d9682faSDavid du Colombier 	for(timeo = 0; timeo < 1000; timeo++){
17795d9682faSDavid du Colombier 		if(!(csr32r(ctlr, Ctrlext) & Eerst))
17805d9682faSDavid du Colombier 			break;
17815d9682faSDavid du Colombier 		delay(1);
17825d9682faSDavid du Colombier 	}
17835d9682faSDavid du Colombier 	if(csr32r(ctlr, Ctrlext) & Eerst)
17845d9682faSDavid du Colombier 		return -1;
17856a081dcdSDavid du Colombier 
17866a081dcdSDavid du Colombier 	switch(ctlr->id){
17876a081dcdSDavid du Colombier 	default:
17886a081dcdSDavid du Colombier 		break;
1789aeb1c8a5SDavid du Colombier 	case i82540em:
1790aeb1c8a5SDavid du Colombier 	case i82540eplp:
1791aeb1c8a5SDavid du Colombier 	case i82541gi:
179226ad7229SDavid du Colombier 	case i82541gi2:
17935efba406SDavid du Colombier 	case i82541pi:
1794c079eed3SDavid du Colombier 	case i82545em:
1795ab3dc52fSDavid du Colombier 	case i82545gmc:
17965efba406SDavid du Colombier 	case i82547gi:
1797aeb1c8a5SDavid du Colombier 	case i82546gb:
17985b7d6169SDavid du Colombier 	case i82546eb:
17996a081dcdSDavid du Colombier 		r = csr32r(ctlr, Manc);
18006a081dcdSDavid du Colombier 		r &= ~Arpen;
18016a081dcdSDavid du Colombier 		csr32w(ctlr, Manc, r);
18026a081dcdSDavid du Colombier 		break;
18036a081dcdSDavid du Colombier 	}
18046a081dcdSDavid du Colombier 
18056a081dcdSDavid du Colombier 	csr32w(ctlr, Imc, ~0);
18065d9682faSDavid du Colombier 	delay(1);
18075d9682faSDavid du Colombier 	for(timeo = 0; timeo < 1000; timeo++){
18085d9682faSDavid du Colombier 		if(!csr32r(ctlr, Icr))
18095d9682faSDavid du Colombier 			break;
18105d9682faSDavid du Colombier 		delay(1);
18115d9682faSDavid du Colombier 	}
18125d9682faSDavid du Colombier 	if(csr32r(ctlr, Icr))
18135d9682faSDavid du Colombier 		return -1;
18145d9682faSDavid du Colombier 
18155d9682faSDavid du Colombier 	return 0;
18166a081dcdSDavid du Colombier }
18176a081dcdSDavid du Colombier 
18180809e9a7SDavid du Colombier static void
igbeshutdown(Ether * ether)18190809e9a7SDavid du Colombier igbeshutdown(Ether* ether)
18200809e9a7SDavid du Colombier {
18210809e9a7SDavid du Colombier 	igbedetach(ether->ctlr);
18220809e9a7SDavid du Colombier }
18230809e9a7SDavid du Colombier 
1824ff8c3af2SDavid du Colombier static int
igbereset(Ctlr * ctlr)1825ff8c3af2SDavid du Colombier igbereset(Ctlr* ctlr)
18266a081dcdSDavid du Colombier {
18276a081dcdSDavid du Colombier 	int ctrl, i, pause, r, swdpio, txcw;
18286a081dcdSDavid du Colombier 
18295d9682faSDavid du Colombier 	if(igbedetach(ctlr))
18305d9682faSDavid du Colombier 		return -1;
18316a081dcdSDavid du Colombier 
18326a081dcdSDavid du Colombier 	/*
18336a081dcdSDavid du Colombier 	 * Read the EEPROM, validate the checksum
18346a081dcdSDavid du Colombier 	 * then get the device back to a power-on state.
18356a081dcdSDavid du Colombier 	 */
18366a081dcdSDavid du Colombier 	if((r = at93c46r(ctlr)) != 0xBABA){
18376a081dcdSDavid du Colombier 		print("igbe: bad EEPROM checksum - 0x%4.4uX\n", r);
18386a081dcdSDavid du Colombier 		return -1;
18396a081dcdSDavid du Colombier 	}
18406a081dcdSDavid du Colombier 
18416a081dcdSDavid du Colombier 	/*
18426a081dcdSDavid du Colombier 	 * Snarf and set up the receive addresses.
18436a081dcdSDavid du Colombier 	 * There are 16 addresses. The first should be the MAC address.
18446a081dcdSDavid du Colombier 	 * The others are cleared and not marked valid (MS bit of Rah).
18456a081dcdSDavid du Colombier 	 */
184641dd6b47SDavid du Colombier 	if ((ctlr->id == i82546gb || ctlr->id == i82546eb) &&
184741dd6b47SDavid du Colombier 	    BUSFNO(ctlr->pcidev->tbdf) == 1)
184841dd6b47SDavid du Colombier 		ctlr->eeprom[Ea+2] += 0x100;		/* second interface */
18497e254d1cSDavid du Colombier 	if(ctlr->id == i82541gi && ctlr->eeprom[Ea] == 0xFFFF)
18507e254d1cSDavid du Colombier 		ctlr->eeprom[Ea] = 0xD000;
18516a081dcdSDavid du Colombier 	for(i = Ea; i < Eaddrlen/2; i++){
18526a081dcdSDavid du Colombier 		ctlr->ra[2*i] = ctlr->eeprom[i];
18536a081dcdSDavid du Colombier 		ctlr->ra[2*i+1] = ctlr->eeprom[i]>>8;
18546a081dcdSDavid du Colombier 	}
18557e254d1cSDavid du Colombier 	/* lan id seems to vary on 82543gc; don't use it */
18567e254d1cSDavid du Colombier 	if (ctlr->id != i82543gc) {
18577e254d1cSDavid du Colombier 		r = (csr32r(ctlr, Status) & Lanid) >> 2;
18587e254d1cSDavid du Colombier 		ctlr->ra[5] += r;		/* ea ctlr[1] = ea ctlr[0]+1 */
18597e254d1cSDavid du Colombier 	}
18607e254d1cSDavid du Colombier 
18616a081dcdSDavid du Colombier 	r = (ctlr->ra[3]<<24)|(ctlr->ra[2]<<16)|(ctlr->ra[1]<<8)|ctlr->ra[0];
18626a081dcdSDavid du Colombier 	csr32w(ctlr, Ral, r);
18636a081dcdSDavid du Colombier 	r = 0x80000000|(ctlr->ra[5]<<8)|ctlr->ra[4];
18646a081dcdSDavid du Colombier 	csr32w(ctlr, Rah, r);
18656a081dcdSDavid du Colombier 	for(i = 1; i < 16; i++){
18666a081dcdSDavid du Colombier 		csr32w(ctlr, Ral+i*8, 0);
18676a081dcdSDavid du Colombier 		csr32w(ctlr, Rah+i*8, 0);
18686a081dcdSDavid du Colombier 	}
18696a081dcdSDavid du Colombier 
18706a081dcdSDavid du Colombier 	/*
18716a081dcdSDavid du Colombier 	 * Clear the Multicast Table Array.
18726a081dcdSDavid du Colombier 	 * It's a 4096 bit vector accessed as 128 32-bit registers.
18736a081dcdSDavid du Colombier 	 */
1874ecea9424SDavid du Colombier 	memset(ctlr->mta, 0, sizeof(ctlr->mta));
18756a081dcdSDavid du Colombier 	for(i = 0; i < 128; i++)
18766a081dcdSDavid du Colombier 		csr32w(ctlr, Mta+i*4, 0);
18776a081dcdSDavid du Colombier 
18786a081dcdSDavid du Colombier 	/*
18796a081dcdSDavid du Colombier 	 * Just in case the Eerst didn't load the defaults
1880208510e1SDavid du Colombier 	 * (doesn't appear to fully on the 82543GC), do it manually.
18816a081dcdSDavid du Colombier 	 */
1882208510e1SDavid du Colombier 	if (ctlr->id == i82543gc) {
18836a081dcdSDavid du Colombier 		txcw = csr32r(ctlr, Txcw);
18846a081dcdSDavid du Colombier 		txcw &= ~(TxcwAne|TxcwPauseMASK|TxcwFd);
18856a081dcdSDavid du Colombier 		ctrl = csr32r(ctlr, Ctrl);
18866a081dcdSDavid du Colombier 		ctrl &= ~(SwdpioloMASK|Frcspd|Ilos|Lrst|Fd);
18876a081dcdSDavid du Colombier 
18886a081dcdSDavid du Colombier 		if(ctlr->eeprom[Icw1] & 0x0400){
18896a081dcdSDavid du Colombier 			ctrl |= Fd;
18906a081dcdSDavid du Colombier 			txcw |= TxcwFd;
18916a081dcdSDavid du Colombier 		}
18926a081dcdSDavid du Colombier 		if(ctlr->eeprom[Icw1] & 0x0200)
18936a081dcdSDavid du Colombier 			ctrl |= Lrst;
18946a081dcdSDavid du Colombier 		if(ctlr->eeprom[Icw1] & 0x0010)
18956a081dcdSDavid du Colombier 			ctrl |= Ilos;
18966a081dcdSDavid du Colombier 		if(ctlr->eeprom[Icw1] & 0x0800)
18976a081dcdSDavid du Colombier 			ctrl |= Frcspd;
18986a081dcdSDavid du Colombier 		swdpio = (ctlr->eeprom[Icw1] & 0x01E0)>>5;
18996a081dcdSDavid du Colombier 		ctrl |= swdpio<<SwdpioloSHIFT;
19006a081dcdSDavid du Colombier 		csr32w(ctlr, Ctrl, ctrl);
19016a081dcdSDavid du Colombier 
19026a081dcdSDavid du Colombier 		ctrl = csr32r(ctlr, Ctrlext);
19036a081dcdSDavid du Colombier 		ctrl &= ~(Ips|SwdpiohiMASK);
19046a081dcdSDavid du Colombier 		swdpio = (ctlr->eeprom[Icw2] & 0x00F0)>>4;
19056a081dcdSDavid du Colombier 		if(ctlr->eeprom[Icw1] & 0x1000)
19066a081dcdSDavid du Colombier 			ctrl |= Ips;
19076a081dcdSDavid du Colombier 		ctrl |= swdpio<<SwdpiohiSHIFT;
19086a081dcdSDavid du Colombier 		csr32w(ctlr, Ctrlext, ctrl);
19096a081dcdSDavid du Colombier 
19106a081dcdSDavid du Colombier 		if(ctlr->eeprom[Icw2] & 0x0800)
19116a081dcdSDavid du Colombier 			txcw |= TxcwAne;
19126a081dcdSDavid du Colombier 		pause = (ctlr->eeprom[Icw2] & 0x3000)>>12;
19136a081dcdSDavid du Colombier 		txcw |= pause<<TxcwPauseSHIFT;
19146a081dcdSDavid du Colombier 		switch(pause){
19156a081dcdSDavid du Colombier 		default:
19166a081dcdSDavid du Colombier 			ctlr->fcrtl = 0x00002000;
19176a081dcdSDavid du Colombier 			ctlr->fcrth = 0x00004000;
19186a081dcdSDavid du Colombier 			txcw |= TxcwAs|TxcwPs;
19196a081dcdSDavid du Colombier 			break;
19206a081dcdSDavid du Colombier 		case 0:
19216a081dcdSDavid du Colombier 			ctlr->fcrtl = 0x00002000;
19226a081dcdSDavid du Colombier 			ctlr->fcrth = 0x00004000;
19236a081dcdSDavid du Colombier 			break;
19246a081dcdSDavid du Colombier 		case 2:
19256a081dcdSDavid du Colombier 			ctlr->fcrtl = 0;
19266a081dcdSDavid du Colombier 			ctlr->fcrth = 0;
19276a081dcdSDavid du Colombier 			txcw |= TxcwAs;
19286a081dcdSDavid du Colombier 			break;
19296a081dcdSDavid du Colombier 		}
19306a081dcdSDavid du Colombier 		ctlr->txcw = txcw;
19316a081dcdSDavid du Colombier 		csr32w(ctlr, Txcw, txcw);
1932aeb1c8a5SDavid du Colombier 	}
1933aeb1c8a5SDavid du Colombier 
19346a081dcdSDavid du Colombier 
19356a081dcdSDavid du Colombier 	/*
19366a081dcdSDavid du Colombier 	 * Flow control - values from the datasheet.
19376a081dcdSDavid du Colombier 	 */
19386a081dcdSDavid du Colombier 	csr32w(ctlr, Fcal, 0x00C28001);
19396a081dcdSDavid du Colombier 	csr32w(ctlr, Fcah, 0x00000100);
19406a081dcdSDavid du Colombier 	csr32w(ctlr, Fct, 0x00008808);
19416a081dcdSDavid du Colombier 	csr32w(ctlr, Fcttv, 0x00000100);
19426a081dcdSDavid du Colombier 
19436a081dcdSDavid du Colombier 	csr32w(ctlr, Fcrtl, ctlr->fcrtl);
19446a081dcdSDavid du Colombier 	csr32w(ctlr, Fcrth, ctlr->fcrth);
19456a081dcdSDavid du Colombier 
19466a081dcdSDavid du Colombier 	if(!(csr32r(ctlr, Status) & Tbimode) && igbemii(ctlr) < 0)
19476a081dcdSDavid du Colombier 		return -1;
19486a081dcdSDavid du Colombier 
19496a081dcdSDavid du Colombier 	return 0;
19506a081dcdSDavid du Colombier }
19516a081dcdSDavid du Colombier 
19526a081dcdSDavid du Colombier static void
igbepci(void)19536a081dcdSDavid du Colombier igbepci(void)
19546a081dcdSDavid du Colombier {
19554de34a7eSDavid du Colombier 	int cls;
19566a081dcdSDavid du Colombier 	Pcidev *p;
19576a081dcdSDavid du Colombier 	Ctlr *ctlr;
19584de34a7eSDavid du Colombier 	void *mem;
19596a081dcdSDavid du Colombier 
19606a081dcdSDavid du Colombier 	p = nil;
19616a081dcdSDavid du Colombier 	while(p = pcimatch(p, 0, 0)){
19626a081dcdSDavid du Colombier 		if(p->ccrb != 0x02 || p->ccru != 0)
19636a081dcdSDavid du Colombier 			continue;
19646a081dcdSDavid du Colombier 
19656a081dcdSDavid du Colombier 		switch((p->did<<16)|p->vid){
19666a081dcdSDavid du Colombier 		default:
19676a081dcdSDavid du Colombier 			continue;
1968aeb1c8a5SDavid du Colombier 		case i82543gc:
1969aeb1c8a5SDavid du Colombier 		case i82544ei:
197007c32395SDavid du Colombier 		case i82544eif:
19719a8968c3SDavid du Colombier 		case i82544gc:
1972aeb1c8a5SDavid du Colombier 		case i82547ei:
19735efba406SDavid du Colombier 		case i82547gi:
1974aeb1c8a5SDavid du Colombier 		case i82540em:
1975aeb1c8a5SDavid du Colombier 		case i82540eplp:
19765efba406SDavid du Colombier 		case i82541ei:
1977aeb1c8a5SDavid du Colombier 		case i82541gi:
197826ad7229SDavid du Colombier 		case i82541gi2:
1979d717568cSDavid du Colombier 		case i82541pi:
1980c079eed3SDavid du Colombier 		case i82545em:
1981ab3dc52fSDavid du Colombier 		case i82545gmc:
1982aeb1c8a5SDavid du Colombier 		case i82546gb:
19835b7d6169SDavid du Colombier 		case i82546eb:
19846a081dcdSDavid du Colombier 			break;
19856a081dcdSDavid du Colombier 		}
19866a081dcdSDavid du Colombier 
19874de34a7eSDavid du Colombier 		mem = vmap(p->mem[0].bar & ~0x0F, p->mem[0].size);
19884de34a7eSDavid du Colombier 		if(mem == nil){
19896a081dcdSDavid du Colombier 			print("igbe: can't map %8.8luX\n", p->mem[0].bar);
19906a081dcdSDavid du Colombier 			continue;
19916a081dcdSDavid du Colombier 		}
19926a081dcdSDavid du Colombier 		cls = pcicfgr8(p, PciCLS);
19936a081dcdSDavid du Colombier 		switch(cls){
19946a081dcdSDavid du Colombier 		default:
19950591a7c1SDavid du Colombier 			print("igbe: p->cls %#ux, setting to 0x10\n", p->cls);
19960591a7c1SDavid du Colombier 			p->cls = 0x10;
19970591a7c1SDavid du Colombier 			pcicfgw8(p, PciCLS, p->cls);
19986a081dcdSDavid du Colombier 			break;
19996a081dcdSDavid du Colombier 		case 0x08:
20006a081dcdSDavid du Colombier 		case 0x10:
20016a081dcdSDavid du Colombier 			break;
20026a081dcdSDavid du Colombier 		}
20036a081dcdSDavid du Colombier 		ctlr = malloc(sizeof(Ctlr));
2004aa72973aSDavid du Colombier 		if(ctlr == nil) {
2005aa72973aSDavid du Colombier 			vunmap(mem, p->mem[0].size);
2006aa72973aSDavid du Colombier 			error(Enomem);
2007aa72973aSDavid du Colombier 		}
20084de34a7eSDavid du Colombier 		ctlr->port = p->mem[0].bar & ~0x0F;
20096a081dcdSDavid du Colombier 		ctlr->pcidev = p;
20106a081dcdSDavid du Colombier 		ctlr->id = (p->did<<16)|p->vid;
20116a081dcdSDavid du Colombier 		ctlr->cls = cls*4;
20124de34a7eSDavid du Colombier 		ctlr->nic = mem;
20136a081dcdSDavid du Colombier 
2014ff8c3af2SDavid du Colombier 		if(igbereset(ctlr)){
20156a081dcdSDavid du Colombier 			free(ctlr);
20165979f962SDavid du Colombier 			vunmap(mem, p->mem[0].size);
20176a081dcdSDavid du Colombier 			continue;
20186a081dcdSDavid du Colombier 		}
20196a081dcdSDavid du Colombier 		pcisetbme(p);
20206a081dcdSDavid du Colombier 
20216a081dcdSDavid du Colombier 		if(igbectlrhead != nil)
20226a081dcdSDavid du Colombier 			igbectlrtail->next = ctlr;
20236a081dcdSDavid du Colombier 		else
20246a081dcdSDavid du Colombier 			igbectlrhead = ctlr;
20256a081dcdSDavid du Colombier 		igbectlrtail = ctlr;
20266a081dcdSDavid du Colombier 	}
20276a081dcdSDavid du Colombier }
20286a081dcdSDavid du Colombier 
20296a081dcdSDavid du Colombier static int
igbepnp(Ether * edev)20306a081dcdSDavid du Colombier igbepnp(Ether* edev)
20316a081dcdSDavid du Colombier {
20326a081dcdSDavid du Colombier 	Ctlr *ctlr;
20336a081dcdSDavid du Colombier 
20346a081dcdSDavid du Colombier 	if(igbectlrhead == nil)
20356a081dcdSDavid du Colombier 		igbepci();
20366a081dcdSDavid du Colombier 
20376a081dcdSDavid du Colombier 	/*
20386a081dcdSDavid du Colombier 	 * Any adapter matches if no edev->port is supplied,
20396a081dcdSDavid du Colombier 	 * otherwise the ports must match.
20406a081dcdSDavid du Colombier 	 */
20416a081dcdSDavid du Colombier 	for(ctlr = igbectlrhead; ctlr != nil; ctlr = ctlr->next){
20426a081dcdSDavid du Colombier 		if(ctlr->active)
20436a081dcdSDavid du Colombier 			continue;
20446a081dcdSDavid du Colombier 		if(edev->port == 0 || edev->port == ctlr->port){
20456a081dcdSDavid du Colombier 			ctlr->active = 1;
20466a081dcdSDavid du Colombier 			break;
20476a081dcdSDavid du Colombier 		}
20486a081dcdSDavid du Colombier 	}
20496a081dcdSDavid du Colombier 	if(ctlr == nil)
20506a081dcdSDavid du Colombier 		return -1;
20516a081dcdSDavid du Colombier 
20526a081dcdSDavid du Colombier 	edev->ctlr = ctlr;
20536a081dcdSDavid du Colombier 	edev->port = ctlr->port;
20546a081dcdSDavid du Colombier 	edev->irq = ctlr->pcidev->intl;
20556a081dcdSDavid du Colombier 	edev->tbdf = ctlr->pcidev->tbdf;
20566a081dcdSDavid du Colombier 	edev->mbps = 1000;
20576a081dcdSDavid du Colombier 	memmove(edev->ea, ctlr->ra, Eaddrlen);
20586a081dcdSDavid du Colombier 
20596a081dcdSDavid du Colombier 	/*
20606a081dcdSDavid du Colombier 	 * Linkage to the generic ethernet driver.
20616a081dcdSDavid du Colombier 	 */
20626a081dcdSDavid du Colombier 	edev->attach = igbeattach;
20636a081dcdSDavid du Colombier 	edev->transmit = igbetransmit;
20646a081dcdSDavid du Colombier 	edev->interrupt = igbeinterrupt;
20656a081dcdSDavid du Colombier 	edev->ifstat = igbeifstat;
20666a081dcdSDavid du Colombier 	edev->ctl = igbectl;
20676a081dcdSDavid du Colombier 
20686a081dcdSDavid du Colombier 	edev->arg = edev;
20696a081dcdSDavid du Colombier 	edev->promiscuous = igbepromiscuous;
20700809e9a7SDavid du Colombier 	edev->shutdown = igbeshutdown;
2071ecea9424SDavid du Colombier 	edev->multicast = igbemulticast;
2072ff8c3af2SDavid du Colombier 
20736a081dcdSDavid du Colombier 	return 0;
20746a081dcdSDavid du Colombier }
20756a081dcdSDavid du Colombier 
20766a081dcdSDavid du Colombier void
etherigbelink(void)20776a081dcdSDavid du Colombier etherigbelink(void)
20786a081dcdSDavid du Colombier {
20796a081dcdSDavid du Colombier 	addethercard("i82543", igbepnp);
20806a081dcdSDavid du Colombier 	addethercard("igbe", igbepnp);
20816a081dcdSDavid du Colombier }
20828466d066SDavid du Colombier 
2083