xref: /plan9-contrib/sys/src/9/pc/etherigbe.c (revision 61d44851dbae9c6db4696bac4b180d884ecea735)
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,
447e696e17dSDavid du Colombier 	/*
448e696e17dSDavid du Colombier 	 * these were 256, 1024 & 64, but 52, 253 & 9 are usually ample;
449e696e17dSDavid du Colombier 	 * however cpu servers and terminals can need more receive buffers
450e696e17dSDavid du Colombier 	 * due to bursts of traffic.
451e696e17dSDavid du Colombier 	 */
452588d0145SDavid du Colombier 	Nrd		= 128,		/* multiple of 8 */
453e696e17dSDavid du Colombier 	Nrb		= 1024,		/* private receive buffers per Ctlr */
454588d0145SDavid du Colombier 	Ntd		= 32,		/* multiple of 8 */
4556a081dcdSDavid du Colombier };
4566a081dcdSDavid du Colombier 
4576a081dcdSDavid du Colombier typedef struct Ctlr Ctlr;
458c02f0a41SDavid du Colombier struct Ctlr {
4598466d066SDavid du Colombier 	int	port;
4606a081dcdSDavid du Colombier 	Pcidev*	pcidev;
4616a081dcdSDavid du Colombier 	Ctlr*	next;
462208510e1SDavid du Colombier 	Ether*	edev;
4636a081dcdSDavid du Colombier 	int	active;
4646a081dcdSDavid du Colombier 	int	started;
4656a081dcdSDavid du Colombier 	int	id;
4666a081dcdSDavid du Colombier 	int	cls;
4676a081dcdSDavid du Colombier 	ushort	eeprom[0x40];
4686a081dcdSDavid du Colombier 
4696a081dcdSDavid du Colombier 	QLock	alock;			/* attach */
4706a081dcdSDavid du Colombier 	void*	alloc;			/* receive/transmit descriptors */
4716a081dcdSDavid du Colombier 	int	nrd;
4726a081dcdSDavid du Colombier 	int	ntd;
473c02f0a41SDavid du Colombier 	int	nrb;			/* # bufs this Ctlr has in the pool */
4746a081dcdSDavid du Colombier 
4756a081dcdSDavid du Colombier 	int*	nic;
4766a081dcdSDavid du Colombier 	Lock	imlock;
4776a081dcdSDavid du Colombier 	int	im;			/* interrupt mask */
4786a081dcdSDavid du Colombier 
4796a081dcdSDavid du Colombier 	Mii*	mii;
4806a081dcdSDavid du Colombier 	Rendez	lrendez;
4816a081dcdSDavid du Colombier 	int	lim;
4826a081dcdSDavid du Colombier 
4836a081dcdSDavid du Colombier 	int	link;
4846a081dcdSDavid du Colombier 
485217e9e83SDavid du Colombier 	Watermark wmrb;
486217e9e83SDavid du Colombier 	Watermark wmrd;
487217e9e83SDavid du Colombier 	Watermark wmtd;
488217e9e83SDavid du Colombier 
4896a081dcdSDavid du Colombier 	QLock	slock;
4906a081dcdSDavid du Colombier 	uint	statistics[Nstatistics];
4916a081dcdSDavid du Colombier 	uint	lsleep;
4926a081dcdSDavid du Colombier 	uint	lintr;
4936a081dcdSDavid du Colombier 	uint	rsleep;
4946a081dcdSDavid du Colombier 	uint	rintr;
4956a081dcdSDavid du Colombier 	uint	txdw;
4966a081dcdSDavid du Colombier 	uint	tintr;
4976a081dcdSDavid du Colombier 	uint	ixsm;
4986a081dcdSDavid du Colombier 	uint	ipcs;
4996a081dcdSDavid du Colombier 	uint	tcpcs;
5006a081dcdSDavid du Colombier 
5016a081dcdSDavid du Colombier 	uchar	ra[Eaddrlen];		/* receive address */
5026a081dcdSDavid du Colombier 	ulong	mta[128];		/* multicast table array */
5036a081dcdSDavid du Colombier 
5046a081dcdSDavid du Colombier 	Rendez	rrendez;
5056a081dcdSDavid du Colombier 	int	rim;
506c02f0a41SDavid du Colombier 	int	rdfree;			/* rx descriptors awaiting packets */
5076a081dcdSDavid du Colombier 	Rd*	rdba;			/* receive descriptor base address */
5086a081dcdSDavid du Colombier 	Block**	rb;			/* receive buffers */
5096a081dcdSDavid du Colombier 	int	rdh;			/* receive descriptor head */
5106a081dcdSDavid du Colombier 	int	rdt;			/* receive descriptor tail */
5116a081dcdSDavid du Colombier 	int	rdtr;			/* receive delay timer ring value */
5126a081dcdSDavid du Colombier 
5136a081dcdSDavid du Colombier 	Lock	tlock;
5146a081dcdSDavid du Colombier 	int	tdfree;
5156a081dcdSDavid du Colombier 	Td*	tdba;			/* transmit descriptor base address */
5166a081dcdSDavid du Colombier 	Block**	tb;			/* transmit buffers */
5176a081dcdSDavid du Colombier 	int	tdh;			/* transmit descriptor head */
5186a081dcdSDavid du Colombier 	int	tdt;			/* transmit descriptor tail */
5196a081dcdSDavid du Colombier 
5206a081dcdSDavid du Colombier 	int	txcw;
5216a081dcdSDavid du Colombier 	int	fcrtl;
5226a081dcdSDavid du Colombier 	int	fcrth;
523c02f0a41SDavid du Colombier };
5246a081dcdSDavid du Colombier 
5256a081dcdSDavid du Colombier #define csr32r(c, r)	(*((c)->nic+((r)/4)))
5266a081dcdSDavid du Colombier #define csr32w(c, r, v)	(*((c)->nic+((r)/4)) = (v))
5276a081dcdSDavid du Colombier 
5286a081dcdSDavid du Colombier static Ctlr* igbectlrhead;
5296a081dcdSDavid du Colombier static Ctlr* igbectlrtail;
5306a081dcdSDavid du Colombier 
5316a081dcdSDavid du Colombier static Lock igberblock;		/* free receive Blocks */
532208510e1SDavid du Colombier static Block* igberbpool;	/* receive Blocks for all igbe controllers */
533217e9e83SDavid du Colombier static int nrbfull;	/* # of rcv Blocks with data awaiting processing */
5346a081dcdSDavid du Colombier 
5356a081dcdSDavid du Colombier static char* statistics[Nstatistics] = {
5366a081dcdSDavid du Colombier 	"CRC Error",
5376a081dcdSDavid du Colombier 	"Alignment Error",
5386a081dcdSDavid du Colombier 	"Symbol Error",
5396a081dcdSDavid du Colombier 	"RX Error",
5406a081dcdSDavid du Colombier 	"Missed Packets",
5416a081dcdSDavid du Colombier 	"Single Collision",
5426a081dcdSDavid du Colombier 	"Excessive Collisions",
5436a081dcdSDavid du Colombier 	"Multiple Collision",
5446a081dcdSDavid du Colombier 	"Late Collisions",
5456a081dcdSDavid du Colombier 	nil,
5466a081dcdSDavid du Colombier 	"Collision",
5476a081dcdSDavid du Colombier 	"Transmit Underrun",
5486a081dcdSDavid du Colombier 	"Defer",
5496a081dcdSDavid du Colombier 	"Transmit - No CRS",
5506a081dcdSDavid du Colombier 	"Sequence Error",
5516a081dcdSDavid du Colombier 	"Carrier Extension Error",
5526a081dcdSDavid du Colombier 	"Receive Error Length",
5536a081dcdSDavid du Colombier 	nil,
5546a081dcdSDavid du Colombier 	"XON Received",
5556a081dcdSDavid du Colombier 	"XON Transmitted",
5566a081dcdSDavid du Colombier 	"XOFF Received",
5576a081dcdSDavid du Colombier 	"XOFF Transmitted",
5586a081dcdSDavid du Colombier 	"FC Received Unsupported",
5596a081dcdSDavid du Colombier 	"Packets Received (64 Bytes)",
5606a081dcdSDavid du Colombier 	"Packets Received (65-127 Bytes)",
5616a081dcdSDavid du Colombier 	"Packets Received (128-255 Bytes)",
5626a081dcdSDavid du Colombier 	"Packets Received (256-511 Bytes)",
5636a081dcdSDavid du Colombier 	"Packets Received (512-1023 Bytes)",
5646a081dcdSDavid du Colombier 	"Packets Received (1024-1522 Bytes)",
5656a081dcdSDavid du Colombier 	"Good Packets Received",
5666a081dcdSDavid du Colombier 	"Broadcast Packets Received",
5676a081dcdSDavid du Colombier 	"Multicast Packets Received",
5686a081dcdSDavid du Colombier 	"Good Packets Transmitted",
5696a081dcdSDavid du Colombier 	nil,
5706a081dcdSDavid du Colombier 	"Good Octets Received",
5716a081dcdSDavid du Colombier 	nil,
5726a081dcdSDavid du Colombier 	"Good Octets Transmitted",
5736a081dcdSDavid du Colombier 	nil,
5746a081dcdSDavid du Colombier 	nil,
5756a081dcdSDavid du Colombier 	nil,
5766a081dcdSDavid du Colombier 	"Receive No Buffers",
5776a081dcdSDavid du Colombier 	"Receive Undersize",
5786a081dcdSDavid du Colombier 	"Receive Fragment",
5796a081dcdSDavid du Colombier 	"Receive Oversize",
5806a081dcdSDavid du Colombier 	"Receive Jabber",
5816a081dcdSDavid du Colombier 	nil,
5826a081dcdSDavid du Colombier 	nil,
5836a081dcdSDavid du Colombier 	nil,
5846a081dcdSDavid du Colombier 	"Total Octets Received",
5856a081dcdSDavid du Colombier 	nil,
5866a081dcdSDavid du Colombier 	"Total Octets Transmitted",
5876a081dcdSDavid du Colombier 	nil,
5886a081dcdSDavid du Colombier 	"Total Packets Received",
5896a081dcdSDavid du Colombier 	"Total Packets Transmitted",
5906a081dcdSDavid du Colombier 	"Packets Transmitted (64 Bytes)",
5916a081dcdSDavid du Colombier 	"Packets Transmitted (65-127 Bytes)",
5926a081dcdSDavid du Colombier 	"Packets Transmitted (128-255 Bytes)",
5936a081dcdSDavid du Colombier 	"Packets Transmitted (256-511 Bytes)",
5946a081dcdSDavid du Colombier 	"Packets Transmitted (512-1023 Bytes)",
5956a081dcdSDavid du Colombier 	"Packets Transmitted (1024-1522 Bytes)",
5966a081dcdSDavid du Colombier 	"Multicast Packets Transmitted",
5976a081dcdSDavid du Colombier 	"Broadcast Packets Transmitted",
5986a081dcdSDavid du Colombier 	"TCP Segmentation Context Transmitted",
5996a081dcdSDavid du Colombier 	"TCP Segmentation Context Fail",
6006a081dcdSDavid du Colombier };
6016a081dcdSDavid du Colombier 
6026a081dcdSDavid du Colombier static long
igbeifstat(Ether * edev,void * a,long n,ulong offset)6036a081dcdSDavid du Colombier igbeifstat(Ether* edev, void* a, long n, ulong offset)
6046a081dcdSDavid du Colombier {
6056a081dcdSDavid du Colombier 	Ctlr *ctlr;
606217e9e83SDavid du Colombier 	char *p, *s, *e;
6076a081dcdSDavid du Colombier 	int i, l, r;
6086a081dcdSDavid du Colombier 	uvlong tuvl, ruvl;
6096a081dcdSDavid du Colombier 
6106a081dcdSDavid du Colombier 	ctlr = edev->ctlr;
6116a081dcdSDavid du Colombier 	qlock(&ctlr->slock);
61246136019SDavid du Colombier 	p = malloc(READSTR);
613aa72973aSDavid du Colombier 	if(p == nil) {
614aa72973aSDavid du Colombier 		qunlock(&ctlr->slock);
615aa72973aSDavid du Colombier 		error(Enomem);
616aa72973aSDavid du Colombier 	}
6176a081dcdSDavid du Colombier 	l = 0;
6186a081dcdSDavid du Colombier 	for(i = 0; i < Nstatistics; i++){
6196a081dcdSDavid du Colombier 		r = csr32r(ctlr, Statistics+i*4);
6206a081dcdSDavid du Colombier 		if((s = statistics[i]) == nil)
6216a081dcdSDavid du Colombier 			continue;
6226a081dcdSDavid du Colombier 		switch(i){
6236a081dcdSDavid du Colombier 		case Gorcl:
6246a081dcdSDavid du Colombier 		case Gotcl:
6256a081dcdSDavid du Colombier 		case Torl:
6266a081dcdSDavid du Colombier 		case Totl:
6276a081dcdSDavid du Colombier 			ruvl = r;
6286a081dcdSDavid du Colombier 			ruvl += ((uvlong)csr32r(ctlr, Statistics+(i+1)*4))<<32;
6296a081dcdSDavid du Colombier 			tuvl = ruvl;
6306a081dcdSDavid du Colombier 			tuvl += ctlr->statistics[i];
6316a081dcdSDavid du Colombier 			tuvl += ((uvlong)ctlr->statistics[i+1])<<32;
6326a081dcdSDavid du Colombier 			if(tuvl == 0)
6336a081dcdSDavid du Colombier 				continue;
6346a081dcdSDavid du Colombier 			ctlr->statistics[i] = tuvl;
6356a081dcdSDavid du Colombier 			ctlr->statistics[i+1] = tuvl>>32;
63646136019SDavid du Colombier 			l += snprint(p+l, READSTR-l, "%s: %llud %llud\n",
6376a081dcdSDavid du Colombier 				s, tuvl, ruvl);
6386a081dcdSDavid du Colombier 			i++;
6396a081dcdSDavid du Colombier 			break;
6406a081dcdSDavid du Colombier 
6416a081dcdSDavid du Colombier 		default:
6426a081dcdSDavid du Colombier 			ctlr->statistics[i] += r;
6436a081dcdSDavid du Colombier 			if(ctlr->statistics[i] == 0)
6446a081dcdSDavid du Colombier 				continue;
64546136019SDavid du Colombier 			l += snprint(p+l, READSTR-l, "%s: %ud %ud\n",
6466a081dcdSDavid du Colombier 				s, ctlr->statistics[i], r);
6476a081dcdSDavid du Colombier 			break;
6486a081dcdSDavid du Colombier 		}
6496a081dcdSDavid du Colombier 	}
6506a081dcdSDavid du Colombier 
65146136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "lintr: %ud %ud\n",
6526a081dcdSDavid du Colombier 		ctlr->lintr, ctlr->lsleep);
65346136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "rintr: %ud %ud\n",
6546a081dcdSDavid du Colombier 		ctlr->rintr, ctlr->rsleep);
65546136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "tintr: %ud %ud\n",
6566a081dcdSDavid du Colombier 		ctlr->tintr, ctlr->txdw);
65746136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "ixcs: %ud %ud %ud\n",
6586a081dcdSDavid du Colombier 		ctlr->ixsm, ctlr->ipcs, ctlr->tcpcs);
65946136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "rdtr: %ud\n", ctlr->rdtr);
66046136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "Ctrlext: %08x\n", csr32r(ctlr, Ctrlext));
6616a081dcdSDavid du Colombier 
66246136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "eeprom:");
6636a081dcdSDavid du Colombier 	for(i = 0; i < 0x40; i++){
6646a081dcdSDavid du Colombier 		if(i && ((i & 0x07) == 0))
66546136019SDavid du Colombier 			l += snprint(p+l, READSTR-l, "\n       ");
66646136019SDavid du Colombier 		l += snprint(p+l, READSTR-l, " %4.4uX", ctlr->eeprom[i]);
6676a081dcdSDavid du Colombier 	}
66846136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "\n");
6696a081dcdSDavid du Colombier 
6706a081dcdSDavid du Colombier 	if(ctlr->mii != nil && ctlr->mii->curphy != nil){
671923c9daaSDavid du Colombier 		l += snprint(p+l, READSTR-l, "phy:   ");
6726a081dcdSDavid du Colombier 		for(i = 0; i < NMiiPhyr; i++){
6736a081dcdSDavid du Colombier 			if(i && ((i & 0x07) == 0))
67446136019SDavid du Colombier 				l += snprint(p+l, READSTR-l, "\n       ");
6756a081dcdSDavid du Colombier 			r = miimir(ctlr->mii, i);
67646136019SDavid du Colombier 			l += snprint(p+l, READSTR-l, " %4.4uX", r);
6776a081dcdSDavid du Colombier 		}
67846136019SDavid du Colombier 		snprint(p+l, READSTR-l, "\n");
6796a081dcdSDavid du Colombier 	}
680217e9e83SDavid du Colombier 	e = p + READSTR;
681217e9e83SDavid du Colombier 	s = p + l + 1;
682217e9e83SDavid du Colombier 	s = seprintmark(s, e, &ctlr->wmrb);
683217e9e83SDavid du Colombier 	s = seprintmark(s, e, &ctlr->wmrd);
684217e9e83SDavid du Colombier 	s = seprintmark(s, e, &ctlr->wmtd);
685217e9e83SDavid du Colombier 	USED(s);
686217e9e83SDavid du Colombier 
6876a081dcdSDavid du Colombier 	n = readstr(offset, a, n, p);
6886a081dcdSDavid du Colombier 	free(p);
6896a081dcdSDavid du Colombier 	qunlock(&ctlr->slock);
6906a081dcdSDavid du Colombier 
6916a081dcdSDavid du Colombier 	return n;
6926a081dcdSDavid du Colombier }
6936a081dcdSDavid du Colombier 
6946a081dcdSDavid du Colombier enum {
6956a081dcdSDavid du Colombier 	CMrdtr,
6966a081dcdSDavid du Colombier };
6976a081dcdSDavid du Colombier 
6986a081dcdSDavid du Colombier static Cmdtab igbectlmsg[] = {
6996a081dcdSDavid du Colombier 	CMrdtr,	"rdtr",	2,
7006a081dcdSDavid du Colombier };
7016a081dcdSDavid du Colombier 
7026a081dcdSDavid du Colombier static long
igbectl(Ether * edev,void * buf,long n)7036a081dcdSDavid du Colombier igbectl(Ether* edev, void* buf, long n)
7046a081dcdSDavid du Colombier {
7056a081dcdSDavid du Colombier 	int v;
7066a081dcdSDavid du Colombier 	char *p;
7076a081dcdSDavid du Colombier 	Ctlr *ctlr;
7086a081dcdSDavid du Colombier 	Cmdbuf *cb;
7096a081dcdSDavid du Colombier 	Cmdtab *ct;
7106a081dcdSDavid du Colombier 
7116a081dcdSDavid du Colombier 	if((ctlr = edev->ctlr) == nil)
7126a081dcdSDavid du Colombier 		error(Enonexist);
7136a081dcdSDavid du Colombier 
7146a081dcdSDavid du Colombier 	cb = parsecmd(buf, n);
7156a081dcdSDavid du Colombier 	if(waserror()){
7166a081dcdSDavid du Colombier 		free(cb);
7176a081dcdSDavid du Colombier 		nexterror();
7186a081dcdSDavid du Colombier 	}
7196a081dcdSDavid du Colombier 
7206a081dcdSDavid du Colombier 	ct = lookupcmd(cb, igbectlmsg, nelem(igbectlmsg));
7216a081dcdSDavid du Colombier 	switch(ct->index){
7226a081dcdSDavid du Colombier 	case CMrdtr:
7236a081dcdSDavid du Colombier 		v = strtol(cb->f[1], &p, 0);
7246a081dcdSDavid du Colombier 		if(v < 0 || p == cb->f[1] || v > 0xFFFF)
7256a081dcdSDavid du Colombier 			error(Ebadarg);
7266520663fSDavid du Colombier 		ctlr->rdtr = v;
7276a081dcdSDavid du Colombier 		csr32w(ctlr, Rdtr, Fpd|v);
7286a081dcdSDavid du Colombier 		break;
7296a081dcdSDavid du Colombier 	}
7306a081dcdSDavid du Colombier 	free(cb);
7316a081dcdSDavid du Colombier 	poperror();
7326a081dcdSDavid du Colombier 
7336a081dcdSDavid du Colombier 	return n;
7346a081dcdSDavid du Colombier }
7356a081dcdSDavid du Colombier 
7366a081dcdSDavid du Colombier static void
igbepromiscuous(void * arg,int on)7376a081dcdSDavid du Colombier igbepromiscuous(void* arg, int on)
7386a081dcdSDavid du Colombier {
7396a081dcdSDavid du Colombier 	int rctl;
7406a081dcdSDavid du Colombier 	Ctlr *ctlr;
7416a081dcdSDavid du Colombier 	Ether *edev;
7426a081dcdSDavid du Colombier 
7436a081dcdSDavid du Colombier 	edev = arg;
7446a081dcdSDavid du Colombier 	ctlr = edev->ctlr;
7456a081dcdSDavid du Colombier 
7466a081dcdSDavid du Colombier 	rctl = csr32r(ctlr, Rctl);
7476a081dcdSDavid du Colombier 	rctl &= ~MoMASK;
7486a081dcdSDavid du Colombier 	rctl |= Mo47b36;
7496a081dcdSDavid du Colombier 	if(on)
7506a081dcdSDavid du Colombier 		rctl |= Upe|Mpe;
7516a081dcdSDavid du Colombier 	else
7526a081dcdSDavid du Colombier 		rctl &= ~(Upe|Mpe);
753b1707c5dSDavid du Colombier 	csr32w(ctlr, Rctl, rctl|Mpe);	/* temporarily keep Mpe on */
7546a081dcdSDavid du Colombier }
7556a081dcdSDavid du Colombier 
756ecea9424SDavid du Colombier static void
igbemulticast(void * arg,uchar * addr,int add)757b1707c5dSDavid du Colombier igbemulticast(void* arg, uchar* addr, int add)
758ecea9424SDavid du Colombier {
759ecea9424SDavid du Colombier 	int bit, x;
760ecea9424SDavid du Colombier 	Ctlr *ctlr;
761ecea9424SDavid du Colombier 	Ether *edev;
762ecea9424SDavid du Colombier 
763ecea9424SDavid du Colombier 	edev = arg;
764ecea9424SDavid du Colombier 	ctlr = edev->ctlr;
765ecea9424SDavid du Colombier 
766ecea9424SDavid du Colombier 	x = addr[5]>>1;
767ecea9424SDavid du Colombier 	bit = ((addr[5] & 1)<<4)|(addr[4]>>4);
768b1707c5dSDavid du Colombier 	/*
769b1707c5dSDavid du Colombier 	 * multiple ether addresses can hash to the same filter bit,
770b1707c5dSDavid du Colombier 	 * so it's never safe to clear a filter bit.
771b1707c5dSDavid du Colombier 	 * if we want to clear filter bits, we need to keep track of
772b1707c5dSDavid du Colombier 	 * all the multicast addresses in use, clear all the filter bits,
773b1707c5dSDavid du Colombier 	 * then set the ones corresponding to in-use addresses.
774b1707c5dSDavid du Colombier 	 */
775b1707c5dSDavid du Colombier 	if(add)
776ecea9424SDavid du Colombier 		ctlr->mta[x] |= 1<<bit;
777b1707c5dSDavid du Colombier //	else
778b1707c5dSDavid du Colombier //		ctlr->mta[x] &= ~(1<<bit);
779ecea9424SDavid du Colombier 
780ecea9424SDavid du Colombier 	csr32w(ctlr, Mta+x*4, ctlr->mta[x]);
781ecea9424SDavid du Colombier }
782ecea9424SDavid du Colombier 
7836a081dcdSDavid du Colombier static Block*
igberballoc(void)7846a081dcdSDavid du Colombier igberballoc(void)
7856a081dcdSDavid du Colombier {
7866a081dcdSDavid du Colombier 	Block *bp;
7876a081dcdSDavid du Colombier 
7886a081dcdSDavid du Colombier 	ilock(&igberblock);
7896a081dcdSDavid du Colombier 	if((bp = igberbpool) != nil){
7906a081dcdSDavid du Colombier 		igberbpool = bp->next;
7916a081dcdSDavid du Colombier 		bp->next = nil;
792*61d44851SDavid du Colombier 		ainc(&bp->ref);	/* prevent bp from being freed */
7936a081dcdSDavid du Colombier 	}
7946a081dcdSDavid du Colombier 	iunlock(&igberblock);
7956a081dcdSDavid du Colombier 
7966a081dcdSDavid du Colombier 	return bp;
7976a081dcdSDavid du Colombier }
7986a081dcdSDavid du Colombier 
7996a081dcdSDavid du Colombier static void
igberbfree(Block * bp)8006a081dcdSDavid du Colombier igberbfree(Block* bp)
8016a081dcdSDavid du Colombier {
802ff8c3af2SDavid du Colombier 	bp->rp = bp->lim - Rbsz;
803ff8c3af2SDavid du Colombier 	bp->wp = bp->rp;
804bfb6eab9SDavid du Colombier  	bp->flag &= ~(Bipck | Budpck | Btcpck | Bpktck);
805ff8c3af2SDavid du Colombier 
8066a081dcdSDavid du Colombier 	ilock(&igberblock);
8076a081dcdSDavid du Colombier 	bp->next = igberbpool;
8086a081dcdSDavid du Colombier 	igberbpool = bp;
809217e9e83SDavid du Colombier 	nrbfull--;
8106a081dcdSDavid du Colombier 	iunlock(&igberblock);
8116a081dcdSDavid du Colombier }
8126a081dcdSDavid du Colombier 
8136a081dcdSDavid du Colombier static void
igbeim(Ctlr * ctlr,int im)8146a081dcdSDavid du Colombier igbeim(Ctlr* ctlr, int im)
8156a081dcdSDavid du Colombier {
8166a081dcdSDavid du Colombier 	ilock(&ctlr->imlock);
8176a081dcdSDavid du Colombier 	ctlr->im |= im;
8186a081dcdSDavid du Colombier 	csr32w(ctlr, Ims, ctlr->im);
8196a081dcdSDavid du Colombier 	iunlock(&ctlr->imlock);
8206a081dcdSDavid du Colombier }
8216a081dcdSDavid du Colombier 
8226a081dcdSDavid du Colombier static int
igbelim(void * ctlr)8236a081dcdSDavid du Colombier igbelim(void* ctlr)
8246a081dcdSDavid du Colombier {
8256a081dcdSDavid du Colombier 	return ((Ctlr*)ctlr)->lim != 0;
8266a081dcdSDavid du Colombier }
8276a081dcdSDavid du Colombier 
8286a081dcdSDavid du Colombier static void
igbelproc(void * arg)829ff8c3af2SDavid du Colombier igbelproc(void* arg)
8306a081dcdSDavid du Colombier {
8316a081dcdSDavid du Colombier 	Ctlr *ctlr;
8326a081dcdSDavid du Colombier 	Ether *edev;
8336a081dcdSDavid du Colombier 	MiiPhy *phy;
8346a081dcdSDavid du Colombier 	int ctrl, r;
8356a081dcdSDavid du Colombier 
836ff8c3af2SDavid du Colombier 	edev = arg;
8376a081dcdSDavid du Colombier 	ctlr = edev->ctlr;
8386a081dcdSDavid du Colombier 	for(;;){
839bf3a53f8SDavid du Colombier 		if(ctlr->mii == nil || ctlr->mii->curphy == nil) {
840bf3a53f8SDavid du Colombier 			sched();
8416a081dcdSDavid du Colombier 			continue;
842bf3a53f8SDavid du Colombier 		}
8436a081dcdSDavid du Colombier 
8446a081dcdSDavid du Colombier 		/*
8456a081dcdSDavid du Colombier 		 * To do:
8466a081dcdSDavid du Colombier 		 *	logic to manage status change,
8476a081dcdSDavid du Colombier 		 *	this is incomplete but should work
8486a081dcdSDavid du Colombier 		 *	one time to set up the hardware.
8496a081dcdSDavid du Colombier 		 *
8506a081dcdSDavid du Colombier 		 *	MiiPhy.speed, etc. should be in Mii.
8516a081dcdSDavid du Colombier 		 */
8526a081dcdSDavid du Colombier 		if(miistatus(ctlr->mii) < 0)
8536a081dcdSDavid du Colombier 			//continue;
8546a081dcdSDavid du Colombier 			goto enable;
8556a081dcdSDavid du Colombier 
8566a081dcdSDavid du Colombier 		phy = ctlr->mii->curphy;
8576a081dcdSDavid du Colombier 		ctrl = csr32r(ctlr, Ctrl);
8586a081dcdSDavid du Colombier 
8596a081dcdSDavid du Colombier 		switch(ctlr->id){
860aeb1c8a5SDavid du Colombier 		case i82543gc:
861aeb1c8a5SDavid du Colombier 		case i82544ei:
86207c32395SDavid du Colombier 		case i82544eif:
8636a081dcdSDavid du Colombier 		default:
8646a081dcdSDavid du Colombier 			if(!(ctrl & Asde)){
8656a081dcdSDavid du Colombier 				ctrl &= ~(SspeedMASK|Ilos|Fd);
8666a081dcdSDavid du Colombier 				ctrl |= Frcdplx|Frcspd;
8676a081dcdSDavid du Colombier 				if(phy->speed == 1000)
8686a081dcdSDavid du Colombier 					ctrl |= Sspeed1000;
8696a081dcdSDavid du Colombier 				else if(phy->speed == 100)
8706a081dcdSDavid du Colombier 					ctrl |= Sspeed100;
8716a081dcdSDavid du Colombier 				if(phy->fd)
8726a081dcdSDavid du Colombier 					ctrl |= Fd;
8736a081dcdSDavid du Colombier 			}
8746a081dcdSDavid du Colombier 			break;
875aeb1c8a5SDavid du Colombier 
876aeb1c8a5SDavid du Colombier 		case i82540em:
877aeb1c8a5SDavid du Colombier 		case i82540eplp:
878aeb1c8a5SDavid du Colombier 		case i82547gi:
879aeb1c8a5SDavid du Colombier 		case i82541gi:
8808466d066SDavid du Colombier 		case i82541gi2:
881d717568cSDavid du Colombier 		case i82541pi:
8826a081dcdSDavid du Colombier 			break;
8836a081dcdSDavid du Colombier 		}
8846a081dcdSDavid du Colombier 
8856a081dcdSDavid du Colombier 		/*
8866a081dcdSDavid du Colombier 		 * Collision Distance.
8876a081dcdSDavid du Colombier 		 */
8886a081dcdSDavid du Colombier 		r = csr32r(ctlr, Tctl);
8896a081dcdSDavid du Colombier 		r &= ~ColdMASK;
8906a081dcdSDavid du Colombier 		if(phy->fd)
8916a081dcdSDavid du Colombier 			r |= 64<<ColdSHIFT;
8926a081dcdSDavid du Colombier 		else
8936a081dcdSDavid du Colombier 			r |= 512<<ColdSHIFT;
8946a081dcdSDavid du Colombier 		csr32w(ctlr, Tctl, r);
8956a081dcdSDavid du Colombier 
8966a081dcdSDavid du Colombier 		/*
8976a081dcdSDavid du Colombier 		 * Flow control.
8986a081dcdSDavid du Colombier 		 */
8996a081dcdSDavid du Colombier 		if(phy->rfc)
9006a081dcdSDavid du Colombier 			ctrl |= Rfce;
9016a081dcdSDavid du Colombier 		if(phy->tfc)
9026a081dcdSDavid du Colombier 			ctrl |= Tfce;
9036a081dcdSDavid du Colombier 		csr32w(ctlr, Ctrl, ctrl);
9046a081dcdSDavid du Colombier 
9056a081dcdSDavid du Colombier enable:
9066a081dcdSDavid du Colombier 		ctlr->lim = 0;
9076a081dcdSDavid du Colombier 		igbeim(ctlr, Lsc);
9086a081dcdSDavid du Colombier 
9096a081dcdSDavid du Colombier 		ctlr->lsleep++;
9106a081dcdSDavid du Colombier 		sleep(&ctlr->lrendez, igbelim, ctlr);
9116a081dcdSDavid du Colombier 	}
9126a081dcdSDavid du Colombier }
9136a081dcdSDavid du Colombier 
9146a081dcdSDavid du Colombier static void
igbetxinit(Ctlr * ctlr)9156a081dcdSDavid du Colombier igbetxinit(Ctlr* ctlr)
9166a081dcdSDavid du Colombier {
9176a081dcdSDavid du Colombier 	int i, r;
9186a081dcdSDavid du Colombier 	Block *bp;
9196a081dcdSDavid du Colombier 
9206a081dcdSDavid du Colombier 	csr32w(ctlr, Tctl, (0x0F<<CtSHIFT)|Psp|(66<<ColdSHIFT));
9216a081dcdSDavid du Colombier 	switch(ctlr->id){
9226a081dcdSDavid du Colombier 	default:
9236a081dcdSDavid du Colombier 		r = 6;
9246a081dcdSDavid du Colombier 		break;
925aeb1c8a5SDavid du Colombier 	case i82543gc:
926aeb1c8a5SDavid du Colombier 	case i82544ei:
92707c32395SDavid du Colombier 	case i82544eif:
9289a8968c3SDavid du Colombier 	case i82544gc:
929aeb1c8a5SDavid du Colombier 	case i82540em:
930aeb1c8a5SDavid du Colombier 	case i82540eplp:
9315efba406SDavid du Colombier 	case i82541ei:
932aeb1c8a5SDavid du Colombier 	case i82541gi:
9338466d066SDavid du Colombier 	case i82541gi2:
934d717568cSDavid du Colombier 	case i82541pi:
935c079eed3SDavid du Colombier 	case i82545em:
936ab3dc52fSDavid du Colombier 	case i82545gmc:
937aeb1c8a5SDavid du Colombier 	case i82546gb:
9385b7d6169SDavid du Colombier 	case i82546eb:
9395efba406SDavid du Colombier 	case i82547ei:
940aeb1c8a5SDavid du Colombier 	case i82547gi:
9416a081dcdSDavid du Colombier 		r = 8;
9426a081dcdSDavid du Colombier 		break;
9436a081dcdSDavid du Colombier 	}
9446a081dcdSDavid du Colombier 	csr32w(ctlr, Tipg, (6<<20)|(8<<10)|r);
9456a081dcdSDavid du Colombier 	csr32w(ctlr, Ait, 0);
9466a081dcdSDavid du Colombier 	csr32w(ctlr, Txdmac, 0);
9476a081dcdSDavid du Colombier 
9486a081dcdSDavid du Colombier 	csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba));
9496a081dcdSDavid du Colombier 	csr32w(ctlr, Tdbah, 0);
9506a081dcdSDavid du Colombier 	csr32w(ctlr, Tdlen, ctlr->ntd*sizeof(Td));
9516a081dcdSDavid du Colombier 	ctlr->tdh = PREV(0, ctlr->ntd);
9526a081dcdSDavid du Colombier 	csr32w(ctlr, Tdh, 0);
9536a081dcdSDavid du Colombier 	ctlr->tdt = 0;
9546a081dcdSDavid du Colombier 	csr32w(ctlr, Tdt, 0);
9556a081dcdSDavid du Colombier 
9566a081dcdSDavid du Colombier 	for(i = 0; i < ctlr->ntd; i++){
9576a081dcdSDavid du Colombier 		if((bp = ctlr->tb[i]) != nil){
9586a081dcdSDavid du Colombier 			ctlr->tb[i] = nil;
9596a081dcdSDavid du Colombier 			freeb(bp);
9606a081dcdSDavid du Colombier 		}
9616a081dcdSDavid du Colombier 		memset(&ctlr->tdba[i], 0, sizeof(Td));
9626a081dcdSDavid du Colombier 	}
9636a081dcdSDavid du Colombier 	ctlr->tdfree = ctlr->ntd;
9646a081dcdSDavid du Colombier 
9656a081dcdSDavid du Colombier 	csr32w(ctlr, Tidv, 128);
9666a081dcdSDavid du Colombier 	r = (4<<WthreshSHIFT)|(4<<HthreshSHIFT)|(8<<PthreshSHIFT);
9676a081dcdSDavid du Colombier 
9686a081dcdSDavid du Colombier 	switch(ctlr->id){
9696a081dcdSDavid du Colombier 	default:
9706a081dcdSDavid du Colombier 		break;
971aeb1c8a5SDavid du Colombier 	case i82540em:
972aeb1c8a5SDavid du Colombier 	case i82540eplp:
973aeb1c8a5SDavid du Colombier 	case i82547gi:
974c079eed3SDavid du Colombier 	case i82545em:
975ab3dc52fSDavid du Colombier 	case i82545gmc:
976aeb1c8a5SDavid du Colombier 	case i82546gb:
9775b7d6169SDavid du Colombier 	case i82546eb:
978aeb1c8a5SDavid du Colombier 	case i82541gi:
9798466d066SDavid du Colombier 	case i82541gi2:
980d717568cSDavid du Colombier 	case i82541pi:
9816a081dcdSDavid du Colombier 		r = csr32r(ctlr, Txdctl);
9826a081dcdSDavid du Colombier 		r &= ~WthreshMASK;
9836a081dcdSDavid du Colombier 		r |= Gran|(4<<WthreshSHIFT);
9846a081dcdSDavid du Colombier 
9856a081dcdSDavid du Colombier 		csr32w(ctlr, Tadv, 64);
9866a081dcdSDavid du Colombier 		break;
9876a081dcdSDavid du Colombier 	}
9886a081dcdSDavid du Colombier 
9896a081dcdSDavid du Colombier 	csr32w(ctlr, Txdctl, r);
9906a081dcdSDavid du Colombier 
9916a081dcdSDavid du Colombier 	r = csr32r(ctlr, Tctl);
9926a081dcdSDavid du Colombier 	r |= Ten;
9936a081dcdSDavid du Colombier 	csr32w(ctlr, Tctl, r);
9946a081dcdSDavid du Colombier }
9956a081dcdSDavid du Colombier 
9966a081dcdSDavid du Colombier static void
igbetransmit(Ether * edev)9976a081dcdSDavid du Colombier igbetransmit(Ether* edev)
9986a081dcdSDavid du Colombier {
9996a081dcdSDavid du Colombier 	Td *td;
10006a081dcdSDavid du Colombier 	Block *bp;
10016a081dcdSDavid du Colombier 	Ctlr *ctlr;
10026a081dcdSDavid du Colombier 	int tdh, tdt;
10036a081dcdSDavid du Colombier 
10046a081dcdSDavid du Colombier 	ctlr = edev->ctlr;
10056a081dcdSDavid du Colombier 
10066a081dcdSDavid du Colombier 	ilock(&ctlr->tlock);
10076a081dcdSDavid du Colombier 
10086a081dcdSDavid du Colombier 	/*
10096a081dcdSDavid du Colombier 	 * Free any completed packets
10106a081dcdSDavid du Colombier 	 */
10116a081dcdSDavid du Colombier 	tdh = ctlr->tdh;
10126a081dcdSDavid du Colombier 	while(NEXT(tdh, ctlr->ntd) != csr32r(ctlr, Tdh)){
10136a081dcdSDavid du Colombier 		if((bp = ctlr->tb[tdh]) != nil){
10146a081dcdSDavid du Colombier 			ctlr->tb[tdh] = nil;
10156a081dcdSDavid du Colombier 			freeb(bp);
10166a081dcdSDavid du Colombier 		}
10176a081dcdSDavid du Colombier 		memset(&ctlr->tdba[tdh], 0, sizeof(Td));
10186a081dcdSDavid du Colombier 		tdh = NEXT(tdh, ctlr->ntd);
10196a081dcdSDavid du Colombier 	}
10206a081dcdSDavid du Colombier 	ctlr->tdh = tdh;
10216a081dcdSDavid du Colombier 
10226a081dcdSDavid du Colombier 	/*
10236a081dcdSDavid du Colombier 	 * Try to fill the ring back up.
10246a081dcdSDavid du Colombier 	 */
10256a081dcdSDavid du Colombier 	tdt = ctlr->tdt;
10266a081dcdSDavid du Colombier 	while(NEXT(tdt, ctlr->ntd) != tdh){
1027ff8c3af2SDavid du Colombier 		if((bp = qget(edev->oq)) == nil)
10286a081dcdSDavid du Colombier 			break;
10296a081dcdSDavid du Colombier 		td = &ctlr->tdba[tdt];
10306a081dcdSDavid du Colombier 		td->addr[0] = PCIWADDR(bp->rp);
10316a081dcdSDavid du Colombier 		td->control = ((BLEN(bp) & LenMASK)<<LenSHIFT);
10326a081dcdSDavid du Colombier 		td->control |= Dext|Ifcs|Teop|DtypeDD;
10336a081dcdSDavid du Colombier 		ctlr->tb[tdt] = bp;
1034217e9e83SDavid du Colombier 		/* note size of queue of tds awaiting transmission */
1035217e9e83SDavid du Colombier 		notemark(&ctlr->wmtd, (tdt + Ntd - tdh) % Ntd);
10366a081dcdSDavid du Colombier 		tdt = NEXT(tdt, ctlr->ntd);
10376a081dcdSDavid du Colombier 		if(NEXT(tdt, ctlr->ntd) == tdh){
10386a081dcdSDavid du Colombier 			td->control |= Rs;
10396a081dcdSDavid du Colombier 			ctlr->txdw++;
10406a081dcdSDavid du Colombier 			ctlr->tdt = tdt;
10416a081dcdSDavid du Colombier 			csr32w(ctlr, Tdt, tdt);
10426a081dcdSDavid du Colombier 			igbeim(ctlr, Txdw);
10436a081dcdSDavid du Colombier 			break;
10446a081dcdSDavid du Colombier 		}
10456a081dcdSDavid du Colombier 		ctlr->tdt = tdt;
10466a081dcdSDavid du Colombier 		csr32w(ctlr, Tdt, tdt);
10476a081dcdSDavid du Colombier 	}
10486a081dcdSDavid du Colombier 
10496a081dcdSDavid du Colombier 	iunlock(&ctlr->tlock);
10506a081dcdSDavid du Colombier }
10516a081dcdSDavid du Colombier 
10526a081dcdSDavid du Colombier static void
igbereplenish(Ctlr * ctlr)10536a081dcdSDavid du Colombier igbereplenish(Ctlr* ctlr)
10546a081dcdSDavid du Colombier {
10556a081dcdSDavid du Colombier 	Rd *rd;
10566a081dcdSDavid du Colombier 	int rdt;
10576a081dcdSDavid du Colombier 	Block *bp;
10586a081dcdSDavid du Colombier 
10596a081dcdSDavid du Colombier 	rdt = ctlr->rdt;
10606a081dcdSDavid du Colombier 	while(NEXT(rdt, ctlr->nrd) != ctlr->rdh){
10616a081dcdSDavid du Colombier 		rd = &ctlr->rdba[rdt];
10626a081dcdSDavid du Colombier 		if(ctlr->rb[rdt] == nil){
10636a081dcdSDavid du Colombier 			bp = igberballoc();
10646a081dcdSDavid du Colombier 			if(bp == nil){
1065208510e1SDavid du Colombier 				iprint("#l%d: igbereplenish: no available buffers\n",
1066208510e1SDavid du Colombier 					ctlr->edev->ctlrno);
10676a081dcdSDavid du Colombier 				break;
10686a081dcdSDavid du Colombier 			}
10696a081dcdSDavid du Colombier 			ctlr->rb[rdt] = bp;
10706a081dcdSDavid du Colombier 			rd->addr[0] = PCIWADDR(bp->rp);
10716a081dcdSDavid du Colombier 			rd->addr[1] = 0;
10726a081dcdSDavid du Colombier 		}
10736a081dcdSDavid du Colombier 		coherence();
10746a081dcdSDavid du Colombier 		rd->status = 0;
10756a081dcdSDavid du Colombier 		rdt = NEXT(rdt, ctlr->nrd);
10766a081dcdSDavid du Colombier 		ctlr->rdfree++;
10776a081dcdSDavid du Colombier 	}
10786a081dcdSDavid du Colombier 	ctlr->rdt = rdt;
10796a081dcdSDavid du Colombier 	csr32w(ctlr, Rdt, rdt);
10806a081dcdSDavid du Colombier }
10816a081dcdSDavid du Colombier 
10826a081dcdSDavid du Colombier static void
igberxinit(Ctlr * ctlr)10836a081dcdSDavid du Colombier igberxinit(Ctlr* ctlr)
10846a081dcdSDavid du Colombier {
10856a081dcdSDavid du Colombier 	int i;
10866a081dcdSDavid du Colombier 	Block *bp;
10876a081dcdSDavid du Colombier 
1088b1707c5dSDavid du Colombier 	/* temporarily keep Mpe on */
1089b1707c5dSDavid du Colombier 	csr32w(ctlr, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF|Mpe);
10906a081dcdSDavid du Colombier 
10916a081dcdSDavid du Colombier 	csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba));
10926a081dcdSDavid du Colombier 	csr32w(ctlr, Rdbah, 0);
10936a081dcdSDavid du Colombier 	csr32w(ctlr, Rdlen, ctlr->nrd*sizeof(Rd));
10946a081dcdSDavid du Colombier 	ctlr->rdh = 0;
10956a081dcdSDavid du Colombier 	csr32w(ctlr, Rdh, 0);
10966a081dcdSDavid du Colombier 	ctlr->rdt = 0;
10976a081dcdSDavid du Colombier 	csr32w(ctlr, Rdt, 0);
10986a081dcdSDavid du Colombier 	ctlr->rdtr = 0;
10996a081dcdSDavid du Colombier 	csr32w(ctlr, Rdtr, Fpd|0);
11006a081dcdSDavid du Colombier 
11016a081dcdSDavid du Colombier 	for(i = 0; i < ctlr->nrd; i++){
11026a081dcdSDavid du Colombier 		if((bp = ctlr->rb[i]) != nil){
11036a081dcdSDavid du Colombier 			ctlr->rb[i] = nil;
11046a081dcdSDavid du Colombier 			freeb(bp);
11056a081dcdSDavid du Colombier 		}
11066a081dcdSDavid du Colombier 	}
11076a081dcdSDavid du Colombier 	igbereplenish(ctlr);
1108217e9e83SDavid du Colombier 	nrbfull = 0;
11096a081dcdSDavid du Colombier 
11106a081dcdSDavid du Colombier 	switch(ctlr->id){
1111aeb1c8a5SDavid du Colombier 	case i82540em:
1112aeb1c8a5SDavid du Colombier 	case i82540eplp:
1113aeb1c8a5SDavid du Colombier 	case i82541gi:
11148466d066SDavid du Colombier 	case i82541gi2:
1115d717568cSDavid du Colombier 	case i82541pi:
1116c079eed3SDavid du Colombier 	case i82545em:
1117ab3dc52fSDavid du Colombier 	case i82545gmc:
1118aeb1c8a5SDavid du Colombier 	case i82546gb:
11195b7d6169SDavid du Colombier 	case i82546eb:
1120aeb1c8a5SDavid du Colombier 	case i82547gi:
11216a081dcdSDavid du Colombier 		csr32w(ctlr, Radv, 64);
11226a081dcdSDavid du Colombier 		break;
11236a081dcdSDavid du Colombier 	}
11246a081dcdSDavid du Colombier 	csr32w(ctlr, Rxdctl, (8<<WthreshSHIFT)|(8<<HthreshSHIFT)|4);
11256a081dcdSDavid du Colombier 
11266a081dcdSDavid du Colombier 	/*
11270591a7c1SDavid du Colombier 	 * Disable checksum offload as it has known bugs.
11286a081dcdSDavid du Colombier 	 */
11290591a7c1SDavid du Colombier 	csr32w(ctlr, Rxcsum, ETHERHDRSIZE<<PcssSHIFT);
11306a081dcdSDavid du Colombier }
11316a081dcdSDavid du Colombier 
11326a081dcdSDavid du Colombier static int
igberim(void * ctlr)11336a081dcdSDavid du Colombier igberim(void* ctlr)
11346a081dcdSDavid du Colombier {
11356a081dcdSDavid du Colombier 	return ((Ctlr*)ctlr)->rim != 0;
11366a081dcdSDavid du Colombier }
11376a081dcdSDavid du Colombier 
11386a081dcdSDavid du Colombier static void
igberproc(void * arg)1139ff8c3af2SDavid du Colombier igberproc(void* arg)
11406a081dcdSDavid du Colombier {
11416a081dcdSDavid du Colombier 	Rd *rd;
11426a081dcdSDavid du Colombier 	Block *bp;
11436a081dcdSDavid du Colombier 	Ctlr *ctlr;
1144217e9e83SDavid du Colombier 	int r, rdh, passed;
11456a081dcdSDavid du Colombier 	Ether *edev;
11466a081dcdSDavid du Colombier 
1147ff8c3af2SDavid du Colombier 	edev = arg;
11486a081dcdSDavid du Colombier 	ctlr = edev->ctlr;
11496a081dcdSDavid du Colombier 
11506a081dcdSDavid du Colombier 	igberxinit(ctlr);
11516a081dcdSDavid du Colombier 	r = csr32r(ctlr, Rctl);
11526a081dcdSDavid du Colombier 	r |= Ren;
11536a081dcdSDavid du Colombier 	csr32w(ctlr, Rctl, r);
11546a081dcdSDavid du Colombier 	for(;;){
11556a081dcdSDavid du Colombier 		ctlr->rim = 0;
11566a081dcdSDavid du Colombier 		igbeim(ctlr, Rxt0|Rxo|Rxdmt0|Rxseq);
11576a081dcdSDavid du Colombier 		ctlr->rsleep++;
11586a081dcdSDavid du Colombier 		sleep(&ctlr->rrendez, igberim, ctlr);
11596a081dcdSDavid du Colombier 
11606a081dcdSDavid du Colombier 		rdh = ctlr->rdh;
1161217e9e83SDavid du Colombier 		passed = 0;
11626a081dcdSDavid du Colombier 		for(;;){
11636a081dcdSDavid du Colombier 			rd = &ctlr->rdba[rdh];
11646a081dcdSDavid du Colombier 
11656a081dcdSDavid du Colombier 			if(!(rd->status & Rdd))
11666a081dcdSDavid du Colombier 				break;
11676a081dcdSDavid du Colombier 
11686a081dcdSDavid du Colombier 			/*
11696a081dcdSDavid du Colombier 			 * Accept eop packets with no errors.
11706a081dcdSDavid du Colombier 			 * With no errors and the Ixsm bit set,
11716a081dcdSDavid du Colombier 			 * the descriptor status Tpcs and Ipcs bits give
11726a081dcdSDavid du Colombier 			 * an indication of whether the checksums were
11736a081dcdSDavid du Colombier 			 * calculated and valid.
11746a081dcdSDavid du Colombier 			 */
11758ccda836SDavid du Colombier 			/* ignore checksum offload as it has known bugs. */
11768ccda836SDavid du Colombier 			rd->errors &= ~(Ipe | Tcpe);
11776a081dcdSDavid du Colombier 			if((rd->status & Reop) && rd->errors == 0){
11786a081dcdSDavid du Colombier 				bp = ctlr->rb[rdh];
11796a081dcdSDavid du Colombier 				ctlr->rb[rdh] = nil;
1180ff8c3af2SDavid du Colombier 				bp->wp += rd->length;
11816a081dcdSDavid du Colombier 				bp->next = nil;
11828ccda836SDavid du Colombier 				/* ignore checksum offload as it has known bugs. */
11838ccda836SDavid du Colombier 				if(0 && !(rd->status & Ixsm)){
11846a081dcdSDavid du Colombier 					ctlr->ixsm++;
11856a081dcdSDavid du Colombier 					if(rd->status & Ipcs){
11866a081dcdSDavid du Colombier 						/*
11876a081dcdSDavid du Colombier 						 * IP checksum calculated
11886a081dcdSDavid du Colombier 						 * (and valid as errors == 0).
11896a081dcdSDavid du Colombier 						 */
11906a081dcdSDavid du Colombier 						ctlr->ipcs++;
11916a081dcdSDavid du Colombier 						bp->flag |= Bipck;
11926a081dcdSDavid du Colombier 					}
11936a081dcdSDavid du Colombier 					if(rd->status & Tcpcs){
11946a081dcdSDavid du Colombier 						/*
11956a081dcdSDavid du Colombier 						 * TCP/UDP checksum calculated
11966a081dcdSDavid du Colombier 						 * (and valid as errors == 0).
11976a081dcdSDavid du Colombier 						 */
11986a081dcdSDavid du Colombier 						ctlr->tcpcs++;
11996a081dcdSDavid du Colombier 						bp->flag |= Btcpck|Budpck;
12006a081dcdSDavid du Colombier 					}
12016a081dcdSDavid du Colombier 					bp->checksum = rd->checksum;
12026a081dcdSDavid du Colombier 					bp->flag |= Bpktck;
12036a081dcdSDavid du Colombier 				}
1204217e9e83SDavid du Colombier 				ilock(&igberblock);
1205217e9e83SDavid du Colombier 				nrbfull++;
1206217e9e83SDavid du Colombier 				iunlock(&igberblock);
1207217e9e83SDavid du Colombier 				notemark(&ctlr->wmrb, nrbfull);
1208ff8c3af2SDavid du Colombier 				etheriq(edev, bp, 1);
1209217e9e83SDavid du Colombier 				passed++;
12106a081dcdSDavid du Colombier 			}
12116a081dcdSDavid du Colombier 			else if(ctlr->rb[rdh] != nil){
12126a081dcdSDavid du Colombier 				freeb(ctlr->rb[rdh]);
12136a081dcdSDavid du Colombier 				ctlr->rb[rdh] = nil;
12146a081dcdSDavid du Colombier 			}
12156a081dcdSDavid du Colombier 
12166a081dcdSDavid du Colombier 			memset(rd, 0, sizeof(Rd));
12176a081dcdSDavid du Colombier 			coherence();
12186a081dcdSDavid du Colombier 			ctlr->rdfree--;
12196a081dcdSDavid du Colombier 			rdh = NEXT(rdh, ctlr->nrd);
12206a081dcdSDavid du Colombier 		}
12216a081dcdSDavid du Colombier 		ctlr->rdh = rdh;
12226a081dcdSDavid du Colombier 
12236a081dcdSDavid du Colombier 		if(ctlr->rdfree < ctlr->nrd/2 || (ctlr->rim & Rxdmt0))
12246a081dcdSDavid du Colombier 			igbereplenish(ctlr);
1225217e9e83SDavid du Colombier 		/* note how many rds had full buffers */
1226217e9e83SDavid du Colombier 		notemark(&ctlr->wmrd, passed);
12276a081dcdSDavid du Colombier 	}
12286a081dcdSDavid du Colombier }
12296a081dcdSDavid du Colombier 
12306a081dcdSDavid du Colombier static void
igbeattach(Ether * edev)12316a081dcdSDavid du Colombier igbeattach(Ether* edev)
12326a081dcdSDavid du Colombier {
12336a081dcdSDavid du Colombier 	Block *bp;
12346a081dcdSDavid du Colombier 	Ctlr *ctlr;
12356a081dcdSDavid du Colombier 	char name[KNAMELEN];
12366a081dcdSDavid du Colombier 
12376a081dcdSDavid du Colombier 	ctlr = edev->ctlr;
1238208510e1SDavid du Colombier 	ctlr->edev = edev;			/* point back to Ether* */
12396a081dcdSDavid du Colombier 	qlock(&ctlr->alock);
1240ea58ad6fSDavid du Colombier 	if(ctlr->alloc != nil){			/* already allocated? */
12416a081dcdSDavid du Colombier 		qunlock(&ctlr->alock);
12426a081dcdSDavid du Colombier 		return;
12436a081dcdSDavid du Colombier 	}
12446a081dcdSDavid du Colombier 
1245aa72973aSDavid du Colombier 	ctlr->tb = nil;
1246aa72973aSDavid du Colombier 	ctlr->rb = nil;
1247aa72973aSDavid du Colombier 	ctlr->alloc = nil;
1248aa72973aSDavid du Colombier 	ctlr->nrb = 0;
12496a081dcdSDavid du Colombier 	if(waserror()){
12506a081dcdSDavid du Colombier 		while(ctlr->nrb > 0){
12516a081dcdSDavid du Colombier 			bp = igberballoc();
12526a081dcdSDavid du Colombier 			bp->free = nil;
12536a081dcdSDavid du Colombier 			freeb(bp);
12546a081dcdSDavid du Colombier 			ctlr->nrb--;
12556a081dcdSDavid du Colombier 		}
12566a081dcdSDavid du Colombier 		free(ctlr->tb);
12576a081dcdSDavid du Colombier 		ctlr->tb = nil;
12586a081dcdSDavid du Colombier 		free(ctlr->rb);
12596a081dcdSDavid du Colombier 		ctlr->rb = nil;
12606a081dcdSDavid du Colombier 		free(ctlr->alloc);
12616a081dcdSDavid du Colombier 		ctlr->alloc = nil;
12626a081dcdSDavid du Colombier 		qunlock(&ctlr->alock);
12636a081dcdSDavid du Colombier 		nexterror();
12646a081dcdSDavid du Colombier 	}
12656a081dcdSDavid du Colombier 
1266aa72973aSDavid du Colombier 	ctlr->nrd = ROUND(Nrd, 8);
1267aa72973aSDavid du Colombier 	ctlr->ntd = ROUND(Ntd, 8);
1268aa72973aSDavid du Colombier 	ctlr->alloc = malloc(ctlr->nrd*sizeof(Rd)+ctlr->ntd*sizeof(Td) + 127);
1269aa72973aSDavid du Colombier 	if(ctlr->alloc == nil) {
1270aa72973aSDavid du Colombier 		print("igbe: can't allocate ctlr->alloc\n");
1271aa72973aSDavid du Colombier 		error(Enomem);
1272aa72973aSDavid du Colombier 	}
1273aa72973aSDavid du Colombier 	ctlr->rdba = (Rd*)ROUNDUP((uintptr)ctlr->alloc, 128);
1274aa72973aSDavid du Colombier 	ctlr->tdba = (Td*)(ctlr->rdba+ctlr->nrd);
1275aa72973aSDavid du Colombier 
1276aa72973aSDavid du Colombier 	ctlr->rb = malloc(ctlr->nrd*sizeof(Block*));
1277aa72973aSDavid du Colombier 	ctlr->tb = malloc(ctlr->ntd*sizeof(Block*));
1278aa72973aSDavid du Colombier 	if (ctlr->rb == nil || ctlr->tb == nil) {
1279aa72973aSDavid du Colombier 		print("igbe: can't allocate ctlr->rb or ctlr->tb\n");
1280aa72973aSDavid du Colombier 		error(Enomem);
1281aa72973aSDavid du Colombier 	}
1282aa72973aSDavid du Colombier 
12836a081dcdSDavid du Colombier 	for(ctlr->nrb = 0; ctlr->nrb < Nrb; ctlr->nrb++){
12846a081dcdSDavid du Colombier 		if((bp = allocb(Rbsz)) == nil)
12856a081dcdSDavid du Colombier 			break;
12866a081dcdSDavid du Colombier 		bp->free = igberbfree;
12876a081dcdSDavid du Colombier 		freeb(bp);
12886a081dcdSDavid du Colombier 	}
1289217e9e83SDavid du Colombier 	initmark(&ctlr->wmrb, Nrb, "rcv bufs unprocessed");
1290217e9e83SDavid du Colombier 	initmark(&ctlr->wmrd, Nrd-1, "rcv descrs processed at once");
1291217e9e83SDavid du Colombier 	initmark(&ctlr->wmtd, Ntd-1, "xmit descr queue len");
12926a081dcdSDavid du Colombier 
12936a081dcdSDavid du Colombier 	snprint(name, KNAMELEN, "#l%dlproc", edev->ctlrno);
12946a081dcdSDavid du Colombier 	kproc(name, igbelproc, edev);
12956a081dcdSDavid du Colombier 
12966a081dcdSDavid du Colombier 	snprint(name, KNAMELEN, "#l%drproc", edev->ctlrno);
12976a081dcdSDavid du Colombier 	kproc(name, igberproc, edev);
12986a081dcdSDavid du Colombier 
12996a081dcdSDavid du Colombier 	igbetxinit(ctlr);
13006a081dcdSDavid du Colombier 
13016a081dcdSDavid du Colombier 	qunlock(&ctlr->alock);
13026a081dcdSDavid du Colombier 	poperror();
13036a081dcdSDavid du Colombier }
13046a081dcdSDavid du Colombier 
13056a081dcdSDavid du Colombier static void
igbeinterrupt(Ureg *,void * arg)13066a081dcdSDavid du Colombier igbeinterrupt(Ureg*, void* arg)
13076a081dcdSDavid du Colombier {
13086a081dcdSDavid du Colombier 	Ctlr *ctlr;
13096a081dcdSDavid du Colombier 	Ether *edev;
13106a081dcdSDavid du Colombier 	int icr, im, txdw;
13116a081dcdSDavid du Colombier 
13126a081dcdSDavid du Colombier 	edev = arg;
13136a081dcdSDavid du Colombier 	ctlr = edev->ctlr;
13146a081dcdSDavid du Colombier 
13156a081dcdSDavid du Colombier 	ilock(&ctlr->imlock);
13166a081dcdSDavid du Colombier 	csr32w(ctlr, Imc, ~0);
13176a081dcdSDavid du Colombier 	im = ctlr->im;
13186a081dcdSDavid du Colombier 	txdw = 0;
13196a081dcdSDavid du Colombier 
13206a081dcdSDavid du Colombier 	while((icr = csr32r(ctlr, Icr) & ctlr->im) != 0){
13216a081dcdSDavid du Colombier 		if(icr & Lsc){
13226a081dcdSDavid du Colombier 			im &= ~Lsc;
13236a081dcdSDavid du Colombier 			ctlr->lim = icr & Lsc;
13246a081dcdSDavid du Colombier 			wakeup(&ctlr->lrendez);
13256a081dcdSDavid du Colombier 			ctlr->lintr++;
13266a081dcdSDavid du Colombier 		}
13276a081dcdSDavid du Colombier 		if(icr & (Rxt0|Rxo|Rxdmt0|Rxseq)){
13286a081dcdSDavid du Colombier 			im &= ~(Rxt0|Rxo|Rxdmt0|Rxseq);
13296a081dcdSDavid du Colombier 			ctlr->rim = icr & (Rxt0|Rxo|Rxdmt0|Rxseq);
13306a081dcdSDavid du Colombier 			wakeup(&ctlr->rrendez);
13316a081dcdSDavid du Colombier 			ctlr->rintr++;
13326a081dcdSDavid du Colombier 		}
13336a081dcdSDavid du Colombier 		if(icr & Txdw){
13346a081dcdSDavid du Colombier 			im &= ~Txdw;
13356a081dcdSDavid du Colombier 			txdw++;
13366a081dcdSDavid du Colombier 			ctlr->tintr++;
13376a081dcdSDavid du Colombier 		}
13386a081dcdSDavid du Colombier 	}
13396a081dcdSDavid du Colombier 
13406a081dcdSDavid du Colombier 	ctlr->im = im;
13416a081dcdSDavid du Colombier 	csr32w(ctlr, Ims, im);
13426a081dcdSDavid du Colombier 	iunlock(&ctlr->imlock);
13436a081dcdSDavid du Colombier 
13446a081dcdSDavid du Colombier 	if(txdw)
13456a081dcdSDavid du Colombier 		igbetransmit(edev);
13466a081dcdSDavid du Colombier }
13476a081dcdSDavid du Colombier 
13486a081dcdSDavid du Colombier static int
i82543mdior(Ctlr * ctlr,int n)13496a081dcdSDavid du Colombier i82543mdior(Ctlr* ctlr, int n)
13506a081dcdSDavid du Colombier {
13516a081dcdSDavid du Colombier 	int ctrl, data, i, r;
13526a081dcdSDavid du Colombier 
13536a081dcdSDavid du Colombier 	/*
13546a081dcdSDavid du Colombier 	 * Read n bits from the Management Data I/O Interface.
13556a081dcdSDavid du Colombier 	 */
13566a081dcdSDavid du Colombier 	ctrl = csr32r(ctlr, Ctrl);
13576a081dcdSDavid du Colombier 	r = (ctrl & ~Mddo)|Mdco;
13586a081dcdSDavid du Colombier 	data = 0;
13596a081dcdSDavid du Colombier 	for(i = n-1; i >= 0; i--){
13606a081dcdSDavid du Colombier 		if(csr32r(ctlr, Ctrl) & Mdd)
13616a081dcdSDavid du Colombier 			data |= (1<<i);
13626a081dcdSDavid du Colombier 		csr32w(ctlr, Ctrl, Mdc|r);
13636a081dcdSDavid du Colombier 		csr32w(ctlr, Ctrl, r);
13646a081dcdSDavid du Colombier 	}
13656a081dcdSDavid du Colombier 	csr32w(ctlr, Ctrl, ctrl);
13666a081dcdSDavid du Colombier 
13676a081dcdSDavid du Colombier 	return data;
13686a081dcdSDavid du Colombier }
13696a081dcdSDavid du Colombier 
13706a081dcdSDavid du Colombier static int
i82543mdiow(Ctlr * ctlr,int bits,int n)13716a081dcdSDavid du Colombier i82543mdiow(Ctlr* ctlr, int bits, int n)
13726a081dcdSDavid du Colombier {
13736a081dcdSDavid du Colombier 	int ctrl, i, r;
13746a081dcdSDavid du Colombier 
13756a081dcdSDavid du Colombier 	/*
13766a081dcdSDavid du Colombier 	 * Write n bits to the Management Data I/O Interface.
13776a081dcdSDavid du Colombier 	 */
13786a081dcdSDavid du Colombier 	ctrl = csr32r(ctlr, Ctrl);
13796a081dcdSDavid du Colombier 	r = Mdco|Mddo|ctrl;
13806a081dcdSDavid du Colombier 	for(i = n-1; i >= 0; i--){
13816a081dcdSDavid du Colombier 		if(bits & (1<<i))
13826a081dcdSDavid du Colombier 			r |= Mdd;
13836a081dcdSDavid du Colombier 		else
13846a081dcdSDavid du Colombier 			r &= ~Mdd;
13856a081dcdSDavid du Colombier 		csr32w(ctlr, Ctrl, Mdc|r);
13866a081dcdSDavid du Colombier 		csr32w(ctlr, Ctrl, r);
13876a081dcdSDavid du Colombier 	}
13886a081dcdSDavid du Colombier 	csr32w(ctlr, Ctrl, ctrl);
13896a081dcdSDavid du Colombier 
13906a081dcdSDavid du Colombier 	return 0;
13916a081dcdSDavid du Colombier }
13926a081dcdSDavid du Colombier 
13936a081dcdSDavid du Colombier static int
i82543miimir(Mii * mii,int pa,int ra)13946a081dcdSDavid du Colombier i82543miimir(Mii* mii, int pa, int ra)
13956a081dcdSDavid du Colombier {
13966a081dcdSDavid du Colombier 	int data;
13976a081dcdSDavid du Colombier 	Ctlr *ctlr;
13986a081dcdSDavid du Colombier 
13996a081dcdSDavid du Colombier 	ctlr = mii->ctlr;
14006a081dcdSDavid du Colombier 
14016a081dcdSDavid du Colombier 	/*
14026a081dcdSDavid du Colombier 	 * MII Management Interface Read.
14036a081dcdSDavid du Colombier 	 *
14046a081dcdSDavid du Colombier 	 * Preamble;
14056a081dcdSDavid du Colombier 	 * ST+OP+PHYAD+REGAD;
14066a081dcdSDavid du Colombier 	 * TA + 16 data bits.
14076a081dcdSDavid du Colombier 	 */
14086a081dcdSDavid du Colombier 	i82543mdiow(ctlr, 0xFFFFFFFF, 32);
14096a081dcdSDavid du Colombier 	i82543mdiow(ctlr, 0x1800|(pa<<5)|ra, 14);
14106a081dcdSDavid du Colombier 	data = i82543mdior(ctlr, 18);
14116a081dcdSDavid du Colombier 
14126a081dcdSDavid du Colombier 	if(data & 0x10000)
14136a081dcdSDavid du Colombier 		return -1;
14146a081dcdSDavid du Colombier 
14156a081dcdSDavid du Colombier 	return data & 0xFFFF;
14166a081dcdSDavid du Colombier }
14176a081dcdSDavid du Colombier 
14186a081dcdSDavid du Colombier static int
i82543miimiw(Mii * mii,int pa,int ra,int data)14196a081dcdSDavid du Colombier i82543miimiw(Mii* mii, int pa, int ra, int data)
14206a081dcdSDavid du Colombier {
14216a081dcdSDavid du Colombier 	Ctlr *ctlr;
14226a081dcdSDavid du Colombier 
14236a081dcdSDavid du Colombier 	ctlr = mii->ctlr;
14246a081dcdSDavid du Colombier 
14256a081dcdSDavid du Colombier 	/*
14266a081dcdSDavid du Colombier 	 * MII Management Interface Write.
14276a081dcdSDavid du Colombier 	 *
14286a081dcdSDavid du Colombier 	 * Preamble;
14296a081dcdSDavid du Colombier 	 * ST+OP+PHYAD+REGAD+TA + 16 data bits;
14306a081dcdSDavid du Colombier 	 * Z.
14316a081dcdSDavid du Colombier 	 */
14326a081dcdSDavid du Colombier 	i82543mdiow(ctlr, 0xFFFFFFFF, 32);
14336a081dcdSDavid du Colombier 	data &= 0xFFFF;
14346a081dcdSDavid du Colombier 	data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16);
14356a081dcdSDavid du Colombier 	i82543mdiow(ctlr, data, 32);
14366a081dcdSDavid du Colombier 
14376a081dcdSDavid du Colombier 	return 0;
14386a081dcdSDavid du Colombier }
14396a081dcdSDavid du Colombier 
14406a081dcdSDavid du Colombier static int
igbemiimir(Mii * mii,int pa,int ra)14416a081dcdSDavid du Colombier igbemiimir(Mii* mii, int pa, int ra)
14426a081dcdSDavid du Colombier {
14436a081dcdSDavid du Colombier 	Ctlr *ctlr;
14446a081dcdSDavid du Colombier 	int mdic, timo;
14456a081dcdSDavid du Colombier 
14466a081dcdSDavid du Colombier 	ctlr = mii->ctlr;
14476a081dcdSDavid du Colombier 
14486a081dcdSDavid du Colombier 	csr32w(ctlr, Mdic, MDIrop|(pa<<MDIpSHIFT)|(ra<<MDIrSHIFT));
14496a081dcdSDavid du Colombier 	mdic = 0;
14506a081dcdSDavid du Colombier 	for(timo = 64; timo; timo--){
14516a081dcdSDavid du Colombier 		mdic = csr32r(ctlr, Mdic);
14526a081dcdSDavid du Colombier 		if(mdic & (MDIe|MDIready))
14536a081dcdSDavid du Colombier 			break;
14546a081dcdSDavid du Colombier 		microdelay(1);
14556a081dcdSDavid du Colombier 	}
14566a081dcdSDavid du Colombier 
14576a081dcdSDavid du Colombier 	if((mdic & (MDIe|MDIready)) == MDIready)
14586a081dcdSDavid du Colombier 		return mdic & 0xFFFF;
14596a081dcdSDavid du Colombier 	return -1;
14606a081dcdSDavid du Colombier }
14616a081dcdSDavid du Colombier 
14626a081dcdSDavid du Colombier static int
igbemiimiw(Mii * mii,int pa,int ra,int data)14636a081dcdSDavid du Colombier igbemiimiw(Mii* mii, int pa, int ra, int data)
14646a081dcdSDavid du Colombier {
14656a081dcdSDavid du Colombier 	Ctlr *ctlr;
14666a081dcdSDavid du Colombier 	int mdic, timo;
14676a081dcdSDavid du Colombier 
14686a081dcdSDavid du Colombier 	ctlr = mii->ctlr;
14696a081dcdSDavid du Colombier 
14706a081dcdSDavid du Colombier 	data &= MDIdMASK;
14716a081dcdSDavid du Colombier 	csr32w(ctlr, Mdic, MDIwop|(pa<<MDIpSHIFT)|(ra<<MDIrSHIFT)|data);
14726a081dcdSDavid du Colombier 	mdic = 0;
14736a081dcdSDavid du Colombier 	for(timo = 64; timo; timo--){
14746a081dcdSDavid du Colombier 		mdic = csr32r(ctlr, Mdic);
14756a081dcdSDavid du Colombier 		if(mdic & (MDIe|MDIready))
14766a081dcdSDavid du Colombier 			break;
14776a081dcdSDavid du Colombier 		microdelay(1);
14786a081dcdSDavid du Colombier 	}
14796a081dcdSDavid du Colombier 	if((mdic & (MDIe|MDIready)) == MDIready)
14806a081dcdSDavid du Colombier 		return 0;
14816a081dcdSDavid du Colombier 	return -1;
14826a081dcdSDavid du Colombier }
14836a081dcdSDavid du Colombier 
14846a081dcdSDavid du Colombier static int
igbemii(Ctlr * ctlr)14856a081dcdSDavid du Colombier igbemii(Ctlr* ctlr)
14866a081dcdSDavid du Colombier {
14876a081dcdSDavid du Colombier 	MiiPhy *phy;
14886a081dcdSDavid du Colombier 	int ctrl, p, r;
14896a081dcdSDavid du Colombier 
14906a081dcdSDavid du Colombier 	r = csr32r(ctlr, Status);
14916a081dcdSDavid du Colombier 	if(r & Tbimode)
14926a081dcdSDavid du Colombier 		return -1;
14936a081dcdSDavid du Colombier 	if((ctlr->mii = malloc(sizeof(Mii))) == nil)
14946a081dcdSDavid du Colombier 		return -1;
14956a081dcdSDavid du Colombier 	ctlr->mii->ctlr = ctlr;
14966a081dcdSDavid du Colombier 
14976a081dcdSDavid du Colombier 	ctrl = csr32r(ctlr, Ctrl);
14986a081dcdSDavid du Colombier 	ctrl |= Slu;
14996a081dcdSDavid du Colombier 
15006a081dcdSDavid du Colombier 	switch(ctlr->id){
1501aeb1c8a5SDavid du Colombier 	case i82543gc:
15026a081dcdSDavid du Colombier 		ctrl |= Frcdplx|Frcspd;
15036a081dcdSDavid du Colombier 		csr32w(ctlr, Ctrl, ctrl);
15046a081dcdSDavid du Colombier 
15056a081dcdSDavid du Colombier 		/*
15066a081dcdSDavid du Colombier 		 * The reset pin direction (Mdro) should already
15076a081dcdSDavid du Colombier 		 * be set from the EEPROM load.
15086a081dcdSDavid du Colombier 		 * If it's not set this configuration is unexpected
15096a081dcdSDavid du Colombier 		 * so bail.
15106a081dcdSDavid du Colombier 		 */
15116a081dcdSDavid du Colombier 		r = csr32r(ctlr, Ctrlext);
15120591a7c1SDavid du Colombier 		if(!(r & Mdro)) {
15130591a7c1SDavid du Colombier 			print("igbe: 82543gc Mdro not set\n");
15146a081dcdSDavid du Colombier 			return -1;
15150591a7c1SDavid du Colombier 		}
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 		r = csr32r(ctlr, Ctrlext);
15236a081dcdSDavid du Colombier 		r |= Mdr;
15246a081dcdSDavid du Colombier 		csr32w(ctlr, Ctrlext, r);
15256a081dcdSDavid du Colombier 		delay(20);
15266a081dcdSDavid du Colombier 
15276a081dcdSDavid du Colombier 		ctlr->mii->mir = i82543miimir;
15286a081dcdSDavid du Colombier 		ctlr->mii->miw = i82543miimiw;
15296a081dcdSDavid du Colombier 		break;
1530aeb1c8a5SDavid du Colombier 	case i82544ei:
153107c32395SDavid du Colombier 	case i82544eif:
15329a8968c3SDavid du Colombier 	case i82544gc:
1533aeb1c8a5SDavid du Colombier 	case i82540em:
1534aeb1c8a5SDavid du Colombier 	case i82540eplp:
15355efba406SDavid du Colombier 	case i82547ei:
1536aeb1c8a5SDavid du Colombier 	case i82547gi:
15375efba406SDavid du Colombier 	case i82541ei:
1538aeb1c8a5SDavid du Colombier 	case i82541gi:
15398466d066SDavid du Colombier 	case i82541gi2:
1540d717568cSDavid du Colombier 	case i82541pi:
1541c079eed3SDavid du Colombier 	case i82545em:
1542ab3dc52fSDavid du Colombier 	case i82545gmc:
1543aeb1c8a5SDavid du Colombier 	case i82546gb:
15445b7d6169SDavid du Colombier 	case i82546eb:
15456a081dcdSDavid du Colombier 		ctrl &= ~(Frcdplx|Frcspd);
15466a081dcdSDavid du Colombier 		csr32w(ctlr, Ctrl, ctrl);
15476a081dcdSDavid du Colombier 		ctlr->mii->mir = igbemiimir;
15486a081dcdSDavid du Colombier 		ctlr->mii->miw = igbemiimiw;
15496a081dcdSDavid du Colombier 		break;
15506a081dcdSDavid du Colombier 	default:
15516a081dcdSDavid du Colombier 		free(ctlr->mii);
15526a081dcdSDavid du Colombier 		ctlr->mii = nil;
15536a081dcdSDavid du Colombier 		return -1;
15546a081dcdSDavid du Colombier 	}
15556a081dcdSDavid du Colombier 
15566a081dcdSDavid du Colombier 	if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){
15576a081dcdSDavid du Colombier 		free(ctlr->mii);
15586a081dcdSDavid du Colombier 		ctlr->mii = nil;
15596a081dcdSDavid du Colombier 		return -1;
15606a081dcdSDavid du Colombier 	}
15614de34a7eSDavid du Colombier 	USED(phy);
15624de34a7eSDavid du Colombier 	// print("oui %X phyno %d\n", phy->oui, phy->phyno);
15636a081dcdSDavid du Colombier 
15646a081dcdSDavid du Colombier 	/*
15656a081dcdSDavid du Colombier 	 * 8254X-specific PHY registers not in 802.3:
15666a081dcdSDavid du Colombier 	 *	0x10	PHY specific control
15676a081dcdSDavid du Colombier 	 *	0x14	extended PHY specific control
15686a081dcdSDavid du Colombier 	 * Set appropriate values then reset the PHY to have
15696a081dcdSDavid du Colombier 	 * changes noted.
15706a081dcdSDavid du Colombier 	 */
1571d717568cSDavid du Colombier 	switch(ctlr->id){
1572d717568cSDavid du Colombier 	case i82547gi:
1573d717568cSDavid du Colombier 	case i82541gi:
15748466d066SDavid du Colombier 	case i82541gi2:
1575d717568cSDavid du Colombier 	case i82541pi:
1576c079eed3SDavid du Colombier 	case i82545em:
1577ab3dc52fSDavid du Colombier 	case i82545gmc:
1578d717568cSDavid du Colombier 	case i82546gb:
1579d717568cSDavid du Colombier 	case i82546eb:
1580d717568cSDavid du Colombier 		break;
1581d717568cSDavid du Colombier 	default:
1582aeb1c8a5SDavid du Colombier 		r = miimir(ctlr->mii, 16);
15836a081dcdSDavid du Colombier 		r |= 0x0800;			/* assert CRS on Tx */
15846a081dcdSDavid du Colombier 		r |= 0x0060;			/* auto-crossover all speeds */
15856a081dcdSDavid du Colombier 		r |= 0x0002;			/* polarity reversal enabled */
1586aeb1c8a5SDavid du Colombier 		miimiw(ctlr->mii, 16, r);
15876a081dcdSDavid du Colombier 
1588aeb1c8a5SDavid du Colombier 		r = miimir(ctlr->mii, 20);
15896a081dcdSDavid du Colombier 		r |= 0x0070;			/* +25MHz clock */
15906a081dcdSDavid du Colombier 		r &= ~0x0F00;
15916a081dcdSDavid du Colombier 		r |= 0x0100;			/* 1x downshift */
1592aeb1c8a5SDavid du Colombier 		miimiw(ctlr->mii, 20, r);
15936a081dcdSDavid du Colombier 
15946a081dcdSDavid du Colombier 		miireset(ctlr->mii);
15956a081dcdSDavid du Colombier 		p = 0;
15966a081dcdSDavid du Colombier 		if(ctlr->txcw & TxcwPs)
15976a081dcdSDavid du Colombier 			p |= AnaP;
15986a081dcdSDavid du Colombier 		if(ctlr->txcw & TxcwAs)
15996a081dcdSDavid du Colombier 			p |= AnaAP;
16006a081dcdSDavid du Colombier 		miiane(ctlr->mii, ~0, p, ~0);
1601d717568cSDavid du Colombier 		break;
1602aeb1c8a5SDavid du Colombier 	}
16036a081dcdSDavid du Colombier 	return 0;
16046a081dcdSDavid du Colombier }
16056a081dcdSDavid du Colombier 
16066a081dcdSDavid du Colombier static int
at93c46io(Ctlr * ctlr,char * op,int data)16076a081dcdSDavid du Colombier at93c46io(Ctlr* ctlr, char* op, int data)
16086a081dcdSDavid du Colombier {
16096a081dcdSDavid du Colombier 	char *lp, *p;
16106a081dcdSDavid du Colombier 	int i, loop, eecd, r;
16116a081dcdSDavid du Colombier 
16126a081dcdSDavid du Colombier 	eecd = csr32r(ctlr, Eecd);
16136a081dcdSDavid du Colombier 
16146a081dcdSDavid du Colombier 	r = 0;
16156a081dcdSDavid du Colombier 	loop = -1;
16166a081dcdSDavid du Colombier 	lp = nil;
16176a081dcdSDavid du Colombier 	for(p = op; *p != '\0'; p++){
16186a081dcdSDavid du Colombier 		switch(*p){
16196a081dcdSDavid du Colombier 		default:
16206a081dcdSDavid du Colombier 			return -1;
16216a081dcdSDavid du Colombier 		case ' ':
16226a081dcdSDavid du Colombier 			continue;
16236a081dcdSDavid du Colombier 		case ':':			/* start of loop */
16246a081dcdSDavid du Colombier 			loop = strtol(p+1, &lp, 0)-1;
16256a081dcdSDavid du Colombier 			lp--;
16266a081dcdSDavid du Colombier 			if(p == lp)
16276a081dcdSDavid du Colombier 				loop = 7;
16286a081dcdSDavid du Colombier 			p = lp;
16296a081dcdSDavid du Colombier 			continue;
16306a081dcdSDavid du Colombier 		case ';':			/* end of loop */
16316a081dcdSDavid du Colombier 			if(lp == nil)
16326a081dcdSDavid du Colombier 				return -1;
16336a081dcdSDavid du Colombier 			loop--;
16346a081dcdSDavid du Colombier 			if(loop >= 0)
16356a081dcdSDavid du Colombier 				p = lp;
16366a081dcdSDavid du Colombier 			else
16376a081dcdSDavid du Colombier 				lp = nil;
16386a081dcdSDavid du Colombier 			continue;
16396a081dcdSDavid du Colombier 		case 'C':			/* assert clock */
16406a081dcdSDavid du Colombier 			eecd |= Sk;
16416a081dcdSDavid du Colombier 			break;
16426a081dcdSDavid du Colombier 		case 'c':			/* deassert clock */
16436a081dcdSDavid du Colombier 			eecd &= ~Sk;
16446a081dcdSDavid du Colombier 			break;
16456a081dcdSDavid du Colombier 		case 'D':			/* next bit in 'data' byte */
16466a081dcdSDavid du Colombier 			if(loop < 0)
16476a081dcdSDavid du Colombier 				return -1;
16486a081dcdSDavid du Colombier 			if(data & (1<<loop))
16496a081dcdSDavid du Colombier 				eecd |= Di;
16506a081dcdSDavid du Colombier 			else
16516a081dcdSDavid du Colombier 				eecd &= ~Di;
16526a081dcdSDavid du Colombier 			break;
16536a081dcdSDavid du Colombier 		case 'O':			/* collect data output */
16546a081dcdSDavid du Colombier 			i = (csr32r(ctlr, Eecd) & Do) != 0;
16556a081dcdSDavid du Colombier 			if(loop >= 0)
16566a081dcdSDavid du Colombier 				r |= (i<<loop);
16576a081dcdSDavid du Colombier 			else
16586a081dcdSDavid du Colombier 				r = i;
16596a081dcdSDavid du Colombier 			continue;
16606a081dcdSDavid du Colombier 		case 'I':			/* assert data input */
16616a081dcdSDavid du Colombier 			eecd |= Di;
16626a081dcdSDavid du Colombier 			break;
16636a081dcdSDavid du Colombier 		case 'i':			/* deassert data input */
16646a081dcdSDavid du Colombier 			eecd &= ~Di;
16656a081dcdSDavid du Colombier 			break;
16666a081dcdSDavid du Colombier 		case 'S':			/* enable chip select */
16676a081dcdSDavid du Colombier 			eecd |= Cs;
16686a081dcdSDavid du Colombier 			break;
16696a081dcdSDavid du Colombier 		case 's':			/* disable chip select */
16706a081dcdSDavid du Colombier 			eecd &= ~Cs;
16716a081dcdSDavid du Colombier 			break;
16726a081dcdSDavid du Colombier 		}
16736a081dcdSDavid du Colombier 		csr32w(ctlr, Eecd, eecd);
16748466d066SDavid du Colombier 		microdelay(50);
16756a081dcdSDavid du Colombier 	}
16766a081dcdSDavid du Colombier 	if(loop >= 0)
16776a081dcdSDavid du Colombier 		return -1;
16786a081dcdSDavid du Colombier 	return r;
16796a081dcdSDavid du Colombier }
16806a081dcdSDavid du Colombier 
16816a081dcdSDavid du Colombier static int
at93c46r(Ctlr * ctlr)16826a081dcdSDavid du Colombier at93c46r(Ctlr* ctlr)
16836a081dcdSDavid du Colombier {
16846a081dcdSDavid du Colombier 	ushort sum;
16856a081dcdSDavid du Colombier 	char rop[20];
16866a081dcdSDavid du Colombier 	int addr, areq, bits, data, eecd, i;
16876a081dcdSDavid du Colombier 
16886a081dcdSDavid du Colombier 	eecd = csr32r(ctlr, Eecd);
16896a081dcdSDavid du Colombier 	if(eecd & Spi){
16906a081dcdSDavid du Colombier 		print("igbe: SPI EEPROM access not implemented\n");
16916a081dcdSDavid du Colombier 		return 0;
16926a081dcdSDavid du Colombier 	}
16938466d066SDavid du Colombier 	if(eecd & (Eeszaddr|Eesz256))
16946a081dcdSDavid du Colombier 		bits = 8;
16956a081dcdSDavid du Colombier 	else
16966a081dcdSDavid du Colombier 		bits = 6;
16976a081dcdSDavid du Colombier 
16986a081dcdSDavid du Colombier 	sum = 0;
16996a081dcdSDavid du Colombier 
17006a081dcdSDavid du Colombier 	switch(ctlr->id){
17016a081dcdSDavid du Colombier 	default:
17026a081dcdSDavid du Colombier 		areq = 0;
17036a081dcdSDavid du Colombier 		break;
1704aeb1c8a5SDavid du Colombier 	case i82540em:
1705aeb1c8a5SDavid du Colombier 	case i82540eplp:
17065efba406SDavid du Colombier 	case i82541ei:
17075efba406SDavid du Colombier 	case i82541gi:
170826ad7229SDavid du Colombier 	case i82541gi2:
17095efba406SDavid du Colombier 	case i82541pi:
1710c079eed3SDavid du Colombier 	case i82545em:
1711ab3dc52fSDavid du Colombier 	case i82545gmc:
1712aeb1c8a5SDavid du Colombier 	case i82546gb:
17135b7d6169SDavid du Colombier 	case i82546eb:
17147e254d1cSDavid du Colombier 	case i82547ei:
17155efba406SDavid du Colombier 	case i82547gi:
17166a081dcdSDavid du Colombier 		areq = 1;
17176a081dcdSDavid du Colombier 		csr32w(ctlr, Eecd, eecd|Areq);
17186a081dcdSDavid du Colombier 		for(i = 0; i < 1000; i++){
17196a081dcdSDavid du Colombier 			if((eecd = csr32r(ctlr, Eecd)) & Agnt)
17206a081dcdSDavid du Colombier 				break;
17216a081dcdSDavid du Colombier 			microdelay(5);
17226a081dcdSDavid du Colombier 		}
17236a081dcdSDavid du Colombier 		if(!(eecd & Agnt)){
17246a081dcdSDavid du Colombier 			print("igbe: not granted EEPROM access\n");
17256a081dcdSDavid du Colombier 			goto release;
17266a081dcdSDavid du Colombier 		}
17276a081dcdSDavid du Colombier 		break;
17286a081dcdSDavid du Colombier 	}
17298466d066SDavid du Colombier 	snprint(rop, sizeof(rop), "S :%dDCc;", bits+3);
17306a081dcdSDavid du Colombier 
17316a081dcdSDavid du Colombier 	for(addr = 0; addr < 0x40; addr++){
17326a081dcdSDavid du Colombier 		/*
17336a081dcdSDavid du Colombier 		 * Read a word at address 'addr' from the Atmel AT93C46
17346a081dcdSDavid du Colombier 		 * 3-Wire Serial EEPROM or compatible. The EEPROM access is
17356a081dcdSDavid du Colombier 		 * controlled by 4 bits in Eecd. See the AT93C46 datasheet
17366a081dcdSDavid du Colombier 		 * for protocol details.
17376a081dcdSDavid du Colombier 		 */
17386a081dcdSDavid du Colombier 		if(at93c46io(ctlr, rop, (0x06<<bits)|addr) != 0){
17396a081dcdSDavid du Colombier 			print("igbe: can't set EEPROM address 0x%2.2X\n", addr);
17406a081dcdSDavid du Colombier 			goto release;
17416a081dcdSDavid du Colombier 		}
17426a081dcdSDavid du Colombier 		data = at93c46io(ctlr, ":16COc;", 0);
17436a081dcdSDavid du Colombier 		at93c46io(ctlr, "sic", 0);
17446a081dcdSDavid du Colombier 		ctlr->eeprom[addr] = data;
17456a081dcdSDavid du Colombier 		sum += data;
17466a081dcdSDavid du Colombier 	}
17476a081dcdSDavid du Colombier 
17486a081dcdSDavid du Colombier release:
17496a081dcdSDavid du Colombier 	if(areq)
17506a081dcdSDavid du Colombier 		csr32w(ctlr, Eecd, eecd & ~Areq);
17516a081dcdSDavid du Colombier 	return sum;
17526a081dcdSDavid du Colombier }
17536a081dcdSDavid du Colombier 
17545d9682faSDavid du Colombier static int
igbedetach(Ctlr * ctlr)17556a081dcdSDavid du Colombier igbedetach(Ctlr* ctlr)
17566a081dcdSDavid du Colombier {
17575d9682faSDavid du Colombier 	int r, timeo;
17586a081dcdSDavid du Colombier 
17596a081dcdSDavid du Colombier 	/*
17606a081dcdSDavid du Colombier 	 * Perform a device reset to get the chip back to the
17616a081dcdSDavid du Colombier 	 * power-on state, followed by an EEPROM reset to read
17626a081dcdSDavid du Colombier 	 * the defaults for some internal registers.
17636a081dcdSDavid du Colombier 	 */
17646a081dcdSDavid du Colombier 	csr32w(ctlr, Imc, ~0);
17656a081dcdSDavid du Colombier 	csr32w(ctlr, Rctl, 0);
17666a081dcdSDavid du Colombier 	csr32w(ctlr, Tctl, 0);
17676a081dcdSDavid du Colombier 
1768ff8c3af2SDavid du Colombier 	delay(10);
17696a081dcdSDavid du Colombier 
17706a081dcdSDavid du Colombier 	csr32w(ctlr, Ctrl, Devrst);
17715d9682faSDavid du Colombier 	delay(1);
17725d9682faSDavid du Colombier 	for(timeo = 0; timeo < 1000; timeo++){
17735d9682faSDavid du Colombier 		if(!(csr32r(ctlr, Ctrl) & Devrst))
17745d9682faSDavid du Colombier 			break;
17755d9682faSDavid du Colombier 		delay(1);
17765d9682faSDavid du Colombier 	}
17775d9682faSDavid du Colombier 	if(csr32r(ctlr, Ctrl) & Devrst)
17785d9682faSDavid du Colombier 		return -1;
1779aeb1c8a5SDavid du Colombier 	r = csr32r(ctlr, Ctrlext);
1780aeb1c8a5SDavid du Colombier 	csr32w(ctlr, Ctrlext, r|Eerst);
17815d9682faSDavid du Colombier 	delay(1);
17825d9682faSDavid du Colombier 	for(timeo = 0; timeo < 1000; timeo++){
17835d9682faSDavid du Colombier 		if(!(csr32r(ctlr, Ctrlext) & Eerst))
17845d9682faSDavid du Colombier 			break;
17855d9682faSDavid du Colombier 		delay(1);
17865d9682faSDavid du Colombier 	}
17875d9682faSDavid du Colombier 	if(csr32r(ctlr, Ctrlext) & Eerst)
17885d9682faSDavid du Colombier 		return -1;
17896a081dcdSDavid du Colombier 
17906a081dcdSDavid du Colombier 	switch(ctlr->id){
17916a081dcdSDavid du Colombier 	default:
17926a081dcdSDavid du Colombier 		break;
1793aeb1c8a5SDavid du Colombier 	case i82540em:
1794aeb1c8a5SDavid du Colombier 	case i82540eplp:
1795aeb1c8a5SDavid du Colombier 	case i82541gi:
179626ad7229SDavid du Colombier 	case i82541gi2:
17975efba406SDavid du Colombier 	case i82541pi:
1798c079eed3SDavid du Colombier 	case i82545em:
1799ab3dc52fSDavid du Colombier 	case i82545gmc:
18005efba406SDavid du Colombier 	case i82547gi:
1801aeb1c8a5SDavid du Colombier 	case i82546gb:
18025b7d6169SDavid du Colombier 	case i82546eb:
18036a081dcdSDavid du Colombier 		r = csr32r(ctlr, Manc);
18046a081dcdSDavid du Colombier 		r &= ~Arpen;
18056a081dcdSDavid du Colombier 		csr32w(ctlr, Manc, r);
18066a081dcdSDavid du Colombier 		break;
18076a081dcdSDavid du Colombier 	}
18086a081dcdSDavid du Colombier 
18096a081dcdSDavid du Colombier 	csr32w(ctlr, Imc, ~0);
18105d9682faSDavid du Colombier 	delay(1);
18115d9682faSDavid du Colombier 	for(timeo = 0; timeo < 1000; timeo++){
18125d9682faSDavid du Colombier 		if(!csr32r(ctlr, Icr))
18135d9682faSDavid du Colombier 			break;
18145d9682faSDavid du Colombier 		delay(1);
18155d9682faSDavid du Colombier 	}
18165d9682faSDavid du Colombier 	if(csr32r(ctlr, Icr))
18175d9682faSDavid du Colombier 		return -1;
18185d9682faSDavid du Colombier 
18195d9682faSDavid du Colombier 	return 0;
18206a081dcdSDavid du Colombier }
18216a081dcdSDavid du Colombier 
18220809e9a7SDavid du Colombier static void
igbeshutdown(Ether * ether)18230809e9a7SDavid du Colombier igbeshutdown(Ether* ether)
18240809e9a7SDavid du Colombier {
18250809e9a7SDavid du Colombier 	igbedetach(ether->ctlr);
18260809e9a7SDavid du Colombier }
18270809e9a7SDavid du Colombier 
1828ff8c3af2SDavid du Colombier static int
igbereset(Ctlr * ctlr)1829ff8c3af2SDavid du Colombier igbereset(Ctlr* ctlr)
18306a081dcdSDavid du Colombier {
18316a081dcdSDavid du Colombier 	int ctrl, i, pause, r, swdpio, txcw;
18326a081dcdSDavid du Colombier 
18335d9682faSDavid du Colombier 	if(igbedetach(ctlr))
18345d9682faSDavid du Colombier 		return -1;
18356a081dcdSDavid du Colombier 
18366a081dcdSDavid du Colombier 	/*
18376a081dcdSDavid du Colombier 	 * Read the EEPROM, validate the checksum
18386a081dcdSDavid du Colombier 	 * then get the device back to a power-on state.
18396a081dcdSDavid du Colombier 	 */
18406a081dcdSDavid du Colombier 	if((r = at93c46r(ctlr)) != 0xBABA){
18416a081dcdSDavid du Colombier 		print("igbe: bad EEPROM checksum - 0x%4.4uX\n", r);
18426a081dcdSDavid du Colombier 		return -1;
18436a081dcdSDavid du Colombier 	}
18446a081dcdSDavid du Colombier 
18456a081dcdSDavid du Colombier 	/*
18466a081dcdSDavid du Colombier 	 * Snarf and set up the receive addresses.
18476a081dcdSDavid du Colombier 	 * There are 16 addresses. The first should be the MAC address.
18486a081dcdSDavid du Colombier 	 * The others are cleared and not marked valid (MS bit of Rah).
18496a081dcdSDavid du Colombier 	 */
185041dd6b47SDavid du Colombier 	if ((ctlr->id == i82546gb || ctlr->id == i82546eb) &&
185141dd6b47SDavid du Colombier 	    BUSFNO(ctlr->pcidev->tbdf) == 1)
185241dd6b47SDavid du Colombier 		ctlr->eeprom[Ea+2] += 0x100;		/* second interface */
18537e254d1cSDavid du Colombier 	if(ctlr->id == i82541gi && ctlr->eeprom[Ea] == 0xFFFF)
18547e254d1cSDavid du Colombier 		ctlr->eeprom[Ea] = 0xD000;
18556a081dcdSDavid du Colombier 	for(i = Ea; i < Eaddrlen/2; i++){
18566a081dcdSDavid du Colombier 		ctlr->ra[2*i] = ctlr->eeprom[i];
18576a081dcdSDavid du Colombier 		ctlr->ra[2*i+1] = ctlr->eeprom[i]>>8;
18586a081dcdSDavid du Colombier 	}
18597e254d1cSDavid du Colombier 	/* lan id seems to vary on 82543gc; don't use it */
18607e254d1cSDavid du Colombier 	if (ctlr->id != i82543gc) {
18617e254d1cSDavid du Colombier 		r = (csr32r(ctlr, Status) & Lanid) >> 2;
18627e254d1cSDavid du Colombier 		ctlr->ra[5] += r;		/* ea ctlr[1] = ea ctlr[0]+1 */
18637e254d1cSDavid du Colombier 	}
18647e254d1cSDavid du Colombier 
18656a081dcdSDavid du Colombier 	r = (ctlr->ra[3]<<24)|(ctlr->ra[2]<<16)|(ctlr->ra[1]<<8)|ctlr->ra[0];
18666a081dcdSDavid du Colombier 	csr32w(ctlr, Ral, r);
18676a081dcdSDavid du Colombier 	r = 0x80000000|(ctlr->ra[5]<<8)|ctlr->ra[4];
18686a081dcdSDavid du Colombier 	csr32w(ctlr, Rah, r);
18696a081dcdSDavid du Colombier 	for(i = 1; i < 16; i++){
18706a081dcdSDavid du Colombier 		csr32w(ctlr, Ral+i*8, 0);
18716a081dcdSDavid du Colombier 		csr32w(ctlr, Rah+i*8, 0);
18726a081dcdSDavid du Colombier 	}
18736a081dcdSDavid du Colombier 
18746a081dcdSDavid du Colombier 	/*
18756a081dcdSDavid du Colombier 	 * Clear the Multicast Table Array.
18766a081dcdSDavid du Colombier 	 * It's a 4096 bit vector accessed as 128 32-bit registers.
18776a081dcdSDavid du Colombier 	 */
1878ecea9424SDavid du Colombier 	memset(ctlr->mta, 0, sizeof(ctlr->mta));
18796a081dcdSDavid du Colombier 	for(i = 0; i < 128; i++)
18806a081dcdSDavid du Colombier 		csr32w(ctlr, Mta+i*4, 0);
18816a081dcdSDavid du Colombier 
18826a081dcdSDavid du Colombier 	/*
18836a081dcdSDavid du Colombier 	 * Just in case the Eerst didn't load the defaults
1884208510e1SDavid du Colombier 	 * (doesn't appear to fully on the 82543GC), do it manually.
18856a081dcdSDavid du Colombier 	 */
1886208510e1SDavid du Colombier 	if (ctlr->id == i82543gc) {
18876a081dcdSDavid du Colombier 		txcw = csr32r(ctlr, Txcw);
18886a081dcdSDavid du Colombier 		txcw &= ~(TxcwAne|TxcwPauseMASK|TxcwFd);
18896a081dcdSDavid du Colombier 		ctrl = csr32r(ctlr, Ctrl);
18906a081dcdSDavid du Colombier 		ctrl &= ~(SwdpioloMASK|Frcspd|Ilos|Lrst|Fd);
18916a081dcdSDavid du Colombier 
18926a081dcdSDavid du Colombier 		if(ctlr->eeprom[Icw1] & 0x0400){
18936a081dcdSDavid du Colombier 			ctrl |= Fd;
18946a081dcdSDavid du Colombier 			txcw |= TxcwFd;
18956a081dcdSDavid du Colombier 		}
18966a081dcdSDavid du Colombier 		if(ctlr->eeprom[Icw1] & 0x0200)
18976a081dcdSDavid du Colombier 			ctrl |= Lrst;
18986a081dcdSDavid du Colombier 		if(ctlr->eeprom[Icw1] & 0x0010)
18996a081dcdSDavid du Colombier 			ctrl |= Ilos;
19006a081dcdSDavid du Colombier 		if(ctlr->eeprom[Icw1] & 0x0800)
19016a081dcdSDavid du Colombier 			ctrl |= Frcspd;
19026a081dcdSDavid du Colombier 		swdpio = (ctlr->eeprom[Icw1] & 0x01E0)>>5;
19036a081dcdSDavid du Colombier 		ctrl |= swdpio<<SwdpioloSHIFT;
19046a081dcdSDavid du Colombier 		csr32w(ctlr, Ctrl, ctrl);
19056a081dcdSDavid du Colombier 
19066a081dcdSDavid du Colombier 		ctrl = csr32r(ctlr, Ctrlext);
19076a081dcdSDavid du Colombier 		ctrl &= ~(Ips|SwdpiohiMASK);
19086a081dcdSDavid du Colombier 		swdpio = (ctlr->eeprom[Icw2] & 0x00F0)>>4;
19096a081dcdSDavid du Colombier 		if(ctlr->eeprom[Icw1] & 0x1000)
19106a081dcdSDavid du Colombier 			ctrl |= Ips;
19116a081dcdSDavid du Colombier 		ctrl |= swdpio<<SwdpiohiSHIFT;
19126a081dcdSDavid du Colombier 		csr32w(ctlr, Ctrlext, ctrl);
19136a081dcdSDavid du Colombier 
19146a081dcdSDavid du Colombier 		if(ctlr->eeprom[Icw2] & 0x0800)
19156a081dcdSDavid du Colombier 			txcw |= TxcwAne;
19166a081dcdSDavid du Colombier 		pause = (ctlr->eeprom[Icw2] & 0x3000)>>12;
19176a081dcdSDavid du Colombier 		txcw |= pause<<TxcwPauseSHIFT;
19186a081dcdSDavid du Colombier 		switch(pause){
19196a081dcdSDavid du Colombier 		default:
19206a081dcdSDavid du Colombier 			ctlr->fcrtl = 0x00002000;
19216a081dcdSDavid du Colombier 			ctlr->fcrth = 0x00004000;
19226a081dcdSDavid du Colombier 			txcw |= TxcwAs|TxcwPs;
19236a081dcdSDavid du Colombier 			break;
19246a081dcdSDavid du Colombier 		case 0:
19256a081dcdSDavid du Colombier 			ctlr->fcrtl = 0x00002000;
19266a081dcdSDavid du Colombier 			ctlr->fcrth = 0x00004000;
19276a081dcdSDavid du Colombier 			break;
19286a081dcdSDavid du Colombier 		case 2:
19296a081dcdSDavid du Colombier 			ctlr->fcrtl = 0;
19306a081dcdSDavid du Colombier 			ctlr->fcrth = 0;
19316a081dcdSDavid du Colombier 			txcw |= TxcwAs;
19326a081dcdSDavid du Colombier 			break;
19336a081dcdSDavid du Colombier 		}
19346a081dcdSDavid du Colombier 		ctlr->txcw = txcw;
19356a081dcdSDavid du Colombier 		csr32w(ctlr, Txcw, txcw);
1936aeb1c8a5SDavid du Colombier 	}
1937aeb1c8a5SDavid du Colombier 
19386a081dcdSDavid du Colombier 
19396a081dcdSDavid du Colombier 	/*
19406a081dcdSDavid du Colombier 	 * Flow control - values from the datasheet.
19416a081dcdSDavid du Colombier 	 */
19426a081dcdSDavid du Colombier 	csr32w(ctlr, Fcal, 0x00C28001);
19436a081dcdSDavid du Colombier 	csr32w(ctlr, Fcah, 0x00000100);
19446a081dcdSDavid du Colombier 	csr32w(ctlr, Fct, 0x00008808);
19456a081dcdSDavid du Colombier 	csr32w(ctlr, Fcttv, 0x00000100);
19466a081dcdSDavid du Colombier 
19476a081dcdSDavid du Colombier 	csr32w(ctlr, Fcrtl, ctlr->fcrtl);
19486a081dcdSDavid du Colombier 	csr32w(ctlr, Fcrth, ctlr->fcrth);
19496a081dcdSDavid du Colombier 
19506a081dcdSDavid du Colombier 	if(!(csr32r(ctlr, Status) & Tbimode) && igbemii(ctlr) < 0)
19516a081dcdSDavid du Colombier 		return -1;
19526a081dcdSDavid du Colombier 
19536a081dcdSDavid du Colombier 	return 0;
19546a081dcdSDavid du Colombier }
19556a081dcdSDavid du Colombier 
19566a081dcdSDavid du Colombier static void
igbepci(void)19576a081dcdSDavid du Colombier igbepci(void)
19586a081dcdSDavid du Colombier {
19594de34a7eSDavid du Colombier 	int cls;
19606a081dcdSDavid du Colombier 	Pcidev *p;
19616a081dcdSDavid du Colombier 	Ctlr *ctlr;
19624de34a7eSDavid du Colombier 	void *mem;
19636a081dcdSDavid du Colombier 
19646a081dcdSDavid du Colombier 	p = nil;
19656a081dcdSDavid du Colombier 	while(p = pcimatch(p, 0, 0)){
19666a081dcdSDavid du Colombier 		if(p->ccrb != 0x02 || p->ccru != 0)
19676a081dcdSDavid du Colombier 			continue;
19686a081dcdSDavid du Colombier 
19696a081dcdSDavid du Colombier 		switch((p->did<<16)|p->vid){
19706a081dcdSDavid du Colombier 		default:
19716a081dcdSDavid du Colombier 			continue;
1972aeb1c8a5SDavid du Colombier 		case i82543gc:
1973aeb1c8a5SDavid du Colombier 		case i82544ei:
197407c32395SDavid du Colombier 		case i82544eif:
19759a8968c3SDavid du Colombier 		case i82544gc:
1976aeb1c8a5SDavid du Colombier 		case i82547ei:
19775efba406SDavid du Colombier 		case i82547gi:
1978aeb1c8a5SDavid du Colombier 		case i82540em:
1979aeb1c8a5SDavid du Colombier 		case i82540eplp:
19805efba406SDavid du Colombier 		case i82541ei:
1981aeb1c8a5SDavid du Colombier 		case i82541gi:
198226ad7229SDavid du Colombier 		case i82541gi2:
1983d717568cSDavid du Colombier 		case i82541pi:
1984c079eed3SDavid du Colombier 		case i82545em:
1985ab3dc52fSDavid du Colombier 		case i82545gmc:
1986aeb1c8a5SDavid du Colombier 		case i82546gb:
19875b7d6169SDavid du Colombier 		case i82546eb:
19886a081dcdSDavid du Colombier 			break;
19896a081dcdSDavid du Colombier 		}
19906a081dcdSDavid du Colombier 
19914de34a7eSDavid du Colombier 		mem = vmap(p->mem[0].bar & ~0x0F, p->mem[0].size);
19924de34a7eSDavid du Colombier 		if(mem == nil){
19936a081dcdSDavid du Colombier 			print("igbe: can't map %8.8luX\n", p->mem[0].bar);
19946a081dcdSDavid du Colombier 			continue;
19956a081dcdSDavid du Colombier 		}
19966a081dcdSDavid du Colombier 		cls = pcicfgr8(p, PciCLS);
19976a081dcdSDavid du Colombier 		switch(cls){
19986a081dcdSDavid du Colombier 		default:
19990591a7c1SDavid du Colombier 			print("igbe: p->cls %#ux, setting to 0x10\n", p->cls);
20000591a7c1SDavid du Colombier 			p->cls = 0x10;
20010591a7c1SDavid du Colombier 			pcicfgw8(p, PciCLS, p->cls);
20026a081dcdSDavid du Colombier 			break;
20036a081dcdSDavid du Colombier 		case 0x08:
20046a081dcdSDavid du Colombier 		case 0x10:
20056a081dcdSDavid du Colombier 			break;
20066a081dcdSDavid du Colombier 		}
20076a081dcdSDavid du Colombier 		ctlr = malloc(sizeof(Ctlr));
2008aa72973aSDavid du Colombier 		if(ctlr == nil) {
2009aa72973aSDavid du Colombier 			vunmap(mem, p->mem[0].size);
2010aa72973aSDavid du Colombier 			error(Enomem);
2011aa72973aSDavid du Colombier 		}
20124de34a7eSDavid du Colombier 		ctlr->port = p->mem[0].bar & ~0x0F;
20136a081dcdSDavid du Colombier 		ctlr->pcidev = p;
20146a081dcdSDavid du Colombier 		ctlr->id = (p->did<<16)|p->vid;
20156a081dcdSDavid du Colombier 		ctlr->cls = cls*4;
20164de34a7eSDavid du Colombier 		ctlr->nic = mem;
20176a081dcdSDavid du Colombier 
2018ff8c3af2SDavid du Colombier 		if(igbereset(ctlr)){
20196a081dcdSDavid du Colombier 			free(ctlr);
20205979f962SDavid du Colombier 			vunmap(mem, p->mem[0].size);
20216a081dcdSDavid du Colombier 			continue;
20226a081dcdSDavid du Colombier 		}
20236a081dcdSDavid du Colombier 		pcisetbme(p);
20246a081dcdSDavid du Colombier 
20256a081dcdSDavid du Colombier 		if(igbectlrhead != nil)
20266a081dcdSDavid du Colombier 			igbectlrtail->next = ctlr;
20276a081dcdSDavid du Colombier 		else
20286a081dcdSDavid du Colombier 			igbectlrhead = ctlr;
20296a081dcdSDavid du Colombier 		igbectlrtail = ctlr;
20306a081dcdSDavid du Colombier 	}
20316a081dcdSDavid du Colombier }
20326a081dcdSDavid du Colombier 
20336a081dcdSDavid du Colombier static int
igbepnp(Ether * edev)20346a081dcdSDavid du Colombier igbepnp(Ether* edev)
20356a081dcdSDavid du Colombier {
20366a081dcdSDavid du Colombier 	Ctlr *ctlr;
20376a081dcdSDavid du Colombier 
20386a081dcdSDavid du Colombier 	if(igbectlrhead == nil)
20396a081dcdSDavid du Colombier 		igbepci();
20406a081dcdSDavid du Colombier 
20416a081dcdSDavid du Colombier 	/*
20426a081dcdSDavid du Colombier 	 * Any adapter matches if no edev->port is supplied,
20436a081dcdSDavid du Colombier 	 * otherwise the ports must match.
20446a081dcdSDavid du Colombier 	 */
20456a081dcdSDavid du Colombier 	for(ctlr = igbectlrhead; ctlr != nil; ctlr = ctlr->next){
20466a081dcdSDavid du Colombier 		if(ctlr->active)
20476a081dcdSDavid du Colombier 			continue;
20486a081dcdSDavid du Colombier 		if(edev->port == 0 || edev->port == ctlr->port){
20496a081dcdSDavid du Colombier 			ctlr->active = 1;
20506a081dcdSDavid du Colombier 			break;
20516a081dcdSDavid du Colombier 		}
20526a081dcdSDavid du Colombier 	}
20536a081dcdSDavid du Colombier 	if(ctlr == nil)
20546a081dcdSDavid du Colombier 		return -1;
20556a081dcdSDavid du Colombier 
20566a081dcdSDavid du Colombier 	edev->ctlr = ctlr;
20576a081dcdSDavid du Colombier 	edev->port = ctlr->port;
20586a081dcdSDavid du Colombier 	edev->irq = ctlr->pcidev->intl;
20596a081dcdSDavid du Colombier 	edev->tbdf = ctlr->pcidev->tbdf;
20606a081dcdSDavid du Colombier 	edev->mbps = 1000;
20616a081dcdSDavid du Colombier 	memmove(edev->ea, ctlr->ra, Eaddrlen);
20626a081dcdSDavid du Colombier 
20636a081dcdSDavid du Colombier 	/*
20646a081dcdSDavid du Colombier 	 * Linkage to the generic ethernet driver.
20656a081dcdSDavid du Colombier 	 */
20666a081dcdSDavid du Colombier 	edev->attach = igbeattach;
20676a081dcdSDavid du Colombier 	edev->transmit = igbetransmit;
20686a081dcdSDavid du Colombier 	edev->interrupt = igbeinterrupt;
20696a081dcdSDavid du Colombier 	edev->ifstat = igbeifstat;
20706a081dcdSDavid du Colombier 	edev->ctl = igbectl;
20716a081dcdSDavid du Colombier 
20726a081dcdSDavid du Colombier 	edev->arg = edev;
20736a081dcdSDavid du Colombier 	edev->promiscuous = igbepromiscuous;
20740809e9a7SDavid du Colombier 	edev->shutdown = igbeshutdown;
2075ecea9424SDavid du Colombier 	edev->multicast = igbemulticast;
2076ff8c3af2SDavid du Colombier 
20776a081dcdSDavid du Colombier 	return 0;
20786a081dcdSDavid du Colombier }
20796a081dcdSDavid du Colombier 
20806a081dcdSDavid du Colombier void
etherigbelink(void)20816a081dcdSDavid du Colombier etherigbelink(void)
20826a081dcdSDavid du Colombier {
20836a081dcdSDavid du Colombier 	addethercard("i82543", igbepnp);
20846a081dcdSDavid du Colombier 	addethercard("igbe", igbepnp);
20856a081dcdSDavid du Colombier }
20868466d066SDavid du Colombier 
2087