summaryrefslogtreecommitdiff
path: root/sys/src/9/pc/etherdp83820.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /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-xsys/src/9/pc/etherdp83820.c1247
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);
+}