| /* |
| * Copyright 2008-2014 |
| * erik quanstrom |
| * |
| * This software is provided `as-is,' without any express or implied |
| * warranty. In no event will the author be held liable for any damages |
| * arising from the use of this software. |
| * |
| * Permission is granted to anyone to use this software for any purpose, |
| * including commercial applications, and to alter it and redistribute it |
| * freely, subject to the following restrictions: |
| * |
| * 1. The origin of this software must not be misrepresented; you must |
| * not claim that you wrote the original software. If you use this |
| * software in a product, an acknowledgment in the product documentation |
| * would be appreciated but is not required. |
| * |
| * 2. Altered source versions must be plainly marked as such, and must |
| * not be misrepresented as being the original software. |
| * |
| * 3. This notice may not be removed or altered from any source |
| * distribution. |
| */ |
| /* This code has been modified by UC Berkeley and Google to work in Akaros. */ |
| /* |
| * Intel Gigabit Ethernet PCI-Express Controllers. |
| * 8256[367], 8257[1-79], 21[078] |
| * Pretty basic, does not use many of the chip smarts. |
| * The interrupt mitigation tuning for each chip variant |
| * is probably different. The reset/initialisation |
| * sequence needs straightened out. Doubt the PHY code |
| * for the 82575eb is right. |
| * |
| * on the assumption that allowing jumbo packets makes the controller |
| * much slower (as is true of the 82579), never allow jumbos. |
| */ |
| #include <assert.h> |
| #include <cpio.h> |
| #include <error.h> |
| #include <kmalloc.h> |
| #include <kref.h> |
| #include <net/ip.h> |
| #include <pmap.h> |
| #include <slab.h> |
| #include <smp.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #define now() TK2MS(MACHP(0)->ticks) |
| |
| /* |
| * these are in the order they appear in the manual, not numeric order. |
| * It was too hard to find them in the book. Ref 21489, rev 2.6 |
| */ |
| |
| enum { |
| /* General */ |
| Ctrl = 0x0000, /* Device Control */ |
| Status = 0x0008, /* Device Status */ |
| Eec = 0x0010, /* EEPROM/Flash Control/Data */ |
| Fextnvm6 = 0x0010, /* Future Extended NVM 6 */ |
| Eerd = 0x0014, /* EEPROM Read */ |
| Ctrlext = 0x0018, /* Extended Device Control */ |
| Fla = 0x001c, /* Flash Access */ |
| Mdic = 0x0020, /* MDI Control */ |
| Seresctl = 0x0024, /* Serdes ana */ |
| Fcal = 0x0028, /* Flow Control Address Low */ |
| Fcah = 0x002C, /* Flow Control Address High */ |
| Fct = 0x0030, /* Flow Control Type */ |
| Kumctrlsta = 0x0034, /* MAC-PHY Interface */ |
| Vet = 0x0038, /* VLAN EtherType */ |
| Fcttv = 0x0170, /* Flow Control Transmit Timer Value */ |
| Txcw = 0x0178, /* Transmit Configuration Word */ |
| Rxcw = 0x0180, /* Receive Configuration Word */ |
| Ledctl = 0x0E00, /* LED control */ |
| Pba = 0x1000, /* Packet Buffer Allocation */ |
| Pbs = 0x1008, /* Packet Buffer Size */ |
| |
| /* Interrupt */ |
| Icr = 0x00C0, /* Interrupt Cause Read */ |
| Itr = 0x00c4, /* Interrupt Throttling Rate */ |
| Ics = 0x00C8, /* Interrupt Cause Set */ |
| Ims = 0x00D0, /* Interrupt Mask Set/Read */ |
| Imc = 0x00D8, /* Interrupt mask Clear */ |
| Iam = 0x00E0, /* Interrupt acknowledge Auto Mask */ |
| |
| /* Receive */ |
| Rctl = 0x0100, /* Control */ |
| Ert = 0x2008, /* Early Receive Threshold (573[EVL], 579 only) */ |
| Fcrtl = 0x2160, /* Flow Control RX Threshold Low */ |
| Fcrth = 0x2168, /* Flow Control Rx Threshold High */ |
| Psrctl = 0x2170, /* Packet Split Receive Control */ |
| Rdbal = 0x2800, /* Rdesc Base Address Low Queue 0 */ |
| Rdbah = 0x2804, /* Rdesc Base Address High Queue 0 */ |
| Rdlen = 0x2808, /* Descriptor Length Queue 0 */ |
| Srrctl = 0x280c, /* split and replication rx control (82575) */ |
| Rdh = 0x2810, /* Descriptor Head Queue 0 */ |
| Rdt = 0x2818, /* Descriptor Tail Queue 0 */ |
| Rdtr = 0x2820, /* Descriptor Timer Ring */ |
| Rxdctl = 0x2828, /* Descriptor Control */ |
| Radv = 0x282C, /* Interrupt Absolute Delay Timer */ |
| Rdbal1 = 0x2900, /* Rdesc Base Address Low Queue 1 */ |
| Rdbah1 = 0x2804, /* Rdesc Base Address High Queue 1 */ |
| Rdlen1 = 0x2908, /* Descriptor Length Queue 1 */ |
| Rdh1 = 0x2910, /* Descriptor Head Queue 1 */ |
| Rdt1 = 0x2918, /* Descriptor Tail Queue 1 */ |
| Rxdctl1 = 0x2928, /* Descriptor Control Queue 1 */ |
| Rsrpd = 0x2c00, /* Small Packet Detect */ |
| Raid = 0x2c08, /* ACK interrupt delay */ |
| Cpuvec = 0x2c10, /* CPU Vector */ |
| Rxcsum = 0x5000, /* Checksum Control */ |
| Rmpl = 0x5004, /* rx maximum packet length (82575) */ |
| Rfctl = 0x5008, /* Filter Control */ |
| Mta = 0x5200, /* Multicast Table Array */ |
| Ral = 0x5400, /* Receive Address Low */ |
| Rah = 0x5404, /* Receive Address High */ |
| Vfta = 0x5600, /* VLAN Filter Table Array */ |
| Mrqc = 0x5818, /* Multiple Receive Queues Command */ |
| Rssim = 0x5864, /* RSS Interrupt Mask */ |
| Rssir = 0x5868, /* RSS Interrupt Request */ |
| Reta = 0x5c00, /* Redirection Table */ |
| Rssrk = 0x5c80, /* RSS Random Key */ |
| |
| /* Transmit */ |
| Tctl = 0x0400, /* Transmit Control */ |
| Tipg = 0x0410, /* Transmit IPG */ |
| Tkabgtxd = |
| 0x3004, /* glci afe band gap transmit ref data, or something */ |
| Tdbal = 0x3800, /* Tdesc Base Address Low */ |
| Tdbah = 0x3804, /* Tdesc Base Address High */ |
| Tdlen = 0x3808, /* Descriptor Length */ |
| Tdh = 0x3810, /* Descriptor Head */ |
| Tdt = 0x3818, /* Descriptor Tail */ |
| Tidv = 0x3820, /* Interrupt Delay Value */ |
| Txdctl = 0x3828, /* Descriptor Control */ |
| Tadv = 0x382C, /* Interrupt Absolute Delay Timer */ |
| Tarc0 = 0x3840, /* Arbitration Counter Queue 0 */ |
| Tdbal1 = 0x3900, /* Descriptor Base Low Queue 1 */ |
| Tdbah1 = 0x3904, /* Descriptor Base High Queue 1 */ |
| Tdlen1 = 0x3908, /* Descriptor Length Queue 1 */ |
| Tdh1 = 0x3910, /* Descriptor Head Queue 1 */ |
| Tdt1 = 0x3918, /* Descriptor Tail Queue 1 */ |
| Txdctl1 = 0x3928, /* Descriptor Control 1 */ |
| Tarc1 = 0x3940, /* Arbitration Counter Queue 1 */ |
| |
| /* Statistics */ |
| Statistics = 0x4000, /* 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 = 0x124 / 4, |
| }; |
| |
| enum { /* Ctrl */ |
| GIOmd = 1 << 2, /* BIO master disable */ |
| Lrst = 1 << 3, /* link reset */ |
| Slu = 1 << 6, /* Set Link Up */ |
| SspeedMASK = 3 << 8, /* Speed Selection */ |
| SspeedSHIFT = 8, |
| Sspeed10 = 0x00000000, /* 10Mb/s */ |
| Sspeed100 = 0x00000100, /* 100Mb/s */ |
| Sspeed1000 = 0x00000200, /* 1000Mb/s */ |
| Frcspd = 1 << 11, /* Force Speed */ |
| Frcdplx = 1 << 12, /* Force Duplex */ |
| SwdpinsloMASK = 0x003C0000, /* Software Defined Pins - lo nibble */ |
| SwdpinsloSHIFT = 18, |
| SwdpioloMASK = 0x03C00000, /* Software Defined Pins - I or O */ |
| SwdpioloSHIFT = 22, |
| Devrst = 1 << 26, /* Device Reset */ |
| Rfce = 1 << 27, /* Receive Flow Control Enable */ |
| Tfce = 1 << 28, /* Transmit Flow Control Enable */ |
| Vme = 1 << 30, /* VLAN Mode Enable */ |
| Phyrst = 1 << 31, /* Phy Reset */ |
| }; |
| |
| enum { /* Status */ |
| Lu = 1 << 1, /* Link Up */ |
| Lanid = 3 << 2, /* mask for Lan ID. */ |
| Txoff = 1 << 4, /* Transmission Paused */ |
| Tbimode = 1 << 5, /* TBI Mode Indication */ |
| Phyra = 1 << 10, /* PHY Reset Asserted */ |
| GIOme = 1 << 19, /* GIO Master Enable Status */ |
| }; |
| |
| enum { |
| /* Eec */ |
| Nvpres = 1 << 8, /* nvram present */ |
| Autord = 1 << 9, /* autoread complete */ |
| Sec1val = 1 << 22, /* sector 1 valid (!sec0) */ |
| }; |
| |
| enum { /* Eerd */ |
| EEstart = 1 << 0, /* Start Read */ |
| EEdone = 1 << 1, /* Read done */ |
| }; |
| |
| enum { /* Ctrlext */ |
| Asdchk = 1 << 12, /* ASD Check */ |
| Eerst = 1 << 13, /* EEPROM Reset */ |
| Spdbyps = 1 << 15, /* Speed Select Bypass */ |
| }; |
| |
| /* |
| * TODO(dcross): 'Ea' is 0 elsewhere. Investigate and possibly correct. |
| */ |
| enum { /* EEPROM content offsets */ |
| OldEa = 0x00, /* Old Ethernet address */ |
| Ea = 0x01, /* Ethernet Address */ |
| Cf = 0x03, /* Compatibility Field */ |
| 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 { /* phy interface registers */ |
| Phyctl = 0, /* phy ctl */ |
| Physsr = 17, /* phy secondary status */ |
| Phyier = 18, /* 82573 phy interrupt enable */ |
| Phyisr = 19, /* 82563 phy interrupt status */ |
| Phylhr = 19, /* 8257[12] link health */ |
| Phyier218 = 24, /* 218 (phy79?) phy interrupt enable */ |
| Phyisr218 = 25, /* 218 (phy79?) phy interrupt status */ |
| Phystat = 26, /* 82580 (phy79?) phy status */ |
| Phypage = 31, /* page number */ |
| |
| Rtlink = 1 << 10, /* realtime link status */ |
| Phyan = 1 << 11, /* phy has auto-negotiated */ |
| |
| /* Phyctl bits */ |
| Ran = 1 << 9, /* restart auto-negotiation */ |
| Ean = 1 << 12, /* enable auto-negotiation */ |
| |
| /* 82573 Phyier interrupt enable bits */ |
| Lscie = 1 << 10, /* link status changed */ |
| Ancie = 1 << 11, /* auto-negotiation complete */ |
| Spdie = 1 << 14, /* speed changed */ |
| Panie = 1 << 15, /* phy auto-negotiation error */ |
| |
| /* Phylhr/Phyisr bits */ |
| Anf = 1 << 6, /* lhr: auto-negotiation fault */ |
| Ane = 1 << 15, /* isr: auto-negotiation error */ |
| |
| /* 82580 Phystat bits */ |
| Ans = 3 << 14, /* 82580 autoneg. status */ |
| Link = 1 << 6, /* 82580 link */ |
| |
| /* 218 Phystat bits */ |
| Anfs = 3 << 13, /* fault status */ |
| Ans218 = 1 << 12, /* autoneg complete */ |
| |
| /* 218 Phyier218 interrupt enable bits */ |
| Spdie218 = 1 << 1, /* speed changed */ |
| Lscie218 = 1 << 2, /* link status changed */ |
| Ancie218 = 1 << 8, /* auto-negotiation changed */ |
| }; |
| |
| 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, /* Rdesc 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, |
| Ack = 0x00020000, /* Receive ACK frame */ |
| }; |
| |
| 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 { /* 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, /* Rdesc 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 */ |
| Bam = 0x00008000, /* Broadcast Accept Mode */ |
| BsizeMASK = 0x00030000, /* Receive Buffer Size */ |
| Bsize16384 = 0x00010000, /* Bsex = 1 */ |
| Bsize8192 = 0x00020000, /* Bsex = 1 */ |
| Bsize2048 = 0x00000000, |
| Bsize1024 = 0x00010000, |
| Bsize512 = 0x00020000, |
| Bsize256 = 0x00030000, |
| BsizeFlex = 0x08000000, /* Flexible Bsize in 1KB increments */ |
| 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 { /* Srrctl */ |
| Dropen = 1 << 31, |
| }; |
| |
| enum { /* Tctl */ |
| Trst = 0x00000001, /* Transmitter Software Reset */ |
| Ten = 0x00000002, /* Transmit Enable */ |
| Psp = 0x00000008, /* Pad Short Packets */ |
| Mulr = 0x10000000, /* Allow multiple concurrent requests */ |
| 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 (descriptors, not cls) */ |
| Qenable = 0x02000000, /* Queue Enable (82575) */ |
| }; |
| |
| enum { /* Rxcsum */ |
| PcssMASK = 0x00FF, /* Packet Checksum Start */ |
| PcssSHIFT = 0, |
| Ipofl = 0x0100, /* IP Checksum Off-load Enable */ |
| Tuofl = 0x0200, /* TCP/UDP Checksum Off-load Enable */ |
| }; |
| |
| enum { /* Receive Delay Timer Ring */ |
| DelayMASK = 0xFFFF, /* delay timer in 1.024nS increments */ |
| DelaySHIFT = 0, |
| Fpd = 0x80000000, /* Flush partial Descriptor Block */ |
| }; |
| |
| struct rd { /* Receive Descriptor */ |
| uint32_t addr[2]; |
| uint16_t length; |
| uint16_t checksum; |
| uint8_t status; |
| uint8_t errors; |
| uint16_t special; |
| }; |
| |
| 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 */ |
| }; |
| |
| struct td { /* Transmit Descriptor */ |
| uint32_t addr[2]; /* Data */ |
| uint32_t control; |
| uint32_t status; |
| }; |
| |
| enum { /* Tdesc 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 { /* Tdesc status */ |
| Tdd = 0x0001, /* Descriptor Done */ |
| Ec = 0x0002, /* Excess Collisions */ |
| Lc = 0x0004, /* Late Collision */ |
| Tu = 0x0008, /* Transmit Underrun */ |
| CssMASK = 0xFF00, /* Checksum Start Field */ |
| CssSHIFT = 8, |
| }; |
| |
| struct flash { |
| uint16_t *reg; |
| uint32_t *reg32; |
| uint16_t base; |
| uint16_t lim; |
| }; |
| |
| enum { |
| /* 16 and 32-bit flash registers for ich flash parts */ |
| Bfpr = 0x00 / 4, /* flash base 0:12; lim 16:28 */ |
| Fsts = 0x04 / 2, /* flash status; Hsfsts */ |
| Fctl = 0x06 / 2, /* flash control; Hsfctl */ |
| Faddr = 0x08 / 4, /* flash address to r/w */ |
| Fdata = 0x10 / 4, /* data @ address */ |
| |
| /* status register */ |
| Fdone = 1 << 0, /* flash cycle done */ |
| Fcerr = 1 << 1, /* cycle error; write 1 to clear */ |
| Ael = 1 << 2, /* direct access error log; 1 to clear */ |
| Scip = 1 << 5, /* spi cycle in progress */ |
| Fvalid = 1 << 14, /* flash descriptor valid */ |
| |
| /* control register */ |
| Fgo = 1 << 0, /* start cycle */ |
| Flcycle = 1 << 1, /* two bits: r=0; w=2 */ |
| Fdbc = 1 << 8, /* bytes to read; 5 bits */ |
| }; |
| |
| /* |
| * the kumeran interface is mac-to-phy for external gigabit ethernet on |
| * intel's esb2 ich8 (io controller hub), it carries mii bits. can be used |
| * to reset the phy. intel proprietary, see "kumeran specification". |
| */ |
| enum { I217inbandctlpage = 770, /* phy page */ |
| I217inbandctlreg = 18, /* phy register */ |
| I217inbandctllnkststxtmoutmask = 0x3F00, |
| I217inbandctllnkststxtmoutshift = 8, |
| |
| Fextnvm6reqpllclk = 0x100, |
| Fextnvm6enak1entrycond = 0x200, /* extend K1 entry latency */ |
| |
| Nvmk1cfg = 0x1B, /* NVM K1 Config Word */ |
| Nvmk1enable = 0x1, /* NVM Enable K1 bit */ |
| |
| Kumctrlstaoff = 0x1F0000, |
| Kumctrlstaoffshift = 16, |
| Kumctrlstaren = 0x200000, |
| Kumctrlstak1cfg = 0x7, |
| Kumctrlstak1enable = 0x2, |
| }; |
| |
| enum { |
| /* |
| * these were 512, 1024 & 64, but 52, 253 & 9 are usually ample; |
| * however cpu servers and terminals can need more receive buffers |
| * due to bursts of traffic. |
| * |
| * Tdlen and Rdlen have to be multiples of 128. Rd and Td are both |
| * 16 bytes long, so Nrd and Ntd must be multiples of 8. |
| */ |
| Ntd = 32, /* power of two >= 8 */ |
| Nrd = 128, /* power of two >= 8 */ |
| Rbalign = 16, |
| Slop = 32, /* for vlan headers, crcs, etc. */ |
| }; |
| |
| enum { Iany = -1, |
| i82563, |
| i82566, |
| i82567, |
| i82567m, |
| i82571, |
| i82572, |
| i82573, |
| i82574, |
| i82575, |
| i82576, |
| i82577, |
| i82577m, |
| i82578, |
| i82578m, |
| i82579, |
| i82580, |
| i82583, |
| i210, |
| i217, |
| i218, |
| i350, |
| Nctlrtype, |
| }; |
| |
| enum { Fload = 1 << 0, |
| Fert = 1 << 1, |
| F75 = 1 << 2, |
| Fpba = 1 << 3, |
| Fflashea = 1 << 4, |
| F79phy = 1 << 5, |
| Fnofct = 1 << 6, |
| }; |
| |
| struct ctlrtype { |
| int type; |
| int mtu; |
| int phyno; |
| char *name; |
| int flag; |
| }; |
| |
| static struct ctlrtype ctlrtab[Nctlrtype] = { |
| {i82563, 9014, 1, "i82563", Fpba}, |
| {i82566, 1514, 1, "i82566", Fload}, |
| {i82567, 9234, 1, "i82567", Fload}, |
| {i82567m, 1514, 1, "i82567m", 0}, |
| {i82571, 9234, 1, "i82571", Fpba}, |
| {i82572, 9234, 1, "i82572", Fpba}, |
| {i82573, 8192, 1, "i82573", Fert}, /* terrible perf above 8k */ |
| {i82574, 9018, 1, "i82574", 0}, |
| {i82575, 9728, 1, "i82575", F75 | Fflashea}, |
| {i82576, 9728, 1, "i82576", F75}, |
| {i82577, 4096, 2, "i82577", Fload | Fert}, |
| {i82577m, 1514, 2, "i82577", Fload | Fert}, |
| {i82578, 4096, 2, "i82578", Fload | Fert}, |
| {i82578m, 1514, 2, "i82578", Fload | Fert}, |
| {i82579, 9018, 2, "i82579", Fload | Fert | F79phy | Fnofct}, |
| {i82580, 9728, 1, "i82580", F75 | F79phy}, |
| {i82583, 1514, 1, "i82583", 0}, |
| {i210, 9728, 1, "i210", F75 | Fnofct | Fert}, |
| {i217, 9728, 1, "i217", F79phy | Fnofct | Fload | Fert}, |
| {i350, 9728, 1, "i350", F75 | F79phy | Fnofct}, |
| }; |
| |
| struct ctlr { |
| uintptr_t mmio_paddr; |
| struct pci_device *pcidev; |
| struct ctlr *next; |
| struct ether *edev; |
| int active; |
| int type; |
| uint16_t eeprom[0x40]; |
| |
| qlock_t alock; /* attach */ |
| void *alloc; |
| unsigned int rbsz; |
| int attached; |
| |
| int *nic; |
| spinlock_t imlock; |
| int im; /* interrupt mask */ |
| |
| struct rendez lrendez; |
| int lim; |
| int phynum; |
| int didk1fix; |
| |
| 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; |
| unsigned int speeds[4]; |
| |
| uint8_t ra[Eaddrlen]; /* receive address */ |
| uint32_t mta[128]; /* multicast table array */ |
| |
| struct rendez rrendez; |
| int rim; |
| int rdfree; /* rx descriptors awaiting packets */ |
| struct rd *rdba; /* receive descriptor base address */ |
| struct block **rb; /* receive buffers */ |
| unsigned int rdh; /* receive descriptor head */ |
| unsigned int rdt; /* receive descriptor tail */ |
| int rdtr; /* receive delay timer ring value */ |
| int radv; /* receive interrupt absolute delay timer */ |
| |
| struct rendez trendez; |
| qlock_t tlock; |
| struct td *tdba; /* transmit descriptor base address */ |
| struct block **tb; /* transmit buffers */ |
| int tdh; /* transmit descriptor head */ |
| int tdt; /* transmit descriptor tail */ |
| |
| int fcrtl; |
| int fcrth; |
| |
| unsigned int pbs; /* packet buffer size */ |
| unsigned int pba; /* packet buffer allocation */ |
| }; |
| |
| 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 *i82563ctlrhead; |
| static struct ctlr *i82563ctlrtail; |
| |
| static int speedtab[] = {10, 100, 1000, 0}; |
| |
| static char *statistics[] = { |
| "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-mtu 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", |
| "Management Packets Rx", |
| "Management Packets Drop", |
| "Management Packets Tx", |
| "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-mtu Bytes)", |
| "Multicast Packets Transmitted", |
| "Broadcast Packets Transmitted", |
| "TCP Segmentation Context Transmitted", |
| "TCP Segmentation Context Fail", |
| "Interrupt Assertion", |
| "Interrupt Rx Pkt Timer", |
| "Interrupt Rx Abs Timer", |
| "Interrupt Tx Pkt Timer", |
| "Interrupt Tx Abs Timer", |
| "Interrupt Tx Queue Empty", |
| "Interrupt Tx Desc Low", |
| "Interrupt Rx Min", |
| "Interrupt Rx Overrun", |
| }; |
| |
| static char *cname(struct ctlr *c) |
| { |
| return ctlrtab[c->type].name; |
| } |
| |
| static int i82563reset(struct ctlr *); |
| |
| static long i82563ifstat(struct ether *edev, void *a, long n, uint32_t offset) |
| { |
| struct ctlr *ctlr; |
| char *s, *p, *e, *stat; |
| int i, r; |
| uint64_t tuvl, ruvl; |
| |
| ctlr = edev->ctlr; |
| qlock(&ctlr->slock); |
| p = s = kzmalloc(READSTR, 0); |
| if (p == NULL) { |
| qunlock(&ctlr->slock); |
| error(ENOMEM, "kzmalloc did not panic"); |
| } |
| e = p + READSTR; |
| |
| for (i = 0; i < Nstatistics; i++) { |
| r = csr32r(ctlr, Statistics + i * 4); |
| stat = statistics[i]; |
| if (stat == 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; |
| p = seprintf(p, e, "%s: %llud %llud\n", stat, tuvl, |
| ruvl); |
| i++; |
| break; |
| |
| default: |
| ctlr->statistics[i] += r; |
| if (ctlr->statistics[i] == 0) |
| continue; |
| p = seprintf(p, e, "%s: %ud %ud\n", stat, |
| ctlr->statistics[i], r); |
| break; |
| } |
| } |
| |
| p = seprintf(p, e, "lintr: %ud %ud\n", ctlr->lintr, ctlr->lsleep); |
| p = seprintf(p, e, "rintr: %ud %ud\n", ctlr->rintr, ctlr->rsleep); |
| p = seprintf(p, e, "tintr: %ud %ud\n", ctlr->tintr, ctlr->txdw); |
| p = seprintf(p, e, "ixcs: %ud %ud %ud\n", ctlr->ixsm, ctlr->ipcs, |
| ctlr->tcpcs); |
| p = seprintf(p, e, "ctrl: %.8ux\n", csr32r(ctlr, Ctrl)); |
| p = seprintf(p, e, "ctrlext: %.8ux\n", csr32r(ctlr, Ctrlext)); |
| p = seprintf(p, e, "status: %.8ux\n", csr32r(ctlr, Status)); |
| p = seprintf(p, e, "txcw: %.8ux\n", csr32r(ctlr, Txcw)); |
| p = seprintf(p, e, "txdctl: %.8ux\n", csr32r(ctlr, Txdctl)); |
| p = seprintf(p, e, "pbs: %dKB\n", ctlr->pbs); |
| p = seprintf(p, e, "pba: %#.8ux\n", ctlr->pba); |
| |
| p = seprintf(p, e, "speeds: 10:%ud 100:%ud 1000:%ud ?:%ud\n", |
| ctlr->speeds[0], ctlr->speeds[1], ctlr->speeds[2], |
| ctlr->speeds[3]); |
| p = seprintf(p, e, "type: %s\n", cname(ctlr)); |
| |
| // p = seprintf(p, e, "eeprom:"); |
| // for(i = 0; i < 0x40; i++){ |
| // if(i && ((i & 7) == 0)) |
| // p = seprintf(p, e, "\n "); |
| // p = seprintf(p, e, " %4.4ux", ctlr->eeprom[i]); |
| // } |
| // p = seprintf(p, e, "\n"); |
| |
| n = readstr(offset, a, n, s); |
| kfree(s); |
| qunlock(&ctlr->slock); |
| |
| return n; |
| } |
| |
| enum { CMrdtr, |
| CMradv, |
| CMpause, |
| CMan, |
| }; |
| |
| static struct cmdtab i82563ctlmsg[] = { |
| {CMrdtr, "rdtr", 2}, |
| {CMradv, "radv", 2}, |
| {CMpause, "pause", 1}, |
| {CMan, "an", 1}, |
| }; |
| |
| static long i82563ctl(struct ether *edev, void *buf, long n) |
| { |
| ERRSTACK(1); |
| char *p; |
| uint32_t v; |
| struct ctlr *ctlr; |
| struct cmdbuf *cb; |
| struct cmdtab *ct; |
| |
| ctlr = edev->ctlr; |
| if (ctlr == NULL) |
| error(ENODEV, "i82563ctl: NULL controller"); |
| |
| cb = parsecmd(buf, n); |
| if (waserror()) { |
| kfree(cb); |
| nexterror(); |
| } |
| |
| ct = lookupcmd(cb, i82563ctlmsg, ARRAY_SIZE(i82563ctlmsg)); |
| switch (ct->index) { |
| case CMrdtr: |
| v = strtoul(cb->f[1], &p, 0); |
| if (*p || v > 0xffff) |
| error(EINVAL, ERROR_FIXME); |
| ctlr->rdtr = v; |
| csr32w(ctlr, Rdtr, v); |
| break; |
| case CMradv: |
| v = strtoul(cb->f[1], &p, 0); |
| if (*p || v > 0xffff) |
| error(EINVAL, ERROR_FIXME); |
| ctlr->radv = v; |
| csr32w(ctlr, Radv, v); |
| break; |
| case CMpause: |
| csr32w(ctlr, Ctrl, csr32r(ctlr, Ctrl) ^ (Rfce | Tfce)); |
| break; |
| case CMan: |
| csr32w(ctlr, Ctrl, csr32r(ctlr, Ctrl) | Lrst | Phyrst); |
| break; |
| } |
| kfree(cb); |
| poperror(); |
| |
| return n; |
| } |
| |
| static void i82563promiscuous(void *arg, int on) |
| { |
| int rctl; |
| struct ctlr *ctlr; |
| struct ether *edev; |
| |
| edev = arg; |
| ctlr = edev->ctlr; |
| |
| rctl = csr32r(ctlr, Rctl) & ~MoMASK; |
| if (on) |
| rctl |= Upe | Mpe; |
| else |
| rctl &= ~(Upe | Mpe); |
| csr32w(ctlr, Rctl, rctl); |
| } |
| |
| /* |
| * Returns the number of bits of mac address used in multicast hash, |
| * thus the number of longs of ctlr->mta (2^(bits-5)). |
| * This must be right for multicast (thus ipv6) to work reliably. |
| * |
| * The default multicast hash for mta is based on 12 bits of MAC address; |
| * the rightmost bit is a function of Rctl's Multicast Offset: 0=>36, |
| * 1=>35, 2=>34, 3=>32. Exceptions include the 578, 579, 217, 218, 219; |
| * they use only 10 bits, ignoring the rightmost 2 of the 12. |
| */ |
| static int mcastbits(struct ctlr *ctlr) |
| { |
| switch (ctlr->type) { |
| /* |
| * openbsd says all `ich8' versions (ich8, ich9, ich10, pch, |
| * pch2 and pch_lpt) have 32 longs (use 10 bits of mac address |
| * for hash). |
| */ |
| case i82566: |
| case i82567: |
| // case i82578: |
| case i82579: |
| case i217: |
| case i218: |
| // case i219: |
| return 10; /* 32 longs */ |
| case i82563: |
| case i82571: |
| case i82572: |
| case i82573: |
| case i82574: |
| // case i82575: |
| // case i82583: |
| case i210: /* includes i211 */ |
| return 12; /* 128 longs */ |
| default: |
| printk("82563: unsure of multicast bits in mac addresses; " |
| "enabling promiscuous multicast reception\n"); |
| csr32w(ctlr, Rctl, csr32r(ctlr, Rctl) | Mpe); |
| return 10; /* be conservative (for mta size) */ |
| } |
| } |
| |
| static int mcbitstolongs(int nmcbits) |
| { |
| return 1 << (nmcbits - 5); /* 2^5 = 32 */ |
| } |
| |
| static void i82563multicast(void *arg, uint8_t *addr, int on) |
| { |
| uint32_t nbits, tblsz, hash, word, bit; |
| struct ctlr *ctlr; |
| struct ether *edev; |
| |
| edev = arg; |
| ctlr = edev->ctlr; |
| |
| nbits = mcastbits(ctlr); |
| tblsz = mcbitstolongs(nbits); |
| /* assume multicast offset in Rctl is 0 (we clear it above) */ |
| hash = addr[5] << 4 | addr[4] >> 4; /* bits 47:36 of mac */ |
| if (nbits == 10) |
| hash >>= 2; /* discard 37:36 of mac */ |
| word = (hash / 32) & (tblsz - 1); |
| bit = 1UL << (hash % 32); |
| /* |
| * 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 (on) |
| ctlr->mta[word] |= bit; |
| // else |
| // ctlr->mta[word] &= ~bit; |
| csr32w(ctlr, Mta + word * 4, ctlr->mta[word]); |
| } |
| |
| static void i82563im(struct ctlr *ctlr, int im) |
| { |
| spin_lock_irqsave(&ctlr->imlock); |
| ctlr->im |= im; |
| csr32w(ctlr, Ims, ctlr->im); |
| spin_unlock_irqsave(&ctlr->imlock); |
| } |
| |
| static void i82563txinit(struct ctlr *ctlr) |
| { |
| int i, r, tctl; |
| struct block *bp; |
| |
| /* |
| * TODO(dcross): Figure out how to integrate this table driven |
| * code into the stanza below. |
| */ |
| tctl = 0x0F << Ctshift | Psp; |
| if (0) { |
| if ((ctlrtab[ctlr->type].flag & F75) == 0) |
| tctl |= (66 << ColdSHIFT | Mulr); |
| } |
| switch (ctlr->type) { |
| case i210: |
| break; |
| default: |
| tctl |= Mulr; |
| /* fall through */ |
| case i217: |
| case i218: |
| tctl |= 66 << ColdSHIFT; |
| break; |
| } |
| csr32w(ctlr, Tctl, tctl); |
| csr32w(ctlr, Tipg, 6 << 20 | 8 << 10 | 8); /* yb sez: 0x702008 */ |
| for (i = 0; i < Ntd; i++) { |
| bp = ctlr->tb[i]; |
| if (bp != NULL) { |
| ctlr->tb[i] = NULL; |
| freeb(bp); |
| } |
| } |
| memset(ctlr->tdba, 0, Ntd * sizeof(struct td)); |
| csr32w(ctlr, Tdbal, paddr_low32(ctlr->tdba)); |
| csr32w(ctlr, Tdbah, paddr_high32(ctlr->tdba)); |
| csr32w(ctlr, Tdlen, Ntd * sizeof(struct td)); |
| ctlr->tdh = PREV_RING(0, Ntd); |
| csr32w(ctlr, Tdh, 0); |
| ctlr->tdt = 0; |
| csr32w(ctlr, Tdt, 0); |
| csr32w(ctlr, Tidv, 0); /* don't coalesce interrupts */ |
| csr32w(ctlr, Tadv, 0); |
| r = csr32r(ctlr, Txdctl) & ~(WthreshMASK | PthreshMASK); |
| r |= 4 << WthreshSHIFT | 4 << PthreshSHIFT; |
| if (ctlrtab[ctlr->type].flag & F75) |
| r |= Qenable; |
| csr32w(ctlr, Txdctl, r); |
| csr32w(ctlr, Tctl, csr32r(ctlr, Tctl) | Ten); |
| } |
| |
| static int i82563cleanup(struct ctlr *ctlr) |
| { |
| struct block *bp; |
| int tdh, n; |
| |
| tdh = ctlr->tdh; |
| while (ctlr->tdba[n = NEXT_RING(tdh, Ntd)].status & Tdd) { |
| tdh = n; |
| bp = ctlr->tb[tdh]; |
| if (bp != NULL) { |
| ctlr->tb[tdh] = NULL; |
| freeb(bp); |
| } else |
| iprint("82563 tx underrun!\n"); |
| ctlr->tdba[tdh].status = 0; |
| } |
| return ctlr->tdh = tdh; |
| } |
| |
| static void i82563transmit(struct ether *edev) |
| { |
| struct td *td; |
| struct block *bp; |
| struct ctlr *ctlr; |
| int tdh, tdt; |
| |
| ctlr = edev->ctlr; |
| qlock(&ctlr->tlock); |
| |
| /* |
| * Free any completed packets |
| */ |
| tdh = i82563cleanup(ctlr); |
| |
| /* if link down on 218, don't try since we need k1fix to run first */ |
| if (!edev->link && ctlr->type == i218 && !ctlr->didk1fix) { |
| qunlock(&ctlr->tlock); |
| return; |
| } |
| |
| /* |
| * Try to fill the ring back up. |
| */ |
| tdt = ctlr->tdt; |
| for (;;) { |
| if (NEXT_RING(tdt, Ntd) == tdh) { /* ring full? */ |
| ctlr->txdw++; |
| i82563im(ctlr, Txdw); |
| break; |
| } |
| bp = qget(edev->oq); |
| if (bp == NULL) |
| break; |
| td = &ctlr->tdba[tdt]; |
| td->addr[0] = paddr_low32(bp->rp); |
| td->addr[1] = paddr_high32(bp->rp); |
| td->control = Ide | Rs | Ifcs | Teop | BLEN(bp); |
| ctlr->tb[tdt] = bp; |
| tdt = NEXT_RING(tdt, Ntd); |
| } |
| if (ctlr->tdt != tdt) { |
| ctlr->tdt = tdt; |
| wmb_f(); |
| csr32w(ctlr, Tdt, tdt); |
| } |
| /* else may not be any new ones, but could be some still in flight */ |
| qunlock(&ctlr->tlock); |
| } |
| |
| static void i82563replenish(struct ctlr *ctlr) |
| { |
| struct rd *rd; |
| int rdt; |
| struct block *bp; |
| |
| rdt = ctlr->rdt; |
| while (NEXT_RING(rdt, Nrd) != ctlr->rdh) { |
| rd = &ctlr->rdba[rdt]; |
| if (ctlr->rb[rdt] != NULL) { |
| printd("#l%d: 82563: rx overrun\n", ctlr->edev->ctlrno); |
| break; |
| } |
| bp = block_alloc(ctlr->rbsz + Slop + Rbalign, MEM_ATOMIC); |
| if (bp == NULL) { |
| warn_once("OOM, trying to survive"); |
| break; |
| } |
| ctlr->rb[rdt] = bp; |
| rd->addr[0] = paddr_low32(bp->rp); |
| rd->addr[1] = paddr_high32(bp->rp); |
| rd->status = 0; |
| ctlr->rdfree++; |
| rdt = NEXT_RING(rdt, Nrd); |
| } |
| if (ctlr->rdt != rdt) { |
| ctlr->rdt = rdt; |
| wmb_f(); |
| csr32w(ctlr, Rdt, rdt); |
| } |
| } |
| |
| static void i82563rxinit(struct ctlr *ctlr) |
| { |
| struct block *bp; |
| int i, r, rctl, type; |
| |
| type = ctlr->type; |
| |
| if (ctlr->rbsz <= 2048) |
| csr32w(ctlr, Rctl, Dpf | Bsize2048 | Bam | RdtmsHALF); |
| else { |
| i = ctlr->rbsz / 1024; |
| if (ctlr->rbsz % 1024) |
| i++; |
| if (ctlrtab[ctlr->type].flag & F75) { |
| csr32w(ctlr, Rctl, |
| Lpe | Dpf | Bsize2048 | Bam | RdtmsHALF | Secrc); |
| if (ctlr->type != i82575) |
| i |= (Nrd / 2 >> 4) << 20; /* RdmsHalf */ |
| csr32w(ctlr, Srrctl, i | Dropen); |
| csr32w(ctlr, Rmpl, ctlr->rbsz); |
| // csr32w(ctlr, Drxmxod, 0x7ff); |
| } else |
| csr32w(ctlr, Rctl, |
| Lpe | Dpf | BsizeFlex * i | Bam | RdtmsHALF | |
| Secrc); |
| } |
| |
| /* |
| * TODO(dcross): Reconcile this with latest above code block. |
| */ |
| if (0) { |
| rctl = Dpf | Bsize2048 | Bam | RdtmsHALF; |
| if (type == i82575 || type == i82576 || type == i210) { |
| /* |
| * Setting Qenable in Rxdctl does not |
| * appear to stick unless Ren is on. |
| */ |
| csr32w(ctlr, Rctl, Ren | rctl); |
| csr32w(ctlr, Rxdctl, csr32r(ctlr, Rxdctl) | Qenable); |
| } |
| csr32w(ctlr, Rctl, rctl); |
| } |
| |
| if (ctlrtab[ctlr->type].flag & Fert) |
| csr32w(ctlr, Ert, 1024 / 8); /* early rx threshold */ |
| |
| csr32w(ctlr, Rdbal, paddr_low32(ctlr->rdba)); |
| csr32w(ctlr, Rdbah, paddr_high32(ctlr->rdba)); |
| csr32w(ctlr, Rdlen, Nrd * sizeof(struct rd)); |
| ctlr->rdh = ctlr->rdt = 0; |
| csr32w(ctlr, Rdh, 0); |
| csr32w(ctlr, Rdt, 0); |
| |
| /* to hell with interrupt moderation, we want low latency */ |
| csr32w(ctlr, Rdtr, 0); |
| csr32w(ctlr, Radv, 0); |
| |
| for (i = 0; i < Nrd; i++) { |
| bp = ctlr->rb[i]; |
| if (bp != NULL) { |
| ctlr->rb[i] = NULL; |
| freeb(bp); |
| } |
| } |
| i82563replenish(ctlr); |
| |
| if (type == i82575 || type == i82576 || type == i210) { |
| /* |
| * See comment above for Qenable. |
| * Could shuffle the code? |
| */ |
| r = csr32r(ctlr, Rxdctl) & ~(WthreshMASK | PthreshMASK); |
| csr32w(ctlr, Rxdctl, r | 2 << WthreshSHIFT | 2 << PthreshSHIFT); |
| } |
| |
| /* |
| * Don't enable checksum offload. In practice, it interferes with |
| * tftp booting on at least the 82575. |
| */ |
| csr32w(ctlr, Rxcsum, 0); |
| } |
| |
| static int i82563rim(void *ctlr) |
| { |
| return ((struct ctlr *)ctlr)->rim != 0; |
| } |
| |
| /* |
| * 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. |
| * |
| * Must be called with rd->errors == 0. |
| */ |
| static void ckcksums(struct ctlr *ctlr, struct rd *rd, struct block *bp) |
| { |
| if (0) { |
| if (rd->status & Ixsm) |
| return; |
| 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; |
| } |
| } |
| |
| static void i82563rproc(void *arg) |
| { |
| struct rd *rd; |
| struct block *bp; |
| struct ctlr *ctlr; |
| int rdh, rim, passed; |
| struct ether *edev; |
| |
| edev = arg; |
| ctlr = edev->ctlr; |
| i82563rxinit(ctlr); |
| csr32w(ctlr, Rctl, csr32r(ctlr, Rctl) | Ren); |
| |
| /* |
| * TODO(dcross): Work references to ctlrtab into this code. |
| */ |
| if (ctlr->type == i210) |
| csr32w(ctlr, Rxdctl, csr32r(ctlr, Rxdctl) | Qenable); |
| |
| for (;;) { |
| i82563replenish(ctlr); |
| i82563im(ctlr, Rxt0 | Rxo | Rxdmt0 | Rxseq | Ack); |
| ctlr->rsleep++; |
| rendez_sleep(&ctlr->rrendez, i82563rim, ctlr); |
| |
| rdh = ctlr->rdh; |
| passed = 0; |
| for (;;) { |
| rim = ctlr->rim; |
| ctlr->rim = 0; |
| rd = &ctlr->rdba[rdh]; |
| if (!(rd->status & Rdd)) |
| break; |
| |
| /* |
| * Accept eop packets with no errors. |
| */ |
| bp = ctlr->rb[rdh]; |
| if ((rd->status & Reop) && rd->errors == 0) { |
| bp->wp += rd->length; |
| bp->lim = bp->wp; /* lie like a dog. */ |
| if (0) |
| ckcksums(ctlr, rd, bp); |
| etheriq(edev, bp, 1); /* pass pkt upstream */ |
| passed++; |
| } else { |
| if (rd->status & Reop && rd->errors) |
| printd("%s: input packet error %#ux\n", |
| tname[ctlr->type], rd->errors); |
| freeb(bp); |
| } |
| ctlr->rb[rdh] = NULL; |
| |
| /* rd needs to be replenished to accept another pkt */ |
| rd->status = 0; |
| ctlr->rdfree--; |
| ctlr->rdh = rdh = NEXT_RING(rdh, Nrd); |
| /* |
| * if number of rds ready for packets is too low, |
| * set up the unready ones. |
| */ |
| if (ctlr->rdfree <= Nrd - 32 || (rim & Rxdmt0)) |
| i82563replenish(ctlr); |
| } |
| } |
| } |
| |
| static int i82563lim(void *ctlr) |
| { |
| return ((struct ctlr *)ctlr)->lim != 0; |
| } |
| |
| static int phynum(struct ctlr *ctlr) |
| { |
| if (ctlr->phynum < 0) |
| switch (ctlr->type) { |
| case i82577: |
| // case i82578: /* not yet implemented |
| // */ |
| case i82579: |
| case i217: |
| case i218: |
| ctlr->phynum = 2; /* pcie phy */ |
| break; |
| default: |
| ctlr->phynum = 1; /* gbe phy */ |
| break; |
| } |
| return ctlr->phynum; |
| } |
| |
| static unsigned int phyread(struct ctlr *ctlr, int reg) |
| { |
| unsigned int phy, i; |
| |
| if (reg >= 32) |
| iprint("phyread: reg %d >= 32\n", reg); |
| csr32w(ctlr, Mdic, |
| MDIrop | phynum(ctlr) << MDIpSHIFT | reg << MDIrSHIFT); |
| phy = 0; |
| for (i = 0; i < 64; i++) { |
| phy = csr32r(ctlr, Mdic); |
| if (phy & (MDIe | MDIready)) |
| break; |
| udelay(1); |
| } |
| if ((phy & (MDIe | MDIready)) != MDIready) |
| return ~0; |
| return phy & 0xffff; |
| } |
| |
| static unsigned int phywrite(struct ctlr *ctlr, int reg, uint16_t val) |
| { |
| unsigned int phy, i; |
| |
| if (reg >= 32) |
| iprint("phyread: reg %d >= 32\n", reg); |
| csr32w(ctlr, Mdic, |
| MDIwop | phynum(ctlr) << MDIpSHIFT | reg << MDIrSHIFT | val); |
| phy = 0; |
| for (i = 0; i < 64; i++) { |
| phy = csr32r(ctlr, Mdic); |
| if (phy & (MDIe | MDIready)) |
| break; |
| udelay(1); |
| } |
| if ((phy & (MDIe | MDIready)) != MDIready) |
| return ~0; |
| return 0; |
| } |
| |
| static uint32_t kmrnread(struct ctlr *ctlr, uint32_t reg_addr) |
| { |
| /* write register address */ |
| csr32w(ctlr, Kumctrlsta, |
| ((reg_addr << Kumctrlstaoffshift) & Kumctrlstaoff) | |
| Kumctrlstaren); |
| udelay(2); |
| /* read data */ |
| return csr32r(ctlr, Kumctrlsta); |
| } |
| |
| static void kmrnwrite(struct ctlr *ctlr, uint32_t reg_addr, uint16_t data) |
| { |
| csr32w(ctlr, Kumctrlsta, |
| ((reg_addr << Kumctrlstaoffshift) & Kumctrlstaoff) | data); |
| udelay(2); |
| } |
| |
| /* |
| * this is essentially black magic. we blindly follow the incantations |
| * prescribed by the god Intel: |
| * |
| * On ESB2, the MAC-to-PHY (Kumeran) interface must be configured after |
| * link is up before any traffic is sent. |
| * |
| * workaround DMA unit hang on I218 |
| * |
| * At 1Gbps link speed, one of the MAC's internal clocks can be stopped |
| * for up to 4us when entering K1 (a power mode of the MAC-PHY |
| * interconnect). If the MAC is waiting for completion indications for 2 |
| * DMA write requests into Host memory (e.g. descriptor writeback or Rx |
| * packet writing) and the indications occur while the clock is stopped, |
| * both indications will be missed by the MAC, causing the MAC to wait |
| * for the completion indications and be unable to generate further DMA |
| * write requests. This results in an apparent hardware hang. |
| * |
| * Work-around the bug by disabling the de-assertion of the clock request |
| * when 1Gbps link is acquired (K1 must be disabled while doing this). |
| * Also, set appropriate Tx re-transmission timeouts for 10 and 100-half |
| * link speeds to avoid Tx hangs. |
| */ |
| static void k1fix(struct ctlr *ctlr) |
| { |
| int txtmout; /* units of 10µs */ |
| uint32_t fextnvm6, status; |
| uint16_t reg; |
| struct ether *edev; |
| |
| edev = ctlr->edev; |
| fextnvm6 = csr32r(ctlr, Fextnvm6); |
| status = csr32r(ctlr, Status); |
| /* status speed bits are different on 217/8 than earlier ctlrs */ |
| if (edev->link && status & (Sspeed1000 >> 2)) { |
| reg = kmrnread(ctlr, Kumctrlstak1cfg); |
| kmrnwrite(ctlr, Kumctrlstak1cfg, reg & ~Kumctrlstak1enable); |
| udelay(10); |
| csr32w(ctlr, Fextnvm6, fextnvm6 | Fextnvm6reqpllclk); |
| kmrnwrite(ctlr, Kumctrlstak1cfg, reg); |
| ctlr->didk1fix = 1; |
| return; |
| } |
| /* else uncommon cases */ |
| |
| fextnvm6 &= ~Fextnvm6reqpllclk; |
| /* |
| * 217 manual claims not to have Frcdplx bit in status; |
| * 218 manual just omits the non-phy registers. |
| */ |
| if (!edev->link || (status & (Sspeed100 >> 2 | Frcdplx)) == |
| (Sspeed100 >> 2 | Frcdplx)) { |
| csr32w(ctlr, Fextnvm6, fextnvm6); |
| ctlr->didk1fix = 1; |
| return; |
| } |
| |
| /* access other page via phy addr 1 reg 31, then access reg 16-30 */ |
| phywrite(ctlr, Phypage, I217inbandctlpage << 5); |
| reg = phyread(ctlr, I217inbandctlreg) & ~I217inbandctllnkststxtmoutmask; |
| if (status & (Sspeed100 >> 2)) { /* 100Mb/s half-duplex? */ |
| txtmout = 5; |
| fextnvm6 &= ~Fextnvm6enak1entrycond; |
| } else { /* 10Mb/s */ |
| txtmout = 50; |
| fextnvm6 |= Fextnvm6enak1entrycond; |
| } |
| phywrite(ctlr, I217inbandctlreg, |
| reg | txtmout << I217inbandctllnkststxtmoutshift); |
| csr32w(ctlr, Fextnvm6, fextnvm6); |
| phywrite(ctlr, Phypage, 0 << 5); /* reset page to usual 0 */ |
| ctlr->didk1fix = 1; |
| } |
| |
| /* |
| * watch for changes of link state |
| */ |
| static void i82563lproc(void *v) |
| { |
| unsigned int phy, sp, a, phy79, prevlink; |
| struct ctlr *ctlr; |
| struct ether *edev; |
| |
| edev = v; |
| ctlr = edev->ctlr; |
| phy79 = 0; |
| switch (ctlr->type) { |
| case i82579: |
| case i82580: |
| case i217: |
| case i218: |
| case i350: |
| phy79 = 1; |
| break; |
| } |
| /* |
| * TODO(dcross): Extract PHY number from ctlrtab. |
| */ |
| if (ctlr->type == i82573 && phyread(ctlr, Phyier) != ~0) { |
| phy = phyread(ctlr, Phyier); |
| phywrite(ctlr, Phyier, phy | Lscie | Ancie | Spdie | Panie); |
| } else if (phy79 && phyread(ctlr, Phyier218) != ~0) { |
| phy = phyread(ctlr, Phyier218); |
| phywrite(ctlr, Phyier218, phy | Lscie218 | Ancie218 | Spdie218); |
| } |
| prevlink = 0; |
| for (;;) { |
| a = 0; |
| phy = phyread(ctlr, phy79 ? Phystat : Physsr); |
| if (phy == ~0) |
| goto next; |
| if (phy79) { |
| sp = (phy >> 8) & 3; |
| // a = phy & (ctlr->type == i218? Anfs: Ans); |
| a = phy & Anfs; |
| } else { |
| sp = (phy >> 14) & 3; |
| switch (ctlr->type) { |
| case i82563: |
| case i210: |
| a = phyread(ctlr, Phyisr) & Ane; /* a-n error */ |
| break; |
| case i82571: |
| case i82572: |
| case i82575: |
| case i82576: |
| a = phyread(ctlr, Phylhr) & Anf; /* a-n fault */ |
| sp = (sp - 1) & 3; |
| break; |
| } |
| } |
| if (a) /* enable & restart autoneg */ /* enable & restart |
| autoneg */ |
| phywrite(ctlr, Phyctl, |
| phyread(ctlr, Phyctl) | Ran | Ean); |
| edev->link = (phy & (phy79 ? Link : Rtlink)) != 0; |
| if (edev->link) { |
| ctlr->speeds[sp]++; |
| if (speedtab[sp]) |
| edev->mbps = speedtab[sp]; |
| if (prevlink == 0 && ctlr->type == i218) |
| k1fix(ctlr); /* link newly up: kludge away */ |
| netif_carrier_on(edev); |
| } else |
| ctlr->didk1fix = 0; /* force fix at next link up */ |
| prevlink = edev->link; |
| next: |
| ctlr->lim = 0; |
| i82563im(ctlr, Lsc); |
| ctlr->lsleep++; |
| rendez_sleep(&ctlr->lrendez, i82563lim, ctlr); |
| } |
| } |
| |
| static int return0(void *unused_void_p) |
| { |
| return 0; |
| } |
| |
| static void i82563tproc(void *v) |
| { |
| struct ether *edev; |
| struct ctlr *ctlr; |
| |
| edev = v; |
| ctlr = edev->ctlr; |
| for (;;) { |
| rendez_sleep(&ctlr->trendez, return0, 0); |
| i82563transmit(edev); |
| } |
| } |
| |
| /* |
| * controller is buggered; shock it back to life. |
| */ |
| static void restart(struct ctlr *ctlr) |
| { |
| if (0) { |
| static spinlock_t rstlock; |
| |
| qlock(&ctlr->tlock); |
| spin_lock_irqsave(&rstlock); |
| iprint("#l%d: resetting...", ctlr->edev->ctlrno); |
| i82563reset(ctlr); |
| /* [rt]xinit reset the ring indices */ |
| i82563txinit(ctlr); |
| i82563rxinit(ctlr); |
| csr32w(ctlr, Rctl, csr32r(ctlr, Rctl) | Ren); |
| spin_unlock_irqsave(&rstlock); |
| qunlock(&ctlr->tlock); |
| iprint("reset\n"); |
| } |
| } |
| |
| static void freemem(struct ctlr *ctlr) |
| { |
| kfree(ctlr->tb); |
| ctlr->tb = NULL; |
| kfree(ctlr->rb); |
| ctlr->rb = NULL; |
| kfree(ctlr->tdba); |
| ctlr->tdba = NULL; |
| kfree(ctlr->rdba); |
| ctlr->rdba = NULL; |
| } |
| |
| static void i82563attach(struct ether *edev) |
| { |
| ERRSTACK(2); |
| int i; |
| struct block *bp; |
| struct ctlr *ctlr; |
| char *lname, *rname, *tname; |
| |
| ctlr = edev->ctlr; |
| qlock(&ctlr->alock); |
| |
| if (ctlr->attached) { |
| qunlock(&ctlr->alock); |
| return; |
| } |
| |
| if (waserror()) { |
| freemem(ctlr); |
| qunlock(&ctlr->alock); |
| nexterror(); |
| } |
| |
| ctlr->alloc = kzmalloc( |
| Nrd * sizeof(struct rd) + Ntd * sizeof(struct td) + 255, MEM_WAIT); |
| if (ctlr->alloc == NULL) { |
| qunlock(&ctlr->alock); |
| error(ENOMEM, "i82563attach: error allocating rx/tx rings"); |
| } |
| ctlr->rdba = (struct rd *)ROUNDUP((uintptr_t)ctlr->alloc, 256); |
| ctlr->tdba = (struct td *)(ctlr->rdba + Nrd); |
| ctlr->rb = kzmalloc(Nrd * sizeof(struct block *), 0); |
| ctlr->tb = kzmalloc(Ntd * sizeof(struct block *), 0); |
| if (ctlr->rb == NULL || ctlr->tb == NULL) { |
| qunlock(&ctlr->alock); |
| error(ENOMEM, "i82563attach: error allocating rx/tx buffers"); |
| } |
| |
| ctlr->edev = edev; /* point back to Ether* */ |
| ctlr->attached = 1; |
| |
| lname = kzmalloc(KNAMELEN, MEM_WAIT); |
| snprintf(lname, KNAMELEN, "#l%dl", edev->ctlrno); |
| ktask(lname, i82563lproc, edev); |
| |
| rname = kzmalloc(KNAMELEN, MEM_WAIT); |
| snprintf(rname, KNAMELEN, "#l%dr", edev->ctlrno); |
| ktask(rname, i82563rproc, edev); |
| |
| tname = kzmalloc(KNAMELEN, MEM_WAIT); |
| snprintf(tname, KNAMELEN, "#l%dt", edev->ctlrno); |
| ktask(tname, i82563tproc, edev); |
| |
| i82563txinit(ctlr); |
| |
| qunlock(&ctlr->alock); |
| poperror(); |
| } |
| |
| static void i82563interrupt(struct hw_trapframe *unused_hw_trapframe, void *arg) |
| { |
| struct ctlr *ctlr; |
| struct ether *edev; |
| int icr, im, i, loops; |
| |
| edev = arg; |
| ctlr = edev->ctlr; |
| spin_lock_irqsave(&ctlr->imlock); |
| csr32w(ctlr, Imc, ~0); |
| im = ctlr->im; |
| loops = 0; |
| i = Nrd; /* don't livelock */ |
| for (icr = csr32r(ctlr, Icr); icr & ctlr->im && i-- > 0; |
| icr = csr32r(ctlr, Icr)) { |
| loops++; |
| if (icr & Lsc) { |
| im &= ~Lsc; |
| ctlr->lim = icr & Lsc; |
| rendez_wakeup(&ctlr->lrendez); |
| ctlr->lintr++; |
| } |
| if (icr & (Rxt0 | Rxo | Rxdmt0 | Rxseq | Ack)) { |
| ctlr->rim = icr & (Rxt0 | Rxo | Rxdmt0 | Rxseq | Ack); |
| im &= ~(Rxt0 | Rxo | Rxdmt0 | Rxseq | Ack); |
| rendez_wakeup(&ctlr->rrendez); |
| ctlr->rintr++; |
| } |
| if (icr & Txdw) { |
| im &= ~Txdw; |
| ctlr->tintr++; |
| rendez_wakeup(&ctlr->trendez); |
| } |
| } |
| ctlr->im = im; |
| csr32w(ctlr, Ims, im); |
| spin_unlock_irqsave(&ctlr->imlock); |
| } |
| |
| /* assume misrouted interrupts and check all controllers */ |
| static void i82575interrupt(struct hw_trapframe *unused_hw_trapframe, |
| void *unused_arg) |
| { |
| struct ctlr *ctlr; |
| |
| for (ctlr = i82563ctlrhead; ctlr != NULL && ctlr->edev != NULL; |
| ctlr = ctlr->next) |
| i82563interrupt(NULL, ctlr->edev); |
| } |
| |
| static int i82563detach0(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(1000 * 1000); |
| |
| /* |
| * Balance Rx/Tx packet buffer. |
| * No need to set PBA register unless using jumbo, defaults to 32KB |
| * for receive. If it is changed, then have to do a MAC reset, |
| * and need to do that at the the right time as it will wipe stuff. |
| * |
| * TODO(dcross): reconcile the following code with the above commentary. |
| */ |
| if (0) { |
| if (ctlr->rbsz > 8192 && ctlrtab[ctlr->type].flag & Fpba) { |
| ctlr->pba = csr32r(ctlr, Pba); |
| r = ctlr->pba >> 16; |
| r += ctlr->pba & 0xffff; |
| r >>= 1; |
| csr32w(ctlr, Pba, r); |
| } else if (ctlr->type == i82573 && ctlr->rbsz > 1514) |
| csr32w(ctlr, Pba, 14); |
| } |
| ctlr->pba = csr32r(ctlr, Pba); |
| |
| /* set packet buffer size if present. no effect until soft reset. */ |
| switch (ctlr->type) { |
| case i82566: |
| case i82567: |
| case i217: |
| ctlr->pbs = 16; /* in KB */ |
| csr32w(ctlr, Pbs, ctlr->pbs); |
| break; |
| case i218: |
| // after pxe or 9fat boot, pba is always 0xe0012 on i218 => 32K |
| ctlr->pbs = (ctlr->pba >> 16) + (uint16_t)ctlr->pba; |
| csr32w(ctlr, Pbs, ctlr->pbs); |
| break; |
| } |
| |
| r = csr32r(ctlr, Ctrl); |
| if (ctlr->type == i82566 || ctlr->type == i82567 || |
| ctlr->type == i82579) |
| r |= Phyrst; |
| csr32w(ctlr, Ctrl, Devrst | r); |
| udelay(1000); |
| for (timeo = 0; timeo < 1000; timeo++) { |
| if (!(csr32r(ctlr, Ctrl) & Devrst)) |
| break; |
| udelay(1000); |
| } |
| if (csr32r(ctlr, Ctrl) & Devrst) |
| return -1; |
| |
| r = csr32r(ctlr, Ctrlext); |
| csr32w(ctlr, Ctrlext, r | Eerst); |
| udelay(1000); |
| for (timeo = 0; timeo < 1000; timeo++) { |
| if (!(csr32r(ctlr, Ctrlext) & Eerst)) |
| break; |
| udelay(1000); |
| } |
| if (csr32r(ctlr, Ctrlext) & Eerst) |
| return -1; |
| |
| csr32w(ctlr, Imc, ~0); |
| udelay(1000); |
| for (timeo = 0; timeo < 1000; timeo++) { |
| if (!csr32r(ctlr, Icr)) |
| break; |
| udelay(1000); |
| } |
| if (csr32r(ctlr, Icr)) |
| return -1; |
| |
| csr32w(ctlr, Ctrl, Slu | csr32r(ctlr, Ctrl)); |
| return 0; |
| } |
| |
| static int i82563detach(struct ctlr *ctlr) |
| { |
| int r; |
| static spinlock_t detlck; |
| |
| spin_lock_irqsave(&detlck); |
| r = i82563detach0(ctlr); |
| spin_unlock_irqsave(&detlck); |
| return r; |
| } |
| |
| static void i82563shutdown(struct ether *ether) |
| { |
| i82563detach(ether->ctlr); |
| } |
| |
| static uint16_t eeread(struct ctlr *ctlr, int adr) |
| { |
| uint32_t n; |
| |
| csr32w(ctlr, Eerd, EEstart | adr << 2); |
| n = 1000000; |
| while (n > 0 && (csr32r(ctlr, Eerd) & EEdone) == 0) |
| n--; |
| if (n == 0) |
| panic("i82563: eeread stuck"); |
| return csr32r(ctlr, Eerd) >> 16; |
| } |
| |
| /* load eeprom into ctlr */ |
| static int eeload(struct ctlr *ctlr) |
| { |
| uint16_t sum; |
| int data, adr; |
| |
| sum = 0; |
| for (adr = 0; adr < 0x40; adr++) { |
| data = eeread(ctlr, adr); |
| ctlr->eeprom[adr] = data; |
| sum += data; |
| } |
| return sum; |
| } |
| |
| static int fcycle(struct ctlr *unused_ctlr_p, struct flash *f) |
| { |
| uint16_t s, i; |
| |
| s = f->reg[Fsts]; |
| if ((s & Fvalid) == 0) |
| return -1; |
| f->reg[Fsts] |= Fcerr | Ael; |
| for (i = 0; i < 10; i++) { |
| if ((s & Scip) == 0) /* spi cycle done? */ |
| return 0; |
| udelay(1000); |
| s = f->reg[Fsts]; |
| } |
| return -1; |
| } |
| |
| static int fread(struct ctlr *ctlr, struct flash *f, int ladr) |
| { |
| uint16_t s; |
| uint32_t n; |
| |
| udelay(1000); |
| if (fcycle(ctlr, f) == -1) |
| return -1; |
| f->reg[Fsts] |= Fdone; |
| f->reg32[Faddr] = ladr; |
| |
| /* setup flash control register */ |
| s = f->reg[Fctl] & ~(0x1f << 8); |
| s |= (2 - 1) << 8; /* 2 bytes */ |
| s &= ~(2 * Flcycle); /* read */ |
| f->reg[Fctl] = s | Fgo; |
| |
| n = 1000000; |
| while (n > 0 && (f->reg[Fsts] & Fdone) == 0) |
| n--; |
| if (n == 0) |
| panic("i82563: fread stuck"); |
| if (f->reg[Fsts] & (Fcerr | Ael)) |
| return -1; |
| return f->reg32[Fdata] & 0xffff; |
| } |
| |
| /* load flash into ctlr */ |
| static int fload(struct ctlr *ctlr) |
| { |
| uint32_t data, r, adr; |
| uint16_t sum; |
| uintptr_t mmio_paddr; |
| struct pci_device *pcidev = ctlr->pcidev; |
| struct flash f; |
| |
| mmio_paddr = pcidev->bar[1].mmio_base32 ? pcidev->bar[1].mmio_base32 |
| : pcidev->bar[1].mmio_base64; |
| f.reg = (void *)vmap_pmem(mmio_paddr, pcidev->bar[1].mmio_sz); |
| if (f.reg == NULL) |
| return -1; |
| f.reg32 = (void *)f.reg; |
| f.base = f.reg32[Bfpr] & 0x1fff; |
| f.lim = (f.reg32[Bfpr] >> 16) & 0x1fff; |
| if (csr32r(ctlr, Eec) & Sec1val) |
| f.base += (f.lim + 1 - f.base) >> 1; |
| r = f.base << 12; |
| |
| sum = 0; |
| for (adr = 0; adr < 0x40; adr++) { |
| data = fread(ctlr, &f, r + adr * 2); |
| if (data == -1) |
| break; |
| ctlr->eeprom[adr] = data; |
| sum += data; |
| } |
| vunmap_vmem((uintptr_t)f.reg, pcidev->bar[1].mmio_sz); |
| return sum; |
| } |
| |
| static int i82563reset(struct ctlr *ctlr) |
| { |
| int i, r, type; |
| |
| /* |
| * TODO(dcross): Integrate ctlrtab references into this code. |
| */ |
| if (i82563detach(ctlr)) { |
| iprint("82563 reset: detach failed\n"); |
| return -1; |
| } |
| type = ctlr->type; |
| if (ctlr->ra[Eaddrlen - 1] != 0) |
| goto macset; |
| switch (type) { |
| case i82566: |
| case i82567: |
| case i82577: |
| // case i82578: /* not yet implemented */ |
| case i82579: |
| case i217: |
| case i218: |
| r = fload(ctlr); |
| break; |
| default: |
| r = eeload(ctlr); |
| break; |
| } |
| if (r != 0 && r != 0xBABA) { |
| printd("%s: bad EEPROM checksum - %#.4ux\n", tname[type], r); |
| // return -1; |
| } |
| |
| /* set mac addr */ |
| for (i = 0; i < Eaddrlen / 2; i++) { |
| ctlr->ra[2 * i] = ctlr->eeprom[Ea + i]; |
| ctlr->ra[2 * i + 1] = ctlr->eeprom[Ea + i] >> 8; |
| } |
| /* ea ctlr[1] = ea ctlr[0]+1 */ |
| ctlr->ra[5] += (csr32r(ctlr, Status) & Lanid) >> 2; |
| /* |
| * zero other mac addresses.` |
| * AV bits should be zeroed by master reset & there may only be 11 |
| * other registers on e.g., the i217. |
| */ |
| for (i = 1; i < 12; i++) { /* `12' used to be `16' here */ |
| csr32w(ctlr, Ral + i * 8, 0); |
| csr32w(ctlr, Rah + i * 8, 0); |
| } |
| memset(ctlr->mta, 0, sizeof(ctlr->mta)); |
| macset: |
| /* low mac addr */ |
| csr32w(ctlr, Ral, |
| ctlr->ra[3] << 24 | ctlr->ra[2] << 16 | ctlr->ra[1] << 8 | |
| ctlr->ra[0]); |
| /* address valid | high mac addr */ |
| csr32w(ctlr, Rah, 0x80000000 | ctlr->ra[5] << 8 | ctlr->ra[4]); |
| |
| /* populate multicast table */ |
| for (i = 0; i < mcbitstolongs(mcastbits(ctlr)); i++) |
| csr32w(ctlr, Mta + i * 4, ctlr->mta[i]); |
| |
| /* |
| * Does autonegotiation affect this manual setting? |
| * The correct values here should depend on the PBA value |
| * and maximum frame length, no? |
| */ |
| /* fixed flow control ethernet address 0x0180c2000001 */ |
| csr32w(ctlr, Fcal, 0x00C28001); |
| csr32w(ctlr, Fcah, 0x0100); |
| if (type != i82579 && type != i210 && type != i217 && type != i218) |
| /* flow control type, dictated by Intel */ |
| csr32w(ctlr, Fct, 0x8808); |
| csr32w(ctlr, Fcttv, 0x0100); /* for XOFF frame */ |
| // ctlr->fcrtl = 0x00002000; /* rcv low water mark: 8KB */ |
| /* rcv high water mark: 16KB, < rcv buffer in PBA & RXA */ |
| // ctlr->fcrth = 0x00004000; |
| ctlr->fcrtl = ctlr->fcrth = 0; |
| csr32w(ctlr, Fcrtl, ctlr->fcrtl); |
| csr32w(ctlr, Fcrth, ctlr->fcrth); |
| return 0; |
| } |
| |
| static void i82563pci(void) |
| { |
| int type; |
| uintptr_t io; |
| void *mem; |
| struct pci_device *p; |
| struct ctlr *ctlr; |
| |
| p = NULL; |
| STAILQ_FOREACH (p, &pci_devices, all_dev) { |
| if (p->ven_id != 0x8086) |
| continue; |
| switch (p->dev_id) { |
| default: |
| continue; |
| case 0x1096: |
| case 0x10ba: |
| case 0x1098: /* serdes; not seen */ |
| case 0x10bb: /* serdes */ |
| type = i82563; |
| break; |
| case 0x1049: /* mm */ |
| case 0x104a: /* dm */ |
| case 0x104b: /* dc */ |
| case 0x104d: /* mc */ |
| case 0x10bd: /* dm */ |
| case 0x294c: /* dc-2 */ |
| type = i82566; |
| break; |
| case 0x10de: /* lm-3 */ |
| case 0x10df: /* lf ich10 */ |
| case 0x10e5: /* lm ich9 */ |
| case 0x10f5: /* lm-2 */ |
| type = i82567; |
| break; |
| case 0x10bf: /* lf ich9m */ |
| case 0x10cb: /* v ich9m */ |
| case 0x10cd: /* lf ich10 */ |
| case 0x10ce: /* v ich10 */ |
| case 0x10cc: /* lm ich10 */ |
| type = i82567m; |
| break; |
| case 0x105e: /* eb */ |
| case 0x105f: /* eb */ |
| case 0x1060: /* eb */ |
| case 0x10a4: /* eb */ |
| case 0x10a5: /* eb fiber */ |
| case 0x10bc: /* eb */ |
| case 0x10d9: /* eb serdes */ |
| case 0x10da: /* eb serdes “ophir” */ |
| type = i82571; |
| break; |
| case 0x107d: /* eb copper */ |
| case 0x107e: /* ei fiber */ |
| case 0x107f: /* ei */ |
| case 0x10b9: /* sic, 82572gi */ |
| type = i82572; |
| break; |
| case 0x108b: /* v */ |
| case 0x108c: /* e (iamt) */ |
| case 0x109a: /* l */ |
| type = i82573; |
| break; |
| case 0x10d3: /* l */ |
| type = i82574; |
| break; |
| case 0x10a7: /* 82575eb: one of a pair of controllers */ |
| case 0x10a9: /* fiber/serdes */ |
| type = i82575; |
| break; |
| case 0x10c9: /* 82576 copper */ |
| case 0x10e6: /* 82576 fiber */ |
| case 0x10e7: /* 82576 serdes */ |
| case 0x150d: /* backplane */ |
| type = i82576; |
| break; |
| case 0x10ea: /* 82577lm */ |
| type = i82577; |
| break; |
| case 0x10eb: /* lm “calpella” */ |
| type = i82577m; |
| break; |
| case 0x1502: /* 82579lm */ |
| case 0x1503: /* 82579v */ |
| type = i82579; |
| break; |
| case 0x10f0: /* dm “king's creek” */ |
| type = i82578m; |
| break; |
| case 0x150e: /* “barton hills” */ |
| case 0x150f: /* fiber */ |
| case 0x1510: /* backplane */ |
| case 0x1511: /* sfp */ |
| case 0x1516: |
| type = i82580; |
| break; |
| case 0x1506: /* v */ |
| type = i82583; |
| break; |
| case 0x1533: /* i210-t1 */ |
| case 0x1534: /* i210 */ |
| case 0x1536: /* i210-fiber */ |
| case 0x1537: /* i210-backplane */ |
| case 0x1538: |
| case 0x1539: /* i211 */ |
| case 0x157b: /* i210 */ |
| case 0x157c: /* i210 */ |
| type = i210; |
| break; |
| case 0x153a: /* i217-lm */ |
| case 0x153b: /* i217-v */ |
| type = i217; |
| break; |
| case 0x15a0: /* i218-lm */ |
| case 0x15a1: /* i218-v */ |
| case 0x15a2: /* i218-lm */ |
| case 0x15a3: /* i218-v */ |
| type = i218; |
| break; |
| case 0x151f: /* “powerville” eeprom-less */ |
| case 0x1521: /* copper */ |
| case 0x1522: /* fiber */ |
| case 0x1523: /* serdes */ |
| case 0x1524: /* sgmii */ |
| type = i350; |
| break; |
| } |
| |
| io = p->bar[0].mmio_base32 ? p->bar[0].mmio_base32 |
| : p->bar[0].mmio_base64; |
| mem = (void *)vmap_pmem(io, p->bar[0].mmio_sz); |
| if (mem == NULL) { |
| printd("%s: can't map %.8lux\n", tname[type], io); |
| continue; |
| } |
| ctlr = kzmalloc(sizeof(struct ctlr), 0); |
| if (ctlr == NULL) { |
| vunmap_vmem((uintptr_t)mem, p->bar[0].mmio_sz); |
| error(ENOMEM, "i82563pci: alloc for ctlr failed"); |
| } |
| ctlr->mmio_paddr = io; |
| ctlr->rbsz = ctlrtab[type].mtu; |
| ctlr->pcidev = p; |
| ctlr->type = type; |
| ctlr->nic = mem; |
| ctlr->phynum = -1; /* not yet known */ |
| |
| qlock_init(&ctlr->alock); |
| spinlock_init_irqsave(&ctlr->imlock); |
| rendez_init(&ctlr->lrendez); |
| qlock_init(&ctlr->slock); |
| rendez_init(&ctlr->rrendez); |
| rendez_init(&ctlr->trendez); |
| qlock_init(&ctlr->tlock); |
| |
| pci_set_bus_master(p); |
| if (i82563reset(ctlr)) { |
| vunmap_vmem((uintptr_t)mem, p->bar[0].mmio_sz); |
| kfree(ctlr); |
| continue; |
| } |
| |
| if (i82563ctlrhead != NULL) |
| i82563ctlrtail->next = ctlr; |
| else |
| i82563ctlrhead = ctlr; |
| i82563ctlrtail = ctlr; |
| } |
| } |
| |
| static int pnp(struct ether *edev, int type) |
| { |
| struct ctlr *ctlr; |
| static int done; |
| |
| if (!done) { |
| i82563pci(); |
| done = 1; |
| } |
| |
| /* |
| * Any adapter matches if no edev->port is supplied, |
| * otherwise the ports must match. |
| */ |
| for (ctlr = i82563ctlrhead; ctlr != NULL; ctlr = ctlr->next) { |
| if (ctlr->active) |
| continue; |
| if (type != Iany && ctlr->type != type) |
| continue; |
| if (edev->port == 0 || edev->port == ctlr->mmio_paddr) { |
| ctlr->active = 1; |
| break; |
| } |
| } |
| if (ctlr == NULL) |
| return -1; |
| |
| edev->ctlr = ctlr; |
| strlcpy(edev->drv_name, "i82563", KNAMELEN); |
| ctlr->edev = edev; /* point back to Ether* */ |
| edev->port = ctlr->mmio_paddr; |
| edev->irq = ctlr->pcidev->irqline; |
| edev->tbdf = pci_to_tbdf(ctlr->pcidev); |
| edev->mbps = 1000; |
| edev->max_mtu = ctlr->rbsz - ETHERHDRSIZE; |
| edev->mtu = edev->mtu; |
| 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 = i82563attach; |
| edev->transmit = i82563transmit; |
| edev->ifstat = i82563ifstat; |
| edev->ctl = i82563ctl; |
| |
| edev->arg = edev; |
| edev->promiscuous = i82563promiscuous; |
| edev->shutdown = i82563shutdown; |
| edev->multicast = i82563multicast; |
| |
| register_irq(edev->irq, |
| ctlr->type == i82575 ? i82575interrupt : i82563interrupt, |
| edev, edev->tbdf); |
| return 0; |
| } |
| |
| static int anypnp(struct ether *e) |
| { |
| return pnp(e, Iany); |
| } |
| |
| static int i82563pnp(struct ether *e) |
| { |
| return pnp(e, i82563); |
| } |
| |
| static int i82566pnp(struct ether *e) |
| { |
| return pnp(e, i82566); |
| } |
| |
| static int i82571pnp(struct ether *e) |
| { |
| return pnp(e, i82571); |
| } |
| |
| static int i82572pnp(struct ether *e) |
| { |
| return pnp(e, i82572); |
| } |
| |
| static int i82573pnp(struct ether *e) |
| { |
| return pnp(e, i82573); |
| } |
| |
| static int i82575pnp(struct ether *e) |
| { |
| return pnp(e, i82575); |
| } |
| |
| static int i82579pnp(struct ether *e) |
| { |
| return pnp(e, i82579); |
| } |
| |
| static int i210pnp(struct ether *e) |
| { |
| return pnp(e, i210); |
| } |
| |
| static int i217pnp(struct ether *e) |
| { |
| return pnp(e, i217); |
| } |
| |
| static int i218pnp(struct ether *e) |
| { |
| return pnp(e, i218); |
| } |
| |
| linker_func_3(ether82563link) |
| { |
| /* recognise lots of model numbers for debugging assistance */ |
| addethercard("i82563", i82563pnp); |
| addethercard("i82566", i82566pnp); |
| addethercard("i82571", i82571pnp); |
| addethercard("i82572", i82572pnp); |
| addethercard("i82573", i82573pnp); |
| addethercard("i82575", i82575pnp); |
| addethercard("i82579", i82579pnp); |
| addethercard("i210", i210pnp); |
| addethercard("i217", i217pnp); |
| addethercard("i218", i218pnp); |
| addethercard("igbepcie", anypnp); |
| } |