/*
* Intel 8254[340]NN Gigabit Ethernet Controller
* as found on the Intel PRO/1000 series of adapters:
* 82543GC Intel PRO/1000 T
* 82544EI Intel PRO/1000 XT
* 82540EM Intel PRO/1000 MT
* 82541[GP]I
* 82547GI
* 82546GB
* 82546EB
* To Do:
* finish autonegotiation code;
* integrate fiber stuff back in (this ONLY handles
* the CAT5 cards at the moment);
* add checksum-offload;
* add tuning control via ctl file;
* this driver is little-endian specific.
*/
#include "all.h"
#include "io.h"
#include "mem.h"
#include "../ip/ip.h"
#include "etherif.h"
#include "ethermii.h"
enum {
i82542 = (0x1000<<16)|0x8086,
i82543gc = (0x1004<<16)|0x8086,
i82544ei = (0x1008<<16)|0x8086,
i82547ei = (0x1019<<16)|0x8086,
i82540em = (0x100E<<16)|0x8086,
i82540eplp = (0x101E<<16)|0x8086,
i82545gmc = (0x1026<<16)|0x8086,
i82547gi = (0x1075<<16)|0x8086,
i82541gi = (0x1076<<16)|0x8086,
i82541gi2 = (0x1077<<16)|0x8086,
i82546gb = (0x1079<<16)|0x8086,
i82541pi = (0x107c<<16)|0x8086,
i82546eb = (0x1010<<16)|0x8086,
};
/* from pci.c */
enum
{ /* command register (pcidev->pcr) */
IOen = (1<<0),
MEMen = (1<<1),
MASen = (1<<2),
MemWrInv = (1<<4),
PErrEn = (1<<6),
SErrEn = (1<<8),
};
enum {
Ctrl = 0x00000000, /* Device Control */
Ctrldup = 0x00000004, /* Device Control Duplicate */
Status = 0x00000008, /* Device Status */
Eecd = 0x00000010, /* EEPROM/Flash Control/Data */
Ctrlext = 0x00000018, /* Extended Device Control */
Mdic = 0x00000020, /* MDI Control */
Fcal = 0x00000028, /* Flow Control Address Low */
Fcah = 0x0000002C, /* Flow Control Address High */
Fct = 0x00000030, /* Flow Control Type */
Icr = 0x000000C0, /* Interrupt Cause Read */
Ics = 0x000000C8, /* Interrupt Cause Set */
Ims = 0x000000D0, /* Interrupt Mask Set/Read */
Imc = 0x000000D8, /* Interrupt mask Clear */
Rctl = 0x00000100, /* Receive Control */
Fcttv = 0x00000170, /* Flow Control Transmit Timer Value */
Txcw = 0x00000178, /* Transmit Configuration Word */
Rxcw = 0x00000180, /* Receive Configuration Word */
Tctl = 0x00000400, /* Transmit Control */
Tipg = 0x00000410, /* Transmit IPG */
Tbt = 0x00000448, /* Transmit Burst Timer */
Ait = 0x00000458, /* Adaptive IFS Throttle */
Fcrtl = 0x00002160, /* Flow Control RX Threshold Low */
Fcrth = 0x00002168, /* Flow Control Rx Threshold High */
Rdfh = 0x00002410, /* Receive data fifo head */
Rdft = 0x00002418, /* Receive data fifo tail */
Rdfhs = 0x00002420, /* Receive data fifo head saved */
Rdfts = 0x00002428, /* Receive data fifo tail saved */
Rdfpc = 0x00002430, /* Receive data fifo packet count */
Rdbal = 0x00002800, /* Rd Base Address Low */
Rdbah = 0x00002804, /* Rd Base Address High */
Rdlen = 0x00002808, /* Receive Descriptor Length */
Rdh = 0x00002810, /* Receive Descriptor Head */
Rdt = 0x00002818, /* Receive Descriptor Tail */
Rdtr = 0x00002820, /* Receive Descriptor Timer Ring */
Rxdctl = 0x00002828, /* Receive Descriptor Control */
Radv = 0x0000282C, /* Receive Interrupt Absolute Delay Timer */
Txdmac = 0x00003000, /* Transfer DMA Control */
Ett = 0x00003008, /* Early Transmit Control */
Tdfh = 0x00003410, /* Transmit data fifo head */
Tdft = 0x00003418, /* Transmit data fifo tail */
Tdfhs = 0x00003420, /* Transmit data Fifo Head saved */
Tdfts = 0x00003428, /* Transmit data fifo tail saved */
Tdfpc = 0x00003430, /* Trasnmit data Fifo packet count */
Tdbal = 0x00003800, /* Td Base Address Low */
Tdbah = 0x00003804, /* Td Base Address High */
Tdlen = 0x00003808, /* Transmit Descriptor Length */
Tdh = 0x00003810, /* Transmit Descriptor Head */
Tdt = 0x00003818, /* Transmit Descriptor Tail */
Tidv = 0x00003820, /* Transmit Interrupt Delay Value */
Txdctl = 0x00003828, /* Transmit Descriptor Control */
Tadv = 0x0000382C, /* Transmit Interrupt Absolute Delay Timer */
Statistics = 0x00004000, /* Start of Statistics Area */
Gorcl = 0x88/4, /* Good Octets Received Count */
Gotcl = 0x90/4, /* Good Octets Transmitted Count */
Torl = 0xC0/4, /* Total Octets Received */
Totl = 0xC8/4, /* Total Octets Transmitted */
Nstatistics = 64,
Rxcsum = 0x00005000, /* Receive Checksum Control */
Mta = 0x00005200, /* Multicast Table Array */
Ral = 0x00005400, /* Receive Address Low */
Rah = 0x00005404, /* Receive Address High */
Manc = 0x00005820, /* Management Control */
};
enum { /* Ctrl */
Bem = 0x00000002, /* Big Endian Mode */
Prior = 0x00000004, /* Priority on the PCI bus */
Lrst = 0x00000008, /* Link Reset */
Asde = 0x00000020, /* Auto-Speed Detection Enable */
Slu = 0x00000040, /* Set Link Up */
Ilos = 0x00000080, /* Invert Loss of Signal (LOS) */
SspeedMASK = 0x00000300, /* Speed Selection */
SspeedSHIFT = 8,
Sspeed10 = 0x00000000, /* 10Mb/s */
Sspeed100 = 0x00000100, /* 100Mb/s */
Sspeed1000 = 0x00000200, /* 1000Mb/s */
Frcspd = 0x00000800, /* Force Speed */
Frcdplx = 0x00001000, /* Force Duplex */
SwdpinsloMASK = 0x003C0000, /* Software Defined Pins - lo nibble */
SwdpinsloSHIFT = 18,
SwdpioloMASK = 0x03C00000, /* Software Defined Pins - I or O */
SwdpioloSHIFT = 22,
Devrst = 0x04000000, /* Device Reset */
Rfce = 0x08000000, /* Receive Flow Control Enable */
Tfce = 0x10000000, /* Transmit Flow Control Enable */
Vme = 0x40000000, /* VLAN Mode Enable */
};
enum { /* Status */
Lu = 0x00000002, /* Link Up */
Tckok = 0x00000004, /* Transmit clock is running */
Rbcok = 0x00000008, /* Receive clock is running */
Txoff = 0x00000010, /* Transmission Paused */
Tbimode = 0x00000020, /* TBI Mode Indication */
LspeedMASK = 0x000000C0, /* Link Speed Setting */
LspeedSHIFT = 6,
Lspeed10 = 0x00000000, /* 10Mb/s */
Lspeed100 = 0x00000040, /* 100Mb/s */
Lspeed1000 = 0x00000080, /* 1000Mb/s */
Mtxckok = 0x00000400, /* MTX clock is running */
Pci66 = 0x00000800, /* PCI Bus speed indication */
Bus64 = 0x00001000, /* PCI Bus width indication */
Pcixmode = 0x00002000, /* PCI-X mode */
PcixspeedMASK = 0x0000C000, /* PCI-X bus speed */
PcixspeedSHIFT = 14,
Pcix66 = 0x00000000, /* 50-66MHz */
Pcix100 = 0x00004000, /* 66-100MHz */
Pcix133 = 0x00008000, /* 100-133MHz */
};
enum { /* Ctrl and Status */
Fd = 0x00000001, /* Full-Duplex */
AsdvMASK = 0x00000300,
AsdvSHIFT = 8,
Asdv10 = 0x00000000, /* 10Mb/s */
Asdv100 = 0x00000100, /* 100Mb/s */
Asdv1000 = 0x00000200, /* 1000Mb/s */
};
enum { /* Eecd */
Sk = 0x00000001, /* Clock input to the EEPROM */
Cs = 0x00000002, /* Chip Select */
Di = 0x00000004, /* Data Input to the EEPROM */
Do = 0x00000008, /* Data Output from the EEPROM */
Areq = 0x00000040, /* EEPROM Access Request */
Agnt = 0x00000080, /* EEPROM Access Grant */
Eepresent = 0x00000100, /* EEPROM Present */
Eesz256 = 0x00000200, /* EEPROM is 256 words not 64 */
Eeszaddr = 0x00000400, /* EEPROM size for 8254[17] */
Spi = 0x00002000, /* EEPROM is SPI not Microwire */
};
enum { /* Ctrlext */
Gpien = 0x0000000F, /* General Purpose Interrupt Enables */
SwdpinshiMASK = 0x000000F0, /* Software Defined Pins - hi nibble */
SwdpinshiSHIFT = 4,
SwdpiohiMASK = 0x00000F00, /* Software Defined Pins - I or O */
SwdpiohiSHIFT = 8,
Asdchk = 0x00001000, /* ASD Check */
Eerst = 0x00002000, /* EEPROM Reset */
Ips = 0x00004000, /* Invert Power State */
Spdbyps = 0x00008000, /* Speed Select Bypass */
};
enum { /* EEPROM content offsets */
Ea = 0x00, /* Ethernet Address */
Cf = 0x03, /* Compatibility Field */
Pba = 0x08, /* Printed Board Assembly number */
/* in fs kernel, Icw1 is defined in io.h; changed it here */
#define Icw1 Igbe_icw1
Icw1 = 0x0A, /* Initialization Control Word 1 */
Sid = 0x0B, /* Subsystem ID */
Svid = 0x0C, /* Subsystem Vendor ID */
Did = 0x0D, /* Device ID */
Vid = 0x0E, /* Vendor ID */
Icw2 = 0x0F, /* Initialization Control Word 2 */
};
enum { /* Mdic */
MDIdMASK = 0x0000FFFF, /* Data */
MDIdSHIFT = 0,
MDIrMASK = 0x001F0000, /* PHY Register Address */
MDIrSHIFT = 16,
MDIpMASK = 0x03E00000, /* PHY Address */
MDIpSHIFT = 21,
MDIwop = 0x04000000, /* Write Operation */
MDIrop = 0x08000000, /* Read Operation */
MDIready = 0x10000000, /* End of Transaction */
MDIie = 0x20000000, /* Interrupt Enable */
MDIe = 0x40000000, /* Error */
};
enum { /* Icr, Ics, Ims, Imc */
Txdw = 0x00000001, /* Transmit Descriptor Written Back */
Txqe = 0x00000002, /* Transmit Queue Empty */
Lsc = 0x00000004, /* Link Status Change */
Rxseq = 0x00000008, /* Receive Sequence Error */
Rxdmt0 = 0x00000010, /* Rd Minimum Threshold Reached */
Rxo = 0x00000040, /* Receiver Overrun */
Rxt0 = 0x00000080, /* Receiver Timer Interrupt */
Mdac = 0x00000200, /* MDIO Access Completed */
Rxcfg = 0x00000400, /* Receiving /C/ ordered sets */
Gpi0 = 0x00000800, /* General Purpose Interrupts */
Gpi1 = 0x00001000,
Gpi2 = 0x00002000,
Gpi3 = 0x00004000,
};
/*
* The Mdic register isn't implemented on the 82543GC,
* the software defined pins are used instead.
* These definitions work for the Intel PRO/1000 T Server Adapter.
* The direction pin bits are read from the EEPROM.
*/
enum {
Mdd = ((1<<2)<<SwdpinsloSHIFT), /* data */
Mddo = ((1<<2)<<SwdpioloSHIFT), /* pin direction */
Mdc = ((1<<3)<<SwdpinsloSHIFT), /* clock */
Mdco = ((1<<3)<<SwdpioloSHIFT), /* pin direction */
Mdr = ((1<<0)<<SwdpinshiSHIFT), /* reset */
Mdro = ((1<<0)<<SwdpiohiSHIFT), /* pin direction */
};
enum { /* Txcw */
TxcwFd = 0x00000020, /* Full Duplex */
TxcwHd = 0x00000040, /* Half Duplex */
TxcwPauseMASK = 0x00000180, /* Pause */
TxcwPauseSHIFT = 7,
TxcwPs = (1<<TxcwPauseSHIFT), /* Pause Supported */
TxcwAs = (2<<TxcwPauseSHIFT), /* Asymmetric FC desired */
TxcwRfiMASK = 0x00003000, /* Remote Fault Indication */
TxcwRfiSHIFT = 12,
TxcwNpr = 0x00008000, /* Next Page Request */
TxcwConfig = 0x40000000, /* Transmit COnfig Control */
TxcwAne = 0x80000000, /* Auto-Negotiation Enable */
};
enum { /* Rxcw */
Rxword = 0x0000FFFF, /* Data from auto-negotiation process */
Rxnocarrier = 0x04000000, /* Carrier Sense indication */
Rxinvalid = 0x08000000, /* Invalid Symbol during configuration */
Rxchange = 0x10000000, /* Change to the Rxword indication */
Rxconfig = 0x20000000, /* /C/ order set reception indication */
Rxsync = 0x40000000, /* Lost bit synchronization indication */
Anc = 0x80000000, /* Auto Negotiation Complete */
};
enum { /* Rctl */
Rrst = 0x00000001, /* Receiver Software Reset */
Ren = 0x00000002, /* Receiver Enable */
Sbp = 0x00000004, /* Store Bad Packets */
Upe = 0x00000008, /* Unicast Promiscuous Enable */
Mpe = 0x00000010, /* Multicast Promiscuous Enable */
Lpe = 0x00000020, /* Long Packet Reception Enable */
LbmMASK = 0x000000C0, /* Loopback Mode */
LbmOFF = 0x00000000, /* No Loopback */
LbmTBI = 0x00000040, /* TBI Loopback */
LbmMII = 0x00000080, /* GMII/MII Loopback */
LbmXCVR = 0x000000C0, /* Transceiver Loopback */
RdtmsMASK = 0x00000300, /* Rd Minimum Threshold Size */
RdtmsHALF = 0x00000000, /* Threshold is 1/2 Rdlen */
RdtmsQUARTER = 0x00000100, /* Threshold is 1/4 Rdlen */
RdtmsEIGHTH = 0x00000200, /* Threshold is 1/8 Rdlen */
MoMASK = 0x00003000, /* Multicast Offset */
Mo47b36 = 0x00000000, /* bits [47:36] of received address */
Mo46b35 = 0x00001000, /* bits [46:35] of received address */
Mo45b34 = 0x00002000, /* bits [45:34] of received address */
Mo43b32 = 0x00003000, /* bits [43:32] of received address */
Bam = 0x00008000, /* Broadcast Accept Mode */
BsizeMASK = 0x00030000, /* Receive Buffer Size */
Bsize2048 = 0x00000000, /* Bsex = 0 */
Bsize1024 = 0x00010000, /* Bsex = 0 */
Bsize8192 = 0x00020000, /* Bsex = 1 */
Bsize16384 = 0x00010000, /* Bsex = 1 */
BsizeFlex = 0x08000000,
Vfe = 0x00040000, /* VLAN Filter Enable */
Cfien = 0x00080000, /* Canonical Form Indicator Enable */
Cfi = 0x00100000, /* Canonical Form Indicator value */
Dpf = 0x00400000, /* Discard Pause Frames */
Pmcf = 0x00800000, /* Pass MAC Control Frames */
Bsex = 0x02000000, /* Buffer Size Extension */
Secrc = 0x04000000, /* Strip CRC from incoming packet */
};
enum { /* Tctl */
Trst = 0x00000001, /* Transmitter Software Reset */
Ten = 0x00000002, /* Transmit Enable */
Psp = 0x00000008, /* Pad Short Packets */
CtMASK = 0x00000FF0, /* Collision Threshold */
CtSHIFT = 4,
ColdMASK = 0x003FF000, /* Collision Distance */
ColdSHIFT = 12,
Swxoff = 0x00400000, /* Sofware XOFF Transmission */
Pbe = 0x00800000, /* Packet Burst Enable */
Rtlc = 0x01000000, /* Re-transmit on Late Collision */
Nrtu = 0x02000000, /* No Re-transmit on Underrrun */
};
enum { /* [RT]xdctl */
PthreshMASK = 0x0000003F, /* Prefetch Threshold */
PthreshSHIFT = 0,
HthreshMASK = 0x00003F00, /* Host Threshold */
HthreshSHIFT = 8,
WthreshMASK = 0x003F0000, /* Writeback Threshold */
WthreshSHIFT = 16,
Gran = 0x01000000, /* Granularity */
LthreshMASK = 0xFE000000, /* Low Threshold */
LthreshSHIFT = 25,
};
enum { /* Rxcsum */
PcssMASK = 0x000000FF, /* Packet Checksum Start */
PcssSHIFT = 0,
Ipofl = 0x00000100, /* IP Checksum Off-load Enable */
Tuofl = 0x00000200, /* TCP/UDP Checksum Off-load Enable */
};
enum { /* Manc */
Arpen = 0x00002000, /* Enable ARP Request Filtering */
};
enum { /* Receive Delay Timer Ring */
DelayMASK = 0x0000FFFF, /* delay timer in 1.024nS increments */
DelaySHIFT = 0,
Fpd = 0x80000000, /* Flush partial Descriptor Msgbuf */
};
typedef struct Rd { /* Receive Descriptor */
uint addr[2];
ushort length;
ushort checksum;
uchar status;
uchar errors;
ushort special;
} Rd;
enum { /* Rd status */
Rdd = 0x01, /* Descriptor Done */
Reop = 0x02, /* End of Packet */
Ixsm = 0x04, /* Ignore Checksum Indication */
Vp = 0x08, /* Packet is 802.1Q (matched VET) */
Tcpcs = 0x20, /* TCP Checksum Calculated on Packet */
Ipcs = 0x40, /* IP Checksum Calculated on Packet */
Pif = 0x80, /* Passed in-exact filter */
};
enum { /* Rd errors */
Ce = 0x01, /* CRC Error or Alignment Error */
Se = 0x02, /* Symbol Error */
Seq = 0x04, /* Sequence Error */
Cxe = 0x10, /* Carrier Extension Error */
Tcpe = 0x20, /* TCP/UDP Checksum Error */
Ipe = 0x40, /* IP Checksum Error */
Rxe = 0x80, /* RX Data Error */
};
typedef struct{ /* Transmit Descriptor */
uint addr[2]; /* Data */
uint control;
uint status;
}Td;
enum { /* Td control */
LenMASK = 0x000FFFFF, /* Data/Packet Length Field */
LenSHIFT = 0,
DtypeCD = 0x00000000, /* Data Type 'Context Descriptor' */
DtypeDD = 0x00100000, /* Data Type 'Data Descriptor' */
PtypeTCP = 0x01000000, /* TCP/UDP Packet Type (CD) */
Teop = 0x01000000, /* End of Packet (DD) */
PtypeIP = 0x02000000, /* IP Packet Type (CD) */
Ifcs = 0x02000000, /* Insert FCS (DD) */
Tse = 0x04000000, /* TCP Segmentation Enable */
Rs = 0x08000000, /* Report Status */
Rps = 0x10000000, /* Report Status Sent */
Dext = 0x20000000, /* Descriptor Extension */
Vle = 0x40000000, /* VLAN Packet Enable */
Ide = 0x80000000, /* Interrupt Delay Enable */
};
enum { /* Td status */
Tdd = 0x00000001, /* Descriptor Done */
Ec = 0x00000002, /* Excess Collisions */
Lc = 0x00000004, /* Late Collision */
Tu = 0x00000008, /* Transmit Underrun */
Iixsm = 0x00000100, /* Insert IP Checksum */
Itxsm = 0x00000200, /* Insert TCP/UDP Checksum */
HdrlenMASK = 0x0000FF00, /* Header Length (Tse) */
HdrlenSHIFT = 8,
VlanMASK = 0x0FFF0000, /* VLAN Identifier */
VlanSHIFT = 16,
Tcfi = 0x10000000, /* Canonical Form Indicator */
PriMASK = 0xE0000000, /* User Priority */
PriSHIFT = 29,
MssMASK = 0xFFFF0000, /* Maximum Segment Size (Tse) */
MssSHIFT = 16,
};
enum {
Nrd = 256, /* multiple of 8 */
/*
* we require a power of 2. also must be a multiple of TDD_INTVL (64).
* chip errata says max 256.
*/
Ntd = 256, /* chip requires multiple of 8 */
Nrb = 512, /* private receive buffers per Ctlr */
Rbsz = 9*1024+128,
Rbsize = Bsize16384|Bsex|Lpe,
};
typedef struct Ctlr Ctlr;
struct Ctlr {
int port;
Pcidev* pcidev;
Ctlr* next;
int active;
int started;
int id;
int cls;
ushort eeprom[0x40];
void* alloc; /* receive/transmit descriptors */
int nrd;
int ntd;
int nrb; /* how many this Ctlr has in the pool */
int* nic;
Lock imlock;
int im; /* interrupt mask */
Mii* mii;
Rendez lrendez;
int lim;
int link;
QLock slock;
uint lsleep;
uint lintr;
uint rsleep;
uint rintr;
uint txdw;
uint tintr;
uint ixsm;
uint ipcs;
uint tcpcs;
uchar ra[Easize]; /* receive address */
Rendez rrendez;
int rim;
int rdfree;
Rd* rdba; /* receive descriptor base address */
Msgbuf** rb; /* receive buffers */
int rdh; /* receive descriptor head */
int rdt; /* receive descriptor tail */
int rdtr; /* receive delay timer ring value */
Rendez trendez;
QLock tlock;
int sent, cleaned;
int tbusy;
int tdfree;
Td* tdba; /* transmit descriptor base address */
Msgbuf** tb; /* transmit buffers */
int tdh; /* transmit descriptor head */
int tdt; /* transmit descriptor tail */
int txcw;
int fcrtl;
int fcrth;
char dname[10];
char rname[10];
char tname[10];
};
#define csr32r(c, r) (*((c)->nic+((r)/4)))
#define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v))
static Ctlr* igbectlrhead;
static Ctlr* igbectlrtail;
static Lock igberblock; /* free receive Msgbufs */
static Msgbuf* igberbpool;
static Msgbuf*
igberballoc(void)
{
Msgbuf *m;
ilock(&igberblock);
if((m = igberbpool) != nil){
igberbpool = m->next;
m->next = nil;
}
iunlock(&igberblock);
m->flags &= ~FREE;
m->count = 0;
m->data = (uchar*)PGROUND((uintptr)m->xdata);
return m;
}
static void
igberbfree(Msgbuf *m)
{
m->flags |= FREE;
ilock(&igberblock);
m->next = igberbpool;
igberbpool = m;
iunlock(&igberblock);
}
static void
igbeim(Ctlr* ctlr, int im)
{
ilock(&ctlr->imlock);
ctlr->im |= im;
csr32w(ctlr, Ims, ctlr->im);
iunlock(&ctlr->imlock);
}
static int
igbelim(void* ctlr)
{
return ((Ctlr*)ctlr)->lim != 0;
}
static void
igbelproc(void)
{
Ctlr *ctlr;
Ether *edev;
MiiPhy *phy;
int ctrl, r;
edev = u->arg;
ctlr = edev->ctlr;
for(;;){
if(ctlr->mii == nil || ctlr->mii->curphy == nil)
continue;
/*
* To do:
* logic to manage status change,
* this is incomplete but should work
* one time to set up the hardware.
*
* MiiPhy.speed, etc. should be in Mii.
*/
if(miistatus(ctlr->mii) < 0)
//continue;
goto enable;
phy = ctlr->mii->curphy;
ctrl = csr32r(ctlr, Ctrl);
switch(ctlr->id){
case i82543gc:
case i82544ei:
default:
if(!(ctrl & Asde)){
ctrl &= ~(SspeedMASK|Ilos|Fd);
ctrl |= Frcdplx|Frcspd;
if(phy->speed == 1000)
ctrl |= Sspeed1000;
else if(phy->speed == 100)
ctrl |= Sspeed100;
if(phy->fd)
ctrl |= Fd;
}
break;
case i82540em:
case i82540eplp:
case i82547gi:
case i82541gi:
case i82541gi2:
case i82541pi:
break;
}
/*
* Collision Distance.
*/
r = csr32r(ctlr, Tctl);
r &= ~ColdMASK;
if(phy->fd)
r |= 64<<ColdSHIFT;
else
r |= 512<<ColdSHIFT;
csr32w(ctlr, Tctl, r);
/*
* Flow control.
*/
if(phy->rfc)
ctrl |= Rfce;
if(phy->tfc)
ctrl |= Tfce;
csr32w(ctlr, Ctrl, ctrl);
enable:
ctlr->lim = 0;
igbeim(ctlr, Lsc);
ctlr->lsleep++;
sleep(&ctlr->lrendez, igbelim, ctlr);
}
}
static void
igbetxinit(Ctlr* ctlr)
{
int i, r;
Msgbuf *bp;
csr32w(ctlr, Tctl, (0x0F<<CtSHIFT)|Psp|(66<<ColdSHIFT));
switch(ctlr->id){
default:
r = 6;
break;
case i82543gc:
case i82544ei:
case i82547ei:
case i82540em:
case i82540eplp:
case i82541gi:
case i82541gi2:
case i82541pi:
case i82545gmc:
case i82546gb:
case i82546eb:
case i82547gi:
r = 8;
break;
}
csr32w(ctlr, Tipg, (6<<20)|(8<<10)|r);
csr32w(ctlr, Ait, 0);
csr32w(ctlr, Txdmac, 0);
csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba));
csr32w(ctlr, Tdbah, 0);
csr32w(ctlr, Tdlen, ctlr->ntd*sizeof(Td));
ctlr->tdh = PREV(0, ctlr->ntd);
csr32w(ctlr, Tdh, 0);
ctlr->tdt = 0;
csr32w(ctlr, Tdt, 0);
for(i = 0; i < ctlr->ntd; i++){
if((bp = ctlr->tb[i]) != nil){
ctlr->tb[i] = nil;
mbfree(bp);
}
memset(&ctlr->tdba[i], 0, sizeof(Td));
}
ctlr->tdfree = ctlr->ntd;
csr32w(ctlr, Tidv, 128);
r = (4<<WthreshSHIFT)|(4<<HthreshSHIFT)|(8<<PthreshSHIFT);
switch(ctlr->id){
default:
break;
case i82540em:
case i82540eplp:
case i82547gi:
case i82545gmc:
case i82546gb:
case i82546eb:
case i82541gi:
case i82541gi2:
case i82541pi:
r = csr32r(ctlr, Txdctl);
r &= ~WthreshMASK;
r |= Gran|(4<<WthreshSHIFT);
csr32w(ctlr, Tadv, 64);
break;
}
csr32w(ctlr, Txdctl, r);
r = csr32r(ctlr, Tctl);
r |= Ten;
csr32w(ctlr, Tctl, r);
}
#define Next(x, m) (((x)+1)&(m))
igbecleanup(Ctlr *c)
{
Msgbuf *b;
int tdh, m, n;
tdh = c->tdh;
m = c->ntd-1;
while(c->tdba[n = Next(tdh, m)].status&Tdd){
tdh = n;
if((b = c->tb[tdh]) != nil){
c->tb[tdh] = nil;
mbfree(b);
}
c->tdba[tdh].status = 0;
c->cleaned++;
}
return c->tdh = tdh;
}
static void
igbetransmit(Ether* edev)
{
Td *td;
Msgbuf *b;
Ctlr *c;
int tdh, tdt, m;
c = edev->ctlr;
qlock(&c->tlock);
tdh = igbecleanup(c);
/*
* Try to fill the ring back up.
*/
tdt = c->tdt;
m = c->ntd-1;
if(Next(tdt, m) != tdh)
for(;;){
if((b = etheroq(edev)) == nil)
break;
td = &c->tdba[tdt];
td->addr[0] = PCIWADDR(b->data);
td->control = DtypeDD|Ide|Rs|Ifcs|Teop|b->count;
c->tb[tdt] = b;
c->sent++;
tdt = Next(tdt, m);
if(Next(tdt, m) == tdh){
td->control |= Rs;
c->txdw++;
c->tdt = tdt;
csr32w(c, Tdt, tdt);
igbeim(c, Txdw);
break;
}
c->tdt = tdt;
csr32w(c, Tdt, tdt);
}
qunlock(&c->tlock);
}
static void
igbereplenish(Ctlr* c)
{
Rd *rd;
int rdt, m;
Msgbuf *b;
rdt = c->rdt;
m = c->nrd-1;
while(Next(rdt, m) != c->rdh){
rd = &c->rdba[rdt];
if(c->rb[rdt] == nil){
b = igberballoc();
if(b == nil){
print("igbe: no available buffers\n");
break;
}
c->rb[rdt] = b;
rd->addr[0] = PCIWADDR(b->data);
// rd->addr[1] = 0;
}
// coherence();
rd->status = 0;
rdt = Next(rdt, m);
c->rdfree++;
}
c->rdt = rdt;
csr32w(c, Rdt, rdt);
}
static void
igberxinit(Ctlr* ctlr)
{
int i;
Msgbuf *bp;
csr32w(ctlr, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF);
csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba));
csr32w(ctlr, Rdbah, 0);
csr32w(ctlr, Rdlen, ctlr->nrd*sizeof(Rd));
ctlr->rdh = 0;
csr32w(ctlr, Rdh, 0);
ctlr->rdt = 0;
csr32w(ctlr, Rdt, 0);
ctlr->rdtr = 0;
csr32w(ctlr, Rdtr, Fpd|0);
for(i = 0; i < ctlr->nrd; i++){
if((bp = ctlr->rb[i]) != nil){
ctlr->rb[i] = nil;
mbfree(bp);
}
}
igbereplenish(ctlr);
switch(ctlr->id){
case i82540em:
case i82540eplp:
case i82541gi:
case i82541gi2:
case i82541pi:
case i82545gmc:
case i82546gb:
case i82546eb:
case i82547gi:
csr32w(ctlr, Radv, 64);
break;
}
csr32w(ctlr, Rxdctl, (8<<WthreshSHIFT)|(8<<HthreshSHIFT)|4);
/*
* Enable checksum offload.
*/
csr32w(ctlr, Rxcsum, Tuofl|Ipofl|Ensize<<PcssSHIFT);
}
static int
igberim(void* ctlr)
{
return ((Ctlr*)ctlr)->rim != 0;
}
static void
igberproc(void)
{
Rd *rd;
Msgbuf *bp;
Ctlr *ctlr;
int r, rdh;
Ether *edev;
edev = u->arg;
ctlr = edev->ctlr;
igberxinit(ctlr);
r = csr32r(ctlr, Rctl);
r |= Ren;
csr32w(ctlr, Rctl, r);
for(;;){
ctlr->rim = 0;
igbeim(ctlr, Rxt0|Rxo|Rxdmt0|Rxseq);
ctlr->rsleep++;
sleep(&ctlr->rrendez, igberim, ctlr);
rdh = ctlr->rdh;
for(;;){
rd = &ctlr->rdba[rdh];
if(!(rd->status & Rdd))
break;
/*
* Accept eop packets with no errors.
* With no errors and the Ixsm bit set,
* the descriptor status Tpcs and Ipcs bits give
* an indication of whether the checksums were
* calculated and valid.
*/
if((rd->status & Reop) && rd->errors == 0){
bp = ctlr->rb[rdh];
ctlr->rb[rdh] = nil;
bp->count += rd->length;
bp->next = nil;
if(!(rd->status & Ixsm)){
ctlr->ixsm++;
if(rd->status & Ipcs){
/*
* IP checksum calculated
* (and valid as errors == 0).
*/
ctlr->ipcs++;
// bp->flag |= Bipck;
}
if(rd->status & Tcpcs){
/*
* TCP/UDP checksum calculated
* (and valid as errors == 0).
*/
ctlr->tcpcs++;
// bp->flag |= Btcpck|Budpck;
}
// bp->checksum = rd->checksum;
// bp->flag |= Bpktck;
}
etheriq(edev, bp);
}
else if(ctlr->rb[rdh] != nil){
mbfree(ctlr->rb[rdh]);
ctlr->rb[rdh] = nil;
}
// memset(rd, 0, sizeof(Rd));
// coherence();
ctlr->rdfree--;
rdh = NEXT(rdh, ctlr->nrd);
}
ctlr->rdh = rdh;
if(ctlr->rdfree < ctlr->nrd/2 || (ctlr->rim & Rxdmt0))
igbereplenish(ctlr);
}
}
static int
txcansleep(void *v)
{
Ctlr *c;
c = v;
return NEXT(c->tdh, c->ntd) != csr32r(c, Tdh);
}
static void
igbetproc(void)
{
Ether *e;
Ctlr *c;
e = u->arg;
c = e->ctlr;
for(;;){
sleep(&c->trendez, txcansleep, c);
igbetransmit(e);
}
}
static void
igbeattach(Ether* edev)
{
Ctlr *ctlr;
ctlr = edev->ctlr;
ctlr->nrd = Nrd;
ctlr->ntd = Ntd;
ctlr->alloc = ialloc(ctlr->nrd*sizeof(Rd)+ctlr->ntd*sizeof(Td) + 127, 128);
ctlr->rdba = (Rd*)ctlr->alloc;
ctlr->tdba = (Td*)(ctlr->rdba+ctlr->nrd);
ctlr->rb = ialloc(ctlr->nrd*sizeof(Msgbuf*), 0);
ctlr->tb = ialloc(ctlr->ntd*sizeof(Msgbuf*), 0);
mballocpool(Nrb, Rbsz, BY2PG, Mbeth1, igberbfree);
snprint(ctlr->rname, sizeof ctlr->rname, "#l%dl", edev->ctlrno);
userinit(igbelproc, edev, ctlr->rname);
snprint(ctlr->dname, sizeof ctlr->dname, "#l%dd", edev->ctlrno);
userinit(igberproc, edev, ctlr->dname);
snprint(ctlr->tname, sizeof ctlr->tname, "#l%dt", edev->ctlrno);
userinit(igbetproc, edev, ctlr->tname);
igbetxinit(ctlr);
}
static void
igbeinterrupt(Ureg*, void* arg)
{
Ctlr *ctlr;
Ether *edev;
int icr, im;
edev = arg;
ctlr = edev->ctlr;
ilock(&ctlr->imlock);
csr32w(ctlr, Imc, ~0);
im = ctlr->im;
while((icr = csr32r(ctlr, Icr) & ctlr->im) != 0){
if(icr & Lsc){
im &= ~Lsc;
ctlr->lim = icr & Lsc;
wakeup(&ctlr->lrendez);
ctlr->lintr++;
}
if(icr & (Rxt0|Rxo|Rxdmt0|Rxseq)){
im &= ~(Rxt0|Rxo|Rxdmt0|Rxseq);
ctlr->rim = icr & (Rxt0|Rxo|Rxdmt0|Rxseq);
wakeup(&ctlr->rrendez);
ctlr->rintr++;
}
if(icr & Txdw){
im &= ~Txdw;
wakeup(&ctlr->trendez);
ctlr->tintr++;
}
}
ctlr->im = im;
csr32w(ctlr, Ims, im);
iunlock(&ctlr->imlock);
}
static int
i82543mdior(Ctlr* ctlr, int n)
{
int ctrl, data, i, r;
/*
* Read n bits from the Management Data I/O Interface.
*/
ctrl = csr32r(ctlr, Ctrl);
r = (ctrl & ~Mddo)|Mdco;
data = 0;
for(i = n-1; i >= 0; i--){
if(csr32r(ctlr, Ctrl) & Mdd)
data |= (1<<i);
csr32w(ctlr, Ctrl, Mdc|r);
csr32w(ctlr, Ctrl, r);
}
csr32w(ctlr, Ctrl, ctrl);
return data;
}
static int
i82543mdiow(Ctlr* ctlr, int bits, int n)
{
int ctrl, i, r;
/*
* Write n bits to the Management Data I/O Interface.
*/
ctrl = csr32r(ctlr, Ctrl);
r = Mdco|Mddo|ctrl;
for(i = n-1; i >= 0; i--){
if(bits & (1<<i))
r |= Mdd;
else
r &= ~Mdd;
csr32w(ctlr, Ctrl, Mdc|r);
csr32w(ctlr, Ctrl, r);
}
csr32w(ctlr, Ctrl, ctrl);
return 0;
}
static int
i82543miimir(Mii* mii, int pa, int ra)
{
int data;
Ctlr *ctlr;
ctlr = mii->ctlr;
/*
* MII Management Interface Read.
*
* Preamble;
* ST+OP+PHYAD+REGAD;
* TA + 16 data bits.
*/
i82543mdiow(ctlr, 0xFFFFFFFF, 32);
i82543mdiow(ctlr, 0x1800|(pa<<5)|ra, 14);
data = i82543mdior(ctlr, 18);
if(data & 0x10000)
return -1;
return data & 0xFFFF;
}
static int
i82543miimiw(Mii* mii, int pa, int ra, int data)
{
Ctlr *ctlr;
ctlr = mii->ctlr;
/*
* MII Management Interface Write.
*
* Preamble;
* ST+OP+PHYAD+REGAD+TA + 16 data bits;
* Z.
*/
i82543mdiow(ctlr, 0xFFFFFFFF, 32);
data &= 0xFFFF;
data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16);
i82543mdiow(ctlr, data, 32);
return 0;
}
static int
igbemiimir(Mii* mii, int pa, int ra)
{
Ctlr *ctlr;
int mdic, timo;
ctlr = mii->ctlr;
csr32w(ctlr, Mdic, MDIrop|(pa<<MDIpSHIFT)|(ra<<MDIrSHIFT));
mdic = 0;
for(timo = 64; timo; timo--){
mdic = csr32r(ctlr, Mdic);
if(mdic & (MDIe|MDIready))
break;
microdelay(1);
}
if((mdic & (MDIe|MDIready)) == MDIready)
return mdic & 0xFFFF;
return -1;
}
static int
igbemiimiw(Mii* mii, int pa, int ra, int data)
{
Ctlr *ctlr;
int mdic, timo;
ctlr = mii->ctlr;
data &= MDIdMASK;
csr32w(ctlr, Mdic, MDIwop|(pa<<MDIpSHIFT)|(ra<<MDIrSHIFT)|data);
mdic = 0;
for(timo = 64; timo; timo--){
mdic = csr32r(ctlr, Mdic);
if(mdic & (MDIe|MDIready))
break;
microdelay(1);
}
if((mdic & (MDIe|MDIready)) == MDIready)
return 0;
return -1;
}
static int
igbemii(Ctlr* ctlr)
{
MiiPhy *phy;
int ctrl, p, r;
r = csr32r(ctlr, Status);
if(r & Tbimode)
return -1;
if((ctlr->mii = ialloc(sizeof(Mii), 0)) == nil)
return -1;
ctlr->mii->ctlr = ctlr;
ctrl = csr32r(ctlr, Ctrl);
ctrl |= Slu;
switch(ctlr->id){
case i82543gc:
ctrl |= Frcdplx|Frcspd;
csr32w(ctlr, Ctrl, ctrl);
/*
* The reset pin direction (Mdro) should already
* be set from the EEPROM load.
* If it's not set this configuration is unexpected
* so bail.
*/
r = csr32r(ctlr, Ctrlext);
if(!(r & Mdro))
return -1;
csr32w(ctlr, Ctrlext, r);
delay(20);
r = csr32r(ctlr, Ctrlext);
r &= ~Mdr;
csr32w(ctlr, Ctrlext, r);
delay(20);
r = csr32r(ctlr, Ctrlext);
r |= Mdr;
csr32w(ctlr, Ctrlext, r);
delay(20);
ctlr->mii->mir = i82543miimir;
ctlr->mii->miw = i82543miimiw;
break;
case i82544ei:
case i82547ei:
case i82540em:
case i82540eplp:
case i82547gi:
case i82541gi:
case i82541gi2:
case i82541pi:
case i82545gmc:
case i82546gb:
case i82546eb:
ctrl &= ~(Frcdplx|Frcspd);
csr32w(ctlr, Ctrl, ctrl);
ctlr->mii->mir = igbemiimir;
ctlr->mii->miw = igbemiimiw;
break;
default:
// free(ctlr->mii);
ctlr->mii = nil;
return -1;
}
if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){
// free(ctlr->mii);
ctlr->mii = nil;
return -1;
}
USED(phy);
// print("oui %X phyno %d\n", phy->oui, phy->phyno);
/*
* 8254X-specific PHY registers not in 802.3:
* 0x10 PHY specific control
* 0x14 extended PHY specific control
* Set appropriate values then reset the PHY to have
* changes noted.
*/
switch(ctlr->id){
case i82547gi:
case i82541gi:
case i82541gi2:
case i82541pi:
case i82545gmc:
case i82546gb:
case i82546eb:
break;
default:
r = miimir(ctlr->mii, 16);
r |= 0x0800; /* assert CRS on Tx */
r |= 0x0060; /* auto-crossover all speeds */
r |= 0x0002; /* polarity reversal enabled */
miimiw(ctlr->mii, 16, r);
r = miimir(ctlr->mii, 20);
r |= 0x0070; /* +25MHz clock */
r &= ~0x0F00;
r |= 0x0100; /* 1x downshift */
miimiw(ctlr->mii, 20, r);
miireset(ctlr->mii);
p = 0;
if(ctlr->txcw & TxcwPs)
p |= AnaP;
if(ctlr->txcw & TxcwAs)
p |= AnaAP;
miiane(ctlr->mii, ~0, p, ~0);
break;
}
return 0;
}
static int
at93c46io(Ctlr* ctlr, char* op, int data)
{
char *lp, *p;
int i, loop, eecd, r;
eecd = csr32r(ctlr, Eecd);
r = 0;
loop = -1;
lp = nil;
for(p = op; *p != '\0'; p++){
switch(*p){
default:
return -1;
case ' ':
continue;
case ':': /* start of loop */
loop = strtoul(p+1, &lp, 0)-1;
lp--;
if(p == lp)
loop = 7;
p = lp;
continue;
case ';': /* end of loop */
if(lp == nil)
return -1;
loop--;
if(loop >= 0)
p = lp;
else
lp = nil;
continue;
case 'C': /* assert clock */
eecd |= Sk;
break;
case 'c': /* deassert clock */
eecd &= ~Sk;
break;
case 'D': /* next bit in 'data' byte */
if(loop < 0)
return -1;
if(data & (1<<loop))
eecd |= Di;
else
eecd &= ~Di;
break;
case 'O': /* collect data output */
i = (csr32r(ctlr, Eecd) & Do) != 0;
if(loop >= 0)
r |= (i<<loop);
else
r = i;
continue;
case 'I': /* assert data input */
eecd |= Di;
break;
case 'i': /* deassert data input */
eecd &= ~Di;
break;
case 'S': /* enable chip select */
eecd |= Cs;
break;
case 's': /* disable chip select */
eecd &= ~Cs;
break;
}
csr32w(ctlr, Eecd, eecd);
microdelay(50);
}
if(loop >= 0)
return -1;
return r;
}
static int
at93c46r(Ctlr* ctlr)
{
ushort sum;
char rop[20];
int addr, areq, bits, data, eecd, i;
eecd = csr32r(ctlr, Eecd);
if(eecd & Spi){
print("igbe: SPI EEPROM access not implemented\n");
return 0;
}
if(eecd & (Eeszaddr|Eesz256))
bits = 8;
else
bits = 6;
sum = 0;
switch(ctlr->id){
default:
areq = 0;
break;
case i82541gi:
case i82547gi:
case i82540em:
case i82540eplp:
case i82541pi:
case i82541gi2:
case i82545gmc:
case i82546gb:
case i82546eb:
areq = 1;
csr32w(ctlr, Eecd, eecd|Areq);
for(i = 0; i < 1000; i++){
if((eecd = csr32r(ctlr, Eecd)) & Agnt)
break;
microdelay(5);
}
if(!(eecd & Agnt)){
print("igbe: not granted EEPROM access\n");
goto release;
}
break;
}
snprint(rop, sizeof(rop), "S :%dDCc;", bits+3);
for(addr = 0; addr < 0x40; addr++){
/*
* Read a word at address 'addr' from the Atmel AT93C46
* 3-Wire Serial EEPROM or compatible. The EEPROM access is
* controlled by 4 bits in Eecd. See the AT93C46 datasheet
* for protocol details.
*/
if(at93c46io(ctlr, rop, (0x06<<bits)|addr) != 0){
print("igbe: can't set EEPROM address 0x%2.2X\n", addr);
goto release;
}
data = at93c46io(ctlr, ":16COc;", 0);
at93c46io(ctlr, "sic", 0);
ctlr->eeprom[addr] = data;
sum += data;
}
release:
if(areq)
csr32w(ctlr, Eecd, eecd & ~Areq);
return sum;
}
static int
igbedetach(Ctlr* ctlr)
{
int r, timeo;
if (ctlr == nil)
return -1;
/*
* Perform a device reset to get the chip back to the
* power-on state, followed by an EEPROM reset to read
* the defaults for some internal registers.
*
* The alpha needs the rest of this routine to run at splhi
* or else the card interrupts and resets the processor. Don't know
* why. Since the alpha isn't important any more, let's ignore that.
*/
csr32w(ctlr, Imc, ~0);
csr32w(ctlr, Rctl, 0);
csr32w(ctlr, Tctl, 0);
delay(10); /* was 10 then 100 */
csr32w(ctlr, Ctrl, Devrst);
delay(1); /* was 100 */
for(timeo = 0; timeo < 1000; timeo++){
if(!(csr32r(ctlr, Ctrl) & Devrst))
break;
delay(1);
}
if(csr32r(ctlr, Ctrl) & Devrst)
return -1;
r = csr32r(ctlr, Ctrlext);
csr32w(ctlr, Ctrlext, r|Eerst);
delay(1);
for(timeo = 0; timeo < 1000; timeo++){
if(!(csr32r(ctlr, Ctrlext) & Eerst))
break;
delay(1);
}
if(csr32r(ctlr, Ctrlext) & Eerst)
return -1;
switch(ctlr->id){
default:
break;
case i82540em:
case i82540eplp:
case i82541gi:
case i82541pi:
case i82547gi:
case i82541gi2:
case i82545gmc:
case i82546gb:
case i82546eb:
r = csr32r(ctlr, Manc);
r &= ~Arpen;
csr32w(ctlr, Manc, r);
break;
}
csr32w(ctlr, Imc, ~0);
delay(1); /* was 100 */
for(timeo = 0; timeo < 1000; timeo++){
if(!csr32r(ctlr, Icr))
break;
delay(1);
}
if(csr32r(ctlr, Icr))
return -1;
return 0;
}
int
etherigbereset(Ctlr* ctlr)
{
int ctrl, i, pause, r, swdpio, txcw;
if(igbedetach(ctlr))
return -1;
/*
* Read the EEPROM, validate the checksum
* then get the device back to a power-on state.
*/
if((r = at93c46r(ctlr)) != 0xBABA){
print("igbe: bad EEPROM checksum - 0x%4.4uX\n", r);
return -1;
}
/*
* Snarf and set up the receive addresses.
* There are 16 addresses. The first should be the MAC address.
* The others are cleared and not marked valid (MS bit of Rah).
*/
if(ctlr->id == i82546gb || ctlr->id == i82546eb)
if(BUSFNO(ctlr->pcidev->tbdf) == 1)
ctlr->eeprom[Ea+2] += 0x100; // second interface
if(ctlr->id == i82541gi)
if(ctlr->eeprom[Ea] == 0xFFFF)
ctlr->eeprom[Ea] = 0xD000;
for(i = 0; i < Easize/2; i++){
ctlr->ra[2*i] = ctlr->eeprom[Ea+i];
ctlr->ra[2*i+1] = ctlr->eeprom[Ea+i]>>8;
}
r = (ctlr->ra[3]<<24)|(ctlr->ra[2]<<16)|(ctlr->ra[1]<<8)|ctlr->ra[0];
csr32w(ctlr, Ral, r);
r = 0x80000000|(ctlr->ra[5]<<8)|ctlr->ra[4];
csr32w(ctlr, Rah, r);
for(i = 1; i < 16; i++){
csr32w(ctlr, Ral+i*8, 0);
csr32w(ctlr, Rah+i*8, 0);
}
/*
* Clear the Multicast Table Array.
* It's a 4096 bit vector accessed as 128 32-bit registers.
*/
for(i = 0; i < 128; i++)
csr32w(ctlr, Mta+i*4, 0);
/*
* Just in case the Eerst didn't load the defaults
* (doesn't appear to fully on the 8243GC), do it manually.
*/
if (ctlr->id == i82543gc) { // 82543
txcw = csr32r(ctlr, Txcw);
txcw &= ~(TxcwAne|TxcwPauseMASK|TxcwFd);
ctrl = csr32r(ctlr, Ctrl);
ctrl &= ~(SwdpioloMASK|Frcspd|Ilos|Lrst|Fd);
if(ctlr->eeprom[Icw1] & 0x0400){
ctrl |= Fd;
txcw |= TxcwFd;
}
if(ctlr->eeprom[Icw1] & 0x0200)
ctrl |= Lrst;
if(ctlr->eeprom[Icw1] & 0x0010)
ctrl |= Ilos;
if(ctlr->eeprom[Icw1] & 0x0800)
ctrl |= Frcspd;
swdpio = (ctlr->eeprom[Icw1] & 0x01E0)>>5;
ctrl |= swdpio<<SwdpioloSHIFT;
csr32w(ctlr, Ctrl, ctrl);
ctrl = csr32r(ctlr, Ctrlext);
ctrl &= ~(Ips|SwdpiohiMASK);
swdpio = (ctlr->eeprom[Icw2] & 0x00F0)>>4;
if(ctlr->eeprom[Icw1] & 0x1000)
ctrl |= Ips;
ctrl |= swdpio<<SwdpiohiSHIFT;
csr32w(ctlr, Ctrlext, ctrl);
if(ctlr->eeprom[Icw2] & 0x0800)
txcw |= TxcwAne;
pause = (ctlr->eeprom[Icw2] & 0x3000)>>12;
txcw |= pause<<TxcwPauseSHIFT;
switch(pause){
default:
ctlr->fcrtl = 0x00002000;
ctlr->fcrth = 0x00004000;
txcw |= TxcwAs|TxcwPs;
break;
case 0:
ctlr->fcrtl = 0x00002000;
ctlr->fcrth = 0x00004000;
break;
case 2:
ctlr->fcrtl = 0;
ctlr->fcrth = 0;
txcw |= TxcwAs;
break;
}
ctlr->txcw = txcw;
csr32w(ctlr, Txcw, txcw);
}
/*
* Flow control - values from the datasheet.
*/
csr32w(ctlr, Fcal, 0x00C28001);
csr32w(ctlr, Fcah, 0x00000100);
csr32w(ctlr, Fct, 0x00008808);
csr32w(ctlr, Fcttv, 0x00000100);
csr32w(ctlr, Fcrtl, ctlr->fcrtl);
csr32w(ctlr, Fcrth, ctlr->fcrth);
if(!(csr32r(ctlr, Status) & Tbimode) && igbemii(ctlr) < 0)
return -1;
return 0;
}
static void
igbepci(void)
{
int cls;
Pcidev *p;
Ctlr *ctlr;
void *mem;
p = nil;
while(p = pcimatch(p, 0, 0)){
switch((p->did<<16)|p->vid){
default:
continue;
case i82543gc:
case i82544ei:
case i82547ei:
case i82540em:
case i82540eplp:
case i82541gi:
case i82547gi:
case i82541gi2:
case i82541pi:
case i82545gmc:
case i82546gb:
case i82546eb:
break;
}
/* cast for FS */
mem = (void *)vmap(p->mem[0].bar & ~0x0F, p->mem[0].size);
if(mem == nil){
print("igbe: can't map %8.8luX\n", p->mem[0].bar);
continue;
}
switch(cls = pcicfgr8(p, PciCLS)){
default:
print("igbe: unexpected CLS - %d\n", cls*4);
break;
case 0x00:
case 0xFF:
print("igbe: unusable CLS\n");
continue;
case 0x08:
case 0x10:
break;
}
ctlr = ialloc(sizeof(Ctlr), 0);
if (ctlr == nil)
panic("ibgepci: no mem");
ctlr->port = p->mem[0].bar & ~0x0F;
ctlr->pcidev = p;
ctlr->id = (p->did<<16)|p->vid;
ctlr->cls = cls*4;
ctlr->nic = mem;
if(etherigbereset(ctlr)){
// free(ctlr);
continue;
}
pcisetbme(p);
if(igbectlrhead != nil)
igbectlrtail->next = ctlr;
else
igbectlrhead = ctlr;
igbectlrtail = ctlr;
}
}
int
igbepnp(Ether* edev)
{
Ctlr *ctlr;
if(igbectlrhead == nil)
igbepci();
/*
* Any adapter matches if no edev->port is supplied,
* otherwise the ports must match.
*/
for(ctlr = igbectlrhead; ctlr != nil; ctlr = ctlr->next){
if(ctlr->active)
continue;
if(edev->port == 0 || edev->port == ctlr->port){
ctlr->active = 1;
break;
}
}
if(ctlr == nil)
return -1;
edev->ctlr = ctlr;
edev->port = ctlr->port;
edev->irq = ctlr->pcidev->intl;
edev->tbdf = ctlr->pcidev->tbdf;
edev->mbps = 1000;
memmove(edev->ea, ctlr->ra, Easize);
/*
* Linkage to the generic ethernet driver.
*/
edev->attach = igbeattach;
edev->transmit = igbetransmit;
edev->interrupt = igbeinterrupt;
return 0;
}
|