| /* This file is part of the UCB release of Plan 9. It is subject to the license | 
 |  * terms in the LICENSE file found in the top-level directory of this | 
 |  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No | 
 |  * part of the UCB release of Plan 9, including this file, may be copied, | 
 |  * modified, propagated, or distributed except according to the terms contained | 
 |  * in the LICENSE file. */ | 
 |  | 
 | /* | 
 |  * Intel 8254[340]NN Gigabit Ethernet PCI Controllers | 
 |  * as found on the Intel PRO/1000 series of adapters: | 
 |  *	82543GC	Intel PRO/1000 T | 
 |  *	82544EI Intel PRO/1000 XT | 
 |  *	82540EM Intel PRO/1000 MT | 
 |  *	82541[GP]I | 
 |  *	82547GI | 
 |  *	82546GB | 
 |  *	82546EB | 
 |  * To Do: | 
 |  *	finish autonegotiation code; | 
 |  *	integrate fiber stuff back in (this ONLY handles | 
 |  *	the CAT5 cards at the moment); | 
 |  *	add checksum-offload; | 
 |  *	add tuning control via ctl file; | 
 |  *	this driver is little-endian specific. | 
 |  * | 
 |  * Modified by brho: | 
 |  * 	ported to Akaros | 
 |  * 	fixed mii bugs (allocation, startup, miirw, etc) | 
 |  * 	fixed CLS bug (continue -> break) | 
 |  * 	made sure igbepci only runs once, even if it fails */ | 
 |  | 
 | #include <vfs.h> | 
 | #include <kfs.h> | 
 | #include <slab.h> | 
 | #include <kmalloc.h> | 
 | #include <kref.h> | 
 | #include <string.h> | 
 | #include <stdio.h> | 
 | #include <assert.h> | 
 | #include <error.h> | 
 | #include <cpio.h> | 
 | #include <pmap.h> | 
 | #include <smp.h> | 
 | #include <arch/pci.h> | 
 | #include <net/ip.h> | 
 | #include <ns.h> | 
 | #include "ethermii.h" | 
 |  | 
 | #define ilock(x) spin_lock_irqsave(x) | 
 | #define iunlock(x) spin_unlock_irqsave(x) | 
 |  | 
 | enum { | 
 | 	i82542		= (0x1000<<16)|0x8086, | 
 | 	i82543gc	= (0x1004<<16)|0x8086, | 
 | 	i82544ei	= (0x1008<<16)|0x8086, | 
 | 	i82544eif	= (0x1009<<16)|0x8086, | 
 | 	i82544gc	= (0x100d<<16)|0x8086, | 
 | 	i82540em	= (0x100E<<16)|0x8086, | 
 | 	i82540eplp	= (0x101E<<16)|0x8086, | 
 | 	i82545em	= (0x100F<<16)|0x8086, | 
 | 	i82545gmc	= (0x1026<<16)|0x8086, | 
 | 	i82547ei	= (0x1019<<16)|0x8086, | 
 | 	i82547gi	= (0x1075<<16)|0x8086, | 
 | 	i82541ei	= (0x1013<<16)|0x8086, | 
 | 	i82541gi	= (0x1076<<16)|0x8086, | 
 | 	i82541gi2	= (0x1077<<16)|0x8086, | 
 | 	i82541pi	= (0x107c<<16)|0x8086, | 
 | 	i82546gb	= (0x1079<<16)|0x8086, | 
 | 	i82546eb	= (0x1010<<16)|0x8086, | 
 | }; | 
 |  | 
 | enum { | 
 | 	Ctrl		= 0x00000000,	/* Device Control */ | 
 | 	Ctrldup		= 0x00000004,	/* Device Control Duplicate */ | 
 | 	Status		= 0x00000008,	/* Device Status */ | 
 | 	Eecd		= 0x00000010,	/* EEPROM/Flash Control/Data */ | 
 | 	Ctrlext		= 0x00000018,	/* Extended Device Control */ | 
 | 	Mdic		= 0x00000020,	/* MDI Control */ | 
 | 	Fcal		= 0x00000028,	/* Flow Control Address Low */ | 
 | 	Fcah		= 0x0000002C,	/* Flow Control Address High */ | 
 | 	Fct		= 0x00000030,	/* Flow Control Type */ | 
 | 	Icr		= 0x000000C0,	/* Interrupt Cause Read */ | 
 | 	Ics		= 0x000000C8,	/* Interrupt Cause Set */ | 
 | 	Ims		= 0x000000D0,	/* Interrupt Mask Set/Read */ | 
 | 	Imc		= 0x000000D8,	/* Interrupt mask Clear */ | 
 | 	Rctl		= 0x00000100,	/* Receive Control */ | 
 | 	Fcttv		= 0x00000170,	/* Flow Control Transmit Timer Value */ | 
 | 	Txcw		= 0x00000178,	/* Transmit Configuration Word */ | 
 | 	Rxcw		= 0x00000180,	/* Receive Configuration Word */ | 
 | 	/* on the oldest cards (8254[23]), the Mta register is at 0x200 */ | 
 | 	Tctl		= 0x00000400,	/* Transmit Control */ | 
 | 	Tipg		= 0x00000410,	/* Transmit IPG */ | 
 | 	Tbt		= 0x00000448,	/* Transmit Burst Timer */ | 
 | 	Ait		= 0x00000458,	/* Adaptive IFS Throttle */ | 
 | 	Fcrtl		= 0x00002160,	/* Flow Control RX Threshold Low */ | 
 | 	Fcrth		= 0x00002168,	/* Flow Control Rx Threshold High */ | 
 | 	Rdfh		= 0x00002410,	/* Receive data fifo head */ | 
 | 	Rdft		= 0x00002418,	/* Receive data fifo tail */ | 
 | 	Rdfhs		= 0x00002420,	/* Receive data fifo head saved */ | 
 | 	Rdfts		= 0x00002428,	/* Receive data fifo tail saved */ | 
 | 	Rdfpc		= 0x00002430,	/* Receive data fifo packet count */ | 
 | 	Rdbal		= 0x00002800,	/* Rd Base Address Low */ | 
 | 	Rdbah		= 0x00002804,	/* Rd Base Address High */ | 
 | 	Rdlen		= 0x00002808,	/* Receive Descriptor Length */ | 
 | 	Rdh		= 0x00002810,	/* Receive Descriptor Head */ | 
 | 	Rdt		= 0x00002818,	/* Receive Descriptor Tail */ | 
 | 	Rdtr		= 0x00002820,	/* Receive Descriptor Timer Ring */ | 
 | 	Rxdctl		= 0x00002828,	/* Receive Descriptor Control */ | 
 | 	Radv		= 0x0000282C,	/* Receive Interrupt Absolute Delay Timer */ | 
 | 	Txdmac		= 0x00003000,	/* Transfer DMA Control */ | 
 | 	Ett		= 0x00003008,	/* Early Transmit Control */ | 
 | 	Tdfh		= 0x00003410,	/* Transmit data fifo head */ | 
 | 	Tdft		= 0x00003418,	/* Transmit data fifo tail */ | 
 | 	Tdfhs		= 0x00003420,	/* Transmit data Fifo Head saved */ | 
 | 	Tdfts		= 0x00003428,	/* Transmit data fifo tail saved */ | 
 | 	Tdfpc		= 0x00003430,	/* Trasnmit data Fifo packet count */ | 
 | 	Tdbal		= 0x00003800,	/* Td Base Address Low */ | 
 | 	Tdbah		= 0x00003804,	/* Td Base Address High */ | 
 | 	Tdlen		= 0x00003808,	/* Transmit Descriptor Length */ | 
 | 	Tdh		= 0x00003810,	/* Transmit Descriptor Head */ | 
 | 	Tdt		= 0x00003818,	/* Transmit Descriptor Tail */ | 
 | 	Tidv		= 0x00003820,	/* Transmit Interrupt Delay Value */ | 
 | 	Txdctl		= 0x00003828,	/* Transmit Descriptor Control */ | 
 | 	Tadv		= 0x0000382C,	/* Transmit Interrupt Absolute Delay Timer */ | 
 |  | 
 | 	Statistics	= 0x00004000,	/* Start of Statistics Area */ | 
 | 	Gorcl		= 0x88/4,	/* Good Octets Received Count */ | 
 | 	Gotcl		= 0x90/4,	/* Good Octets Transmitted Count */ | 
 | 	Torl		= 0xC0/4,	/* Total Octets Received */ | 
 | 	Totl		= 0xC8/4,	/* Total Octets Transmitted */ | 
 | 	Nstatistics	= 64, | 
 |  | 
 | 	Rxcsum		= 0x00005000,	/* Receive Checksum Control */ | 
 | 	Mta		= 0x00005200,	/* Multicast Table Array */ | 
 | 	Ral		= 0x00005400,	/* Receive Address Low */ | 
 | 	Rah		= 0x00005404,	/* Receive Address High */ | 
 | 	Manc		= 0x00005820,	/* Management Control */ | 
 | }; | 
 |  | 
 | enum {					/* Ctrl */ | 
 | 	Bem		= 0x00000002,	/* Big Endian Mode */ | 
 | 	Prior		= 0x00000004,	/* Priority on the PCI bus */ | 
 | 	Lrst		= 0x00000008,	/* Link Reset */ | 
 | 	Asde		= 0x00000020,	/* Auto-Speed Detection Enable */ | 
 | 	Slu		= 0x00000040,	/* Set Link Up */ | 
 | 	Ilos		= 0x00000080,	/* Invert Loss of Signal (LOS) */ | 
 | 	SspeedMASK	= 0x00000300,	/* Speed Selection */ | 
 | 	SspeedSHIFT	= 8, | 
 | 	Sspeed10	= 0x00000000,	/* 10Mb/s */ | 
 | 	Sspeed100	= 0x00000100,	/* 100Mb/s */ | 
 | 	Sspeed1000	= 0x00000200,	/* 1000Mb/s */ | 
 | 	Frcspd		= 0x00000800,	/* Force Speed */ | 
 | 	Frcdplx		= 0x00001000,	/* Force Duplex */ | 
 | 	SwdpinsloMASK	= 0x003C0000,	/* Software Defined Pins - lo nibble */ | 
 | 	SwdpinsloSHIFT	= 18, | 
 | 	SwdpioloMASK	= 0x03C00000,	/* Software Defined Pins - I or O */ | 
 | 	SwdpioloSHIFT	= 22, | 
 | 	Devrst		= 0x04000000,	/* Device Reset */ | 
 | 	Rfce		= 0x08000000,	/* Receive Flow Control Enable */ | 
 | 	Tfce		= 0x10000000,	/* Transmit Flow Control Enable */ | 
 | 	Vme		= 0x40000000,	/* VLAN Mode Enable */ | 
 | }; | 
 |  | 
 | /* | 
 |  * can't find Tckok nor Rbcok in any Intel docs, | 
 |  * but even 82543gc docs define Lanid. | 
 |  */ | 
 | enum {					/* Status */ | 
 | 	Lu		= 0x00000002,	/* Link Up */ | 
 | 	Lanid		= 0x0000000C,	/* mask for Lan ID. (function id) */ | 
 | //	Tckok		= 0x00000004,	/* Transmit clock is running */ | 
 | //	Rbcok		= 0x00000008,	/* Receive clock is running */ | 
 | 	Txoff		= 0x00000010,	/* Transmission Paused */ | 
 | 	Tbimode		= 0x00000020,	/* TBI Mode Indication */ | 
 | 	LspeedMASK	= 0x000000C0,	/* Link Speed Setting */ | 
 | 	LspeedSHIFT	= 6, | 
 | 	Lspeed10	= 0x00000000,	/* 10Mb/s */ | 
 | 	Lspeed100	= 0x00000040,	/* 100Mb/s */ | 
 | 	Lspeed1000	= 0x00000080,	/* 1000Mb/s */ | 
 | 	Mtxckok		= 0x00000400,	/* MTX clock is running */ | 
 | 	Pci66		= 0x00000800,	/* PCI Bus speed indication */ | 
 | 	Bus64		= 0x00001000,	/* PCI Bus width indication */ | 
 | 	Pcixmode	= 0x00002000,	/* PCI-X mode */ | 
 | 	PcixspeedMASK	= 0x0000C000,	/* PCI-X bus speed */ | 
 | 	PcixspeedSHIFT	= 14, | 
 | 	Pcix66		= 0x00000000,	/* 50-66MHz */ | 
 | 	Pcix100		= 0x00004000,	/* 66-100MHz */ | 
 | 	Pcix133		= 0x00008000,	/* 100-133MHz */ | 
 | }; | 
 |  | 
 | enum {					/* Ctrl and Status */ | 
 | 	Fd		= 0x00000001,	/* Full-Duplex */ | 
 | 	AsdvMASK	= 0x00000300, | 
 | 	AsdvSHIFT	= 8, | 
 | 	Asdv10		= 0x00000000,	/* 10Mb/s */ | 
 | 	Asdv100		= 0x00000100,	/* 100Mb/s */ | 
 | 	Asdv1000	= 0x00000200,	/* 1000Mb/s */ | 
 | }; | 
 |  | 
 | enum {					/* Eecd */ | 
 | 	Sk		= 0x00000001,	/* Clock input to the EEPROM */ | 
 | 	Cs		= 0x00000002,	/* Chip Select */ | 
 | 	Di		= 0x00000004,	/* Data Input to the EEPROM */ | 
 | 	Do		= 0x00000008,	/* Data Output from the EEPROM */ | 
 | 	Areq		= 0x00000040,	/* EEPROM Access Request */ | 
 | 	Agnt		= 0x00000080,	/* EEPROM Access Grant */ | 
 | 	Eepresent	= 0x00000100,	/* EEPROM Present */ | 
 | 	Eesz256		= 0x00000200,	/* EEPROM is 256 words not 64 */ | 
 | 	Eeszaddr	= 0x00000400,	/* EEPROM size for 8254[17] */ | 
 | 	Spi		= 0x00002000,	/* EEPROM is SPI not Microwire */ | 
 | }; | 
 |  | 
 | enum {					/* Ctrlext */ | 
 | 	Gpien		= 0x0000000F,	/* General Purpose Interrupt Enables */ | 
 | 	SwdpinshiMASK	= 0x000000F0,	/* Software Defined Pins - hi nibble */ | 
 | 	SwdpinshiSHIFT	= 4, | 
 | 	SwdpiohiMASK	= 0x00000F00,	/* Software Defined Pins - I or O */ | 
 | 	SwdpiohiSHIFT	= 8, | 
 | 	Asdchk		= 0x00001000,	/* ASD Check */ | 
 | 	Eerst		= 0x00002000,	/* EEPROM Reset */ | 
 | 	Ips		= 0x00004000,	/* Invert Power State */ | 
 | 	Spdbyps		= 0x00008000,	/* Speed Select Bypass */ | 
 | }; | 
 |  | 
 | enum {					/* EEPROM content offsets */ | 
 | 	Ea		= 0x00,		/* Ethernet Address */ | 
 | 	Cf		= 0x03,		/* Compatibility Field */ | 
 | 	Pba		= 0x08,		/* Printed Board Assembly number */ | 
 | 	Icw1		= 0x0A,		/* Initialization Control Word 1 */ | 
 | 	Sid		= 0x0B,		/* Subsystem ID */ | 
 | 	Svid		= 0x0C,		/* Subsystem Vendor ID */ | 
 | 	Did		= 0x0D,		/* Device ID */ | 
 | 	Vid		= 0x0E,		/* Vendor ID */ | 
 | 	Icw2		= 0x0F,		/* Initialization Control Word 2 */ | 
 | }; | 
 |  | 
 | enum {					/* Mdic */ | 
 | 	MDIdMASK	= 0x0000FFFF,	/* Data */ | 
 | 	MDIdSHIFT	= 0, | 
 | 	MDIrMASK	= 0x001F0000,	/* PHY Register Address */ | 
 | 	MDIrSHIFT	= 16, | 
 | 	MDIpMASK	= 0x03E00000,	/* PHY Address */ | 
 | 	MDIpSHIFT	= 21, | 
 | 	MDIwop		= 0x04000000,	/* Write Operation */ | 
 | 	MDIrop		= 0x08000000,	/* Read Operation */ | 
 | 	MDIready	= 0x10000000,	/* End of Transaction */ | 
 | 	MDIie		= 0x20000000,	/* Interrupt Enable */ | 
 | 	MDIe		= 0x40000000,	/* Error */ | 
 | }; | 
 |  | 
 | enum {					/* Icr, Ics, Ims, Imc */ | 
 | 	Txdw		= 0x00000001,	/* Transmit Descriptor Written Back */ | 
 | 	Txqe		= 0x00000002,	/* Transmit Queue Empty */ | 
 | 	Lsc		= 0x00000004,	/* Link Status Change */ | 
 | 	Rxseq		= 0x00000008,	/* Receive Sequence Error */ | 
 | 	Rxdmt0		= 0x00000010,	/* Rd Minimum Threshold Reached */ | 
 | 	Rxo		= 0x00000040,	/* Receiver Overrun */ | 
 | 	Rxt0		= 0x00000080,	/* Receiver Timer Interrupt */ | 
 | 	Mdac		= 0x00000200,	/* MDIO Access Completed */ | 
 | 	Rxcfg		= 0x00000400,	/* Receiving /C/ ordered sets */ | 
 | 	Gpi0		= 0x00000800,	/* General Purpose Interrupts */ | 
 | 	Gpi1		= 0x00001000, | 
 | 	Gpi2		= 0x00002000, | 
 | 	Gpi3		= 0x00004000, | 
 | }; | 
 |  | 
 | /* | 
 |  * The Mdic register isn't implemented on the 82543GC, | 
 |  * the software defined pins are used instead. | 
 |  * These definitions work for the Intel PRO/1000 T Server Adapter. | 
 |  * The direction pin bits are read from the EEPROM. | 
 |  */ | 
 | enum { | 
 | 	Mdd		= ((1<<2)<<SwdpinsloSHIFT),	/* data */ | 
 | 	Mddo		= ((1<<2)<<SwdpioloSHIFT),	/* pin direction */ | 
 | 	Mdc		= ((1<<3)<<SwdpinsloSHIFT),	/* clock */ | 
 | 	Mdco		= ((1<<3)<<SwdpioloSHIFT),	/* pin direction */ | 
 | 	Mdr		= ((1<<0)<<SwdpinshiSHIFT),	/* reset */ | 
 | 	Mdro		= ((1<<0)<<SwdpiohiSHIFT),	/* pin direction */ | 
 | }; | 
 |  | 
 | enum {					/* Txcw */ | 
 | 	TxcwFd		= 0x00000020,	/* Full Duplex */ | 
 | 	TxcwHd		= 0x00000040,	/* Half Duplex */ | 
 | 	TxcwPauseMASK	= 0x00000180,	/* Pause */ | 
 | 	TxcwPauseSHIFT	= 7, | 
 | 	TxcwPs		= (1<<TxcwPauseSHIFT),	/* Pause Supported */ | 
 | 	TxcwAs		= (2<<TxcwPauseSHIFT),	/* Asymmetric FC desired */ | 
 | 	TxcwRfiMASK	= 0x00003000,	/* Remote Fault Indication */ | 
 | 	TxcwRfiSHIFT	= 12, | 
 | 	TxcwNpr		= 0x00008000,	/* Next Page Request */ | 
 | 	TxcwConfig	= 0x40000000,	/* Transmit COnfig Control */ | 
 | 	TxcwAne		= 0x80000000,	/* Auto-Negotiation Enable */ | 
 | }; | 
 |  | 
 | enum {					/* Rxcw */ | 
 | 	Rxword		= 0x0000FFFF,	/* Data from auto-negotiation process */ | 
 | 	Rxnocarrier	= 0x04000000,	/* Carrier Sense indication */ | 
 | 	Rxinvalid	= 0x08000000,	/* Invalid Symbol during configuration */ | 
 | 	Rxchange	= 0x10000000,	/* Change to the Rxword indication */ | 
 | 	Rxconfig	= 0x20000000,	/* /C/ order set reception indication */ | 
 | 	Rxsync		= 0x40000000,	/* Lost bit synchronization indication */ | 
 | 	Anc		= 0x80000000,	/* Auto Negotiation Complete */ | 
 | }; | 
 |  | 
 | enum {					/* Rctl */ | 
 | 	Rrst		= 0x00000001,	/* Receiver Software Reset */ | 
 | 	Ren		= 0x00000002,	/* Receiver Enable */ | 
 | 	Sbp		= 0x00000004,	/* Store Bad Packets */ | 
 | 	Upe		= 0x00000008,	/* Unicast Promiscuous Enable */ | 
 | 	Mpe		= 0x00000010,	/* Multicast Promiscuous Enable */ | 
 | 	Lpe		= 0x00000020,	/* Long Packet Reception Enable */ | 
 | 	LbmMASK		= 0x000000C0,	/* Loopback Mode */ | 
 | 	LbmOFF		= 0x00000000,	/* No Loopback */ | 
 | 	LbmTBI		= 0x00000040,	/* TBI Loopback */ | 
 | 	LbmMII		= 0x00000080,	/* GMII/MII Loopback */ | 
 | 	LbmXCVR		= 0x000000C0,	/* Transceiver Loopback */ | 
 | 	RdtmsMASK	= 0x00000300,	/* Rd Minimum Threshold Size */ | 
 | 	RdtmsHALF	= 0x00000000,	/* Threshold is 1/2 Rdlen */ | 
 | 	RdtmsQUARTER	= 0x00000100,	/* Threshold is 1/4 Rdlen */ | 
 | 	RdtmsEIGHTH	= 0x00000200,	/* Threshold is 1/8 Rdlen */ | 
 | 	MoMASK		= 0x00003000,	/* Multicast Offset */ | 
 | 	Mo47b36		= 0x00000000,	/* bits [47:36] of received address */ | 
 | 	Mo46b35		= 0x00001000,	/* bits [46:35] of received address */ | 
 | 	Mo45b34		= 0x00002000,	/* bits [45:34] of received address */ | 
 | 	Mo43b32		= 0x00003000,	/* bits [43:32] of received address */ | 
 | 	Bam		= 0x00008000,	/* Broadcast Accept Mode */ | 
 | 	BsizeMASK	= 0x00030000,	/* Receive Buffer Size */ | 
 | 	Bsize2048	= 0x00000000,	/* Bsex = 0 */ | 
 | 	Bsize1024	= 0x00010000,	/* Bsex = 0 */ | 
 | 	Bsize512	= 0x00020000,	/* Bsex = 0 */ | 
 | 	Bsize256	= 0x00030000,	/* Bsex = 0 */ | 
 | 	Bsize16384	= 0x00010000,	/* Bsex = 1 */ | 
 | 	Vfe		= 0x00040000,	/* VLAN Filter Enable */ | 
 | 	Cfien		= 0x00080000,	/* Canonical Form Indicator Enable */ | 
 | 	Cfi		= 0x00100000,	/* Canonical Form Indicator value */ | 
 | 	Dpf		= 0x00400000,	/* Discard Pause Frames */ | 
 | 	Pmcf		= 0x00800000,	/* Pass MAC Control Frames */ | 
 | 	Bsex		= 0x02000000,	/* Buffer Size Extension */ | 
 | 	Secrc		= 0x04000000,	/* Strip CRC from incoming packet */ | 
 | }; | 
 |  | 
 | enum {					/* Tctl */ | 
 | 	Trst		= 0x00000001,	/* Transmitter Software Reset */ | 
 | 	Ten		= 0x00000002,	/* Transmit Enable */ | 
 | 	Psp		= 0x00000008,	/* Pad Short Packets */ | 
 | 	CtMASK		= 0x00000FF0,	/* Collision Threshold */ | 
 | 	CtSHIFT		= 4, | 
 | 	ColdMASK	= 0x003FF000,	/* Collision Distance */ | 
 | 	ColdSHIFT	= 12, | 
 | 	Swxoff		= 0x00400000,	/* Sofware XOFF Transmission */ | 
 | 	Pbe		= 0x00800000,	/* Packet Burst Enable */ | 
 | 	Rtlc		= 0x01000000,	/* Re-transmit on Late Collision */ | 
 | 	Nrtu		= 0x02000000,	/* No Re-transmit on Underrrun */ | 
 | }; | 
 |  | 
 | enum {					/* [RT]xdctl */ | 
 | 	PthreshMASK	= 0x0000003F,	/* Prefetch Threshold */ | 
 | 	PthreshSHIFT	= 0, | 
 | 	HthreshMASK	= 0x00003F00,	/* Host Threshold */ | 
 | 	HthreshSHIFT	= 8, | 
 | 	WthreshMASK	= 0x003F0000,	/* Writeback Threshold */ | 
 | 	WthreshSHIFT	= 16, | 
 | 	Gran		= 0x01000000,	/* Granularity */ | 
 | 	LthreshMASK	= 0xFE000000,	/* Low Threshold */ | 
 | 	LthreshSHIFT	= 25, | 
 | }; | 
 |  | 
 | enum {					/* Rxcsum */ | 
 | 	PcssMASK	= 0x000000FF,	/* Packet Checksum Start */ | 
 | 	PcssSHIFT	= 0, | 
 | 	Ipofl		= 0x00000100,	/* IP Checksum Off-load Enable */ | 
 | 	Tuofl		= 0x00000200,	/* TCP/UDP Checksum Off-load Enable */ | 
 | }; | 
 |  | 
 | enum {					/* Manc */ | 
 | 	Arpen		= 0x00002000,	/* Enable ARP Request Filtering */ | 
 | }; | 
 |  | 
 | enum {					/* Receive Delay Timer Ring */ | 
 | 	DelayMASK	= 0x0000FFFF,	/* delay timer in 1.024nS increments */ | 
 | 	DelaySHIFT	= 0, | 
 | 	Fpd		= 0x80000000,	/* Flush partial Descriptor Block */ | 
 | }; | 
 |  | 
 | typedef struct Rd {			/* Receive Descriptor */ | 
 | 	unsigned int	addr[2]; | 
 | 	uint16_t	length; | 
 | 	uint16_t	checksum; | 
 | 	uint8_t	status; | 
 | 	uint8_t	errors; | 
 | 	uint16_t	special; | 
 | } Rd; | 
 |  | 
 | enum {					/* Rd status */ | 
 | 	Rdd		= 0x01,		/* Descriptor Done */ | 
 | 	Reop		= 0x02,		/* End of Packet */ | 
 | 	Ixsm		= 0x04,		/* Ignore Checksum Indication */ | 
 | 	Vp		= 0x08,		/* Packet is 802.1Q (matched VET) */ | 
 | 	Tcpcs		= 0x20,		/* TCP Checksum Calculated on Packet */ | 
 | 	Ipcs		= 0x40,		/* IP Checksum Calculated on Packet */ | 
 | 	Pif		= 0x80,		/* Passed in-exact filter */ | 
 | }; | 
 |  | 
 | enum {					/* Rd errors */ | 
 | 	Ce		= 0x01,		/* CRC Error or Alignment Error */ | 
 | 	Se		= 0x02,		/* Symbol Error */ | 
 | 	Seq		= 0x04,		/* Sequence Error */ | 
 | 	Cxe		= 0x10,		/* Carrier Extension Error */ | 
 | 	Tcpe		= 0x20,		/* TCP/UDP Checksum Error */ | 
 | 	Ipe		= 0x40,		/* IP Checksum Error */ | 
 | 	Rxe		= 0x80,		/* RX Data Error */ | 
 | }; | 
 |  | 
 | typedef struct Td Td; | 
 | struct Td {				/* Transmit Descriptor */ | 
 | 	union { | 
 | 		unsigned int	addr[2];	/* Data */ | 
 | 		struct {		/* Context */ | 
 | 			uint8_t	ipcss; | 
 | 			uint8_t	ipcso; | 
 | 			uint16_t	ipcse; | 
 | 			uint8_t	tucss; | 
 | 			uint8_t	tucso; | 
 | 			uint16_t	tucse; | 
 | 		}; | 
 | 	}; | 
 | 	unsigned int	control; | 
 | 	unsigned int	status; | 
 | }; | 
 |  | 
 | enum {					/* Td control */ | 
 | 	LenMASK		= 0x000FFFFF,	/* Data/Packet Length Field */ | 
 | 	LenSHIFT	= 0, | 
 | 	DtypeCD		= 0x00000000,	/* Data Type 'Context Descriptor' */ | 
 | 	DtypeDD		= 0x00100000,	/* Data Type 'Data Descriptor' */ | 
 | 	PtypeTCP	= 0x01000000,	/* TCP/UDP Packet Type (CD) */ | 
 | 	Teop		= 0x01000000,	/* End of Packet (DD) */ | 
 | 	PtypeIP		= 0x02000000,	/* IP Packet Type (CD) */ | 
 | 	Ifcs		= 0x02000000,	/* Insert FCS (DD) */ | 
 | 	Tse		= 0x04000000,	/* TCP Segmentation Enable */ | 
 | 	Rs		= 0x08000000,	/* Report Status */ | 
 | 	Rps		= 0x10000000,	/* Report Status Sent */ | 
 | 	Dext		= 0x20000000,	/* Descriptor Extension */ | 
 | 	Vle		= 0x40000000,	/* VLAN Packet Enable */ | 
 | 	Ide		= 0x80000000,	/* Interrupt Delay Enable */ | 
 | }; | 
 |  | 
 | enum {					/* Td status */ | 
 | 	Tdd		= 0x00000001,	/* Descriptor Done */ | 
 | 	Ec		= 0x00000002,	/* Excess Collisions */ | 
 | 	Lc		= 0x00000004,	/* Late Collision */ | 
 | 	Tu		= 0x00000008,	/* Transmit Underrun */ | 
 | 	Iixsm		= 0x00000100,	/* Insert IP Checksum */ | 
 | 	Itxsm		= 0x00000200,	/* Insert TCP/UDP Checksum */ | 
 | 	HdrlenMASK	= 0x0000FF00,	/* Header Length (Tse) */ | 
 | 	HdrlenSHIFT	= 8, | 
 | 	VlanMASK	= 0x0FFF0000,	/* VLAN Identifier */ | 
 | 	VlanSHIFT	= 16, | 
 | 	Tcfi		= 0x10000000,	/* Canonical Form Indicator */ | 
 | 	PriMASK		= 0xE0000000,	/* User Priority */ | 
 | 	PriSHIFT	= 29, | 
 | 	MssMASK		= 0xFFFF0000,	/* Maximum Segment Size (Tse) */ | 
 | 	MssSHIFT	= 16, | 
 | }; | 
 |  | 
 | enum { | 
 | 	Nrd		= 256,		/* multiple of 8 */ | 
 | 	Ntd		= 64,		/* multiple of 8 */ | 
 | 	Rbsz		= 2048, | 
 | }; | 
 |  | 
 | struct ctlr { | 
 | 	int	port; | 
 | 	struct pci_device *pci; | 
 | 	struct ctlr*	next; | 
 | 	struct ether*	edev; | 
 | 	int	active; | 
 | 	int	started; | 
 | 	int	id; | 
 | 	int	cls; | 
 | 	uint16_t	eeprom[0x40]; | 
 |  | 
 | 	qlock_t	alock;			/* attach */ | 
 | 	void*	alloc;			/* receive/transmit descriptors */ | 
 | 	int	nrd; | 
 | 	int	ntd; | 
 |  | 
 | 	int*	nic; | 
 | 	spinlock_t	imlock; | 
 | 	int	im;			/* interrupt mask */ | 
 |  | 
 | 	struct mii*	mii; | 
 | 	struct rendez	lrendez; | 
 | 	int	lim; | 
 |  | 
 | 	int	link; | 
 |  | 
 | 	qlock_t	slock; | 
 | 	unsigned int	statistics[Nstatistics]; | 
 | 	unsigned int	lsleep; | 
 | 	unsigned int	lintr; | 
 | 	unsigned int	rsleep; | 
 | 	unsigned int	rintr; | 
 | 	unsigned int	txdw; | 
 | 	unsigned int	tintr; | 
 | 	unsigned int	ixsm; | 
 | 	unsigned int	ipcs; | 
 | 	unsigned int	tcpcs; | 
 |  | 
 | 	uint8_t	ra[Eaddrlen];		/* receive address */ | 
 | 	uint32_t	mta[128];		/* multicast table array */ | 
 |  | 
 | 	struct rendez	rrendez; | 
 | 	int	rim; | 
 | 	int	rdfree; | 
 | 	Rd*	rdba;			/* receive descriptor base address */ | 
 | 	struct block**	rb;			/* receive buffers */ | 
 | 	int	rdh;			/* receive descriptor head */ | 
 | 	int	rdt;			/* receive descriptor tail */ | 
 | 	int	rdtr;			/* receive delay timer ring value */ | 
 |  | 
 | 	spinlock_t	tlock; | 
 | 	int	tbusy; | 
 | 	int	tdfree; | 
 | 	Td*	tdba;			/* transmit descriptor base address */ | 
 | 	struct block**	tb;			/* transmit buffers */ | 
 | 	int	tdh;			/* transmit descriptor head */ | 
 | 	int	tdt;			/* transmit descriptor tail */ | 
 |  | 
 | 	int	txcw; | 
 | 	int	fcrtl; | 
 | 	int	fcrth; | 
 | }; | 
 |  | 
 | static inline uint32_t csr32r(struct ctlr *c, uintptr_t reg) | 
 | { | 
 | 	return read_mmreg32((uintptr_t)(c->nic + (reg / 4))); | 
 | } | 
 |  | 
 | static inline void csr32w(struct ctlr *c, uintptr_t reg, uint32_t val) | 
 | { | 
 | 	write_mmreg32((uintptr_t)(c->nic + (reg / 4)), val); | 
 | } | 
 |  | 
 | static struct ctlr* igbectlrhead; | 
 | static struct ctlr* igbectlrtail; | 
 |  | 
 | static char* statistics[Nstatistics] = { | 
 | 	"CRC Error", | 
 | 	"Alignment Error", | 
 | 	"Symbol Error", | 
 | 	"RX Error", | 
 | 	"Missed Packets", | 
 | 	"Single Collision", | 
 | 	"Excessive Collisions", | 
 | 	"Multiple Collision", | 
 | 	"Late Collisions", | 
 | 	NULL, | 
 | 	"Collision", | 
 | 	"Transmit Underrun", | 
 | 	"Defer", | 
 | 	"Transmit - No CRS", | 
 | 	"Sequence Error", | 
 | 	"Carrier Extension Error", | 
 | 	"Receive Error Length", | 
 | 	NULL, | 
 | 	"XON Received", | 
 | 	"XON Transmitted", | 
 | 	"XOFF Received", | 
 | 	"XOFF Transmitted", | 
 | 	"FC Received Unsupported", | 
 | 	"Packets Received (64 Bytes)", | 
 | 	"Packets Received (65-127 Bytes)", | 
 | 	"Packets Received (128-255 Bytes)", | 
 | 	"Packets Received (256-511 Bytes)", | 
 | 	"Packets Received (512-1023 Bytes)", | 
 | 	"Packets Received (1024-1522 Bytes)", | 
 | 	"Good Packets Received", | 
 | 	"Broadcast Packets Received", | 
 | 	"Multicast Packets Received", | 
 | 	"Good Packets Transmitted", | 
 | 	NULL, | 
 | 	"Good Octets Received", | 
 | 	NULL, | 
 | 	"Good Octets Transmitted", | 
 | 	NULL, | 
 | 	NULL, | 
 | 	NULL, | 
 | 	"Receive No Buffers", | 
 | 	"Receive Undersize", | 
 | 	"Receive Fragment", | 
 | 	"Receive Oversize", | 
 | 	"Receive Jabber", | 
 | 	NULL, | 
 | 	NULL, | 
 | 	NULL, | 
 | 	"Total Octets Received", | 
 | 	NULL, | 
 | 	"Total Octets Transmitted", | 
 | 	NULL, | 
 | 	"Total Packets Received", | 
 | 	"Total Packets Transmitted", | 
 | 	"Packets Transmitted (64 Bytes)", | 
 | 	"Packets Transmitted (65-127 Bytes)", | 
 | 	"Packets Transmitted (128-255 Bytes)", | 
 | 	"Packets Transmitted (256-511 Bytes)", | 
 | 	"Packets Transmitted (512-1023 Bytes)", | 
 | 	"Packets Transmitted (1024-1522 Bytes)", | 
 | 	"Multicast Packets Transmitted", | 
 | 	"Broadcast Packets Transmitted", | 
 | 	"TCP Segmentation Context Transmitted", | 
 | 	"TCP Segmentation Context Fail", | 
 | }; | 
 |  | 
 | static void igbe_print_rd(struct Rd *rd) | 
 | { | 
 | 	printk("Rd %p: stat 0x%02x, err 0x%02x, len 0x%04x, check 0x%04x, " | 
 | 	       "spec 0x%04x, addr[1] 0x%08x, addr[0] 0x%08x\n", rd, | 
 | 		   rd->status, | 
 | 		   rd->errors, | 
 | 		   rd->length, | 
 | 		   rd->checksum, | 
 | 		   rd->special, | 
 | 		   rd->addr[1], | 
 | 		   rd->addr[0]); | 
 | } | 
 |  | 
 | static long | 
 | igbeifstat(struct ether* edev, void* a, long n, uint32_t offset) | 
 | { | 
 | 	struct ctlr *ctlr; | 
 | 	char *p, *s; | 
 | 	int i, l, r; | 
 | 	uint64_t tuvl, ruvl; | 
 |  | 
 | 	ctlr = edev->ctlr; | 
 | 	qlock(&ctlr->slock); | 
 | 	p = kzmalloc(READSTR, 0); | 
 | 	if(p == NULL) { | 
 | 		qunlock(&ctlr->slock); | 
 | 		error(ENOMEM, ERROR_FIXME); | 
 | 	} | 
 | 	l = 0; | 
 | 	for(i = 0; i < Nstatistics; i++){ | 
 | 		r = csr32r(ctlr, Statistics+i*4); | 
 | 		if((s = statistics[i]) == NULL) | 
 | 			continue; | 
 | 		switch(i){ | 
 | 		case Gorcl: | 
 | 		case Gotcl: | 
 | 		case Torl: | 
 | 		case Totl: | 
 | 			ruvl = r; | 
 | 			ruvl += ((uint64_t)csr32r(ctlr, Statistics+(i+1)*4))<<32; | 
 | 			tuvl = ruvl; | 
 | 			tuvl += ctlr->statistics[i]; | 
 | 			tuvl += ((uint64_t)ctlr->statistics[i+1])<<32; | 
 | 			if(tuvl == 0) | 
 | 				continue; | 
 | 			ctlr->statistics[i] = tuvl; | 
 | 			ctlr->statistics[i+1] = tuvl>>32; | 
 | 			l += snprintf(p+l, READSTR-l, "%s: %llud %llud\n", | 
 | 				s, tuvl, ruvl); | 
 | 			i++; | 
 | 			break; | 
 |  | 
 | 		default: | 
 | 			ctlr->statistics[i] += r; | 
 | 			if(ctlr->statistics[i] == 0) | 
 | 				continue; | 
 | 			l += snprintf(p+l, READSTR-l, "%s: %ud %ud\n", | 
 | 				s, ctlr->statistics[i], r); | 
 | 			break; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	l += snprintf(p+l, READSTR-l, "lintr: %ud %ud\n", | 
 | 		ctlr->lintr, ctlr->lsleep); | 
 | 	l += snprintf(p+l, READSTR-l, "rintr: %ud %ud\n", | 
 | 		ctlr->rintr, ctlr->rsleep); | 
 | 	l += snprintf(p+l, READSTR-l, "tintr: %ud %ud\n", | 
 | 		ctlr->tintr, ctlr->txdw); | 
 | 	l += snprintf(p+l, READSTR-l, "ixcs: %ud %ud %ud\n", | 
 | 		ctlr->ixsm, ctlr->ipcs, ctlr->tcpcs); | 
 | 	l += snprintf(p+l, READSTR-l, "rdtr: %ud\n", ctlr->rdtr); | 
 | 	l += snprintf(p+l, READSTR-l, "Ctrlext: %08x\n", csr32r(ctlr, Ctrlext)); | 
 |  | 
 | 	l += snprintf(p+l, READSTR-l, "eeprom:"); | 
 | 	for(i = 0; i < 0x40; i++){ | 
 | 		if(i && ((i & 0x07) == 0)) | 
 | 			l += snprintf(p+l, READSTR-l, "\n       "); | 
 | 		l += snprintf(p+l, READSTR-l, " %4.4uX", ctlr->eeprom[i]); | 
 | 	} | 
 | 	l += snprintf(p+l, READSTR-l, "\n"); | 
 |  | 
 | 	if(ctlr->mii != NULL && ctlr->mii->curphy != NULL){ | 
 | 		l += snprintf(p+l, READSTR-l, "phy:   "); | 
 | 		for(i = 0; i < NMiiPhyr; i++){ | 
 | 			if(i && ((i & 0x07) == 0)) | 
 | 				l += snprintf(p+l, READSTR-l, "\n       "); | 
 | 			r = miimir(ctlr->mii, i); | 
 | 			l += snprintf(p+l, READSTR-l, " %4.4uX", r); | 
 | 		} | 
 | 		snprintf(p+l, READSTR-l, "\n"); | 
 | 	} | 
 | 	n = readstr(offset, a, n, p); | 
 | 	kfree(p); | 
 | 	qunlock(&ctlr->slock); | 
 |  | 
 | 	return n; | 
 | } | 
 |  | 
 | enum { | 
 | 	CMrdtr, | 
 | }; | 
 |  | 
 | static struct cmdtab igbectlmsg[] = { | 
 | 	{CMrdtr,	"rdtr",	2}, | 
 | }; | 
 |  | 
 | static long | 
 | igbectl(struct ether* edev, void* buf, long n) | 
 | { | 
 | 	ERRSTACK(2); | 
 | 	int v; | 
 | 	char *p; | 
 | 	struct ctlr *ctlr; | 
 | 	struct cmdbuf *cb; | 
 | 	struct cmdtab *ct; | 
 |  | 
 | 	if((ctlr = edev->ctlr) == NULL) | 
 | 		error(ENODEV, ERROR_FIXME); | 
 |  | 
 | 	cb = parsecmd(buf, n); | 
 | 	if(waserror()){ | 
 | 		kfree(cb); | 
 | 		nexterror(); | 
 | 	} | 
 |  | 
 | 	ct = lookupcmd(cb, igbectlmsg, ARRAY_SIZE(igbectlmsg)); | 
 | 	switch(ct->index){ | 
 | 	case CMrdtr: | 
 | 		v = strtol(cb->f[1], &p, 0); | 
 | 		if(v < 0 || p == cb->f[1] || v > 0xFFFF) | 
 | 			error(EINVAL, ERROR_FIXME); | 
 | 		ctlr->rdtr = v; | 
 | 		csr32w(ctlr, Rdtr, Fpd|v); | 
 | 		break; | 
 | 	} | 
 | 	kfree(cb); | 
 | 	poperror(); | 
 |  | 
 | 	return n; | 
 | } | 
 |  | 
 | static void | 
 | igbepromiscuous(void* arg, int on) | 
 | { | 
 | 	int rctl; | 
 | 	struct ctlr *ctlr; | 
 | 	struct ether *edev; | 
 |  | 
 | 	edev = arg; | 
 | 	ctlr = edev->ctlr; | 
 |  | 
 | 	rctl = csr32r(ctlr, Rctl); | 
 | 	rctl &= ~MoMASK; | 
 | 	rctl |= Mo47b36; | 
 | 	if(on) | 
 | 		rctl |= Upe|Mpe; | 
 | 	else | 
 | 		rctl &= ~(Upe|Mpe); | 
 | 	csr32w(ctlr, Rctl, rctl|Mpe);	/* temporarily keep Mpe on */ | 
 | } | 
 |  | 
 | static void | 
 | igbemulticast(void* arg, uint8_t* addr, int add) | 
 | { | 
 | 	int bit, x; | 
 | 	struct ctlr *ctlr; | 
 | 	struct ether *edev; | 
 |  | 
 | 	edev = arg; | 
 | 	ctlr = edev->ctlr; | 
 |  | 
 | 	x = addr[5]>>1; | 
 | 	bit = ((addr[5] & 1)<<4)|(addr[4]>>4); | 
 | 	/* | 
 | 	 * multiple ether addresses can hash to the same filter bit, | 
 | 	 * so it's never safe to clear a filter bit. | 
 | 	 * if we want to clear filter bits, we need to keep track of | 
 | 	 * all the multicast addresses in use, clear all the filter bits, | 
 | 	 * then set the ones corresponding to in-use addresses. | 
 | 	 */ | 
 | 	if(add) | 
 | 		ctlr->mta[x] |= 1<<bit; | 
 | //	else | 
 | //		ctlr->mta[x] &= ~(1<<bit); | 
 |  | 
 | 	csr32w(ctlr, Mta+x*4, ctlr->mta[x]); | 
 | } | 
 |  | 
 | static void | 
 | igbeim(struct ctlr* ctlr, int im) | 
 | { | 
 | 	ilock(&ctlr->imlock); | 
 | 	ctlr->im |= im; | 
 | 	csr32w(ctlr, Ims, ctlr->im); | 
 | 	iunlock(&ctlr->imlock); | 
 | } | 
 |  | 
 | static int | 
 | igbelim(void* ctlr) | 
 | { | 
 | 	return ((struct ctlr*)ctlr)->lim != 0; | 
 | } | 
 |  | 
 | static void | 
 | igbelproc(void* arg) | 
 | { | 
 | 	struct ctlr *ctlr; | 
 | 	struct ether *edev; | 
 | 	struct miiphy *phy; | 
 | 	int ctrl, r; | 
 |  | 
 | 	edev = arg; | 
 | 	ctlr = edev->ctlr; | 
 | 	for(;;){ | 
 | 		/* plan9 originally had a busy loop here (just called continue).  though | 
 | 		 * either you have the mii or you don't.  i don't think it'll magically | 
 | 		 * show up later (it should have been initialized during pnp/pci, which | 
 | 		 * is before attach, which is before lproc).  -brho */ | 
 | 		if (ctlr->mii == NULL || ctlr->mii->curphy == NULL) { | 
 | 			printk("[kernel] igbelproc can't find a mii/curphy, aborting!\n"); | 
 | 			/* name alloc'd in attach */ | 
 | 			kfree(per_cpu_info[core_id()].cur_kthread->name); | 
 | 			return; | 
 | 		} | 
 | 		/* | 
 | 		 * To do: | 
 | 		 *	logic to manage status change, | 
 | 		 *	this is incomplete but should work | 
 | 		 *	one time to set up the hardware. | 
 | 		 * | 
 | 		 *	MiiPhy.speed, etc. should be in Mii. | 
 | 		 */ | 
 | 		if(miistatus(ctlr->mii) < 0) | 
 | 			//continue; 	/* this comment out was plan9, not brho */ | 
 | 			goto enable; | 
 |  | 
 | 		phy = ctlr->mii->curphy; | 
 | 		ctrl = csr32r(ctlr, Ctrl); | 
 |  | 
 | 		switch(ctlr->id){ | 
 | 		case i82543gc: | 
 | 		case i82544ei: | 
 | 		case i82544eif: | 
 | 		default: | 
 | 			if(!(ctrl & Asde)){ | 
 | 				ctrl &= ~(SspeedMASK|Ilos|Fd); | 
 | 				ctrl |= Frcdplx|Frcspd; | 
 | 				if(phy->speed == 1000) | 
 | 					ctrl |= Sspeed1000; | 
 | 				else if(phy->speed == 100) | 
 | 					ctrl |= Sspeed100; | 
 | 				if(phy->fd) | 
 | 					ctrl |= Fd; | 
 | 			} | 
 | 			break; | 
 |  | 
 | 		case i82540em: | 
 | 		case i82540eplp: | 
 | 		case i82547gi: | 
 | 		case i82541gi: | 
 | 		case i82541gi2: | 
 | 		case i82541pi: | 
 | 			break; | 
 | 		} | 
 |  | 
 | 		/* | 
 | 		 * Collision Distance. | 
 | 		 */ | 
 | 		r = csr32r(ctlr, Tctl); | 
 | 		r &= ~ColdMASK; | 
 | 		if(phy->fd) | 
 | 			r |= 64<<ColdSHIFT; | 
 | 		else | 
 | 			r |= 512<<ColdSHIFT; | 
 | 		csr32w(ctlr, Tctl, r); | 
 |  | 
 | 		/* | 
 | 		 * Flow control. | 
 | 		 */ | 
 | 		if(phy->rfc) | 
 | 			ctrl |= Rfce; | 
 | 		if(phy->tfc) | 
 | 			ctrl |= Tfce; | 
 | 		csr32w(ctlr, Ctrl, ctrl); | 
 |  | 
 | enable: | 
 | 		netif_carrier_on(edev); | 
 | 		ctlr->lim = 0; | 
 | 		igbeim(ctlr, Lsc); | 
 |  | 
 | 		ctlr->lsleep++; | 
 | 		rendez_sleep(&ctlr->lrendez, igbelim, ctlr); | 
 | 	} | 
 | } | 
 |  | 
 | static void | 
 | igbetxinit(struct ctlr* ctlr) | 
 | { | 
 | 	int i, r; | 
 | 	struct block *bp; | 
 |  | 
 | 	csr32w(ctlr, Tctl, (0x0F<<CtSHIFT)|Psp|(66<<ColdSHIFT)); | 
 | 	switch(ctlr->id){ | 
 | 	default: | 
 | 		r = 6; | 
 | 		break; | 
 | 	case i82543gc: | 
 | 	case i82544ei: | 
 | 	case i82544eif: | 
 | 	case i82544gc: | 
 | 	case i82540em: | 
 | 	case i82540eplp: | 
 | 	case i82541ei: | 
 | 	case i82541gi: | 
 | 	case i82541gi2: | 
 | 	case i82541pi: | 
 | 	case i82545em: | 
 | 	case i82545gmc: | 
 | 	case i82546gb: | 
 | 	case i82546eb: | 
 | 	case i82547ei: | 
 | 	case i82547gi: | 
 | 		r = 8; | 
 | 		break; | 
 | 	} | 
 | 	csr32w(ctlr, Tipg, (6<<20)|(8<<10)|r); | 
 | 	csr32w(ctlr, Ait, 0); | 
 | 	csr32w(ctlr, Txdmac, 0); | 
 | 	csr32w(ctlr, Tdbal, paddr_low32(ctlr->tdba)); | 
 | 	csr32w(ctlr, Tdbah, paddr_high32(ctlr->tdba)); | 
 | 	csr32w(ctlr, Tdlen, ctlr->ntd*sizeof(Td)); | 
 | 	ctlr->tdh = PREV_RING(0, ctlr->ntd); | 
 | 	csr32w(ctlr, Tdh, 0); | 
 | 	ctlr->tdt = 0; | 
 | 	csr32w(ctlr, Tdt, 0); | 
 |  | 
 | 	for(i = 0; i < ctlr->ntd; i++){ | 
 | 		if((bp = ctlr->tb[i]) != NULL){ | 
 | 			ctlr->tb[i] = NULL; | 
 | 			freeb(bp); | 
 | 		} | 
 | 		memset(&ctlr->tdba[i], 0, sizeof(Td)); | 
 | 	} | 
 | 	ctlr->tdfree = ctlr->ntd; | 
 |  | 
 | 	csr32w(ctlr, Tidv, 128); | 
 | 	r = (4<<WthreshSHIFT)|(4<<HthreshSHIFT)|(8<<PthreshSHIFT); | 
 |  | 
 | 	switch(ctlr->id){ | 
 | 	default: | 
 | 		break; | 
 | 	case i82540em: | 
 | 	case i82540eplp: | 
 | 	case i82547gi: | 
 | 	case i82545em: | 
 | 	case i82545gmc: | 
 | 	case i82546gb: | 
 | 	case i82546eb: | 
 | 	case i82541gi: | 
 | 	case i82541gi2: | 
 | 	case i82541pi: | 
 | 		r = csr32r(ctlr, Txdctl); | 
 | 		r &= ~WthreshMASK; | 
 | 		r |= Gran|(4<<WthreshSHIFT); | 
 |  | 
 | 		csr32w(ctlr, Tadv, 64); | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	csr32w(ctlr, Txdctl, r); | 
 |  | 
 | 	r = csr32r(ctlr, Tctl); | 
 | 	r |= Ten; | 
 | 	csr32w(ctlr, Tctl, r); | 
 | } | 
 |  | 
 | static void | 
 | igbetransmit(struct ether* edev) | 
 | { | 
 | 	Td *td; | 
 | 	struct block *bp; | 
 | 	struct ctlr *ctlr; | 
 | 	int tdh, tdt; | 
 |  | 
 | 	ctlr = edev->ctlr; | 
 |  | 
 | 	ilock(&ctlr->tlock); | 
 |  | 
 | 	/* | 
 | 	 * Free any completed packets | 
 | 	 */ | 
 | 	tdh = ctlr->tdh; | 
 | 	while(NEXT_RING(tdh, ctlr->ntd) != csr32r(ctlr, Tdh)){ | 
 | 		if((bp = ctlr->tb[tdh]) != NULL){ | 
 | 			ctlr->tb[tdh] = NULL; | 
 | 			freeb(bp); | 
 | 		} | 
 | 		memset(&ctlr->tdba[tdh], 0, sizeof(Td)); | 
 | 		tdh = NEXT_RING(tdh, ctlr->ntd); | 
 | 	} | 
 | 	ctlr->tdh = tdh; | 
 |  | 
 | 	/* | 
 | 	 * Try to fill the ring back up. | 
 | 	 */ | 
 | 	tdt = ctlr->tdt; | 
 | 	while(NEXT_RING(tdt, ctlr->ntd) != tdh){ | 
 | 		if((bp = qget(edev->oq)) == NULL) | 
 | 			break; | 
 | 		td = &ctlr->tdba[tdt]; | 
 | 		td->addr[0] = paddr_low32(bp->rp); | 
 | 		td->addr[1] = paddr_high32(bp->rp); | 
 | 		td->control = ((BLEN(bp) & LenMASK)<<LenSHIFT); | 
 | 		td->control |= Dext|Ifcs|Teop|DtypeDD; | 
 | 		ctlr->tb[tdt] = bp; | 
 | 		tdt = NEXT_RING(tdt, ctlr->ntd); | 
 | 		if(NEXT_RING(tdt, ctlr->ntd) == tdh){ | 
 | 			td->control |= Rs; | 
 | 			ctlr->txdw++; | 
 | 			ctlr->tdt = tdt; | 
 | 			csr32w(ctlr, Tdt, tdt); | 
 | 			igbeim(ctlr, Txdw); | 
 | 			break; | 
 | 		} | 
 | 		ctlr->tdt = tdt; | 
 | 		csr32w(ctlr, Tdt, tdt); | 
 | 	} | 
 |  | 
 | 	iunlock(&ctlr->tlock); | 
 | } | 
 |  | 
 | static void | 
 | igbereplenish(struct ctlr* ctlr) | 
 | { | 
 | 	Rd *rd; | 
 | 	int rdt; | 
 | 	struct block *bp; | 
 |  | 
 | 	rdt = ctlr->rdt; | 
 | 	while(NEXT_RING(rdt, ctlr->nrd) != ctlr->rdh){ | 
 | 		rd = &ctlr->rdba[rdt]; | 
 | 		if(ctlr->rb[rdt] == NULL){ | 
 | 			bp = block_alloc(Rbsz, MEM_ATOMIC); | 
 | 			if(bp == NULL){ | 
 | 				/* needs to be a safe print for interrupt level */ | 
 | 				printk("#l%d: igbereplenish: no available buffers\n", | 
 | 					ctlr->edev->ctlrno); | 
 | 				break; | 
 | 			} | 
 | 			ctlr->rb[rdt] = bp; | 
 | 			rd->addr[0] = paddr_low32(bp->rp); | 
 | 			rd->addr[1] = paddr_high32(bp->rp); | 
 | 		} | 
 | 		wmb();	/* ensure prev rd writes come before status = 0. */ | 
 | 		rd->status = 0; | 
 | 		rdt = NEXT_RING(rdt, ctlr->nrd); | 
 | 		ctlr->rdfree++; | 
 | 	} | 
 | 	ctlr->rdt = rdt; | 
 | 	csr32w(ctlr, Rdt, rdt); | 
 | } | 
 |  | 
 | static void | 
 | igberxinit(struct ctlr* ctlr) | 
 | { | 
 | 	int i; | 
 | 	struct block *bp; | 
 |  | 
 | 	/* temporarily keep Mpe on */ | 
 | 	csr32w(ctlr, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF|Mpe); | 
 | 	csr32w(ctlr, Rdbal, paddr_low32(ctlr->rdba)); | 
 | 	csr32w(ctlr, Rdbah, paddr_high32(ctlr->rdba)); | 
 | 	csr32w(ctlr, Rdlen, ctlr->nrd*sizeof(Rd)); | 
 | 	ctlr->rdh = 0; | 
 | 	csr32w(ctlr, Rdh, 0); | 
 | 	ctlr->rdt = 0; | 
 | 	csr32w(ctlr, Rdt, 0); | 
 | 	ctlr->rdtr = 0; | 
 | 	csr32w(ctlr, Rdtr, Fpd|0); | 
 |  | 
 | 	for(i = 0; i < ctlr->nrd; i++){ | 
 | 		if((bp = ctlr->rb[i]) != NULL){ | 
 | 			ctlr->rb[i] = NULL; | 
 | 			freeb(bp); | 
 | 		} | 
 | 	} | 
 | 	igbereplenish(ctlr); | 
 |  | 
 | 	switch(ctlr->id){ | 
 | 	case i82540em: | 
 | 	case i82540eplp: | 
 | 	case i82541gi: | 
 | 	case i82541gi2: | 
 | 	case i82541pi: | 
 | 	case i82545em: | 
 | 	case i82545gmc: | 
 | 	case i82546gb: | 
 | 	case i82546eb: | 
 | 	case i82547gi: | 
 | 		csr32w(ctlr, Radv, 64); | 
 | 		break; | 
 | 	} | 
 | 	csr32w(ctlr, Rxdctl, (8<<WthreshSHIFT)|(8<<HthreshSHIFT)|4); | 
 |  | 
 | 	/* | 
 | 	 * Enable checksum offload. | 
 | 	 */ | 
 | 	csr32w(ctlr, Rxcsum, Tuofl|Ipofl|(ETHERHDRSIZE<<PcssSHIFT)); | 
 | } | 
 |  | 
 | static int | 
 | igberim(void* ctlr) | 
 | { | 
 | 	return ((struct ctlr*)ctlr)->rim != 0; | 
 | } | 
 |  | 
 | static void | 
 | igberproc(void* arg) | 
 | { | 
 | 	Rd *rd; | 
 | 	struct block *bp; | 
 | 	struct ctlr *ctlr; | 
 | 	int r, rdh; | 
 | 	struct ether *edev; | 
 |  | 
 | 	edev = arg; | 
 | 	ctlr = edev->ctlr; | 
 |  | 
 | 	igberxinit(ctlr); | 
 | 	r = csr32r(ctlr, Rctl); | 
 | 	r |= Ren; | 
 | 	csr32w(ctlr, Rctl, r); | 
 |  | 
 | 	for(;;){ | 
 | 		ctlr->rim = 0; | 
 | 		igbeim(ctlr, Rxt0|Rxo|Rxdmt0|Rxseq); | 
 | 		ctlr->rsleep++; | 
 | 		rendez_sleep(&ctlr->rrendez, igberim, ctlr); | 
 |  | 
 | 		rdh = ctlr->rdh; | 
 | 		for(;;){ | 
 | 			rd = &ctlr->rdba[rdh]; | 
 |  | 
 | 			if(!(rd->status & Rdd)) | 
 | 				break; | 
 |  | 
 | 			/* | 
 | 			 * Accept eop packets with no errors. | 
 | 			 * With no errors and the Ixsm bit set, | 
 | 			 * the descriptor status Tpcs and Ipcs bits give | 
 | 			 * an indication of whether the checksums were | 
 | 			 * calculated and valid. | 
 | 			 */ | 
 | 			if((rd->status & Reop) && rd->errors == 0){ | 
 | 				bp = ctlr->rb[rdh]; | 
 | 				ctlr->rb[rdh] = NULL; | 
 | 				bp->wp += rd->length; | 
 | 				bp->next = NULL; | 
 | 				if(!(rd->status & Ixsm)){ | 
 | 					ctlr->ixsm++; | 
 | 					if(rd->status & Ipcs){ | 
 | 						/* | 
 | 						 * IP checksum calculated | 
 | 						 * (and valid as errors == 0). | 
 | 						 */ | 
 | 						ctlr->ipcs++; | 
 | 						bp->flag |= Bipck; | 
 | 					} | 
 | 					if(rd->status & Tcpcs){ | 
 | 						/* | 
 | 						 * TCP/UDP checksum calculated | 
 | 						 * (and valid as errors == 0). | 
 | 						 */ | 
 | 						ctlr->tcpcs++; | 
 | 						bp->flag |= Btcpck|Budpck; | 
 | 					} | 
 | 					bp->flag |= Bpktck; | 
 | 				} | 
 | 				etheriq(edev, bp, 1); | 
 | 			} | 
 | 			else if(ctlr->rb[rdh] != NULL){ | 
 | 				freeb(ctlr->rb[rdh]); | 
 | 				ctlr->rb[rdh] = NULL; | 
 | 			} | 
 |  | 
 | 			memset(rd, 0, sizeof(Rd)); | 
 | 			wmb();	/* make sure the zeroing happens before free (i think) */ | 
 | 			ctlr->rdfree--; | 
 | 			rdh = NEXT_RING(rdh, ctlr->nrd); | 
 | 		} | 
 | 		ctlr->rdh = rdh; | 
 |  | 
 | 		if(ctlr->rdfree < ctlr->nrd/2 || (ctlr->rim & Rxdmt0)) | 
 | 			igbereplenish(ctlr); | 
 | 	} | 
 | } | 
 |  | 
 | static void | 
 | igbeattach(struct ether* edev) | 
 | { | 
 | 	ERRSTACK(1); | 
 | 	struct block *bp; | 
 | 	struct ctlr *ctlr; | 
 | 	char *name; | 
 |  | 
 | 	ctlr = edev->ctlr; | 
 | 	ctlr->edev = edev;			/* point back to Ether* */ | 
 | 	qlock(&ctlr->alock); | 
 | 	if(ctlr->alloc != NULL){			/* already allocated? */ | 
 | 		qunlock(&ctlr->alock); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	ctlr->tb = NULL; | 
 | 	ctlr->rb = NULL; | 
 | 	ctlr->alloc = NULL; | 
 | 	if(waserror()){ | 
 | 		kfree(ctlr->tb); | 
 | 		ctlr->tb = NULL; | 
 | 		kfree(ctlr->rb); | 
 | 		ctlr->rb = NULL; | 
 | 		kfree(ctlr->alloc); | 
 | 		ctlr->alloc = NULL; | 
 | 		qunlock(&ctlr->alock); | 
 | 		nexterror(); | 
 | 	} | 
 |  | 
 | 	ctlr->nrd = Nrd; | 
 | 	ctlr->ntd = Ntd; | 
 | 	ctlr->alloc = kzmalloc(ctlr->nrd * sizeof(Rd) + ctlr->ntd * sizeof(Td) + 127, 0); | 
 | 	if(ctlr->alloc == NULL) { | 
 | 		printd("igbe: can't allocate ctlr->alloc\n"); | 
 | 		error(ENOMEM, ERROR_FIXME); | 
 | 	} | 
 | 	ctlr->rdba = (Rd*)ROUNDUP((uintptr_t)ctlr->alloc, 128); | 
 | 	ctlr->tdba = (Td*)(ctlr->rdba+ctlr->nrd); | 
 |  | 
 | 	ctlr->rb = kzmalloc(ctlr->nrd * sizeof(struct block *), 0); | 
 | 	ctlr->tb = kzmalloc(ctlr->ntd * sizeof(struct block *), 0); | 
 | 	if (ctlr->rb == NULL || ctlr->tb == NULL) { | 
 | 		printd("igbe: can't allocate ctlr->rb or ctlr->tb\n"); | 
 | 		error(ENOMEM, ERROR_FIXME); | 
 | 	} | 
 |  | 
 | 	/* the ktasks should free these names, if they ever exit */ | 
 | 	name = kmalloc(KNAMELEN, MEM_WAIT); | 
 | 	snprintf(name, KNAMELEN, "#l%dlproc", edev->ctlrno); | 
 | 	ktask(name, igbelproc, edev); | 
 |  | 
 | 	name = kmalloc(KNAMELEN, MEM_WAIT); | 
 | 	snprintf(name, KNAMELEN, "#l%drproc", edev->ctlrno); | 
 | 	ktask(name, igberproc, edev); | 
 |  | 
 | 	igbetxinit(ctlr); | 
 |  | 
 | 	qunlock(&ctlr->alock); | 
 | 	poperror(); | 
 | } | 
 |  | 
 | static void igbeinterrupt(struct hw_trapframe *hw_tf, void *arg) | 
 | { | 
 | 	struct ctlr *ctlr; | 
 | 	struct ether *edev; | 
 | 	int icr, im, txdw; | 
 |  | 
 | 	edev = arg; | 
 | 	ctlr = edev->ctlr; | 
 |  | 
 | 	ilock(&ctlr->imlock); | 
 | 	csr32w(ctlr, Imc, ~0); | 
 | 	im = ctlr->im; | 
 | 	txdw = 0; | 
 |  | 
 | 	while((icr = csr32r(ctlr, Icr) & ctlr->im) != 0){ | 
 | 		if(icr & Lsc){ | 
 | 			im &= ~Lsc; | 
 | 			ctlr->lim = icr & Lsc; | 
 | 			rendez_wakeup(&ctlr->lrendez); | 
 | 			ctlr->lintr++; | 
 | 		} | 
 | 		if(icr & (Rxt0|Rxo|Rxdmt0|Rxseq)){ | 
 | 			im &= ~(Rxt0|Rxo|Rxdmt0|Rxseq); | 
 | 			ctlr->rim = icr & (Rxt0|Rxo|Rxdmt0|Rxseq); | 
 | 			rendez_wakeup(&ctlr->rrendez); | 
 | 			ctlr->rintr++; | 
 | 		} | 
 | 		if(icr & Txdw){ | 
 | 			im &= ~Txdw; | 
 | 			txdw++; | 
 | 			ctlr->tintr++; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	ctlr->im = im; | 
 | 	csr32w(ctlr, Ims, im); | 
 | 	iunlock(&ctlr->imlock); | 
 |  | 
 | 	if(txdw) | 
 | 		igbetransmit(edev); | 
 | } | 
 |  | 
 | static int | 
 | i82543mdior(struct ctlr* ctlr, int n) | 
 | { | 
 | 	int ctrl, data, i, r; | 
 |  | 
 | 	/* | 
 | 	 * Read n bits from the Management Data I/O Interface. | 
 | 	 */ | 
 | 	ctrl = csr32r(ctlr, Ctrl); | 
 | 	r = (ctrl & ~Mddo)|Mdco; | 
 | 	data = 0; | 
 | 	for(i = n-1; i >= 0; i--){ | 
 | 		if(csr32r(ctlr, Ctrl) & Mdd) | 
 | 			data |= (1<<i); | 
 | 		csr32w(ctlr, Ctrl, Mdc|r); | 
 | 		csr32w(ctlr, Ctrl, r); | 
 | 	} | 
 | 	csr32w(ctlr, Ctrl, ctrl); | 
 |  | 
 | 	return data; | 
 | } | 
 |  | 
 | static int | 
 | i82543mdiow(struct ctlr* ctlr, int bits, int n) | 
 | { | 
 | 	int ctrl, i, r; | 
 |  | 
 | 	/* | 
 | 	 * Write n bits to the Management Data I/O Interface. | 
 | 	 */ | 
 | 	ctrl = csr32r(ctlr, Ctrl); | 
 | 	r = Mdco|Mddo|ctrl; | 
 | 	for(i = n-1; i >= 0; i--){ | 
 | 		if(bits & (1<<i)) | 
 | 			r |= Mdd; | 
 | 		else | 
 | 			r &= ~Mdd; | 
 | 		csr32w(ctlr, Ctrl, Mdc|r); | 
 | 		csr32w(ctlr, Ctrl, r); | 
 | 	} | 
 | 	csr32w(ctlr, Ctrl, ctrl); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int | 
 | i82543miimir(struct mii* mii, int pa, int ra) | 
 | { | 
 | 	int data; | 
 | 	struct ctlr *ctlr; | 
 |  | 
 | 	ctlr = mii->ctlr; | 
 |  | 
 | 	/* | 
 | 	 * MII Management Interface Read. | 
 | 	 * | 
 | 	 * Preamble; | 
 | 	 * ST+OP+PHYAD+REGAD; | 
 | 	 * TA + 16 data bits. | 
 | 	 */ | 
 | 	i82543mdiow(ctlr, 0xFFFFFFFF, 32); | 
 | 	i82543mdiow(ctlr, 0x1800|(pa<<5)|ra, 14); | 
 | 	data = i82543mdior(ctlr, 18); | 
 |  | 
 | 	if(data & 0x10000) | 
 | 		return -1; | 
 |  | 
 | 	return data & 0xFFFF; | 
 | } | 
 |  | 
 | static int | 
 | i82543miimiw(struct mii* mii, int pa, int ra, int data) | 
 | { | 
 | 	struct ctlr *ctlr; | 
 |  | 
 | 	ctlr = mii->ctlr; | 
 |  | 
 | 	/* | 
 | 	 * MII Management Interface Write. | 
 | 	 * | 
 | 	 * Preamble; | 
 | 	 * ST+OP+PHYAD+REGAD+TA + 16 data bits; | 
 | 	 * Z. | 
 | 	 */ | 
 | 	i82543mdiow(ctlr, 0xFFFFFFFF, 32); | 
 | 	data &= 0xFFFF; | 
 | 	data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16); | 
 | 	i82543mdiow(ctlr, data, 32); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int | 
 | igbemiimir(struct mii* mii, int pa, int ra) | 
 | { | 
 | 	struct ctlr *ctlr; | 
 | 	int mdic, timo; | 
 |  | 
 | 	ctlr = mii->ctlr; | 
 |  | 
 | 	csr32w(ctlr, Mdic, MDIrop|(pa<<MDIpSHIFT)|(ra<<MDIrSHIFT)); | 
 | 	mdic = 0; | 
 | 	for(timo = 64; timo; timo--){ | 
 | 		mdic = csr32r(ctlr, Mdic); | 
 | 		if(mdic & (MDIe|MDIready)) | 
 | 			break; | 
 | 		udelay(1); | 
 | 	} | 
 |  | 
 | 	if((mdic & (MDIe|MDIready)) == MDIready) | 
 | 		return mdic & 0xFFFF; | 
 | 	return -1; | 
 | } | 
 |  | 
 | static int | 
 | igbemiimiw(struct mii* mii, int pa, int ra, int data) | 
 | { | 
 | 	struct ctlr *ctlr; | 
 | 	int mdic, timo; | 
 |  | 
 | 	ctlr = mii->ctlr; | 
 |  | 
 | 	data &= MDIdMASK; | 
 | 	csr32w(ctlr, Mdic, MDIwop|(pa<<MDIpSHIFT)|(ra<<MDIrSHIFT)|data); | 
 | 	mdic = 0; | 
 | 	for(timo = 64; timo; timo--){ | 
 | 		mdic = csr32r(ctlr, Mdic); | 
 | 		if(mdic & (MDIe|MDIready)) | 
 | 			break; | 
 | 		udelay(1); | 
 | 	} | 
 | 	if((mdic & (MDIe|MDIready)) == MDIready) | 
 | 		return 0; | 
 | 	return -1; | 
 | } | 
 |  | 
 | static int | 
 | i82543miirw(struct mii* mii, int write, int pa, int ra, int data) | 
 | { | 
 | 	if(write) | 
 | 		return i82543miimiw(mii, pa, ra, data); | 
 |  | 
 | 	return i82543miimir(mii, pa, ra); | 
 | } | 
 |  | 
 | static int | 
 | igbemiirw(struct mii* mii, int write, int pa, int ra, int data) | 
 | { | 
 | 	if(write) | 
 | 		return igbemiimiw(mii, pa, ra, data); | 
 |  | 
 | 	return igbemiimir(mii, pa, ra); | 
 | } | 
 |  | 
 | static int | 
 | igbemii(struct ctlr* ctlr) | 
 | { | 
 | 	int ctrl, p, r; | 
 | 	int (*rw)(struct mii*, int unused_int, int, int, int); | 
 |  | 
 | 	r = csr32r(ctlr, Status); | 
 | 	if(r & Tbimode) | 
 | 		return -1; | 
 |  | 
 | 	ctrl = csr32r(ctlr, Ctrl); | 
 | 	ctrl |= Slu; | 
 |  | 
 | 	switch(ctlr->id){ | 
 | 	case i82543gc: | 
 | 		ctrl |= Frcdplx|Frcspd; | 
 | 		csr32w(ctlr, Ctrl, ctrl); | 
 |  | 
 | 		/* | 
 | 		 * The reset pin direction (Mdro) should already | 
 | 		 * be set from the EEPROM load. | 
 | 		 * If it's not set this configuration is unexpected | 
 | 		 * so bail. | 
 | 		 */ | 
 | 		r = csr32r(ctlr, Ctrlext); | 
 | 		if(!(r & Mdro)) | 
 | 			return -1; | 
 | 		csr32w(ctlr, Ctrlext, r); | 
 | 		udelay(20*1000); | 
 | 		r = csr32r(ctlr, Ctrlext); | 
 | 		r &= ~Mdr; | 
 | 		csr32w(ctlr, Ctrlext, r); | 
 | 		udelay(20*1000); | 
 | 		r = csr32r(ctlr, Ctrlext); | 
 | 		r |= Mdr; | 
 | 		csr32w(ctlr, Ctrlext, r); | 
 | 		udelay(20*1000); | 
 |  | 
 | 		rw = i82543miirw; | 
 | 		break; | 
 | 	case i82544ei: | 
 | 	case i82544eif: | 
 | 	case i82544gc: | 
 | 	case i82540em: | 
 | 	case i82540eplp: | 
 | 	case i82547ei: | 
 | 	case i82547gi: | 
 | 	case i82541ei: | 
 | 	case i82541gi: | 
 | 	case i82541gi2: | 
 | 	case i82541pi: | 
 | 	case i82545em: | 
 | 	case i82545gmc: | 
 | 	case i82546gb: | 
 | 	case i82546eb: | 
 | 		ctrl &= ~(Frcdplx|Frcspd); | 
 | 		csr32w(ctlr, Ctrl, ctrl); | 
 | 		rw = igbemiirw; | 
 | 		break; | 
 | 	default: | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	if (!(ctlr->mii = miiattach(ctlr, ~0, rw))) | 
 | 		return -1; | 
 | 	// print("oui %X phyno %d\n", phy->oui, phy->phyno); | 
 |  | 
 | 	/* | 
 | 	 * 8254X-specific PHY registers not in 802.3: | 
 | 	 *	0x10	PHY specific control | 
 | 	 *	0x14	extended PHY specific control | 
 | 	 * Set appropriate values then reset the PHY to have | 
 | 	 * changes noted. | 
 | 	 */ | 
 | 	switch(ctlr->id){ | 
 | 	case i82547gi: | 
 | 	case i82541gi: | 
 | 	case i82541gi2: | 
 | 	case i82541pi: | 
 | 	case i82545em: | 
 | 	case i82545gmc: | 
 | 	case i82546gb: | 
 | 	case i82546eb: | 
 | 		break; | 
 | 	default: | 
 | 		r = miimir(ctlr->mii, 16); | 
 | 		r |= 0x0800;			/* assert CRS on Tx */ | 
 | 		r |= 0x0060;			/* auto-crossover all speeds */ | 
 | 		r |= 0x0002;			/* polarity reversal enabled */ | 
 | 		miimiw(ctlr->mii, 16, r); | 
 |  | 
 | 		r = miimir(ctlr->mii, 20); | 
 | 		r |= 0x0070;			/* +25MHz clock */ | 
 | 		r &= ~0x0F00; | 
 | 		r |= 0x0100;			/* 1x downshift */ | 
 | 		miimiw(ctlr->mii, 20, r); | 
 |  | 
 | 		miireset(ctlr->mii); | 
 | 		p = 0; | 
 | 		if(ctlr->txcw & TxcwPs) | 
 | 			p |= AnaP; | 
 | 		if(ctlr->txcw & TxcwAs) | 
 | 			p |= AnaAP; | 
 | 		miiane(ctlr->mii, ~0, p, ~0); | 
 | 		break; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int | 
 | at93c46io(struct ctlr* ctlr, char* op, int data) | 
 | { | 
 | 	char *lp, *p; | 
 | 	int i, loop, eecd, r; | 
 |  | 
 | 	eecd = csr32r(ctlr, Eecd); | 
 |  | 
 | 	r = 0; | 
 | 	loop = -1; | 
 | 	lp = NULL; | 
 | 	for(p = op; *p != '\0'; p++){ | 
 | 		switch(*p){ | 
 | 		default: | 
 | 			return -1; | 
 | 		case ' ': | 
 | 			continue; | 
 | 		case ':':			/* start of loop */ | 
 | 			loop = strtol(p+1, &lp, 0)-1; | 
 | 			lp--; | 
 | 			if(p == lp) | 
 | 				loop = 7; | 
 | 			p = lp; | 
 | 			continue; | 
 | 		case ';':			/* end of loop */ | 
 | 			if(lp == NULL) | 
 | 				return -1; | 
 | 			loop--; | 
 | 			if(loop >= 0) | 
 | 				p = lp; | 
 | 			else | 
 | 				lp = NULL; | 
 | 			continue; | 
 | 		case 'C':			/* assert clock */ | 
 | 			eecd |= Sk; | 
 | 			break; | 
 | 		case 'c':			/* deassert clock */ | 
 | 			eecd &= ~Sk; | 
 | 			break; | 
 | 		case 'D':			/* next bit in 'data' byte */ | 
 | 			if(loop < 0) | 
 | 				return -1; | 
 | 			if(data & (1<<loop)) | 
 | 				eecd |= Di; | 
 | 			else | 
 | 				eecd &= ~Di; | 
 | 			break; | 
 | 		case 'O':			/* collect data output */ | 
 | 			i = (csr32r(ctlr, Eecd) & Do) != 0; | 
 | 			if(loop >= 0) | 
 | 				r |= (i<<loop); | 
 | 			else | 
 | 				r = i; | 
 | 			continue; | 
 | 		case 'I':			/* assert data input */ | 
 | 			eecd |= Di; | 
 | 			break; | 
 | 		case 'i':			/* deassert data input */ | 
 | 			eecd &= ~Di; | 
 | 			break; | 
 | 		case 'S':			/* enable chip select */ | 
 | 			eecd |= Cs; | 
 | 			break; | 
 | 		case 's':			/* disable chip select */ | 
 | 			eecd &= ~Cs; | 
 | 			break; | 
 | 		} | 
 | 		csr32w(ctlr, Eecd, eecd); | 
 | 		udelay(50); | 
 | 	} | 
 | 	if(loop >= 0) | 
 | 		return -1; | 
 | 	return r; | 
 | } | 
 |  | 
 | static int | 
 | at93c46r(struct ctlr* ctlr) | 
 | { | 
 | 	uint16_t sum; | 
 | 	char rop[20]; | 
 | 	int addr, areq, bits, data, eecd, i; | 
 |  | 
 | 	eecd = csr32r(ctlr, Eecd); | 
 | 	if(eecd & Spi){ | 
 | 		printd("igbe: SPI EEPROM access not implemented\n"); | 
 | 		return 0; | 
 | 	} | 
 | 	if(eecd & (Eeszaddr|Eesz256)) | 
 | 		bits = 8; | 
 | 	else | 
 | 		bits = 6; | 
 |  | 
 | 	sum = 0; | 
 |  | 
 | 	switch(ctlr->id){ | 
 | 	default: | 
 | 		areq = 0; | 
 | 		break; | 
 | 	case i82540em: | 
 | 	case i82540eplp: | 
 | 	case i82541ei: | 
 | 	case i82541gi: | 
 | 	case i82541gi2: | 
 | 	case i82541pi: | 
 | 	case i82545em: | 
 | 	case i82545gmc: | 
 | 	case i82546gb: | 
 | 	case i82546eb: | 
 | 	case i82547ei: | 
 | 	case i82547gi: | 
 | 		areq = 1; | 
 | 		csr32w(ctlr, Eecd, eecd|Areq); | 
 | 		for(i = 0; i < 1000; i++){ | 
 | 			if((eecd = csr32r(ctlr, Eecd)) & Agnt) | 
 | 				break; | 
 | 			udelay(5); | 
 | 		} | 
 | 		if(!(eecd & Agnt)){ | 
 | 			printd("igbe: not granted EEPROM access\n"); | 
 | 			goto release; | 
 | 		} | 
 | 		break; | 
 | 	} | 
 | 	snprintf(rop, sizeof(rop), "S :%dDCc;", bits+3); | 
 |  | 
 | 	for(addr = 0; addr < 0x40; addr++){ | 
 | 		/* | 
 | 		 * Read a word at address 'addr' from the Atmel AT93C46 | 
 | 		 * 3-Wire Serial EEPROM or compatible. The EEPROM access is | 
 | 		 * controlled by 4 bits in Eecd. See the AT93C46 datasheet | 
 | 		 * for protocol details. | 
 | 		 */ | 
 | 		if(at93c46io(ctlr, rop, (0x06<<bits)|addr) != 0){ | 
 | 			printd("igbe: can't set EEPROM address 0x%2.2X\n", addr); | 
 | 			goto release; | 
 | 		} | 
 | 		data = at93c46io(ctlr, ":16COc;", 0); | 
 | 		at93c46io(ctlr, "sic", 0); | 
 | 		ctlr->eeprom[addr] = data; | 
 | 		sum += data; | 
 | 	} | 
 |  | 
 | release: | 
 | 	if(areq) | 
 | 		csr32w(ctlr, Eecd, eecd & ~Areq); | 
 | 	return sum; | 
 | } | 
 |  | 
 | static int | 
 | igbedetach(struct ctlr* ctlr) | 
 | { | 
 | 	int r, timeo; | 
 |  | 
 | 	/* | 
 | 	 * Perform a device reset to get the chip back to the | 
 | 	 * power-on state, followed by an EEPROM reset to read | 
 | 	 * the defaults for some internal registers. | 
 | 	 */ | 
 | 	csr32w(ctlr, Imc, ~0); | 
 | 	csr32w(ctlr, Rctl, 0); | 
 | 	csr32w(ctlr, Tctl, 0); | 
 |  | 
 | 	udelay(10*1000); | 
 |  | 
 | 	csr32w(ctlr, Ctrl, Devrst); | 
 | 	udelay(1*1000); | 
 | 	for(timeo = 0; timeo < 1000; timeo++){ | 
 | 		if(!(csr32r(ctlr, Ctrl) & Devrst)) | 
 | 			break; | 
 | 		udelay(1*1000); | 
 | 	} | 
 | 	if(csr32r(ctlr, Ctrl) & Devrst) | 
 | 		return -1; | 
 | 	r = csr32r(ctlr, Ctrlext); | 
 | 	csr32w(ctlr, Ctrlext, r|Eerst); | 
 | 	udelay(1*1000); | 
 |  | 
 | 	for(timeo = 0; timeo < 1000; timeo++){ | 
 | 		if(!(csr32r(ctlr, Ctrlext) & Eerst)) | 
 | 			break; | 
 | 		udelay(1*1000); | 
 | 	} | 
 | 	if(csr32r(ctlr, Ctrlext) & Eerst) | 
 | 		return -1; | 
 |  | 
 | 	switch(ctlr->id){ | 
 | 	default: | 
 | 		break; | 
 | 	case i82540em: | 
 | 	case i82540eplp: | 
 | 	case i82541gi: | 
 | 	case i82541gi2: | 
 | 	case i82541pi: | 
 | 	case i82545em: | 
 | 	case i82545gmc: | 
 | 	case i82547gi: | 
 | 	case i82546gb: | 
 | 	case i82546eb: | 
 | 		r = csr32r(ctlr, Manc); | 
 | 		r &= ~Arpen; | 
 | 		csr32w(ctlr, Manc, r); | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	csr32w(ctlr, Imc, ~0); | 
 | 	udelay(1*1000); | 
 | 	for(timeo = 0; timeo < 1000; timeo++){ | 
 | 		if(!csr32r(ctlr, Icr)) | 
 | 			break; | 
 | 		udelay(1*1000); | 
 | 	} | 
 | 	if(csr32r(ctlr, Icr)) | 
 | 		return -1; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void | 
 | igbeshutdown(struct ether* ether) | 
 | { | 
 | 	igbedetach(ether->ctlr); | 
 | } | 
 |  | 
 | static int | 
 | igbereset(struct ctlr* ctlr) | 
 | { | 
 | 	int ctrl, i, pause, r, swdpio, txcw; | 
 |  | 
 | 	if(igbedetach(ctlr)) | 
 | 		return -1; | 
 |  | 
 | 	/* | 
 | 	 * Read the EEPROM, validate the checksum | 
 | 	 * then get the device back to a power-on state. | 
 | 	 */ | 
 | 	if((r = at93c46r(ctlr)) != 0xBABA){ | 
 | 		printd("igbe: bad EEPROM checksum - 0x%4.4uX\n", r); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * Snarf and set up the receive addresses. | 
 | 	 * There are 16 addresses. The first should be the MAC address. | 
 | 	 * The others are cleared and not marked valid (MS bit of Rah). | 
 | 	 */ | 
 | 	if ((ctlr->id == i82546gb || ctlr->id == i82546eb) && | 
 | 	    (pci_config_addr(ctlr->pci->bus, ctlr->pci->dev, 0, 0) == | 
 | 		 pci_config_addr(0, 1, 0, 0))) | 
 | 		ctlr->eeprom[Ea+2] += 0x100;		/* second interface */ | 
 | 	if(ctlr->id == i82541gi && ctlr->eeprom[Ea] == 0xFFFF) | 
 | 		ctlr->eeprom[Ea] = 0xD000; | 
 | 	for(i = Ea; i < Eaddrlen/2; i++){ | 
 | 		ctlr->ra[2*i] = ctlr->eeprom[i]; | 
 | 		ctlr->ra[2*i+1] = ctlr->eeprom[i]>>8; | 
 | 	} | 
 | 	/* lan id seems to vary on 82543gc; don't use it */ | 
 | 	if (ctlr->id != i82543gc) { | 
 | 		r = (csr32r(ctlr, Status) & Lanid) >> 2; | 
 | 		ctlr->ra[5] += r;		/* ea ctlr[1] = ea ctlr[0]+1 */ | 
 | 	} | 
 |  | 
 | 	r = (ctlr->ra[3]<<24)|(ctlr->ra[2]<<16)|(ctlr->ra[1]<<8)|ctlr->ra[0]; | 
 | 	csr32w(ctlr, Ral, r); | 
 | 	r = 0x80000000|(ctlr->ra[5]<<8)|ctlr->ra[4]; | 
 | 	csr32w(ctlr, Rah, r); | 
 | 	for(i = 1; i < 16; i++){ | 
 | 		csr32w(ctlr, Ral+i*8, 0); | 
 | 		csr32w(ctlr, Rah+i*8, 0); | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * Clear the Multicast Table Array. | 
 | 	 * It's a 4096 bit vector accessed as 128 32-bit registers. | 
 | 	 */ | 
 | 	memset(ctlr->mta, 0, sizeof(ctlr->mta)); | 
 | 	for(i = 0; i < 128; i++) | 
 | 		csr32w(ctlr, Mta+i*4, 0); | 
 |  | 
 | 	/* | 
 | 	 * Just in case the Eerst didn't load the defaults | 
 | 	 * (doesn't appear to fully on the 82543GC), do it manually. | 
 | 	 */ | 
 | 	if (ctlr->id == i82543gc) { | 
 | 		txcw = csr32r(ctlr, Txcw); | 
 | 		txcw &= ~(TxcwAne|TxcwPauseMASK|TxcwFd); | 
 | 		ctrl = csr32r(ctlr, Ctrl); | 
 | 		ctrl &= ~(SwdpioloMASK|Frcspd|Ilos|Lrst|Fd); | 
 |  | 
 | 		if(ctlr->eeprom[Icw1] & 0x0400){ | 
 | 			ctrl |= Fd; | 
 | 			txcw |= TxcwFd; | 
 | 		} | 
 | 		if(ctlr->eeprom[Icw1] & 0x0200) | 
 | 			ctrl |= Lrst; | 
 | 		if(ctlr->eeprom[Icw1] & 0x0010) | 
 | 			ctrl |= Ilos; | 
 | 		if(ctlr->eeprom[Icw1] & 0x0800) | 
 | 			ctrl |= Frcspd; | 
 | 		swdpio = (ctlr->eeprom[Icw1] & 0x01E0)>>5; | 
 | 		ctrl |= swdpio<<SwdpioloSHIFT; | 
 | 		csr32w(ctlr, Ctrl, ctrl); | 
 |  | 
 | 		ctrl = csr32r(ctlr, Ctrlext); | 
 | 		ctrl &= ~(Ips|SwdpiohiMASK); | 
 | 		swdpio = (ctlr->eeprom[Icw2] & 0x00F0)>>4; | 
 | 		if(ctlr->eeprom[Icw1] & 0x1000) | 
 | 			ctrl |= Ips; | 
 | 		ctrl |= swdpio<<SwdpiohiSHIFT; | 
 | 		csr32w(ctlr, Ctrlext, ctrl); | 
 |  | 
 | 		if(ctlr->eeprom[Icw2] & 0x0800) | 
 | 			txcw |= TxcwAne; | 
 | 		pause = (ctlr->eeprom[Icw2] & 0x3000)>>12; | 
 | 		txcw |= pause<<TxcwPauseSHIFT; | 
 | 		switch(pause){ | 
 | 		default: | 
 | 			ctlr->fcrtl = 0x00002000; | 
 | 			ctlr->fcrth = 0x00004000; | 
 | 			txcw |= TxcwAs|TxcwPs; | 
 | 			break; | 
 | 		case 0: | 
 | 			ctlr->fcrtl = 0x00002000; | 
 | 			ctlr->fcrth = 0x00004000; | 
 | 			break; | 
 | 		case 2: | 
 | 			ctlr->fcrtl = 0; | 
 | 			ctlr->fcrth = 0; | 
 | 			txcw |= TxcwAs; | 
 | 			break; | 
 | 		} | 
 | 		ctlr->txcw = txcw; | 
 | 		csr32w(ctlr, Txcw, txcw); | 
 | 	} | 
 |  | 
 |  | 
 | 	/* | 
 | 	 * Flow control - values from the datasheet. | 
 | 	 */ | 
 | 	csr32w(ctlr, Fcal, 0x00C28001); | 
 | 	csr32w(ctlr, Fcah, 0x00000100); | 
 | 	csr32w(ctlr, Fct, 0x00008808); | 
 | 	csr32w(ctlr, Fcttv, 0x00000100); | 
 |  | 
 | 	csr32w(ctlr, Fcrtl, ctlr->fcrtl); | 
 | 	csr32w(ctlr, Fcrth, ctlr->fcrth); | 
 |  | 
 | 	/* FYI, igbemii checks status right away too. */ | 
 | 	if(!(csr32r(ctlr, Status) & Tbimode) && igbemii(ctlr) < 0) { | 
 | 		printk("igbemii failed!  igbe failing to reset!\n"); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void | 
 | igbepci(void) | 
 | { | 
 | 	int id; | 
 | 	struct pci_device *pcidev; | 
 | 	struct ctlr *ctlr; | 
 | 	void *mem; | 
 | 	uintptr_t mmio_paddr; | 
 |  | 
 | 	STAILQ_FOREACH(pcidev, &pci_devices, all_dev) { | 
 | 		/* This checks that pcidev is a Network Controller for Ethernet */ | 
 | 		if (pcidev->class != 0x02 || pcidev->subclass != 0x00) | 
 | 			continue; | 
 | 		id = pcidev->dev_id << 16 | pcidev->ven_id; | 
 | 		switch (id) { | 
 | 		default: | 
 | 			continue; | 
 | 		case i82543gc: | 
 | 		case i82544ei: | 
 | 		case i82544eif: | 
 | 		case i82544gc: | 
 | 		case i82547ei: | 
 | 		case i82547gi: | 
 | 		case i82540em: | 
 | 		case i82540eplp: | 
 | 		case i82541ei: | 
 | 		case i82541gi: | 
 | 		case i82541gi2: | 
 | 		case i82541pi: | 
 | 		case i82545em: | 
 | 		case i82545gmc: | 
 | 		case i82546gb: | 
 | 		case i82546eb: | 
 | 			break; | 
 | 		} | 
 | 		printk("igbe/e1000 driver found 0x%04x:%04x at %02x:%02x.%x\n", | 
 | 		       pcidev->ven_id, pcidev->dev_id, | 
 | 		       pcidev->bus, pcidev->dev, pcidev->func); | 
 |  | 
 | 		mmio_paddr = pcidev->bar[0].mmio_base32 ? pcidev->bar[0].mmio_base32 : | 
 | 		                                          pcidev->bar[0].mmio_base64; | 
 | 		mem = (void*)vmap_pmem_nocache(mmio_paddr, pcidev->bar[0].mmio_sz); | 
 | 		if(mem == NULL){ | 
 | 			printd("igbe: can't map %p\n", pcidev->bar[0].mmio_base32); | 
 | 			continue; | 
 | 		} | 
 | 		pci_set_cacheline_size(pcidev); | 
 | 		ctlr = kzmalloc(sizeof(struct ctlr), 0); | 
 | 		if(ctlr == NULL) { | 
 | 			vunmap_vmem((uintptr_t)mem, pcidev->bar[0].mmio_sz); | 
 | 			error(ENOMEM, ERROR_FIXME); | 
 | 		} | 
 | 		spinlock_init_irqsave(&ctlr->imlock); | 
 | 		spinlock_init_irqsave(&ctlr->tlock); | 
 | 		qlock_init(&ctlr->alock); | 
 | 		qlock_init(&ctlr->slock); | 
 | 		rendez_init(&ctlr->lrendez); | 
 | 		rendez_init(&ctlr->rrendez); | 
 | 		/* port seems to be unused, and only used for some comparison with edev. | 
 | 		 * plan9 just used the top of the raw bar, regardless of the type. */ | 
 | 		ctlr->port = pcidev->bar[0].raw_bar & ~0x0f; | 
 | 		ctlr->pci = pcidev; | 
 | 		ctlr->id = id; | 
 | 		ctlr->cls = pcidev_read8(pcidev, PCI_CLSZ_REG); | 
 | 		ctlr->nic = mem; | 
 |  | 
 | 		if(igbereset(ctlr)){ | 
 | 			kfree(ctlr); | 
 | 			vunmap_vmem((uintptr_t)mem, pcidev->bar[0].mmio_sz); | 
 | 			continue; | 
 | 		} | 
 | 		pci_set_bus_master(pcidev); | 
 |  | 
 | 		if(igbectlrhead != NULL) | 
 | 			igbectlrtail->next = ctlr; | 
 | 		else | 
 | 			igbectlrhead = ctlr; | 
 | 		igbectlrtail = ctlr; | 
 | 	} | 
 | } | 
 |  | 
 | static int | 
 | igbepnp(struct ether* edev) | 
 | { | 
 | 	struct ctlr *ctlr; | 
 |  | 
 | 	run_once(igbepci()); | 
 |  | 
 | 	/* | 
 | 	 * Any adapter matches if no edev->port is supplied, | 
 | 	 * otherwise the ports must match. | 
 | 	 */ | 
 | 	for(ctlr = igbectlrhead; ctlr != NULL; ctlr = ctlr->next){ | 
 | 		if(ctlr->active) | 
 | 			continue; | 
 | 		if(edev->port == 0 || edev->port == ctlr->port){ | 
 | 			ctlr->active = 1; | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | 	if(ctlr == NULL) | 
 | 		return -1; | 
 |  | 
 | 	edev->ctlr = ctlr; | 
 | 	strlcpy(edev->drv_name, "igbe", KNAMELEN); | 
 | 	edev->port = ctlr->port; | 
 | 	edev->irq = ctlr->pci->irqline; | 
 | 	edev->tbdf = pci_to_tbdf(ctlr->pci); | 
 | 	edev->mbps = 1000; | 
 | 	memmove(edev->ea, ctlr->ra, Eaddrlen); | 
 | 	/* Jim or whoever have this turned on already.  We might be capable of other | 
 | 	 * features. */ | 
 | 	edev->feat = NETF_RXCSUM; | 
 |  | 
 | 	/* | 
 | 	 * Linkage to the generic ethernet driver. | 
 | 	 */ | 
 | 	edev->attach = igbeattach; | 
 | 	edev->transmit = igbetransmit; | 
 | 	edev->ifstat = igbeifstat; | 
 | 	edev->ctl = igbectl; | 
 | 	edev->shutdown = igbeshutdown; | 
 |  | 
 | 	edev->arg = edev; | 
 | 	edev->promiscuous = igbepromiscuous; | 
 | 	edev->multicast = igbemulticast; | 
 |  | 
 | 	register_irq(edev->irq, igbeinterrupt, edev, edev->tbdf); | 
 | 	return 0; | 
 | } | 
 |  | 
 | linker_func_3(etherigbelink) | 
 | { | 
 | 	addethercard("i82543", igbepnp); | 
 | 	addethercard("igbe", igbepnp); | 
 | } |