| /* 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 <arch/pci.h> | 
 | #include <assert.h> | 
 | #include <cpio.h> | 
 | #include <error.h> | 
 | #include <kmalloc.h> | 
 | #include <kref.h> | 
 | #include <net/ip.h> | 
 | #include <ns.h> | 
 | #include <pmap.h> | 
 | #include <slab.h> | 
 | #include <smp.h> | 
 | #include <stdio.h> | 
 | #include <string.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, size_t 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)); | 
 | 			/* make sure the zeroing happens before free (i think) | 
 | 			 */ | 
 | 			wmb(); | 
 | 			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; | 
 |  | 
 | 	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); | 
 |  | 
 | 		mem = pci_get_mmio_bar_kva(pcidev, 0); | 
 | 		if (mem == NULL) { | 
 | 			printd("igbe: can't map BAR 0!\n"); | 
 | 			continue; | 
 | 		} | 
 | 		pci_set_cacheline_size(pcidev); | 
 | 		ctlr = kzmalloc(sizeof(struct ctlr), 0); | 
 | 		if (ctlr == NULL) | 
 | 			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); | 
 | 			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; | 
 | } | 
 |  | 
 | static void __init etherigbelink() | 
 | { | 
 | 	addethercard("i82543", igbepnp); | 
 | 	addethercard("igbe", igbepnp); | 
 | } | 
 | init_func_3(etherigbelink); |