| /* |
| * 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 to work in Akaros. */ |
| /* |
| * Intel 8256[367], 8257[1-9], 8258[03], i21[01], i350 |
| * Gigabit Ethernet PCI-Express Controllers |
| * Coraid EtherDrive® hba |
| */ |
| #include <vfs.h> |
| #include <kfs.h> |
| #include <slab.h> |
| #include <kmalloc.h> |
| #include <kref.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <assert.h> |
| #include <error.h> |
| #include <cpio.h> |
| #include <pmap.h> |
| #include <smp.h> |
| #include <ip.h> |
| |
| /* |
| * note: the 82575, 82576 and 82580 are operated using registers aliased |
| * to the 82563-style architecture. many features seen in the 82598 |
| * are also seen in the 82575 part. |
| */ |
| |
| enum { |
| /* General */ |
| |
| Ctrl = 0x0000, /* Device Control */ |
| Status = 0x0008, /* Device Status */ |
| Eec = 0x0010, /* EEPROM/Flash Control/Data */ |
| Eerd = 0x0014, /* EEPROM Read */ |
| Ctrlext = 0x0018, /* Extended Device Control */ |
| Fla = 0x001c, /* Flash Access */ |
| Mdic = 0x0020, /* MDI Control */ |
| Fcal = 0x0028, /* Flow Control Address Low */ |
| Fcah = 0x002c, /* Flow Control Address High */ |
| Fct = 0x0030, /* Flow Control Type */ |
| Kumctrlsta = 0x0034, /* Kumeran Control and Status Register */ |
| Connsw = 0x0034, /* copper / fiber switch control; 82575/82576 */ |
| 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 */ |
| Ivar = 0x00e4, /* Ivar: interrupt allocation */ |
| Eitr = 0x1680, /* Extended itr; 82575/6 80 only */ |
| P3gio = 0x5b00, /* */ |
| Pbaclr = 0x5b68, /* clear msi-x pba */ |
| |
| /* Receive */ |
| |
| Rctl = 0x0100, /* Control */ |
| Ert = 0x2008, /* Early Receive Threshold (573[EVL], 82578 only) */ |
| Fcrtl = 0x2160, /* Flow Control RX Threshold Low */ |
| Fcrth = 0x2168, /* Flow Control Rx Threshold High */ |
| Psrctl = 0x2170, /* Packet Split Receive Control */ |
| Drxmxod = 0x2540, /* dma max outstanding bytes (82575) */ |
| 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 */ |
| 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 */ |
| |
| /* 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 */ |
| |
| /* 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 */ |
| Lrst = 1 << 3, /* link reset */ |
| Slu = 1 << 6, /* Set Link Up */ |
| Devrst = 1 << 26, /* Device Reset */ |
| Rfce = 1 << 27, /* Receive Flow Control Enable */ |
| Tfce = 1 << 28, /* Transmit Flow Control 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 */ |
| Eerst = 1 << 13, /* EEPROM Reset */ |
| Linkmode = 3 << 22, /* linkmode */ |
| Internalphy = 0 << 22, /* " internal phy (copper) */ |
| Sgmii = 2 << 22, /* " sgmii */ |
| Serdes = 3 << 22, /* " serdes */ |
| Eiame = 1 << 24, /* extended auto mask enable */ |
| Iame = 1 << 27, /* auto mask enable */ |
| Pbasup = 1 << 31, /* msi-x pba support */ |
| }; |
| |
| enum { |
| /* Connsw */ |
| Enrgirq = 1 << 2, /* interrupt on power detect (enrgsrc) */ |
| }; |
| |
| enum { /* EEPROM content offsets */ |
| Ea = 0x00, /* Ethernet Address */ |
| }; |
| |
| 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 */ |
| Phyctl = 0, /* phy ctl register */ |
| Phyisr = 19, /* 82563 phy interrupt status register */ |
| Phylhr = 19, /* 8257[12] link health register */ |
| Physsr = 17, /* phy secondary status register */ |
| Phyprst = 193 << 8 | 17, /* 8256[34] phy port reset */ |
| Phyier = 18, /* 82573 phy interrupt enable register */ |
| Phypage = 22, /* 8256[34] page register */ |
| Phystat = 26, /* 82580 phy status */ |
| Phyapage = 29, |
| Phy79page = 31, /* 82579 phy page register (all pages) */ |
| |
| Rtlink = 1 << 10, /* realtime link status */ |
| Phyan = 1 << 11, /* phy has autonegotiated */ |
| |
| /* Phyctl bits */ |
| Ran = 1 << 9, /* restart auto negotiation */ |
| Ean = 1 << 12, /* enable auto negotiation */ |
| |
| /* Phyprst bits */ |
| Prst = 1 << 0, /* reset the port */ |
| |
| /* 82573 Phyier bits */ |
| Lscie = 1 << 10, /* link status changed ie */ |
| Ancie = 1 << 11, /* auto negotiation complete ie */ |
| Spdie = 1 << 14, /* speed changed ie */ |
| Panie = 1 << 15, /* phy auto negotiation error ie */ |
| |
| /* Phylhr/Phyisr bits */ |
| Anf = 1 << 6, /* lhr: auto negotiation fault */ |
| Ane = 1 << 15, /* isr: auto negotiation error */ |
| |
| /* 82580 Phystat bits */ |
| Ans = 1 << 14 | 1 << 15, /* 82580 autoneg. status */ |
| Link = 1 << 6, /* 82580 Link */ |
| |
| /* Rxcw builtin serdes */ |
| Anc = 1 << 31, |
| Rxsynch = 1 << 30, |
| Rxcfg = 1 << 29, |
| Rxcfgch = 1 << 28, |
| Rxcfgbad = 1 << 27, |
| Rxnc = 1 << 26, |
| |
| /* Txcw */ |
| Txane = 1 << 31, |
| Txcfg = 1 << 30, |
| }; |
| |
| enum { /* fiber (pcs) interface */ |
| Pcsctl = 0x4208, /* pcs control */ |
| Pcsstat = 0x420c, /* pcs status */ |
| |
| /* Pcsctl bits */ |
| Pan = 1 << 16, /* autonegotiate */ |
| Prestart = 1 << 17, /* restart an (self clearing) */ |
| |
| /* Pcsstat bits */ |
| Linkok = 1 << 0, /* link is okay */ |
| Andone = 1 << 16, /* an phase is done see below for success */ |
| Anbad = 1 << 19 | 1 << 20, /* Anerror | Anremfault */ |
| }; |
| |
| 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; !82575/6/80 only */ |
| Rxdw = 0x00000080, /* Rdesc write back; 82575/6/80 only */ |
| Mdac = 0x00000200, /* MDIO Access Completed */ |
| Rxcfgset = 0x00000400, /* Receiving /C/ ordered sets */ |
| Ack = 0x00020000, /* Receive ACK frame */ |
| Omed = 1 << 20, /* media change; pcs interface */ |
| }; |
| |
| 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 */ |
| 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, /* Flexable 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; not 82575 */ |
| Enable = 0x02000000, |
| }; |
| |
| enum { /* Rxcsum */ |
| Ipofl = 0x0100, /* IP Checksum Off-load Enable */ |
| Tuofl = 0x0200, /* TCP/UDP Checksum Off-load Enable */ |
| }; |
| |
| typedef struct Rd { /* Receive Descriptor */ |
| uint32_t 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 { /* Transmit Descriptor */ |
| uint32_t addr[2]; /* Data */ |
| uint32_t control; |
| uint32_t status; |
| } Td; |
| |
| 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, |
| }; |
| |
| typedef struct { |
| uint16_t *reg; |
| uint32_t *reg32; |
| unsigned int base; |
| unsigned int lim; |
| } Flash; |
| |
| 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 */ |
| }; |
| |
| enum { |
| Nrd = 256, /* power of two */ |
| Ntd = 256, /* power of two */ |
| Rbalign = 16, /* rx buffer alignment */ |
| Npool = 10, |
| }; |
| |
| enum { |
| i82563, |
| i82566, |
| i82567, |
| i82567m, |
| i82571, |
| i82572, |
| i82573, |
| i82574, |
| i82575, |
| i82576, |
| i82577, |
| i82577m, |
| i82578, |
| i82578m, |
| i82579, |
| i82580, |
| i82583, |
| i210, |
| i217, |
| i350, |
| Nctlrtype, |
| }; |
| |
| enum { |
| Fload = 1 << 0, |
| Fert = 1 << 1, |
| F75 = 1 << 2, |
| Fpba = 1 << 3, |
| Fflashea = 1 << 4, |
| F79phy = 1 << 5, |
| Fnofct = 1 << 6, |
| }; |
| |
| typedef struct Ctlrtype Ctlrtype; |
| struct Ctlrtype { |
| int type; |
| int mtu; |
| int phyno; |
| char *name; |
| int flag; |
| }; |
| |
| static Ctlrtype cttab[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}, |
| }; |
| |
| typedef void (*Freefn) (struct block *); |
| |
| typedef struct ctlr Ctlr; |
| struct ctlr { |
| uintptr_t mmio_paddr; |
| struct pci_device *pcidev; |
| struct ctlr *next; |
| int active; |
| int type; |
| uint16_t eeprom[0x40]; |
| |
| qlock_t alock; /* attach */ |
| void *alloc; /* receive/transmit descriptors */ |
| int nrd; |
| int ntd; |
| unsigned int rbsz; |
| |
| uint32_t *nic; |
| spinlock_t imlock; |
| int im; /* interrupt mask */ |
| |
| struct rendez lrendez; |
| int lim; |
| |
| qlock_t slock; |
| uint32_t 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]; |
| unsigned int phyerrata; |
| |
| 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 */ |
| 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; |
| int tbusy; |
| 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; |
| |
| uint32_t 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 *i82563ctlr; |
| |
| 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-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 cttab[c->type].name; |
| } |
| |
| static long i82563ifstat(struct ether* edev, void* a, long n, uint32_t offset) |
| { |
| char *s, *p, *e, *stat; |
| int i, r; |
| uint64_t tuvl, ruvl; |
| struct ctlr *ctlr; |
| |
| ctlr = edev->ctlr; |
| qlock(&ctlr->slock); |
| p = s = kzmalloc(READSTR, 0); |
| e = p + READSTR; |
| |
| for (i = 0; i < Nstatistics; i++) { |
| r = csr32r(ctlr, Statistics + i * 4); |
| if ((stat = 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; |
| 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, "rdtr: %ud\n", ctlr->rdtr); |
| p = seprintf(p, e, "radv: %ud\n", ctlr->radv); |
| 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, "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)); |
| |
| n = readstr(offset, a, n, s); |
| kfree(s); |
| qunlock(&ctlr->slock); |
| |
| 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); |
| rctl &= ~MoMASK; |
| if (on) |
| rctl |= Upe | Mpe; |
| else |
| rctl &= ~(Upe | Mpe); |
| csr32w(ctlr, Rctl, rctl); |
| } |
| |
| static void i82563multicast(void *arg, uint8_t * addr, int on) |
| { |
| int bit, x; |
| struct ctlr *ctlr; |
| struct ether *edev; |
| |
| edev = arg; |
| ctlr = edev->ctlr; |
| |
| x = addr[5] >> 1; |
| if (ctlr->type == i82566) |
| x &= 31; |
| if (ctlr->type == i210 || ctlr->type == i217) |
| x &= 15; |
| 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 (on) |
| ctlr->mta[x] |= 1 << bit; |
| // else |
| // ctlr->mta[x] &= ~(1<<bit); |
| |
| csr32w(ctlr, Mta + x * 4, ctlr->mta[x]); |
| } |
| |
| 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; |
| uint32_t r; |
| struct block *b; |
| |
| if (cttab[ctlr->type].flag & F75) |
| csr32w(ctlr, Tctl, 0x0F << CtSHIFT | Psp); |
| else |
| csr32w(ctlr, Tctl, 0x0F << CtSHIFT | Psp | 66 << ColdSHIFT | Mulr); |
| csr32w(ctlr, Tipg, 6 << 20 | 8 << 10 | 8); /* yb sez: 0x702008 */ |
| 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 ((b = ctlr->tb[i]) != NULL) { |
| ctlr->tb[i] = NULL; |
| freeb(b); |
| } |
| memset(&ctlr->tdba[i], 0, sizeof(Td)); |
| } |
| csr32w(ctlr, Tidv, 128); |
| csr32w(ctlr, Tadv, 64); |
| csr32w(ctlr, Tctl, csr32r(ctlr, Tctl) | Ten); |
| r = csr32r(ctlr, Txdctl) & ~WthreshMASK; |
| r |= 4 << WthreshSHIFT | 4 << PthreshSHIFT; |
| if (cttab[ctlr->type].flag & F75) |
| r |= Enable; |
| csr32w(ctlr, Txdctl, r); |
| } |
| |
| static int i82563cleanup(struct ether *e) |
| { |
| struct block *b; |
| struct ctlr *c; |
| int tdh, m, n; |
| |
| c = e->ctlr; |
| tdh = c->tdh; |
| m = c->ntd; |
| while (c->tdba[n = NEXT_RING(tdh, m)].status & Tdd) { |
| tdh = n; |
| if ((b = c->tb[tdh]) != NULL) { |
| c->tb[tdh] = NULL; |
| freeb(b); |
| } else |
| printk("#l%d: %s tx underrun! %d\n", e->ctlrno, cname(c), n); |
| c->tdba[tdh].status = 0; |
| } |
| |
| return c->tdh = tdh; |
| } |
| |
| static int notrim(void *v) |
| { |
| struct ctlr *c; |
| |
| c = v; |
| return (c->im & Txdw) == 0; |
| } |
| |
| static void i82563tproc(void *v) |
| { |
| Td *td; |
| struct block *bp; |
| struct ether *edev; |
| struct ctlr *ctlr; |
| int tdh, tdt, m; |
| |
| edev = v; |
| ctlr = edev->ctlr; |
| tdt = ctlr->tdt; |
| m = ctlr->ntd; |
| |
| i82563txinit(ctlr); |
| |
| for (;;) { |
| tdh = i82563cleanup(edev); |
| |
| if (NEXT_RING(tdt, m) == tdh) { |
| ctlr->txdw++; |
| i82563im(ctlr, Txdw); |
| rendez_sleep(&ctlr->trendez, notrim, ctlr); |
| continue; |
| } |
| bp = qbread(edev->oq, 100000); |
| if (!bp) { |
| /* this only happens if the q is closed. qbread can also throw, |
| * btw, which we don't handle. */ |
| warn("i350 tproc failed to get a block, aborting!"); |
| return; |
| } |
| 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, m); |
| wmb_f(); |
| csr32w(ctlr, Tdt, tdt); |
| } |
| } |
| |
| static int i82563replenish(struct ctlr *ctlr, int maysleep) |
| { |
| unsigned int rdt, m; |
| struct block *bp; |
| Rd *rd; |
| int retval = 0; |
| |
| rdt = ctlr->rdt; |
| m = ctlr->nrd; |
| for (; NEXT_RING(rdt, m) != ctlr->rdh; rdt = NEXT_RING(rdt, m)) { |
| rd = &ctlr->rdba[rdt]; |
| if (ctlr->rb[rdt] != NULL) { |
| printk("%s: tx overrun\n", cname(ctlr)); |
| break; |
| } |
| bp = iallocb(ctlr->rbsz + Rbalign); |
| if (bp == NULL) { |
| /* could do a sleeping allocb btw, we're a ktask */ |
| 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++; |
| } |
| if (ctlr->rdt != rdt) { |
| ctlr->rdt = rdt; |
| wmb_f(); |
| csr32w(ctlr, Rdt, rdt); |
| } |
| return retval; |
| } |
| |
| static void i82563rxinit(struct ctlr *ctlr) |
| { |
| int i; |
| struct block *bp; |
| |
| if (ctlr->rbsz <= 2048) |
| csr32w(ctlr, Rctl, Dpf | Bsize2048 | Bam | RdtmsHALF); |
| else { |
| i = ctlr->rbsz / 1024; |
| if (ctlr->rbsz % 1024) |
| i++; |
| if (cttab[ctlr->type].flag & F75) { |
| csr32w(ctlr, Rctl, Lpe | Dpf | Bsize2048 | Bam | RdtmsHALF | Secrc); |
| if (ctlr->type != i82575) |
| i |= (ctlr->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); |
| } |
| |
| if (cttab[ctlr->type].flag & Fert) |
| csr32w(ctlr, Ert, 1024 / 8); |
| |
| if (ctlr->type == i82566) |
| csr32w(ctlr, Pbs, 16); |
| |
| 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; //25; |
| ctlr->radv = 0; //500; |
| csr32w(ctlr, Rdtr, ctlr->rdtr); |
| csr32w(ctlr, Radv, ctlr->radv); |
| |
| for (i = 0; i < ctlr->nrd; i++) |
| if ((bp = ctlr->rb[i]) != NULL) { |
| ctlr->rb[i] = NULL; |
| freeb(bp); |
| } |
| if (cttab[ctlr->type].flag & F75) |
| csr32w(ctlr, Rxdctl, |
| 1 << WthreshSHIFT | 8 << PthreshSHIFT | 1 << HthreshSHIFT | |
| Enable); |
| else |
| csr32w(ctlr, Rxdctl, 2 << WthreshSHIFT | 2 << PthreshSHIFT); |
| |
| /* |
| * Enable checksum offload. |
| */ |
| csr32w(ctlr, Rxcsum, Tuofl | Ipofl | ETHERHDRSIZE); |
| } |
| |
| static int i82563rim(void *v) |
| { |
| return ((struct ctlr *)v)->rim != 0; |
| } |
| |
| static void i82563rproc(void *arg) |
| { |
| unsigned int m, rdh, rim, im; |
| struct block *bp; |
| struct ctlr *ctlr; |
| struct ether *edev; |
| Rd *rd; |
| |
| edev = arg; |
| ctlr = edev->ctlr; |
| |
| i82563rxinit(ctlr); |
| csr32w(ctlr, Rctl, csr32r(ctlr, Rctl) | Ren); |
| if (cttab[ctlr->type].flag & F75) { |
| csr32w(ctlr, Rxdctl, csr32r(ctlr, Rxdctl) | Enable); |
| im = Rxt0 | Rxo | Rxdmt0 | Rxseq | Ack; |
| } else |
| im = Rxt0 | Rxo | Rxdmt0 | Rxseq | Ack; |
| m = ctlr->nrd; |
| |
| for (;;) { |
| i82563im(ctlr, im); |
| ctlr->rsleep++; |
| i82563replenish(ctlr, 1); |
| rendez_sleep(&ctlr->rrendez, i82563rim, ctlr); |
| |
| rdh = ctlr->rdh; |
| for (;;) { |
| rd = &ctlr->rdba[rdh]; |
| rim = ctlr->rim; |
| ctlr->rim = 0; |
| 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. |
| */ |
| bp = ctlr->rb[rdh]; |
| if ((rd->status & Reop) && rd->errors == 0) { |
| bp->wp += rd->length; |
| bp->lim = bp->wp; /* lie like a dog. avoid packblock. */ |
| if (!(rd->status & Ixsm)) { |
| ctlr->ixsm++; |
| if (rd->status & Ipcs) { |
| /* |
| * IP checksum calculated |
| * (and valid as errors == 0). |
| */ |
| ctlr->ipcs++; |
| bp->flag |= Bipck; |
| } |
| if (rd->status & Tcpcs) { |
| /* |
| * TCP/UDP checksum calculated |
| * (and valid as errors == 0). |
| */ |
| ctlr->tcpcs++; |
| bp->flag |= Btcpck | Budpck; |
| } |
| bp->checksum = rd->checksum; |
| bp->flag |= Bpktck; |
| } |
| etheriq(edev, bp, 1); |
| } else |
| freeb(bp); |
| ctlr->rb[rdh] = NULL; |
| rd->status = 0; |
| ctlr->rdfree--; |
| ctlr->rdh = rdh = NEXT_RING(rdh, m); |
| if (ctlr->nrd - ctlr->rdfree >= 32 || (rim & Rxdmt0)) |
| if (i82563replenish(ctlr, 0) == -1) |
| break; |
| } |
| } |
| } |
| |
| static int i82563lim(void *v) |
| { |
| return ((struct ctlr *)v)->lim != 0; |
| } |
| |
| static int speedtab[] = { |
| 10, 100, 1000, 0 |
| }; |
| |
| static unsigned int phywrite0(struct ctlr *, int unused_int, int, uint16_t); |
| |
| static unsigned int |
| setpage(struct ctlr *c, unsigned int phyno, unsigned int p, unsigned int r) |
| { |
| unsigned int pr; |
| |
| switch (c->type) { |
| case i82563: |
| if (r >= 16 && r <= 28 && r != 22) |
| pr = Phypage; |
| else if (r == 30 || r == 31) |
| pr = Phyapage; |
| else |
| return 0; |
| return phywrite0(c, phyno, pr, p); |
| case i82576: |
| case i82577: |
| case i82578: |
| return phywrite0(c, phyno, Phy79page, p); /* unverified */ |
| case i82579: |
| return phywrite0(c, phyno, Phy79page, p << 5); |
| default: |
| if (p == 0) |
| return 0; |
| return ~0; |
| } |
| } |
| |
| static unsigned int phyread0(struct ctlr *c, int phyno, int reg) |
| { |
| unsigned int phy, i; |
| |
| csr32w(c, Mdic, MDIrop | phyno << MDIpSHIFT | reg << MDIrSHIFT); |
| phy = 0; |
| for (i = 0; i < 64; i++) { |
| phy = csr32r(c, Mdic); |
| if (phy & (MDIe | MDIready)) |
| break; |
| udelay(1); |
| } |
| if ((phy & (MDIe | MDIready)) != MDIready) { |
| printd("%s: phy %d wedged %.8ux\n", cttab[c->type].name, phyno, phy); |
| return ~0; |
| } |
| return phy & 0xffff; |
| } |
| |
| static unsigned int |
| phyread(struct ctlr *c, unsigned int phyno, unsigned int reg) |
| { |
| if (setpage(c, phyno, reg >> 8, reg & 0xff) == ~0) { |
| printd("%s: phyread: bad phy page %d\n", cname(c), reg >> 8); |
| return ~0; |
| } |
| return phyread0(c, phyno, reg & 0xff); |
| } |
| |
| static unsigned int phywrite0(struct ctlr *c, int phyno, int reg, uint16_t val) |
| { |
| unsigned int phy, i; |
| |
| csr32w(c, Mdic, MDIwop | phyno << MDIpSHIFT | reg << MDIrSHIFT | val); |
| phy = 0; |
| for (i = 0; i < 64; i++) { |
| phy = csr32r(c, Mdic); |
| if (phy & (MDIe | MDIready)) |
| break; |
| udelay(1); |
| } |
| if ((phy & (MDIe | MDIready)) != MDIready) |
| return ~0; |
| return 0; |
| } |
| |
| static unsigned int |
| phywrite(struct ctlr *c, unsigned int phyno, unsigned int reg, uint16_t v) |
| { |
| if (setpage(c, phyno, reg >> 8, reg & 0xff) == ~0) |
| panic("%s: bad phy reg %.4ux", cname(c), reg); |
| return phywrite0(c, phyno, reg & 0xff, v); |
| } |
| |
| static void phyerrata(struct ether *e, struct ctlr *c, unsigned int phyno) |
| { |
| if (e->netif.mbps == 0) { |
| if (c->phyerrata == 0) { |
| c->phyerrata++; |
| phywrite(c, phyno, Phyprst, Prst); /* try a port reset */ |
| printd("ether%d: %s: phy port reset\n", e->ctlrno, cname(c)); |
| } |
| } else { |
| c->phyerrata = 0; |
| } |
| } |
| |
| static void phyl79proc(void *v) |
| { |
| unsigned int a, i, r, phy, phyno; |
| struct ctlr *c; |
| struct ether *e; |
| |
| e = v; |
| c = e->ctlr; |
| |
| phyno = cttab[c->type].phyno; |
| for (;;) { |
| phy = phyread(c, phyno, Phystat); |
| if (phy == ~0) { |
| phy = 0; |
| i = 3; |
| goto next; |
| } |
| i = (phy >> 8) & 3; |
| a = phy & Ans; |
| if (a) { |
| r = phyread(c, phyno, Phyctl); |
| phywrite(c, phyno, Phyctl, r | Ran | Ean); |
| } |
| next: |
| e->netif.link = i != 3 && (phy & Link) != 0; |
| if (e->netif.link == 0) |
| i = 3; |
| c->speeds[i]++; |
| e->netif.mbps = speedtab[i]; |
| c->lim = 0; |
| i82563im(c, Lsc); |
| c->lsleep++; |
| rendez_sleep(&c->lrendez, i82563lim, c); |
| } |
| } |
| |
| static void phylproc(void *v) |
| { |
| unsigned int a, i, phy, phyno; |
| struct ctlr *c; |
| struct ether *e; |
| |
| e = v; |
| c = e->ctlr; |
| phyno = cttab[c->type].phyno; |
| |
| if (c->type == i82573 && (phy = phyread(c, 1, Phyier)) != ~0) |
| phywrite(c, phyno, Phyier, phy | Lscie | Ancie | Spdie | Panie); |
| for (;;) { |
| phy = phyread(c, phyno, Physsr); |
| if (phy == ~0) { |
| phy = 0; |
| i = 3; |
| goto next; |
| } |
| i = (phy >> 14) & 3; |
| switch (c->type) { |
| default: |
| a = 0; |
| break; |
| case i82563: |
| case i82578: |
| case i82578m: |
| case i82583: |
| case i210: |
| a = phyread(c, phyno, Phyisr) & Ane; |
| break; |
| case i82571: |
| case i82572: |
| case i82575: |
| case i82576: |
| a = phyread(c, phyno, Phylhr) & Anf; |
| i = (i - 1) & 3; |
| break; |
| } |
| if (a) |
| phywrite(c, phyno, Phyctl, phyread(c, phyno, Phyctl) | Ran | Ean); |
| next: |
| e->netif.link = (phy & Rtlink) != 0; |
| if (e->netif.link == 0) |
| i = 3; |
| c->speeds[i]++; |
| e->netif.mbps = speedtab[i]; |
| if (c->type == i82563) |
| phyerrata(e, c, phyno); |
| c->lim = 0; |
| i82563im(c, Lsc); |
| c->lsleep++; |
| rendez_sleep(&c->lrendez, i82563lim, c); |
| } |
| } |
| |
| static void pcslproc(void *v) |
| { |
| unsigned int i, phy; |
| struct ctlr *c; |
| struct ether *e; |
| |
| e = v; |
| c = e->ctlr; |
| |
| if (c->type == i82575 || c->type == i82576) |
| csr32w(c, Connsw, Enrgirq); |
| for (;;) { |
| phy = csr32r(c, Pcsstat); |
| e->netif.link = phy & Linkok; |
| i = 3; |
| if (e->netif.link) |
| i = (phy & 6) >> 1; |
| else if (phy & Anbad) |
| csr32w(c, Pcsctl, csr32r(c, Pcsctl) | Pan | Prestart); |
| c->speeds[i]++; |
| e->netif.mbps = speedtab[i]; |
| c->lim = 0; |
| i82563im(c, Lsc | Omed); |
| c->lsleep++; |
| rendez_sleep(&c->lrendez, i82563lim, c); |
| } |
| } |
| |
| static void serdeslproc(void *v) |
| { |
| unsigned int i, tx, rx; |
| struct ctlr *c; |
| struct ether *e; |
| |
| e = v; |
| c = e->ctlr; |
| |
| for (;;) { |
| rx = csr32r(c, Rxcw); |
| tx = csr32r(c, Txcw); |
| e->netif.link = (rx & 1 << 31) != 0; |
| // e->netif.link = (csr32r(c, Status) & Lu) != 0; |
| i = 3; |
| if (e->netif.link) |
| i = 2; |
| c->speeds[i]++; |
| e->netif.mbps = speedtab[i]; |
| c->lim = 0; |
| i82563im(c, Lsc); |
| c->lsleep++; |
| rendez_sleep(&c->lrendez, i82563lim, c); |
| } |
| } |
| |
| static void i82563attach(struct ether *edev) |
| { |
| ERRSTACK(1); |
| char *lname, *rname, *tname; |
| int i; |
| struct block *bp; |
| struct ctlr *ctlr; |
| |
| ctlr = edev->ctlr; |
| qlock(&ctlr->alock); |
| if (ctlr->alloc != NULL) { |
| qunlock(&ctlr->alock); |
| return; |
| } |
| |
| ctlr->nrd = Nrd; |
| ctlr->ntd = Ntd; |
| ctlr->alloc = |
| kzmalloc(ctlr->nrd * sizeof(Rd) + ctlr->ntd * sizeof(Td) + 255, 0); |
| if (ctlr->alloc == NULL) { |
| qunlock(&ctlr->alock); |
| error(Enomem); |
| } |
| ctlr->rdba = (Rd *) ROUNDUP((uintptr_t) ctlr->alloc, 256); |
| 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 (waserror()) { |
| kfree(ctlr->tb); |
| ctlr->tb = NULL; |
| kfree(ctlr->rb); |
| ctlr->rb = NULL; |
| kfree(ctlr->alloc); |
| ctlr->alloc = NULL; |
| qunlock(&ctlr->alock); |
| nexterror(); |
| } |
| |
| /* the ktasks should free these names, if they ever exit */ |
| lname = kmalloc(KNAMELEN, KMALLOC_WAIT); |
| rname = kmalloc(KNAMELEN, KMALLOC_WAIT); |
| tname = kmalloc(KNAMELEN, KMALLOC_WAIT); |
| |
| snprintf(lname, KNAMELEN, "#l%dlproc", edev->ctlrno); |
| |
| if (csr32r(ctlr, Status) & Tbimode) |
| ktask(lname, serdeslproc, edev); /* mac based serdes */ |
| else if ((csr32r(ctlr, Ctrlext) & Linkmode) == Serdes) |
| ktask(lname, pcslproc, edev); /* phy based serdes */ |
| else if (cttab[ctlr->type].flag & F79phy) |
| ktask(lname, phyl79proc, edev); |
| else |
| ktask(lname, phylproc, edev); |
| |
| snprintf(rname, KNAMELEN, "#l%drproc", edev->ctlrno); |
| ktask(rname, i82563rproc, edev); |
| |
| snprintf(tname, KNAMELEN, "#l%dtproc", edev->ctlrno); |
| ktask(tname, i82563tproc, edev); |
| |
| qunlock(&ctlr->alock); |
| poperror(); |
| } |
| |
| static void i82563interrupt(struct hw_trapframe *hw_tf, void *arg) |
| { |
| struct ctlr *ctlr; |
| struct ether *edev; |
| uint32_t icr, im; |
| |
| edev = arg; |
| ctlr = edev->ctlr; |
| |
| spin_lock_irqsave(&ctlr->imlock); |
| csr32w(ctlr, Imc, ~0); |
| im = ctlr->im; |
| |
| while ((icr = csr32r(ctlr, Icr)) & ctlr->im) { |
| if (icr & (Lsc | Omed)) { |
| im &= ~(Lsc | Omed); |
| ctlr->lim = icr & (Lsc | Omed); |
| 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); |
| } |
| |
| static int i82563detach(struct ctlr *ctlr) |
| { |
| int r, timeo; |
| |
| /* balance rx/tx packet buffer; survives reset */ |
| if (ctlr->rbsz > 8192 && cttab[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); |
| |
| /* |
| * 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, csr32r(ctlr, Tctl) & ~Ten); |
| |
| udelay(1000 * 1000); |
| |
| r = csr32r(ctlr, Ctrl); |
| if (ctlr->type == i82566 || ctlr->type == i82579) |
| r |= Phyrst; |
| csr32w(ctlr, Ctrl, Devrst | r); |
| udelay(1000 * 1000); |
| for (timeo = 0;; timeo++) { |
| if ((csr32r(ctlr, Ctrl) & (Devrst | Phyrst)) == 0) |
| break; |
| if (timeo >= 1000) |
| return -1; |
| udelay(1000); |
| } |
| |
| r = csr32r(ctlr, Ctrl); |
| csr32w(ctlr, Ctrl, Slu | r); |
| |
| 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) & ~Rxcfg) == 0) |
| break; |
| udelay(1000); |
| } |
| if (csr32r(ctlr, Icr) & ~Rxcfg) |
| return -1; |
| |
| return 0; |
| } |
| |
| static void i82563shutdown(struct ether *edev) |
| { |
| i82563detach(edev->ctlr); |
| } |
| |
| static uint16_t eeread(struct ctlr *ctlr, int adr) |
| { |
| csr32w(ctlr, Eerd, EEstart | adr << 2); |
| while ((csr32r(ctlr, Eerd) & EEdone) == 0) |
| cpu_relax(); |
| return csr32r(ctlr, Eerd) >> 16; |
| } |
| |
| 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, 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) |
| return 0; |
| udelay(1000); |
| s = f->reg[Fsts]; |
| } |
| return -1; |
| } |
| |
| static int fread(struct ctlr *c, Flash * f, int ladr) |
| { |
| uint16_t s; |
| |
| udelay(1000); |
| if (fcycle(c, f) == -1) |
| return -1; |
| f->reg[Fsts] |= Fdone; |
| f->reg32[Faddr] = ladr; |
| |
| /* setup flash control register */ |
| s = f->reg[Fctl] & ~0x3ff; |
| f->reg[Fctl] = s | 1 << 8 | Fgo; /* 2 byte read */ |
| |
| while ((f->reg[Fsts] & Fdone) == 0) ; |
| if (f->reg[Fsts] & (Fcerr | Ael)) |
| return -1; |
| return f->reg32[Fdata] & 0xffff; |
| } |
| |
| static int fload(struct ctlr *c) |
| { |
| unsigned int data, r, adr; |
| uint16_t sum; |
| uintptr_t mmio_paddr; |
| struct pci_device *pcidev = c->pcidev; |
| 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 = (uint32_t *) f.reg; |
| f.base = f.reg32[Bfpr] & 0x1fff; |
| f.lim = f.reg32[Bfpr] >> 16 & 0x1fff; |
| if (csr32r(c, Eec) & Sec1val) |
| f.base += (f.lim + 1) - (f.base >> 1); |
| r = f.base << 12; |
| sum = 0; |
| for (adr = 0; adr < 0x40; adr++) { |
| data = fread(c, &f, r + adr * 2); |
| if (data == -1) |
| return -1; |
| c->eeprom[adr] = data; |
| sum += data; |
| } |
| vunmap_vmem((uintptr_t)f.reg, c->pcidev->bar[1].mmio_sz); |
| return sum; |
| } |
| |
| static void defaultea(struct ctlr *ctlr, uint8_t * ra) |
| { |
| unsigned int i, r; |
| uint64_t u; |
| static uint8_t NULLea[Eaddrlen]; |
| |
| if (memcmp(ra, NULLea, Eaddrlen) != 0) |
| return; |
| if (cttab[ctlr->type].flag & Fflashea) { |
| /* intel mb bug */ |
| u = (uint64_t) csr32r(ctlr, Rah) << 32u | (unsigned int)csr32r(ctlr, |
| Ral); |
| for (i = 0; i < Eaddrlen; i++) |
| ra[i] = u >> 8 * i; |
| } |
| if (memcmp(ra, NULLea, Eaddrlen) != 0) |
| return; |
| for (i = 0; i < Eaddrlen / 2; i++) { |
| ra[2 * i] = ctlr->eeprom[Ea + i]; |
| ra[2 * i + 1] = ctlr->eeprom[Ea + i] >> 8; |
| } |
| r = (csr32r(ctlr, Status) & Lanid) >> 2; |
| ra[5] += r; /* ea ctlr[n] = ea ctlr[0]+n */ |
| } |
| |
| static int i82563reset(struct ctlr *ctlr) |
| { |
| uint8_t *ra; |
| int i, r; |
| |
| if (i82563detach(ctlr)) |
| return -1; |
| if (cttab[ctlr->type].flag & Fload) |
| r = fload(ctlr); |
| else |
| r = eeload(ctlr); |
| if (r != 0 && r != 0xbaba) { |
| printd("%s: bad eeprom checksum - %#.4ux\n", cname(ctlr), r); |
| return -1; |
| } |
| |
| ra = ctlr->ra; |
| defaultea(ctlr, ra); |
| csr32w(ctlr, Ral, ra[3] << 24 | ra[2] << 16 | ra[1] << 8 | ra[0]); |
| csr32w(ctlr, Rah, 1 << 31 | ra[5] << 8 | ra[4]); |
| for (i = 1; i < 16; i++) { |
| csr32w(ctlr, Ral + i * 8, 0); |
| csr32w(ctlr, Rah + i * 8, 0); |
| } |
| memset(ctlr->mta, 0, sizeof(ctlr->mta)); |
| for (i = 0; i < 128; i++) |
| csr32w(ctlr, Mta + i * 4, 0); |
| csr32w(ctlr, Fcal, 0x00C28001); |
| csr32w(ctlr, Fcah, 0x0100); |
| if ((cttab[ctlr->type].flag & Fnofct) == 0) |
| csr32w(ctlr, Fct, 0x8808); |
| csr32w(ctlr, Fcttv, 0x0100); |
| csr32w(ctlr, Fcrtl, ctlr->fcrtl); |
| csr32w(ctlr, Fcrth, ctlr->fcrth); |
| if (cttab[ctlr->type].flag & F75) |
| csr32w(ctlr, Eitr, 128 << 2); /* 128 ¼ microsecond intervals */ |
| return 0; |
| } |
| |
| 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; |
| |
| if ((ctlr = edev->ctlr) == NULL) |
| error(Enonexist); |
| |
| 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(Ebadarg); |
| ctlr->rdtr = v; |
| csr32w(ctlr, Rdtr, v); |
| break; |
| case CMradv: |
| v = strtoul(cb->f[1], &p, 0); |
| if (*p || v > 0xffff) |
| error(Ebadarg); |
| 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 int didtype(int d) |
| { |
| switch (d) { |
| case 0x1096: |
| case 0x10ba: /* “gilgal” */ |
| case 0x1098: /* serdes; not seen */ |
| case 0x10bb: /* serdes */ |
| return i82563; |
| case 0x1049: /* mm */ |
| case 0x104a: /* dm */ |
| case 0x104b: /* dc */ |
| case 0x104d: /* v “ninevah” */ |
| case 0x10bd: /* dm-2 */ |
| case 0x294c: /* ich 9 */ |
| return i82566; |
| case 0x10de: /* lm ich10d */ |
| case 0x10df: /* lf ich10 */ |
| case 0x10e5: /* lm ich9 */ |
| case 0x10f5: /* lm ich9m; “boazman” */ |
| return i82567; |
| case 0x10bf: /* lf ich9m */ |
| case 0x10cb: /* v ich9m */ |
| case 0x10cd: /* lf ich10 */ |
| case 0x10ce: /* v ich10 */ |
| case 0x10cc: /* lm ich10 */ |
| return i82567m; |
| 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” */ |
| return i82571; |
| case 0x107d: /* eb copper */ |
| case 0x107e: /* ei fiber */ |
| case 0x107f: /* ei */ |
| case 0x10b9: /* ei “rimon” */ |
| return i82572; |
| case 0x108b: /* e “vidalia” */ |
| case 0x108c: /* e (iamt) */ |
| case 0x109a: /* l “tekoa” */ |
| return i82573; |
| case 0x10d3: /* l or it; “hartwell” */ |
| return i82574; |
| case 0x10a7: |
| case 0x10a9: /* fiber/serdes */ |
| return i82575; |
| case 0x10c9: /* copper */ |
| case 0x10e6: /* fiber */ |
| case 0x10e7: /* serdes; “kawela” */ |
| case 0x150d: /* backplane */ |
| return i82576; |
| case 0x10ea: /* lc “calpella”; aka pch lan */ |
| return i82577; |
| case 0x10eb: /* lm “calpella” */ |
| return i82577m; |
| case 0x10ef: /* dc “piketon” */ |
| return i82578; |
| case 0x1502: /* lm */ |
| case 0x1503: /* v “lewisville” */ |
| return i82579; |
| case 0x10f0: /* dm “king's creek” */ |
| return i82578m; |
| case 0x150e: /* “barton hills” */ |
| case 0x150f: /* fiber */ |
| case 0x1510: /* backplane */ |
| case 0x1511: /* sfp */ |
| case 0x1516: |
| return i82580; |
| case 0x1506: /* v */ |
| return i82583; |
| case 0x1533: /* i210-t1 */ |
| case 0x1534: |
| case 0x1536: /* fiber */ |
| case 0x1537: /* backplane */ |
| case 0x1538: |
| case 0x1539: /* i211 */ |
| return i210; |
| case 0x153a: /* i217-lm */ |
| case 0x153b: /* i217-v */ |
| case 0x15a0: /* i218-lm */ |
| case 0x15a1: /* i218-v */ |
| case 0x15a2: /* i218-lm */ |
| case 0x15a3: /* i218-v */ |
| return i217; |
| case 0x151f: /* “powerville” eeprom-less */ |
| case 0x1521: /* copper */ |
| case 0x1522: /* fiber */ |
| case 0x1523: /* serdes */ |
| case 0x1524: /* sgmii */ |
| return i350; |
| } |
| return -1; |
| } |
| |
| static void hbafixup(struct pci_device *p) |
| { |
| unsigned int i; |
| |
| i = pcidev_read32(p, PciSVID); |
| if ((i & 0xffff) == 0x1b52 && p->dev_id == 1) |
| p->dev_id = i >> 16; |
| } |
| |
| static void i82563pci(void) |
| { |
| int type; |
| struct ctlr *c, **cc; |
| struct pci_device *p; |
| |
| cc = &i82563ctlr; |
| STAILQ_FOREACH(p, &pci_devices, all_dev) { |
| if (p->ven_id != 0x8086) |
| continue; |
| hbafixup(p); |
| if ((type = didtype(p->dev_id)) == -1) |
| continue; |
| c = kzmalloc(sizeof *c, KMALLOC_WAIT); |
| |
| qlock_init(&c->alock); |
| spinlock_init_irqsave(&c->imlock); |
| rendez_init(&c->lrendez); |
| qlock_init(&c->slock); |
| rendez_init(&c->rrendez); |
| rendez_init(&c->trendez); |
| qlock_init(&c->tlock); |
| |
| c->type = type; |
| c->pcidev = p; |
| c->rbsz = cttab[type].mtu; |
| /* plan9 called this c->port, and just used the top of the raw bar, |
| * regardless of the type. */ |
| c->mmio_paddr = p->bar[0].mmio_base32 ? p->bar[0].mmio_base32 : |
| p->bar[0].mmio_base64; |
| *cc = c; |
| cc = &c->next; |
| } |
| } |
| |
| static int setup(struct ctlr *ctlr) |
| { |
| struct pci_device *p; |
| |
| p = ctlr->pcidev; |
| ctlr->nic = (void*)vmap_pmem(ctlr->mmio_paddr, p->bar[0].mmio_sz); |
| if (ctlr->nic == NULL) { |
| printd("%s: can't map %p\n", cname(ctlr), ctlr->mmio_paddr); |
| return -1; |
| } |
| pci_set_bus_master(p); |
| if (i82563reset(ctlr)) { |
| vunmap_vmem((uintptr_t)ctlr->nic, p->bar[0].mmio_sz); |
| return -1; |
| } |
| return 0; |
| } |
| |
| static void i82563_init(void) |
| { |
| i82563pci(); |
| } |
| |
| static int pnp(struct ether *edev, int type) |
| { |
| struct ctlr *ctlr; |
| |
| run_once(i82563_init()); |
| |
| /* |
| * Any adapter matches if no edev->port is supplied, |
| * otherwise the ports must match. |
| */ |
| for (ctlr = i82563ctlr;; ctlr = ctlr->next) { |
| if (ctlr == NULL) |
| return -1; |
| if (ctlr->active) |
| continue; |
| if (type != -1 && ctlr->type != type) |
| continue; |
| if (edev->port == 0 || edev->port == ctlr->mmio_paddr) { |
| ctlr->active = 1; |
| memmove(ctlr->ra, edev->ea, Eaddrlen); |
| if (setup(ctlr) == 0) |
| break; |
| } |
| } |
| |
| edev->ctlr = ctlr; |
| edev->port = ctlr->mmio_paddr; |
| edev->irq = ctlr->pcidev->irqline; |
| edev->tbdf = MKBUS(BusPCI, ctlr->pcidev->bus, ctlr->pcidev->dev, |
| ctlr->pcidev->func); |
| edev->netif.mbps = 1000; |
| edev->maxmtu = ctlr->rbsz; |
| memmove(edev->ea, ctlr->ra, Eaddrlen); |
| |
| /* |
| * Linkage to the generic ethernet driver. |
| */ |
| edev->attach = i82563attach; |
| edev->ifstat = i82563ifstat; |
| edev->ctl = i82563ctl; |
| |
| edev->netif.arg = edev; |
| edev->netif.promiscuous = i82563promiscuous; |
| edev->shutdown = i82563shutdown; |
| edev->netif.multicast = i82563multicast; |
| |
| register_irq(edev->irq, i82563interrupt, edev, edev->tbdf); |
| return 0; |
| } |
| |
| static int anypnp(struct ether *e) |
| { |
| return pnp(e, -1); |
| } |
| |
| linker_func_3(ether82563link) |
| { |
| addethercard("i82563", anypnp); |
| } |