16a081dcdSDavid du Colombier /*
27e254d1cSDavid du Colombier * Intel 8254[340]NN Gigabit Ethernet PCI Controllers
36a081dcdSDavid du Colombier * as found on the Intel PRO/1000 series of adapters:
46a081dcdSDavid du Colombier * 82543GC Intel PRO/1000 T
56a081dcdSDavid du Colombier * 82544EI Intel PRO/1000 XT
66a081dcdSDavid du Colombier * 82540EM Intel PRO/1000 MT
7d717568cSDavid du Colombier * 82541[GP]I
8aeb1c8a5SDavid du Colombier * 82547GI
9aeb1c8a5SDavid du Colombier * 82546GB
105b7d6169SDavid du Colombier * 82546EB
116a081dcdSDavid du Colombier * To Do:
126a081dcdSDavid du Colombier * finish autonegotiation code;
136a081dcdSDavid du Colombier * integrate fiber stuff back in (this ONLY handles
146a081dcdSDavid du Colombier * the CAT5 cards at the moment);
156a081dcdSDavid du Colombier * add tuning control via ctl file;
166a081dcdSDavid du Colombier * this driver is little-endian specific.
176a081dcdSDavid du Colombier */
186a081dcdSDavid du Colombier #include "u.h"
196a081dcdSDavid du Colombier #include "../port/lib.h"
206a081dcdSDavid du Colombier #include "mem.h"
216a081dcdSDavid du Colombier #include "dat.h"
226a081dcdSDavid du Colombier #include "fns.h"
236a081dcdSDavid du Colombier #include "io.h"
246a081dcdSDavid du Colombier #include "../port/error.h"
256a081dcdSDavid du Colombier #include "../port/netif.h"
266a081dcdSDavid du Colombier
276a081dcdSDavid du Colombier #include "etherif.h"
286a081dcdSDavid du Colombier #include "ethermii.h"
296a081dcdSDavid du Colombier
306a081dcdSDavid du Colombier enum {
31aeb1c8a5SDavid du Colombier i82542 = (0x1000<<16)|0x8086,
32aeb1c8a5SDavid du Colombier i82543gc = (0x1004<<16)|0x8086,
33aeb1c8a5SDavid du Colombier i82544ei = (0x1008<<16)|0x8086,
3407c32395SDavid du Colombier i82544eif = (0x1009<<16)|0x8086,
359a8968c3SDavid du Colombier i82544gc = (0x100d<<16)|0x8086,
36aeb1c8a5SDavid du Colombier i82540em = (0x100E<<16)|0x8086,
37aeb1c8a5SDavid du Colombier i82540eplp = (0x101E<<16)|0x8086,
38c079eed3SDavid du Colombier i82545em = (0x100F<<16)|0x8086,
39ab3dc52fSDavid du Colombier i82545gmc = (0x1026<<16)|0x8086,
405efba406SDavid du Colombier i82547ei = (0x1019<<16)|0x8086,
41aeb1c8a5SDavid du Colombier i82547gi = (0x1075<<16)|0x8086,
425efba406SDavid du Colombier i82541ei = (0x1013<<16)|0x8086,
43aeb1c8a5SDavid du Colombier i82541gi = (0x1076<<16)|0x8086,
4426ad7229SDavid du Colombier i82541gi2 = (0x1077<<16)|0x8086,
45d717568cSDavid du Colombier i82541pi = (0x107c<<16)|0x8086,
465efba406SDavid du Colombier i82546gb = (0x1079<<16)|0x8086,
475b7d6169SDavid du Colombier i82546eb = (0x1010<<16)|0x8086,
48aeb1c8a5SDavid du Colombier };
49aeb1c8a5SDavid du Colombier
50aeb1c8a5SDavid du Colombier enum {
516a081dcdSDavid du Colombier Ctrl = 0x00000000, /* Device Control */
52ab3dc52fSDavid du Colombier Ctrldup = 0x00000004, /* Device Control Duplicate */
536a081dcdSDavid du Colombier Status = 0x00000008, /* Device Status */
546a081dcdSDavid du Colombier Eecd = 0x00000010, /* EEPROM/Flash Control/Data */
556a081dcdSDavid du Colombier Ctrlext = 0x00000018, /* Extended Device Control */
566a081dcdSDavid du Colombier Mdic = 0x00000020, /* MDI Control */
576a081dcdSDavid du Colombier Fcal = 0x00000028, /* Flow Control Address Low */
586a081dcdSDavid du Colombier Fcah = 0x0000002C, /* Flow Control Address High */
596a081dcdSDavid du Colombier Fct = 0x00000030, /* Flow Control Type */
606a081dcdSDavid du Colombier Icr = 0x000000C0, /* Interrupt Cause Read */
616a081dcdSDavid du Colombier Ics = 0x000000C8, /* Interrupt Cause Set */
626a081dcdSDavid du Colombier Ims = 0x000000D0, /* Interrupt Mask Set/Read */
636a081dcdSDavid du Colombier Imc = 0x000000D8, /* Interrupt mask Clear */
646a081dcdSDavid du Colombier Rctl = 0x00000100, /* Receive Control */
656a081dcdSDavid du Colombier Fcttv = 0x00000170, /* Flow Control Transmit Timer Value */
666a081dcdSDavid du Colombier Txcw = 0x00000178, /* Transmit Configuration Word */
676a081dcdSDavid du Colombier Rxcw = 0x00000180, /* Receive Configuration Word */
68b1707c5dSDavid du Colombier /* on the oldest cards (8254[23]), the Mta register is at 0x200 */
696a081dcdSDavid du Colombier Tctl = 0x00000400, /* Transmit Control */
706a081dcdSDavid du Colombier Tipg = 0x00000410, /* Transmit IPG */
716a081dcdSDavid du Colombier Tbt = 0x00000448, /* Transmit Burst Timer */
726a081dcdSDavid du Colombier Ait = 0x00000458, /* Adaptive IFS Throttle */
736a081dcdSDavid du Colombier Fcrtl = 0x00002160, /* Flow Control RX Threshold Low */
746a081dcdSDavid du Colombier Fcrth = 0x00002168, /* Flow Control Rx Threshold High */
756a081dcdSDavid du Colombier Rdfh = 0x00002410, /* Receive data fifo head */
766a081dcdSDavid du Colombier Rdft = 0x00002418, /* Receive data fifo tail */
776a081dcdSDavid du Colombier Rdfhs = 0x00002420, /* Receive data fifo head saved */
786a081dcdSDavid du Colombier Rdfts = 0x00002428, /* Receive data fifo tail saved */
796a081dcdSDavid du Colombier Rdfpc = 0x00002430, /* Receive data fifo packet count */
806a081dcdSDavid du Colombier Rdbal = 0x00002800, /* Rd Base Address Low */
816a081dcdSDavid du Colombier Rdbah = 0x00002804, /* Rd Base Address High */
826a081dcdSDavid du Colombier Rdlen = 0x00002808, /* Receive Descriptor Length */
836a081dcdSDavid du Colombier Rdh = 0x00002810, /* Receive Descriptor Head */
846a081dcdSDavid du Colombier Rdt = 0x00002818, /* Receive Descriptor Tail */
856a081dcdSDavid du Colombier Rdtr = 0x00002820, /* Receive Descriptor Timer Ring */
866a081dcdSDavid du Colombier Rxdctl = 0x00002828, /* Receive Descriptor Control */
876a081dcdSDavid du Colombier Radv = 0x0000282C, /* Receive Interrupt Absolute Delay Timer */
886a081dcdSDavid du Colombier Txdmac = 0x00003000, /* Transfer DMA Control */
896a081dcdSDavid du Colombier Ett = 0x00003008, /* Early Transmit Control */
906a081dcdSDavid du Colombier Tdfh = 0x00003410, /* Transmit data fifo head */
916a081dcdSDavid du Colombier Tdft = 0x00003418, /* Transmit data fifo tail */
926a081dcdSDavid du Colombier Tdfhs = 0x00003420, /* Transmit data Fifo Head saved */
936a081dcdSDavid du Colombier Tdfts = 0x00003428, /* Transmit data fifo tail saved */
946a081dcdSDavid du Colombier Tdfpc = 0x00003430, /* Trasnmit data Fifo packet count */
956a081dcdSDavid du Colombier Tdbal = 0x00003800, /* Td Base Address Low */
966a081dcdSDavid du Colombier Tdbah = 0x00003804, /* Td Base Address High */
976a081dcdSDavid du Colombier Tdlen = 0x00003808, /* Transmit Descriptor Length */
986a081dcdSDavid du Colombier Tdh = 0x00003810, /* Transmit Descriptor Head */
996a081dcdSDavid du Colombier Tdt = 0x00003818, /* Transmit Descriptor Tail */
1006a081dcdSDavid du Colombier Tidv = 0x00003820, /* Transmit Interrupt Delay Value */
1016a081dcdSDavid du Colombier Txdctl = 0x00003828, /* Transmit Descriptor Control */
1026a081dcdSDavid du Colombier Tadv = 0x0000382C, /* Transmit Interrupt Absolute Delay Timer */
1036a081dcdSDavid du Colombier
1046a081dcdSDavid du Colombier Statistics = 0x00004000, /* Start of Statistics Area */
1056a081dcdSDavid du Colombier Gorcl = 0x88/4, /* Good Octets Received Count */
1066a081dcdSDavid du Colombier Gotcl = 0x90/4, /* Good Octets Transmitted Count */
1076a081dcdSDavid du Colombier Torl = 0xC0/4, /* Total Octets Received */
1086a081dcdSDavid du Colombier Totl = 0xC8/4, /* Total Octets Transmitted */
1096a081dcdSDavid du Colombier Nstatistics = 64,
1106a081dcdSDavid du Colombier
1116a081dcdSDavid du Colombier Rxcsum = 0x00005000, /* Receive Checksum Control */
1126a081dcdSDavid du Colombier Mta = 0x00005200, /* Multicast Table Array */
1136a081dcdSDavid du Colombier Ral = 0x00005400, /* Receive Address Low */
1146a081dcdSDavid du Colombier Rah = 0x00005404, /* Receive Address High */
1156a081dcdSDavid du Colombier Manc = 0x00005820, /* Management Control */
1166a081dcdSDavid du Colombier };
1176a081dcdSDavid du Colombier
1186a081dcdSDavid du Colombier enum { /* Ctrl */
1196a081dcdSDavid du Colombier Bem = 0x00000002, /* Big Endian Mode */
1206a081dcdSDavid du Colombier Prior = 0x00000004, /* Priority on the PCI bus */
1216a081dcdSDavid du Colombier Lrst = 0x00000008, /* Link Reset */
1226a081dcdSDavid du Colombier Asde = 0x00000020, /* Auto-Speed Detection Enable */
1236a081dcdSDavid du Colombier Slu = 0x00000040, /* Set Link Up */
1246a081dcdSDavid du Colombier Ilos = 0x00000080, /* Invert Loss of Signal (LOS) */
1256a081dcdSDavid du Colombier SspeedMASK = 0x00000300, /* Speed Selection */
1266a081dcdSDavid du Colombier SspeedSHIFT = 8,
1276a081dcdSDavid du Colombier Sspeed10 = 0x00000000, /* 10Mb/s */
1286a081dcdSDavid du Colombier Sspeed100 = 0x00000100, /* 100Mb/s */
1296a081dcdSDavid du Colombier Sspeed1000 = 0x00000200, /* 1000Mb/s */
1306a081dcdSDavid du Colombier Frcspd = 0x00000800, /* Force Speed */
1316a081dcdSDavid du Colombier Frcdplx = 0x00001000, /* Force Duplex */
1326a081dcdSDavid du Colombier SwdpinsloMASK = 0x003C0000, /* Software Defined Pins - lo nibble */
1336a081dcdSDavid du Colombier SwdpinsloSHIFT = 18,
1346a081dcdSDavid du Colombier SwdpioloMASK = 0x03C00000, /* Software Defined Pins - I or O */
1356a081dcdSDavid du Colombier SwdpioloSHIFT = 22,
1366a081dcdSDavid du Colombier Devrst = 0x04000000, /* Device Reset */
1376a081dcdSDavid du Colombier Rfce = 0x08000000, /* Receive Flow Control Enable */
1386a081dcdSDavid du Colombier Tfce = 0x10000000, /* Transmit Flow Control Enable */
1396a081dcdSDavid du Colombier Vme = 0x40000000, /* VLAN Mode Enable */
1406a081dcdSDavid du Colombier };
1416a081dcdSDavid du Colombier
1427e254d1cSDavid du Colombier /*
1437e254d1cSDavid du Colombier * can't find Tckok nor Rbcok in any Intel docs,
1447e254d1cSDavid du Colombier * but even 82543gc docs define Lanid.
1457e254d1cSDavid du Colombier */
1466a081dcdSDavid du Colombier enum { /* Status */
1476a081dcdSDavid du Colombier Lu = 0x00000002, /* Link Up */
1487e254d1cSDavid du Colombier Lanid = 0x0000000C, /* mask for Lan ID. (function id) */
1497e254d1cSDavid du Colombier // Tckok = 0x00000004, /* Transmit clock is running */
1507e254d1cSDavid du Colombier // Rbcok = 0x00000008, /* Receive clock is running */
1516a081dcdSDavid du Colombier Txoff = 0x00000010, /* Transmission Paused */
1526a081dcdSDavid du Colombier Tbimode = 0x00000020, /* TBI Mode Indication */
1536a081dcdSDavid du Colombier LspeedMASK = 0x000000C0, /* Link Speed Setting */
1546a081dcdSDavid du Colombier LspeedSHIFT = 6,
1556a081dcdSDavid du Colombier Lspeed10 = 0x00000000, /* 10Mb/s */
1566a081dcdSDavid du Colombier Lspeed100 = 0x00000040, /* 100Mb/s */
1576a081dcdSDavid du Colombier Lspeed1000 = 0x00000080, /* 1000Mb/s */
1586a081dcdSDavid du Colombier Mtxckok = 0x00000400, /* MTX clock is running */
1596a081dcdSDavid du Colombier Pci66 = 0x00000800, /* PCI Bus speed indication */
1606a081dcdSDavid du Colombier Bus64 = 0x00001000, /* PCI Bus width indication */
1616a081dcdSDavid du Colombier Pcixmode = 0x00002000, /* PCI-X mode */
1626a081dcdSDavid du Colombier PcixspeedMASK = 0x0000C000, /* PCI-X bus speed */
1636a081dcdSDavid du Colombier PcixspeedSHIFT = 14,
1646a081dcdSDavid du Colombier Pcix66 = 0x00000000, /* 50-66MHz */
1656a081dcdSDavid du Colombier Pcix100 = 0x00004000, /* 66-100MHz */
1666a081dcdSDavid du Colombier Pcix133 = 0x00008000, /* 100-133MHz */
1676a081dcdSDavid du Colombier };
1686a081dcdSDavid du Colombier
1696a081dcdSDavid du Colombier enum { /* Ctrl and Status */
1706a081dcdSDavid du Colombier Fd = 0x00000001, /* Full-Duplex */
1716a081dcdSDavid du Colombier AsdvMASK = 0x00000300,
1726a081dcdSDavid du Colombier AsdvSHIFT = 8,
1736a081dcdSDavid du Colombier Asdv10 = 0x00000000, /* 10Mb/s */
1746a081dcdSDavid du Colombier Asdv100 = 0x00000100, /* 100Mb/s */
1756a081dcdSDavid du Colombier Asdv1000 = 0x00000200, /* 1000Mb/s */
1766a081dcdSDavid du Colombier };
1776a081dcdSDavid du Colombier
1786a081dcdSDavid du Colombier enum { /* Eecd */
1796a081dcdSDavid du Colombier Sk = 0x00000001, /* Clock input to the EEPROM */
1806a081dcdSDavid du Colombier Cs = 0x00000002, /* Chip Select */
1816a081dcdSDavid du Colombier Di = 0x00000004, /* Data Input to the EEPROM */
1826a081dcdSDavid du Colombier Do = 0x00000008, /* Data Output from the EEPROM */
1836a081dcdSDavid du Colombier Areq = 0x00000040, /* EEPROM Access Request */
1846a081dcdSDavid du Colombier Agnt = 0x00000080, /* EEPROM Access Grant */
1858466d066SDavid du Colombier Eepresent = 0x00000100, /* EEPROM Present */
1866a081dcdSDavid du Colombier Eesz256 = 0x00000200, /* EEPROM is 256 words not 64 */
1878466d066SDavid du Colombier Eeszaddr = 0x00000400, /* EEPROM size for 8254[17] */
1886a081dcdSDavid du Colombier Spi = 0x00002000, /* EEPROM is SPI not Microwire */
1896a081dcdSDavid du Colombier };
1906a081dcdSDavid du Colombier
1916a081dcdSDavid du Colombier enum { /* Ctrlext */
1926a081dcdSDavid du Colombier Gpien = 0x0000000F, /* General Purpose Interrupt Enables */
1936a081dcdSDavid du Colombier SwdpinshiMASK = 0x000000F0, /* Software Defined Pins - hi nibble */
1946a081dcdSDavid du Colombier SwdpinshiSHIFT = 4,
1956a081dcdSDavid du Colombier SwdpiohiMASK = 0x00000F00, /* Software Defined Pins - I or O */
1966a081dcdSDavid du Colombier SwdpiohiSHIFT = 8,
1976a081dcdSDavid du Colombier Asdchk = 0x00001000, /* ASD Check */
1986a081dcdSDavid du Colombier Eerst = 0x00002000, /* EEPROM Reset */
1996a081dcdSDavid du Colombier Ips = 0x00004000, /* Invert Power State */
2006a081dcdSDavid du Colombier Spdbyps = 0x00008000, /* Speed Select Bypass */
2016a081dcdSDavid du Colombier };
2026a081dcdSDavid du Colombier
2036a081dcdSDavid du Colombier enum { /* EEPROM content offsets */
2046a081dcdSDavid du Colombier Ea = 0x00, /* Ethernet Address */
2056a081dcdSDavid du Colombier Cf = 0x03, /* Compatibility Field */
2066a081dcdSDavid du Colombier Pba = 0x08, /* Printed Board Assembly number */
2076a081dcdSDavid du Colombier Icw1 = 0x0A, /* Initialization Control Word 1 */
2086a081dcdSDavid du Colombier Sid = 0x0B, /* Subsystem ID */
2096a081dcdSDavid du Colombier Svid = 0x0C, /* Subsystem Vendor ID */
2106a081dcdSDavid du Colombier Did = 0x0D, /* Device ID */
2116a081dcdSDavid du Colombier Vid = 0x0E, /* Vendor ID */
2126a081dcdSDavid du Colombier Icw2 = 0x0F, /* Initialization Control Word 2 */
2136a081dcdSDavid du Colombier };
2146a081dcdSDavid du Colombier
2156a081dcdSDavid du Colombier enum { /* Mdic */
2166a081dcdSDavid du Colombier MDIdMASK = 0x0000FFFF, /* Data */
2176a081dcdSDavid du Colombier MDIdSHIFT = 0,
2186a081dcdSDavid du Colombier MDIrMASK = 0x001F0000, /* PHY Register Address */
2196a081dcdSDavid du Colombier MDIrSHIFT = 16,
2206a081dcdSDavid du Colombier MDIpMASK = 0x03E00000, /* PHY Address */
2216a081dcdSDavid du Colombier MDIpSHIFT = 21,
2226a081dcdSDavid du Colombier MDIwop = 0x04000000, /* Write Operation */
2236a081dcdSDavid du Colombier MDIrop = 0x08000000, /* Read Operation */
2246a081dcdSDavid du Colombier MDIready = 0x10000000, /* End of Transaction */
2256a081dcdSDavid du Colombier MDIie = 0x20000000, /* Interrupt Enable */
2266a081dcdSDavid du Colombier MDIe = 0x40000000, /* Error */
2276a081dcdSDavid du Colombier };
2286a081dcdSDavid du Colombier
2296a081dcdSDavid du Colombier enum { /* Icr, Ics, Ims, Imc */
2306a081dcdSDavid du Colombier Txdw = 0x00000001, /* Transmit Descriptor Written Back */
2316a081dcdSDavid du Colombier Txqe = 0x00000002, /* Transmit Queue Empty */
2326a081dcdSDavid du Colombier Lsc = 0x00000004, /* Link Status Change */
2336a081dcdSDavid du Colombier Rxseq = 0x00000008, /* Receive Sequence Error */
2346a081dcdSDavid du Colombier Rxdmt0 = 0x00000010, /* Rd Minimum Threshold Reached */
2356a081dcdSDavid du Colombier Rxo = 0x00000040, /* Receiver Overrun */
2366a081dcdSDavid du Colombier Rxt0 = 0x00000080, /* Receiver Timer Interrupt */
2376a081dcdSDavid du Colombier Mdac = 0x00000200, /* MDIO Access Completed */
2386a081dcdSDavid du Colombier Rxcfg = 0x00000400, /* Receiving /C/ ordered sets */
2396a081dcdSDavid du Colombier Gpi0 = 0x00000800, /* General Purpose Interrupts */
2406a081dcdSDavid du Colombier Gpi1 = 0x00001000,
2416a081dcdSDavid du Colombier Gpi2 = 0x00002000,
2426a081dcdSDavid du Colombier Gpi3 = 0x00004000,
2436a081dcdSDavid du Colombier };
2446a081dcdSDavid du Colombier
2456a081dcdSDavid du Colombier /*
2466a081dcdSDavid du Colombier * The Mdic register isn't implemented on the 82543GC,
2476a081dcdSDavid du Colombier * the software defined pins are used instead.
2486a081dcdSDavid du Colombier * These definitions work for the Intel PRO/1000 T Server Adapter.
2496a081dcdSDavid du Colombier * The direction pin bits are read from the EEPROM.
2506a081dcdSDavid du Colombier */
2516a081dcdSDavid du Colombier enum {
2526a081dcdSDavid du Colombier Mdd = ((1<<2)<<SwdpinsloSHIFT), /* data */
2536a081dcdSDavid du Colombier Mddo = ((1<<2)<<SwdpioloSHIFT), /* pin direction */
2546a081dcdSDavid du Colombier Mdc = ((1<<3)<<SwdpinsloSHIFT), /* clock */
2556a081dcdSDavid du Colombier Mdco = ((1<<3)<<SwdpioloSHIFT), /* pin direction */
2566a081dcdSDavid du Colombier Mdr = ((1<<0)<<SwdpinshiSHIFT), /* reset */
2576a081dcdSDavid du Colombier Mdro = ((1<<0)<<SwdpiohiSHIFT), /* pin direction */
2586a081dcdSDavid du Colombier };
2596a081dcdSDavid du Colombier
2606a081dcdSDavid du Colombier enum { /* Txcw */
2616a081dcdSDavid du Colombier TxcwFd = 0x00000020, /* Full Duplex */
2626a081dcdSDavid du Colombier TxcwHd = 0x00000040, /* Half Duplex */
2636a081dcdSDavid du Colombier TxcwPauseMASK = 0x00000180, /* Pause */
2646a081dcdSDavid du Colombier TxcwPauseSHIFT = 7,
2656a081dcdSDavid du Colombier TxcwPs = (1<<TxcwPauseSHIFT), /* Pause Supported */
2666a081dcdSDavid du Colombier TxcwAs = (2<<TxcwPauseSHIFT), /* Asymmetric FC desired */
2676a081dcdSDavid du Colombier TxcwRfiMASK = 0x00003000, /* Remote Fault Indication */
2686a081dcdSDavid du Colombier TxcwRfiSHIFT = 12,
2696a081dcdSDavid du Colombier TxcwNpr = 0x00008000, /* Next Page Request */
2706a081dcdSDavid du Colombier TxcwConfig = 0x40000000, /* Transmit COnfig Control */
2716a081dcdSDavid du Colombier TxcwAne = 0x80000000, /* Auto-Negotiation Enable */
2726a081dcdSDavid du Colombier };
2736a081dcdSDavid du Colombier
2746a081dcdSDavid du Colombier enum { /* Rxcw */
2756a081dcdSDavid du Colombier Rxword = 0x0000FFFF, /* Data from auto-negotiation process */
2766a081dcdSDavid du Colombier Rxnocarrier = 0x04000000, /* Carrier Sense indication */
2776a081dcdSDavid du Colombier Rxinvalid = 0x08000000, /* Invalid Symbol during configuration */
2786a081dcdSDavid du Colombier Rxchange = 0x10000000, /* Change to the Rxword indication */
2796a081dcdSDavid du Colombier Rxconfig = 0x20000000, /* /C/ order set reception indication */
2806a081dcdSDavid du Colombier Rxsync = 0x40000000, /* Lost bit synchronization indication */
2816a081dcdSDavid du Colombier Anc = 0x80000000, /* Auto Negotiation Complete */
2826a081dcdSDavid du Colombier };
2836a081dcdSDavid du Colombier
2846a081dcdSDavid du Colombier enum { /* Rctl */
2856a081dcdSDavid du Colombier Rrst = 0x00000001, /* Receiver Software Reset */
2866a081dcdSDavid du Colombier Ren = 0x00000002, /* Receiver Enable */
2876a081dcdSDavid du Colombier Sbp = 0x00000004, /* Store Bad Packets */
2886a081dcdSDavid du Colombier Upe = 0x00000008, /* Unicast Promiscuous Enable */
2896a081dcdSDavid du Colombier Mpe = 0x00000010, /* Multicast Promiscuous Enable */
2906a081dcdSDavid du Colombier Lpe = 0x00000020, /* Long Packet Reception Enable */
2916a081dcdSDavid du Colombier LbmMASK = 0x000000C0, /* Loopback Mode */
2926a081dcdSDavid du Colombier LbmOFF = 0x00000000, /* No Loopback */
2936a081dcdSDavid du Colombier LbmTBI = 0x00000040, /* TBI Loopback */
2946a081dcdSDavid du Colombier LbmMII = 0x00000080, /* GMII/MII Loopback */
2956a081dcdSDavid du Colombier LbmXCVR = 0x000000C0, /* Transceiver Loopback */
2966a081dcdSDavid du Colombier RdtmsMASK = 0x00000300, /* Rd Minimum Threshold Size */
2976a081dcdSDavid du Colombier RdtmsHALF = 0x00000000, /* Threshold is 1/2 Rdlen */
2986a081dcdSDavid du Colombier RdtmsQUARTER = 0x00000100, /* Threshold is 1/4 Rdlen */
2996a081dcdSDavid du Colombier RdtmsEIGHTH = 0x00000200, /* Threshold is 1/8 Rdlen */
3006a081dcdSDavid du Colombier MoMASK = 0x00003000, /* Multicast Offset */
3016a081dcdSDavid du Colombier Mo47b36 = 0x00000000, /* bits [47:36] of received address */
3026a081dcdSDavid du Colombier Mo46b35 = 0x00001000, /* bits [46:35] of received address */
3036a081dcdSDavid du Colombier Mo45b34 = 0x00002000, /* bits [45:34] of received address */
3046a081dcdSDavid du Colombier Mo43b32 = 0x00003000, /* bits [43:32] of received address */
3056a081dcdSDavid du Colombier Bam = 0x00008000, /* Broadcast Accept Mode */
3066a081dcdSDavid du Colombier BsizeMASK = 0x00030000, /* Receive Buffer Size */
3076a081dcdSDavid du Colombier Bsize2048 = 0x00000000, /* Bsex = 0 */
3086a081dcdSDavid du Colombier Bsize1024 = 0x00010000, /* Bsex = 0 */
3096a081dcdSDavid du Colombier Bsize512 = 0x00020000, /* Bsex = 0 */
3106a081dcdSDavid du Colombier Bsize256 = 0x00030000, /* Bsex = 0 */
3116a081dcdSDavid du Colombier Bsize16384 = 0x00010000, /* Bsex = 1 */
3126a081dcdSDavid du Colombier Vfe = 0x00040000, /* VLAN Filter Enable */
3136a081dcdSDavid du Colombier Cfien = 0x00080000, /* Canonical Form Indicator Enable */
3146a081dcdSDavid du Colombier Cfi = 0x00100000, /* Canonical Form Indicator value */
3156a081dcdSDavid du Colombier Dpf = 0x00400000, /* Discard Pause Frames */
3166a081dcdSDavid du Colombier Pmcf = 0x00800000, /* Pass MAC Control Frames */
3176a081dcdSDavid du Colombier Bsex = 0x02000000, /* Buffer Size Extension */
3186a081dcdSDavid du Colombier Secrc = 0x04000000, /* Strip CRC from incoming packet */
3196a081dcdSDavid du Colombier };
3206a081dcdSDavid du Colombier
3216a081dcdSDavid du Colombier enum { /* Tctl */
3226a081dcdSDavid du Colombier Trst = 0x00000001, /* Transmitter Software Reset */
3236a081dcdSDavid du Colombier Ten = 0x00000002, /* Transmit Enable */
3246a081dcdSDavid du Colombier Psp = 0x00000008, /* Pad Short Packets */
3256a081dcdSDavid du Colombier CtMASK = 0x00000FF0, /* Collision Threshold */
3266a081dcdSDavid du Colombier CtSHIFT = 4,
3276a081dcdSDavid du Colombier ColdMASK = 0x003FF000, /* Collision Distance */
3286a081dcdSDavid du Colombier ColdSHIFT = 12,
3296a081dcdSDavid du Colombier Swxoff = 0x00400000, /* Sofware XOFF Transmission */
3306a081dcdSDavid du Colombier Pbe = 0x00800000, /* Packet Burst Enable */
3316a081dcdSDavid du Colombier Rtlc = 0x01000000, /* Re-transmit on Late Collision */
3326a081dcdSDavid du Colombier Nrtu = 0x02000000, /* No Re-transmit on Underrrun */
3336a081dcdSDavid du Colombier };
3346a081dcdSDavid du Colombier
3356a081dcdSDavid du Colombier enum { /* [RT]xdctl */
3366a081dcdSDavid du Colombier PthreshMASK = 0x0000003F, /* Prefetch Threshold */
3376a081dcdSDavid du Colombier PthreshSHIFT = 0,
3386a081dcdSDavid du Colombier HthreshMASK = 0x00003F00, /* Host Threshold */
3396a081dcdSDavid du Colombier HthreshSHIFT = 8,
3406a081dcdSDavid du Colombier WthreshMASK = 0x003F0000, /* Writeback Threshold */
3416a081dcdSDavid du Colombier WthreshSHIFT = 16,
3426a081dcdSDavid du Colombier Gran = 0x01000000, /* Granularity */
3436a081dcdSDavid du Colombier LthreshMASK = 0xFE000000, /* Low Threshold */
3446a081dcdSDavid du Colombier LthreshSHIFT = 25,
3456a081dcdSDavid du Colombier };
3466a081dcdSDavid du Colombier
3476a081dcdSDavid du Colombier enum { /* Rxcsum */
3486a081dcdSDavid du Colombier PcssMASK = 0x000000FF, /* Packet Checksum Start */
3496a081dcdSDavid du Colombier PcssSHIFT = 0,
3506a081dcdSDavid du Colombier Ipofl = 0x00000100, /* IP Checksum Off-load Enable */
3516a081dcdSDavid du Colombier Tuofl = 0x00000200, /* TCP/UDP Checksum Off-load Enable */
3526a081dcdSDavid du Colombier };
3536a081dcdSDavid du Colombier
3546a081dcdSDavid du Colombier enum { /* Manc */
3556a081dcdSDavid du Colombier Arpen = 0x00002000, /* Enable ARP Request Filtering */
3566a081dcdSDavid du Colombier };
3576a081dcdSDavid du Colombier
3586a081dcdSDavid du Colombier enum { /* Receive Delay Timer Ring */
3596a081dcdSDavid du Colombier DelayMASK = 0x0000FFFF, /* delay timer in 1.024nS increments */
3606a081dcdSDavid du Colombier DelaySHIFT = 0,
3616a081dcdSDavid du Colombier Fpd = 0x80000000, /* Flush partial Descriptor Block */
3626a081dcdSDavid du Colombier };
3636a081dcdSDavid du Colombier
3646a081dcdSDavid du Colombier typedef struct Rd { /* Receive Descriptor */
3656a081dcdSDavid du Colombier uint addr[2];
3666a081dcdSDavid du Colombier ushort length;
3676a081dcdSDavid du Colombier ushort checksum;
3686a081dcdSDavid du Colombier uchar status;
3696a081dcdSDavid du Colombier uchar errors;
3706a081dcdSDavid du Colombier ushort special;
3716a081dcdSDavid du Colombier } Rd;
3726a081dcdSDavid du Colombier
3736a081dcdSDavid du Colombier enum { /* Rd status */
3746a081dcdSDavid du Colombier Rdd = 0x01, /* Descriptor Done */
3756a081dcdSDavid du Colombier Reop = 0x02, /* End of Packet */
3766a081dcdSDavid du Colombier Ixsm = 0x04, /* Ignore Checksum Indication */
3776a081dcdSDavid du Colombier Vp = 0x08, /* Packet is 802.1Q (matched VET) */
3786a081dcdSDavid du Colombier Tcpcs = 0x20, /* TCP Checksum Calculated on Packet */
3796a081dcdSDavid du Colombier Ipcs = 0x40, /* IP Checksum Calculated on Packet */
3806a081dcdSDavid du Colombier Pif = 0x80, /* Passed in-exact filter */
3816a081dcdSDavid du Colombier };
3826a081dcdSDavid du Colombier
3836a081dcdSDavid du Colombier enum { /* Rd errors */
3846a081dcdSDavid du Colombier Ce = 0x01, /* CRC Error or Alignment Error */
3856a081dcdSDavid du Colombier Se = 0x02, /* Symbol Error */
3866a081dcdSDavid du Colombier Seq = 0x04, /* Sequence Error */
3876a081dcdSDavid du Colombier Cxe = 0x10, /* Carrier Extension Error */
3886a081dcdSDavid du Colombier Tcpe = 0x20, /* TCP/UDP Checksum Error */
3896a081dcdSDavid du Colombier Ipe = 0x40, /* IP Checksum Error */
3906a081dcdSDavid du Colombier Rxe = 0x80, /* RX Data Error */
3916a081dcdSDavid du Colombier };
3926a081dcdSDavid du Colombier
39359c21d95SDavid du Colombier typedef struct Td Td;
39459c21d95SDavid du Colombier struct Td { /* Transmit Descriptor */
3956a081dcdSDavid du Colombier union {
3966a081dcdSDavid du Colombier uint addr[2]; /* Data */
3976a081dcdSDavid du Colombier struct { /* Context */
3986a081dcdSDavid du Colombier uchar ipcss;
3996a081dcdSDavid du Colombier uchar ipcso;
4006a081dcdSDavid du Colombier ushort ipcse;
4016a081dcdSDavid du Colombier uchar tucss;
4026a081dcdSDavid du Colombier uchar tucso;
4036a081dcdSDavid du Colombier ushort tucse;
4046a081dcdSDavid du Colombier };
4056a081dcdSDavid du Colombier };
4066a081dcdSDavid du Colombier uint control;
4076a081dcdSDavid du Colombier uint status;
40859c21d95SDavid du Colombier };
4096a081dcdSDavid du Colombier
4106a081dcdSDavid du Colombier enum { /* Td control */
4116a081dcdSDavid du Colombier LenMASK = 0x000FFFFF, /* Data/Packet Length Field */
4126a081dcdSDavid du Colombier LenSHIFT = 0,
4136a081dcdSDavid du Colombier DtypeCD = 0x00000000, /* Data Type 'Context Descriptor' */
4146a081dcdSDavid du Colombier DtypeDD = 0x00100000, /* Data Type 'Data Descriptor' */
4156a081dcdSDavid du Colombier PtypeTCP = 0x01000000, /* TCP/UDP Packet Type (CD) */
4166a081dcdSDavid du Colombier Teop = 0x01000000, /* End of Packet (DD) */
4176a081dcdSDavid du Colombier PtypeIP = 0x02000000, /* IP Packet Type (CD) */
4186a081dcdSDavid du Colombier Ifcs = 0x02000000, /* Insert FCS (DD) */
4196a081dcdSDavid du Colombier Tse = 0x04000000, /* TCP Segmentation Enable */
4206a081dcdSDavid du Colombier Rs = 0x08000000, /* Report Status */
4216a081dcdSDavid du Colombier Rps = 0x10000000, /* Report Status Sent */
4226a081dcdSDavid du Colombier Dext = 0x20000000, /* Descriptor Extension */
4236a081dcdSDavid du Colombier Vle = 0x40000000, /* VLAN Packet Enable */
4246a081dcdSDavid du Colombier Ide = 0x80000000, /* Interrupt Delay Enable */
4256a081dcdSDavid du Colombier };
4266a081dcdSDavid du Colombier
4276a081dcdSDavid du Colombier enum { /* Td status */
4286a081dcdSDavid du Colombier Tdd = 0x00000001, /* Descriptor Done */
4296a081dcdSDavid du Colombier Ec = 0x00000002, /* Excess Collisions */
4306a081dcdSDavid du Colombier Lc = 0x00000004, /* Late Collision */
4316a081dcdSDavid du Colombier Tu = 0x00000008, /* Transmit Underrun */
4326a081dcdSDavid du Colombier Iixsm = 0x00000100, /* Insert IP Checksum */
4336a081dcdSDavid du Colombier Itxsm = 0x00000200, /* Insert TCP/UDP Checksum */
4346a081dcdSDavid du Colombier HdrlenMASK = 0x0000FF00, /* Header Length (Tse) */
4356a081dcdSDavid du Colombier HdrlenSHIFT = 8,
4366a081dcdSDavid du Colombier VlanMASK = 0x0FFF0000, /* VLAN Identifier */
4376a081dcdSDavid du Colombier VlanSHIFT = 16,
4386a081dcdSDavid du Colombier Tcfi = 0x10000000, /* Canonical Form Indicator */
4396a081dcdSDavid du Colombier PriMASK = 0xE0000000, /* User Priority */
4406a081dcdSDavid du Colombier PriSHIFT = 29,
4416a081dcdSDavid du Colombier MssMASK = 0xFFFF0000, /* Maximum Segment Size (Tse) */
4426a081dcdSDavid du Colombier MssSHIFT = 16,
4436a081dcdSDavid du Colombier };
4446a081dcdSDavid du Colombier
4456a081dcdSDavid du Colombier enum {
4466a081dcdSDavid du Colombier Rbsz = 2048,
447588d0145SDavid du Colombier /* were 256, 1024 & 64, but 52, 253 and 9 are ample. */
448588d0145SDavid du Colombier Nrd = 128, /* multiple of 8 */
449*cebd3b46SDavid du Colombier Nrb = 512, /* private receive buffers per Ctlr */
450588d0145SDavid du Colombier Ntd = 32, /* multiple of 8 */
4516a081dcdSDavid du Colombier };
4526a081dcdSDavid du Colombier
4536a081dcdSDavid du Colombier typedef struct Ctlr Ctlr;
454c02f0a41SDavid du Colombier struct Ctlr {
4558466d066SDavid du Colombier int port;
4566a081dcdSDavid du Colombier Pcidev* pcidev;
4576a081dcdSDavid du Colombier Ctlr* next;
458208510e1SDavid du Colombier Ether* edev;
4596a081dcdSDavid du Colombier int active;
4606a081dcdSDavid du Colombier int started;
4616a081dcdSDavid du Colombier int id;
4626a081dcdSDavid du Colombier int cls;
4636a081dcdSDavid du Colombier ushort eeprom[0x40];
4646a081dcdSDavid du Colombier
4656a081dcdSDavid du Colombier QLock alock; /* attach */
4666a081dcdSDavid du Colombier void* alloc; /* receive/transmit descriptors */
4676a081dcdSDavid du Colombier int nrd;
4686a081dcdSDavid du Colombier int ntd;
469c02f0a41SDavid du Colombier int nrb; /* # bufs this Ctlr has in the pool */
4706a081dcdSDavid du Colombier
4716a081dcdSDavid du Colombier int* nic;
4726a081dcdSDavid du Colombier Lock imlock;
4736a081dcdSDavid du Colombier int im; /* interrupt mask */
4746a081dcdSDavid du Colombier
4756a081dcdSDavid du Colombier Mii* mii;
4766a081dcdSDavid du Colombier Rendez lrendez;
4776a081dcdSDavid du Colombier int lim;
4786a081dcdSDavid du Colombier
4796a081dcdSDavid du Colombier int link;
4806a081dcdSDavid du Colombier
481217e9e83SDavid du Colombier Watermark wmrb;
482217e9e83SDavid du Colombier Watermark wmrd;
483217e9e83SDavid du Colombier Watermark wmtd;
484217e9e83SDavid du Colombier
4856a081dcdSDavid du Colombier QLock slock;
4866a081dcdSDavid du Colombier uint statistics[Nstatistics];
4876a081dcdSDavid du Colombier uint lsleep;
4886a081dcdSDavid du Colombier uint lintr;
4896a081dcdSDavid du Colombier uint rsleep;
4906a081dcdSDavid du Colombier uint rintr;
4916a081dcdSDavid du Colombier uint txdw;
4926a081dcdSDavid du Colombier uint tintr;
4936a081dcdSDavid du Colombier uint ixsm;
4946a081dcdSDavid du Colombier uint ipcs;
4956a081dcdSDavid du Colombier uint tcpcs;
4966a081dcdSDavid du Colombier
4976a081dcdSDavid du Colombier uchar ra[Eaddrlen]; /* receive address */
4986a081dcdSDavid du Colombier ulong mta[128]; /* multicast table array */
4996a081dcdSDavid du Colombier
5006a081dcdSDavid du Colombier Rendez rrendez;
5016a081dcdSDavid du Colombier int rim;
502c02f0a41SDavid du Colombier int rdfree; /* rx descriptors awaiting packets */
5036a081dcdSDavid du Colombier Rd* rdba; /* receive descriptor base address */
5046a081dcdSDavid du Colombier Block** rb; /* receive buffers */
5056a081dcdSDavid du Colombier int rdh; /* receive descriptor head */
5066a081dcdSDavid du Colombier int rdt; /* receive descriptor tail */
5076a081dcdSDavid du Colombier int rdtr; /* receive delay timer ring value */
5086a081dcdSDavid du Colombier
5096a081dcdSDavid du Colombier Lock tlock;
5106a081dcdSDavid du Colombier int tdfree;
5116a081dcdSDavid du Colombier Td* tdba; /* transmit descriptor base address */
5126a081dcdSDavid du Colombier Block** tb; /* transmit buffers */
5136a081dcdSDavid du Colombier int tdh; /* transmit descriptor head */
5146a081dcdSDavid du Colombier int tdt; /* transmit descriptor tail */
5156a081dcdSDavid du Colombier
5166a081dcdSDavid du Colombier int txcw;
5176a081dcdSDavid du Colombier int fcrtl;
5186a081dcdSDavid du Colombier int fcrth;
519c02f0a41SDavid du Colombier };
5206a081dcdSDavid du Colombier
5216a081dcdSDavid du Colombier #define csr32r(c, r) (*((c)->nic+((r)/4)))
5226a081dcdSDavid du Colombier #define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v))
5236a081dcdSDavid du Colombier
5246a081dcdSDavid du Colombier static Ctlr* igbectlrhead;
5256a081dcdSDavid du Colombier static Ctlr* igbectlrtail;
5266a081dcdSDavid du Colombier
5276a081dcdSDavid du Colombier static Lock igberblock; /* free receive Blocks */
528208510e1SDavid du Colombier static Block* igberbpool; /* receive Blocks for all igbe controllers */
529217e9e83SDavid du Colombier static int nrbfull; /* # of rcv Blocks with data awaiting processing */
5306a081dcdSDavid du Colombier
5316a081dcdSDavid du Colombier static char* statistics[Nstatistics] = {
5326a081dcdSDavid du Colombier "CRC Error",
5336a081dcdSDavid du Colombier "Alignment Error",
5346a081dcdSDavid du Colombier "Symbol Error",
5356a081dcdSDavid du Colombier "RX Error",
5366a081dcdSDavid du Colombier "Missed Packets",
5376a081dcdSDavid du Colombier "Single Collision",
5386a081dcdSDavid du Colombier "Excessive Collisions",
5396a081dcdSDavid du Colombier "Multiple Collision",
5406a081dcdSDavid du Colombier "Late Collisions",
5416a081dcdSDavid du Colombier nil,
5426a081dcdSDavid du Colombier "Collision",
5436a081dcdSDavid du Colombier "Transmit Underrun",
5446a081dcdSDavid du Colombier "Defer",
5456a081dcdSDavid du Colombier "Transmit - No CRS",
5466a081dcdSDavid du Colombier "Sequence Error",
5476a081dcdSDavid du Colombier "Carrier Extension Error",
5486a081dcdSDavid du Colombier "Receive Error Length",
5496a081dcdSDavid du Colombier nil,
5506a081dcdSDavid du Colombier "XON Received",
5516a081dcdSDavid du Colombier "XON Transmitted",
5526a081dcdSDavid du Colombier "XOFF Received",
5536a081dcdSDavid du Colombier "XOFF Transmitted",
5546a081dcdSDavid du Colombier "FC Received Unsupported",
5556a081dcdSDavid du Colombier "Packets Received (64 Bytes)",
5566a081dcdSDavid du Colombier "Packets Received (65-127 Bytes)",
5576a081dcdSDavid du Colombier "Packets Received (128-255 Bytes)",
5586a081dcdSDavid du Colombier "Packets Received (256-511 Bytes)",
5596a081dcdSDavid du Colombier "Packets Received (512-1023 Bytes)",
5606a081dcdSDavid du Colombier "Packets Received (1024-1522 Bytes)",
5616a081dcdSDavid du Colombier "Good Packets Received",
5626a081dcdSDavid du Colombier "Broadcast Packets Received",
5636a081dcdSDavid du Colombier "Multicast Packets Received",
5646a081dcdSDavid du Colombier "Good Packets Transmitted",
5656a081dcdSDavid du Colombier nil,
5666a081dcdSDavid du Colombier "Good Octets Received",
5676a081dcdSDavid du Colombier nil,
5686a081dcdSDavid du Colombier "Good Octets Transmitted",
5696a081dcdSDavid du Colombier nil,
5706a081dcdSDavid du Colombier nil,
5716a081dcdSDavid du Colombier nil,
5726a081dcdSDavid du Colombier "Receive No Buffers",
5736a081dcdSDavid du Colombier "Receive Undersize",
5746a081dcdSDavid du Colombier "Receive Fragment",
5756a081dcdSDavid du Colombier "Receive Oversize",
5766a081dcdSDavid du Colombier "Receive Jabber",
5776a081dcdSDavid du Colombier nil,
5786a081dcdSDavid du Colombier nil,
5796a081dcdSDavid du Colombier nil,
5806a081dcdSDavid du Colombier "Total Octets Received",
5816a081dcdSDavid du Colombier nil,
5826a081dcdSDavid du Colombier "Total Octets Transmitted",
5836a081dcdSDavid du Colombier nil,
5846a081dcdSDavid du Colombier "Total Packets Received",
5856a081dcdSDavid du Colombier "Total Packets Transmitted",
5866a081dcdSDavid du Colombier "Packets Transmitted (64 Bytes)",
5876a081dcdSDavid du Colombier "Packets Transmitted (65-127 Bytes)",
5886a081dcdSDavid du Colombier "Packets Transmitted (128-255 Bytes)",
5896a081dcdSDavid du Colombier "Packets Transmitted (256-511 Bytes)",
5906a081dcdSDavid du Colombier "Packets Transmitted (512-1023 Bytes)",
5916a081dcdSDavid du Colombier "Packets Transmitted (1024-1522 Bytes)",
5926a081dcdSDavid du Colombier "Multicast Packets Transmitted",
5936a081dcdSDavid du Colombier "Broadcast Packets Transmitted",
5946a081dcdSDavid du Colombier "TCP Segmentation Context Transmitted",
5956a081dcdSDavid du Colombier "TCP Segmentation Context Fail",
5966a081dcdSDavid du Colombier };
5976a081dcdSDavid du Colombier
5986a081dcdSDavid du Colombier static long
igbeifstat(Ether * edev,void * a,long n,ulong offset)5996a081dcdSDavid du Colombier igbeifstat(Ether* edev, void* a, long n, ulong offset)
6006a081dcdSDavid du Colombier {
6016a081dcdSDavid du Colombier Ctlr *ctlr;
602217e9e83SDavid du Colombier char *p, *s, *e;
6036a081dcdSDavid du Colombier int i, l, r;
6046a081dcdSDavid du Colombier uvlong tuvl, ruvl;
6056a081dcdSDavid du Colombier
6066a081dcdSDavid du Colombier ctlr = edev->ctlr;
6076a081dcdSDavid du Colombier qlock(&ctlr->slock);
60846136019SDavid du Colombier p = malloc(READSTR);
609aa72973aSDavid du Colombier if(p == nil) {
610aa72973aSDavid du Colombier qunlock(&ctlr->slock);
611aa72973aSDavid du Colombier error(Enomem);
612aa72973aSDavid du Colombier }
6136a081dcdSDavid du Colombier l = 0;
6146a081dcdSDavid du Colombier for(i = 0; i < Nstatistics; i++){
6156a081dcdSDavid du Colombier r = csr32r(ctlr, Statistics+i*4);
6166a081dcdSDavid du Colombier if((s = statistics[i]) == nil)
6176a081dcdSDavid du Colombier continue;
6186a081dcdSDavid du Colombier switch(i){
6196a081dcdSDavid du Colombier case Gorcl:
6206a081dcdSDavid du Colombier case Gotcl:
6216a081dcdSDavid du Colombier case Torl:
6226a081dcdSDavid du Colombier case Totl:
6236a081dcdSDavid du Colombier ruvl = r;
6246a081dcdSDavid du Colombier ruvl += ((uvlong)csr32r(ctlr, Statistics+(i+1)*4))<<32;
6256a081dcdSDavid du Colombier tuvl = ruvl;
6266a081dcdSDavid du Colombier tuvl += ctlr->statistics[i];
6276a081dcdSDavid du Colombier tuvl += ((uvlong)ctlr->statistics[i+1])<<32;
6286a081dcdSDavid du Colombier if(tuvl == 0)
6296a081dcdSDavid du Colombier continue;
6306a081dcdSDavid du Colombier ctlr->statistics[i] = tuvl;
6316a081dcdSDavid du Colombier ctlr->statistics[i+1] = tuvl>>32;
63246136019SDavid du Colombier l += snprint(p+l, READSTR-l, "%s: %llud %llud\n",
6336a081dcdSDavid du Colombier s, tuvl, ruvl);
6346a081dcdSDavid du Colombier i++;
6356a081dcdSDavid du Colombier break;
6366a081dcdSDavid du Colombier
6376a081dcdSDavid du Colombier default:
6386a081dcdSDavid du Colombier ctlr->statistics[i] += r;
6396a081dcdSDavid du Colombier if(ctlr->statistics[i] == 0)
6406a081dcdSDavid du Colombier continue;
64146136019SDavid du Colombier l += snprint(p+l, READSTR-l, "%s: %ud %ud\n",
6426a081dcdSDavid du Colombier s, ctlr->statistics[i], r);
6436a081dcdSDavid du Colombier break;
6446a081dcdSDavid du Colombier }
6456a081dcdSDavid du Colombier }
6466a081dcdSDavid du Colombier
64746136019SDavid du Colombier l += snprint(p+l, READSTR-l, "lintr: %ud %ud\n",
6486a081dcdSDavid du Colombier ctlr->lintr, ctlr->lsleep);
64946136019SDavid du Colombier l += snprint(p+l, READSTR-l, "rintr: %ud %ud\n",
6506a081dcdSDavid du Colombier ctlr->rintr, ctlr->rsleep);
65146136019SDavid du Colombier l += snprint(p+l, READSTR-l, "tintr: %ud %ud\n",
6526a081dcdSDavid du Colombier ctlr->tintr, ctlr->txdw);
65346136019SDavid du Colombier l += snprint(p+l, READSTR-l, "ixcs: %ud %ud %ud\n",
6546a081dcdSDavid du Colombier ctlr->ixsm, ctlr->ipcs, ctlr->tcpcs);
65546136019SDavid du Colombier l += snprint(p+l, READSTR-l, "rdtr: %ud\n", ctlr->rdtr);
65646136019SDavid du Colombier l += snprint(p+l, READSTR-l, "Ctrlext: %08x\n", csr32r(ctlr, Ctrlext));
6576a081dcdSDavid du Colombier
65846136019SDavid du Colombier l += snprint(p+l, READSTR-l, "eeprom:");
6596a081dcdSDavid du Colombier for(i = 0; i < 0x40; i++){
6606a081dcdSDavid du Colombier if(i && ((i & 0x07) == 0))
66146136019SDavid du Colombier l += snprint(p+l, READSTR-l, "\n ");
66246136019SDavid du Colombier l += snprint(p+l, READSTR-l, " %4.4uX", ctlr->eeprom[i]);
6636a081dcdSDavid du Colombier }
66446136019SDavid du Colombier l += snprint(p+l, READSTR-l, "\n");
6656a081dcdSDavid du Colombier
6666a081dcdSDavid du Colombier if(ctlr->mii != nil && ctlr->mii->curphy != nil){
667923c9daaSDavid du Colombier l += snprint(p+l, READSTR-l, "phy: ");
6686a081dcdSDavid du Colombier for(i = 0; i < NMiiPhyr; i++){
6696a081dcdSDavid du Colombier if(i && ((i & 0x07) == 0))
67046136019SDavid du Colombier l += snprint(p+l, READSTR-l, "\n ");
6716a081dcdSDavid du Colombier r = miimir(ctlr->mii, i);
67246136019SDavid du Colombier l += snprint(p+l, READSTR-l, " %4.4uX", r);
6736a081dcdSDavid du Colombier }
67446136019SDavid du Colombier snprint(p+l, READSTR-l, "\n");
6756a081dcdSDavid du Colombier }
676217e9e83SDavid du Colombier e = p + READSTR;
677217e9e83SDavid du Colombier s = p + l + 1;
678217e9e83SDavid du Colombier s = seprintmark(s, e, &ctlr->wmrb);
679217e9e83SDavid du Colombier s = seprintmark(s, e, &ctlr->wmrd);
680217e9e83SDavid du Colombier s = seprintmark(s, e, &ctlr->wmtd);
681217e9e83SDavid du Colombier USED(s);
682217e9e83SDavid du Colombier
6836a081dcdSDavid du Colombier n = readstr(offset, a, n, p);
6846a081dcdSDavid du Colombier free(p);
6856a081dcdSDavid du Colombier qunlock(&ctlr->slock);
6866a081dcdSDavid du Colombier
6876a081dcdSDavid du Colombier return n;
6886a081dcdSDavid du Colombier }
6896a081dcdSDavid du Colombier
6906a081dcdSDavid du Colombier enum {
6916a081dcdSDavid du Colombier CMrdtr,
6926a081dcdSDavid du Colombier };
6936a081dcdSDavid du Colombier
6946a081dcdSDavid du Colombier static Cmdtab igbectlmsg[] = {
6956a081dcdSDavid du Colombier CMrdtr, "rdtr", 2,
6966a081dcdSDavid du Colombier };
6976a081dcdSDavid du Colombier
6986a081dcdSDavid du Colombier static long
igbectl(Ether * edev,void * buf,long n)6996a081dcdSDavid du Colombier igbectl(Ether* edev, void* buf, long n)
7006a081dcdSDavid du Colombier {
7016a081dcdSDavid du Colombier int v;
7026a081dcdSDavid du Colombier char *p;
7036a081dcdSDavid du Colombier Ctlr *ctlr;
7046a081dcdSDavid du Colombier Cmdbuf *cb;
7056a081dcdSDavid du Colombier Cmdtab *ct;
7066a081dcdSDavid du Colombier
7076a081dcdSDavid du Colombier if((ctlr = edev->ctlr) == nil)
7086a081dcdSDavid du Colombier error(Enonexist);
7096a081dcdSDavid du Colombier
7106a081dcdSDavid du Colombier cb = parsecmd(buf, n);
7116a081dcdSDavid du Colombier if(waserror()){
7126a081dcdSDavid du Colombier free(cb);
7136a081dcdSDavid du Colombier nexterror();
7146a081dcdSDavid du Colombier }
7156a081dcdSDavid du Colombier
7166a081dcdSDavid du Colombier ct = lookupcmd(cb, igbectlmsg, nelem(igbectlmsg));
7176a081dcdSDavid du Colombier switch(ct->index){
7186a081dcdSDavid du Colombier case CMrdtr:
7196a081dcdSDavid du Colombier v = strtol(cb->f[1], &p, 0);
7206a081dcdSDavid du Colombier if(v < 0 || p == cb->f[1] || v > 0xFFFF)
7216a081dcdSDavid du Colombier error(Ebadarg);
7226520663fSDavid du Colombier ctlr->rdtr = v;
7236a081dcdSDavid du Colombier csr32w(ctlr, Rdtr, Fpd|v);
7246a081dcdSDavid du Colombier break;
7256a081dcdSDavid du Colombier }
7266a081dcdSDavid du Colombier free(cb);
7276a081dcdSDavid du Colombier poperror();
7286a081dcdSDavid du Colombier
7296a081dcdSDavid du Colombier return n;
7306a081dcdSDavid du Colombier }
7316a081dcdSDavid du Colombier
7326a081dcdSDavid du Colombier static void
igbepromiscuous(void * arg,int on)7336a081dcdSDavid du Colombier igbepromiscuous(void* arg, int on)
7346a081dcdSDavid du Colombier {
7356a081dcdSDavid du Colombier int rctl;
7366a081dcdSDavid du Colombier Ctlr *ctlr;
7376a081dcdSDavid du Colombier Ether *edev;
7386a081dcdSDavid du Colombier
7396a081dcdSDavid du Colombier edev = arg;
7406a081dcdSDavid du Colombier ctlr = edev->ctlr;
7416a081dcdSDavid du Colombier
7426a081dcdSDavid du Colombier rctl = csr32r(ctlr, Rctl);
7436a081dcdSDavid du Colombier rctl &= ~MoMASK;
7446a081dcdSDavid du Colombier rctl |= Mo47b36;
7456a081dcdSDavid du Colombier if(on)
7466a081dcdSDavid du Colombier rctl |= Upe|Mpe;
7476a081dcdSDavid du Colombier else
7486a081dcdSDavid du Colombier rctl &= ~(Upe|Mpe);
749b1707c5dSDavid du Colombier csr32w(ctlr, Rctl, rctl|Mpe); /* temporarily keep Mpe on */
7506a081dcdSDavid du Colombier }
7516a081dcdSDavid du Colombier
752ecea9424SDavid du Colombier static void
igbemulticast(void * arg,uchar * addr,int add)753b1707c5dSDavid du Colombier igbemulticast(void* arg, uchar* addr, int add)
754ecea9424SDavid du Colombier {
755ecea9424SDavid du Colombier int bit, x;
756ecea9424SDavid du Colombier Ctlr *ctlr;
757ecea9424SDavid du Colombier Ether *edev;
758ecea9424SDavid du Colombier
759ecea9424SDavid du Colombier edev = arg;
760ecea9424SDavid du Colombier ctlr = edev->ctlr;
761ecea9424SDavid du Colombier
762ecea9424SDavid du Colombier x = addr[5]>>1;
763ecea9424SDavid du Colombier bit = ((addr[5] & 1)<<4)|(addr[4]>>4);
764b1707c5dSDavid du Colombier /*
765b1707c5dSDavid du Colombier * multiple ether addresses can hash to the same filter bit,
766b1707c5dSDavid du Colombier * so it's never safe to clear a filter bit.
767b1707c5dSDavid du Colombier * if we want to clear filter bits, we need to keep track of
768b1707c5dSDavid du Colombier * all the multicast addresses in use, clear all the filter bits,
769b1707c5dSDavid du Colombier * then set the ones corresponding to in-use addresses.
770b1707c5dSDavid du Colombier */
771b1707c5dSDavid du Colombier if(add)
772ecea9424SDavid du Colombier ctlr->mta[x] |= 1<<bit;
773b1707c5dSDavid du Colombier // else
774b1707c5dSDavid du Colombier // ctlr->mta[x] &= ~(1<<bit);
775ecea9424SDavid du Colombier
776ecea9424SDavid du Colombier csr32w(ctlr, Mta+x*4, ctlr->mta[x]);
777ecea9424SDavid du Colombier }
778ecea9424SDavid du Colombier
7796a081dcdSDavid du Colombier static Block*
igberballoc(void)7806a081dcdSDavid du Colombier igberballoc(void)
7816a081dcdSDavid du Colombier {
7826a081dcdSDavid du Colombier Block *bp;
7836a081dcdSDavid du Colombier
7846a081dcdSDavid du Colombier ilock(&igberblock);
7856a081dcdSDavid du Colombier if((bp = igberbpool) != nil){
7866a081dcdSDavid du Colombier igberbpool = bp->next;
7876a081dcdSDavid du Colombier bp->next = nil;
788b8a11165SDavid du Colombier _xinc(&bp->ref); /* prevent bp from being freed */
7896a081dcdSDavid du Colombier }
7906a081dcdSDavid du Colombier iunlock(&igberblock);
7916a081dcdSDavid du Colombier
7926a081dcdSDavid du Colombier return bp;
7936a081dcdSDavid du Colombier }
7946a081dcdSDavid du Colombier
7956a081dcdSDavid du Colombier static void
igberbfree(Block * bp)7966a081dcdSDavid du Colombier igberbfree(Block* bp)
7976a081dcdSDavid du Colombier {
798ff8c3af2SDavid du Colombier bp->rp = bp->lim - Rbsz;
799ff8c3af2SDavid du Colombier bp->wp = bp->rp;
800bfb6eab9SDavid du Colombier bp->flag &= ~(Bipck | Budpck | Btcpck | Bpktck);
801ff8c3af2SDavid du Colombier
8026a081dcdSDavid du Colombier ilock(&igberblock);
8036a081dcdSDavid du Colombier bp->next = igberbpool;
8046a081dcdSDavid du Colombier igberbpool = bp;
805217e9e83SDavid du Colombier nrbfull--;
8066a081dcdSDavid du Colombier iunlock(&igberblock);
8076a081dcdSDavid du Colombier }
8086a081dcdSDavid du Colombier
8096a081dcdSDavid du Colombier static void
igbeim(Ctlr * ctlr,int im)8106a081dcdSDavid du Colombier igbeim(Ctlr* ctlr, int im)
8116a081dcdSDavid du Colombier {
8126a081dcdSDavid du Colombier ilock(&ctlr->imlock);
8136a081dcdSDavid du Colombier ctlr->im |= im;
8146a081dcdSDavid du Colombier csr32w(ctlr, Ims, ctlr->im);
8156a081dcdSDavid du Colombier iunlock(&ctlr->imlock);
8166a081dcdSDavid du Colombier }
8176a081dcdSDavid du Colombier
8186a081dcdSDavid du Colombier static int
igbelim(void * ctlr)8196a081dcdSDavid du Colombier igbelim(void* ctlr)
8206a081dcdSDavid du Colombier {
8216a081dcdSDavid du Colombier return ((Ctlr*)ctlr)->lim != 0;
8226a081dcdSDavid du Colombier }
8236a081dcdSDavid du Colombier
8246a081dcdSDavid du Colombier static void
igbelproc(void * arg)825ff8c3af2SDavid du Colombier igbelproc(void* arg)
8266a081dcdSDavid du Colombier {
8276a081dcdSDavid du Colombier Ctlr *ctlr;
8286a081dcdSDavid du Colombier Ether *edev;
8296a081dcdSDavid du Colombier MiiPhy *phy;
8306a081dcdSDavid du Colombier int ctrl, r;
8316a081dcdSDavid du Colombier
832ff8c3af2SDavid du Colombier edev = arg;
8336a081dcdSDavid du Colombier ctlr = edev->ctlr;
8346a081dcdSDavid du Colombier for(;;){
835bf3a53f8SDavid du Colombier if(ctlr->mii == nil || ctlr->mii->curphy == nil) {
836bf3a53f8SDavid du Colombier sched();
8376a081dcdSDavid du Colombier continue;
838bf3a53f8SDavid du Colombier }
8396a081dcdSDavid du Colombier
8406a081dcdSDavid du Colombier /*
8416a081dcdSDavid du Colombier * To do:
8426a081dcdSDavid du Colombier * logic to manage status change,
8436a081dcdSDavid du Colombier * this is incomplete but should work
8446a081dcdSDavid du Colombier * one time to set up the hardware.
8456a081dcdSDavid du Colombier *
8466a081dcdSDavid du Colombier * MiiPhy.speed, etc. should be in Mii.
8476a081dcdSDavid du Colombier */
8486a081dcdSDavid du Colombier if(miistatus(ctlr->mii) < 0)
8496a081dcdSDavid du Colombier //continue;
8506a081dcdSDavid du Colombier goto enable;
8516a081dcdSDavid du Colombier
8526a081dcdSDavid du Colombier phy = ctlr->mii->curphy;
8536a081dcdSDavid du Colombier ctrl = csr32r(ctlr, Ctrl);
8546a081dcdSDavid du Colombier
8556a081dcdSDavid du Colombier switch(ctlr->id){
856aeb1c8a5SDavid du Colombier case i82543gc:
857aeb1c8a5SDavid du Colombier case i82544ei:
85807c32395SDavid du Colombier case i82544eif:
8596a081dcdSDavid du Colombier default:
8606a081dcdSDavid du Colombier if(!(ctrl & Asde)){
8616a081dcdSDavid du Colombier ctrl &= ~(SspeedMASK|Ilos|Fd);
8626a081dcdSDavid du Colombier ctrl |= Frcdplx|Frcspd;
8636a081dcdSDavid du Colombier if(phy->speed == 1000)
8646a081dcdSDavid du Colombier ctrl |= Sspeed1000;
8656a081dcdSDavid du Colombier else if(phy->speed == 100)
8666a081dcdSDavid du Colombier ctrl |= Sspeed100;
8676a081dcdSDavid du Colombier if(phy->fd)
8686a081dcdSDavid du Colombier ctrl |= Fd;
8696a081dcdSDavid du Colombier }
8706a081dcdSDavid du Colombier break;
871aeb1c8a5SDavid du Colombier
872aeb1c8a5SDavid du Colombier case i82540em:
873aeb1c8a5SDavid du Colombier case i82540eplp:
874aeb1c8a5SDavid du Colombier case i82547gi:
875aeb1c8a5SDavid du Colombier case i82541gi:
8768466d066SDavid du Colombier case i82541gi2:
877d717568cSDavid du Colombier case i82541pi:
8786a081dcdSDavid du Colombier break;
8796a081dcdSDavid du Colombier }
8806a081dcdSDavid du Colombier
8816a081dcdSDavid du Colombier /*
8826a081dcdSDavid du Colombier * Collision Distance.
8836a081dcdSDavid du Colombier */
8846a081dcdSDavid du Colombier r = csr32r(ctlr, Tctl);
8856a081dcdSDavid du Colombier r &= ~ColdMASK;
8866a081dcdSDavid du Colombier if(phy->fd)
8876a081dcdSDavid du Colombier r |= 64<<ColdSHIFT;
8886a081dcdSDavid du Colombier else
8896a081dcdSDavid du Colombier r |= 512<<ColdSHIFT;
8906a081dcdSDavid du Colombier csr32w(ctlr, Tctl, r);
8916a081dcdSDavid du Colombier
8926a081dcdSDavid du Colombier /*
8936a081dcdSDavid du Colombier * Flow control.
8946a081dcdSDavid du Colombier */
8956a081dcdSDavid du Colombier if(phy->rfc)
8966a081dcdSDavid du Colombier ctrl |= Rfce;
8976a081dcdSDavid du Colombier if(phy->tfc)
8986a081dcdSDavid du Colombier ctrl |= Tfce;
8996a081dcdSDavid du Colombier csr32w(ctlr, Ctrl, ctrl);
9006a081dcdSDavid du Colombier
9016a081dcdSDavid du Colombier enable:
9026a081dcdSDavid du Colombier ctlr->lim = 0;
9036a081dcdSDavid du Colombier igbeim(ctlr, Lsc);
9046a081dcdSDavid du Colombier
9056a081dcdSDavid du Colombier ctlr->lsleep++;
9066a081dcdSDavid du Colombier sleep(&ctlr->lrendez, igbelim, ctlr);
9076a081dcdSDavid du Colombier }
9086a081dcdSDavid du Colombier }
9096a081dcdSDavid du Colombier
9106a081dcdSDavid du Colombier static void
igbetxinit(Ctlr * ctlr)9116a081dcdSDavid du Colombier igbetxinit(Ctlr* ctlr)
9126a081dcdSDavid du Colombier {
9136a081dcdSDavid du Colombier int i, r;
9146a081dcdSDavid du Colombier Block *bp;
9156a081dcdSDavid du Colombier
9166a081dcdSDavid du Colombier csr32w(ctlr, Tctl, (0x0F<<CtSHIFT)|Psp|(66<<ColdSHIFT));
9176a081dcdSDavid du Colombier switch(ctlr->id){
9186a081dcdSDavid du Colombier default:
9196a081dcdSDavid du Colombier r = 6;
9206a081dcdSDavid du Colombier break;
921aeb1c8a5SDavid du Colombier case i82543gc:
922aeb1c8a5SDavid du Colombier case i82544ei:
92307c32395SDavid du Colombier case i82544eif:
9249a8968c3SDavid du Colombier case i82544gc:
925aeb1c8a5SDavid du Colombier case i82540em:
926aeb1c8a5SDavid du Colombier case i82540eplp:
9275efba406SDavid du Colombier case i82541ei:
928aeb1c8a5SDavid du Colombier case i82541gi:
9298466d066SDavid du Colombier case i82541gi2:
930d717568cSDavid du Colombier case i82541pi:
931c079eed3SDavid du Colombier case i82545em:
932ab3dc52fSDavid du Colombier case i82545gmc:
933aeb1c8a5SDavid du Colombier case i82546gb:
9345b7d6169SDavid du Colombier case i82546eb:
9355efba406SDavid du Colombier case i82547ei:
936aeb1c8a5SDavid du Colombier case i82547gi:
9376a081dcdSDavid du Colombier r = 8;
9386a081dcdSDavid du Colombier break;
9396a081dcdSDavid du Colombier }
9406a081dcdSDavid du Colombier csr32w(ctlr, Tipg, (6<<20)|(8<<10)|r);
9416a081dcdSDavid du Colombier csr32w(ctlr, Ait, 0);
9426a081dcdSDavid du Colombier csr32w(ctlr, Txdmac, 0);
9436a081dcdSDavid du Colombier
9446a081dcdSDavid du Colombier csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba));
9456a081dcdSDavid du Colombier csr32w(ctlr, Tdbah, 0);
9466a081dcdSDavid du Colombier csr32w(ctlr, Tdlen, ctlr->ntd*sizeof(Td));
9476a081dcdSDavid du Colombier ctlr->tdh = PREV(0, ctlr->ntd);
9486a081dcdSDavid du Colombier csr32w(ctlr, Tdh, 0);
9496a081dcdSDavid du Colombier ctlr->tdt = 0;
9506a081dcdSDavid du Colombier csr32w(ctlr, Tdt, 0);
9516a081dcdSDavid du Colombier
9526a081dcdSDavid du Colombier for(i = 0; i < ctlr->ntd; i++){
9536a081dcdSDavid du Colombier if((bp = ctlr->tb[i]) != nil){
9546a081dcdSDavid du Colombier ctlr->tb[i] = nil;
9556a081dcdSDavid du Colombier freeb(bp);
9566a081dcdSDavid du Colombier }
9576a081dcdSDavid du Colombier memset(&ctlr->tdba[i], 0, sizeof(Td));
9586a081dcdSDavid du Colombier }
9596a081dcdSDavid du Colombier ctlr->tdfree = ctlr->ntd;
9606a081dcdSDavid du Colombier
9616a081dcdSDavid du Colombier csr32w(ctlr, Tidv, 128);
9626a081dcdSDavid du Colombier r = (4<<WthreshSHIFT)|(4<<HthreshSHIFT)|(8<<PthreshSHIFT);
9636a081dcdSDavid du Colombier
9646a081dcdSDavid du Colombier switch(ctlr->id){
9656a081dcdSDavid du Colombier default:
9666a081dcdSDavid du Colombier break;
967aeb1c8a5SDavid du Colombier case i82540em:
968aeb1c8a5SDavid du Colombier case i82540eplp:
969aeb1c8a5SDavid du Colombier case i82547gi:
970c079eed3SDavid du Colombier case i82545em:
971ab3dc52fSDavid du Colombier case i82545gmc:
972aeb1c8a5SDavid du Colombier case i82546gb:
9735b7d6169SDavid du Colombier case i82546eb:
974aeb1c8a5SDavid du Colombier case i82541gi:
9758466d066SDavid du Colombier case i82541gi2:
976d717568cSDavid du Colombier case i82541pi:
9776a081dcdSDavid du Colombier r = csr32r(ctlr, Txdctl);
9786a081dcdSDavid du Colombier r &= ~WthreshMASK;
9796a081dcdSDavid du Colombier r |= Gran|(4<<WthreshSHIFT);
9806a081dcdSDavid du Colombier
9816a081dcdSDavid du Colombier csr32w(ctlr, Tadv, 64);
9826a081dcdSDavid du Colombier break;
9836a081dcdSDavid du Colombier }
9846a081dcdSDavid du Colombier
9856a081dcdSDavid du Colombier csr32w(ctlr, Txdctl, r);
9866a081dcdSDavid du Colombier
9876a081dcdSDavid du Colombier r = csr32r(ctlr, Tctl);
9886a081dcdSDavid du Colombier r |= Ten;
9896a081dcdSDavid du Colombier csr32w(ctlr, Tctl, r);
9906a081dcdSDavid du Colombier }
9916a081dcdSDavid du Colombier
9926a081dcdSDavid du Colombier static void
igbetransmit(Ether * edev)9936a081dcdSDavid du Colombier igbetransmit(Ether* edev)
9946a081dcdSDavid du Colombier {
9956a081dcdSDavid du Colombier Td *td;
9966a081dcdSDavid du Colombier Block *bp;
9976a081dcdSDavid du Colombier Ctlr *ctlr;
9986a081dcdSDavid du Colombier int tdh, tdt;
9996a081dcdSDavid du Colombier
10006a081dcdSDavid du Colombier ctlr = edev->ctlr;
10016a081dcdSDavid du Colombier
10026a081dcdSDavid du Colombier ilock(&ctlr->tlock);
10036a081dcdSDavid du Colombier
10046a081dcdSDavid du Colombier /*
10056a081dcdSDavid du Colombier * Free any completed packets
10066a081dcdSDavid du Colombier */
10076a081dcdSDavid du Colombier tdh = ctlr->tdh;
10086a081dcdSDavid du Colombier while(NEXT(tdh, ctlr->ntd) != csr32r(ctlr, Tdh)){
10096a081dcdSDavid du Colombier if((bp = ctlr->tb[tdh]) != nil){
10106a081dcdSDavid du Colombier ctlr->tb[tdh] = nil;
10116a081dcdSDavid du Colombier freeb(bp);
10126a081dcdSDavid du Colombier }
10136a081dcdSDavid du Colombier memset(&ctlr->tdba[tdh], 0, sizeof(Td));
10146a081dcdSDavid du Colombier tdh = NEXT(tdh, ctlr->ntd);
10156a081dcdSDavid du Colombier }
10166a081dcdSDavid du Colombier ctlr->tdh = tdh;
10176a081dcdSDavid du Colombier
10186a081dcdSDavid du Colombier /*
10196a081dcdSDavid du Colombier * Try to fill the ring back up.
10206a081dcdSDavid du Colombier */
10216a081dcdSDavid du Colombier tdt = ctlr->tdt;
10226a081dcdSDavid du Colombier while(NEXT(tdt, ctlr->ntd) != tdh){
1023ff8c3af2SDavid du Colombier if((bp = qget(edev->oq)) == nil)
10246a081dcdSDavid du Colombier break;
10256a081dcdSDavid du Colombier td = &ctlr->tdba[tdt];
10266a081dcdSDavid du Colombier td->addr[0] = PCIWADDR(bp->rp);
10276a081dcdSDavid du Colombier td->control = ((BLEN(bp) & LenMASK)<<LenSHIFT);
10286a081dcdSDavid du Colombier td->control |= Dext|Ifcs|Teop|DtypeDD;
10296a081dcdSDavid du Colombier ctlr->tb[tdt] = bp;
1030217e9e83SDavid du Colombier /* note size of queue of tds awaiting transmission */
1031217e9e83SDavid du Colombier notemark(&ctlr->wmtd, (tdt + Ntd - tdh) % Ntd);
10326a081dcdSDavid du Colombier tdt = NEXT(tdt, ctlr->ntd);
10336a081dcdSDavid du Colombier if(NEXT(tdt, ctlr->ntd) == tdh){
10346a081dcdSDavid du Colombier td->control |= Rs;
10356a081dcdSDavid du Colombier ctlr->txdw++;
10366a081dcdSDavid du Colombier ctlr->tdt = tdt;
10376a081dcdSDavid du Colombier csr32w(ctlr, Tdt, tdt);
10386a081dcdSDavid du Colombier igbeim(ctlr, Txdw);
10396a081dcdSDavid du Colombier break;
10406a081dcdSDavid du Colombier }
10416a081dcdSDavid du Colombier ctlr->tdt = tdt;
10426a081dcdSDavid du Colombier csr32w(ctlr, Tdt, tdt);
10436a081dcdSDavid du Colombier }
10446a081dcdSDavid du Colombier
10456a081dcdSDavid du Colombier iunlock(&ctlr->tlock);
10466a081dcdSDavid du Colombier }
10476a081dcdSDavid du Colombier
10486a081dcdSDavid du Colombier static void
igbereplenish(Ctlr * ctlr)10496a081dcdSDavid du Colombier igbereplenish(Ctlr* ctlr)
10506a081dcdSDavid du Colombier {
10516a081dcdSDavid du Colombier Rd *rd;
10526a081dcdSDavid du Colombier int rdt;
10536a081dcdSDavid du Colombier Block *bp;
10546a081dcdSDavid du Colombier
10556a081dcdSDavid du Colombier rdt = ctlr->rdt;
10566a081dcdSDavid du Colombier while(NEXT(rdt, ctlr->nrd) != ctlr->rdh){
10576a081dcdSDavid du Colombier rd = &ctlr->rdba[rdt];
10586a081dcdSDavid du Colombier if(ctlr->rb[rdt] == nil){
10596a081dcdSDavid du Colombier bp = igberballoc();
10606a081dcdSDavid du Colombier if(bp == nil){
1061208510e1SDavid du Colombier iprint("#l%d: igbereplenish: no available buffers\n",
1062208510e1SDavid du Colombier ctlr->edev->ctlrno);
10636a081dcdSDavid du Colombier break;
10646a081dcdSDavid du Colombier }
10656a081dcdSDavid du Colombier ctlr->rb[rdt] = bp;
10666a081dcdSDavid du Colombier rd->addr[0] = PCIWADDR(bp->rp);
10676a081dcdSDavid du Colombier rd->addr[1] = 0;
10686a081dcdSDavid du Colombier }
10696a081dcdSDavid du Colombier coherence();
10706a081dcdSDavid du Colombier rd->status = 0;
10716a081dcdSDavid du Colombier rdt = NEXT(rdt, ctlr->nrd);
10726a081dcdSDavid du Colombier ctlr->rdfree++;
10736a081dcdSDavid du Colombier }
10746a081dcdSDavid du Colombier ctlr->rdt = rdt;
10756a081dcdSDavid du Colombier csr32w(ctlr, Rdt, rdt);
10766a081dcdSDavid du Colombier }
10776a081dcdSDavid du Colombier
10786a081dcdSDavid du Colombier static void
igberxinit(Ctlr * ctlr)10796a081dcdSDavid du Colombier igberxinit(Ctlr* ctlr)
10806a081dcdSDavid du Colombier {
10816a081dcdSDavid du Colombier int i;
10826a081dcdSDavid du Colombier Block *bp;
10836a081dcdSDavid du Colombier
1084b1707c5dSDavid du Colombier /* temporarily keep Mpe on */
1085b1707c5dSDavid du Colombier csr32w(ctlr, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF|Mpe);
10866a081dcdSDavid du Colombier
10876a081dcdSDavid du Colombier csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba));
10886a081dcdSDavid du Colombier csr32w(ctlr, Rdbah, 0);
10896a081dcdSDavid du Colombier csr32w(ctlr, Rdlen, ctlr->nrd*sizeof(Rd));
10906a081dcdSDavid du Colombier ctlr->rdh = 0;
10916a081dcdSDavid du Colombier csr32w(ctlr, Rdh, 0);
10926a081dcdSDavid du Colombier ctlr->rdt = 0;
10936a081dcdSDavid du Colombier csr32w(ctlr, Rdt, 0);
10946a081dcdSDavid du Colombier ctlr->rdtr = 0;
10956a081dcdSDavid du Colombier csr32w(ctlr, Rdtr, Fpd|0);
10966a081dcdSDavid du Colombier
10976a081dcdSDavid du Colombier for(i = 0; i < ctlr->nrd; i++){
10986a081dcdSDavid du Colombier if((bp = ctlr->rb[i]) != nil){
10996a081dcdSDavid du Colombier ctlr->rb[i] = nil;
11006a081dcdSDavid du Colombier freeb(bp);
11016a081dcdSDavid du Colombier }
11026a081dcdSDavid du Colombier }
11036a081dcdSDavid du Colombier igbereplenish(ctlr);
1104217e9e83SDavid du Colombier nrbfull = 0;
11056a081dcdSDavid du Colombier
11066a081dcdSDavid du Colombier switch(ctlr->id){
1107aeb1c8a5SDavid du Colombier case i82540em:
1108aeb1c8a5SDavid du Colombier case i82540eplp:
1109aeb1c8a5SDavid du Colombier case i82541gi:
11108466d066SDavid du Colombier case i82541gi2:
1111d717568cSDavid du Colombier case i82541pi:
1112c079eed3SDavid du Colombier case i82545em:
1113ab3dc52fSDavid du Colombier case i82545gmc:
1114aeb1c8a5SDavid du Colombier case i82546gb:
11155b7d6169SDavid du Colombier case i82546eb:
1116aeb1c8a5SDavid du Colombier case i82547gi:
11176a081dcdSDavid du Colombier csr32w(ctlr, Radv, 64);
11186a081dcdSDavid du Colombier break;
11196a081dcdSDavid du Colombier }
11206a081dcdSDavid du Colombier csr32w(ctlr, Rxdctl, (8<<WthreshSHIFT)|(8<<HthreshSHIFT)|4);
11216a081dcdSDavid du Colombier
11226a081dcdSDavid du Colombier /*
11230591a7c1SDavid du Colombier * Disable checksum offload as it has known bugs.
11246a081dcdSDavid du Colombier */
11250591a7c1SDavid du Colombier csr32w(ctlr, Rxcsum, ETHERHDRSIZE<<PcssSHIFT);
11266a081dcdSDavid du Colombier }
11276a081dcdSDavid du Colombier
11286a081dcdSDavid du Colombier static int
igberim(void * ctlr)11296a081dcdSDavid du Colombier igberim(void* ctlr)
11306a081dcdSDavid du Colombier {
11316a081dcdSDavid du Colombier return ((Ctlr*)ctlr)->rim != 0;
11326a081dcdSDavid du Colombier }
11336a081dcdSDavid du Colombier
11346a081dcdSDavid du Colombier static void
igberproc(void * arg)1135ff8c3af2SDavid du Colombier igberproc(void* arg)
11366a081dcdSDavid du Colombier {
11376a081dcdSDavid du Colombier Rd *rd;
11386a081dcdSDavid du Colombier Block *bp;
11396a081dcdSDavid du Colombier Ctlr *ctlr;
1140217e9e83SDavid du Colombier int r, rdh, passed;
11416a081dcdSDavid du Colombier Ether *edev;
11426a081dcdSDavid du Colombier
1143ff8c3af2SDavid du Colombier edev = arg;
11446a081dcdSDavid du Colombier ctlr = edev->ctlr;
11456a081dcdSDavid du Colombier
11466a081dcdSDavid du Colombier igberxinit(ctlr);
11476a081dcdSDavid du Colombier r = csr32r(ctlr, Rctl);
11486a081dcdSDavid du Colombier r |= Ren;
11496a081dcdSDavid du Colombier csr32w(ctlr, Rctl, r);
11506a081dcdSDavid du Colombier for(;;){
11516a081dcdSDavid du Colombier ctlr->rim = 0;
11526a081dcdSDavid du Colombier igbeim(ctlr, Rxt0|Rxo|Rxdmt0|Rxseq);
11536a081dcdSDavid du Colombier ctlr->rsleep++;
11546a081dcdSDavid du Colombier sleep(&ctlr->rrendez, igberim, ctlr);
11556a081dcdSDavid du Colombier
11566a081dcdSDavid du Colombier rdh = ctlr->rdh;
1157217e9e83SDavid du Colombier passed = 0;
11586a081dcdSDavid du Colombier for(;;){
11596a081dcdSDavid du Colombier rd = &ctlr->rdba[rdh];
11606a081dcdSDavid du Colombier
11616a081dcdSDavid du Colombier if(!(rd->status & Rdd))
11626a081dcdSDavid du Colombier break;
11636a081dcdSDavid du Colombier
11646a081dcdSDavid du Colombier /*
11656a081dcdSDavid du Colombier * Accept eop packets with no errors.
11666a081dcdSDavid du Colombier * With no errors and the Ixsm bit set,
11676a081dcdSDavid du Colombier * the descriptor status Tpcs and Ipcs bits give
11686a081dcdSDavid du Colombier * an indication of whether the checksums were
11696a081dcdSDavid du Colombier * calculated and valid.
11706a081dcdSDavid du Colombier */
11718ccda836SDavid du Colombier /* ignore checksum offload as it has known bugs. */
11728ccda836SDavid du Colombier rd->errors &= ~(Ipe | Tcpe);
11736a081dcdSDavid du Colombier if((rd->status & Reop) && rd->errors == 0){
11746a081dcdSDavid du Colombier bp = ctlr->rb[rdh];
11756a081dcdSDavid du Colombier ctlr->rb[rdh] = nil;
1176ff8c3af2SDavid du Colombier bp->wp += rd->length;
11776a081dcdSDavid du Colombier bp->next = nil;
11788ccda836SDavid du Colombier /* ignore checksum offload as it has known bugs. */
11798ccda836SDavid du Colombier if(0 && !(rd->status & Ixsm)){
11806a081dcdSDavid du Colombier ctlr->ixsm++;
11816a081dcdSDavid du Colombier if(rd->status & Ipcs){
11826a081dcdSDavid du Colombier /*
11836a081dcdSDavid du Colombier * IP checksum calculated
11846a081dcdSDavid du Colombier * (and valid as errors == 0).
11856a081dcdSDavid du Colombier */
11866a081dcdSDavid du Colombier ctlr->ipcs++;
11876a081dcdSDavid du Colombier bp->flag |= Bipck;
11886a081dcdSDavid du Colombier }
11896a081dcdSDavid du Colombier if(rd->status & Tcpcs){
11906a081dcdSDavid du Colombier /*
11916a081dcdSDavid du Colombier * TCP/UDP checksum calculated
11926a081dcdSDavid du Colombier * (and valid as errors == 0).
11936a081dcdSDavid du Colombier */
11946a081dcdSDavid du Colombier ctlr->tcpcs++;
11956a081dcdSDavid du Colombier bp->flag |= Btcpck|Budpck;
11966a081dcdSDavid du Colombier }
11976a081dcdSDavid du Colombier bp->checksum = rd->checksum;
11986a081dcdSDavid du Colombier bp->flag |= Bpktck;
11996a081dcdSDavid du Colombier }
1200217e9e83SDavid du Colombier ilock(&igberblock);
1201217e9e83SDavid du Colombier nrbfull++;
1202217e9e83SDavid du Colombier iunlock(&igberblock);
1203217e9e83SDavid du Colombier notemark(&ctlr->wmrb, nrbfull);
1204ff8c3af2SDavid du Colombier etheriq(edev, bp, 1);
1205217e9e83SDavid du Colombier passed++;
12066a081dcdSDavid du Colombier }
12076a081dcdSDavid du Colombier else if(ctlr->rb[rdh] != nil){
12086a081dcdSDavid du Colombier freeb(ctlr->rb[rdh]);
12096a081dcdSDavid du Colombier ctlr->rb[rdh] = nil;
12106a081dcdSDavid du Colombier }
12116a081dcdSDavid du Colombier
12126a081dcdSDavid du Colombier memset(rd, 0, sizeof(Rd));
12136a081dcdSDavid du Colombier coherence();
12146a081dcdSDavid du Colombier ctlr->rdfree--;
12156a081dcdSDavid du Colombier rdh = NEXT(rdh, ctlr->nrd);
12166a081dcdSDavid du Colombier }
12176a081dcdSDavid du Colombier ctlr->rdh = rdh;
12186a081dcdSDavid du Colombier
12196a081dcdSDavid du Colombier if(ctlr->rdfree < ctlr->nrd/2 || (ctlr->rim & Rxdmt0))
12206a081dcdSDavid du Colombier igbereplenish(ctlr);
1221217e9e83SDavid du Colombier /* note how many rds had full buffers */
1222217e9e83SDavid du Colombier notemark(&ctlr->wmrd, passed);
12236a081dcdSDavid du Colombier }
12246a081dcdSDavid du Colombier }
12256a081dcdSDavid du Colombier
12266a081dcdSDavid du Colombier static void
igbeattach(Ether * edev)12276a081dcdSDavid du Colombier igbeattach(Ether* edev)
12286a081dcdSDavid du Colombier {
12296a081dcdSDavid du Colombier Block *bp;
12306a081dcdSDavid du Colombier Ctlr *ctlr;
12316a081dcdSDavid du Colombier char name[KNAMELEN];
12326a081dcdSDavid du Colombier
12336a081dcdSDavid du Colombier ctlr = edev->ctlr;
1234208510e1SDavid du Colombier ctlr->edev = edev; /* point back to Ether* */
12356a081dcdSDavid du Colombier qlock(&ctlr->alock);
1236ea58ad6fSDavid du Colombier if(ctlr->alloc != nil){ /* already allocated? */
12376a081dcdSDavid du Colombier qunlock(&ctlr->alock);
12386a081dcdSDavid du Colombier return;
12396a081dcdSDavid du Colombier }
12406a081dcdSDavid du Colombier
1241aa72973aSDavid du Colombier ctlr->tb = nil;
1242aa72973aSDavid du Colombier ctlr->rb = nil;
1243aa72973aSDavid du Colombier ctlr->alloc = nil;
1244aa72973aSDavid du Colombier ctlr->nrb = 0;
12456a081dcdSDavid du Colombier if(waserror()){
12466a081dcdSDavid du Colombier while(ctlr->nrb > 0){
12476a081dcdSDavid du Colombier bp = igberballoc();
12486a081dcdSDavid du Colombier bp->free = nil;
12496a081dcdSDavid du Colombier freeb(bp);
12506a081dcdSDavid du Colombier ctlr->nrb--;
12516a081dcdSDavid du Colombier }
12526a081dcdSDavid du Colombier free(ctlr->tb);
12536a081dcdSDavid du Colombier ctlr->tb = nil;
12546a081dcdSDavid du Colombier free(ctlr->rb);
12556a081dcdSDavid du Colombier ctlr->rb = nil;
12566a081dcdSDavid du Colombier free(ctlr->alloc);
12576a081dcdSDavid du Colombier ctlr->alloc = nil;
12586a081dcdSDavid du Colombier qunlock(&ctlr->alock);
12596a081dcdSDavid du Colombier nexterror();
12606a081dcdSDavid du Colombier }
12616a081dcdSDavid du Colombier
1262aa72973aSDavid du Colombier ctlr->nrd = ROUND(Nrd, 8);
1263aa72973aSDavid du Colombier ctlr->ntd = ROUND(Ntd, 8);
1264aa72973aSDavid du Colombier ctlr->alloc = malloc(ctlr->nrd*sizeof(Rd)+ctlr->ntd*sizeof(Td) + 127);
1265aa72973aSDavid du Colombier if(ctlr->alloc == nil) {
1266aa72973aSDavid du Colombier print("igbe: can't allocate ctlr->alloc\n");
1267aa72973aSDavid du Colombier error(Enomem);
1268aa72973aSDavid du Colombier }
1269aa72973aSDavid du Colombier ctlr->rdba = (Rd*)ROUNDUP((uintptr)ctlr->alloc, 128);
1270aa72973aSDavid du Colombier ctlr->tdba = (Td*)(ctlr->rdba+ctlr->nrd);
1271aa72973aSDavid du Colombier
1272aa72973aSDavid du Colombier ctlr->rb = malloc(ctlr->nrd*sizeof(Block*));
1273aa72973aSDavid du Colombier ctlr->tb = malloc(ctlr->ntd*sizeof(Block*));
1274aa72973aSDavid du Colombier if (ctlr->rb == nil || ctlr->tb == nil) {
1275aa72973aSDavid du Colombier print("igbe: can't allocate ctlr->rb or ctlr->tb\n");
1276aa72973aSDavid du Colombier error(Enomem);
1277aa72973aSDavid du Colombier }
1278aa72973aSDavid du Colombier
12796a081dcdSDavid du Colombier for(ctlr->nrb = 0; ctlr->nrb < Nrb; ctlr->nrb++){
12806a081dcdSDavid du Colombier if((bp = allocb(Rbsz)) == nil)
12816a081dcdSDavid du Colombier break;
12826a081dcdSDavid du Colombier bp->free = igberbfree;
12836a081dcdSDavid du Colombier freeb(bp);
12846a081dcdSDavid du Colombier }
1285217e9e83SDavid du Colombier initmark(&ctlr->wmrb, Nrb, "rcv bufs unprocessed");
1286217e9e83SDavid du Colombier initmark(&ctlr->wmrd, Nrd-1, "rcv descrs processed at once");
1287217e9e83SDavid du Colombier initmark(&ctlr->wmtd, Ntd-1, "xmit descr queue len");
12886a081dcdSDavid du Colombier
12896a081dcdSDavid du Colombier snprint(name, KNAMELEN, "#l%dlproc", edev->ctlrno);
12906a081dcdSDavid du Colombier kproc(name, igbelproc, edev);
12916a081dcdSDavid du Colombier
12926a081dcdSDavid du Colombier snprint(name, KNAMELEN, "#l%drproc", edev->ctlrno);
12936a081dcdSDavid du Colombier kproc(name, igberproc, edev);
12946a081dcdSDavid du Colombier
12956a081dcdSDavid du Colombier igbetxinit(ctlr);
12966a081dcdSDavid du Colombier
12976a081dcdSDavid du Colombier qunlock(&ctlr->alock);
12986a081dcdSDavid du Colombier poperror();
12996a081dcdSDavid du Colombier }
13006a081dcdSDavid du Colombier
13016a081dcdSDavid du Colombier static void
igbeinterrupt(Ureg *,void * arg)13026a081dcdSDavid du Colombier igbeinterrupt(Ureg*, void* arg)
13036a081dcdSDavid du Colombier {
13046a081dcdSDavid du Colombier Ctlr *ctlr;
13056a081dcdSDavid du Colombier Ether *edev;
13066a081dcdSDavid du Colombier int icr, im, txdw;
13076a081dcdSDavid du Colombier
13086a081dcdSDavid du Colombier edev = arg;
13096a081dcdSDavid du Colombier ctlr = edev->ctlr;
13106a081dcdSDavid du Colombier
13116a081dcdSDavid du Colombier ilock(&ctlr->imlock);
13126a081dcdSDavid du Colombier csr32w(ctlr, Imc, ~0);
13136a081dcdSDavid du Colombier im = ctlr->im;
13146a081dcdSDavid du Colombier txdw = 0;
13156a081dcdSDavid du Colombier
13166a081dcdSDavid du Colombier while((icr = csr32r(ctlr, Icr) & ctlr->im) != 0){
13176a081dcdSDavid du Colombier if(icr & Lsc){
13186a081dcdSDavid du Colombier im &= ~Lsc;
13196a081dcdSDavid du Colombier ctlr->lim = icr & Lsc;
13206a081dcdSDavid du Colombier wakeup(&ctlr->lrendez);
13216a081dcdSDavid du Colombier ctlr->lintr++;
13226a081dcdSDavid du Colombier }
13236a081dcdSDavid du Colombier if(icr & (Rxt0|Rxo|Rxdmt0|Rxseq)){
13246a081dcdSDavid du Colombier im &= ~(Rxt0|Rxo|Rxdmt0|Rxseq);
13256a081dcdSDavid du Colombier ctlr->rim = icr & (Rxt0|Rxo|Rxdmt0|Rxseq);
13266a081dcdSDavid du Colombier wakeup(&ctlr->rrendez);
13276a081dcdSDavid du Colombier ctlr->rintr++;
13286a081dcdSDavid du Colombier }
13296a081dcdSDavid du Colombier if(icr & Txdw){
13306a081dcdSDavid du Colombier im &= ~Txdw;
13316a081dcdSDavid du Colombier txdw++;
13326a081dcdSDavid du Colombier ctlr->tintr++;
13336a081dcdSDavid du Colombier }
13346a081dcdSDavid du Colombier }
13356a081dcdSDavid du Colombier
13366a081dcdSDavid du Colombier ctlr->im = im;
13376a081dcdSDavid du Colombier csr32w(ctlr, Ims, im);
13386a081dcdSDavid du Colombier iunlock(&ctlr->imlock);
13396a081dcdSDavid du Colombier
13406a081dcdSDavid du Colombier if(txdw)
13416a081dcdSDavid du Colombier igbetransmit(edev);
13426a081dcdSDavid du Colombier }
13436a081dcdSDavid du Colombier
13446a081dcdSDavid du Colombier static int
i82543mdior(Ctlr * ctlr,int n)13456a081dcdSDavid du Colombier i82543mdior(Ctlr* ctlr, int n)
13466a081dcdSDavid du Colombier {
13476a081dcdSDavid du Colombier int ctrl, data, i, r;
13486a081dcdSDavid du Colombier
13496a081dcdSDavid du Colombier /*
13506a081dcdSDavid du Colombier * Read n bits from the Management Data I/O Interface.
13516a081dcdSDavid du Colombier */
13526a081dcdSDavid du Colombier ctrl = csr32r(ctlr, Ctrl);
13536a081dcdSDavid du Colombier r = (ctrl & ~Mddo)|Mdco;
13546a081dcdSDavid du Colombier data = 0;
13556a081dcdSDavid du Colombier for(i = n-1; i >= 0; i--){
13566a081dcdSDavid du Colombier if(csr32r(ctlr, Ctrl) & Mdd)
13576a081dcdSDavid du Colombier data |= (1<<i);
13586a081dcdSDavid du Colombier csr32w(ctlr, Ctrl, Mdc|r);
13596a081dcdSDavid du Colombier csr32w(ctlr, Ctrl, r);
13606a081dcdSDavid du Colombier }
13616a081dcdSDavid du Colombier csr32w(ctlr, Ctrl, ctrl);
13626a081dcdSDavid du Colombier
13636a081dcdSDavid du Colombier return data;
13646a081dcdSDavid du Colombier }
13656a081dcdSDavid du Colombier
13666a081dcdSDavid du Colombier static int
i82543mdiow(Ctlr * ctlr,int bits,int n)13676a081dcdSDavid du Colombier i82543mdiow(Ctlr* ctlr, int bits, int n)
13686a081dcdSDavid du Colombier {
13696a081dcdSDavid du Colombier int ctrl, i, r;
13706a081dcdSDavid du Colombier
13716a081dcdSDavid du Colombier /*
13726a081dcdSDavid du Colombier * Write n bits to the Management Data I/O Interface.
13736a081dcdSDavid du Colombier */
13746a081dcdSDavid du Colombier ctrl = csr32r(ctlr, Ctrl);
13756a081dcdSDavid du Colombier r = Mdco|Mddo|ctrl;
13766a081dcdSDavid du Colombier for(i = n-1; i >= 0; i--){
13776a081dcdSDavid du Colombier if(bits & (1<<i))
13786a081dcdSDavid du Colombier r |= Mdd;
13796a081dcdSDavid du Colombier else
13806a081dcdSDavid du Colombier r &= ~Mdd;
13816a081dcdSDavid du Colombier csr32w(ctlr, Ctrl, Mdc|r);
13826a081dcdSDavid du Colombier csr32w(ctlr, Ctrl, r);
13836a081dcdSDavid du Colombier }
13846a081dcdSDavid du Colombier csr32w(ctlr, Ctrl, ctrl);
13856a081dcdSDavid du Colombier
13866a081dcdSDavid du Colombier return 0;
13876a081dcdSDavid du Colombier }
13886a081dcdSDavid du Colombier
13896a081dcdSDavid du Colombier static int
i82543miimir(Mii * mii,int pa,int ra)13906a081dcdSDavid du Colombier i82543miimir(Mii* mii, int pa, int ra)
13916a081dcdSDavid du Colombier {
13926a081dcdSDavid du Colombier int data;
13936a081dcdSDavid du Colombier Ctlr *ctlr;
13946a081dcdSDavid du Colombier
13956a081dcdSDavid du Colombier ctlr = mii->ctlr;
13966a081dcdSDavid du Colombier
13976a081dcdSDavid du Colombier /*
13986a081dcdSDavid du Colombier * MII Management Interface Read.
13996a081dcdSDavid du Colombier *
14006a081dcdSDavid du Colombier * Preamble;
14016a081dcdSDavid du Colombier * ST+OP+PHYAD+REGAD;
14026a081dcdSDavid du Colombier * TA + 16 data bits.
14036a081dcdSDavid du Colombier */
14046a081dcdSDavid du Colombier i82543mdiow(ctlr, 0xFFFFFFFF, 32);
14056a081dcdSDavid du Colombier i82543mdiow(ctlr, 0x1800|(pa<<5)|ra, 14);
14066a081dcdSDavid du Colombier data = i82543mdior(ctlr, 18);
14076a081dcdSDavid du Colombier
14086a081dcdSDavid du Colombier if(data & 0x10000)
14096a081dcdSDavid du Colombier return -1;
14106a081dcdSDavid du Colombier
14116a081dcdSDavid du Colombier return data & 0xFFFF;
14126a081dcdSDavid du Colombier }
14136a081dcdSDavid du Colombier
14146a081dcdSDavid du Colombier static int
i82543miimiw(Mii * mii,int pa,int ra,int data)14156a081dcdSDavid du Colombier i82543miimiw(Mii* mii, int pa, int ra, int data)
14166a081dcdSDavid du Colombier {
14176a081dcdSDavid du Colombier Ctlr *ctlr;
14186a081dcdSDavid du Colombier
14196a081dcdSDavid du Colombier ctlr = mii->ctlr;
14206a081dcdSDavid du Colombier
14216a081dcdSDavid du Colombier /*
14226a081dcdSDavid du Colombier * MII Management Interface Write.
14236a081dcdSDavid du Colombier *
14246a081dcdSDavid du Colombier * Preamble;
14256a081dcdSDavid du Colombier * ST+OP+PHYAD+REGAD+TA + 16 data bits;
14266a081dcdSDavid du Colombier * Z.
14276a081dcdSDavid du Colombier */
14286a081dcdSDavid du Colombier i82543mdiow(ctlr, 0xFFFFFFFF, 32);
14296a081dcdSDavid du Colombier data &= 0xFFFF;
14306a081dcdSDavid du Colombier data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16);
14316a081dcdSDavid du Colombier i82543mdiow(ctlr, data, 32);
14326a081dcdSDavid du Colombier
14336a081dcdSDavid du Colombier return 0;
14346a081dcdSDavid du Colombier }
14356a081dcdSDavid du Colombier
14366a081dcdSDavid du Colombier static int
igbemiimir(Mii * mii,int pa,int ra)14376a081dcdSDavid du Colombier igbemiimir(Mii* mii, int pa, int ra)
14386a081dcdSDavid du Colombier {
14396a081dcdSDavid du Colombier Ctlr *ctlr;
14406a081dcdSDavid du Colombier int mdic, timo;
14416a081dcdSDavid du Colombier
14426a081dcdSDavid du Colombier ctlr = mii->ctlr;
14436a081dcdSDavid du Colombier
14446a081dcdSDavid du Colombier csr32w(ctlr, Mdic, MDIrop|(pa<<MDIpSHIFT)|(ra<<MDIrSHIFT));
14456a081dcdSDavid du Colombier mdic = 0;
14466a081dcdSDavid du Colombier for(timo = 64; timo; timo--){
14476a081dcdSDavid du Colombier mdic = csr32r(ctlr, Mdic);
14486a081dcdSDavid du Colombier if(mdic & (MDIe|MDIready))
14496a081dcdSDavid du Colombier break;
14506a081dcdSDavid du Colombier microdelay(1);
14516a081dcdSDavid du Colombier }
14526a081dcdSDavid du Colombier
14536a081dcdSDavid du Colombier if((mdic & (MDIe|MDIready)) == MDIready)
14546a081dcdSDavid du Colombier return mdic & 0xFFFF;
14556a081dcdSDavid du Colombier return -1;
14566a081dcdSDavid du Colombier }
14576a081dcdSDavid du Colombier
14586a081dcdSDavid du Colombier static int
igbemiimiw(Mii * mii,int pa,int ra,int data)14596a081dcdSDavid du Colombier igbemiimiw(Mii* mii, int pa, int ra, int data)
14606a081dcdSDavid du Colombier {
14616a081dcdSDavid du Colombier Ctlr *ctlr;
14626a081dcdSDavid du Colombier int mdic, timo;
14636a081dcdSDavid du Colombier
14646a081dcdSDavid du Colombier ctlr = mii->ctlr;
14656a081dcdSDavid du Colombier
14666a081dcdSDavid du Colombier data &= MDIdMASK;
14676a081dcdSDavid du Colombier csr32w(ctlr, Mdic, MDIwop|(pa<<MDIpSHIFT)|(ra<<MDIrSHIFT)|data);
14686a081dcdSDavid du Colombier mdic = 0;
14696a081dcdSDavid du Colombier for(timo = 64; timo; timo--){
14706a081dcdSDavid du Colombier mdic = csr32r(ctlr, Mdic);
14716a081dcdSDavid du Colombier if(mdic & (MDIe|MDIready))
14726a081dcdSDavid du Colombier break;
14736a081dcdSDavid du Colombier microdelay(1);
14746a081dcdSDavid du Colombier }
14756a081dcdSDavid du Colombier if((mdic & (MDIe|MDIready)) == MDIready)
14766a081dcdSDavid du Colombier return 0;
14776a081dcdSDavid du Colombier return -1;
14786a081dcdSDavid du Colombier }
14796a081dcdSDavid du Colombier
14806a081dcdSDavid du Colombier static int
igbemii(Ctlr * ctlr)14816a081dcdSDavid du Colombier igbemii(Ctlr* ctlr)
14826a081dcdSDavid du Colombier {
14836a081dcdSDavid du Colombier MiiPhy *phy;
14846a081dcdSDavid du Colombier int ctrl, p, r;
14856a081dcdSDavid du Colombier
14866a081dcdSDavid du Colombier r = csr32r(ctlr, Status);
14876a081dcdSDavid du Colombier if(r & Tbimode)
14886a081dcdSDavid du Colombier return -1;
14896a081dcdSDavid du Colombier if((ctlr->mii = malloc(sizeof(Mii))) == nil)
14906a081dcdSDavid du Colombier return -1;
14916a081dcdSDavid du Colombier ctlr->mii->ctlr = ctlr;
14926a081dcdSDavid du Colombier
14936a081dcdSDavid du Colombier ctrl = csr32r(ctlr, Ctrl);
14946a081dcdSDavid du Colombier ctrl |= Slu;
14956a081dcdSDavid du Colombier
14966a081dcdSDavid du Colombier switch(ctlr->id){
1497aeb1c8a5SDavid du Colombier case i82543gc:
14986a081dcdSDavid du Colombier ctrl |= Frcdplx|Frcspd;
14996a081dcdSDavid du Colombier csr32w(ctlr, Ctrl, ctrl);
15006a081dcdSDavid du Colombier
15016a081dcdSDavid du Colombier /*
15026a081dcdSDavid du Colombier * The reset pin direction (Mdro) should already
15036a081dcdSDavid du Colombier * be set from the EEPROM load.
15046a081dcdSDavid du Colombier * If it's not set this configuration is unexpected
15056a081dcdSDavid du Colombier * so bail.
15066a081dcdSDavid du Colombier */
15076a081dcdSDavid du Colombier r = csr32r(ctlr, Ctrlext);
15080591a7c1SDavid du Colombier if(!(r & Mdro)) {
15090591a7c1SDavid du Colombier print("igbe: 82543gc Mdro not set\n");
15106a081dcdSDavid du Colombier return -1;
15110591a7c1SDavid du Colombier }
15126a081dcdSDavid du Colombier csr32w(ctlr, Ctrlext, r);
15136a081dcdSDavid du Colombier delay(20);
15146a081dcdSDavid du Colombier r = csr32r(ctlr, Ctrlext);
15156a081dcdSDavid du Colombier r &= ~Mdr;
15166a081dcdSDavid du Colombier csr32w(ctlr, Ctrlext, r);
15176a081dcdSDavid du Colombier delay(20);
15186a081dcdSDavid du Colombier r = csr32r(ctlr, Ctrlext);
15196a081dcdSDavid du Colombier r |= Mdr;
15206a081dcdSDavid du Colombier csr32w(ctlr, Ctrlext, r);
15216a081dcdSDavid du Colombier delay(20);
15226a081dcdSDavid du Colombier
15236a081dcdSDavid du Colombier ctlr->mii->mir = i82543miimir;
15246a081dcdSDavid du Colombier ctlr->mii->miw = i82543miimiw;
15256a081dcdSDavid du Colombier break;
1526aeb1c8a5SDavid du Colombier case i82544ei:
152707c32395SDavid du Colombier case i82544eif:
15289a8968c3SDavid du Colombier case i82544gc:
1529aeb1c8a5SDavid du Colombier case i82540em:
1530aeb1c8a5SDavid du Colombier case i82540eplp:
15315efba406SDavid du Colombier case i82547ei:
1532aeb1c8a5SDavid du Colombier case i82547gi:
15335efba406SDavid du Colombier case i82541ei:
1534aeb1c8a5SDavid du Colombier case i82541gi:
15358466d066SDavid du Colombier case i82541gi2:
1536d717568cSDavid du Colombier case i82541pi:
1537c079eed3SDavid du Colombier case i82545em:
1538ab3dc52fSDavid du Colombier case i82545gmc:
1539aeb1c8a5SDavid du Colombier case i82546gb:
15405b7d6169SDavid du Colombier case i82546eb:
15416a081dcdSDavid du Colombier ctrl &= ~(Frcdplx|Frcspd);
15426a081dcdSDavid du Colombier csr32w(ctlr, Ctrl, ctrl);
15436a081dcdSDavid du Colombier ctlr->mii->mir = igbemiimir;
15446a081dcdSDavid du Colombier ctlr->mii->miw = igbemiimiw;
15456a081dcdSDavid du Colombier break;
15466a081dcdSDavid du Colombier default:
15476a081dcdSDavid du Colombier free(ctlr->mii);
15486a081dcdSDavid du Colombier ctlr->mii = nil;
15496a081dcdSDavid du Colombier return -1;
15506a081dcdSDavid du Colombier }
15516a081dcdSDavid du Colombier
15526a081dcdSDavid du Colombier if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){
15536a081dcdSDavid du Colombier free(ctlr->mii);
15546a081dcdSDavid du Colombier ctlr->mii = nil;
15556a081dcdSDavid du Colombier return -1;
15566a081dcdSDavid du Colombier }
15574de34a7eSDavid du Colombier USED(phy);
15584de34a7eSDavid du Colombier // print("oui %X phyno %d\n", phy->oui, phy->phyno);
15596a081dcdSDavid du Colombier
15606a081dcdSDavid du Colombier /*
15616a081dcdSDavid du Colombier * 8254X-specific PHY registers not in 802.3:
15626a081dcdSDavid du Colombier * 0x10 PHY specific control
15636a081dcdSDavid du Colombier * 0x14 extended PHY specific control
15646a081dcdSDavid du Colombier * Set appropriate values then reset the PHY to have
15656a081dcdSDavid du Colombier * changes noted.
15666a081dcdSDavid du Colombier */
1567d717568cSDavid du Colombier switch(ctlr->id){
1568d717568cSDavid du Colombier case i82547gi:
1569d717568cSDavid du Colombier case i82541gi:
15708466d066SDavid du Colombier case i82541gi2:
1571d717568cSDavid du Colombier case i82541pi:
1572c079eed3SDavid du Colombier case i82545em:
1573ab3dc52fSDavid du Colombier case i82545gmc:
1574d717568cSDavid du Colombier case i82546gb:
1575d717568cSDavid du Colombier case i82546eb:
1576d717568cSDavid du Colombier break;
1577d717568cSDavid du Colombier default:
1578aeb1c8a5SDavid du Colombier r = miimir(ctlr->mii, 16);
15796a081dcdSDavid du Colombier r |= 0x0800; /* assert CRS on Tx */
15806a081dcdSDavid du Colombier r |= 0x0060; /* auto-crossover all speeds */
15816a081dcdSDavid du Colombier r |= 0x0002; /* polarity reversal enabled */
1582aeb1c8a5SDavid du Colombier miimiw(ctlr->mii, 16, r);
15836a081dcdSDavid du Colombier
1584aeb1c8a5SDavid du Colombier r = miimir(ctlr->mii, 20);
15856a081dcdSDavid du Colombier r |= 0x0070; /* +25MHz clock */
15866a081dcdSDavid du Colombier r &= ~0x0F00;
15876a081dcdSDavid du Colombier r |= 0x0100; /* 1x downshift */
1588aeb1c8a5SDavid du Colombier miimiw(ctlr->mii, 20, r);
15896a081dcdSDavid du Colombier
15906a081dcdSDavid du Colombier miireset(ctlr->mii);
15916a081dcdSDavid du Colombier p = 0;
15926a081dcdSDavid du Colombier if(ctlr->txcw & TxcwPs)
15936a081dcdSDavid du Colombier p |= AnaP;
15946a081dcdSDavid du Colombier if(ctlr->txcw & TxcwAs)
15956a081dcdSDavid du Colombier p |= AnaAP;
15966a081dcdSDavid du Colombier miiane(ctlr->mii, ~0, p, ~0);
1597d717568cSDavid du Colombier break;
1598aeb1c8a5SDavid du Colombier }
15996a081dcdSDavid du Colombier return 0;
16006a081dcdSDavid du Colombier }
16016a081dcdSDavid du Colombier
16026a081dcdSDavid du Colombier static int
at93c46io(Ctlr * ctlr,char * op,int data)16036a081dcdSDavid du Colombier at93c46io(Ctlr* ctlr, char* op, int data)
16046a081dcdSDavid du Colombier {
16056a081dcdSDavid du Colombier char *lp, *p;
16066a081dcdSDavid du Colombier int i, loop, eecd, r;
16076a081dcdSDavid du Colombier
16086a081dcdSDavid du Colombier eecd = csr32r(ctlr, Eecd);
16096a081dcdSDavid du Colombier
16106a081dcdSDavid du Colombier r = 0;
16116a081dcdSDavid du Colombier loop = -1;
16126a081dcdSDavid du Colombier lp = nil;
16136a081dcdSDavid du Colombier for(p = op; *p != '\0'; p++){
16146a081dcdSDavid du Colombier switch(*p){
16156a081dcdSDavid du Colombier default:
16166a081dcdSDavid du Colombier return -1;
16176a081dcdSDavid du Colombier case ' ':
16186a081dcdSDavid du Colombier continue;
16196a081dcdSDavid du Colombier case ':': /* start of loop */
16206a081dcdSDavid du Colombier loop = strtol(p+1, &lp, 0)-1;
16216a081dcdSDavid du Colombier lp--;
16226a081dcdSDavid du Colombier if(p == lp)
16236a081dcdSDavid du Colombier loop = 7;
16246a081dcdSDavid du Colombier p = lp;
16256a081dcdSDavid du Colombier continue;
16266a081dcdSDavid du Colombier case ';': /* end of loop */
16276a081dcdSDavid du Colombier if(lp == nil)
16286a081dcdSDavid du Colombier return -1;
16296a081dcdSDavid du Colombier loop--;
16306a081dcdSDavid du Colombier if(loop >= 0)
16316a081dcdSDavid du Colombier p = lp;
16326a081dcdSDavid du Colombier else
16336a081dcdSDavid du Colombier lp = nil;
16346a081dcdSDavid du Colombier continue;
16356a081dcdSDavid du Colombier case 'C': /* assert clock */
16366a081dcdSDavid du Colombier eecd |= Sk;
16376a081dcdSDavid du Colombier break;
16386a081dcdSDavid du Colombier case 'c': /* deassert clock */
16396a081dcdSDavid du Colombier eecd &= ~Sk;
16406a081dcdSDavid du Colombier break;
16416a081dcdSDavid du Colombier case 'D': /* next bit in 'data' byte */
16426a081dcdSDavid du Colombier if(loop < 0)
16436a081dcdSDavid du Colombier return -1;
16446a081dcdSDavid du Colombier if(data & (1<<loop))
16456a081dcdSDavid du Colombier eecd |= Di;
16466a081dcdSDavid du Colombier else
16476a081dcdSDavid du Colombier eecd &= ~Di;
16486a081dcdSDavid du Colombier break;
16496a081dcdSDavid du Colombier case 'O': /* collect data output */
16506a081dcdSDavid du Colombier i = (csr32r(ctlr, Eecd) & Do) != 0;
16516a081dcdSDavid du Colombier if(loop >= 0)
16526a081dcdSDavid du Colombier r |= (i<<loop);
16536a081dcdSDavid du Colombier else
16546a081dcdSDavid du Colombier r = i;
16556a081dcdSDavid du Colombier continue;
16566a081dcdSDavid du Colombier case 'I': /* assert data input */
16576a081dcdSDavid du Colombier eecd |= Di;
16586a081dcdSDavid du Colombier break;
16596a081dcdSDavid du Colombier case 'i': /* deassert data input */
16606a081dcdSDavid du Colombier eecd &= ~Di;
16616a081dcdSDavid du Colombier break;
16626a081dcdSDavid du Colombier case 'S': /* enable chip select */
16636a081dcdSDavid du Colombier eecd |= Cs;
16646a081dcdSDavid du Colombier break;
16656a081dcdSDavid du Colombier case 's': /* disable chip select */
16666a081dcdSDavid du Colombier eecd &= ~Cs;
16676a081dcdSDavid du Colombier break;
16686a081dcdSDavid du Colombier }
16696a081dcdSDavid du Colombier csr32w(ctlr, Eecd, eecd);
16708466d066SDavid du Colombier microdelay(50);
16716a081dcdSDavid du Colombier }
16726a081dcdSDavid du Colombier if(loop >= 0)
16736a081dcdSDavid du Colombier return -1;
16746a081dcdSDavid du Colombier return r;
16756a081dcdSDavid du Colombier }
16766a081dcdSDavid du Colombier
16776a081dcdSDavid du Colombier static int
at93c46r(Ctlr * ctlr)16786a081dcdSDavid du Colombier at93c46r(Ctlr* ctlr)
16796a081dcdSDavid du Colombier {
16806a081dcdSDavid du Colombier ushort sum;
16816a081dcdSDavid du Colombier char rop[20];
16826a081dcdSDavid du Colombier int addr, areq, bits, data, eecd, i;
16836a081dcdSDavid du Colombier
16846a081dcdSDavid du Colombier eecd = csr32r(ctlr, Eecd);
16856a081dcdSDavid du Colombier if(eecd & Spi){
16866a081dcdSDavid du Colombier print("igbe: SPI EEPROM access not implemented\n");
16876a081dcdSDavid du Colombier return 0;
16886a081dcdSDavid du Colombier }
16898466d066SDavid du Colombier if(eecd & (Eeszaddr|Eesz256))
16906a081dcdSDavid du Colombier bits = 8;
16916a081dcdSDavid du Colombier else
16926a081dcdSDavid du Colombier bits = 6;
16936a081dcdSDavid du Colombier
16946a081dcdSDavid du Colombier sum = 0;
16956a081dcdSDavid du Colombier
16966a081dcdSDavid du Colombier switch(ctlr->id){
16976a081dcdSDavid du Colombier default:
16986a081dcdSDavid du Colombier areq = 0;
16996a081dcdSDavid du Colombier break;
1700aeb1c8a5SDavid du Colombier case i82540em:
1701aeb1c8a5SDavid du Colombier case i82540eplp:
17025efba406SDavid du Colombier case i82541ei:
17035efba406SDavid du Colombier case i82541gi:
170426ad7229SDavid du Colombier case i82541gi2:
17055efba406SDavid du Colombier case i82541pi:
1706c079eed3SDavid du Colombier case i82545em:
1707ab3dc52fSDavid du Colombier case i82545gmc:
1708aeb1c8a5SDavid du Colombier case i82546gb:
17095b7d6169SDavid du Colombier case i82546eb:
17107e254d1cSDavid du Colombier case i82547ei:
17115efba406SDavid du Colombier case i82547gi:
17126a081dcdSDavid du Colombier areq = 1;
17136a081dcdSDavid du Colombier csr32w(ctlr, Eecd, eecd|Areq);
17146a081dcdSDavid du Colombier for(i = 0; i < 1000; i++){
17156a081dcdSDavid du Colombier if((eecd = csr32r(ctlr, Eecd)) & Agnt)
17166a081dcdSDavid du Colombier break;
17176a081dcdSDavid du Colombier microdelay(5);
17186a081dcdSDavid du Colombier }
17196a081dcdSDavid du Colombier if(!(eecd & Agnt)){
17206a081dcdSDavid du Colombier print("igbe: not granted EEPROM access\n");
17216a081dcdSDavid du Colombier goto release;
17226a081dcdSDavid du Colombier }
17236a081dcdSDavid du Colombier break;
17246a081dcdSDavid du Colombier }
17258466d066SDavid du Colombier snprint(rop, sizeof(rop), "S :%dDCc;", bits+3);
17266a081dcdSDavid du Colombier
17276a081dcdSDavid du Colombier for(addr = 0; addr < 0x40; addr++){
17286a081dcdSDavid du Colombier /*
17296a081dcdSDavid du Colombier * Read a word at address 'addr' from the Atmel AT93C46
17306a081dcdSDavid du Colombier * 3-Wire Serial EEPROM or compatible. The EEPROM access is
17316a081dcdSDavid du Colombier * controlled by 4 bits in Eecd. See the AT93C46 datasheet
17326a081dcdSDavid du Colombier * for protocol details.
17336a081dcdSDavid du Colombier */
17346a081dcdSDavid du Colombier if(at93c46io(ctlr, rop, (0x06<<bits)|addr) != 0){
17356a081dcdSDavid du Colombier print("igbe: can't set EEPROM address 0x%2.2X\n", addr);
17366a081dcdSDavid du Colombier goto release;
17376a081dcdSDavid du Colombier }
17386a081dcdSDavid du Colombier data = at93c46io(ctlr, ":16COc;", 0);
17396a081dcdSDavid du Colombier at93c46io(ctlr, "sic", 0);
17406a081dcdSDavid du Colombier ctlr->eeprom[addr] = data;
17416a081dcdSDavid du Colombier sum += data;
17426a081dcdSDavid du Colombier }
17436a081dcdSDavid du Colombier
17446a081dcdSDavid du Colombier release:
17456a081dcdSDavid du Colombier if(areq)
17466a081dcdSDavid du Colombier csr32w(ctlr, Eecd, eecd & ~Areq);
17476a081dcdSDavid du Colombier return sum;
17486a081dcdSDavid du Colombier }
17496a081dcdSDavid du Colombier
17505d9682faSDavid du Colombier static int
igbedetach(Ctlr * ctlr)17516a081dcdSDavid du Colombier igbedetach(Ctlr* ctlr)
17526a081dcdSDavid du Colombier {
17535d9682faSDavid du Colombier int r, timeo;
17546a081dcdSDavid du Colombier
17556a081dcdSDavid du Colombier /*
17566a081dcdSDavid du Colombier * Perform a device reset to get the chip back to the
17576a081dcdSDavid du Colombier * power-on state, followed by an EEPROM reset to read
17586a081dcdSDavid du Colombier * the defaults for some internal registers.
17596a081dcdSDavid du Colombier */
17606a081dcdSDavid du Colombier csr32w(ctlr, Imc, ~0);
17616a081dcdSDavid du Colombier csr32w(ctlr, Rctl, 0);
17626a081dcdSDavid du Colombier csr32w(ctlr, Tctl, 0);
17636a081dcdSDavid du Colombier
1764ff8c3af2SDavid du Colombier delay(10);
17656a081dcdSDavid du Colombier
17666a081dcdSDavid du Colombier csr32w(ctlr, Ctrl, Devrst);
17675d9682faSDavid du Colombier delay(1);
17685d9682faSDavid du Colombier for(timeo = 0; timeo < 1000; timeo++){
17695d9682faSDavid du Colombier if(!(csr32r(ctlr, Ctrl) & Devrst))
17705d9682faSDavid du Colombier break;
17715d9682faSDavid du Colombier delay(1);
17725d9682faSDavid du Colombier }
17735d9682faSDavid du Colombier if(csr32r(ctlr, Ctrl) & Devrst)
17745d9682faSDavid du Colombier return -1;
1775aeb1c8a5SDavid du Colombier r = csr32r(ctlr, Ctrlext);
1776aeb1c8a5SDavid du Colombier csr32w(ctlr, Ctrlext, r|Eerst);
17775d9682faSDavid du Colombier delay(1);
17785d9682faSDavid du Colombier for(timeo = 0; timeo < 1000; timeo++){
17795d9682faSDavid du Colombier if(!(csr32r(ctlr, Ctrlext) & Eerst))
17805d9682faSDavid du Colombier break;
17815d9682faSDavid du Colombier delay(1);
17825d9682faSDavid du Colombier }
17835d9682faSDavid du Colombier if(csr32r(ctlr, Ctrlext) & Eerst)
17845d9682faSDavid du Colombier return -1;
17856a081dcdSDavid du Colombier
17866a081dcdSDavid du Colombier switch(ctlr->id){
17876a081dcdSDavid du Colombier default:
17886a081dcdSDavid du Colombier break;
1789aeb1c8a5SDavid du Colombier case i82540em:
1790aeb1c8a5SDavid du Colombier case i82540eplp:
1791aeb1c8a5SDavid du Colombier case i82541gi:
179226ad7229SDavid du Colombier case i82541gi2:
17935efba406SDavid du Colombier case i82541pi:
1794c079eed3SDavid du Colombier case i82545em:
1795ab3dc52fSDavid du Colombier case i82545gmc:
17965efba406SDavid du Colombier case i82547gi:
1797aeb1c8a5SDavid du Colombier case i82546gb:
17985b7d6169SDavid du Colombier case i82546eb:
17996a081dcdSDavid du Colombier r = csr32r(ctlr, Manc);
18006a081dcdSDavid du Colombier r &= ~Arpen;
18016a081dcdSDavid du Colombier csr32w(ctlr, Manc, r);
18026a081dcdSDavid du Colombier break;
18036a081dcdSDavid du Colombier }
18046a081dcdSDavid du Colombier
18056a081dcdSDavid du Colombier csr32w(ctlr, Imc, ~0);
18065d9682faSDavid du Colombier delay(1);
18075d9682faSDavid du Colombier for(timeo = 0; timeo < 1000; timeo++){
18085d9682faSDavid du Colombier if(!csr32r(ctlr, Icr))
18095d9682faSDavid du Colombier break;
18105d9682faSDavid du Colombier delay(1);
18115d9682faSDavid du Colombier }
18125d9682faSDavid du Colombier if(csr32r(ctlr, Icr))
18135d9682faSDavid du Colombier return -1;
18145d9682faSDavid du Colombier
18155d9682faSDavid du Colombier return 0;
18166a081dcdSDavid du Colombier }
18176a081dcdSDavid du Colombier
18180809e9a7SDavid du Colombier static void
igbeshutdown(Ether * ether)18190809e9a7SDavid du Colombier igbeshutdown(Ether* ether)
18200809e9a7SDavid du Colombier {
18210809e9a7SDavid du Colombier igbedetach(ether->ctlr);
18220809e9a7SDavid du Colombier }
18230809e9a7SDavid du Colombier
1824ff8c3af2SDavid du Colombier static int
igbereset(Ctlr * ctlr)1825ff8c3af2SDavid du Colombier igbereset(Ctlr* ctlr)
18266a081dcdSDavid du Colombier {
18276a081dcdSDavid du Colombier int ctrl, i, pause, r, swdpio, txcw;
18286a081dcdSDavid du Colombier
18295d9682faSDavid du Colombier if(igbedetach(ctlr))
18305d9682faSDavid du Colombier return -1;
18316a081dcdSDavid du Colombier
18326a081dcdSDavid du Colombier /*
18336a081dcdSDavid du Colombier * Read the EEPROM, validate the checksum
18346a081dcdSDavid du Colombier * then get the device back to a power-on state.
18356a081dcdSDavid du Colombier */
18366a081dcdSDavid du Colombier if((r = at93c46r(ctlr)) != 0xBABA){
18376a081dcdSDavid du Colombier print("igbe: bad EEPROM checksum - 0x%4.4uX\n", r);
18386a081dcdSDavid du Colombier return -1;
18396a081dcdSDavid du Colombier }
18406a081dcdSDavid du Colombier
18416a081dcdSDavid du Colombier /*
18426a081dcdSDavid du Colombier * Snarf and set up the receive addresses.
18436a081dcdSDavid du Colombier * There are 16 addresses. The first should be the MAC address.
18446a081dcdSDavid du Colombier * The others are cleared and not marked valid (MS bit of Rah).
18456a081dcdSDavid du Colombier */
184641dd6b47SDavid du Colombier if ((ctlr->id == i82546gb || ctlr->id == i82546eb) &&
184741dd6b47SDavid du Colombier BUSFNO(ctlr->pcidev->tbdf) == 1)
184841dd6b47SDavid du Colombier ctlr->eeprom[Ea+2] += 0x100; /* second interface */
18497e254d1cSDavid du Colombier if(ctlr->id == i82541gi && ctlr->eeprom[Ea] == 0xFFFF)
18507e254d1cSDavid du Colombier ctlr->eeprom[Ea] = 0xD000;
18516a081dcdSDavid du Colombier for(i = Ea; i < Eaddrlen/2; i++){
18526a081dcdSDavid du Colombier ctlr->ra[2*i] = ctlr->eeprom[i];
18536a081dcdSDavid du Colombier ctlr->ra[2*i+1] = ctlr->eeprom[i]>>8;
18546a081dcdSDavid du Colombier }
18557e254d1cSDavid du Colombier /* lan id seems to vary on 82543gc; don't use it */
18567e254d1cSDavid du Colombier if (ctlr->id != i82543gc) {
18577e254d1cSDavid du Colombier r = (csr32r(ctlr, Status) & Lanid) >> 2;
18587e254d1cSDavid du Colombier ctlr->ra[5] += r; /* ea ctlr[1] = ea ctlr[0]+1 */
18597e254d1cSDavid du Colombier }
18607e254d1cSDavid du Colombier
18616a081dcdSDavid du Colombier r = (ctlr->ra[3]<<24)|(ctlr->ra[2]<<16)|(ctlr->ra[1]<<8)|ctlr->ra[0];
18626a081dcdSDavid du Colombier csr32w(ctlr, Ral, r);
18636a081dcdSDavid du Colombier r = 0x80000000|(ctlr->ra[5]<<8)|ctlr->ra[4];
18646a081dcdSDavid du Colombier csr32w(ctlr, Rah, r);
18656a081dcdSDavid du Colombier for(i = 1; i < 16; i++){
18666a081dcdSDavid du Colombier csr32w(ctlr, Ral+i*8, 0);
18676a081dcdSDavid du Colombier csr32w(ctlr, Rah+i*8, 0);
18686a081dcdSDavid du Colombier }
18696a081dcdSDavid du Colombier
18706a081dcdSDavid du Colombier /*
18716a081dcdSDavid du Colombier * Clear the Multicast Table Array.
18726a081dcdSDavid du Colombier * It's a 4096 bit vector accessed as 128 32-bit registers.
18736a081dcdSDavid du Colombier */
1874ecea9424SDavid du Colombier memset(ctlr->mta, 0, sizeof(ctlr->mta));
18756a081dcdSDavid du Colombier for(i = 0; i < 128; i++)
18766a081dcdSDavid du Colombier csr32w(ctlr, Mta+i*4, 0);
18776a081dcdSDavid du Colombier
18786a081dcdSDavid du Colombier /*
18796a081dcdSDavid du Colombier * Just in case the Eerst didn't load the defaults
1880208510e1SDavid du Colombier * (doesn't appear to fully on the 82543GC), do it manually.
18816a081dcdSDavid du Colombier */
1882208510e1SDavid du Colombier if (ctlr->id == i82543gc) {
18836a081dcdSDavid du Colombier txcw = csr32r(ctlr, Txcw);
18846a081dcdSDavid du Colombier txcw &= ~(TxcwAne|TxcwPauseMASK|TxcwFd);
18856a081dcdSDavid du Colombier ctrl = csr32r(ctlr, Ctrl);
18866a081dcdSDavid du Colombier ctrl &= ~(SwdpioloMASK|Frcspd|Ilos|Lrst|Fd);
18876a081dcdSDavid du Colombier
18886a081dcdSDavid du Colombier if(ctlr->eeprom[Icw1] & 0x0400){
18896a081dcdSDavid du Colombier ctrl |= Fd;
18906a081dcdSDavid du Colombier txcw |= TxcwFd;
18916a081dcdSDavid du Colombier }
18926a081dcdSDavid du Colombier if(ctlr->eeprom[Icw1] & 0x0200)
18936a081dcdSDavid du Colombier ctrl |= Lrst;
18946a081dcdSDavid du Colombier if(ctlr->eeprom[Icw1] & 0x0010)
18956a081dcdSDavid du Colombier ctrl |= Ilos;
18966a081dcdSDavid du Colombier if(ctlr->eeprom[Icw1] & 0x0800)
18976a081dcdSDavid du Colombier ctrl |= Frcspd;
18986a081dcdSDavid du Colombier swdpio = (ctlr->eeprom[Icw1] & 0x01E0)>>5;
18996a081dcdSDavid du Colombier ctrl |= swdpio<<SwdpioloSHIFT;
19006a081dcdSDavid du Colombier csr32w(ctlr, Ctrl, ctrl);
19016a081dcdSDavid du Colombier
19026a081dcdSDavid du Colombier ctrl = csr32r(ctlr, Ctrlext);
19036a081dcdSDavid du Colombier ctrl &= ~(Ips|SwdpiohiMASK);
19046a081dcdSDavid du Colombier swdpio = (ctlr->eeprom[Icw2] & 0x00F0)>>4;
19056a081dcdSDavid du Colombier if(ctlr->eeprom[Icw1] & 0x1000)
19066a081dcdSDavid du Colombier ctrl |= Ips;
19076a081dcdSDavid du Colombier ctrl |= swdpio<<SwdpiohiSHIFT;
19086a081dcdSDavid du Colombier csr32w(ctlr, Ctrlext, ctrl);
19096a081dcdSDavid du Colombier
19106a081dcdSDavid du Colombier if(ctlr->eeprom[Icw2] & 0x0800)
19116a081dcdSDavid du Colombier txcw |= TxcwAne;
19126a081dcdSDavid du Colombier pause = (ctlr->eeprom[Icw2] & 0x3000)>>12;
19136a081dcdSDavid du Colombier txcw |= pause<<TxcwPauseSHIFT;
19146a081dcdSDavid du Colombier switch(pause){
19156a081dcdSDavid du Colombier default:
19166a081dcdSDavid du Colombier ctlr->fcrtl = 0x00002000;
19176a081dcdSDavid du Colombier ctlr->fcrth = 0x00004000;
19186a081dcdSDavid du Colombier txcw |= TxcwAs|TxcwPs;
19196a081dcdSDavid du Colombier break;
19206a081dcdSDavid du Colombier case 0:
19216a081dcdSDavid du Colombier ctlr->fcrtl = 0x00002000;
19226a081dcdSDavid du Colombier ctlr->fcrth = 0x00004000;
19236a081dcdSDavid du Colombier break;
19246a081dcdSDavid du Colombier case 2:
19256a081dcdSDavid du Colombier ctlr->fcrtl = 0;
19266a081dcdSDavid du Colombier ctlr->fcrth = 0;
19276a081dcdSDavid du Colombier txcw |= TxcwAs;
19286a081dcdSDavid du Colombier break;
19296a081dcdSDavid du Colombier }
19306a081dcdSDavid du Colombier ctlr->txcw = txcw;
19316a081dcdSDavid du Colombier csr32w(ctlr, Txcw, txcw);
1932aeb1c8a5SDavid du Colombier }
1933aeb1c8a5SDavid du Colombier
19346a081dcdSDavid du Colombier
19356a081dcdSDavid du Colombier /*
19366a081dcdSDavid du Colombier * Flow control - values from the datasheet.
19376a081dcdSDavid du Colombier */
19386a081dcdSDavid du Colombier csr32w(ctlr, Fcal, 0x00C28001);
19396a081dcdSDavid du Colombier csr32w(ctlr, Fcah, 0x00000100);
19406a081dcdSDavid du Colombier csr32w(ctlr, Fct, 0x00008808);
19416a081dcdSDavid du Colombier csr32w(ctlr, Fcttv, 0x00000100);
19426a081dcdSDavid du Colombier
19436a081dcdSDavid du Colombier csr32w(ctlr, Fcrtl, ctlr->fcrtl);
19446a081dcdSDavid du Colombier csr32w(ctlr, Fcrth, ctlr->fcrth);
19456a081dcdSDavid du Colombier
19466a081dcdSDavid du Colombier if(!(csr32r(ctlr, Status) & Tbimode) && igbemii(ctlr) < 0)
19476a081dcdSDavid du Colombier return -1;
19486a081dcdSDavid du Colombier
19496a081dcdSDavid du Colombier return 0;
19506a081dcdSDavid du Colombier }
19516a081dcdSDavid du Colombier
19526a081dcdSDavid du Colombier static void
igbepci(void)19536a081dcdSDavid du Colombier igbepci(void)
19546a081dcdSDavid du Colombier {
19554de34a7eSDavid du Colombier int cls;
19566a081dcdSDavid du Colombier Pcidev *p;
19576a081dcdSDavid du Colombier Ctlr *ctlr;
19584de34a7eSDavid du Colombier void *mem;
19596a081dcdSDavid du Colombier
19606a081dcdSDavid du Colombier p = nil;
19616a081dcdSDavid du Colombier while(p = pcimatch(p, 0, 0)){
19626a081dcdSDavid du Colombier if(p->ccrb != 0x02 || p->ccru != 0)
19636a081dcdSDavid du Colombier continue;
19646a081dcdSDavid du Colombier
19656a081dcdSDavid du Colombier switch((p->did<<16)|p->vid){
19666a081dcdSDavid du Colombier default:
19676a081dcdSDavid du Colombier continue;
1968aeb1c8a5SDavid du Colombier case i82543gc:
1969aeb1c8a5SDavid du Colombier case i82544ei:
197007c32395SDavid du Colombier case i82544eif:
19719a8968c3SDavid du Colombier case i82544gc:
1972aeb1c8a5SDavid du Colombier case i82547ei:
19735efba406SDavid du Colombier case i82547gi:
1974aeb1c8a5SDavid du Colombier case i82540em:
1975aeb1c8a5SDavid du Colombier case i82540eplp:
19765efba406SDavid du Colombier case i82541ei:
1977aeb1c8a5SDavid du Colombier case i82541gi:
197826ad7229SDavid du Colombier case i82541gi2:
1979d717568cSDavid du Colombier case i82541pi:
1980c079eed3SDavid du Colombier case i82545em:
1981ab3dc52fSDavid du Colombier case i82545gmc:
1982aeb1c8a5SDavid du Colombier case i82546gb:
19835b7d6169SDavid du Colombier case i82546eb:
19846a081dcdSDavid du Colombier break;
19856a081dcdSDavid du Colombier }
19866a081dcdSDavid du Colombier
19874de34a7eSDavid du Colombier mem = vmap(p->mem[0].bar & ~0x0F, p->mem[0].size);
19884de34a7eSDavid du Colombier if(mem == nil){
19896a081dcdSDavid du Colombier print("igbe: can't map %8.8luX\n", p->mem[0].bar);
19906a081dcdSDavid du Colombier continue;
19916a081dcdSDavid du Colombier }
19926a081dcdSDavid du Colombier cls = pcicfgr8(p, PciCLS);
19936a081dcdSDavid du Colombier switch(cls){
19946a081dcdSDavid du Colombier default:
19950591a7c1SDavid du Colombier print("igbe: p->cls %#ux, setting to 0x10\n", p->cls);
19960591a7c1SDavid du Colombier p->cls = 0x10;
19970591a7c1SDavid du Colombier pcicfgw8(p, PciCLS, p->cls);
19986a081dcdSDavid du Colombier break;
19996a081dcdSDavid du Colombier case 0x08:
20006a081dcdSDavid du Colombier case 0x10:
20016a081dcdSDavid du Colombier break;
20026a081dcdSDavid du Colombier }
20036a081dcdSDavid du Colombier ctlr = malloc(sizeof(Ctlr));
2004aa72973aSDavid du Colombier if(ctlr == nil) {
2005aa72973aSDavid du Colombier vunmap(mem, p->mem[0].size);
2006aa72973aSDavid du Colombier error(Enomem);
2007aa72973aSDavid du Colombier }
20084de34a7eSDavid du Colombier ctlr->port = p->mem[0].bar & ~0x0F;
20096a081dcdSDavid du Colombier ctlr->pcidev = p;
20106a081dcdSDavid du Colombier ctlr->id = (p->did<<16)|p->vid;
20116a081dcdSDavid du Colombier ctlr->cls = cls*4;
20124de34a7eSDavid du Colombier ctlr->nic = mem;
20136a081dcdSDavid du Colombier
2014ff8c3af2SDavid du Colombier if(igbereset(ctlr)){
20156a081dcdSDavid du Colombier free(ctlr);
20165979f962SDavid du Colombier vunmap(mem, p->mem[0].size);
20176a081dcdSDavid du Colombier continue;
20186a081dcdSDavid du Colombier }
20196a081dcdSDavid du Colombier pcisetbme(p);
20206a081dcdSDavid du Colombier
20216a081dcdSDavid du Colombier if(igbectlrhead != nil)
20226a081dcdSDavid du Colombier igbectlrtail->next = ctlr;
20236a081dcdSDavid du Colombier else
20246a081dcdSDavid du Colombier igbectlrhead = ctlr;
20256a081dcdSDavid du Colombier igbectlrtail = ctlr;
20266a081dcdSDavid du Colombier }
20276a081dcdSDavid du Colombier }
20286a081dcdSDavid du Colombier
20296a081dcdSDavid du Colombier static int
igbepnp(Ether * edev)20306a081dcdSDavid du Colombier igbepnp(Ether* edev)
20316a081dcdSDavid du Colombier {
20326a081dcdSDavid du Colombier Ctlr *ctlr;
20336a081dcdSDavid du Colombier
20346a081dcdSDavid du Colombier if(igbectlrhead == nil)
20356a081dcdSDavid du Colombier igbepci();
20366a081dcdSDavid du Colombier
20376a081dcdSDavid du Colombier /*
20386a081dcdSDavid du Colombier * Any adapter matches if no edev->port is supplied,
20396a081dcdSDavid du Colombier * otherwise the ports must match.
20406a081dcdSDavid du Colombier */
20416a081dcdSDavid du Colombier for(ctlr = igbectlrhead; ctlr != nil; ctlr = ctlr->next){
20426a081dcdSDavid du Colombier if(ctlr->active)
20436a081dcdSDavid du Colombier continue;
20446a081dcdSDavid du Colombier if(edev->port == 0 || edev->port == ctlr->port){
20456a081dcdSDavid du Colombier ctlr->active = 1;
20466a081dcdSDavid du Colombier break;
20476a081dcdSDavid du Colombier }
20486a081dcdSDavid du Colombier }
20496a081dcdSDavid du Colombier if(ctlr == nil)
20506a081dcdSDavid du Colombier return -1;
20516a081dcdSDavid du Colombier
20526a081dcdSDavid du Colombier edev->ctlr = ctlr;
20536a081dcdSDavid du Colombier edev->port = ctlr->port;
20546a081dcdSDavid du Colombier edev->irq = ctlr->pcidev->intl;
20556a081dcdSDavid du Colombier edev->tbdf = ctlr->pcidev->tbdf;
20566a081dcdSDavid du Colombier edev->mbps = 1000;
20576a081dcdSDavid du Colombier memmove(edev->ea, ctlr->ra, Eaddrlen);
20586a081dcdSDavid du Colombier
20596a081dcdSDavid du Colombier /*
20606a081dcdSDavid du Colombier * Linkage to the generic ethernet driver.
20616a081dcdSDavid du Colombier */
20626a081dcdSDavid du Colombier edev->attach = igbeattach;
20636a081dcdSDavid du Colombier edev->transmit = igbetransmit;
20646a081dcdSDavid du Colombier edev->interrupt = igbeinterrupt;
20656a081dcdSDavid du Colombier edev->ifstat = igbeifstat;
20666a081dcdSDavid du Colombier edev->ctl = igbectl;
20676a081dcdSDavid du Colombier
20686a081dcdSDavid du Colombier edev->arg = edev;
20696a081dcdSDavid du Colombier edev->promiscuous = igbepromiscuous;
20700809e9a7SDavid du Colombier edev->shutdown = igbeshutdown;
2071ecea9424SDavid du Colombier edev->multicast = igbemulticast;
2072ff8c3af2SDavid du Colombier
20736a081dcdSDavid du Colombier return 0;
20746a081dcdSDavid du Colombier }
20756a081dcdSDavid du Colombier
20766a081dcdSDavid du Colombier void
etherigbelink(void)20776a081dcdSDavid du Colombier etherigbelink(void)
20786a081dcdSDavid du Colombier {
20796a081dcdSDavid du Colombier addethercard("i82543", igbepnp);
20806a081dcdSDavid du Colombier addethercard("igbe", igbepnp);
20816a081dcdSDavid du Colombier }
20828466d066SDavid du Colombier
2083