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,
447e696e17dSDavid du Colombier /*
448e696e17dSDavid du Colombier * these were 256, 1024 & 64, but 52, 253 & 9 are usually ample;
449e696e17dSDavid du Colombier * however cpu servers and terminals can need more receive buffers
450e696e17dSDavid du Colombier * due to bursts of traffic.
451e696e17dSDavid du Colombier */
452588d0145SDavid du Colombier Nrd = 128, /* multiple of 8 */
453e696e17dSDavid du Colombier Nrb = 1024, /* private receive buffers per Ctlr */
454588d0145SDavid du Colombier Ntd = 32, /* multiple of 8 */
4556a081dcdSDavid du Colombier };
4566a081dcdSDavid du Colombier
4576a081dcdSDavid du Colombier typedef struct Ctlr Ctlr;
458c02f0a41SDavid du Colombier struct Ctlr {
4598466d066SDavid du Colombier int port;
4606a081dcdSDavid du Colombier Pcidev* pcidev;
4616a081dcdSDavid du Colombier Ctlr* next;
462208510e1SDavid du Colombier Ether* edev;
4636a081dcdSDavid du Colombier int active;
4646a081dcdSDavid du Colombier int started;
4656a081dcdSDavid du Colombier int id;
4666a081dcdSDavid du Colombier int cls;
4676a081dcdSDavid du Colombier ushort eeprom[0x40];
4686a081dcdSDavid du Colombier
4696a081dcdSDavid du Colombier QLock alock; /* attach */
4706a081dcdSDavid du Colombier void* alloc; /* receive/transmit descriptors */
4716a081dcdSDavid du Colombier int nrd;
4726a081dcdSDavid du Colombier int ntd;
473c02f0a41SDavid du Colombier int nrb; /* # bufs this Ctlr has in the pool */
4746a081dcdSDavid du Colombier
4756a081dcdSDavid du Colombier int* nic;
4766a081dcdSDavid du Colombier Lock imlock;
4776a081dcdSDavid du Colombier int im; /* interrupt mask */
4786a081dcdSDavid du Colombier
4796a081dcdSDavid du Colombier Mii* mii;
4806a081dcdSDavid du Colombier Rendez lrendez;
4816a081dcdSDavid du Colombier int lim;
4826a081dcdSDavid du Colombier
4836a081dcdSDavid du Colombier int link;
4846a081dcdSDavid du Colombier
485217e9e83SDavid du Colombier Watermark wmrb;
486217e9e83SDavid du Colombier Watermark wmrd;
487217e9e83SDavid du Colombier Watermark wmtd;
488217e9e83SDavid du Colombier
4896a081dcdSDavid du Colombier QLock slock;
4906a081dcdSDavid du Colombier uint statistics[Nstatistics];
4916a081dcdSDavid du Colombier uint lsleep;
4926a081dcdSDavid du Colombier uint lintr;
4936a081dcdSDavid du Colombier uint rsleep;
4946a081dcdSDavid du Colombier uint rintr;
4956a081dcdSDavid du Colombier uint txdw;
4966a081dcdSDavid du Colombier uint tintr;
4976a081dcdSDavid du Colombier uint ixsm;
4986a081dcdSDavid du Colombier uint ipcs;
4996a081dcdSDavid du Colombier uint tcpcs;
5006a081dcdSDavid du Colombier
5016a081dcdSDavid du Colombier uchar ra[Eaddrlen]; /* receive address */
5026a081dcdSDavid du Colombier ulong mta[128]; /* multicast table array */
5036a081dcdSDavid du Colombier
5046a081dcdSDavid du Colombier Rendez rrendez;
5056a081dcdSDavid du Colombier int rim;
506c02f0a41SDavid du Colombier int rdfree; /* rx descriptors awaiting packets */
5076a081dcdSDavid du Colombier Rd* rdba; /* receive descriptor base address */
5086a081dcdSDavid du Colombier Block** rb; /* receive buffers */
5096a081dcdSDavid du Colombier int rdh; /* receive descriptor head */
5106a081dcdSDavid du Colombier int rdt; /* receive descriptor tail */
5116a081dcdSDavid du Colombier int rdtr; /* receive delay timer ring value */
5126a081dcdSDavid du Colombier
5136a081dcdSDavid du Colombier Lock tlock;
5146a081dcdSDavid du Colombier int tdfree;
5156a081dcdSDavid du Colombier Td* tdba; /* transmit descriptor base address */
5166a081dcdSDavid du Colombier Block** tb; /* transmit buffers */
5176a081dcdSDavid du Colombier int tdh; /* transmit descriptor head */
5186a081dcdSDavid du Colombier int tdt; /* transmit descriptor tail */
5196a081dcdSDavid du Colombier
5206a081dcdSDavid du Colombier int txcw;
5216a081dcdSDavid du Colombier int fcrtl;
5226a081dcdSDavid du Colombier int fcrth;
523c02f0a41SDavid du Colombier };
5246a081dcdSDavid du Colombier
5256a081dcdSDavid du Colombier #define csr32r(c, r) (*((c)->nic+((r)/4)))
5266a081dcdSDavid du Colombier #define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v))
5276a081dcdSDavid du Colombier
5286a081dcdSDavid du Colombier static Ctlr* igbectlrhead;
5296a081dcdSDavid du Colombier static Ctlr* igbectlrtail;
5306a081dcdSDavid du Colombier
5316a081dcdSDavid du Colombier static Lock igberblock; /* free receive Blocks */
532208510e1SDavid du Colombier static Block* igberbpool; /* receive Blocks for all igbe controllers */
533217e9e83SDavid du Colombier static int nrbfull; /* # of rcv Blocks with data awaiting processing */
5346a081dcdSDavid du Colombier
5356a081dcdSDavid du Colombier static char* statistics[Nstatistics] = {
5366a081dcdSDavid du Colombier "CRC Error",
5376a081dcdSDavid du Colombier "Alignment Error",
5386a081dcdSDavid du Colombier "Symbol Error",
5396a081dcdSDavid du Colombier "RX Error",
5406a081dcdSDavid du Colombier "Missed Packets",
5416a081dcdSDavid du Colombier "Single Collision",
5426a081dcdSDavid du Colombier "Excessive Collisions",
5436a081dcdSDavid du Colombier "Multiple Collision",
5446a081dcdSDavid du Colombier "Late Collisions",
5456a081dcdSDavid du Colombier nil,
5466a081dcdSDavid du Colombier "Collision",
5476a081dcdSDavid du Colombier "Transmit Underrun",
5486a081dcdSDavid du Colombier "Defer",
5496a081dcdSDavid du Colombier "Transmit - No CRS",
5506a081dcdSDavid du Colombier "Sequence Error",
5516a081dcdSDavid du Colombier "Carrier Extension Error",
5526a081dcdSDavid du Colombier "Receive Error Length",
5536a081dcdSDavid du Colombier nil,
5546a081dcdSDavid du Colombier "XON Received",
5556a081dcdSDavid du Colombier "XON Transmitted",
5566a081dcdSDavid du Colombier "XOFF Received",
5576a081dcdSDavid du Colombier "XOFF Transmitted",
5586a081dcdSDavid du Colombier "FC Received Unsupported",
5596a081dcdSDavid du Colombier "Packets Received (64 Bytes)",
5606a081dcdSDavid du Colombier "Packets Received (65-127 Bytes)",
5616a081dcdSDavid du Colombier "Packets Received (128-255 Bytes)",
5626a081dcdSDavid du Colombier "Packets Received (256-511 Bytes)",
5636a081dcdSDavid du Colombier "Packets Received (512-1023 Bytes)",
5646a081dcdSDavid du Colombier "Packets Received (1024-1522 Bytes)",
5656a081dcdSDavid du Colombier "Good Packets Received",
5666a081dcdSDavid du Colombier "Broadcast Packets Received",
5676a081dcdSDavid du Colombier "Multicast Packets Received",
5686a081dcdSDavid du Colombier "Good Packets Transmitted",
5696a081dcdSDavid du Colombier nil,
5706a081dcdSDavid du Colombier "Good Octets Received",
5716a081dcdSDavid du Colombier nil,
5726a081dcdSDavid du Colombier "Good Octets Transmitted",
5736a081dcdSDavid du Colombier nil,
5746a081dcdSDavid du Colombier nil,
5756a081dcdSDavid du Colombier nil,
5766a081dcdSDavid du Colombier "Receive No Buffers",
5776a081dcdSDavid du Colombier "Receive Undersize",
5786a081dcdSDavid du Colombier "Receive Fragment",
5796a081dcdSDavid du Colombier "Receive Oversize",
5806a081dcdSDavid du Colombier "Receive Jabber",
5816a081dcdSDavid du Colombier nil,
5826a081dcdSDavid du Colombier nil,
5836a081dcdSDavid du Colombier nil,
5846a081dcdSDavid du Colombier "Total Octets Received",
5856a081dcdSDavid du Colombier nil,
5866a081dcdSDavid du Colombier "Total Octets Transmitted",
5876a081dcdSDavid du Colombier nil,
5886a081dcdSDavid du Colombier "Total Packets Received",
5896a081dcdSDavid du Colombier "Total Packets Transmitted",
5906a081dcdSDavid du Colombier "Packets Transmitted (64 Bytes)",
5916a081dcdSDavid du Colombier "Packets Transmitted (65-127 Bytes)",
5926a081dcdSDavid du Colombier "Packets Transmitted (128-255 Bytes)",
5936a081dcdSDavid du Colombier "Packets Transmitted (256-511 Bytes)",
5946a081dcdSDavid du Colombier "Packets Transmitted (512-1023 Bytes)",
5956a081dcdSDavid du Colombier "Packets Transmitted (1024-1522 Bytes)",
5966a081dcdSDavid du Colombier "Multicast Packets Transmitted",
5976a081dcdSDavid du Colombier "Broadcast Packets Transmitted",
5986a081dcdSDavid du Colombier "TCP Segmentation Context Transmitted",
5996a081dcdSDavid du Colombier "TCP Segmentation Context Fail",
6006a081dcdSDavid du Colombier };
6016a081dcdSDavid du Colombier
6026a081dcdSDavid du Colombier static long
igbeifstat(Ether * edev,void * a,long n,ulong offset)6036a081dcdSDavid du Colombier igbeifstat(Ether* edev, void* a, long n, ulong offset)
6046a081dcdSDavid du Colombier {
6056a081dcdSDavid du Colombier Ctlr *ctlr;
606217e9e83SDavid du Colombier char *p, *s, *e;
6076a081dcdSDavid du Colombier int i, l, r;
6086a081dcdSDavid du Colombier uvlong tuvl, ruvl;
6096a081dcdSDavid du Colombier
6106a081dcdSDavid du Colombier ctlr = edev->ctlr;
6116a081dcdSDavid du Colombier qlock(&ctlr->slock);
61246136019SDavid du Colombier p = malloc(READSTR);
613aa72973aSDavid du Colombier if(p == nil) {
614aa72973aSDavid du Colombier qunlock(&ctlr->slock);
615aa72973aSDavid du Colombier error(Enomem);
616aa72973aSDavid du Colombier }
6176a081dcdSDavid du Colombier l = 0;
6186a081dcdSDavid du Colombier for(i = 0; i < Nstatistics; i++){
6196a081dcdSDavid du Colombier r = csr32r(ctlr, Statistics+i*4);
6206a081dcdSDavid du Colombier if((s = statistics[i]) == nil)
6216a081dcdSDavid du Colombier continue;
6226a081dcdSDavid du Colombier switch(i){
6236a081dcdSDavid du Colombier case Gorcl:
6246a081dcdSDavid du Colombier case Gotcl:
6256a081dcdSDavid du Colombier case Torl:
6266a081dcdSDavid du Colombier case Totl:
6276a081dcdSDavid du Colombier ruvl = r;
6286a081dcdSDavid du Colombier ruvl += ((uvlong)csr32r(ctlr, Statistics+(i+1)*4))<<32;
6296a081dcdSDavid du Colombier tuvl = ruvl;
6306a081dcdSDavid du Colombier tuvl += ctlr->statistics[i];
6316a081dcdSDavid du Colombier tuvl += ((uvlong)ctlr->statistics[i+1])<<32;
6326a081dcdSDavid du Colombier if(tuvl == 0)
6336a081dcdSDavid du Colombier continue;
6346a081dcdSDavid du Colombier ctlr->statistics[i] = tuvl;
6356a081dcdSDavid du Colombier ctlr->statistics[i+1] = tuvl>>32;
63646136019SDavid du Colombier l += snprint(p+l, READSTR-l, "%s: %llud %llud\n",
6376a081dcdSDavid du Colombier s, tuvl, ruvl);
6386a081dcdSDavid du Colombier i++;
6396a081dcdSDavid du Colombier break;
6406a081dcdSDavid du Colombier
6416a081dcdSDavid du Colombier default:
6426a081dcdSDavid du Colombier ctlr->statistics[i] += r;
6436a081dcdSDavid du Colombier if(ctlr->statistics[i] == 0)
6446a081dcdSDavid du Colombier continue;
64546136019SDavid du Colombier l += snprint(p+l, READSTR-l, "%s: %ud %ud\n",
6466a081dcdSDavid du Colombier s, ctlr->statistics[i], r);
6476a081dcdSDavid du Colombier break;
6486a081dcdSDavid du Colombier }
6496a081dcdSDavid du Colombier }
6506a081dcdSDavid du Colombier
65146136019SDavid du Colombier l += snprint(p+l, READSTR-l, "lintr: %ud %ud\n",
6526a081dcdSDavid du Colombier ctlr->lintr, ctlr->lsleep);
65346136019SDavid du Colombier l += snprint(p+l, READSTR-l, "rintr: %ud %ud\n",
6546a081dcdSDavid du Colombier ctlr->rintr, ctlr->rsleep);
65546136019SDavid du Colombier l += snprint(p+l, READSTR-l, "tintr: %ud %ud\n",
6566a081dcdSDavid du Colombier ctlr->tintr, ctlr->txdw);
65746136019SDavid du Colombier l += snprint(p+l, READSTR-l, "ixcs: %ud %ud %ud\n",
6586a081dcdSDavid du Colombier ctlr->ixsm, ctlr->ipcs, ctlr->tcpcs);
65946136019SDavid du Colombier l += snprint(p+l, READSTR-l, "rdtr: %ud\n", ctlr->rdtr);
66046136019SDavid du Colombier l += snprint(p+l, READSTR-l, "Ctrlext: %08x\n", csr32r(ctlr, Ctrlext));
6616a081dcdSDavid du Colombier
66246136019SDavid du Colombier l += snprint(p+l, READSTR-l, "eeprom:");
6636a081dcdSDavid du Colombier for(i = 0; i < 0x40; i++){
6646a081dcdSDavid du Colombier if(i && ((i & 0x07) == 0))
66546136019SDavid du Colombier l += snprint(p+l, READSTR-l, "\n ");
66646136019SDavid du Colombier l += snprint(p+l, READSTR-l, " %4.4uX", ctlr->eeprom[i]);
6676a081dcdSDavid du Colombier }
66846136019SDavid du Colombier l += snprint(p+l, READSTR-l, "\n");
6696a081dcdSDavid du Colombier
6706a081dcdSDavid du Colombier if(ctlr->mii != nil && ctlr->mii->curphy != nil){
671923c9daaSDavid du Colombier l += snprint(p+l, READSTR-l, "phy: ");
6726a081dcdSDavid du Colombier for(i = 0; i < NMiiPhyr; i++){
6736a081dcdSDavid du Colombier if(i && ((i & 0x07) == 0))
67446136019SDavid du Colombier l += snprint(p+l, READSTR-l, "\n ");
6756a081dcdSDavid du Colombier r = miimir(ctlr->mii, i);
67646136019SDavid du Colombier l += snprint(p+l, READSTR-l, " %4.4uX", r);
6776a081dcdSDavid du Colombier }
67846136019SDavid du Colombier snprint(p+l, READSTR-l, "\n");
6796a081dcdSDavid du Colombier }
680217e9e83SDavid du Colombier e = p + READSTR;
681217e9e83SDavid du Colombier s = p + l + 1;
682217e9e83SDavid du Colombier s = seprintmark(s, e, &ctlr->wmrb);
683217e9e83SDavid du Colombier s = seprintmark(s, e, &ctlr->wmrd);
684217e9e83SDavid du Colombier s = seprintmark(s, e, &ctlr->wmtd);
685217e9e83SDavid du Colombier USED(s);
686217e9e83SDavid du Colombier
6876a081dcdSDavid du Colombier n = readstr(offset, a, n, p);
6886a081dcdSDavid du Colombier free(p);
6896a081dcdSDavid du Colombier qunlock(&ctlr->slock);
6906a081dcdSDavid du Colombier
6916a081dcdSDavid du Colombier return n;
6926a081dcdSDavid du Colombier }
6936a081dcdSDavid du Colombier
6946a081dcdSDavid du Colombier enum {
6956a081dcdSDavid du Colombier CMrdtr,
6966a081dcdSDavid du Colombier };
6976a081dcdSDavid du Colombier
6986a081dcdSDavid du Colombier static Cmdtab igbectlmsg[] = {
6996a081dcdSDavid du Colombier CMrdtr, "rdtr", 2,
7006a081dcdSDavid du Colombier };
7016a081dcdSDavid du Colombier
7026a081dcdSDavid du Colombier static long
igbectl(Ether * edev,void * buf,long n)7036a081dcdSDavid du Colombier igbectl(Ether* edev, void* buf, long n)
7046a081dcdSDavid du Colombier {
7056a081dcdSDavid du Colombier int v;
7066a081dcdSDavid du Colombier char *p;
7076a081dcdSDavid du Colombier Ctlr *ctlr;
7086a081dcdSDavid du Colombier Cmdbuf *cb;
7096a081dcdSDavid du Colombier Cmdtab *ct;
7106a081dcdSDavid du Colombier
7116a081dcdSDavid du Colombier if((ctlr = edev->ctlr) == nil)
7126a081dcdSDavid du Colombier error(Enonexist);
7136a081dcdSDavid du Colombier
7146a081dcdSDavid du Colombier cb = parsecmd(buf, n);
7156a081dcdSDavid du Colombier if(waserror()){
7166a081dcdSDavid du Colombier free(cb);
7176a081dcdSDavid du Colombier nexterror();
7186a081dcdSDavid du Colombier }
7196a081dcdSDavid du Colombier
7206a081dcdSDavid du Colombier ct = lookupcmd(cb, igbectlmsg, nelem(igbectlmsg));
7216a081dcdSDavid du Colombier switch(ct->index){
7226a081dcdSDavid du Colombier case CMrdtr:
7236a081dcdSDavid du Colombier v = strtol(cb->f[1], &p, 0);
7246a081dcdSDavid du Colombier if(v < 0 || p == cb->f[1] || v > 0xFFFF)
7256a081dcdSDavid du Colombier error(Ebadarg);
7266520663fSDavid du Colombier ctlr->rdtr = v;
7276a081dcdSDavid du Colombier csr32w(ctlr, Rdtr, Fpd|v);
7286a081dcdSDavid du Colombier break;
7296a081dcdSDavid du Colombier }
7306a081dcdSDavid du Colombier free(cb);
7316a081dcdSDavid du Colombier poperror();
7326a081dcdSDavid du Colombier
7336a081dcdSDavid du Colombier return n;
7346a081dcdSDavid du Colombier }
7356a081dcdSDavid du Colombier
7366a081dcdSDavid du Colombier static void
igbepromiscuous(void * arg,int on)7376a081dcdSDavid du Colombier igbepromiscuous(void* arg, int on)
7386a081dcdSDavid du Colombier {
7396a081dcdSDavid du Colombier int rctl;
7406a081dcdSDavid du Colombier Ctlr *ctlr;
7416a081dcdSDavid du Colombier Ether *edev;
7426a081dcdSDavid du Colombier
7436a081dcdSDavid du Colombier edev = arg;
7446a081dcdSDavid du Colombier ctlr = edev->ctlr;
7456a081dcdSDavid du Colombier
7466a081dcdSDavid du Colombier rctl = csr32r(ctlr, Rctl);
7476a081dcdSDavid du Colombier rctl &= ~MoMASK;
7486a081dcdSDavid du Colombier rctl |= Mo47b36;
7496a081dcdSDavid du Colombier if(on)
7506a081dcdSDavid du Colombier rctl |= Upe|Mpe;
7516a081dcdSDavid du Colombier else
7526a081dcdSDavid du Colombier rctl &= ~(Upe|Mpe);
753b1707c5dSDavid du Colombier csr32w(ctlr, Rctl, rctl|Mpe); /* temporarily keep Mpe on */
7546a081dcdSDavid du Colombier }
7556a081dcdSDavid du Colombier
756ecea9424SDavid du Colombier static void
igbemulticast(void * arg,uchar * addr,int add)757b1707c5dSDavid du Colombier igbemulticast(void* arg, uchar* addr, int add)
758ecea9424SDavid du Colombier {
759ecea9424SDavid du Colombier int bit, x;
760ecea9424SDavid du Colombier Ctlr *ctlr;
761ecea9424SDavid du Colombier Ether *edev;
762ecea9424SDavid du Colombier
763ecea9424SDavid du Colombier edev = arg;
764ecea9424SDavid du Colombier ctlr = edev->ctlr;
765ecea9424SDavid du Colombier
766ecea9424SDavid du Colombier x = addr[5]>>1;
767ecea9424SDavid du Colombier bit = ((addr[5] & 1)<<4)|(addr[4]>>4);
768b1707c5dSDavid du Colombier /*
769b1707c5dSDavid du Colombier * multiple ether addresses can hash to the same filter bit,
770b1707c5dSDavid du Colombier * so it's never safe to clear a filter bit.
771b1707c5dSDavid du Colombier * if we want to clear filter bits, we need to keep track of
772b1707c5dSDavid du Colombier * all the multicast addresses in use, clear all the filter bits,
773b1707c5dSDavid du Colombier * then set the ones corresponding to in-use addresses.
774b1707c5dSDavid du Colombier */
775b1707c5dSDavid du Colombier if(add)
776ecea9424SDavid du Colombier ctlr->mta[x] |= 1<<bit;
777b1707c5dSDavid du Colombier // else
778b1707c5dSDavid du Colombier // ctlr->mta[x] &= ~(1<<bit);
779ecea9424SDavid du Colombier
780ecea9424SDavid du Colombier csr32w(ctlr, Mta+x*4, ctlr->mta[x]);
781ecea9424SDavid du Colombier }
782ecea9424SDavid du Colombier
7836a081dcdSDavid du Colombier static Block*
igberballoc(void)7846a081dcdSDavid du Colombier igberballoc(void)
7856a081dcdSDavid du Colombier {
7866a081dcdSDavid du Colombier Block *bp;
7876a081dcdSDavid du Colombier
7886a081dcdSDavid du Colombier ilock(&igberblock);
7896a081dcdSDavid du Colombier if((bp = igberbpool) != nil){
7906a081dcdSDavid du Colombier igberbpool = bp->next;
7916a081dcdSDavid du Colombier bp->next = nil;
792*61d44851SDavid du Colombier ainc(&bp->ref); /* prevent bp from being freed */
7936a081dcdSDavid du Colombier }
7946a081dcdSDavid du Colombier iunlock(&igberblock);
7956a081dcdSDavid du Colombier
7966a081dcdSDavid du Colombier return bp;
7976a081dcdSDavid du Colombier }
7986a081dcdSDavid du Colombier
7996a081dcdSDavid du Colombier static void
igberbfree(Block * bp)8006a081dcdSDavid du Colombier igberbfree(Block* bp)
8016a081dcdSDavid du Colombier {
802ff8c3af2SDavid du Colombier bp->rp = bp->lim - Rbsz;
803ff8c3af2SDavid du Colombier bp->wp = bp->rp;
804bfb6eab9SDavid du Colombier bp->flag &= ~(Bipck | Budpck | Btcpck | Bpktck);
805ff8c3af2SDavid du Colombier
8066a081dcdSDavid du Colombier ilock(&igberblock);
8076a081dcdSDavid du Colombier bp->next = igberbpool;
8086a081dcdSDavid du Colombier igberbpool = bp;
809217e9e83SDavid du Colombier nrbfull--;
8106a081dcdSDavid du Colombier iunlock(&igberblock);
8116a081dcdSDavid du Colombier }
8126a081dcdSDavid du Colombier
8136a081dcdSDavid du Colombier static void
igbeim(Ctlr * ctlr,int im)8146a081dcdSDavid du Colombier igbeim(Ctlr* ctlr, int im)
8156a081dcdSDavid du Colombier {
8166a081dcdSDavid du Colombier ilock(&ctlr->imlock);
8176a081dcdSDavid du Colombier ctlr->im |= im;
8186a081dcdSDavid du Colombier csr32w(ctlr, Ims, ctlr->im);
8196a081dcdSDavid du Colombier iunlock(&ctlr->imlock);
8206a081dcdSDavid du Colombier }
8216a081dcdSDavid du Colombier
8226a081dcdSDavid du Colombier static int
igbelim(void * ctlr)8236a081dcdSDavid du Colombier igbelim(void* ctlr)
8246a081dcdSDavid du Colombier {
8256a081dcdSDavid du Colombier return ((Ctlr*)ctlr)->lim != 0;
8266a081dcdSDavid du Colombier }
8276a081dcdSDavid du Colombier
8286a081dcdSDavid du Colombier static void
igbelproc(void * arg)829ff8c3af2SDavid du Colombier igbelproc(void* arg)
8306a081dcdSDavid du Colombier {
8316a081dcdSDavid du Colombier Ctlr *ctlr;
8326a081dcdSDavid du Colombier Ether *edev;
8336a081dcdSDavid du Colombier MiiPhy *phy;
8346a081dcdSDavid du Colombier int ctrl, r;
8356a081dcdSDavid du Colombier
836ff8c3af2SDavid du Colombier edev = arg;
8376a081dcdSDavid du Colombier ctlr = edev->ctlr;
8386a081dcdSDavid du Colombier for(;;){
839bf3a53f8SDavid du Colombier if(ctlr->mii == nil || ctlr->mii->curphy == nil) {
840bf3a53f8SDavid du Colombier sched();
8416a081dcdSDavid du Colombier continue;
842bf3a53f8SDavid du Colombier }
8436a081dcdSDavid du Colombier
8446a081dcdSDavid du Colombier /*
8456a081dcdSDavid du Colombier * To do:
8466a081dcdSDavid du Colombier * logic to manage status change,
8476a081dcdSDavid du Colombier * this is incomplete but should work
8486a081dcdSDavid du Colombier * one time to set up the hardware.
8496a081dcdSDavid du Colombier *
8506a081dcdSDavid du Colombier * MiiPhy.speed, etc. should be in Mii.
8516a081dcdSDavid du Colombier */
8526a081dcdSDavid du Colombier if(miistatus(ctlr->mii) < 0)
8536a081dcdSDavid du Colombier //continue;
8546a081dcdSDavid du Colombier goto enable;
8556a081dcdSDavid du Colombier
8566a081dcdSDavid du Colombier phy = ctlr->mii->curphy;
8576a081dcdSDavid du Colombier ctrl = csr32r(ctlr, Ctrl);
8586a081dcdSDavid du Colombier
8596a081dcdSDavid du Colombier switch(ctlr->id){
860aeb1c8a5SDavid du Colombier case i82543gc:
861aeb1c8a5SDavid du Colombier case i82544ei:
86207c32395SDavid du Colombier case i82544eif:
8636a081dcdSDavid du Colombier default:
8646a081dcdSDavid du Colombier if(!(ctrl & Asde)){
8656a081dcdSDavid du Colombier ctrl &= ~(SspeedMASK|Ilos|Fd);
8666a081dcdSDavid du Colombier ctrl |= Frcdplx|Frcspd;
8676a081dcdSDavid du Colombier if(phy->speed == 1000)
8686a081dcdSDavid du Colombier ctrl |= Sspeed1000;
8696a081dcdSDavid du Colombier else if(phy->speed == 100)
8706a081dcdSDavid du Colombier ctrl |= Sspeed100;
8716a081dcdSDavid du Colombier if(phy->fd)
8726a081dcdSDavid du Colombier ctrl |= Fd;
8736a081dcdSDavid du Colombier }
8746a081dcdSDavid du Colombier break;
875aeb1c8a5SDavid du Colombier
876aeb1c8a5SDavid du Colombier case i82540em:
877aeb1c8a5SDavid du Colombier case i82540eplp:
878aeb1c8a5SDavid du Colombier case i82547gi:
879aeb1c8a5SDavid du Colombier case i82541gi:
8808466d066SDavid du Colombier case i82541gi2:
881d717568cSDavid du Colombier case i82541pi:
8826a081dcdSDavid du Colombier break;
8836a081dcdSDavid du Colombier }
8846a081dcdSDavid du Colombier
8856a081dcdSDavid du Colombier /*
8866a081dcdSDavid du Colombier * Collision Distance.
8876a081dcdSDavid du Colombier */
8886a081dcdSDavid du Colombier r = csr32r(ctlr, Tctl);
8896a081dcdSDavid du Colombier r &= ~ColdMASK;
8906a081dcdSDavid du Colombier if(phy->fd)
8916a081dcdSDavid du Colombier r |= 64<<ColdSHIFT;
8926a081dcdSDavid du Colombier else
8936a081dcdSDavid du Colombier r |= 512<<ColdSHIFT;
8946a081dcdSDavid du Colombier csr32w(ctlr, Tctl, r);
8956a081dcdSDavid du Colombier
8966a081dcdSDavid du Colombier /*
8976a081dcdSDavid du Colombier * Flow control.
8986a081dcdSDavid du Colombier */
8996a081dcdSDavid du Colombier if(phy->rfc)
9006a081dcdSDavid du Colombier ctrl |= Rfce;
9016a081dcdSDavid du Colombier if(phy->tfc)
9026a081dcdSDavid du Colombier ctrl |= Tfce;
9036a081dcdSDavid du Colombier csr32w(ctlr, Ctrl, ctrl);
9046a081dcdSDavid du Colombier
9056a081dcdSDavid du Colombier enable:
9066a081dcdSDavid du Colombier ctlr->lim = 0;
9076a081dcdSDavid du Colombier igbeim(ctlr, Lsc);
9086a081dcdSDavid du Colombier
9096a081dcdSDavid du Colombier ctlr->lsleep++;
9106a081dcdSDavid du Colombier sleep(&ctlr->lrendez, igbelim, ctlr);
9116a081dcdSDavid du Colombier }
9126a081dcdSDavid du Colombier }
9136a081dcdSDavid du Colombier
9146a081dcdSDavid du Colombier static void
igbetxinit(Ctlr * ctlr)9156a081dcdSDavid du Colombier igbetxinit(Ctlr* ctlr)
9166a081dcdSDavid du Colombier {
9176a081dcdSDavid du Colombier int i, r;
9186a081dcdSDavid du Colombier Block *bp;
9196a081dcdSDavid du Colombier
9206a081dcdSDavid du Colombier csr32w(ctlr, Tctl, (0x0F<<CtSHIFT)|Psp|(66<<ColdSHIFT));
9216a081dcdSDavid du Colombier switch(ctlr->id){
9226a081dcdSDavid du Colombier default:
9236a081dcdSDavid du Colombier r = 6;
9246a081dcdSDavid du Colombier break;
925aeb1c8a5SDavid du Colombier case i82543gc:
926aeb1c8a5SDavid du Colombier case i82544ei:
92707c32395SDavid du Colombier case i82544eif:
9289a8968c3SDavid du Colombier case i82544gc:
929aeb1c8a5SDavid du Colombier case i82540em:
930aeb1c8a5SDavid du Colombier case i82540eplp:
9315efba406SDavid du Colombier case i82541ei:
932aeb1c8a5SDavid du Colombier case i82541gi:
9338466d066SDavid du Colombier case i82541gi2:
934d717568cSDavid du Colombier case i82541pi:
935c079eed3SDavid du Colombier case i82545em:
936ab3dc52fSDavid du Colombier case i82545gmc:
937aeb1c8a5SDavid du Colombier case i82546gb:
9385b7d6169SDavid du Colombier case i82546eb:
9395efba406SDavid du Colombier case i82547ei:
940aeb1c8a5SDavid du Colombier case i82547gi:
9416a081dcdSDavid du Colombier r = 8;
9426a081dcdSDavid du Colombier break;
9436a081dcdSDavid du Colombier }
9446a081dcdSDavid du Colombier csr32w(ctlr, Tipg, (6<<20)|(8<<10)|r);
9456a081dcdSDavid du Colombier csr32w(ctlr, Ait, 0);
9466a081dcdSDavid du Colombier csr32w(ctlr, Txdmac, 0);
9476a081dcdSDavid du Colombier
9486a081dcdSDavid du Colombier csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba));
9496a081dcdSDavid du Colombier csr32w(ctlr, Tdbah, 0);
9506a081dcdSDavid du Colombier csr32w(ctlr, Tdlen, ctlr->ntd*sizeof(Td));
9516a081dcdSDavid du Colombier ctlr->tdh = PREV(0, ctlr->ntd);
9526a081dcdSDavid du Colombier csr32w(ctlr, Tdh, 0);
9536a081dcdSDavid du Colombier ctlr->tdt = 0;
9546a081dcdSDavid du Colombier csr32w(ctlr, Tdt, 0);
9556a081dcdSDavid du Colombier
9566a081dcdSDavid du Colombier for(i = 0; i < ctlr->ntd; i++){
9576a081dcdSDavid du Colombier if((bp = ctlr->tb[i]) != nil){
9586a081dcdSDavid du Colombier ctlr->tb[i] = nil;
9596a081dcdSDavid du Colombier freeb(bp);
9606a081dcdSDavid du Colombier }
9616a081dcdSDavid du Colombier memset(&ctlr->tdba[i], 0, sizeof(Td));
9626a081dcdSDavid du Colombier }
9636a081dcdSDavid du Colombier ctlr->tdfree = ctlr->ntd;
9646a081dcdSDavid du Colombier
9656a081dcdSDavid du Colombier csr32w(ctlr, Tidv, 128);
9666a081dcdSDavid du Colombier r = (4<<WthreshSHIFT)|(4<<HthreshSHIFT)|(8<<PthreshSHIFT);
9676a081dcdSDavid du Colombier
9686a081dcdSDavid du Colombier switch(ctlr->id){
9696a081dcdSDavid du Colombier default:
9706a081dcdSDavid du Colombier break;
971aeb1c8a5SDavid du Colombier case i82540em:
972aeb1c8a5SDavid du Colombier case i82540eplp:
973aeb1c8a5SDavid du Colombier case i82547gi:
974c079eed3SDavid du Colombier case i82545em:
975ab3dc52fSDavid du Colombier case i82545gmc:
976aeb1c8a5SDavid du Colombier case i82546gb:
9775b7d6169SDavid du Colombier case i82546eb:
978aeb1c8a5SDavid du Colombier case i82541gi:
9798466d066SDavid du Colombier case i82541gi2:
980d717568cSDavid du Colombier case i82541pi:
9816a081dcdSDavid du Colombier r = csr32r(ctlr, Txdctl);
9826a081dcdSDavid du Colombier r &= ~WthreshMASK;
9836a081dcdSDavid du Colombier r |= Gran|(4<<WthreshSHIFT);
9846a081dcdSDavid du Colombier
9856a081dcdSDavid du Colombier csr32w(ctlr, Tadv, 64);
9866a081dcdSDavid du Colombier break;
9876a081dcdSDavid du Colombier }
9886a081dcdSDavid du Colombier
9896a081dcdSDavid du Colombier csr32w(ctlr, Txdctl, r);
9906a081dcdSDavid du Colombier
9916a081dcdSDavid du Colombier r = csr32r(ctlr, Tctl);
9926a081dcdSDavid du Colombier r |= Ten;
9936a081dcdSDavid du Colombier csr32w(ctlr, Tctl, r);
9946a081dcdSDavid du Colombier }
9956a081dcdSDavid du Colombier
9966a081dcdSDavid du Colombier static void
igbetransmit(Ether * edev)9976a081dcdSDavid du Colombier igbetransmit(Ether* edev)
9986a081dcdSDavid du Colombier {
9996a081dcdSDavid du Colombier Td *td;
10006a081dcdSDavid du Colombier Block *bp;
10016a081dcdSDavid du Colombier Ctlr *ctlr;
10026a081dcdSDavid du Colombier int tdh, tdt;
10036a081dcdSDavid du Colombier
10046a081dcdSDavid du Colombier ctlr = edev->ctlr;
10056a081dcdSDavid du Colombier
10066a081dcdSDavid du Colombier ilock(&ctlr->tlock);
10076a081dcdSDavid du Colombier
10086a081dcdSDavid du Colombier /*
10096a081dcdSDavid du Colombier * Free any completed packets
10106a081dcdSDavid du Colombier */
10116a081dcdSDavid du Colombier tdh = ctlr->tdh;
10126a081dcdSDavid du Colombier while(NEXT(tdh, ctlr->ntd) != csr32r(ctlr, Tdh)){
10136a081dcdSDavid du Colombier if((bp = ctlr->tb[tdh]) != nil){
10146a081dcdSDavid du Colombier ctlr->tb[tdh] = nil;
10156a081dcdSDavid du Colombier freeb(bp);
10166a081dcdSDavid du Colombier }
10176a081dcdSDavid du Colombier memset(&ctlr->tdba[tdh], 0, sizeof(Td));
10186a081dcdSDavid du Colombier tdh = NEXT(tdh, ctlr->ntd);
10196a081dcdSDavid du Colombier }
10206a081dcdSDavid du Colombier ctlr->tdh = tdh;
10216a081dcdSDavid du Colombier
10226a081dcdSDavid du Colombier /*
10236a081dcdSDavid du Colombier * Try to fill the ring back up.
10246a081dcdSDavid du Colombier */
10256a081dcdSDavid du Colombier tdt = ctlr->tdt;
10266a081dcdSDavid du Colombier while(NEXT(tdt, ctlr->ntd) != tdh){
1027ff8c3af2SDavid du Colombier if((bp = qget(edev->oq)) == nil)
10286a081dcdSDavid du Colombier break;
10296a081dcdSDavid du Colombier td = &ctlr->tdba[tdt];
10306a081dcdSDavid du Colombier td->addr[0] = PCIWADDR(bp->rp);
10316a081dcdSDavid du Colombier td->control = ((BLEN(bp) & LenMASK)<<LenSHIFT);
10326a081dcdSDavid du Colombier td->control |= Dext|Ifcs|Teop|DtypeDD;
10336a081dcdSDavid du Colombier ctlr->tb[tdt] = bp;
1034217e9e83SDavid du Colombier /* note size of queue of tds awaiting transmission */
1035217e9e83SDavid du Colombier notemark(&ctlr->wmtd, (tdt + Ntd - tdh) % Ntd);
10366a081dcdSDavid du Colombier tdt = NEXT(tdt, ctlr->ntd);
10376a081dcdSDavid du Colombier if(NEXT(tdt, ctlr->ntd) == tdh){
10386a081dcdSDavid du Colombier td->control |= Rs;
10396a081dcdSDavid du Colombier ctlr->txdw++;
10406a081dcdSDavid du Colombier ctlr->tdt = tdt;
10416a081dcdSDavid du Colombier csr32w(ctlr, Tdt, tdt);
10426a081dcdSDavid du Colombier igbeim(ctlr, Txdw);
10436a081dcdSDavid du Colombier break;
10446a081dcdSDavid du Colombier }
10456a081dcdSDavid du Colombier ctlr->tdt = tdt;
10466a081dcdSDavid du Colombier csr32w(ctlr, Tdt, tdt);
10476a081dcdSDavid du Colombier }
10486a081dcdSDavid du Colombier
10496a081dcdSDavid du Colombier iunlock(&ctlr->tlock);
10506a081dcdSDavid du Colombier }
10516a081dcdSDavid du Colombier
10526a081dcdSDavid du Colombier static void
igbereplenish(Ctlr * ctlr)10536a081dcdSDavid du Colombier igbereplenish(Ctlr* ctlr)
10546a081dcdSDavid du Colombier {
10556a081dcdSDavid du Colombier Rd *rd;
10566a081dcdSDavid du Colombier int rdt;
10576a081dcdSDavid du Colombier Block *bp;
10586a081dcdSDavid du Colombier
10596a081dcdSDavid du Colombier rdt = ctlr->rdt;
10606a081dcdSDavid du Colombier while(NEXT(rdt, ctlr->nrd) != ctlr->rdh){
10616a081dcdSDavid du Colombier rd = &ctlr->rdba[rdt];
10626a081dcdSDavid du Colombier if(ctlr->rb[rdt] == nil){
10636a081dcdSDavid du Colombier bp = igberballoc();
10646a081dcdSDavid du Colombier if(bp == nil){
1065208510e1SDavid du Colombier iprint("#l%d: igbereplenish: no available buffers\n",
1066208510e1SDavid du Colombier ctlr->edev->ctlrno);
10676a081dcdSDavid du Colombier break;
10686a081dcdSDavid du Colombier }
10696a081dcdSDavid du Colombier ctlr->rb[rdt] = bp;
10706a081dcdSDavid du Colombier rd->addr[0] = PCIWADDR(bp->rp);
10716a081dcdSDavid du Colombier rd->addr[1] = 0;
10726a081dcdSDavid du Colombier }
10736a081dcdSDavid du Colombier coherence();
10746a081dcdSDavid du Colombier rd->status = 0;
10756a081dcdSDavid du Colombier rdt = NEXT(rdt, ctlr->nrd);
10766a081dcdSDavid du Colombier ctlr->rdfree++;
10776a081dcdSDavid du Colombier }
10786a081dcdSDavid du Colombier ctlr->rdt = rdt;
10796a081dcdSDavid du Colombier csr32w(ctlr, Rdt, rdt);
10806a081dcdSDavid du Colombier }
10816a081dcdSDavid du Colombier
10826a081dcdSDavid du Colombier static void
igberxinit(Ctlr * ctlr)10836a081dcdSDavid du Colombier igberxinit(Ctlr* ctlr)
10846a081dcdSDavid du Colombier {
10856a081dcdSDavid du Colombier int i;
10866a081dcdSDavid du Colombier Block *bp;
10876a081dcdSDavid du Colombier
1088b1707c5dSDavid du Colombier /* temporarily keep Mpe on */
1089b1707c5dSDavid du Colombier csr32w(ctlr, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF|Mpe);
10906a081dcdSDavid du Colombier
10916a081dcdSDavid du Colombier csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba));
10926a081dcdSDavid du Colombier csr32w(ctlr, Rdbah, 0);
10936a081dcdSDavid du Colombier csr32w(ctlr, Rdlen, ctlr->nrd*sizeof(Rd));
10946a081dcdSDavid du Colombier ctlr->rdh = 0;
10956a081dcdSDavid du Colombier csr32w(ctlr, Rdh, 0);
10966a081dcdSDavid du Colombier ctlr->rdt = 0;
10976a081dcdSDavid du Colombier csr32w(ctlr, Rdt, 0);
10986a081dcdSDavid du Colombier ctlr->rdtr = 0;
10996a081dcdSDavid du Colombier csr32w(ctlr, Rdtr, Fpd|0);
11006a081dcdSDavid du Colombier
11016a081dcdSDavid du Colombier for(i = 0; i < ctlr->nrd; i++){
11026a081dcdSDavid du Colombier if((bp = ctlr->rb[i]) != nil){
11036a081dcdSDavid du Colombier ctlr->rb[i] = nil;
11046a081dcdSDavid du Colombier freeb(bp);
11056a081dcdSDavid du Colombier }
11066a081dcdSDavid du Colombier }
11076a081dcdSDavid du Colombier igbereplenish(ctlr);
1108217e9e83SDavid du Colombier nrbfull = 0;
11096a081dcdSDavid du Colombier
11106a081dcdSDavid du Colombier switch(ctlr->id){
1111aeb1c8a5SDavid du Colombier case i82540em:
1112aeb1c8a5SDavid du Colombier case i82540eplp:
1113aeb1c8a5SDavid du Colombier case i82541gi:
11148466d066SDavid du Colombier case i82541gi2:
1115d717568cSDavid du Colombier case i82541pi:
1116c079eed3SDavid du Colombier case i82545em:
1117ab3dc52fSDavid du Colombier case i82545gmc:
1118aeb1c8a5SDavid du Colombier case i82546gb:
11195b7d6169SDavid du Colombier case i82546eb:
1120aeb1c8a5SDavid du Colombier case i82547gi:
11216a081dcdSDavid du Colombier csr32w(ctlr, Radv, 64);
11226a081dcdSDavid du Colombier break;
11236a081dcdSDavid du Colombier }
11246a081dcdSDavid du Colombier csr32w(ctlr, Rxdctl, (8<<WthreshSHIFT)|(8<<HthreshSHIFT)|4);
11256a081dcdSDavid du Colombier
11266a081dcdSDavid du Colombier /*
11270591a7c1SDavid du Colombier * Disable checksum offload as it has known bugs.
11286a081dcdSDavid du Colombier */
11290591a7c1SDavid du Colombier csr32w(ctlr, Rxcsum, ETHERHDRSIZE<<PcssSHIFT);
11306a081dcdSDavid du Colombier }
11316a081dcdSDavid du Colombier
11326a081dcdSDavid du Colombier static int
igberim(void * ctlr)11336a081dcdSDavid du Colombier igberim(void* ctlr)
11346a081dcdSDavid du Colombier {
11356a081dcdSDavid du Colombier return ((Ctlr*)ctlr)->rim != 0;
11366a081dcdSDavid du Colombier }
11376a081dcdSDavid du Colombier
11386a081dcdSDavid du Colombier static void
igberproc(void * arg)1139ff8c3af2SDavid du Colombier igberproc(void* arg)
11406a081dcdSDavid du Colombier {
11416a081dcdSDavid du Colombier Rd *rd;
11426a081dcdSDavid du Colombier Block *bp;
11436a081dcdSDavid du Colombier Ctlr *ctlr;
1144217e9e83SDavid du Colombier int r, rdh, passed;
11456a081dcdSDavid du Colombier Ether *edev;
11466a081dcdSDavid du Colombier
1147ff8c3af2SDavid du Colombier edev = arg;
11486a081dcdSDavid du Colombier ctlr = edev->ctlr;
11496a081dcdSDavid du Colombier
11506a081dcdSDavid du Colombier igberxinit(ctlr);
11516a081dcdSDavid du Colombier r = csr32r(ctlr, Rctl);
11526a081dcdSDavid du Colombier r |= Ren;
11536a081dcdSDavid du Colombier csr32w(ctlr, Rctl, r);
11546a081dcdSDavid du Colombier for(;;){
11556a081dcdSDavid du Colombier ctlr->rim = 0;
11566a081dcdSDavid du Colombier igbeim(ctlr, Rxt0|Rxo|Rxdmt0|Rxseq);
11576a081dcdSDavid du Colombier ctlr->rsleep++;
11586a081dcdSDavid du Colombier sleep(&ctlr->rrendez, igberim, ctlr);
11596a081dcdSDavid du Colombier
11606a081dcdSDavid du Colombier rdh = ctlr->rdh;
1161217e9e83SDavid du Colombier passed = 0;
11626a081dcdSDavid du Colombier for(;;){
11636a081dcdSDavid du Colombier rd = &ctlr->rdba[rdh];
11646a081dcdSDavid du Colombier
11656a081dcdSDavid du Colombier if(!(rd->status & Rdd))
11666a081dcdSDavid du Colombier break;
11676a081dcdSDavid du Colombier
11686a081dcdSDavid du Colombier /*
11696a081dcdSDavid du Colombier * Accept eop packets with no errors.
11706a081dcdSDavid du Colombier * With no errors and the Ixsm bit set,
11716a081dcdSDavid du Colombier * the descriptor status Tpcs and Ipcs bits give
11726a081dcdSDavid du Colombier * an indication of whether the checksums were
11736a081dcdSDavid du Colombier * calculated and valid.
11746a081dcdSDavid du Colombier */
11758ccda836SDavid du Colombier /* ignore checksum offload as it has known bugs. */
11768ccda836SDavid du Colombier rd->errors &= ~(Ipe | Tcpe);
11776a081dcdSDavid du Colombier if((rd->status & Reop) && rd->errors == 0){
11786a081dcdSDavid du Colombier bp = ctlr->rb[rdh];
11796a081dcdSDavid du Colombier ctlr->rb[rdh] = nil;
1180ff8c3af2SDavid du Colombier bp->wp += rd->length;
11816a081dcdSDavid du Colombier bp->next = nil;
11828ccda836SDavid du Colombier /* ignore checksum offload as it has known bugs. */
11838ccda836SDavid du Colombier if(0 && !(rd->status & Ixsm)){
11846a081dcdSDavid du Colombier ctlr->ixsm++;
11856a081dcdSDavid du Colombier if(rd->status & Ipcs){
11866a081dcdSDavid du Colombier /*
11876a081dcdSDavid du Colombier * IP checksum calculated
11886a081dcdSDavid du Colombier * (and valid as errors == 0).
11896a081dcdSDavid du Colombier */
11906a081dcdSDavid du Colombier ctlr->ipcs++;
11916a081dcdSDavid du Colombier bp->flag |= Bipck;
11926a081dcdSDavid du Colombier }
11936a081dcdSDavid du Colombier if(rd->status & Tcpcs){
11946a081dcdSDavid du Colombier /*
11956a081dcdSDavid du Colombier * TCP/UDP checksum calculated
11966a081dcdSDavid du Colombier * (and valid as errors == 0).
11976a081dcdSDavid du Colombier */
11986a081dcdSDavid du Colombier ctlr->tcpcs++;
11996a081dcdSDavid du Colombier bp->flag |= Btcpck|Budpck;
12006a081dcdSDavid du Colombier }
12016a081dcdSDavid du Colombier bp->checksum = rd->checksum;
12026a081dcdSDavid du Colombier bp->flag |= Bpktck;
12036a081dcdSDavid du Colombier }
1204217e9e83SDavid du Colombier ilock(&igberblock);
1205217e9e83SDavid du Colombier nrbfull++;
1206217e9e83SDavid du Colombier iunlock(&igberblock);
1207217e9e83SDavid du Colombier notemark(&ctlr->wmrb, nrbfull);
1208ff8c3af2SDavid du Colombier etheriq(edev, bp, 1);
1209217e9e83SDavid du Colombier passed++;
12106a081dcdSDavid du Colombier }
12116a081dcdSDavid du Colombier else if(ctlr->rb[rdh] != nil){
12126a081dcdSDavid du Colombier freeb(ctlr->rb[rdh]);
12136a081dcdSDavid du Colombier ctlr->rb[rdh] = nil;
12146a081dcdSDavid du Colombier }
12156a081dcdSDavid du Colombier
12166a081dcdSDavid du Colombier memset(rd, 0, sizeof(Rd));
12176a081dcdSDavid du Colombier coherence();
12186a081dcdSDavid du Colombier ctlr->rdfree--;
12196a081dcdSDavid du Colombier rdh = NEXT(rdh, ctlr->nrd);
12206a081dcdSDavid du Colombier }
12216a081dcdSDavid du Colombier ctlr->rdh = rdh;
12226a081dcdSDavid du Colombier
12236a081dcdSDavid du Colombier if(ctlr->rdfree < ctlr->nrd/2 || (ctlr->rim & Rxdmt0))
12246a081dcdSDavid du Colombier igbereplenish(ctlr);
1225217e9e83SDavid du Colombier /* note how many rds had full buffers */
1226217e9e83SDavid du Colombier notemark(&ctlr->wmrd, passed);
12276a081dcdSDavid du Colombier }
12286a081dcdSDavid du Colombier }
12296a081dcdSDavid du Colombier
12306a081dcdSDavid du Colombier static void
igbeattach(Ether * edev)12316a081dcdSDavid du Colombier igbeattach(Ether* edev)
12326a081dcdSDavid du Colombier {
12336a081dcdSDavid du Colombier Block *bp;
12346a081dcdSDavid du Colombier Ctlr *ctlr;
12356a081dcdSDavid du Colombier char name[KNAMELEN];
12366a081dcdSDavid du Colombier
12376a081dcdSDavid du Colombier ctlr = edev->ctlr;
1238208510e1SDavid du Colombier ctlr->edev = edev; /* point back to Ether* */
12396a081dcdSDavid du Colombier qlock(&ctlr->alock);
1240ea58ad6fSDavid du Colombier if(ctlr->alloc != nil){ /* already allocated? */
12416a081dcdSDavid du Colombier qunlock(&ctlr->alock);
12426a081dcdSDavid du Colombier return;
12436a081dcdSDavid du Colombier }
12446a081dcdSDavid du Colombier
1245aa72973aSDavid du Colombier ctlr->tb = nil;
1246aa72973aSDavid du Colombier ctlr->rb = nil;
1247aa72973aSDavid du Colombier ctlr->alloc = nil;
1248aa72973aSDavid du Colombier ctlr->nrb = 0;
12496a081dcdSDavid du Colombier if(waserror()){
12506a081dcdSDavid du Colombier while(ctlr->nrb > 0){
12516a081dcdSDavid du Colombier bp = igberballoc();
12526a081dcdSDavid du Colombier bp->free = nil;
12536a081dcdSDavid du Colombier freeb(bp);
12546a081dcdSDavid du Colombier ctlr->nrb--;
12556a081dcdSDavid du Colombier }
12566a081dcdSDavid du Colombier free(ctlr->tb);
12576a081dcdSDavid du Colombier ctlr->tb = nil;
12586a081dcdSDavid du Colombier free(ctlr->rb);
12596a081dcdSDavid du Colombier ctlr->rb = nil;
12606a081dcdSDavid du Colombier free(ctlr->alloc);
12616a081dcdSDavid du Colombier ctlr->alloc = nil;
12626a081dcdSDavid du Colombier qunlock(&ctlr->alock);
12636a081dcdSDavid du Colombier nexterror();
12646a081dcdSDavid du Colombier }
12656a081dcdSDavid du Colombier
1266aa72973aSDavid du Colombier ctlr->nrd = ROUND(Nrd, 8);
1267aa72973aSDavid du Colombier ctlr->ntd = ROUND(Ntd, 8);
1268aa72973aSDavid du Colombier ctlr->alloc = malloc(ctlr->nrd*sizeof(Rd)+ctlr->ntd*sizeof(Td) + 127);
1269aa72973aSDavid du Colombier if(ctlr->alloc == nil) {
1270aa72973aSDavid du Colombier print("igbe: can't allocate ctlr->alloc\n");
1271aa72973aSDavid du Colombier error(Enomem);
1272aa72973aSDavid du Colombier }
1273aa72973aSDavid du Colombier ctlr->rdba = (Rd*)ROUNDUP((uintptr)ctlr->alloc, 128);
1274aa72973aSDavid du Colombier ctlr->tdba = (Td*)(ctlr->rdba+ctlr->nrd);
1275aa72973aSDavid du Colombier
1276aa72973aSDavid du Colombier ctlr->rb = malloc(ctlr->nrd*sizeof(Block*));
1277aa72973aSDavid du Colombier ctlr->tb = malloc(ctlr->ntd*sizeof(Block*));
1278aa72973aSDavid du Colombier if (ctlr->rb == nil || ctlr->tb == nil) {
1279aa72973aSDavid du Colombier print("igbe: can't allocate ctlr->rb or ctlr->tb\n");
1280aa72973aSDavid du Colombier error(Enomem);
1281aa72973aSDavid du Colombier }
1282aa72973aSDavid du Colombier
12836a081dcdSDavid du Colombier for(ctlr->nrb = 0; ctlr->nrb < Nrb; ctlr->nrb++){
12846a081dcdSDavid du Colombier if((bp = allocb(Rbsz)) == nil)
12856a081dcdSDavid du Colombier break;
12866a081dcdSDavid du Colombier bp->free = igberbfree;
12876a081dcdSDavid du Colombier freeb(bp);
12886a081dcdSDavid du Colombier }
1289217e9e83SDavid du Colombier initmark(&ctlr->wmrb, Nrb, "rcv bufs unprocessed");
1290217e9e83SDavid du Colombier initmark(&ctlr->wmrd, Nrd-1, "rcv descrs processed at once");
1291217e9e83SDavid du Colombier initmark(&ctlr->wmtd, Ntd-1, "xmit descr queue len");
12926a081dcdSDavid du Colombier
12936a081dcdSDavid du Colombier snprint(name, KNAMELEN, "#l%dlproc", edev->ctlrno);
12946a081dcdSDavid du Colombier kproc(name, igbelproc, edev);
12956a081dcdSDavid du Colombier
12966a081dcdSDavid du Colombier snprint(name, KNAMELEN, "#l%drproc", edev->ctlrno);
12976a081dcdSDavid du Colombier kproc(name, igberproc, edev);
12986a081dcdSDavid du Colombier
12996a081dcdSDavid du Colombier igbetxinit(ctlr);
13006a081dcdSDavid du Colombier
13016a081dcdSDavid du Colombier qunlock(&ctlr->alock);
13026a081dcdSDavid du Colombier poperror();
13036a081dcdSDavid du Colombier }
13046a081dcdSDavid du Colombier
13056a081dcdSDavid du Colombier static void
igbeinterrupt(Ureg *,void * arg)13066a081dcdSDavid du Colombier igbeinterrupt(Ureg*, void* arg)
13076a081dcdSDavid du Colombier {
13086a081dcdSDavid du Colombier Ctlr *ctlr;
13096a081dcdSDavid du Colombier Ether *edev;
13106a081dcdSDavid du Colombier int icr, im, txdw;
13116a081dcdSDavid du Colombier
13126a081dcdSDavid du Colombier edev = arg;
13136a081dcdSDavid du Colombier ctlr = edev->ctlr;
13146a081dcdSDavid du Colombier
13156a081dcdSDavid du Colombier ilock(&ctlr->imlock);
13166a081dcdSDavid du Colombier csr32w(ctlr, Imc, ~0);
13176a081dcdSDavid du Colombier im = ctlr->im;
13186a081dcdSDavid du Colombier txdw = 0;
13196a081dcdSDavid du Colombier
13206a081dcdSDavid du Colombier while((icr = csr32r(ctlr, Icr) & ctlr->im) != 0){
13216a081dcdSDavid du Colombier if(icr & Lsc){
13226a081dcdSDavid du Colombier im &= ~Lsc;
13236a081dcdSDavid du Colombier ctlr->lim = icr & Lsc;
13246a081dcdSDavid du Colombier wakeup(&ctlr->lrendez);
13256a081dcdSDavid du Colombier ctlr->lintr++;
13266a081dcdSDavid du Colombier }
13276a081dcdSDavid du Colombier if(icr & (Rxt0|Rxo|Rxdmt0|Rxseq)){
13286a081dcdSDavid du Colombier im &= ~(Rxt0|Rxo|Rxdmt0|Rxseq);
13296a081dcdSDavid du Colombier ctlr->rim = icr & (Rxt0|Rxo|Rxdmt0|Rxseq);
13306a081dcdSDavid du Colombier wakeup(&ctlr->rrendez);
13316a081dcdSDavid du Colombier ctlr->rintr++;
13326a081dcdSDavid du Colombier }
13336a081dcdSDavid du Colombier if(icr & Txdw){
13346a081dcdSDavid du Colombier im &= ~Txdw;
13356a081dcdSDavid du Colombier txdw++;
13366a081dcdSDavid du Colombier ctlr->tintr++;
13376a081dcdSDavid du Colombier }
13386a081dcdSDavid du Colombier }
13396a081dcdSDavid du Colombier
13406a081dcdSDavid du Colombier ctlr->im = im;
13416a081dcdSDavid du Colombier csr32w(ctlr, Ims, im);
13426a081dcdSDavid du Colombier iunlock(&ctlr->imlock);
13436a081dcdSDavid du Colombier
13446a081dcdSDavid du Colombier if(txdw)
13456a081dcdSDavid du Colombier igbetransmit(edev);
13466a081dcdSDavid du Colombier }
13476a081dcdSDavid du Colombier
13486a081dcdSDavid du Colombier static int
i82543mdior(Ctlr * ctlr,int n)13496a081dcdSDavid du Colombier i82543mdior(Ctlr* ctlr, int n)
13506a081dcdSDavid du Colombier {
13516a081dcdSDavid du Colombier int ctrl, data, i, r;
13526a081dcdSDavid du Colombier
13536a081dcdSDavid du Colombier /*
13546a081dcdSDavid du Colombier * Read n bits from the Management Data I/O Interface.
13556a081dcdSDavid du Colombier */
13566a081dcdSDavid du Colombier ctrl = csr32r(ctlr, Ctrl);
13576a081dcdSDavid du Colombier r = (ctrl & ~Mddo)|Mdco;
13586a081dcdSDavid du Colombier data = 0;
13596a081dcdSDavid du Colombier for(i = n-1; i >= 0; i--){
13606a081dcdSDavid du Colombier if(csr32r(ctlr, Ctrl) & Mdd)
13616a081dcdSDavid du Colombier data |= (1<<i);
13626a081dcdSDavid du Colombier csr32w(ctlr, Ctrl, Mdc|r);
13636a081dcdSDavid du Colombier csr32w(ctlr, Ctrl, r);
13646a081dcdSDavid du Colombier }
13656a081dcdSDavid du Colombier csr32w(ctlr, Ctrl, ctrl);
13666a081dcdSDavid du Colombier
13676a081dcdSDavid du Colombier return data;
13686a081dcdSDavid du Colombier }
13696a081dcdSDavid du Colombier
13706a081dcdSDavid du Colombier static int
i82543mdiow(Ctlr * ctlr,int bits,int n)13716a081dcdSDavid du Colombier i82543mdiow(Ctlr* ctlr, int bits, int n)
13726a081dcdSDavid du Colombier {
13736a081dcdSDavid du Colombier int ctrl, i, r;
13746a081dcdSDavid du Colombier
13756a081dcdSDavid du Colombier /*
13766a081dcdSDavid du Colombier * Write n bits to the Management Data I/O Interface.
13776a081dcdSDavid du Colombier */
13786a081dcdSDavid du Colombier ctrl = csr32r(ctlr, Ctrl);
13796a081dcdSDavid du Colombier r = Mdco|Mddo|ctrl;
13806a081dcdSDavid du Colombier for(i = n-1; i >= 0; i--){
13816a081dcdSDavid du Colombier if(bits & (1<<i))
13826a081dcdSDavid du Colombier r |= Mdd;
13836a081dcdSDavid du Colombier else
13846a081dcdSDavid du Colombier r &= ~Mdd;
13856a081dcdSDavid du Colombier csr32w(ctlr, Ctrl, Mdc|r);
13866a081dcdSDavid du Colombier csr32w(ctlr, Ctrl, r);
13876a081dcdSDavid du Colombier }
13886a081dcdSDavid du Colombier csr32w(ctlr, Ctrl, ctrl);
13896a081dcdSDavid du Colombier
13906a081dcdSDavid du Colombier return 0;
13916a081dcdSDavid du Colombier }
13926a081dcdSDavid du Colombier
13936a081dcdSDavid du Colombier static int
i82543miimir(Mii * mii,int pa,int ra)13946a081dcdSDavid du Colombier i82543miimir(Mii* mii, int pa, int ra)
13956a081dcdSDavid du Colombier {
13966a081dcdSDavid du Colombier int data;
13976a081dcdSDavid du Colombier Ctlr *ctlr;
13986a081dcdSDavid du Colombier
13996a081dcdSDavid du Colombier ctlr = mii->ctlr;
14006a081dcdSDavid du Colombier
14016a081dcdSDavid du Colombier /*
14026a081dcdSDavid du Colombier * MII Management Interface Read.
14036a081dcdSDavid du Colombier *
14046a081dcdSDavid du Colombier * Preamble;
14056a081dcdSDavid du Colombier * ST+OP+PHYAD+REGAD;
14066a081dcdSDavid du Colombier * TA + 16 data bits.
14076a081dcdSDavid du Colombier */
14086a081dcdSDavid du Colombier i82543mdiow(ctlr, 0xFFFFFFFF, 32);
14096a081dcdSDavid du Colombier i82543mdiow(ctlr, 0x1800|(pa<<5)|ra, 14);
14106a081dcdSDavid du Colombier data = i82543mdior(ctlr, 18);
14116a081dcdSDavid du Colombier
14126a081dcdSDavid du Colombier if(data & 0x10000)
14136a081dcdSDavid du Colombier return -1;
14146a081dcdSDavid du Colombier
14156a081dcdSDavid du Colombier return data & 0xFFFF;
14166a081dcdSDavid du Colombier }
14176a081dcdSDavid du Colombier
14186a081dcdSDavid du Colombier static int
i82543miimiw(Mii * mii,int pa,int ra,int data)14196a081dcdSDavid du Colombier i82543miimiw(Mii* mii, int pa, int ra, int data)
14206a081dcdSDavid du Colombier {
14216a081dcdSDavid du Colombier Ctlr *ctlr;
14226a081dcdSDavid du Colombier
14236a081dcdSDavid du Colombier ctlr = mii->ctlr;
14246a081dcdSDavid du Colombier
14256a081dcdSDavid du Colombier /*
14266a081dcdSDavid du Colombier * MII Management Interface Write.
14276a081dcdSDavid du Colombier *
14286a081dcdSDavid du Colombier * Preamble;
14296a081dcdSDavid du Colombier * ST+OP+PHYAD+REGAD+TA + 16 data bits;
14306a081dcdSDavid du Colombier * Z.
14316a081dcdSDavid du Colombier */
14326a081dcdSDavid du Colombier i82543mdiow(ctlr, 0xFFFFFFFF, 32);
14336a081dcdSDavid du Colombier data &= 0xFFFF;
14346a081dcdSDavid du Colombier data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16);
14356a081dcdSDavid du Colombier i82543mdiow(ctlr, data, 32);
14366a081dcdSDavid du Colombier
14376a081dcdSDavid du Colombier return 0;
14386a081dcdSDavid du Colombier }
14396a081dcdSDavid du Colombier
14406a081dcdSDavid du Colombier static int
igbemiimir(Mii * mii,int pa,int ra)14416a081dcdSDavid du Colombier igbemiimir(Mii* mii, int pa, int ra)
14426a081dcdSDavid du Colombier {
14436a081dcdSDavid du Colombier Ctlr *ctlr;
14446a081dcdSDavid du Colombier int mdic, timo;
14456a081dcdSDavid du Colombier
14466a081dcdSDavid du Colombier ctlr = mii->ctlr;
14476a081dcdSDavid du Colombier
14486a081dcdSDavid du Colombier csr32w(ctlr, Mdic, MDIrop|(pa<<MDIpSHIFT)|(ra<<MDIrSHIFT));
14496a081dcdSDavid du Colombier mdic = 0;
14506a081dcdSDavid du Colombier for(timo = 64; timo; timo--){
14516a081dcdSDavid du Colombier mdic = csr32r(ctlr, Mdic);
14526a081dcdSDavid du Colombier if(mdic & (MDIe|MDIready))
14536a081dcdSDavid du Colombier break;
14546a081dcdSDavid du Colombier microdelay(1);
14556a081dcdSDavid du Colombier }
14566a081dcdSDavid du Colombier
14576a081dcdSDavid du Colombier if((mdic & (MDIe|MDIready)) == MDIready)
14586a081dcdSDavid du Colombier return mdic & 0xFFFF;
14596a081dcdSDavid du Colombier return -1;
14606a081dcdSDavid du Colombier }
14616a081dcdSDavid du Colombier
14626a081dcdSDavid du Colombier static int
igbemiimiw(Mii * mii,int pa,int ra,int data)14636a081dcdSDavid du Colombier igbemiimiw(Mii* mii, int pa, int ra, int data)
14646a081dcdSDavid du Colombier {
14656a081dcdSDavid du Colombier Ctlr *ctlr;
14666a081dcdSDavid du Colombier int mdic, timo;
14676a081dcdSDavid du Colombier
14686a081dcdSDavid du Colombier ctlr = mii->ctlr;
14696a081dcdSDavid du Colombier
14706a081dcdSDavid du Colombier data &= MDIdMASK;
14716a081dcdSDavid du Colombier csr32w(ctlr, Mdic, MDIwop|(pa<<MDIpSHIFT)|(ra<<MDIrSHIFT)|data);
14726a081dcdSDavid du Colombier mdic = 0;
14736a081dcdSDavid du Colombier for(timo = 64; timo; timo--){
14746a081dcdSDavid du Colombier mdic = csr32r(ctlr, Mdic);
14756a081dcdSDavid du Colombier if(mdic & (MDIe|MDIready))
14766a081dcdSDavid du Colombier break;
14776a081dcdSDavid du Colombier microdelay(1);
14786a081dcdSDavid du Colombier }
14796a081dcdSDavid du Colombier if((mdic & (MDIe|MDIready)) == MDIready)
14806a081dcdSDavid du Colombier return 0;
14816a081dcdSDavid du Colombier return -1;
14826a081dcdSDavid du Colombier }
14836a081dcdSDavid du Colombier
14846a081dcdSDavid du Colombier static int
igbemii(Ctlr * ctlr)14856a081dcdSDavid du Colombier igbemii(Ctlr* ctlr)
14866a081dcdSDavid du Colombier {
14876a081dcdSDavid du Colombier MiiPhy *phy;
14886a081dcdSDavid du Colombier int ctrl, p, r;
14896a081dcdSDavid du Colombier
14906a081dcdSDavid du Colombier r = csr32r(ctlr, Status);
14916a081dcdSDavid du Colombier if(r & Tbimode)
14926a081dcdSDavid du Colombier return -1;
14936a081dcdSDavid du Colombier if((ctlr->mii = malloc(sizeof(Mii))) == nil)
14946a081dcdSDavid du Colombier return -1;
14956a081dcdSDavid du Colombier ctlr->mii->ctlr = ctlr;
14966a081dcdSDavid du Colombier
14976a081dcdSDavid du Colombier ctrl = csr32r(ctlr, Ctrl);
14986a081dcdSDavid du Colombier ctrl |= Slu;
14996a081dcdSDavid du Colombier
15006a081dcdSDavid du Colombier switch(ctlr->id){
1501aeb1c8a5SDavid du Colombier case i82543gc:
15026a081dcdSDavid du Colombier ctrl |= Frcdplx|Frcspd;
15036a081dcdSDavid du Colombier csr32w(ctlr, Ctrl, ctrl);
15046a081dcdSDavid du Colombier
15056a081dcdSDavid du Colombier /*
15066a081dcdSDavid du Colombier * The reset pin direction (Mdro) should already
15076a081dcdSDavid du Colombier * be set from the EEPROM load.
15086a081dcdSDavid du Colombier * If it's not set this configuration is unexpected
15096a081dcdSDavid du Colombier * so bail.
15106a081dcdSDavid du Colombier */
15116a081dcdSDavid du Colombier r = csr32r(ctlr, Ctrlext);
15120591a7c1SDavid du Colombier if(!(r & Mdro)) {
15130591a7c1SDavid du Colombier print("igbe: 82543gc Mdro not set\n");
15146a081dcdSDavid du Colombier return -1;
15150591a7c1SDavid du Colombier }
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 r = csr32r(ctlr, Ctrlext);
15236a081dcdSDavid du Colombier r |= Mdr;
15246a081dcdSDavid du Colombier csr32w(ctlr, Ctrlext, r);
15256a081dcdSDavid du Colombier delay(20);
15266a081dcdSDavid du Colombier
15276a081dcdSDavid du Colombier ctlr->mii->mir = i82543miimir;
15286a081dcdSDavid du Colombier ctlr->mii->miw = i82543miimiw;
15296a081dcdSDavid du Colombier break;
1530aeb1c8a5SDavid du Colombier case i82544ei:
153107c32395SDavid du Colombier case i82544eif:
15329a8968c3SDavid du Colombier case i82544gc:
1533aeb1c8a5SDavid du Colombier case i82540em:
1534aeb1c8a5SDavid du Colombier case i82540eplp:
15355efba406SDavid du Colombier case i82547ei:
1536aeb1c8a5SDavid du Colombier case i82547gi:
15375efba406SDavid du Colombier case i82541ei:
1538aeb1c8a5SDavid du Colombier case i82541gi:
15398466d066SDavid du Colombier case i82541gi2:
1540d717568cSDavid du Colombier case i82541pi:
1541c079eed3SDavid du Colombier case i82545em:
1542ab3dc52fSDavid du Colombier case i82545gmc:
1543aeb1c8a5SDavid du Colombier case i82546gb:
15445b7d6169SDavid du Colombier case i82546eb:
15456a081dcdSDavid du Colombier ctrl &= ~(Frcdplx|Frcspd);
15466a081dcdSDavid du Colombier csr32w(ctlr, Ctrl, ctrl);
15476a081dcdSDavid du Colombier ctlr->mii->mir = igbemiimir;
15486a081dcdSDavid du Colombier ctlr->mii->miw = igbemiimiw;
15496a081dcdSDavid du Colombier break;
15506a081dcdSDavid du Colombier default:
15516a081dcdSDavid du Colombier free(ctlr->mii);
15526a081dcdSDavid du Colombier ctlr->mii = nil;
15536a081dcdSDavid du Colombier return -1;
15546a081dcdSDavid du Colombier }
15556a081dcdSDavid du Colombier
15566a081dcdSDavid du Colombier if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){
15576a081dcdSDavid du Colombier free(ctlr->mii);
15586a081dcdSDavid du Colombier ctlr->mii = nil;
15596a081dcdSDavid du Colombier return -1;
15606a081dcdSDavid du Colombier }
15614de34a7eSDavid du Colombier USED(phy);
15624de34a7eSDavid du Colombier // print("oui %X phyno %d\n", phy->oui, phy->phyno);
15636a081dcdSDavid du Colombier
15646a081dcdSDavid du Colombier /*
15656a081dcdSDavid du Colombier * 8254X-specific PHY registers not in 802.3:
15666a081dcdSDavid du Colombier * 0x10 PHY specific control
15676a081dcdSDavid du Colombier * 0x14 extended PHY specific control
15686a081dcdSDavid du Colombier * Set appropriate values then reset the PHY to have
15696a081dcdSDavid du Colombier * changes noted.
15706a081dcdSDavid du Colombier */
1571d717568cSDavid du Colombier switch(ctlr->id){
1572d717568cSDavid du Colombier case i82547gi:
1573d717568cSDavid du Colombier case i82541gi:
15748466d066SDavid du Colombier case i82541gi2:
1575d717568cSDavid du Colombier case i82541pi:
1576c079eed3SDavid du Colombier case i82545em:
1577ab3dc52fSDavid du Colombier case i82545gmc:
1578d717568cSDavid du Colombier case i82546gb:
1579d717568cSDavid du Colombier case i82546eb:
1580d717568cSDavid du Colombier break;
1581d717568cSDavid du Colombier default:
1582aeb1c8a5SDavid du Colombier r = miimir(ctlr->mii, 16);
15836a081dcdSDavid du Colombier r |= 0x0800; /* assert CRS on Tx */
15846a081dcdSDavid du Colombier r |= 0x0060; /* auto-crossover all speeds */
15856a081dcdSDavid du Colombier r |= 0x0002; /* polarity reversal enabled */
1586aeb1c8a5SDavid du Colombier miimiw(ctlr->mii, 16, r);
15876a081dcdSDavid du Colombier
1588aeb1c8a5SDavid du Colombier r = miimir(ctlr->mii, 20);
15896a081dcdSDavid du Colombier r |= 0x0070; /* +25MHz clock */
15906a081dcdSDavid du Colombier r &= ~0x0F00;
15916a081dcdSDavid du Colombier r |= 0x0100; /* 1x downshift */
1592aeb1c8a5SDavid du Colombier miimiw(ctlr->mii, 20, r);
15936a081dcdSDavid du Colombier
15946a081dcdSDavid du Colombier miireset(ctlr->mii);
15956a081dcdSDavid du Colombier p = 0;
15966a081dcdSDavid du Colombier if(ctlr->txcw & TxcwPs)
15976a081dcdSDavid du Colombier p |= AnaP;
15986a081dcdSDavid du Colombier if(ctlr->txcw & TxcwAs)
15996a081dcdSDavid du Colombier p |= AnaAP;
16006a081dcdSDavid du Colombier miiane(ctlr->mii, ~0, p, ~0);
1601d717568cSDavid du Colombier break;
1602aeb1c8a5SDavid du Colombier }
16036a081dcdSDavid du Colombier return 0;
16046a081dcdSDavid du Colombier }
16056a081dcdSDavid du Colombier
16066a081dcdSDavid du Colombier static int
at93c46io(Ctlr * ctlr,char * op,int data)16076a081dcdSDavid du Colombier at93c46io(Ctlr* ctlr, char* op, int data)
16086a081dcdSDavid du Colombier {
16096a081dcdSDavid du Colombier char *lp, *p;
16106a081dcdSDavid du Colombier int i, loop, eecd, r;
16116a081dcdSDavid du Colombier
16126a081dcdSDavid du Colombier eecd = csr32r(ctlr, Eecd);
16136a081dcdSDavid du Colombier
16146a081dcdSDavid du Colombier r = 0;
16156a081dcdSDavid du Colombier loop = -1;
16166a081dcdSDavid du Colombier lp = nil;
16176a081dcdSDavid du Colombier for(p = op; *p != '\0'; p++){
16186a081dcdSDavid du Colombier switch(*p){
16196a081dcdSDavid du Colombier default:
16206a081dcdSDavid du Colombier return -1;
16216a081dcdSDavid du Colombier case ' ':
16226a081dcdSDavid du Colombier continue;
16236a081dcdSDavid du Colombier case ':': /* start of loop */
16246a081dcdSDavid du Colombier loop = strtol(p+1, &lp, 0)-1;
16256a081dcdSDavid du Colombier lp--;
16266a081dcdSDavid du Colombier if(p == lp)
16276a081dcdSDavid du Colombier loop = 7;
16286a081dcdSDavid du Colombier p = lp;
16296a081dcdSDavid du Colombier continue;
16306a081dcdSDavid du Colombier case ';': /* end of loop */
16316a081dcdSDavid du Colombier if(lp == nil)
16326a081dcdSDavid du Colombier return -1;
16336a081dcdSDavid du Colombier loop--;
16346a081dcdSDavid du Colombier if(loop >= 0)
16356a081dcdSDavid du Colombier p = lp;
16366a081dcdSDavid du Colombier else
16376a081dcdSDavid du Colombier lp = nil;
16386a081dcdSDavid du Colombier continue;
16396a081dcdSDavid du Colombier case 'C': /* assert clock */
16406a081dcdSDavid du Colombier eecd |= Sk;
16416a081dcdSDavid du Colombier break;
16426a081dcdSDavid du Colombier case 'c': /* deassert clock */
16436a081dcdSDavid du Colombier eecd &= ~Sk;
16446a081dcdSDavid du Colombier break;
16456a081dcdSDavid du Colombier case 'D': /* next bit in 'data' byte */
16466a081dcdSDavid du Colombier if(loop < 0)
16476a081dcdSDavid du Colombier return -1;
16486a081dcdSDavid du Colombier if(data & (1<<loop))
16496a081dcdSDavid du Colombier eecd |= Di;
16506a081dcdSDavid du Colombier else
16516a081dcdSDavid du Colombier eecd &= ~Di;
16526a081dcdSDavid du Colombier break;
16536a081dcdSDavid du Colombier case 'O': /* collect data output */
16546a081dcdSDavid du Colombier i = (csr32r(ctlr, Eecd) & Do) != 0;
16556a081dcdSDavid du Colombier if(loop >= 0)
16566a081dcdSDavid du Colombier r |= (i<<loop);
16576a081dcdSDavid du Colombier else
16586a081dcdSDavid du Colombier r = i;
16596a081dcdSDavid du Colombier continue;
16606a081dcdSDavid du Colombier case 'I': /* assert data input */
16616a081dcdSDavid du Colombier eecd |= Di;
16626a081dcdSDavid du Colombier break;
16636a081dcdSDavid du Colombier case 'i': /* deassert data input */
16646a081dcdSDavid du Colombier eecd &= ~Di;
16656a081dcdSDavid du Colombier break;
16666a081dcdSDavid du Colombier case 'S': /* enable chip select */
16676a081dcdSDavid du Colombier eecd |= Cs;
16686a081dcdSDavid du Colombier break;
16696a081dcdSDavid du Colombier case 's': /* disable chip select */
16706a081dcdSDavid du Colombier eecd &= ~Cs;
16716a081dcdSDavid du Colombier break;
16726a081dcdSDavid du Colombier }
16736a081dcdSDavid du Colombier csr32w(ctlr, Eecd, eecd);
16748466d066SDavid du Colombier microdelay(50);
16756a081dcdSDavid du Colombier }
16766a081dcdSDavid du Colombier if(loop >= 0)
16776a081dcdSDavid du Colombier return -1;
16786a081dcdSDavid du Colombier return r;
16796a081dcdSDavid du Colombier }
16806a081dcdSDavid du Colombier
16816a081dcdSDavid du Colombier static int
at93c46r(Ctlr * ctlr)16826a081dcdSDavid du Colombier at93c46r(Ctlr* ctlr)
16836a081dcdSDavid du Colombier {
16846a081dcdSDavid du Colombier ushort sum;
16856a081dcdSDavid du Colombier char rop[20];
16866a081dcdSDavid du Colombier int addr, areq, bits, data, eecd, i;
16876a081dcdSDavid du Colombier
16886a081dcdSDavid du Colombier eecd = csr32r(ctlr, Eecd);
16896a081dcdSDavid du Colombier if(eecd & Spi){
16906a081dcdSDavid du Colombier print("igbe: SPI EEPROM access not implemented\n");
16916a081dcdSDavid du Colombier return 0;
16926a081dcdSDavid du Colombier }
16938466d066SDavid du Colombier if(eecd & (Eeszaddr|Eesz256))
16946a081dcdSDavid du Colombier bits = 8;
16956a081dcdSDavid du Colombier else
16966a081dcdSDavid du Colombier bits = 6;
16976a081dcdSDavid du Colombier
16986a081dcdSDavid du Colombier sum = 0;
16996a081dcdSDavid du Colombier
17006a081dcdSDavid du Colombier switch(ctlr->id){
17016a081dcdSDavid du Colombier default:
17026a081dcdSDavid du Colombier areq = 0;
17036a081dcdSDavid du Colombier break;
1704aeb1c8a5SDavid du Colombier case i82540em:
1705aeb1c8a5SDavid du Colombier case i82540eplp:
17065efba406SDavid du Colombier case i82541ei:
17075efba406SDavid du Colombier case i82541gi:
170826ad7229SDavid du Colombier case i82541gi2:
17095efba406SDavid du Colombier case i82541pi:
1710c079eed3SDavid du Colombier case i82545em:
1711ab3dc52fSDavid du Colombier case i82545gmc:
1712aeb1c8a5SDavid du Colombier case i82546gb:
17135b7d6169SDavid du Colombier case i82546eb:
17147e254d1cSDavid du Colombier case i82547ei:
17155efba406SDavid du Colombier case i82547gi:
17166a081dcdSDavid du Colombier areq = 1;
17176a081dcdSDavid du Colombier csr32w(ctlr, Eecd, eecd|Areq);
17186a081dcdSDavid du Colombier for(i = 0; i < 1000; i++){
17196a081dcdSDavid du Colombier if((eecd = csr32r(ctlr, Eecd)) & Agnt)
17206a081dcdSDavid du Colombier break;
17216a081dcdSDavid du Colombier microdelay(5);
17226a081dcdSDavid du Colombier }
17236a081dcdSDavid du Colombier if(!(eecd & Agnt)){
17246a081dcdSDavid du Colombier print("igbe: not granted EEPROM access\n");
17256a081dcdSDavid du Colombier goto release;
17266a081dcdSDavid du Colombier }
17276a081dcdSDavid du Colombier break;
17286a081dcdSDavid du Colombier }
17298466d066SDavid du Colombier snprint(rop, sizeof(rop), "S :%dDCc;", bits+3);
17306a081dcdSDavid du Colombier
17316a081dcdSDavid du Colombier for(addr = 0; addr < 0x40; addr++){
17326a081dcdSDavid du Colombier /*
17336a081dcdSDavid du Colombier * Read a word at address 'addr' from the Atmel AT93C46
17346a081dcdSDavid du Colombier * 3-Wire Serial EEPROM or compatible. The EEPROM access is
17356a081dcdSDavid du Colombier * controlled by 4 bits in Eecd. See the AT93C46 datasheet
17366a081dcdSDavid du Colombier * for protocol details.
17376a081dcdSDavid du Colombier */
17386a081dcdSDavid du Colombier if(at93c46io(ctlr, rop, (0x06<<bits)|addr) != 0){
17396a081dcdSDavid du Colombier print("igbe: can't set EEPROM address 0x%2.2X\n", addr);
17406a081dcdSDavid du Colombier goto release;
17416a081dcdSDavid du Colombier }
17426a081dcdSDavid du Colombier data = at93c46io(ctlr, ":16COc;", 0);
17436a081dcdSDavid du Colombier at93c46io(ctlr, "sic", 0);
17446a081dcdSDavid du Colombier ctlr->eeprom[addr] = data;
17456a081dcdSDavid du Colombier sum += data;
17466a081dcdSDavid du Colombier }
17476a081dcdSDavid du Colombier
17486a081dcdSDavid du Colombier release:
17496a081dcdSDavid du Colombier if(areq)
17506a081dcdSDavid du Colombier csr32w(ctlr, Eecd, eecd & ~Areq);
17516a081dcdSDavid du Colombier return sum;
17526a081dcdSDavid du Colombier }
17536a081dcdSDavid du Colombier
17545d9682faSDavid du Colombier static int
igbedetach(Ctlr * ctlr)17556a081dcdSDavid du Colombier igbedetach(Ctlr* ctlr)
17566a081dcdSDavid du Colombier {
17575d9682faSDavid du Colombier int r, timeo;
17586a081dcdSDavid du Colombier
17596a081dcdSDavid du Colombier /*
17606a081dcdSDavid du Colombier * Perform a device reset to get the chip back to the
17616a081dcdSDavid du Colombier * power-on state, followed by an EEPROM reset to read
17626a081dcdSDavid du Colombier * the defaults for some internal registers.
17636a081dcdSDavid du Colombier */
17646a081dcdSDavid du Colombier csr32w(ctlr, Imc, ~0);
17656a081dcdSDavid du Colombier csr32w(ctlr, Rctl, 0);
17666a081dcdSDavid du Colombier csr32w(ctlr, Tctl, 0);
17676a081dcdSDavid du Colombier
1768ff8c3af2SDavid du Colombier delay(10);
17696a081dcdSDavid du Colombier
17706a081dcdSDavid du Colombier csr32w(ctlr, Ctrl, Devrst);
17715d9682faSDavid du Colombier delay(1);
17725d9682faSDavid du Colombier for(timeo = 0; timeo < 1000; timeo++){
17735d9682faSDavid du Colombier if(!(csr32r(ctlr, Ctrl) & Devrst))
17745d9682faSDavid du Colombier break;
17755d9682faSDavid du Colombier delay(1);
17765d9682faSDavid du Colombier }
17775d9682faSDavid du Colombier if(csr32r(ctlr, Ctrl) & Devrst)
17785d9682faSDavid du Colombier return -1;
1779aeb1c8a5SDavid du Colombier r = csr32r(ctlr, Ctrlext);
1780aeb1c8a5SDavid du Colombier csr32w(ctlr, Ctrlext, r|Eerst);
17815d9682faSDavid du Colombier delay(1);
17825d9682faSDavid du Colombier for(timeo = 0; timeo < 1000; timeo++){
17835d9682faSDavid du Colombier if(!(csr32r(ctlr, Ctrlext) & Eerst))
17845d9682faSDavid du Colombier break;
17855d9682faSDavid du Colombier delay(1);
17865d9682faSDavid du Colombier }
17875d9682faSDavid du Colombier if(csr32r(ctlr, Ctrlext) & Eerst)
17885d9682faSDavid du Colombier return -1;
17896a081dcdSDavid du Colombier
17906a081dcdSDavid du Colombier switch(ctlr->id){
17916a081dcdSDavid du Colombier default:
17926a081dcdSDavid du Colombier break;
1793aeb1c8a5SDavid du Colombier case i82540em:
1794aeb1c8a5SDavid du Colombier case i82540eplp:
1795aeb1c8a5SDavid du Colombier case i82541gi:
179626ad7229SDavid du Colombier case i82541gi2:
17975efba406SDavid du Colombier case i82541pi:
1798c079eed3SDavid du Colombier case i82545em:
1799ab3dc52fSDavid du Colombier case i82545gmc:
18005efba406SDavid du Colombier case i82547gi:
1801aeb1c8a5SDavid du Colombier case i82546gb:
18025b7d6169SDavid du Colombier case i82546eb:
18036a081dcdSDavid du Colombier r = csr32r(ctlr, Manc);
18046a081dcdSDavid du Colombier r &= ~Arpen;
18056a081dcdSDavid du Colombier csr32w(ctlr, Manc, r);
18066a081dcdSDavid du Colombier break;
18076a081dcdSDavid du Colombier }
18086a081dcdSDavid du Colombier
18096a081dcdSDavid du Colombier csr32w(ctlr, Imc, ~0);
18105d9682faSDavid du Colombier delay(1);
18115d9682faSDavid du Colombier for(timeo = 0; timeo < 1000; timeo++){
18125d9682faSDavid du Colombier if(!csr32r(ctlr, Icr))
18135d9682faSDavid du Colombier break;
18145d9682faSDavid du Colombier delay(1);
18155d9682faSDavid du Colombier }
18165d9682faSDavid du Colombier if(csr32r(ctlr, Icr))
18175d9682faSDavid du Colombier return -1;
18185d9682faSDavid du Colombier
18195d9682faSDavid du Colombier return 0;
18206a081dcdSDavid du Colombier }
18216a081dcdSDavid du Colombier
18220809e9a7SDavid du Colombier static void
igbeshutdown(Ether * ether)18230809e9a7SDavid du Colombier igbeshutdown(Ether* ether)
18240809e9a7SDavid du Colombier {
18250809e9a7SDavid du Colombier igbedetach(ether->ctlr);
18260809e9a7SDavid du Colombier }
18270809e9a7SDavid du Colombier
1828ff8c3af2SDavid du Colombier static int
igbereset(Ctlr * ctlr)1829ff8c3af2SDavid du Colombier igbereset(Ctlr* ctlr)
18306a081dcdSDavid du Colombier {
18316a081dcdSDavid du Colombier int ctrl, i, pause, r, swdpio, txcw;
18326a081dcdSDavid du Colombier
18335d9682faSDavid du Colombier if(igbedetach(ctlr))
18345d9682faSDavid du Colombier return -1;
18356a081dcdSDavid du Colombier
18366a081dcdSDavid du Colombier /*
18376a081dcdSDavid du Colombier * Read the EEPROM, validate the checksum
18386a081dcdSDavid du Colombier * then get the device back to a power-on state.
18396a081dcdSDavid du Colombier */
18406a081dcdSDavid du Colombier if((r = at93c46r(ctlr)) != 0xBABA){
18416a081dcdSDavid du Colombier print("igbe: bad EEPROM checksum - 0x%4.4uX\n", r);
18426a081dcdSDavid du Colombier return -1;
18436a081dcdSDavid du Colombier }
18446a081dcdSDavid du Colombier
18456a081dcdSDavid du Colombier /*
18466a081dcdSDavid du Colombier * Snarf and set up the receive addresses.
18476a081dcdSDavid du Colombier * There are 16 addresses. The first should be the MAC address.
18486a081dcdSDavid du Colombier * The others are cleared and not marked valid (MS bit of Rah).
18496a081dcdSDavid du Colombier */
185041dd6b47SDavid du Colombier if ((ctlr->id == i82546gb || ctlr->id == i82546eb) &&
185141dd6b47SDavid du Colombier BUSFNO(ctlr->pcidev->tbdf) == 1)
185241dd6b47SDavid du Colombier ctlr->eeprom[Ea+2] += 0x100; /* second interface */
18537e254d1cSDavid du Colombier if(ctlr->id == i82541gi && ctlr->eeprom[Ea] == 0xFFFF)
18547e254d1cSDavid du Colombier ctlr->eeprom[Ea] = 0xD000;
18556a081dcdSDavid du Colombier for(i = Ea; i < Eaddrlen/2; i++){
18566a081dcdSDavid du Colombier ctlr->ra[2*i] = ctlr->eeprom[i];
18576a081dcdSDavid du Colombier ctlr->ra[2*i+1] = ctlr->eeprom[i]>>8;
18586a081dcdSDavid du Colombier }
18597e254d1cSDavid du Colombier /* lan id seems to vary on 82543gc; don't use it */
18607e254d1cSDavid du Colombier if (ctlr->id != i82543gc) {
18617e254d1cSDavid du Colombier r = (csr32r(ctlr, Status) & Lanid) >> 2;
18627e254d1cSDavid du Colombier ctlr->ra[5] += r; /* ea ctlr[1] = ea ctlr[0]+1 */
18637e254d1cSDavid du Colombier }
18647e254d1cSDavid du Colombier
18656a081dcdSDavid du Colombier r = (ctlr->ra[3]<<24)|(ctlr->ra[2]<<16)|(ctlr->ra[1]<<8)|ctlr->ra[0];
18666a081dcdSDavid du Colombier csr32w(ctlr, Ral, r);
18676a081dcdSDavid du Colombier r = 0x80000000|(ctlr->ra[5]<<8)|ctlr->ra[4];
18686a081dcdSDavid du Colombier csr32w(ctlr, Rah, r);
18696a081dcdSDavid du Colombier for(i = 1; i < 16; i++){
18706a081dcdSDavid du Colombier csr32w(ctlr, Ral+i*8, 0);
18716a081dcdSDavid du Colombier csr32w(ctlr, Rah+i*8, 0);
18726a081dcdSDavid du Colombier }
18736a081dcdSDavid du Colombier
18746a081dcdSDavid du Colombier /*
18756a081dcdSDavid du Colombier * Clear the Multicast Table Array.
18766a081dcdSDavid du Colombier * It's a 4096 bit vector accessed as 128 32-bit registers.
18776a081dcdSDavid du Colombier */
1878ecea9424SDavid du Colombier memset(ctlr->mta, 0, sizeof(ctlr->mta));
18796a081dcdSDavid du Colombier for(i = 0; i < 128; i++)
18806a081dcdSDavid du Colombier csr32w(ctlr, Mta+i*4, 0);
18816a081dcdSDavid du Colombier
18826a081dcdSDavid du Colombier /*
18836a081dcdSDavid du Colombier * Just in case the Eerst didn't load the defaults
1884208510e1SDavid du Colombier * (doesn't appear to fully on the 82543GC), do it manually.
18856a081dcdSDavid du Colombier */
1886208510e1SDavid du Colombier if (ctlr->id == i82543gc) {
18876a081dcdSDavid du Colombier txcw = csr32r(ctlr, Txcw);
18886a081dcdSDavid du Colombier txcw &= ~(TxcwAne|TxcwPauseMASK|TxcwFd);
18896a081dcdSDavid du Colombier ctrl = csr32r(ctlr, Ctrl);
18906a081dcdSDavid du Colombier ctrl &= ~(SwdpioloMASK|Frcspd|Ilos|Lrst|Fd);
18916a081dcdSDavid du Colombier
18926a081dcdSDavid du Colombier if(ctlr->eeprom[Icw1] & 0x0400){
18936a081dcdSDavid du Colombier ctrl |= Fd;
18946a081dcdSDavid du Colombier txcw |= TxcwFd;
18956a081dcdSDavid du Colombier }
18966a081dcdSDavid du Colombier if(ctlr->eeprom[Icw1] & 0x0200)
18976a081dcdSDavid du Colombier ctrl |= Lrst;
18986a081dcdSDavid du Colombier if(ctlr->eeprom[Icw1] & 0x0010)
18996a081dcdSDavid du Colombier ctrl |= Ilos;
19006a081dcdSDavid du Colombier if(ctlr->eeprom[Icw1] & 0x0800)
19016a081dcdSDavid du Colombier ctrl |= Frcspd;
19026a081dcdSDavid du Colombier swdpio = (ctlr->eeprom[Icw1] & 0x01E0)>>5;
19036a081dcdSDavid du Colombier ctrl |= swdpio<<SwdpioloSHIFT;
19046a081dcdSDavid du Colombier csr32w(ctlr, Ctrl, ctrl);
19056a081dcdSDavid du Colombier
19066a081dcdSDavid du Colombier ctrl = csr32r(ctlr, Ctrlext);
19076a081dcdSDavid du Colombier ctrl &= ~(Ips|SwdpiohiMASK);
19086a081dcdSDavid du Colombier swdpio = (ctlr->eeprom[Icw2] & 0x00F0)>>4;
19096a081dcdSDavid du Colombier if(ctlr->eeprom[Icw1] & 0x1000)
19106a081dcdSDavid du Colombier ctrl |= Ips;
19116a081dcdSDavid du Colombier ctrl |= swdpio<<SwdpiohiSHIFT;
19126a081dcdSDavid du Colombier csr32w(ctlr, Ctrlext, ctrl);
19136a081dcdSDavid du Colombier
19146a081dcdSDavid du Colombier if(ctlr->eeprom[Icw2] & 0x0800)
19156a081dcdSDavid du Colombier txcw |= TxcwAne;
19166a081dcdSDavid du Colombier pause = (ctlr->eeprom[Icw2] & 0x3000)>>12;
19176a081dcdSDavid du Colombier txcw |= pause<<TxcwPauseSHIFT;
19186a081dcdSDavid du Colombier switch(pause){
19196a081dcdSDavid du Colombier default:
19206a081dcdSDavid du Colombier ctlr->fcrtl = 0x00002000;
19216a081dcdSDavid du Colombier ctlr->fcrth = 0x00004000;
19226a081dcdSDavid du Colombier txcw |= TxcwAs|TxcwPs;
19236a081dcdSDavid du Colombier break;
19246a081dcdSDavid du Colombier case 0:
19256a081dcdSDavid du Colombier ctlr->fcrtl = 0x00002000;
19266a081dcdSDavid du Colombier ctlr->fcrth = 0x00004000;
19276a081dcdSDavid du Colombier break;
19286a081dcdSDavid du Colombier case 2:
19296a081dcdSDavid du Colombier ctlr->fcrtl = 0;
19306a081dcdSDavid du Colombier ctlr->fcrth = 0;
19316a081dcdSDavid du Colombier txcw |= TxcwAs;
19326a081dcdSDavid du Colombier break;
19336a081dcdSDavid du Colombier }
19346a081dcdSDavid du Colombier ctlr->txcw = txcw;
19356a081dcdSDavid du Colombier csr32w(ctlr, Txcw, txcw);
1936aeb1c8a5SDavid du Colombier }
1937aeb1c8a5SDavid du Colombier
19386a081dcdSDavid du Colombier
19396a081dcdSDavid du Colombier /*
19406a081dcdSDavid du Colombier * Flow control - values from the datasheet.
19416a081dcdSDavid du Colombier */
19426a081dcdSDavid du Colombier csr32w(ctlr, Fcal, 0x00C28001);
19436a081dcdSDavid du Colombier csr32w(ctlr, Fcah, 0x00000100);
19446a081dcdSDavid du Colombier csr32w(ctlr, Fct, 0x00008808);
19456a081dcdSDavid du Colombier csr32w(ctlr, Fcttv, 0x00000100);
19466a081dcdSDavid du Colombier
19476a081dcdSDavid du Colombier csr32w(ctlr, Fcrtl, ctlr->fcrtl);
19486a081dcdSDavid du Colombier csr32w(ctlr, Fcrth, ctlr->fcrth);
19496a081dcdSDavid du Colombier
19506a081dcdSDavid du Colombier if(!(csr32r(ctlr, Status) & Tbimode) && igbemii(ctlr) < 0)
19516a081dcdSDavid du Colombier return -1;
19526a081dcdSDavid du Colombier
19536a081dcdSDavid du Colombier return 0;
19546a081dcdSDavid du Colombier }
19556a081dcdSDavid du Colombier
19566a081dcdSDavid du Colombier static void
igbepci(void)19576a081dcdSDavid du Colombier igbepci(void)
19586a081dcdSDavid du Colombier {
19594de34a7eSDavid du Colombier int cls;
19606a081dcdSDavid du Colombier Pcidev *p;
19616a081dcdSDavid du Colombier Ctlr *ctlr;
19624de34a7eSDavid du Colombier void *mem;
19636a081dcdSDavid du Colombier
19646a081dcdSDavid du Colombier p = nil;
19656a081dcdSDavid du Colombier while(p = pcimatch(p, 0, 0)){
19666a081dcdSDavid du Colombier if(p->ccrb != 0x02 || p->ccru != 0)
19676a081dcdSDavid du Colombier continue;
19686a081dcdSDavid du Colombier
19696a081dcdSDavid du Colombier switch((p->did<<16)|p->vid){
19706a081dcdSDavid du Colombier default:
19716a081dcdSDavid du Colombier continue;
1972aeb1c8a5SDavid du Colombier case i82543gc:
1973aeb1c8a5SDavid du Colombier case i82544ei:
197407c32395SDavid du Colombier case i82544eif:
19759a8968c3SDavid du Colombier case i82544gc:
1976aeb1c8a5SDavid du Colombier case i82547ei:
19775efba406SDavid du Colombier case i82547gi:
1978aeb1c8a5SDavid du Colombier case i82540em:
1979aeb1c8a5SDavid du Colombier case i82540eplp:
19805efba406SDavid du Colombier case i82541ei:
1981aeb1c8a5SDavid du Colombier case i82541gi:
198226ad7229SDavid du Colombier case i82541gi2:
1983d717568cSDavid du Colombier case i82541pi:
1984c079eed3SDavid du Colombier case i82545em:
1985ab3dc52fSDavid du Colombier case i82545gmc:
1986aeb1c8a5SDavid du Colombier case i82546gb:
19875b7d6169SDavid du Colombier case i82546eb:
19886a081dcdSDavid du Colombier break;
19896a081dcdSDavid du Colombier }
19906a081dcdSDavid du Colombier
19914de34a7eSDavid du Colombier mem = vmap(p->mem[0].bar & ~0x0F, p->mem[0].size);
19924de34a7eSDavid du Colombier if(mem == nil){
19936a081dcdSDavid du Colombier print("igbe: can't map %8.8luX\n", p->mem[0].bar);
19946a081dcdSDavid du Colombier continue;
19956a081dcdSDavid du Colombier }
19966a081dcdSDavid du Colombier cls = pcicfgr8(p, PciCLS);
19976a081dcdSDavid du Colombier switch(cls){
19986a081dcdSDavid du Colombier default:
19990591a7c1SDavid du Colombier print("igbe: p->cls %#ux, setting to 0x10\n", p->cls);
20000591a7c1SDavid du Colombier p->cls = 0x10;
20010591a7c1SDavid du Colombier pcicfgw8(p, PciCLS, p->cls);
20026a081dcdSDavid du Colombier break;
20036a081dcdSDavid du Colombier case 0x08:
20046a081dcdSDavid du Colombier case 0x10:
20056a081dcdSDavid du Colombier break;
20066a081dcdSDavid du Colombier }
20076a081dcdSDavid du Colombier ctlr = malloc(sizeof(Ctlr));
2008aa72973aSDavid du Colombier if(ctlr == nil) {
2009aa72973aSDavid du Colombier vunmap(mem, p->mem[0].size);
2010aa72973aSDavid du Colombier error(Enomem);
2011aa72973aSDavid du Colombier }
20124de34a7eSDavid du Colombier ctlr->port = p->mem[0].bar & ~0x0F;
20136a081dcdSDavid du Colombier ctlr->pcidev = p;
20146a081dcdSDavid du Colombier ctlr->id = (p->did<<16)|p->vid;
20156a081dcdSDavid du Colombier ctlr->cls = cls*4;
20164de34a7eSDavid du Colombier ctlr->nic = mem;
20176a081dcdSDavid du Colombier
2018ff8c3af2SDavid du Colombier if(igbereset(ctlr)){
20196a081dcdSDavid du Colombier free(ctlr);
20205979f962SDavid du Colombier vunmap(mem, p->mem[0].size);
20216a081dcdSDavid du Colombier continue;
20226a081dcdSDavid du Colombier }
20236a081dcdSDavid du Colombier pcisetbme(p);
20246a081dcdSDavid du Colombier
20256a081dcdSDavid du Colombier if(igbectlrhead != nil)
20266a081dcdSDavid du Colombier igbectlrtail->next = ctlr;
20276a081dcdSDavid du Colombier else
20286a081dcdSDavid du Colombier igbectlrhead = ctlr;
20296a081dcdSDavid du Colombier igbectlrtail = ctlr;
20306a081dcdSDavid du Colombier }
20316a081dcdSDavid du Colombier }
20326a081dcdSDavid du Colombier
20336a081dcdSDavid du Colombier static int
igbepnp(Ether * edev)20346a081dcdSDavid du Colombier igbepnp(Ether* edev)
20356a081dcdSDavid du Colombier {
20366a081dcdSDavid du Colombier Ctlr *ctlr;
20376a081dcdSDavid du Colombier
20386a081dcdSDavid du Colombier if(igbectlrhead == nil)
20396a081dcdSDavid du Colombier igbepci();
20406a081dcdSDavid du Colombier
20416a081dcdSDavid du Colombier /*
20426a081dcdSDavid du Colombier * Any adapter matches if no edev->port is supplied,
20436a081dcdSDavid du Colombier * otherwise the ports must match.
20446a081dcdSDavid du Colombier */
20456a081dcdSDavid du Colombier for(ctlr = igbectlrhead; ctlr != nil; ctlr = ctlr->next){
20466a081dcdSDavid du Colombier if(ctlr->active)
20476a081dcdSDavid du Colombier continue;
20486a081dcdSDavid du Colombier if(edev->port == 0 || edev->port == ctlr->port){
20496a081dcdSDavid du Colombier ctlr->active = 1;
20506a081dcdSDavid du Colombier break;
20516a081dcdSDavid du Colombier }
20526a081dcdSDavid du Colombier }
20536a081dcdSDavid du Colombier if(ctlr == nil)
20546a081dcdSDavid du Colombier return -1;
20556a081dcdSDavid du Colombier
20566a081dcdSDavid du Colombier edev->ctlr = ctlr;
20576a081dcdSDavid du Colombier edev->port = ctlr->port;
20586a081dcdSDavid du Colombier edev->irq = ctlr->pcidev->intl;
20596a081dcdSDavid du Colombier edev->tbdf = ctlr->pcidev->tbdf;
20606a081dcdSDavid du Colombier edev->mbps = 1000;
20616a081dcdSDavid du Colombier memmove(edev->ea, ctlr->ra, Eaddrlen);
20626a081dcdSDavid du Colombier
20636a081dcdSDavid du Colombier /*
20646a081dcdSDavid du Colombier * Linkage to the generic ethernet driver.
20656a081dcdSDavid du Colombier */
20666a081dcdSDavid du Colombier edev->attach = igbeattach;
20676a081dcdSDavid du Colombier edev->transmit = igbetransmit;
20686a081dcdSDavid du Colombier edev->interrupt = igbeinterrupt;
20696a081dcdSDavid du Colombier edev->ifstat = igbeifstat;
20706a081dcdSDavid du Colombier edev->ctl = igbectl;
20716a081dcdSDavid du Colombier
20726a081dcdSDavid du Colombier edev->arg = edev;
20736a081dcdSDavid du Colombier edev->promiscuous = igbepromiscuous;
20740809e9a7SDavid du Colombier edev->shutdown = igbeshutdown;
2075ecea9424SDavid du Colombier edev->multicast = igbemulticast;
2076ff8c3af2SDavid du Colombier
20776a081dcdSDavid du Colombier return 0;
20786a081dcdSDavid du Colombier }
20796a081dcdSDavid du Colombier
20806a081dcdSDavid du Colombier void
etherigbelink(void)20816a081dcdSDavid du Colombier etherigbelink(void)
20826a081dcdSDavid du Colombier {
20836a081dcdSDavid du Colombier addethercard("i82543", igbepnp);
20846a081dcdSDavid du Colombier addethercard("igbe", igbepnp);
20856a081dcdSDavid du Colombier }
20868466d066SDavid du Colombier
2087