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/boot/pc/etherdp83820.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/boot/pc/etherdp83820.c')
-rwxr-xr-x | sys/src/boot/pc/etherdp83820.c | 1222 |
1 files changed, 1222 insertions, 0 deletions
diff --git a/sys/src/boot/pc/etherdp83820.c b/sys/src/boot/pc/etherdp83820.c new file mode 100755 index 000000000..a7b2c7704 --- /dev/null +++ b/sys/src/boot/pc/etherdp83820.c @@ -0,0 +1,1222 @@ +/* + * boot driver for + * 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 "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "etherif.h" +#include "ethermii.h" + +#define iprint print +#define waserror() (0) +#define poperror() + +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 = 32, /* was 256 */ + Nrbf = 4*Nrd, + Rbsz = ROUNDUP(sizeof(Etherpkt)+8, 8), + Ntd = 8, /* was 128 */ +}; + +typedef struct Ctlr Ctlr; +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; + + Lock 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; +}; + +#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; + lock(&ctlr->alock); + if(ctlr->alloc != nil){ + unlock(&ctlr->alock); + return; + } + + if(waserror()){ +err: + if(ctlr->mii != nil){ + free(ctlr->mii); + ctlr->mii = nil; + } + if(ctlr->alloc != nil){ + free(ctlr->alloc); + ctlr->alloc = nil; + } + unlock(&ctlr->alock); + return; + } + + if(!(ctlr->cfg & Tbien)){ + if((ctlr->mii = malloc(sizeof(Mii))) == nil) + goto err; + ctlr->mii->ctlr = ctlr; + ctlr->mii->mir = dp83820miimir; + ctlr->mii->miw = dp83820miimiw; + if(mii(ctlr->mii, ~0) == 0) + goto err; + ctlr->cfg |= Dupstsien|Lnkstsien|Spdstsien; + ctlr->imr |= Phy; + } + + ctlr->nrd = Nrd; + ctlr->nrb = Nrbf; + ctlr->ntd = Ntd; + ctlr->alloc = mallocz((ctlr->nrd+ctlr->ntd)*sizeof(Desc) + 7, 0); + if(ctlr->alloc == nil) + goto err; + + /* + * the Bill Paul bsd drivers claim that receive buffers must be aligned + * on 8-byte boundaries. + */ + for(ctlr->nrb = 0; ctlr->nrb < Nrbf; ctlr->nrb++){ + if((bp = allocb(Rbsz+8-1)) == nil) + break; + bp->rp += 8 - (uintptr)bp->rp % 8; + bp->wp = bp->rp; +// bp->free = dp83820rbfree; /* TODO: fix */ + dp83820rbfree(bp); + } + + dp83820init(edev); + + unlock(&ctlr->alock); + poperror(); +} + +/* + * free a list of blocks + */ +static void +freeblist(Block *b) +{ + Block *next; + + for(; b != 0; b = next){ + next = b->next; + b->next = 0; + freeb(b); + } +} + +static void +toringbuf(Ether *ether, Block *bp) +{ + RingBuf *rb = ðer->rb[ether->ri]; + + if (rb->owner == Interface) { + rb->len = BLEN(bp); + memmove(rb->pkt, bp->rp, rb->len); + rb->owner = Host; + ether->ri = NEXT(ether->ri, ether->nrb); + } + /* else no one is expecting packets from the network */ +} + +static Block * +fromringbuf(Ether *ether) +{ + RingBuf *tb = ðer->tb[ether->ti]; + Block *bp = allocb(tb->len); + + memmove(bp->wp, tb->pkt, tb->len); + memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen); + bp->wp += tb->len; + return bp; +} + +static void +dp83820transmit(Ether* edev) +{ + Block *bp; + Ctlr *ctlr; + Desc *desc; + RingBuf *tb; + 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++; + } + desc->bp->next = bp; + bp = desc->bp; + desc->bp = nil; + + ctlr->ntq--; + } + ctlr->tdh = x; + if(bp != nil) + freeblist(bp); + + x = ctlr->tdt; +// tb = &edev->tb[edev->ti]; + while(ctlr->ntq < ctlr->ntd - 1 /* && tb->owner == Interface */ ){ + bp = fromringbuf(edev); + if (bp == nil) + break; + + /* transmit packet from bp */ + desc = &ctlr->td[x]; + desc->bufptr = PCIWADDR(bp->rp); + desc->bp = bp; + ctlr->ntq++; + coherence(); + desc->cmdsts = Own|Intr|BLEN(bp); /* kick transmitter */ +//print("t"); + tb = &edev->tb[edev->ti]; + tb->owner = Host; /* give descriptor back */ + edev->ti = NEXT(edev->ti, edev->ntb); + + 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){ + /* receive a packet into desc->bp */ + bp = desc->bp; + desc->bp = nil; + // bp->rp = desc->bufptr; + bp->wp += cmdsts & SizeMASK; + + toringbuf(edev, bp); +//print("r"); + } + 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; + } +// TODO fix if(isr) +// TODO fix iprint("dp83820: isr %8.8uX\n", isr); + USED(isr); + } +} + +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. + */ + csr32w(ctlr, Cr, Rst); + delay(1); + while(csr32r(ctlr, Cr) & Rst) + delay(1); + + 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) +{ + int port; + Pcidev *p; + Ctlr *ctlr; + + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + + switch((p->did<<16)|p->vid){ + default: + continue; + case (0x0022<<16)|0x100B: /* DP83820 (Gig-NIC) */ + break; + } + + port = upamalloc(p->mem[1].bar & ~0x0F, p->mem[1].size, 0); + if(port == 0){ + print("dp83820: can't map %d @ 0x%8.8luX\n", + p->mem[1].size, p->mem[1].bar); + continue; + } + + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = port; + ctlr->pcidev = p; + ctlr->id = p->did<<16 | p->vid; + + ctlr->nic = KADDR(ctlr->port); + if(dp83820reset(ctlr)){ + free(ctlr); + continue; + } + pcisetbme(p); + + if(dp83820ctlrhead != nil) + dp83820ctlrtail->next = ctlr; + else + dp83820ctlrhead = ctlr; + dp83820ctlrtail = ctlr; + } +} + +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; + + /* + * 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->detach = dp83820shutdown; + return 0; +} |