ref: 61cc0694649a4d4b109d168032f431751f57f083
dir: /sys/src/9/pc/etherdp83820.c/
/* * National Semiconductor DP83820 * 10/100/1000 Mb/s Ethernet Network Interface Controller * (Gig-NIC). * Driver assumes little-endian and 32-bit host throughout. */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/pci.h" #include "../port/error.h" #include "../port/netif.h" #include "../port/etherif.h" #include "../port/ethermii.h" enum { /* Registers */ Cr = 0x00, /* Command */ Cfg = 0x04, /* Configuration and Media Status */ Mear = 0x08, /* MII/EEPROM Access */ Ptscr = 0x0C, /* PCI Test Control */ Isr = 0x10, /* Interrupt Status */ Imr = 0x14, /* Interrupt Mask */ Ier = 0x18, /* Interrupt Enable */ Ihr = 0x1C, /* Interrupt Holdoff */ Txdp = 0x20, /* Transmit Descriptor Pointer */ Txdphi = 0x24, /* Transmit Descriptor Pointer Hi */ Txcfg = 0x28, /* Transmit Configuration */ Gpior = 0x2C, /* General Purpose I/O Control */ Rxdp = 0x30, /* Receive Descriptor Pointer */ Rxdphi = 0x34, /* Receive Descriptor Pointer Hi */ Rxcfg = 0x38, /* Receive Configuration */ Pqcr = 0x3C, /* Priority Queueing Control */ Wcsr = 0x40, /* Wake on LAN Control/Status */ Pcr = 0x44, /* Pause Control/Status */ Rfcr = 0x48, /* Receive Filter/Match Control */ Rfdr = 0x4C, /* Receive Filter/Match Data */ Brar = 0x50, /* Boot ROM Address */ Brdr = 0x54, /* Boot ROM Data */ Srr = 0x58, /* Silicon Revision */ Mibc = 0x5C, /* MIB Control */ Mibd = 0x60, /* MIB Data */ Txdp1 = 0xA0, /* Txdp Priority 1 */ Txdp2 = 0xA4, /* Txdp Priority 2 */ Txdp3 = 0xA8, /* Txdp Priority 3 */ Rxdp1 = 0xB0, /* Rxdp Priority 1 */ Rxdp2 = 0xB4, /* Rxdp Priority 2 */ Rxdp3 = 0xB8, /* Rxdp Priority 3 */ Vrcr = 0xBC, /* VLAN/IP Receive Control */ Vtcr = 0xC0, /* VLAN/IP Transmit Control */ Vdr = 0xC4, /* VLAN Data */ Ccsr = 0xCC, /* Clockrun Control/Status */ Tbicr = 0xE0, /* TBI Control */ Tbisr = 0xE4, /* TBI Status */ Tanar = 0xE8, /* TBI ANAR */ Tanlpar = 0xEC, /* TBI ANLPAR */ Taner = 0xF0, /* TBI ANER */ Tesr = 0xF4, /* TBI ESR */ }; enum { /* Cr */ Txe = 0x00000001, /* Transmit Enable */ Txd = 0x00000002, /* Transmit Disable */ Rxe = 0x00000004, /* Receiver Enable */ Rxd = 0x00000008, /* Receiver Disable */ Txr = 0x00000010, /* Transmitter Reset */ Rxr = 0x00000020, /* Receiver Reset */ Swien = 0x00000080, /* Software Interrupt Enable */ Rst = 0x00000100, /* Reset */ TxpriSHFT = 9, /* Tx Priority Queue Select */ TxpriMASK = 0x00001E00, RxpriSHFT = 13, /* Rx Priority Queue Select */ RxpriMASK = 0x0001E000, }; enum { /* Configuration and Media Status */ Bem = 0x00000001, /* Big Endian Mode */ Ext125 = 0x00000002, /* External 125MHz reference Select */ Bromdis = 0x00000004, /* Disable Boot ROM interface */ Pesel = 0x00000008, /* Parity Error Detection Action */ Exd = 0x00000010, /* Excessive Deferral Abort */ Pow = 0x00000020, /* Program Out of Window Timer */ Sb = 0x00000040, /* Single Back-off */ Reqalg = 0x00000080, /* PCI Bus Request Algorithm */ Extstsen = 0x00000100, /* Extended Status Enable */ Phydis = 0x00000200, /* Disable PHY */ Phyrst = 0x00000400, /* Reset PHY */ M64addren = 0x00000800, /* Master 64-bit Addressing Enable */ Data64en = 0x00001000, /* 64-bit Data Enable */ Pci64det = 0x00002000, /* PCI 64-bit Bus Detected */ T64addren = 0x00004000, /* Target 64-bit Addressing Enable */ Mwidis = 0x00008000, /* MWI Disable */ Mrmdis = 0x00010000, /* MRM Disable */ Tmrtest = 0x00020000, /* Timer Test Mode */ Spdstsien = 0x00040000, /* PHY Spdsts Interrupt Enable */ Lnkstsien = 0x00080000, /* PHY Lnksts Interrupt Enable */ Dupstsien = 0x00100000, /* PHY Dupsts Interrupt Enable */ Mode1000 = 0x00400000, /* 1000Mb/s Mode Control */ Tbien = 0x01000000, /* Ten-Bit Interface Enable */ Dupsts = 0x10000000, /* Full Duplex Status */ Spdsts100 = 0x20000000, /* SPEED100 Input Pin Status */ Spdsts1000 = 0x40000000, /* SPEED1000 Input Pin Status */ Lnksts = 0x80000000, /* Link Status */ }; enum { /* MII/EEPROM Access */ Eedi = 0x00000001, /* EEPROM Data In */ Eedo = 0x00000002, /* EEPROM Data Out */ Eeclk = 0x00000004, /* EEPROM Serial Clock */ Eesel = 0x00000008, /* EEPROM Chip Select */ Mdio = 0x00000010, /* MII Management Data */ Mddir = 0x00000020, /* MII Management Direction */ Mdc = 0x00000040, /* MII Management Clock */ }; enum { /* Interrupts */ Rxok = 0x00000001, /* Rx OK */ Rxdesc = 0x00000002, /* Rx Descriptor */ Rxerr = 0x00000004, /* Rx Packet Error */ Rxearly = 0x00000008, /* Rx Early Threshold */ Rxidle = 0x00000010, /* Rx Idle */ Rxorn = 0x00000020, /* Rx Overrun */ Txok = 0x00000040, /* Tx Packet OK */ Txdesc = 0x00000080, /* Tx Descriptor */ Txerr = 0x00000100, /* Tx Packet Error */ Txidle = 0x00000200, /* Tx Idle */ Txurn = 0x00000400, /* Tx Underrun */ Mib = 0x00000800, /* MIB Service */ Swi = 0x00001000, /* Software Interrupt */ Pme = 0x00002000, /* Power Management Event */ Phy = 0x00004000, /* PHY Interrupt */ Hibint = 0x00008000, /* High Bits Interrupt Set */ Rxsovr = 0x00010000, /* Rx Status FIFO Overrun */ Rtabt = 0x00020000, /* Received Target Abort */ Rmabt = 0x00040000, /* Received Master Abort */ Sserr = 0x00080000, /* Signalled System Error */ Dperr = 0x00100000, /* Detected Parity Error */ Rxrcmp = 0x00200000, /* Receive Reset Complete */ Txrcmp = 0x00400000, /* Transmit Reset Complete */ Rxdesc0 = 0x00800000, /* Rx Descriptor for Priority Queue 0 */ Rxdesc1 = 0x01000000, /* Rx Descriptor for Priority Queue 1 */ Rxdesc2 = 0x02000000, /* Rx Descriptor for Priority Queue 2 */ Rxdesc3 = 0x04000000, /* Rx Descriptor for Priority Queue 3 */ Txdesc0 = 0x08000000, /* Tx Descriptor for Priority Queue 0 */ Txdesc1 = 0x10000000, /* Tx Descriptor for Priority Queue 1 */ Txdesc2 = 0x20000000, /* Tx Descriptor for Priority Queue 2 */ Txdesc3 = 0x40000000, /* Tx Descriptor for Priority Queue 3 */ }; enum { /* Interrupt Enable */ Ien = 0x00000001, /* Interrupt Enable */ }; enum { /* Interrupt Holdoff */ IhSHFT = 0, /* Interrupt Holdoff */ IhMASK = 0x000000FF, Ihctl = 0x00000100, /* Interrupt Holdoff Control */ }; enum { /* Transmit Configuration */ TxdrthSHFT = 0, /* Tx Drain Threshold */ TxdrthMASK = 0x000000FF, FlthSHFT = 16, /* Tx Fill Threshold */ FlthMASK = 0x0000FF00, Brstdis = 0x00080000, /* 1000Mb/s Burst Disable */ MxdmaSHFT = 20, /* Max Size per Tx DMA Burst */ MxdmaMASK = 0x00700000, Ecretryen = 0x00800000, /* Excessive Collision Retry Enable */ Atp = 0x10000000, /* Automatic Transmit Padding */ Mlb = 0x20000000, /* MAC Loopback */ Hbi = 0x40000000, /* Heartbeat Ignore */ Csi = 0x80000000, /* Carrier Sense Ignore */ }; enum { /* Receive Configuration */ RxdrthSHFT = 1, /* Rx Drain Threshold */ RxdrthMASK = 0x0000003E, Airl = 0x04000000, /* Accept In-Range Length Errored */ Alp = 0x08000000, /* Accept Long Packets */ Rxfd = 0x10000000, /* Receive Full Duplex */ Stripcrc = 0x20000000, /* Strip CRC */ Arp = 0x40000000, /* Accept Runt Packets */ Aep = 0x80000000, /* Accept Errored Packets */ }; enum { /* Priority Queueing Control */ Txpqen = 0x00000001, /* Transmit Priority Queuing Enable */ Txfairen = 0x00000002, /* Transmit Fairness Enable */ RxpqenSHFT = 2, /* Receive Priority Queue Enable */ RxpqenMASK = 0x0000000C, }; enum { /* Pause Control/Status */ PscntSHFT = 0, /* Pause Counter Value */ PscntMASK = 0x0000FFFF, Pstx = 0x00020000, /* Transmit Pause Frame */ PsffloSHFT = 18, /* Rx Data FIFO Lo Threshold */ PsffloMASK = 0x000C0000, PsffhiSHFT = 20, /* Rx Data FIFO Hi Threshold */ PsffhiMASK = 0x00300000, PsstloSHFT = 22, /* Rx Stat FIFO Hi Threshold */ PsstloMASK = 0x00C00000, PssthiSHFT = 24, /* Rx Stat FIFO Hi Threshold */ PssthiMASK = 0x03000000, Psrcvd = 0x08000000, /* Pause Frame Received */ Psact = 0x10000000, /* Pause Active */ Psda = 0x20000000, /* Pause on Destination Address */ Psmcast = 0x40000000, /* Pause on Multicast */ Psen = 0x80000000, /* Pause Enable */ }; enum { /* Receive Filter/Match Control */ RfaddrSHFT = 0, /* Extended Register Address */ RfaddrMASK = 0x000003FF, Ulm = 0x00080000, /* U/L bit mask */ Uhen = 0x00100000, /* Unicast Hash Enable */ Mhen = 0x00200000, /* Multicast Hash Enable */ Aarp = 0x00400000, /* Accept ARP Packets */ ApatSHFT = 23, /* Accept on Pattern Match */ ApatMASK = 0x07800000, Apm = 0x08000000, /* Accept on Perfect Match */ Aau = 0x10000000, /* Accept All Unicast */ Aam = 0x20000000, /* Accept All Multicast */ Aab = 0x40000000, /* Accept All Broadcast */ Rfen = 0x80000000, /* Rx Filter Enable */ }; enum { /* Receive Filter/Match Data */ RfdataSHFT = 0, /* Receive Filter Data */ RfdataMASK = 0x0000FFFF, BmaskSHFT = 16, /* Byte Mask */ BmaskMASK = 0x00030000, }; enum { /* MIB Control */ Wrn = 0x00000001, /* Warning Test Indicator */ Frz = 0x00000002, /* Freeze All Counters */ Aclr = 0x00000004, /* Clear All Counters */ Mibs = 0x00000008, /* MIB Counter Strobe */ }; enum { /* MIB Data */ Nmibd = 11, /* Number of MIB Data Registers */ }; enum { /* VLAN/IP Receive Control */ Vtden = 0x00000001, /* VLAN Tag Detection Enable */ Vtren = 0x00000002, /* VLAN Tag Removal Enable */ Dvtf = 0x00000004, /* Discard VLAN Tagged Frames */ Dutf = 0x00000008, /* Discard Untagged Frames */ Ipen = 0x00000010, /* IP Checksum Enable */ Ripe = 0x00000020, /* Reject IP Checksum Errors */ Rtcpe = 0x00000040, /* Reject TCP Checksum Errors */ Rudpe = 0x00000080, /* Reject UDP Checksum Errors */ }; enum { /* VLAN/IP Transmit Control */ Vgti = 0x00000001, /* VLAN Global Tag Insertion */ Vppti = 0x00000002, /* VLAN Per-Packet Tag Insertion */ Gchk = 0x00000004, /* Global Checksum Generation */ Ppchk = 0x00000008, /* Per-Packet Checksum Generation */ }; enum { /* VLAN Data */ VtypeSHFT = 0, /* VLAN Type Field */ VtypeMASK = 0x0000FFFF, VtciSHFT = 16, /* VLAN Tag Control Information */ VtciMASK = 0xFFFF0000, }; enum { /* Clockrun Control/Status */ Clkrunen = 0x00000001, /* CLKRUN Enable */ Pmeen = 0x00000100, /* PME Enable */ Pmests = 0x00008000, /* PME Status */ }; typedef struct { u32int link; /* Link to the next descriptor */ u32int bufptr; /* pointer to data Buffer */ int cmdsts; /* Command/Status */ int extsts; /* optional Extended Status */ Block* bp; /* Block containing bufptr */ u32int unused; /* pad to 64-bit */ } Desc; enum { /* Common cmdsts bits */ SizeMASK = 0x0000FFFF, /* Descriptor Byte Count */ SizeSHFT = 0, Ok = 0x08000000, /* Packet OK */ Crc = 0x10000000, /* Suppress/Include CRC */ Intr = 0x20000000, /* Interrupt on ownership transfer */ More = 0x40000000, /* not last descriptor in a packet */ Own = 0x80000000, /* Descriptor Ownership */ }; enum { /* Transmit cmdsts bits */ CcntMASK = 0x000F0000, /* Collision Count */ CcntSHFT = 16, Ec = 0x00100000, /* Excessive Collisions */ Owc = 0x00200000, /* Out of Window Collision */ Ed = 0x00400000, /* Excessive Deferral */ Td = 0x00800000, /* Transmit Deferred */ Crs = 0x01000000, /* Carrier Sense Lost */ Tfu = 0x02000000, /* Transmit FIFO Underrun */ Txa = 0x04000000, /* Transmit Abort */ }; enum { /* Receive cmdsts bits */ Irl = 0x00010000, /* In-Range Length Error */ Lbp = 0x00020000, /* Loopback Packet */ Fae = 0x00040000, /* Frame Alignment Error */ Crce = 0x00080000, /* CRC Error */ Ise = 0x00100000, /* Invalid Symbol Error */ Runt = 0x00200000, /* Runt Packet Received */ Long = 0x00400000, /* Too Long Packet Received */ DestMASK = 0x01800000, /* Destination Class */ DestSHFT = 23, Rxo = 0x02000000, /* Receive Overrun */ Rxa = 0x04000000, /* Receive Aborted */ }; enum { /* extsts bits */ EvtciMASK = 0x0000FFFF, /* VLAN Tag Control Information */ EvtciSHFT = 0, Vpkt = 0x00010000, /* VLAN Packet */ Ippkt = 0x00020000, /* IP Packet */ Iperr = 0x00040000, /* IP Checksum Error */ Tcppkt = 0x00080000, /* TCP Packet */ Tcperr = 0x00100000, /* TCP Checksum Error */ Udppkt = 0x00200000, /* UDP Packet */ Udperr = 0x00400000, /* UDP Checksum Error */ }; enum { Nrd = 256, Nrb = 4*Nrd, Rbsz = ROUNDUP(sizeof(Etherpkt)+8, 8), Ntd = 128, }; typedef struct Ctlr Ctlr; typedef struct Ctlr { uvlong port; Pcidev* pcidev; Ctlr* next; int active; int id; int eepromsz; /* address size in bits */ ushort* eeprom; int* nic; int cfg; int imr; QLock alock; /* attach */ Lock ilock; /* init */ void* alloc; /* base of per-Ctlr allocated data */ Mii* mii; Lock rdlock; /* receive */ Desc* rd; int nrd; int nrb; int rdx; int rxcfg; Lock tlock; /* transmit */ Desc* td; int ntd; int tdh; int tdt; int ntq; int txcfg; int rxidle; uint mibd[Nmibd]; int ec; int owc; int ed; int crs; int tfu; int txa; } Ctlr; #define csr32r(c, r) (*((c)->nic+((r)/4))) #define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v)) static Ctlr* dp83820ctlrhead; static Ctlr* dp83820ctlrtail; static Lock dp83820rblock; /* free receive Blocks */ static Block* dp83820rbpool; static char* dp83820mibs[Nmibd] = { "RXErroredPkts", "RXFCSErrors", "RXMsdPktErrors", "RXFAErrors", "RXSymbolErrors", "RXFrameToLong", "RXIRLErrors", "RXBadOpcodes", "RXPauseFrames", "TXPauseFrames", "TXSQEErrors", }; static int mdior(Ctlr* ctlr, int n) { int data, i, mear, r; mear = csr32r(ctlr, Mear); r = ~(Mdc|Mddir) & mear; data = 0; for(i = n-1; i >= 0; i--){ if(csr32r(ctlr, Mear) & Mdio) data |= (1<<i); csr32w(ctlr, Mear, Mdc|r); csr32w(ctlr, Mear, r); } csr32w(ctlr, Mear, mear); return data; } static void mdiow(Ctlr* ctlr, int bits, int n) { int i, mear, r; mear = csr32r(ctlr, Mear); r = Mddir|(~Mdc & mear); for(i = n-1; i >= 0; i--){ if(bits & (1<<i)) r |= Mdio; else r &= ~Mdio; csr32w(ctlr, Mear, r); csr32w(ctlr, Mear, Mdc|r); } csr32w(ctlr, Mear, mear); } static int dp83820miimir(Mii* mii, int pa, int ra) { int data; Ctlr *ctlr; ctlr = mii->ctlr; /* * MII Management Interface Read. * * Preamble; * ST+OP+PA+RA; * LT + 16 data bits. */ mdiow(ctlr, 0xFFFFFFFF, 32); mdiow(ctlr, 0x1800|(pa<<5)|ra, 14); data = mdior(ctlr, 18); if(data & 0x10000) return -1; return data & 0xFFFF; } static int dp83820miimiw(Mii* mii, int pa, int ra, int data) { Ctlr *ctlr; ctlr = mii->ctlr; /* * MII Management Interface Write. * * Preamble; * ST+OP+PA+RA+LT + 16 data bits; * Z. */ mdiow(ctlr, 0xFFFFFFFF, 32); data &= 0xFFFF; data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16); mdiow(ctlr, data, 32); return 0; } static Block * dp83820rballoc(Desc* desc) { Block *bp; if(desc->bp == nil){ ilock(&dp83820rblock); if((bp = dp83820rbpool) == nil){ iunlock(&dp83820rblock); desc->bp = nil; desc->cmdsts = Own; return nil; } dp83820rbpool = bp->next; bp->next = nil; iunlock(&dp83820rblock); desc->bufptr = PCIWADDR(bp->rp); desc->bp = bp; } else{ bp = desc->bp; bp->rp = bp->lim - Rbsz; bp->wp = bp->rp; } coherence(); desc->cmdsts = Intr|Rbsz; return bp; } static void dp83820rbfree(Block *bp) { bp->rp = bp->lim - Rbsz; bp->wp = bp->rp; ilock(&dp83820rblock); bp->next = dp83820rbpool; dp83820rbpool = bp; iunlock(&dp83820rblock); } static void dp83820halt(Ctlr* ctlr) { int i, timeo; ilock(&ctlr->ilock); csr32w(ctlr, Imr, 0); csr32w(ctlr, Ier, 0); csr32w(ctlr, Cr, Rxd|Txd); for(timeo = 0; timeo < 1000; timeo++){ if(!(csr32r(ctlr, Cr) & (Rxe|Txe))) break; microdelay(1); } csr32w(ctlr, Mibc, Frz); iunlock(&ctlr->ilock); if(ctlr->rd != nil){ for(i = 0; i < ctlr->nrd; i++){ if(ctlr->rd[i].bp == nil) continue; freeb(ctlr->rd[i].bp); ctlr->rd[i].bp = nil; } } if(ctlr->td != nil){ for(i = 0; i < ctlr->ntd; i++){ if(ctlr->td[i].bp == nil) continue; freeb(ctlr->td[i].bp); ctlr->td[i].bp = nil; } } } static void dp83820cfg(Ctlr* ctlr) { int cfg; /* * Don't know how to deal with a TBI yet. */ if(ctlr->mii == nil) return; /* * The polarity of these bits is at the mercy * of the board designer. * The correct answer for all speed and duplex questions * should be to query the phy. */ cfg = csr32r(ctlr, Cfg); if(!(cfg & Dupsts)){ ctlr->rxcfg |= Rxfd; ctlr->txcfg |= Csi|Hbi; iprint("83820: full duplex, "); } else{ ctlr->rxcfg &= ~Rxfd; ctlr->txcfg &= ~(Csi|Hbi); iprint("83820: half duplex, "); } csr32w(ctlr, Rxcfg, ctlr->rxcfg); csr32w(ctlr, Txcfg, ctlr->txcfg); switch(cfg & (Spdsts1000|Spdsts100)){ case Spdsts1000: /* 100Mbps */ default: /* 10Mbps */ ctlr->cfg &= ~Mode1000; if((cfg & (Spdsts1000|Spdsts100)) == Spdsts1000) iprint("100Mb/s\n"); else iprint("10Mb/s\n"); break; case Spdsts100: /* 1Gbps */ ctlr->cfg |= Mode1000; iprint("1Gb/s\n"); break; } csr32w(ctlr, Cfg, ctlr->cfg); } static void dp83820init(Ether* edev) { int i; Ctlr *ctlr; Desc *desc; uchar *alloc; ctlr = edev->ctlr; dp83820halt(ctlr); /* * Receiver */ alloc = (uchar*)ROUNDUP((ulong)ctlr->alloc, 8); ctlr->rd = (Desc*)alloc; alloc += ctlr->nrd*sizeof(Desc); memset(ctlr->rd, 0, ctlr->nrd*sizeof(Desc)); ctlr->rdx = 0; for(i = 0; i < ctlr->nrd; i++){ desc = &ctlr->rd[i]; desc->link = PCIWADDR(&ctlr->rd[NEXT(i, ctlr->nrd)]); if(dp83820rballoc(desc) == nil) continue; } csr32w(ctlr, Rxdphi, 0); csr32w(ctlr, Rxdp, PCIWADDR(ctlr->rd)); for(i = 0; i < Eaddrlen; i += 2){ csr32w(ctlr, Rfcr, i); csr32w(ctlr, Rfdr, (edev->ea[i+1]<<8)|edev->ea[i]); } csr32w(ctlr, Rfcr, Rfen|Aab|Aam|Apm); ctlr->rxcfg = Stripcrc|(((2*(ETHERMINTU+4))/8)<<RxdrthSHFT); ctlr->imr |= Rxorn|Rxidle|Rxearly|Rxdesc|Rxok; /* * Transmitter. */ ctlr->td = (Desc*)alloc; memset(ctlr->td, 0, ctlr->ntd*sizeof(Desc)); ctlr->tdh = ctlr->tdt = ctlr->ntq = 0; for(i = 0; i < ctlr->ntd; i++){ desc = &ctlr->td[i]; desc->link = PCIWADDR(&ctlr->td[NEXT(i, ctlr->ntd)]); } csr32w(ctlr, Txdphi, 0); csr32w(ctlr, Txdp, PCIWADDR(ctlr->td)); ctlr->txcfg = Atp|(((2*(ETHERMINTU+4))/32)<<FlthSHFT)|((4096/32)<<TxdrthSHFT); ctlr->imr |= Txurn|Txidle|Txdesc|Txok; ilock(&ctlr->ilock); dp83820cfg(ctlr); csr32w(ctlr, Mibc, Aclr); ctlr->imr |= Mib; csr32w(ctlr, Imr, ctlr->imr); /* try coalescing adjacent interrupts; use hold-off interval of 100µs */ csr32w(ctlr, Ihr, Ihctl|(1<<IhSHFT)); csr32w(ctlr, Ier, Ien); csr32w(ctlr, Cr, Rxe|Txe); iunlock(&ctlr->ilock); } static void dp83820attach(Ether* edev) { Block *bp; Ctlr *ctlr; ctlr = edev->ctlr; qlock(&ctlr->alock); if(ctlr->alloc != nil){ qunlock(&ctlr->alock); return; } if(waserror()){ if(ctlr->mii != nil){ free(ctlr->mii); ctlr->mii = nil; } if(ctlr->alloc != nil){ free(ctlr->alloc); ctlr->alloc = nil; } qunlock(&ctlr->alock); nexterror(); } if(!(ctlr->cfg & Tbien)){ if((ctlr->mii = malloc(sizeof(Mii))) == nil) error(Enomem); ctlr->mii->ctlr = ctlr; ctlr->mii->mir = dp83820miimir; ctlr->mii->miw = dp83820miimiw; if(mii(ctlr->mii, ~0) == 0) error("no PHY"); ctlr->cfg |= Dupstsien|Lnkstsien|Spdstsien; ctlr->imr |= Phy; } ctlr->nrd = Nrd; ctlr->nrb = Nrb; ctlr->ntd = Ntd; ctlr->alloc = mallocz((ctlr->nrd+ctlr->ntd)*sizeof(Desc) + 7, 0); if(ctlr->alloc == nil) error(Enomem); for(ctlr->nrb = 0; ctlr->nrb < Nrb; ctlr->nrb++){ if((bp = allocb(Rbsz)) == nil) break; bp->free = dp83820rbfree; dp83820rbfree(bp); } dp83820init(edev); qunlock(&ctlr->alock); poperror(); } static void dp83820transmit(Ether* edev) { Block *bp; Ctlr *ctlr; Desc *desc; int cmdsts, r, x; ctlr = edev->ctlr; ilock(&ctlr->tlock); bp = nil; for(x = ctlr->tdh; ctlr->ntq; x = NEXT(x, ctlr->ntd)){ desc = &ctlr->td[x]; if((cmdsts = desc->cmdsts) & Own) break; if(!(cmdsts & Ok)){ if(cmdsts & Ec) ctlr->ec++; if(cmdsts & Owc) ctlr->owc++; if(cmdsts & Ed) ctlr->ed++; if(cmdsts & Crs) ctlr->crs++; if(cmdsts & Tfu) ctlr->tfu++; if(cmdsts & Txa) ctlr->txa++; edev->oerrs++; } desc->bp->next = bp; bp = desc->bp; desc->bp = nil; ctlr->ntq--; } ctlr->tdh = x; if(bp != nil) freeblist(bp); x = ctlr->tdt; while(ctlr->ntq < (ctlr->ntd-1)){ if((bp = qget(edev->oq)) == nil) break; desc = &ctlr->td[x]; desc->bufptr = PCIWADDR(bp->rp); desc->bp = bp; ctlr->ntq++; coherence(); desc->cmdsts = Own|Intr|BLEN(bp); x = NEXT(x, ctlr->ntd); } if(x != ctlr->tdt){ ctlr->tdt = x; r = csr32r(ctlr, Cr); csr32w(ctlr, Cr, Txe|r); } iunlock(&ctlr->tlock); } static void dp83820interrupt(Ureg*, void* arg) { Block *bp; Ctlr *ctlr; Desc *desc; Ether *edev; int cmdsts, i, isr, r, x; edev = arg; ctlr = edev->ctlr; for(isr = csr32r(ctlr, Isr); isr & ctlr->imr; isr = csr32r(ctlr, Isr)){ if(isr & (Rxorn|Rxidle|Rxearly|Rxerr|Rxdesc|Rxok)){ x = ctlr->rdx; desc = &ctlr->rd[x]; while((cmdsts = desc->cmdsts) & Own){ if((cmdsts & Ok) && desc->bp != nil){ bp = desc->bp; desc->bp = nil; bp->wp += cmdsts & SizeMASK; etheriq(edev, bp); } else if(0 && !(cmdsts & Ok)){ iprint("dp83820: rx %8.8uX:", cmdsts); bp = desc->bp; for(i = 0; i < 20; i++) iprint(" %2.2uX", bp->rp[i]); iprint("\n"); } dp83820rballoc(desc); x = NEXT(x, ctlr->nrd); desc = &ctlr->rd[x]; } ctlr->rdx = x; if(isr & Rxidle){ r = csr32r(ctlr, Cr); csr32w(ctlr, Cr, Rxe|r); ctlr->rxidle++; } isr &= ~(Rxorn|Rxidle|Rxearly|Rxerr|Rxdesc|Rxok); } if(isr & Txurn){ x = (ctlr->txcfg & TxdrthMASK)>>TxdrthSHFT; r = (ctlr->txcfg & FlthMASK)>>FlthSHFT; if(x < ((TxdrthMASK)>>TxdrthSHFT) && x < (2048/32 - r)){ ctlr->txcfg &= ~TxdrthMASK; x++; ctlr->txcfg |= x<<TxdrthSHFT; csr32w(ctlr, Txcfg, ctlr->txcfg); } } if(isr & (Txurn|Txidle|Txdesc|Txok)){ dp83820transmit(edev); isr &= ~(Txurn|Txidle|Txdesc|Txok); } if(isr & Mib){ for(i = 0; i < Nmibd; i++){ r = csr32r(ctlr, Mibd+(i*sizeof(int))); ctlr->mibd[i] += r & 0xFFFF; } isr &= ~Mib; } if((isr & Phy) && ctlr->mii != nil){ ctlr->mii->mir(ctlr->mii, 1, Bmsr); print("phy: cfg %8.8uX bmsr %4.4uX\n", csr32r(ctlr, Cfg), ctlr->mii->mir(ctlr->mii, 1, Bmsr)); dp83820cfg(ctlr); isr &= ~Phy; } if(isr) iprint("dp83820: isr %8.8uX\n", isr); } } static long dp83820ifstat(Ether* edev, void* a, long n, ulong offset) { char *p; Ctlr *ctlr; int i, l, r; ctlr = edev->ctlr; edev->crcs = ctlr->mibd[Mibd+(1*sizeof(int))]; edev->frames = ctlr->mibd[Mibd+(3*sizeof(int))]; edev->buffs = ctlr->mibd[Mibd+(5*sizeof(int))]; edev->overflows = ctlr->mibd[Mibd+(2*sizeof(int))]; if(n == 0) return 0; p = smalloc(READSTR); l = 0; for(i = 0; i < Nmibd; i++){ r = csr32r(ctlr, Mibd+(i*sizeof(int))); ctlr->mibd[i] += r & 0xFFFF; if(ctlr->mibd[i] != 0 && dp83820mibs[i] != nil) l += snprint(p+l, READSTR-l, "%s: %ud %ud\n", dp83820mibs[i], ctlr->mibd[i], r); } l += snprint(p+l, READSTR-l, "rxidle %d\n", ctlr->rxidle); l += snprint(p+l, READSTR-l, "ec %d\n", ctlr->ec); l += snprint(p+l, READSTR-l, "owc %d\n", ctlr->owc); l += snprint(p+l, READSTR-l, "ed %d\n", ctlr->ed); l += snprint(p+l, READSTR-l, "crs %d\n", ctlr->crs); l += snprint(p+l, READSTR-l, "tfu %d\n", ctlr->tfu); l += snprint(p+l, READSTR-l, "txa %d\n", ctlr->txa); l += snprint(p+l, READSTR-l, "rom:"); for(i = 0; i < 0x10; i++){ if(i && ((i & 0x07) == 0)) l += snprint(p+l, READSTR-l, "\n "); l += snprint(p+l, READSTR-l, " %4.4uX", ctlr->eeprom[i]); } l += snprint(p+l, READSTR-l, "\n"); if(ctlr->mii != nil && ctlr->mii->curphy != nil){ l += snprint(p+l, READSTR-l, "phy:"); for(i = 0; i < NMiiPhyr; i++){ if(i && ((i & 0x07) == 0)) l += snprint(p+l, READSTR-l, "\n "); r = miimir(ctlr->mii, i); l += snprint(p+l, READSTR-l, " %4.4uX", r); } snprint(p+l, READSTR-l, "\n"); } n = readstr(offset, a, n, p); free(p); return n; } static void dp83820promiscuous(void* arg, int on) { USED(arg, on); } /* multicast already on, don't need to do anything */ static void dp83820multicast(void*, uchar*, int) { } static int dp83820detach(Ctlr* ctlr) { /* * Soft reset the controller. */ csr32w(ctlr, Cr, Rst); delay(1); while(csr32r(ctlr, Cr) & Rst) delay(1); return 0; } static void dp83820shutdown(Ether* ether) { print("dp83820shutdown\n"); dp83820detach(ether->ctlr); } static int atc93c46r(Ctlr* ctlr, int address) { int data, i, mear, r, size; /* * Analog Technology, Inc. ATC93C46 * or equivalent serial EEPROM. */ mear = csr32r(ctlr, Mear); mear &= ~(Eesel|Eeclk|Eedo|Eedi); r = Eesel|mear; reread: csr32w(ctlr, Mear, r); data = 0x06; for(i = 3-1; i >= 0; i--){ if(data & (1<<i)) r |= Eedi; else r &= ~Eedi; csr32w(ctlr, Mear, r); csr32w(ctlr, Mear, Eeclk|r); microdelay(1); csr32w(ctlr, Mear, r); microdelay(1); } /* * First time through must work out the EEPROM size. */ if((size = ctlr->eepromsz) == 0) size = 8; for(size = size-1; size >= 0; size--){ if(address & (1<<size)) r |= Eedi; else r &= ~Eedi; csr32w(ctlr, Mear, r); microdelay(1); csr32w(ctlr, Mear, Eeclk|r); microdelay(1); csr32w(ctlr, Mear, r); microdelay(1); if(!(csr32r(ctlr, Mear) & Eedo)) break; } r &= ~Eedi; data = 0; for(i = 16-1; i >= 0; i--){ csr32w(ctlr, Mear, Eeclk|r); microdelay(1); if(csr32r(ctlr, Mear) & Eedo) data |= (1<<i); csr32w(ctlr, Mear, r); microdelay(1); } csr32w(ctlr, Mear, mear); if(ctlr->eepromsz == 0){ ctlr->eepromsz = 8-size; ctlr->eeprom = malloc((1<<ctlr->eepromsz)*sizeof(ushort)); goto reread; } return data; } static int dp83820reset(Ctlr* ctlr) { int i, r; unsigned char sum; /* * Soft reset the controller; * read the EEPROM to get the initial settings * of the Cfg and Gpior bits which should be cleared by * the reset. */ dp83820detach(ctlr); atc93c46r(ctlr, 0); if(ctlr->eeprom == nil) { print("dp83820reset: no eeprom\n"); return -1; } sum = 0; for(i = 0; i < 0x0E; i++){ r = atc93c46r(ctlr, i); ctlr->eeprom[i] = r; sum += r; sum += r>>8; } if(sum != 0){ print("dp83820reset: bad EEPROM checksum\n"); return -1; } #ifdef notdef csr32w(ctlr, Gpior, ctlr->eeprom[4]); cfg = Extstsen|Exd; r = csr32r(ctlr, Cfg); if(ctlr->eeprom[5] & 0x0001) cfg |= Ext125; if(ctlr->eeprom[5] & 0x0002) cfg |= M64addren; if((ctlr->eeprom[5] & 0x0004) && (r & Pci64det)) cfg |= Data64en; if(ctlr->eeprom[5] & 0x0008) cfg |= T64addren; if(!(pcicfgr16(ctlr->pcidev, PciPCR) & 0x10)) cfg |= Mwidis; if(ctlr->eeprom[5] & 0x0020) cfg |= Mrmdis; if(ctlr->eeprom[5] & 0x0080) cfg |= Mode1000; if(ctlr->eeprom[5] & 0x0200) cfg |= Tbien|Mode1000; /* * What about RO bits we might have destroyed with Rst? * What about Exd, Tmrtest, Extstsen, Pintctl? * Why does it think it has detected a 64-bit bus when * it hasn't? */ #else // r = csr32r(ctlr, Cfg); // r &= ~(Mode1000|T64addren|Data64en|M64addren); // csr32w(ctlr, Cfg, r); // csr32w(ctlr, Cfg, 0x2000); #endif /* notdef */ ctlr->cfg = csr32r(ctlr, Cfg); print("cfg %8.8uX pcicfg %8.8uX\n", ctlr->cfg, pcicfgr32(ctlr->pcidev, PciPCR)); ctlr->cfg &= ~(T64addren|Data64en|M64addren); csr32w(ctlr, Cfg, ctlr->cfg); csr32w(ctlr, Mibc, Aclr|Frz); return 0; } static void dp83820pci(void) { void *mem; Pcidev *p; Ctlr *ctlr; p = nil; while(p = pcimatch(p, 0, 0)){ if(p->ccrb != Pcibcnet || p->ccru != Pciscether) continue; if(p->mem[1].bar & 1) continue; switch((p->did<<16)|p->vid){ default: continue; case (0x0022<<16)|0x100B: /* DP83820 (Gig-NIC) */ break; } mem = vmap(p->mem[1].bar & ~0xF, p->mem[1].size); if(mem == nil){ print("DP83820: can't map %llux\n", p->mem[1].bar & ~0xF); continue; } ctlr = malloc(sizeof(Ctlr)); if(ctlr == nil){ print("DP83820: can't allocate memory\n"); continue; } ctlr->port = p->mem[1].bar & ~0xF; ctlr->pcidev = p; pcienable(p); ctlr->id = (p->did<<16)|p->vid; ctlr->nic = mem; if(dp83820reset(ctlr)){ free(ctlr); continue; } pcisetbme(p); if(dp83820ctlrhead != nil) dp83820ctlrtail->next = ctlr; else dp83820ctlrhead = ctlr; dp83820ctlrtail = ctlr; } } static int dp83820pnp(Ether* edev) { int i; Ctlr *ctlr; uchar ea[Eaddrlen]; if(dp83820ctlrhead == nil) dp83820pci(); /* * Any adapter matches if no edev->port is supplied, * otherwise the ports must match. */ for(ctlr = dp83820ctlrhead; 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; /* * Check if the adapter's station address is to be overridden. * If not, read it from the EEPROM and set in ether->ea prior to * loading the station address in the hardware. */ memset(ea, 0, Eaddrlen); if(memcmp(ea, edev->ea, Eaddrlen) == 0) for(i = 0; i < Eaddrlen/2; i++){ edev->ea[2*i] = ctlr->eeprom[0x0C-i]; edev->ea[2*i+1] = ctlr->eeprom[0x0C-i]>>8; } edev->attach = dp83820attach; edev->transmit = dp83820transmit; edev->ifstat = dp83820ifstat; edev->arg = edev; edev->promiscuous = dp83820promiscuous; edev->multicast = dp83820multicast; edev->shutdown = dp83820shutdown; intrenable(edev->irq, dp83820interrupt, edev, edev->tbdf, edev->name); return 0; } void etherdp83820link(void) { addethercard("DP83820", dp83820pnp); }