summaryrefslogtreecommitdiff
path: root/sys/src/9/pc/ethervt6105m.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/ethervt6105m.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/pc/ethervt6105m.c')
-rwxr-xr-xsys/src/9/pc/ethervt6105m.c1220
1 files changed, 1220 insertions, 0 deletions
diff --git a/sys/src/9/pc/ethervt6105m.c b/sys/src/9/pc/ethervt6105m.c
new file mode 100755
index 000000000..06d02c0f2
--- /dev/null
+++ b/sys/src/9/pc/ethervt6105m.c
@@ -0,0 +1,1220 @@
+/*
+ * VIA VT6105M Fast Ethernet Controller (Rhine III).
+ * To do:
+ * cache-line size alignments - done
+ * reduce tx interrupts - done
+ * reorganise initialisation/shutdown/reset
+ * adjust Tx FIFO threshold on underflow - untested
+ * why does the link status never cause an interrupt?
+ * use the lproc as a periodic timer for stalls, etc.
+ * checksum offload - done
+ * take non-HW stuff out of descriptor for 64-bit
+ * cleanliness
+ * why does the receive buffer alloc have a +3?
+ */
+#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 {
+ Par0 = 0x00, /* Ethernet Address */
+ Rcr = 0x06, /* Receive Configuration */
+ Tcr = 0x07, /* Transmit Configuration */
+ Cr = 0x08, /* Control */
+ Tqw = 0x0A, /* Transmit Queue Wake */
+ Isr = 0x0C, /* Interrupt Status */
+ Imr = 0x0E, /* Interrupt Mask */
+ Mcfilt0 = 0x10, /* Multicast Filter 0 */
+ Mcfilt1 = 0x14, /* Multicast Filter 1 */
+ Rxdaddr = 0x18, /* Current Rd Address */
+ Txdaddr = 0x1C, /* Current Td Address */
+ Phyadr = 0x6C, /* Phy Address */
+ Miisr = 0x6D, /* MII Status */
+ Bcr0 = 0x6E, /* Bus Control */
+ Bcr1 = 0x6F,
+ Miicr = 0x70, /* MII Control */
+ Miiadr = 0x71, /* MII Address */
+ Miidata = 0x72, /* MII Data */
+ Eecsr = 0x74, /* EEPROM Control and Status */
+ CfgA = 0x78, /* Chip Configuration A */
+ CfgB = 0x79,
+ CfgC = 0x7A,
+ CfgD = 0x7B,
+ Cr0 = 0x80, /* Miscellaneous Control */
+ Cr1 = 0x81,
+ Pmcc = 0x82, /* Power Mgmt Capability Control */
+ Stickhw = 0x83, /* Sticky Hardware Control */
+ Misr = 0x84, /* MII Interrupt Control */
+ Mimr = 0x85, /* MII Interrupt Mask */
+ Wolcrclr = 0xA4,
+ Wolcgclr = 0xA7,
+ Pwrcsrclr = 0xAC,
+};
+
+enum { /* Rcr */
+ Sep = 0x01, /* Accept Error Packets */
+ Ar = 0x02, /* Accept Small Packets */
+ Am = 0x04, /* Accept Multicast */
+ Ab = 0x08, /* Accept Broadcast */
+ Prom = 0x10, /* Accept Physical Address Packets */
+ RrftMASK = 0xE0, /* Receive FIFO Threshold */
+ RrftSHIFT = 5,
+ Rrft64 = 0<<RrftSHIFT,
+ Rrft32 = 1<<RrftSHIFT,
+ Rrft128 = 2<<RrftSHIFT,
+ Rrft256 = 3<<RrftSHIFT,
+ Rrft512 = 4<<RrftSHIFT,
+ Rrft768 = 5<<RrftSHIFT,
+ Rrft1024 = 6<<RrftSHIFT,
+ RrftSAF = 7<<RrftSHIFT,
+};
+
+enum { /* Tcr */
+ Lb0 = 0x02, /* Loopback Mode */
+ Lb1 = 0x04,
+ Ofset = 0x08, /* Select Back-off Priority */
+ RtsfMASK = 0xE0, /* Transmit FIFO Threshold */
+ RtsfSHIFT = 5,
+ Rtsf128 = 0<<RtsfSHIFT,
+ Rtsf256 = 1<<RtsfSHIFT,
+ Rtsf512 = 2<<RtsfSHIFT,
+ Rtsf1024 = 3<<RtsfSHIFT,
+ RtsfSAF = 7<<RtsfSHIFT,
+};
+
+enum { /* Cr */
+ Init = 0x0001, /* INIT Process Begin */
+ Strt = 0x0002, /* Start NIC */
+ Stop = 0x0004, /* Stop NIC */
+ Rxon = 0x0008, /* Turn on Receive Process */
+ Txon = 0x0010, /* Turn on Transmit Process */
+ Tdmd = 0x0020, /* Transmit Poll Demand */
+ Rdmd = 0x0040, /* Receive Poll Demand */
+ Eren = 0x0100, /* Early Receive Enable */
+ Fdx = 0x0400, /* Set MAC to Full Duplex */
+ Dpoll = 0x0800, /* Disable Td/Rd Auto Polling */
+ Tdmd1 = 0x2000, /* Transmit Poll Demand 1 */
+ Rdmd1 = 0x4000, /* Receive Poll Demand 1 */
+ Sfrst = 0x8000, /* Software Reset */
+};
+
+enum { /* Isr/Imr */
+ Prx = 0x0001, /* Packet Received OK */
+ Ptx = 0x0002, /* Packet Transmitted OK */
+ Rxe = 0x0004, /* Receive Error */
+ Txe = 0x0008, /* Transmit Error */
+ Tu = 0x0010, /* Transmit Buffer Underflow */
+ Ru = 0x0020, /* Receive Buffer Link Error */
+ Be = 0x0040, /* PCI Bus Error */
+ Cnt = 0x0080, /* Counter Overflow */
+ Eri = 0x0100, /* Early Receive Interrupt */
+ Udfi = 0x0200, /* Tx FIFO Underflow */
+ Ovfi = 0x0400, /* Receive FIFO Overflow */
+ Pktrace = 0x0800, /* Hmmm... */
+ Norbf = 0x1000, /* No Receive Buffers */
+ Abti = 0x2000, /* Transmission Abort */
+ Srci = 0x4000, /* Port State Change */
+ Geni = 0x8000, /* General Purpose Interrupt */
+};
+
+enum { /* Phyadr */
+ PhyadMASK = 0x1F, /* PHY Address */
+ PhyadSHIFT = 0,
+ Mfdc = 0x20, /* Accelerate MDC Speed */
+ Mpo0 = 0x40, /* MII Polling Timer Interval */
+ Mpo1 = 0x80,
+};
+
+enum { /* Bcr0 */
+ DmaMASK = 0x07, /* DMA Length */
+ DmaSHIFT = 0,
+ Dma32 = 0<<DmaSHIFT,
+ Dma64 = 1<<DmaSHIFT,
+ Dma128 = 2<<DmaSHIFT,
+ Dma256 = 3<<DmaSHIFT,
+ Dma512 = 4<<DmaSHIFT,
+ Dma1024 = 5<<DmaSHIFT,
+ DmaSAF = 7<<DmaSHIFT,
+ CrftMASK = 0x38, /* Rx FIFO Threshold */
+ CrftSHIFT = 3,
+ Crft64 = 1<<CrftSHIFT,
+ Crft128 = 2<<CrftSHIFT,
+ Crft256 = 3<<CrftSHIFT,
+ Crft512 = 4<<CrftSHIFT,
+ Crft1024 = 5<<CrftSHIFT,
+ CrftSAF = 7<<CrftSHIFT,
+ Extled = 0x40, /* Extra LED Support Control */
+ Med2 = 0x80, /* Medium Select Control */
+};
+
+enum { /* Bcr1 */
+ PotMASK = 0x07, /* Polling Timer Interval */
+ PotSHIFT = 0,
+ CtftMASK = 0x38, /* Tx FIFO Threshold */
+ CtftSHIFT = 3,
+ Ctft64 = 1<<CtftSHIFT,
+ Ctft128 = 2<<CtftSHIFT,
+ Ctft256 = 3<<CtftSHIFT,
+ Ctft512 = 4<<CtftSHIFT,
+ Ctft1024 = 5<<CtftSHIFT,
+ CtftSAF = 7<<CtftSHIFT,
+};
+
+enum { /* Miicr */
+ Mdc = 0x01, /* Clock */
+ Mdi = 0x02, /* Data In */
+ Mdo = 0x04, /* Data Out */
+ Mout = 0x08, /* Output Enable */
+ Mdpm = 0x10, /* Direct Program Mode Enable */
+ Wcmd = 0x20, /* Write Enable */
+ Rcmd = 0x40, /* Read Enable */
+ Mauto = 0x80, /* Auto Polling Enable */
+};
+
+enum { /* Miiadr */
+ MadMASK = 0x1F, /* MII Port Address */
+ MadSHIFT = 0,
+ Mdone = 0x20, /* Accelerate MDC Speed */
+ Msrcen = 0x40, /* MII Polling Timer Interval */
+ Midle = 0x80,
+};
+
+enum { /* Eecsr */
+ Edo = 0x01, /* Data Out */
+ Edi = 0x02, /* Data In */
+ Eck = 0x04, /* Clock */
+ Ecs = 0x08, /* Chip Select */
+ Dpm = 0x10, /* Direct Program Mode Enable */
+ Autold = 0x20, /* Dynamic Reload */
+ Embp = 0x40, /* Embedded Program Enable */
+ Eepr = 0x80, /* Programmed */
+};
+
+/*
+ * Ring descriptor. The space allocated for each
+ * of these will be rounded up to a cache-line boundary.
+ * The first 4 elements are known to the hardware.
+ */
+typedef struct Ds Ds;
+typedef struct Ds {
+ u32int status;
+ u32int control;
+ u32int addr;
+ u32int branch;
+
+ Block* bp;
+ Ds* next;
+ Ds* prev;
+} Ds;
+
+enum { /* Rx Ds status */
+ Rerr = 0x00000001, /* Buff|Rxserr|Fov|Fae|Crc */
+ Crc = 0x00000002, /* CRC Error */
+ Fae = 0x00000004, /* Frame Alignment Error */
+ Fov = 0x00000008, /* FIFO Overflow */
+ Long = 0x00000010, /* A Long Packet */
+ Runt = 0x00000020, /* A Runt Packet */
+ Rxserr = 0x00000040, /* System Error */
+ Buff = 0x00000080, /* Buffer Underflow Error */
+ Rxedp = 0x00000100, /* End of Packet Buffer */
+ Rxstp = 0x00000200, /* Packet Start */
+ Chn = 0x00000400, /* Chain Buffer */
+ Phy = 0x00000800, /* Physical Address Packet */
+ Bar = 0x00001000, /* Broadcast Packet */
+ Mar = 0x00002000, /* Multicast Packet */
+ Rxok = 0x00008000, /* Packet Received OK */
+ LengthMASK = 0x07FF0000, /* Received Packet Length */
+ LengthSHIFT = 16,
+
+ Own = 0x80000000, /* Descriptor Owned by NIC */
+};
+
+enum { /* Rx Ds control */
+ RbsizeMASK = 0x000007FF, /* Receive Buffer Size */
+ RbsizeSHIFT = 0,
+ Tag = 0x00010000, /* Receive a Tagged Packet */
+ Udpkt = 0x00020000, /* Receive a UDP Packet */
+ Tcpkt = 0x00040000, /* Receive a TCP Packet */
+ Ipkt = 0x00080000, /* Receive an IP Packet */
+ Tuok = 0x00100000, /* TCP/UDP Checksum OK */
+ Ipok = 0x00200000, /* IP Checksum OK */
+ Snaptag = 0x00400000, /* Snap Packet + 802.1q Tag */
+ Rxlerr = 0x00800000, /* Receive Length Check Error */
+ IpktMASK = 0xff000000, /* Interesting Packet */
+ IpktSHIFT = 24,
+};
+
+enum { /* Tx Ds status */
+ NcrMASK = 0x0000000F, /* Collision Retry Count */
+ NcrSHIFT = 0,
+ Cols = 0x00000010, /* Experienced Collisions */
+ Cdh = 0x00000080, /* CD Heartbeat */
+ Abt = 0x00000100, /* Aborted after Excessive Collisions */
+ Owc = 0x00000200, /* Out of Window Collision */
+ Crs = 0x00000400, /* Carrier Sense Lost */
+ Udf = 0x00000800, /* FIFO Underflow */
+ Tbuff = 0x00001000, /* Invalid Td */
+ Txserr = 0x00002000, /* System Error */
+ Terr = 0x00008000, /* Excessive Collisions */
+};
+
+enum { /* Tx Ds control */
+ TbsMASK = 0x000007FF, /* Tx Buffer Size */
+ TbsSHIFT = 0,
+ Chain = 0x00008000, /* Chain Buffer */
+ Crcdisable = 0x00010000, /* Disable CRC generation */
+ Stp = 0x00200000, /* Start of Packet */
+ Edp = 0x00400000, /* End of Packet */
+ Ic = 0x00800000, /* Interrupt Control */
+};
+
+enum { /* Tx Ds branch */
+ Tdctl = 0x00000001, /* No Interrupt Generated */
+};
+
+enum {
+ Nrd = 196,
+ Ntd = 128,
+ Crcsz = 4,
+ Bslop = 48,
+ Rdbsz = ETHERMAXTU+Crcsz+Bslop,
+
+ Nrxstats = 8,
+ Ntxstats = 9,
+
+ Txcopy = 128,
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Ctlr {
+ int port;
+ Pcidev* pcidev;
+ Ctlr* next;
+ int active;
+ int id;
+ uchar par[Eaddrlen];
+
+ QLock alock; /* attach */
+ void* alloc; /* descriptors, etc. */
+ int cls; /* alignment */
+ int nrd;
+ int ntd;
+
+ Ds* rd;
+ Ds* rdh;
+
+ Lock tlock;
+ Ds* td;
+ Ds* tdh;
+ Ds* tdt;
+ int tdused;
+
+ Lock clock; /* */
+ int cr;
+ int imr;
+ int tft; /* Tx threshold */
+
+ Mii* mii;
+ Rendez lrendez;
+ int lwakeup;
+
+ uint rxstats[Nrxstats]; /* statistics */
+ uint txstats[Ntxstats];
+ ulong totalt;
+ uint intr;
+ uint lintr;
+ uint lsleep;
+ uint rintr;
+ uint tintr;
+ uint txdw;
+ int tdumax;
+
+ uint abt;
+ uint tbuff;
+ uint udf;
+
+ uint abti;
+ uint udfi;
+ uint tu;
+
+ uint tuok;
+ uint ipok;
+} Ctlr;
+
+static Ctlr* vt6105Mctlrhead;
+static Ctlr* vt6105Mctlrtail;
+
+#define csr8r(c, r) (inb((c)->port+(r)))
+#define csr16r(c, r) (ins((c)->port+(r)))
+#define csr32r(c, r) (inl((c)->port+(r)))
+#define csr8w(c, r, b) (outb((c)->port+(r), (int)(b)))
+#define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w)))
+#define csr32w(c, r, w) (outl((c)->port+(r), (ulong)(w)))
+
+static Lock vt6105Mrblock; /* receive Block freelist */
+static Block* vt6105Mrbpool;
+static uint vt6105Mrbpoolsz;
+
+typedef struct Regs Regs;
+typedef struct Regs {
+ char* name;
+ int offset;
+ int size;
+} Regs;
+
+static Regs regs[] = {
+// "Par0", Par0, 1,
+// "Par1", Par0+1, 1,
+// "Par2", Par0+2, 1,
+// "Par3", Par0+3, 1,
+// "Par4", Par0+4, 1,
+// "Par5", Par0+5, 1,
+ "Rcr", Rcr, 1,
+ "Tcr", Tcr, 1,
+ "Cr0", Cr, 1,
+ "Cr1", Cr+1, 1,
+ "Isr0", Isr, 1,
+ "Isr1", Isr+1, 1,
+ "Imr0", Imr, 1,
+ "Imr1", Imr+1, 1,
+// "Mcfilt0", Mcfilt0,4,
+// "Mcfilt1", Mcfilt1,4,
+// "Rxdaddr", Rxdaddr,4,
+// "Txdaddr", Txdaddr,4,
+ "Phyadr", Phyadr, 1,
+ "Miisr", Miisr, 1,
+ "Bcr0", Bcr0, 1,
+ "Bcr1", Bcr1, 1,
+ "Miicr", Miicr, 1,
+ "Miiadr", Miiadr, 1,
+// "Miidata", Miidata,2,
+ "Eecsr", Eecsr, 1,
+ "CfgA", CfgA, 1,
+ "CfgB", CfgB, 1,
+ "CfgC", CfgC, 1,
+ "CfgD", CfgD, 1,
+ "Cr0", Cr0, 1,
+ "Cr1", Cr1, 1,
+ "Pmcc", Pmcc, 1,
+ "Stickhw", Stickhw,1,
+ "Misr", Misr, 1,
+ "Mimr", Mimr, 1,
+ nil,
+};
+
+static char* rxstats[Nrxstats] = {
+ "Receiver Error",
+ "CRC Error",
+ "Frame Alignment Error",
+ "FIFO Overflow",
+ "Long Packet",
+ "Runt Packet",
+ "System Error",
+ "Buffer Underflow Error",
+};
+static char* txstats[Ntxstats] = {
+ "Aborted after Excessive Collisions",
+ "Out of Window Collision Seen",
+ "Carrier Sense Lost",
+ "FIFO Underflow",
+ "Invalid Td",
+ "System Error",
+ nil,
+ "Excessive Collisions",
+};
+
+static long
+vt6105Mifstat(Ether* edev, void* a, long n, ulong offset)
+{
+ int i, r;
+ Ctlr *ctlr;
+ char *alloc, *e, *p;
+
+ ctlr = edev->ctlr;
+
+ alloc = malloc(READSTR);
+ p = alloc;
+ e = p + READSTR;
+ for(i = 0; i < Nrxstats; i++){
+ p = seprint(p, e, "%s: %ud\n", rxstats[i], ctlr->rxstats[i]);
+ }
+ for(i = 0; i < Ntxstats; i++){
+ if(txstats[i] == nil)
+ continue;
+ p = seprint(p, e, "%s: %ud\n", txstats[i], ctlr->txstats[i]);
+ }
+ p = seprint(p, e, "cls: %ud\n", ctlr->cls);
+ p = seprint(p, e, "intr: %ud\n", ctlr->intr);
+ p = seprint(p, e, "lintr: %ud\n", ctlr->lintr);
+ p = seprint(p, e, "lsleep: %ud\n", ctlr->lsleep);
+ p = seprint(p, e, "rintr: %ud\n", ctlr->rintr);
+ p = seprint(p, e, "tintr: %ud\n", ctlr->tintr);
+ p = seprint(p, e, "txdw: %ud\n", ctlr->txdw);
+ p = seprint(p, e, "tdumax: %ud\n", ctlr->tdumax);
+ p = seprint(p, e, "tft: %ud\n", ctlr->tft);
+
+ p = seprint(p, e, "abt: %ud\n", ctlr->abt);
+ p = seprint(p, e, "tbuff: %ud\n", ctlr->tbuff);
+ p = seprint(p, e, "udf: %ud\n", ctlr->udf);
+ p = seprint(p, e, "abti: %ud\n", ctlr->abti);
+ p = seprint(p, e, "udfi: %ud\n", ctlr->udfi);
+ p = seprint(p, e, "tu: %ud\n", ctlr->tu);
+
+ p = seprint(p, e, "tuok: %ud\n", ctlr->tuok);
+ p = seprint(p, e, "ipok: %ud\n", ctlr->ipok);
+
+ p = seprint(p, e, "rbpoolsz: %ud\n", vt6105Mrbpoolsz);
+ p = seprint(p, e, "totalt: %uld\n", ctlr->totalt);
+
+ for(i = 0; regs[i].name != nil; i++){
+ p = seprint(p, e, "%s: %2.2x\n",
+ regs[i].name, csr8r(ctlr, regs[i].offset));
+ }
+
+ if(ctlr->mii != nil && ctlr->mii->curphy != nil){
+ p = seprint(p, e, "phy: ");
+ for(i = 0; i < NMiiPhyr; i++){
+ if(i && ((i & 0x07) == 0))
+ p = seprint(p, e, "\n ");
+ r = miimir(ctlr->mii, i);
+ p = seprint(p, e, " %4.4uX", r);
+ }
+ seprint(p, e, "\n");
+ }
+
+ n = readstr(offset, a, n, alloc);
+ free(alloc);
+
+ return n;
+}
+
+static void
+vt6105Mpromiscuous(void* arg, int on)
+{
+ int rcr;
+ Ctlr *ctlr;
+ Ether *edev;
+
+ edev = arg;
+ ctlr = edev->ctlr;
+ rcr = csr8r(ctlr, Rcr);
+ if(on)
+ rcr |= Prom;
+ else
+ rcr &= ~Prom;
+ csr8w(ctlr, Rcr, rcr);
+}
+
+static void
+vt6105Mmulticast(void* arg, uchar* addr, int on)
+{
+ /*
+ * For now Am is set in Rcr.
+ * Will need to interlock with promiscuous
+ * when this gets filled in.
+ */
+ USED(arg, addr, on);
+}
+
+static int
+vt6105Mwakeup(void* v)
+{
+ return *((int*)v) != 0;
+}
+
+static void
+vt6105Mimr(Ctlr* ctlr, int imr)
+{
+ ilock(&ctlr->clock);
+ ctlr->imr |= imr;
+ csr16w(ctlr, Imr, ctlr->imr);
+ iunlock(&ctlr->clock);
+}
+
+static void
+vt6105Mlproc(void* arg)
+{
+ Ctlr *ctlr;
+ Ether *edev;
+ MiiPhy *phy;
+
+ edev = arg;
+ ctlr = edev->ctlr;
+ for(;;){
+ if(ctlr->mii == nil || ctlr->mii->curphy == nil)
+ break;
+ if(miistatus(ctlr->mii) < 0)
+ goto enable;
+
+ phy = ctlr->mii->curphy;
+ ilock(&ctlr->clock);
+ csr16w(ctlr, Cr, ctlr->cr & ~(Txon|Rxon));
+ if(phy->fd)
+ ctlr->cr |= Fdx;
+ else
+ ctlr->cr &= ~Fdx;
+ csr16w(ctlr, Cr, ctlr->cr);
+ iunlock(&ctlr->clock);
+enable:
+ ctlr->lwakeup = 0;
+ vt6105Mimr(ctlr, Srci);
+
+ ctlr->lsleep++;
+ sleep(&ctlr->lrendez, vt6105Mwakeup, &ctlr->lwakeup);
+
+ }
+ pexit("vt6105Mlproc: done", 1);
+}
+
+static void
+vt6105Mrbfree(Block* bp)
+{
+ bp->rp = bp->lim - (Rdbsz+3);
+ bp->wp = bp->rp;
+ bp->flag &= ~(Bipck | Budpck | Btcpck | Bpktck);
+
+ ilock(&vt6105Mrblock);
+ bp->next = vt6105Mrbpool;
+ vt6105Mrbpool = bp;
+ iunlock(&vt6105Mrblock);
+}
+
+static Block*
+vt6105Mrballoc(void)
+{
+ Block *bp;
+
+ ilock(&vt6105Mrblock);
+ if((bp = vt6105Mrbpool) != nil){
+ vt6105Mrbpool = bp->next;
+ bp->next = nil;
+ _xinc(&bp->ref); /* prevent bp from being freed */
+ }
+ iunlock(&vt6105Mrblock);
+
+ if(bp == nil && (bp = iallocb(Rdbsz+3)) != nil){
+ bp->free = vt6105Mrbfree;
+ vt6105Mrbpoolsz++;
+ }
+ return bp;
+}
+
+static void
+vt6105Mattach(Ether* edev)
+{
+ Ctlr *ctlr;
+// MiiPhy *phy;
+ uchar *alloc;
+ Ds *ds, *prev;
+ int dsz, i, timeo;
+ char name[KNAMELEN];
+
+ ctlr = edev->ctlr;
+ qlock(&ctlr->alock);
+ if(ctlr->alloc != nil){
+ qunlock(&ctlr->alock);
+ return;
+ }
+
+ /*
+ * Descriptor space.
+ * Receive descriptors should all be aligned on a 4-byte boundary,
+ * but try to do cache-line alignment.
+ */
+ ctlr->nrd = Nrd;
+ ctlr->ntd = Ntd;
+ dsz = ROUNDUP(sizeof(Ds), ctlr->cls);
+ alloc = mallocalign((ctlr->nrd+ctlr->ntd)*dsz, dsz, 0, 0);
+ if(alloc == nil){
+ qunlock(&ctlr->alock);
+ return;
+ }
+ ctlr->alloc = alloc;
+
+ ctlr->rd = (Ds*)alloc;
+
+ if(waserror()){
+ ds = ctlr->rd;
+ for(i = 0; i < ctlr->nrd; i++){
+ if(ds->bp != nil){
+ freeb(ds->bp);
+ ds->bp = nil;
+ }
+ if((ds = ds->next) == nil)
+ break;
+ }
+ free(ctlr->alloc);
+ ctlr->alloc = nil;
+ qunlock(&ctlr->alock);
+ nexterror();
+ }
+
+ prev = (Ds*)(alloc + (ctlr->nrd-1)*dsz);
+ for(i = 0; i < ctlr->nrd; i++){
+ ds = (Ds*)alloc;
+ alloc += dsz;
+
+ ds->control = Ipkt|Tcpkt|Udpkt|Rdbsz;
+ ds->branch = PCIWADDR(alloc);
+
+ ds->bp = vt6105Mrballoc();
+ if(ds->bp == nil)
+ error("vt6105M: can't allocate receive ring\n");
+ ds->bp->rp = (uchar*)ROUNDUP((ulong)ds->bp->rp, 4);
+ ds->addr = PCIWADDR(ds->bp->rp);
+
+ ds->next = (Ds*)alloc;
+ ds->prev = prev;
+ prev = ds;
+
+ ds->status = Own;
+ }
+ prev->branch = 0;
+ prev->next = ctlr->rd;
+ prev->status = 0;
+ ctlr->rdh = ctlr->rd;
+
+ ctlr->td = (Ds*)alloc;
+ prev = (Ds*)(alloc + (ctlr->ntd-1)*dsz);
+ for(i = 0; i < ctlr->ntd; i++){
+ ds = (Ds*)alloc;
+ alloc += dsz;
+
+ ds->next = (Ds*)alloc;
+ ds->prev = prev;
+ prev = ds;
+ }
+ prev->next = ctlr->td;
+ ctlr->tdh = ctlr->tdt = ctlr->td;
+ ctlr->tdused = 0;
+
+ ctlr->cr = Dpoll|Rdmd/*|Txon|Rxon*/|Strt;
+ /*Srci|Abti|Norbf|Pktrace|Ovfi|Udfi|Be|Ru|Tu|Txe|Rxe|Ptx|Prx*/
+ ctlr->imr = Abti|Norbf|Pktrace|Ovfi|Udfi|Be|Ru|Tu|Txe|Rxe|Ptx|Prx;
+
+ ilock(&ctlr->clock);
+ csr32w(ctlr, Rxdaddr, PCIWADDR(ctlr->rd));
+ csr32w(ctlr, Txdaddr, PCIWADDR(ctlr->td));
+ csr16w(ctlr, Isr, ~0);
+ csr16w(ctlr, Imr, ctlr->imr);
+ csr16w(ctlr, Cr, ctlr->cr);
+ iunlock(&ctlr->clock);
+
+ /*
+ * Wait for link to be ready.
+ */
+ for(timeo = 0; timeo < 350; timeo++){
+ if(miistatus(ctlr->mii) == 0)
+ break;
+ tsleep(&up->sleep, return0, 0, 10);
+ }
+// phy = ctlr->mii->curphy;
+// print("%s: speed %d fd %d link %d rfc %d tfc %d\n",
+// edev->name, phy->speed, phy->fd, phy->link, phy->rfc, phy->tfc);
+
+ ilock(&ctlr->clock);
+ ctlr->cr |= Txon|Rxon;
+ csr16w(ctlr, Cr, ctlr->cr);
+ iunlock(&ctlr->clock);
+
+ snprint(name, KNAMELEN, "#l%dlproc", edev->ctlrno);
+ kproc(name, vt6105Mlproc, edev);
+
+ qunlock(&ctlr->alock);
+ poperror();
+}
+
+static void
+vt6105Mtransmit(Ether* edev)
+{
+ Block *bp;
+ Ctlr *ctlr;
+ Ds *ds, *next;
+ int control, i, size, tdused, timeo;
+ long t;
+
+ ctlr = edev->ctlr;
+
+ ilock(&ctlr->tlock);
+ t = lcycles();
+
+ /*
+ * Free any completed packets
+ */
+ ds = ctlr->tdh;
+ for(tdused = ctlr->tdused; tdused > 0; tdused--){
+ /*
+ * For some errors the chip will turn the Tx engine
+ * off. Wait for that to happen.
+ * Could reset and re-init the chip here if it doesn't
+ * play fair.
+ * To do: adjust Tx FIFO threshold on underflow.
+ */
+ if(ds->status & (Abt|Tbuff|Udf)){
+ if(ds->status & Abt)
+ ctlr->abt++;
+ if(ds->status & Tbuff)
+ ctlr->tbuff++;
+ if(ds->status & Udf)
+ ctlr->udf++;
+ for(timeo = 0; timeo < 1000; timeo++){
+ if(!(csr16r(ctlr, Cr) & Txon))
+ break;
+ microdelay(1);
+ }
+ ds->status = Own;
+ csr32w(ctlr, Txdaddr, PCIWADDR(ds));
+ }
+
+ if(ds->status & Own)
+ break;
+ ds->addr = 0;
+ ds->branch = 0;
+
+ if(ds->bp != nil){
+ freeb(ds->bp);
+ ds->bp = nil;
+ }
+ for(i = 0; i < Ntxstats-1; i++){
+ if(ds->status & (1<<i))
+ ctlr->txstats[i]++;
+ }
+ ctlr->txstats[i] += (ds->status & NcrMASK)>>NcrSHIFT;
+
+ ds = ds->next;
+ }
+ ctlr->tdh = ds;
+
+ /*
+ * Try to fill the ring back up.
+ */
+ ds = ctlr->tdt;
+ while(tdused < ctlr->ntd-2){
+ if((bp = qget(edev->oq)) == nil)
+ break;
+ tdused++;
+
+ size = BLEN(bp);
+
+ next = ds->next;
+ ds->branch = PCIWADDR(ds->next)|Tdctl;
+
+ ds->bp = bp;
+ ds->addr = PCIWADDR(bp->rp);
+ control = Edp|Stp|((size<<TbsSHIFT) & TbsMASK);
+
+ ds->control = control;
+ if(tdused >= ctlr->ntd-2){
+ ctlr->txdw++;
+ ds->branch &= ~Tdctl;
+ }
+ coherence();
+ ds->status = Own;
+
+ ds = next;
+ }
+ ctlr->tdt = ds;
+ ctlr->tdused = tdused;
+ if(ctlr->tdused){
+ csr16w(ctlr, Cr, Tdmd|ctlr->cr);
+ if(tdused > ctlr->tdumax)
+ ctlr->tdumax = tdused;
+ }
+
+ ctlr->totalt += lcycles() - t;
+ iunlock(&ctlr->tlock);
+}
+
+static void
+vt6105Mreceive(Ether* edev)
+{
+ Ds *ds;
+ Block *bp;
+ Ctlr *ctlr;
+ int i, len;
+
+ ctlr = edev->ctlr;
+
+ ds = ctlr->rdh;
+ while(!(ds->status & Own) && ds->status != 0){
+ /*
+ * Can Long packets be received OK?
+ * What happens to the Rxok bit?
+ */
+ if(ds->status & Rerr){
+ for(i = 0; i < Nrxstats; i++){
+ if(ds->status & (1<<i))
+ ctlr->rxstats[i]++;
+ }
+ }
+ else if(bp = vt6105Mrballoc()){
+ if(ds->control & Tuok){
+ ds->bp->flag |= Btcpck|Budpck;
+ ctlr->tuok++;
+ }
+ if(ds->control & Ipok){
+ ds->bp->flag |= Bipck;
+ ctlr->ipok++;
+ }
+ len = ((ds->status & LengthMASK)>>LengthSHIFT)-4;
+ ds->bp->wp = ds->bp->rp+len;
+ etheriq(edev, ds->bp, 1);
+ bp->rp = (uchar*)ROUNDUP((ulong)bp->rp, 4);
+ ds->addr = PCIWADDR(bp->rp);
+ ds->bp = bp;
+ }
+ ds->control = Ipkt|Tcpkt|Udpkt|Rdbsz;
+ ds->branch = 0;
+ ds->status = 0;
+
+ ds->prev->branch = PCIWADDR(ds);
+ coherence();
+ ds->prev->status = Own;
+
+ ds = ds->next;
+ }
+ ctlr->rdh = ds;
+
+ csr16w(ctlr, Cr, ctlr->cr);
+}
+
+static void
+vt6105Minterrupt(Ureg*, void* arg)
+{
+ Ctlr *ctlr;
+ Ether *edev;
+ int imr, isr, r, timeo;
+ long t;
+
+ edev = arg;
+ ctlr = edev->ctlr;
+
+ ilock(&ctlr->clock);
+ t = lcycles();
+
+ csr16w(ctlr, Imr, 0);
+ imr = ctlr->imr;
+ ctlr->intr++;
+ for(;;){
+ if((isr = csr16r(ctlr, Isr)) != 0)
+ csr16w(ctlr, Isr, isr);
+ if((isr & ctlr->imr) == 0)
+ break;
+
+ if(isr & Srci){
+ imr &= ~Srci;
+ ctlr->lwakeup = isr & Srci;
+ wakeup(&ctlr->lrendez);
+ isr &= ~Srci;
+ ctlr->lintr++;
+ }
+ if(isr & (Norbf|Pktrace|Ovfi|Ru|Rxe|Prx)){
+ vt6105Mreceive(edev);
+ isr &= ~(Norbf|Pktrace|Ovfi|Ru|Rxe|Prx);
+ ctlr->rintr++;
+ }
+ if(isr & (Abti|Udfi|Tu|Txe|Ptx)){
+ if(isr & (Abti|Udfi|Tu)){
+ if(isr & Abti)
+ ctlr->abti++;
+ if(isr & Udfi)
+ ctlr->udfi++;
+ if(isr & Tu)
+ ctlr->tu++;
+ for(timeo = 0; timeo < 1000; timeo++){
+ if(!(csr16r(ctlr, Cr) & Txon))
+ break;
+ microdelay(1);
+ }
+
+ if((isr & Udfi) && ctlr->tft < CtftSAF){
+ ctlr->tft += 1<<CtftSHIFT;
+ r = csr8r(ctlr, Bcr1) & ~CtftMASK;
+ csr8w(ctlr, Bcr1, r|ctlr->tft);
+ }
+ }
+
+
+ ctlr->totalt += lcycles() - t;
+ vt6105Mtransmit(edev);
+ t = lcycles();
+ isr &= ~(Abti|Udfi|Tu|Txe|Ptx);
+ ctlr->tintr++;
+ }
+ if(isr)
+ panic("vt6105M: isr %4.4uX\n", isr);
+ }
+ ctlr->imr = imr;
+ csr16w(ctlr, Imr, ctlr->imr);
+
+ ctlr->totalt += lcycles() - t;
+ iunlock(&ctlr->clock);
+}
+
+static int
+vt6105Mmiimicmd(Mii* mii, int pa, int ra, int cmd, int data)
+{
+ Ctlr *ctlr;
+ int r, timeo;
+
+ ctlr = mii->ctlr;
+
+ csr8w(ctlr, Miicr, 0);
+ r = csr8r(ctlr, Phyadr);
+ csr8w(ctlr, Phyadr, (r & ~PhyadMASK)|pa);
+ csr8w(ctlr, Phyadr, pa);
+ csr8w(ctlr, Miiadr, ra);
+ if(cmd == Wcmd)
+ csr16w(ctlr, Miidata, data);
+ csr8w(ctlr, Miicr, cmd);
+
+ for(timeo = 0; timeo < 10000; timeo++){
+ if(!(csr8r(ctlr, Miicr) & cmd))
+ break;
+ microdelay(1);
+ }
+ if(timeo >= 10000)
+ return -1;
+
+ if(cmd == Wcmd)
+ return 0;
+ return csr16r(ctlr, Miidata);
+}
+
+static int
+vt6105Mmiimir(Mii* mii, int pa, int ra)
+{
+ return vt6105Mmiimicmd(mii, pa, ra, Rcmd, 0);
+}
+
+static int
+vt6105Mmiimiw(Mii* mii, int pa, int ra, int data)
+{
+ return vt6105Mmiimicmd(mii, pa, ra, Wcmd, data);
+}
+
+static int
+vt6105Mdetach(Ctlr* ctlr)
+{
+ int revid, timeo;
+
+ /*
+ * Reset power management registers.
+ */
+ revid = pcicfgr8(ctlr->pcidev, PciRID);
+ if(revid >= 0x40){
+ /* Set power state D0. */
+ csr8w(ctlr, Stickhw, csr8r(ctlr, Stickhw) & 0xFC);
+
+ /* Disable force PME-enable. */
+ csr8w(ctlr, Wolcgclr, 0x80);
+
+ /* Clear WOL config and status bits. */
+ csr8w(ctlr, Wolcrclr, 0xFF);
+ csr8w(ctlr, Pwrcsrclr, 0xFF);
+ }
+
+ /*
+ * Soft reset the controller.
+ */
+ csr16w(ctlr, Cr, Stop);
+ csr16w(ctlr, Cr, Stop|Sfrst);
+ for(timeo = 0; timeo < 10000; timeo++){
+ if(!(csr16r(ctlr, Cr) & Sfrst))
+ break;
+ microdelay(1);
+ }
+ if(timeo >= 1000)
+ return -1;
+
+ return 0;
+}
+
+static int
+vt6105Mreset(Ctlr* ctlr)
+{
+ MiiPhy *phy;
+ int i, r, timeo;
+
+ if(vt6105Mdetach(ctlr) < 0)
+ return -1;
+
+ /*
+ * Load the MAC address into the PAR[01]
+ * registers.
+ */
+ r = csr8r(ctlr, Eecsr);
+ csr8w(ctlr, Eecsr, Autold|r);
+ for(timeo = 0; timeo < 100; timeo++){
+ if(!(csr8r(ctlr, Cr) & Autold))
+ break;
+ microdelay(1);
+ }
+ if(timeo >= 100)
+ return -1;
+
+ for(i = 0; i < Eaddrlen; i++)
+ ctlr->par[i] = csr8r(ctlr, Par0+i);
+
+ /*
+ * Configure DMA and Rx/Tx thresholds.
+ * If the Rx/Tx threshold bits in Bcr[01] are 0 then
+ * the thresholds are determined by Rcr/Tcr.
+ */
+ r = csr8r(ctlr, Bcr0) & ~(CrftMASK|DmaMASK);
+ csr8w(ctlr, Bcr0, r|Crft128|DmaSAF);
+ r = csr8r(ctlr, Bcr1) & ~CtftMASK;
+ csr8w(ctlr, Bcr1, r|ctlr->tft);
+
+ r = csr8r(ctlr, Rcr) & ~(RrftMASK|Prom|Ar|Sep);
+ csr8w(ctlr, Rcr, r|Ab|Am);
+ csr32w(ctlr, Mcfilt0, ~0UL); /* accept all multicast */
+ csr32w(ctlr, Mcfilt1, ~0UL);
+
+ r = csr8r(ctlr, Tcr) & ~(RtsfMASK|Ofset|Lb1|Lb0);
+ csr8w(ctlr, Tcr, r);
+
+ /*
+ * Link management.
+ */
+ if((ctlr->mii = malloc(sizeof(Mii))) == nil)
+ return -1;
+ ctlr->mii->mir = vt6105Mmiimir;
+ ctlr->mii->miw = vt6105Mmiimiw;
+ ctlr->mii->ctlr = ctlr;
+
+ if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){
+ free(ctlr->mii);
+ ctlr->mii = nil;
+ return -1;
+ }
+// print("oui %X phyno %d\n", phy->oui, phy->phyno);
+ USED(phy);
+
+ if(miistatus(ctlr->mii) < 0){
+// miireset(ctlr->mii);
+ miiane(ctlr->mii, ~0, ~0, ~0);
+ }
+
+ return 0;
+}
+
+static void
+vt6105Mpci(void)
+{
+ Pcidev *p;
+ Ctlr *ctlr;
+ int cls, port;
+
+ 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 (0x3053<<16)|0x1106: /* Rhine III-M vt6105M */
+ break;
+ }
+
+ port = p->mem[0].bar & ~0x01;
+ if(ioalloc(port, p->mem[0].size, 0, "vt6105M") < 0){
+ print("vt6105M: port 0x%uX in use\n", port);
+ continue;
+ }
+ ctlr = malloc(sizeof(Ctlr));
+ ctlr->port = port;
+ ctlr->pcidev = p;
+ ctlr->id = (p->did<<16)|p->vid;
+ if((cls = pcicfgr8(p, PciCLS)) == 0 || cls == 0xFF)
+ cls = 0x10;
+ ctlr->cls = cls*4;
+ if(ctlr->cls < sizeof(Ds)){
+ print("vt6105M: cls %d < sizeof(Ds)\n", ctlr->cls);
+ iofree(port);
+ free(ctlr);
+ continue;
+ }
+ ctlr->tft = CtftSAF;
+
+ if(vt6105Mreset(ctlr)){
+ iofree(port);
+ free(ctlr);
+ continue;
+ }
+ pcisetbme(p);
+
+ if(vt6105Mctlrhead != nil)
+ vt6105Mctlrtail->next = ctlr;
+ else
+ vt6105Mctlrhead = ctlr;
+ vt6105Mctlrtail = ctlr;
+ }
+}
+
+static int
+vt6105Mpnp(Ether* edev)
+{
+ Ctlr *ctlr;
+
+ if(vt6105Mctlrhead == nil)
+ vt6105Mpci();
+
+ /*
+ * Any adapter matches if no edev->port is supplied,
+ * otherwise the ports must match.
+ */
+ for(ctlr = vt6105Mctlrhead; 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;
+ /*
+ * Set to 1000Mb/s to fool the bsz calculation. We need
+ * something better, though.
+ */
+ edev->mbps = 1000;
+ memmove(edev->ea, ctlr->par, Eaddrlen);
+
+ /*
+ * Linkage to the generic ethernet driver.
+ */
+ edev->attach = vt6105Mattach;
+ edev->transmit = vt6105Mtransmit;
+ edev->interrupt = vt6105Minterrupt;
+ edev->ifstat = vt6105Mifstat;
+ edev->ctl = nil;
+
+ edev->arg = edev;
+ edev->promiscuous = vt6105Mpromiscuous;
+ edev->multicast = vt6105Mmulticast;
+
+ edev->maxmtu = ETHERMAXTU+Bslop;
+
+ return 0;
+}
+
+void
+ethervt6105mlink(void)
+{
+ addethercard("vt6105M", vt6105Mpnp);
+}