diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/pc/etherdp83820.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/pc/etherdp83820.c')
-rwxr-xr-x | sys/src/9/pc/etherdp83820.c | 1247 |
1 files changed, 1247 insertions, 0 deletions
diff --git a/sys/src/9/pc/etherdp83820.c b/sys/src/9/pc/etherdp83820.c new file mode 100755 index 000000000..98938e6d7 --- /dev/null +++ b/sys/src/9/pc/etherdp83820.c @@ -0,0 +1,1247 @@ +/* + * 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/error.h" +#include "../port/netif.h" + +#include "etherif.h" +#include "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 { + int 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; + _xinc(&bp->ref); /* prevent bp from being freed */ + 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, 1); + } + 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 = malloc(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, "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, "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; + + switch((p->did<<16)|p->vid){ + default: + continue; + case (0x0022<<16)|0x100B: /* DP83820 (Gig-NIC) */ + break; + } + + mem = vmap(p->mem[1].bar & ~0x0F, p->mem[1].size); + if(mem == 0){ + print("DP83820: can't map %8.8luX\n", p->mem[1].bar); + continue; + } + + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = p->mem[1].bar & ~0x0F; + ctlr->pcidev = 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->interrupt = dp83820interrupt; + edev->ifstat = dp83820ifstat; + + edev->arg = edev; + edev->promiscuous = dp83820promiscuous; + edev->multicast = dp83820multicast; + edev->shutdown = dp83820shutdown; + + return 0; +} + +void +etherdp83820link(void) +{ + addethercard("DP83820", dp83820pnp); +} |