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