|  | /* 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 <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: | 
|  | 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->checksum = rd->checksum; | 
|  | 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 cls, 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; | 
|  | } | 
|  | cls = pcidev_read8(pcidev, PCI_CLSZ_REG); | 
|  | switch(cls){ | 
|  | default: | 
|  | printd("igbe: unexpected CLS - %d\n", cls*4); | 
|  | break; | 
|  | case 0x00: | 
|  | case 0xFF: | 
|  | /* bogus value; use a sane default.  cls is set in DWORD (u32) | 
|  | * units. */ | 
|  | cls = ARCH_CL_SIZE / sizeof(long); | 
|  | pcidev_write8(pcidev, PCI_CLSZ_REG, cls); | 
|  | break; | 
|  | case 0x08: | 
|  | case 0x10: | 
|  | break; | 
|  | } | 
|  | 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 = cls * sizeof(long); | 
|  | 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; | 
|  | 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); | 
|  |  | 
|  | /* | 
|  | * 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); | 
|  | } |