@@ 6,14 6,9 @@
* The datasheet is not very clear about running on a big-endian system
* and this driver assumes little-endian throughout.
* To do:
- * auto-negotiation and flow control setup and enable
* GMII/MII
* receive tuning
- * check recovery from receive no buffers condition
* transmit tuning
- * automatic ett adjustment
- * chip-specific offload...
- * check PCI MWI and CLS initialisation
*/
#include "u.h"
#include "../port/lib.h"
@@ 41,12 36,19 @@ enum {
Imc = 0x000000D8, /* Interrupt mask Clear */
Rctl = 0x00000100, /* Receive Control */
Fcttv = 0x00000170, /* Flow Control Transmit Timer Value */
+ Txcw = 0x00000178, /* Transmit configuration word reg. */
+ Rxcw = 0x00000180, /* Receive configuration word reg. */
Tctl = 0x00000400, /* Transmit Control */
Tipg = 0x00000410, /* Transmit IPG */
Tbt = 0x00000448, /* Transmit Burst Timer */
Ait = 0x00000458, /* Adaptive IFS Throttle */
Fcrtl = 0x00002160, /* Flow Control RX Threshold Low */
Fcrth = 0x00002168, /* Flow Control Rx Threshold High */
+ Rdfh = 0x00002410, /* Receive data fifo head */
+ Rdft = 0x00002418, /* Receive data fifo tail */
+ Rdfhs = 0x00002420, /* Receive data fifo head saved */
+ Rdfts = 0x00002428, /* Receive data fifo tail saved */
+ Rdfpc = 0x00002430, /* Receive data fifo packet count */
Rdbal = 0x00002800, /* Rdesc Base Address Low */
Rdbah = 0x00002804, /* Rdesc Base Address High */
Rdlen = 0x00002808, /* Receive Descriptor Length */
@@ 56,6 58,11 @@ enum {
Rxdctl = 0x00002828, /* Receive Descriptor Control */
Txdmac = 0x00003000, /* Transfer DMA Control */
Ett = 0x00003008, /* Early Transmit Control */
+ Tdfh = 0x00003410, /* Transmit data fifo head */
+ Tdft = 0x00003418, /* Transmit data fifo tail */
+ Tdfhs = 0x00003420, /* Transmit data Fifo Head saved */
+ Tdfts = 0x00003428, /* Transmit data fifo tail saved */
+ Tdfpc = 0x00003430, /* Trasnmit data Fifo packet count */
Tdbal = 0x00003800, /* Tdesc Base Address Low */
Tdbah = 0x00003804, /* Tdesc Base Address High */
Tdlen = 0x00003808, /* Transmit Descriptor Length */
@@ 87,7 94,15 @@ enum { /* Ctrl */
Frcspd = 0x00000800, /* Force Speed */
Frcdplx = 0x00001000, /* Force Duplex */
Swdpinslo = 0x003C0000, /* Software Defined Pins - lo nibble */
- Swdpiolo = 0x03C00000, /* Software Defined Pins - I or O */
+ Swdpin0 = 0x00040000,
+ Swdpin1 = 0x00080000,
+ Swdpin2 = 0x00100000,
+ Swdpin3 = 0x00200000,
+ Swdpiolo = 0x03C00000, /* Software Defined I/O Pins */
+ Swdpio0 = 0x00400000,
+ Swdpio1 = 0x00800000,
+ Swdpio2 = 0x01000000,
+ Swdpio3 = 0x02000000,
Devrst = 0x04000000, /* Device Reset */
Rfce = 0x08000000, /* Receive Flow Control Enable */
Tfce = 0x10000000, /* Transmit Flow Control Enable */
@@ 116,7 131,7 @@ enum { /* Ctrl and Status */
Asdv100 = 0x00000100, /* 100Mb/s */
Asdv1000 = 0x00000200, /* 1000Mb/s */
};
-
+
enum { /* Eecd */
Sk = 0x00000001, /* Clock input to the EEPROM */
Cs = 0x00000002, /* Chip Select */
@@ 134,6 149,18 @@ enum { /* Ctrlext */
Spdbyps = 0x00008000, /* Speed Select Bypass */
};
+enum { /* EEPROM content offsets */
+ Ea = 0x00, /* Ethernet Address */
+ Cf = 0x03, /* Compatibility Field */
+ Pba = 0x08, /* Printed Board Assembly number */
+ Icw1 = 0x0A, /* Initialization Control Word 1 */
+ Sid = 0x0B, /* Subsystem ID */
+ Svid = 0x0C, /* Subsystem Vendor ID */
+ Did = 0x0D, /* Device ID */
+ Vid = 0x0E, /* Vendor ID */
+ Icw2 = 0x0F, /* Initialization Control Word 2 */
+};
+
enum { /* Mdic */
MDIdMASK = 0x0000FFFF, /* Data */
MDIdSHIFT = 0,
@@ 164,6 191,25 @@ enum { /* Icr, Ics, Ims, Imc */
Gpi3 = 0x00004000,
};
+enum { /* Txcw */
+ Ane = 0x80000000, /* Autonegotiate enable */
+ Np = 0x00008000, /* Next Page */
+ As = 0x00000100, /* Asymmetric Flow control desired */
+ Ps = 0x00000080, /* Pause supported */
+ Hd = 0x00000040, /* Half duplex supported */
+ TxcwFd = 0x00000020, /* Full Duplex supported */
+};
+
+enum { /* Rxcw */
+ Rxword = 0x0000FFFF, /* Data from auto-negotiation process */
+ Rxnocarrier = 0x04000000, /* Carrier Sense indication */
+ Rxinvalid = 0x08000000, /* Invalid Symbol during configuration */
+ Rxchange = 0x10000000, /* Change to the Rxword indication */
+ Rxconfig = 0x20000000, /* /C/ order set reception indication */
+ Rxsync = 0x40000000, /* Lost bit synchronization indication */
+ Anc = 0x80000000, /* Auto Negotiation Complete */
+};
+
enum { /* Rctl */
Rrst = 0x00000001, /* Receiver Software Reset */
Ren = 0x00000002, /* Receiver Enable */
@@ 183,10 229,11 @@ enum { /* Rctl */
MoMASK = 0x00003000, /* Multicast Offset */
Bam = 0x00008000, /* Broadcast Accept Mode */
BsizeMASK = 0x00030000, /* Receive Buffer Size */
- Bsize2048 = 0x00000000,
- Bsize1024 = 0x00010000,
- Bsize512 = 0x00020000,
- Bsize256 = 0x00030000,
+ Bsize2048 = 0x00000000, /* Bsex = 0 */
+ Bsize1024 = 0x00010000, /* Bsex = 0 */
+ Bsize512 = 0x00020000, /* Bsex = 0 */
+ Bsize256 = 0x00030000, /* Bsex = 0 */
+ Bsize16384 = 0x00010000, /* Bsex = 1 */
Vfe = 0x00040000, /* VLAN Filter Enable */
Cfien = 0x00080000, /* Canonical Form Indicator Enable */
Cfi = 0x00100000, /* Canonical Form Indicator value */
@@ 215,9 262,10 @@ enum { /* [RT]xdctl */
PthreshSHIFT = 0,
HthreshMASK = 0x00003F00, /* Host Threshold */
HthreshSHIFT = 8,
- WthreshMASK = 0x003F0000, /* Writebacj Threshold */
+ WthreshMASK = 0x003F0000, /* Writeback Threshold */
WthreshSHIFT = 16,
- Gran = 0x01000000, /* Granularity */
+ Gran = 0x00000000, /* Granularity */
+ RxGran = 0x01000000, /* Granularity */
};
enum { /* Rxcsum */
@@ 227,6 275,10 @@ enum { /* Rxcsum */
Tuofl = 0x00000200, /* TCP/UDP Checksum Off-load Enable */
};
+enum { /* Receive Delay Timer Ring */
+ Fpd = 0x80000000, /* Flush partial Descriptor Block */
+};
+
typedef struct Rdesc { /* Receive Descriptor */
uint addr[2];
ushort length;
@@ 286,8 338,15 @@ enum { /* Tdesc status */
};
enum {
- Nrdesc = 128, /* multiple of 8 */
- Ntdesc = 128, /* multiple of 8 */
+ Nrdesc = 256, /* multiple of 8 */
+ Ntdesc = 256, /* multiple of 8 */
+ Nblocks = 4098, /* total number of blocks to use */
+
+ SBLOCKSIZE = 2048,
+ JBLOCKSIZE = 16384,
+
+ NORMAL = 1,
+ JUMBO = 2,
};
typedef struct Ctlr Ctlr;
@@ 296,6 355,7 @@ typedef struct Ctlr {
Pcidev* pcidev;
Ctlr* next;
int active;
+ int started;
int id;
ushort eeprom[0x40];
@@ 305,47 365,69 @@ typedef struct Ctlr {
Lock slock;
uint statistics[Nstatistics];
+ Lock rdlock;
Rdesc* rdba; /* receive descriptor base address */
- Block** rb; /* receive buffers */
+ Block* rb[Nrdesc]; /* receive buffers */
int rdh; /* receive descriptor head */
int rdt; /* receive descriptor tail */
+ Block** freehead; /* points to long or short head */
- Tdesc* tdba; /* transmit descriptor base address */
Lock tdlock;
- Block** tb; /* transmit buffers */
+ Tdesc* tdba; /* transmit descriptor base address */
+ Block* tb[Ntdesc]; /* transmit buffers */
int tdh; /* transmit descriptor head */
int tdt; /* transmit descriptor tail */
- int ett; /* early transmit threshold */
+ Rendez timer; /* watchdog timer */
+ int txstalled; /* count of times unable to send */
+
+ int txcw;
+ int fcrtl;
+ int fcrth;
ulong multimask[128]; /* bit mask for multicast addresses */
} Ctlr;
-static Ctlr* ctlrhead;
-static Ctlr* ctlrtail;
+static Ctlr* gc82543ctlrhead;
+static Ctlr* gc82543ctlrtail;
+
+static Lock freelistlock;
+static Block* freeShortHead;
+static Block* freeJumboHead;
#define csr32r(c, r) (*((c)->nic+((r)/4)))
#define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v))
+static void gc82543watchdog(void* arg);
+
static void
gc82543attach(Ether* edev)
{
int ctl;
Ctlr *ctlr;
+ char name[KNAMELEN];
/*
* To do here:
* one-time stuff;
- * start off a kproc for link status change:
* adjust queue length depending on speed;
* flow control.
* more needed here...
*/
ctlr = edev->ctlr;
- csr32w(ctlr, Ims, ctlr->im);
+ lock(&ctlr->slock);
+ if(ctlr->started == 0){
+ ctlr->started = 1;
+ snprint(name, KNAMELEN, "#l%d82543", edev->ctlrno);
+ kproc(name, gc82543watchdog, edev);
+ }
+ unlock(&ctlr->slock);
+
ctl = csr32r(ctlr, Rctl)|Ren;
csr32w(ctlr, Rctl, ctl);
ctl = csr32r(ctlr, Tctl)|Ten;
csr32w(ctlr, Tctl, ctl);
+
+ csr32w(ctlr, Ims, ctlr->im);
}
static char* statistics[Nstatistics] = {
@@ 424,10 506,9 @@ gc82543ifstat(Ether* edev, void* a, long n, ulong offset)
uvlong tuvl, ruvl;
ctlr = edev->ctlr;
-
+ lock(&ctlr->slock);
p = malloc(2*READSTR);
l = 0;
-
for(i = 0; i < Nstatistics; i++){
r = csr32r(ctlr, Statistics+i*4);
if((s = statistics[i]) == nil)
@@ 450,7 531,7 @@ gc82543ifstat(Ether* edev, void* a, long n, ulong offset)
s, tuvl, ruvl);
i++;
break;
-
+
default:
ctlr->statistics[i] += r;
if(ctlr->statistics[i] == 0)
@@ 461,7 542,6 @@ gc82543ifstat(Ether* edev, void* a, long n, ulong offset)
}
}
- l += snprint(p+l, 2*READSTR-l, "ett: %d\n", ctlr->ett);
l += snprint(p+l, 2*READSTR-l, "eeprom:");
for(i = 0; i < 0x40; i++){
if(i && ((i & 0x07) == 0))
@@ 469,74 549,220 @@ gc82543ifstat(Ether* edev, void* a, long n, ulong offset)
l += snprint(p+l, 2*READSTR-l, " %4.4uX", ctlr->eeprom[i]);
}
- snprint(p+l, 2*READSTR-l, "\n");
+ snprint(p+l, 2*READSTR-l, "\ntxstalled %d\n", ctlr->txstalled);
n = readstr(offset, a, n, p);
free(p);
+ unlock(&ctlr->slock);
return n;
}
static void
-gc82543transmit(Ether* edev)
+gc82543promiscuous(void* arg, int on)
{
- Block *bp;
+ int rctl;
Ctlr *ctlr;
- Tdesc *tdesc;
- int tdh, tdt;
+ Ether *edev;
- /*
- * For now there are no smarts here. Tuning comes later.
- */
+ edev = arg;
ctlr = edev->ctlr;
- ilock(&ctlr->tdlock);
+
+ rctl = csr32r(ctlr, Rctl);
+ rctl &= ~MoMASK; /* make sure we're using bits 47:36 */
+ if(on)
+ rctl |= Upe|Mpe;
+ else
+ rctl &= ~(Upe|Mpe);
+ csr32w(ctlr, Rctl, rctl);
+}
+
+static void
+gc82543multicast(void* arg, uchar* addr, int on)
+{
+ int bit, x;
+ Ctlr *ctlr;
+ Ether *edev;
+
+ edev = arg;
+ ctlr = edev->ctlr;
+ x = addr[5]>>1;
+ bit = ((addr[5] & 1)<<4)|(addr[4]>>4);
+ if(on)
+ ctlr->multimask[x] |= 1<<bit;
+ else
+ ctlr->multimask[x] &= ~(1<<bit);
+
+ csr32w(ctlr, Mta+x*4, ctlr->multimask[x]);
+}
+
+static long
+gc82543ctl(Ether* edev, void* buf, long n)
+{
+ Cmdbuf *cb;
+ Ctlr *ctlr;
+ int ctrl, i, r;
+
+ ctlr = edev->ctlr;
+ if(ctlr == nil)
+ error(Enonexist);
+
+ lock(&ctlr->slock);
+ r = 0;
+ cb = parsecmd(buf, n);
+ if(cb->nf < 2)
+ r = -1;
+ else if(cistrcmp(cb->f[0], "auto") == 0){
+ ctrl = csr32r(ctlr, Ctrl);
+ if(cistrcmp(cb->f[1], "off") == 0){
+ csr32w(ctlr, Txcw, ctlr->txcw & ~Ane);
+ ctrl |= (Slu|Fd);
+ if(ctlr->txcw & As)
+ ctrl |= Rfce;
+ if(ctlr->txcw & Ps)
+ ctrl |= Tfce;
+ csr32w(ctlr, Ctrl, ctrl);
+ }
+ else if(cistrcmp(cb->f[1], "on") == 0){
+ csr32w(ctlr, Txcw, ctlr->txcw);
+ ctrl &= ~(Slu|Fd);
+ csr32w(ctlr, Ctrl, ctrl);
+ }
+ else
+ r = -1;
+ }
+ else if(cistrcmp(cb->f[0], "clear") == 0){
+ if(cistrcmp(cb->f[1], "stats") == 0){
+ for(i = 0; i < Nstatistics; i++)
+ ctlr->statistics[i] = 0;
+ }
+ else
+ r = -1;
+ }
+ else
+ r = -1;
+ unlock(&ctlr->slock);
+
+ free(cb);
+ return (r == 0) ? n : r;
+}
+
+static void
+gc82543txinit(Ctlr* ctlr)
+{
+ int i;
+ int tdsize;
+ Block *bp, **bpp;
+
+ tdsize = ROUND(Ntdesc*sizeof(Tdesc), 4096);
+
+ if(ctlr->tdba == nil)
+ ctlr->tdba = xspanalloc(tdsize, 32, 0);
+
+ for(i = 0; i < Ntdesc; i++){
+ bpp = &ctlr->tb[i];
+ bp = *bpp;
+ if(bp != nil){
+ *bpp = nil;
+ freeb(bp);
+ }
+ memset(&ctlr->tdba[i], 0, sizeof(Tdesc));
+ }
+
+ csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba));
+ csr32w(ctlr, Tdbah, 0);
+ csr32w(ctlr, Tdlen, Ntdesc*sizeof(Tdesc));
/*
- * Free any completed packets
- * - try to get the soft tdh to catch the tdt;
- * - if the packet had an underrun bump the threshold
- * - the Tu bit doesn't seem to ever be set, perhaps
- * because Rs mode is used?
+ * set the ring head and tail pointers.
*/
+ ctlr->tdh = 0;
+ csr32w(ctlr, Tdh, ctlr->tdh);
+ ctlr->tdt = 0;
+ csr32w(ctlr, Tdt, ctlr->tdt);
+
+ csr32w(ctlr, Tipg, (6<<20)|(8<<10)|6);
+ csr32w(ctlr, Tidv, 128);
+ csr32w(ctlr, Ait, 0);
+ csr32w(ctlr, Txdmac, 0);
+ csr32w(ctlr, Txdctl, Gran|(4<<WthreshSHIFT)|(1<<HthreshSHIFT)|16);
+ csr32w(ctlr, Tctl, (0x0F<<CtSHIFT)|Psp|(6<<ColdSHIFT));
+
+ ctlr->im |= Txdw;
+}
+
+static void
+gc82543transmit(Ether* edev)
+{
+ Block *bp, **bpp;
+ Ctlr *ctlr;
+ Tdesc *tdesc;
+ int tdh, tdt, s;
+
+ ctlr = edev->ctlr;
+
+ ilock(&ctlr->tdlock);
tdh = ctlr->tdh;
for(;;){
+ /*
+ * Free any completed packets
+ */
tdesc = &ctlr->tdba[tdh];
if(!(tdesc->status & Tdd))
break;
- if(tdesc->status & Tu){
- ctlr->ett++;
- csr32w(ctlr, Ett, ctlr->ett);
- }
- tdesc->status = 0;
- if(ctlr->tb[tdh] != nil){
- freeb(ctlr->tb[tdh]);
- ctlr->tb[tdh] = nil;
+ memset(tdesc, 0, sizeof(Tdesc));
+ bpp = &ctlr->tb[tdh];
+ bp = *bpp;
+ if(bp != nil){
+ *bpp = nil;
+ freeb(bp);
}
tdh = NEXT(tdh, Ntdesc);
}
ctlr->tdh = tdh;
+ s = csr32r(ctlr, Status);
/*
- * Try to fill the ring back up.
+ * Try to fill the ring back up
+ * but only if link is up and transmission isn't paused.
*/
- tdh = PREV(ctlr->tdh, Ntdesc);
- for(tdt = ctlr->tdt; tdt != tdh; tdt = NEXT(tdt, Ntdesc)){
- if((bp = qget(edev->oq)) == nil)
- break;
+ if((s & (Txoff|Lu)) == Lu){
+ tdt = ctlr->tdt;
+ while(NEXT(tdt, Ntdesc) != tdh){
+ if((bp = qget(edev->oq)) == nil)
+ break;
- tdesc = &ctlr->tdba[tdt];
- tdesc->addr[0] = PCIWADDR(bp->rp);
- tdesc->addr[1] = 0;
- tdesc->control = Ide|Rs|Ifcs|Teop|BLEN(bp);
- tdesc->status = 0;
+ tdesc = &ctlr->tdba[tdt];
+ tdesc->addr[0] = PCIWADDR(bp->rp);
+ tdesc->control = Ide|Rs|Ifcs|Teop|BLEN(bp);
+ ctlr->tb[tdt] = bp;
+ tdt = NEXT(tdt, Ntdesc);
+ }
- ctlr->tb[tdt] = bp;
+ if(tdt != ctlr->tdt){
+ ctlr->tdt = tdt;
+ csr32w(ctlr, Tdt, tdt);
+ }
}
- ctlr->tdt = tdt;
- csr32w(ctlr, Tdt, tdt);
+ else
+ ctlr->txstalled++;
iunlock(&ctlr->tdlock);
}
+static Block *
+gc82543allocb(Ctlr* ctlr)
+{
+ Block *bp;
+
+ ilock(&freelistlock);
+ if((bp = *(ctlr->freehead)) != nil){
+ *(ctlr->freehead) = bp->next;
+ bp->next = nil;
+ }
+ iunlock(&freelistlock);
+ return bp;
+}
+
static void
gc82543replenish(Ctlr* ctlr)
{
@@ 544,72 770,171 @@ gc82543replenish(Ctlr* ctlr)
Block *bp;
Rdesc *rdesc;
+ ilock(&ctlr->rdlock);
rdt = ctlr->rdt;
while(NEXT(rdt, Nrdesc) != ctlr->rdh){
rdesc = &ctlr->rdba[rdt];
- if(ctlr->rb[rdt] != nil){
- /* nothing to do */
- }
- else if((bp = iallocb(2048)) != nil){
+ if(ctlr->rb[rdt] == nil){
+ bp = gc82543allocb(ctlr);
+ if(bp == nil){
+ iprint("no available buffers\n");
+ break;
+ }
ctlr->rb[rdt] = bp;
rdesc->addr[0] = PCIWADDR(bp->rp);
rdesc->addr[1] = 0;
}
- else
- break;
+ coherence();
rdesc->status = 0;
-
rdt = NEXT(rdt, Nrdesc);
}
ctlr->rdt = rdt;
csr32w(ctlr, Rdt, rdt);
+ iunlock(&ctlr->rdlock);
}
static void
-gc82543interrupt(Ureg*, void* arg)
+gc82543rxinit(Ctlr* ctlr)
+{
+ int rdsize, i;
+
+ csr32w(ctlr, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF);
+
+ /*
+ * Allocate the descriptor ring and load its
+ * address and length into the NIC.
+ */
+ rdsize = ROUND(Nrdesc*sizeof(Rdesc), 4096);
+ if(ctlr->rdba == nil)
+ ctlr->rdba = xspanalloc(rdsize, 32, 0);
+ memset(ctlr->rdba, 0, rdsize);
+
+ ctlr->rdh = 0;
+ ctlr->rdt = 0;
+
+ csr32w(ctlr, Rdtr, Fpd|64);
+ csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba));
+ csr32w(ctlr, Rdbah, 0);
+ csr32w(ctlr, Rdlen, Nrdesc*sizeof(Rdesc));
+ csr32w(ctlr, Rdh, 0);
+ csr32w(ctlr, Rdt, 0);
+ for(i = 0; i < Nrdesc; i++){
+ if(ctlr->rb[i] != nil){
+ freeb(ctlr->rb[i]);
+ ctlr->rb[i] = nil;
+ }
+ }
+ gc82543replenish(ctlr);
+
+ csr32w(ctlr, Rxdctl, RxGran|(8<<WthreshSHIFT)|(4<<HthreshSHIFT)|1);
+ ctlr->im |= Rxt0|Rxo|Rxdmt0|Rxseq;
+}
+
+static void
+gc82543recv(Ether* edev, int icr)
{
Block *bp;
Ctlr *ctlr;
- Ether *edev;
Rdesc *rdesc;
- int icr, rdh;
+ int rdh;
+
+ ctlr = edev->ctlr;
+
+ rdh = ctlr->rdh;
+ for(;;){
+ rdesc = &ctlr->rdba[rdh];
+
+ if(!(rdesc->status & Rdd))
+ break;
+
+ if((rdesc->status & Reop) && rdesc->errors == 0){
+ bp = ctlr->rb[rdh];
+ ctlr->rb[rdh] = nil;
+ bp->wp += rdesc->length;
+ bp->next = nil;
+ etheriq(edev, bp, 1);
+ }
+
+ if(ctlr->rb[rdh] != nil){
+ /* either non eop packet, or error */
+ freeb(ctlr->rb[rdh]);
+ ctlr->rb[rdh] = nil;
+ }
+ memset(rdesc, 0, sizeof(Rdesc));
+ coherence();
+ rdh = NEXT(rdh, Nrdesc);
+ }
+ ctlr->rdh = rdh;
+
+ if(icr & Rxdmt0)
+ gc82543replenish(ctlr);
+}
+
+static void
+freegc82543short(Block *bp)
+{
+ ilock(&freelistlock);
+ /* reset read/write pointer to proper positions */
+ bp->rp = bp->lim - ROUND(SBLOCKSIZE, BLOCKALIGN);
+ bp->wp = bp->rp;
+ bp->next = freeShortHead;
+ freeShortHead = bp;
+ iunlock(&freelistlock);
+}
+
+static void
+freegc82532jumbo(Block *bp)
+{
+ ilock(&freelistlock);
+ /* reset read/write pointer to proper positions */
+ bp->rp = bp->lim - ROUND(JBLOCKSIZE, BLOCKALIGN);
+ bp->wp = bp->rp;
+ bp->next = freeJumboHead;
+ freeJumboHead = bp;
+ iunlock(&freelistlock);
+}
+
+static void
+linkintr(Ctlr* ctlr)
+{
+ int ctrl;
+
+ ctrl = csr32r(ctlr, Ctrl);
+
+ if((ctrl & Swdpin1) ||
+ ((csr32r(ctlr, Rxcw) & Rxconfig) && !(csr32r(ctlr, Txcw) & Ane))){
+ csr32w(ctlr, Txcw, ctlr->txcw);
+ ctrl &= ~(Slu|Fd|Frcdplx);
+ csr32w(ctlr, Ctrl, ctrl);
+ }
+}
+
+static void
+gc82543interrupt(Ureg*, void* arg)
+{
+ Ctlr *ctlr;
+ Ether *edev;
+ int icr;
edev = arg;
ctlr = edev->ctlr;
- for(icr = csr32r(ctlr, Icr); icr & ctlr->im; icr = csr32r(ctlr, Icr)){
+ while((icr = csr32r(ctlr, Icr) & ctlr->im) != 0){
/*
* Link status changed.
*/
- if(icr & (Rxseq|Lsc)){
- /*
- * More here...
- */
- }
+ if(icr & (Lsc|Rxseq))
+ linkintr(ctlr);
/*
- * Process any received packets.
+ * Process recv buffers.
*/
- rdh = ctlr->rdh;
- for(;;){
- rdesc = &ctlr->rdba[rdh];
- if(!(rdesc->status & Rdd))
- break;
- if((rdesc->status & Reop) && rdesc->errors == 0){
- bp = ctlr->rb[rdh];
- ctlr->rb[rdh] = nil;
- bp->wp += rdesc->length-4;
- etheriq(edev, bp, 1);
- }
- rdesc->status = 0;
- rdh = NEXT(rdh, Nrdesc);
- }
- ctlr->rdh = rdh;
+ gc82543recv(edev, icr);
+ /*
+ * Refill transmit ring and free packets.
+ */
gc82543transmit(edev);
-
- if(icr & Rxdmt0)
- gc82543replenish(ctlr);
}
}
@@ 617,11 942,33 @@ static int
gc82543init(Ether* edev)
{
int csr, i;
-
+ Block *bp;
Ctlr *ctlr;
+
ctlr = edev->ctlr;
/*
+ * Allocate private buffer pool to use for receiving packets.
+ */
+ ilock(&freelistlock);
+ if (ctlr->freehead == nil){
+ for(i = 0; i < Nblocks; i++){
+ bp = iallocb(SBLOCKSIZE);
+ if(bp != nil){
+ bp->next = freeShortHead;
+ bp->free = freegc82543short;
+ freeShortHead = bp;
+ }
+ else{
+ print("82543gc: no memory\n");
+ break;
+ }
+ }
+ ctlr->freehead = &freeShortHead;
+ }
+ iunlock(&freelistlock);
+
+ /*
* Set up the receive addresses.
* There are 16 addresses. The first should be the MAC address.
* The others are cleared and not marked valid (MS bit of Rah).
@@ 642,102 989,8 @@ gc82543init(Ether* edev)
for(i = 0; i < 128; i++)
csr32w(ctlr, Mta+i*4, 0);
- /*
- * Receive initialisation.
- * Mostly defaults from the datasheet, will
- * need some tuning for performance:
- * Rctl descriptor mimimum threshold size
- * discard pause frames
- * strip CRC
- * Rdtr interrupt delay
- * Rxdctl all the thresholds
- */
- csr32w(ctlr, Rctl, 0);
-
- /*
- * Allocate the descriptor ring and load its
- * address and length into the NIC.
- */
- ctlr->rdba = xspanalloc(Nrdesc*sizeof(Rdesc), 16, 0);
- csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba));
- csr32w(ctlr, Rdbah, 0);
- csr32w(ctlr, Rdlen, Nrdesc*sizeof(Rdesc));
-
- /*
- * Initialise the ring head and tail pointers and
- * populate the ring with Blocks.
- * The datasheet says the tail pointer is set to beyond the last
- * descriptor hardware can process, which implies the initial
- * condition is Rdh == Rdt. However, experience shows Rdt must
- * always be 'behind' Rdh; the replenish routine ensures this.
- */
- ctlr->rdh = 0;
- csr32w(ctlr, Rdh, ctlr->rdh);
- ctlr->rdt = 0;
- csr32w(ctlr, Rdt, ctlr->rdt);
- ctlr->rb = malloc(sizeof(Block*)*Nrdesc);
- gc82543replenish(ctlr);
-
- /*
- * Set up Rctl but don't enable receiver (yet).
- */
- csr32w(ctlr, Rdtr, 0);
- csr32w(ctlr, Rxdctl, Gran|(1<<WthreshSHIFT)|(1<<HthreshSHIFT)|4);
-
- csr32w(ctlr, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF);
- ctlr->im |= Rxt0|Rxo|Rxdmt0|Rxseq;
-
- /*
- * Transmit initialisation.
- * Mostly defaults from the datasheet, will
- * need some tuning for performance. The normal mode will
- * be full-duplex and things to tune for half-duplex are
- * Tctl re-transmit on late collision
- * Tipg all IPG times
- * Tbt burst timer
- * Ait adaptive IFS throttle
- * and in general
- * Txdmac packet prefetching
- * Ett transmit early threshold
- * Tidv interrupt delay value
- * Txdctl all the thresholds
- */
- csr = (0x0F<<CtSHIFT)|Psp;
- if(csr32r(ctlr, Ctrl) & Fd)
- csr |= 64<<ColdSHIFT;
- else
- csr |= 512<<ColdSHIFT;
- csr32w(ctlr, Tctl, csr);
- csr32w(ctlr, Tipg, (6<<20)|(2<<10)|6);
- csr32w(ctlr, Tbt, 0x2000);
- csr32w(ctlr, Ait, 0);
- csr32w(ctlr, Txdmac, 0);
- if(csr32r(ctlr, Status) & Bus64)
- ctlr->ett = 120/8;
- else
- ctlr->ett = 0;
- csr32w(ctlr, Ett, ctlr->ett);
- csr32w(ctlr, Tidv, 128);
- csr32w(ctlr, Txdctl, Gran|(16<<WthreshSHIFT)|(1<<HthreshSHIFT)|4);
-
- /*
- * Allocate the descriptor ring and load its
- * address and length into the NIC.
- */
- ctlr->tdba = xspanalloc(Ntdesc*sizeof(Tdesc), 16, 0);
- csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba));
- csr32w(ctlr, Tdbah, 0);
- csr32w(ctlr, Tdlen, Ntdesc*sizeof(Tdesc));
-
- /*
- * Initialise the ring head and tail pointers.
- */
- ctlr->tdh = 0;
- csr32w(ctlr, Tdh, ctlr->tdh);
- ctlr->tdt = 0;
- csr32w(ctlr, Tdt, ctlr->tdt);
- ctlr->tb = malloc(sizeof(Block*)*Ntdesc);
- ctlr->im |= Txqe|Txdw;
+ gc82543txinit(ctlr);
+ gc82543rxinit(ctlr);
return 0;
}
@@ 854,6 1107,12 @@ gc82543detach(Ctlr* ctlr)
* power-on state, followed by an EEPROM reset to read
* the defaults for some internal registers.
*/
+ csr32w(ctlr, Imc, ~0);
+ csr32w(ctlr, Rctl, 0);
+ csr32w(ctlr, Tctl, 0);
+
+ delay(10);
+
csr32w(ctlr, Ctrl, Devrst);
while(csr32r(ctlr, Ctrl) & Devrst)
;
@@ 861,26 1120,89 @@ gc82543detach(Ctlr* ctlr)
csr32w(ctlr, Ctrlext, Eerst);
while(csr32r(ctlr, Ctrlext) & Eerst)
;
+
+ csr32w(ctlr, Imc, ~0);
+ while(csr32r(ctlr, Icr))
+ ;
+}
+
+static void
+gc82543checklink(Ctlr* ctlr)
+{
+ int ctrl, status, rxcw;
+
+ ctrl = csr32r(ctlr, Ctrl);
+ status = csr32r(ctlr, Status);
+ rxcw = csr32r(ctlr, Rxcw);
+
+ if(!(status & Lu)){
+ if(!(ctrl & (Swdpin1|Slu)) && !(rxcw & Rxconfig)){
+ csr32w(ctlr, Txcw, ctlr->txcw & ~Ane);
+ ctrl |= (Slu|Fd);
+ if(ctlr->txcw & As)
+ ctrl |= Rfce;
+ if(ctlr->txcw & Ps)
+ ctrl |= Tfce;
+ csr32w(ctlr, Ctrl, ctrl);
+ }
+ }
+ else if((ctrl & Slu) && (rxcw & Rxconfig)){
+ csr32w(ctlr, Txcw, ctlr->txcw);
+ ctrl &= ~(Slu|Fd);
+ csr32w(ctlr, Ctrl, ctrl);
+ }
}
static void
gc82543shutdown(Ether* ether)
{
-print("gc82543shutdown\n");
gc82543detach(ether->ctlr);
}
static int
gc82543reset(Ctlr* ctlr)
{
+ int ctl;
+ int te;
+
/*
* Read the EEPROM, validate the checksum
* then get the device back to a power-on state.
*/
if(at93c46r(ctlr) != 0xBABA)
return -1;
+
gc82543detach(ctlr);
+ te = ctlr->eeprom[Icw2];
+ if((te & 0x3000) == 0){
+ ctlr->fcrtl = 0x00002000;
+ ctlr->fcrth = 0x00004000;
+ ctlr->txcw = Ane|TxcwFd;
+ }
+ else if((te & 0x3000) == 0x2000){
+ ctlr->fcrtl = 0;
+ ctlr->fcrth = 0;
+ ctlr->txcw = Ane|TxcwFd|As;
+ }
+ else{
+ ctlr->fcrtl = 0x00002000;
+ ctlr->fcrth = 0x00004000;
+ ctlr->txcw = Ane|TxcwFd|As|Ps;
+ }
+
+ csr32w(ctlr, Txcw, ctlr->txcw);
+
+ csr32w(ctlr, Ctrlext, (te & 0x00f0)<<4);
+
+ csr32w(ctlr, Tctl, csr32r(ctlr, Tctl)|(64<<ColdSHIFT));
+
+ te = ctlr->eeprom[Icw1];
+ ctl = ((te & 0x01E0)<<17)|(te & 0x0010)<<3;
+ csr32w(ctlr, Ctrl, ctl);
+
+ delay(10);
+
/*
* Flow control - values from the datasheet.
*/
@@ 889,23 1211,41 @@ gc82543reset(Ctlr* ctlr)
csr32w(ctlr, Fct, 0x00008808);
csr32w(ctlr, Fcttv, 0x00000100);
- /*
- * More here...
- * Need to take deal with making sure hardware auto-negotiation is
- * set up, how Rxcw and Txcw are configured, Fcrtl, Fcrth, etc.
- */
+ csr32w(ctlr, Fcrtl, ctlr->fcrtl);
+ csr32w(ctlr, Fcrth, ctlr->fcrth);
- csr32w(ctlr, Imc, ~0);
ctlr->im = Lsc;
- csr32w(ctlr, Ims, ctlr->im);
+ gc82543checklink(ctlr);
return 0;
}
static void
+gc82543watchdog(void* arg)
+{
+ Ether *edev;
+ Ctlr *ctlr;
+
+ edev = arg;
+ for(;;){
+ ctlr = edev->ctlr;
+ tsleep(&ctlr->timer, return0, 0, 1000);
+
+ ctlr = edev->ctlr;
+ if(ctlr == nil){
+ print("%s: exiting\n", up->text);
+ pexit("disabled", 0);
+ }
+
+ gc82543checklink(ctlr);
+ gc82543replenish(ctlr);
+ }
+}
+
+static void
gc82543pci(void)
{
- int port;
+ int port, cls;
Pcidev *p;
Ctlr *ctlr;
@@ 931,64 1271,38 @@ gc82543pci(void)
print("gc82543: can't map %8.8luX\n", p->mem[0].bar);
continue;
}
-
+ cls = pcicfgr8(p, PciCLS);
+ switch(cls){
+ case 0x00:
+ case 0xFF:
+ print("82543gc: unusable cache line size\n");
+ continue;
+ case 0x08:
+ break;
+ default:
+ print("82543gc: cache line size %d, expected 32\n",
+ cls*4);
+ }
ctlr = malloc(sizeof(Ctlr));
ctlr->port = port;
ctlr->pcidev = p;
ctlr->id = (p->did<<16)|p->vid;
ctlr->nic = KADDR(ctlr->port);
-print("status0 %8.8uX\n", csr32r(ctlr, Status));
+
if(gc82543reset(ctlr)){
free(ctlr);
continue;
}
-print("status1 %8.8uX\n", csr32r(ctlr, Status));
- if(ctlrhead != nil)
- ctlrtail->next = ctlr;
+ if(gc82543ctlrhead != nil)
+ gc82543ctlrtail->next = ctlr;
else
- ctlrhead = ctlr;
- ctlrtail = ctlr;
+ gc82543ctlrhead = ctlr;
+ gc82543ctlrtail = ctlr;
}
}
-static void
-gc82543promiscuous(void* arg, int on)
-{
- Ether *edev=arg;
- Ctlr *ctlr;
- ulong ctl;
-
- ctlr = edev->ctlr;
- ctl = csr32r(ctlr, Rctl);
- ctl &= ~MoMASK; /* make sure we're using bits 47:36 */
- if(on)
- ctl |= Upe|Mpe;
- else
- ctl &= ~(Upe|Mpe);
- csr32w(ctlr, Rctl, ctl);
-}
-
-static void
-gc82543multicast(void* arg, uchar *addr, int on)
-{
- Ether *edev=arg;
- Ctlr *ctlr;
- int x;
- int bit;
-
- ctlr = edev->ctlr;
- x = addr[5]>>1;
- bit = ((addr[5]&1)<<4)|(addr[4]>>4);
- if(on)
- ctlr->multimask[x] |= 1<<bit;
- else
- ctlr->multimask[x] &= ~(1<<bit);
-
- csr32w(ctlr, Mta+x*4, ctlr->multimask[x]);
-}
-
static int
gc82543pnp(Ether* edev)
{
@@ 996,14 1310,14 @@ gc82543pnp(Ether* edev)
Ctlr *ctlr;
uchar ea[Eaddrlen];
- if(ctlrhead == nil)
+ if(gc82543ctlrhead == nil)
gc82543pci();
/*
* Any adapter matches if no edev->port is supplied,
* otherwise the ports must match.
*/
- for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
+ for(ctlr = gc82543ctlrhead; ctlr != nil; ctlr = ctlr->next){
if(ctlr->active)
continue;
if(edev->port == 0 || edev->port == ctlr->port){
@@ 1027,7 1341,7 @@ gc82543pnp(Ether* edev)
*/
memset(ea, 0, Eaddrlen);
if(memcmp(ea, edev->ea, Eaddrlen) == 0){
- for(i = 0; i < Eaddrlen/2; i++){
+ for(i = Ea; i < Eaddrlen/2; i++){
edev->ea[2*i] = ctlr->eeprom[i];
edev->ea[2*i+1] = ctlr->eeprom[i]>>8;
}
@@ 1042,12 1356,11 @@ gc82543pnp(Ether* edev)
edev->interrupt = gc82543interrupt;
edev->ifstat = gc82543ifstat;
edev->shutdown = gc82543shutdown;
+ edev->ctl = gc82543ctl;
+ edev->arg = edev;
edev->promiscuous = gc82543promiscuous;
edev->multicast = gc82543multicast;
- edev->arg = edev;
- edev->promiscuous = nil;
-
return 0;
}