
The new pci code is moved to port/pci.[hc] and shared by all ports. Each port has its own PCI controller implementation, providing the pcicfgrw*() functions for low level pci config space access. The locking for pcicfgrw*() is now done by the caller (only port/pci.c). Device drivers now need to include "../port/pci.h" in addition to "io.h". The new code now checks bridge windows and membars, while enumerating the bus, giving the pc driver a chance to re-assign them. This is needed because some UEFI implementations fail to assign the bars for some devices, so we need to do it outselfs. (See pcireservemem()). While working on this, it was discovered that the pci code assimed the smallest I/O bar size is 16 (pcibarsize()), which is wrong. I/O bars can be as small as 4 bytes. Bit 1 in an I/O bar is also reserved and should be masked off, making the port mask: port = bar & ~3;
919 lines
17 KiB
C
919 lines
17 KiB
C
/*
|
|
* intel 10GB ethernet pci-express driver
|
|
* 6.0.0: net 02.00.00 8086/15c8 11 0:dfc0000c 2097152 4:dfe0400c 16384
|
|
* Intel Corporation Ethernet Connection X553/X550-AT 10GBASE-T
|
|
*/
|
|
#include "u.h"
|
|
#include "../port/lib.h"
|
|
#include "mem.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
#include "io.h"
|
|
#include "../port/pci.h"
|
|
#include "../port/error.h"
|
|
#include "../port/netif.h"
|
|
#include "../port/etherif.h"
|
|
|
|
|
|
enum {
|
|
/* general */
|
|
Ctrl = 0x00000/4, /* Device Control */
|
|
Status = 0x00008/4, /* Device Status */
|
|
Ctrlext = 0x00018/4, /* Extended Device Control */
|
|
Tcptimer = 0x0004c/4, /* tcp timer */
|
|
|
|
/* nvm */
|
|
Eec = 0x10010/4, /* eeprom/flash control */
|
|
Eemngctl = 0x10110/4, /* Manageability EEPROM-Mode Control */
|
|
|
|
/* interrupt */
|
|
Icr = 0x00800/4, /* interrupt cause read */
|
|
Ics = 0x00808/4, /* " set */
|
|
Ims = 0x00880/4, /* " mask read/set */
|
|
Imc = 0x00888/4, /* " mask clear */
|
|
Iac = 0x00810/4, /* " auto clear */
|
|
Iam = 0x00890/4, /* " auto mask enable */
|
|
Itr = 0x00820/4, /* " throttling rate (0-19) */
|
|
Ivar = 0x00900/4, /* " vector allocation regs. */
|
|
|
|
/* rx dma */
|
|
Rdbal = 0x01000/4, /* rx desc base low (0-63) +0x40n */
|
|
Rdbah = 0x01004/4, /* " high */
|
|
Rdlen = 0x01008/4, /* " length */
|
|
Rdh = 0x01010/4, /* " head */
|
|
Rdt = 0x01018/4, /* " tail */
|
|
Rxdctl = 0x01028/4, /* " control */
|
|
|
|
Srrctl = 0x02100/4, /* split and replication rx ctl. */
|
|
Rdrxctl = 0x02f00/4, /* rx dma control */
|
|
Rxpbsize = 0x03c00/4, /* rx packet buffer size */
|
|
Rxctrl = 0x03000/4, /* rx control */
|
|
|
|
/* rx */
|
|
Rxcsum = 0x05000/4, /* rx checksum control */
|
|
Mcstctrl = 0x05090/4, /* multicast control register */
|
|
Mta = 0x05200/4, /* multicast table array (0-127) */
|
|
Ral = 0x05400/4, /* rx address low */
|
|
Rah = 0x05404/4,
|
|
Vfta = 0x0a000/4, /* vlan filter table array. */
|
|
Fctrl = 0x05080/4, /* filter control */
|
|
|
|
/* tx */
|
|
Tdbal = 0x06000/4, /* tx desc base low +0x40n */
|
|
Tdbah = 0x06004/4, /* " high */
|
|
Tdlen = 0x06008/4, /* " len */
|
|
Tdh = 0x06010/4, /* " head */
|
|
Tdt = 0x06018/4, /* " tail */
|
|
Txdctl = 0x06028/4, /* " control */
|
|
Dmatxctl = 0x04a80/4,
|
|
|
|
/* mac */
|
|
Hlreg0 = 0x04240/4, /* highlander control reg 0 */
|
|
Hlreg1 = 0x04244/4, /* highlander control reg 1 (ro) */
|
|
Maxfrs = 0x04268/4, /* max frame size */
|
|
Links = 0x042a4/4, /* link status */
|
|
};
|
|
|
|
enum {
|
|
/* Ctrl */
|
|
Rst = 1<<26, /* full nic reset */
|
|
|
|
/* Ctrlext */
|
|
Drvload = 1<<28, /* Driver Load */
|
|
|
|
/* Eec */
|
|
AutoRd = 1<<9, /* NVM auto read done */
|
|
|
|
/* Eemngctl */
|
|
CfgDone0 = 1<<18, /* Configuration Done Port 0 */
|
|
CfgDone1 = 1<<19, /* Configuration Done Port 1 */
|
|
|
|
/* Txdctl */
|
|
Pthresh = 0, /* prefresh threshold shift in bits */
|
|
Hthresh = 8, /* host buffer minimum threshold */
|
|
Wthresh = 16, /* writeback threshold */
|
|
Ten = 1<<25,
|
|
|
|
/* Fctrl */
|
|
Bam = 1<<10, /* broadcast accept mode */
|
|
Upe = 1<<9, /* unicast promiscuous */
|
|
Mpe = 1<<8, /* multicast promiscuous */
|
|
|
|
/* Rxdctl */
|
|
Renable = 1<<25,
|
|
|
|
/* Dmatxctl */
|
|
Txen = 1<<0,
|
|
|
|
/* Rxctl */
|
|
Rxen = 1<<0,
|
|
|
|
/* Rdrxctl */
|
|
Dmaidone = 1<<3,
|
|
|
|
/* Rxcsum */
|
|
Ippcse = 1<<12, /* ip payload checksum enable */
|
|
|
|
/* Mcstctrl */
|
|
Mo = 0, /* multicast offset 47:36 */
|
|
Mfe = 1<<2, /* multicast filter enable */
|
|
|
|
/* Rah */
|
|
Av = 1<<31,
|
|
|
|
/* interrupts */
|
|
Irx0 = 1<<0, /* driver defined - rx interrupt */
|
|
Itx0 = 1<<1, /* driver defined - tx interrupt */
|
|
Lsc = 1<<20, /* link status change */
|
|
|
|
/* Ivar Interrupt Vector Allocation Register */
|
|
Intalloc0 = 0, /* Map the 0th queue Rx interrupt to the 0th bit of EICR register */
|
|
Intallocval0 = 1<<7,
|
|
intalloc1 = 1<<8, /* Map the 0th queue Tx interrupt to the 1st bit of EICR register */
|
|
Intallocval1 = 1<<15,
|
|
|
|
/* Links */
|
|
Lnkup = 1<<30,
|
|
Lnkspd = 1<<29,
|
|
|
|
/* Hlreg0 */
|
|
Jumboen = 1<<2,
|
|
};
|
|
|
|
typedef struct {
|
|
uint reg;
|
|
char *name;
|
|
} Stat;
|
|
|
|
static
|
|
Stat stattab[] = {
|
|
0x4000, "crc error",
|
|
0x4004, "illegal byte",
|
|
0x4008, "short packet",
|
|
0x3fa0, "missed pkt0",
|
|
0x4034, "mac local flt",
|
|
0x4038, "mac rmt flt",
|
|
0x4040, "rx length err",
|
|
0x405c, "rx 040",
|
|
0x4060, "rx 07f",
|
|
0x4064, "rx 100",
|
|
0x4068, "rx 200",
|
|
0x406c, "rx 3ff",
|
|
0x4070, "rx big",
|
|
0x4074, "rx ok",
|
|
0x4078, "rx bcast",
|
|
0x407c, "rx mcast",
|
|
0x4080, "tx ok",
|
|
0x40a4, "rx runt",
|
|
0x40a8, "rx frag",
|
|
0x40ac, "rx ovrsz",
|
|
0x40b0, "rx jab",
|
|
0x40d0, "rx pkt",
|
|
0x40d4, "tx pkt",
|
|
0x40d8, "tx 040",
|
|
0x40dc, "tx 07f",
|
|
0x40e0, "tx 100",
|
|
0x40e4, "tx 200",
|
|
0x40e8, "tx 3ff",
|
|
0x40ec, "tx big",
|
|
0x40f0, "tx mcast",
|
|
0x40f4, "tx bcast",
|
|
0x4120, "xsum err",
|
|
};
|
|
|
|
/* status */
|
|
enum {
|
|
Pif = 1<<7, /* past exact filter (sic) */
|
|
Ipcs = 1<<6, /* ip checksum calcuated */
|
|
L4cs = 1<<5, /* layer 2 */
|
|
Udpcs = 1<<4, /* udp checksum calcuated */
|
|
Vp = 1<<3, /* 802.1q packet matched vet */
|
|
Reop = 1<<1, /* end of packet */
|
|
Rdd = 1<<0, /* descriptor done */
|
|
};
|
|
|
|
typedef struct {
|
|
u32int addr[2];
|
|
ushort length;
|
|
ushort cksum;
|
|
uchar status;
|
|
uchar errors;
|
|
ushort vlan;
|
|
} Rd;
|
|
|
|
enum {
|
|
/* Td cmd */
|
|
Rs = 1<<3,
|
|
Ic = 1<<2,
|
|
Ifcs = 1<<1,
|
|
Teop = 1<<0,
|
|
|
|
/* Td status */
|
|
Tdd = 1<<0,
|
|
};
|
|
|
|
typedef struct {
|
|
u32int addr[2];
|
|
u16int length;
|
|
uchar cso;
|
|
uchar cmd;
|
|
uchar status;
|
|
uchar css;
|
|
ushort vlan;
|
|
} Td;
|
|
|
|
enum {
|
|
Factive = 1<<0,
|
|
Fstarted = 1<<1,
|
|
};
|
|
|
|
typedef struct {
|
|
Pcidev *p;
|
|
Ether *edev;
|
|
uintptr io;
|
|
u32int *reg;
|
|
u32int *regmsi;
|
|
uchar flag;
|
|
int nrd;
|
|
int ntd;
|
|
int rbsz;
|
|
Lock slock;
|
|
Lock alock;
|
|
QLock tlock;
|
|
Rendez lrendez;
|
|
Rendez trendez;
|
|
Rendez rrendez;
|
|
uint im;
|
|
uint lim;
|
|
uint rim;
|
|
uint tim;
|
|
Lock imlock;
|
|
char *alloc;
|
|
|
|
Rd *rdba;
|
|
Block **rb;
|
|
uint rdt;
|
|
uint rdfree;
|
|
|
|
Td *tdba;
|
|
uint tdh;
|
|
uint tdt;
|
|
Block **tb;
|
|
|
|
uchar ra[Eaddrlen];
|
|
u32int mta[128];
|
|
ulong stats[nelem(stattab)];
|
|
uint speeds[3];
|
|
} Ctlr;
|
|
|
|
/* tweakable paramaters */
|
|
enum {
|
|
Mtu = 12*1024,
|
|
Nrd = 256,
|
|
Ntd = 256,
|
|
Nrb = 256,
|
|
};
|
|
|
|
static Ctlr *ctlrtab[4];
|
|
static int nctlr;
|
|
|
|
static void
|
|
readstats(Ctlr *c)
|
|
{
|
|
int i;
|
|
|
|
lock(&c->slock);
|
|
for(i = 0; i < nelem(c->stats); i++)
|
|
c->stats[i] += c->reg[stattab[i].reg >> 2];
|
|
unlock(&c->slock);
|
|
}
|
|
|
|
static int speedtab[] = {
|
|
0,
|
|
1000,
|
|
10000,
|
|
};
|
|
|
|
static long
|
|
ifstat(Ether *e, void *a, long n, ulong offset)
|
|
{
|
|
uint i, *t;
|
|
char *s, *p, *q;
|
|
Ctlr *c;
|
|
|
|
p = s = smalloc(READSTR);
|
|
q = p + READSTR;
|
|
|
|
c = e->ctlr;
|
|
readstats(c);
|
|
for(i = 0; i < nelem(stattab); i++)
|
|
if(c->stats[i] > 0)
|
|
p = seprint(p, q, "%.10s %uld\n", stattab[i].name,
|
|
c->stats[i]);
|
|
t = c->speeds;
|
|
p = seprint(p, q, "speeds: 0:%d 1000:%d 10000:%d\n", t[0], t[1], t[2]);
|
|
seprint(p, q, "rdfree %d rdh %d rdt %d\n", c->rdfree, c->reg[Rdt],
|
|
c->reg[Rdh]);
|
|
n = readstr(offset, a, n, s);
|
|
free(s);
|
|
|
|
return n;
|
|
}
|
|
|
|
static void
|
|
im(Ctlr *c, int i)
|
|
{
|
|
ilock(&c->imlock);
|
|
c->im |= i;
|
|
c->reg[Ims] = c->im;
|
|
iunlock(&c->imlock);
|
|
}
|
|
|
|
static int
|
|
lim(void *v)
|
|
{
|
|
return ((Ctlr*)v)->lim != 0;
|
|
}
|
|
|
|
static void
|
|
lproc(void *v)
|
|
{
|
|
int r, i;
|
|
Ctlr *c;
|
|
Ether *e;
|
|
|
|
e = v;
|
|
c = e->ctlr;
|
|
while(waserror())
|
|
;
|
|
for (;;) {
|
|
r = c->reg[Links];
|
|
e->link = (r & Lnkup) != 0;
|
|
i = 0;
|
|
if(e->link)
|
|
i = 1 + ((r & Lnkspd) != 0);
|
|
c->speeds[i]++;
|
|
e->mbps = speedtab[i];
|
|
c->lim = 0;
|
|
im(c, Lsc);
|
|
sleep(&c->lrendez, lim, c);
|
|
c->lim = 0;
|
|
}
|
|
}
|
|
|
|
static long
|
|
ctl(Ether *, void *, long)
|
|
{
|
|
error(Ebadarg);
|
|
return -1;
|
|
}
|
|
|
|
#define Next(x, m) (((x)+1) & (m))
|
|
|
|
static int
|
|
cleanup(Ctlr *c, int tdh)
|
|
{
|
|
Block *b;
|
|
uint m, n;
|
|
|
|
m = c->ntd - 1;
|
|
while(c->tdba[n = Next(tdh, m)].status & Tdd){
|
|
tdh = n;
|
|
b = c->tb[tdh];
|
|
c->tb[tdh] = 0;
|
|
freeb(b);
|
|
c->tdba[tdh].status = 0;
|
|
}
|
|
return tdh;
|
|
}
|
|
|
|
static void
|
|
transmit(Ether *e)
|
|
{
|
|
uint i, m, tdt, tdh;
|
|
Ctlr *c;
|
|
Block *b;
|
|
Td *t;
|
|
|
|
c = e->ctlr;
|
|
if(!canqlock(&c->tlock)){
|
|
im(c, Itx0);
|
|
return;
|
|
}
|
|
tdh = c->tdh = cleanup(c, c->tdh);
|
|
tdt = c->tdt;
|
|
m = c->ntd - 1;
|
|
for(i = 0; i < 8; i++){
|
|
if(Next(tdt, m) == tdh){
|
|
im(c, Itx0);
|
|
break;
|
|
}
|
|
if(!(b = qget(e->oq)))
|
|
break;
|
|
t = c->tdba + tdt;
|
|
t->addr[0] = PCIWADDR(b->rp);
|
|
t->length = BLEN(b);
|
|
t->cmd = Rs | Ifcs | Teop;
|
|
c->tb[tdt] = b;
|
|
tdt = Next(tdt, m);
|
|
}
|
|
if(i){
|
|
c->tdt = tdt;
|
|
c->reg[Tdt] = tdt;
|
|
}
|
|
qunlock(&c->tlock);
|
|
}
|
|
|
|
static int
|
|
tim(void *c)
|
|
{
|
|
return ((Ctlr*)c)->tim != 0;
|
|
}
|
|
|
|
static void
|
|
tproc(void *v)
|
|
{
|
|
Ctlr *c;
|
|
Ether *e;
|
|
|
|
e = v;
|
|
c = e->ctlr;
|
|
while(waserror())
|
|
;
|
|
for (;;) {
|
|
sleep(&c->trendez, tim, c); /* transmit kicks us */
|
|
c->tim = 0;
|
|
transmit(e);
|
|
}
|
|
}
|
|
|
|
static void
|
|
rxinit(Ctlr *c)
|
|
{
|
|
int i;
|
|
Block *b;
|
|
|
|
c->reg[Rxctrl] &= ~Rxen;
|
|
/* Pg 144 Step 2
|
|
Receive buffers of appropriate size should be allocated
|
|
and pointers to these buffers should be stored in the
|
|
descriptor ring - replinish() does this? */
|
|
for(i = 0; i < c->nrd; i++){
|
|
b = c->rb[i];
|
|
c->rb[i] = 0;
|
|
if(b)
|
|
freeb(b);
|
|
}
|
|
c->rdfree = 0;
|
|
|
|
c->reg[Fctrl] |= Bam;
|
|
c->reg[Rxcsum] |= Ippcse;
|
|
c->reg[Srrctl] = c->rbsz / 1024;
|
|
c->reg[Maxfrs] = c->rbsz << 16;
|
|
c->reg[Hlreg0] |= Jumboen;
|
|
|
|
c->reg[Rdbal] = PCIWADDR(c->rdba);
|
|
c->reg[Rdbah] = 0;
|
|
c->reg[Rdlen] = c->nrd*sizeof(Rd);
|
|
c->reg[Rdh] = 0;
|
|
c->reg[Rdt] = c->rdt = 0;
|
|
|
|
c->reg[Rxdctl] = Renable;
|
|
while((c->reg[Rxdctl] & Renable) == 0)
|
|
;
|
|
/* TODO? bump the tail pointer RDT to enable descriptors
|
|
fetching by setting it to the ring length minus 1. Pg 145 */
|
|
c->reg[Rxctrl] |= Rxen;
|
|
}
|
|
|
|
static void
|
|
replenish(Ctlr *c, uint rdh)
|
|
{
|
|
int rdt, m, i;
|
|
Block *b;
|
|
Rd *r;
|
|
|
|
m = c->nrd - 1;
|
|
i = 0;
|
|
for(rdt = c->rdt; Next(rdt, m) != rdh; rdt = Next(rdt, m)){
|
|
b = allocb(c->rbsz+BY2PG);
|
|
b->rp = (uchar*)PGROUND((uintptr)b->base);
|
|
b->wp = b->rp;
|
|
c->rb[rdt] = b;
|
|
r = c->rdba + rdt;
|
|
r->addr[0] = PCIWADDR(b->rp);
|
|
r->status = 0;
|
|
c->rdfree++;
|
|
i++;
|
|
}
|
|
if(i)
|
|
c->reg[Rdt] = c->rdt = rdt;
|
|
}
|
|
|
|
static int
|
|
rim(void *v)
|
|
{
|
|
return ((Ctlr*)v)->rim != 0;
|
|
}
|
|
|
|
static uchar zeroea[Eaddrlen];
|
|
|
|
static void
|
|
rproc(void *v)
|
|
{
|
|
uint m, rdh;
|
|
Block *b;
|
|
Ctlr *c;
|
|
Ether *e;
|
|
Rd *r;
|
|
|
|
e = v;
|
|
c = e->ctlr;
|
|
m = c->nrd - 1;
|
|
rdh = 0;
|
|
while(waserror())
|
|
;
|
|
loop:
|
|
replenish(c, rdh);
|
|
im(c, Irx0);
|
|
sleep(&c->rrendez, rim, c);
|
|
loop1:
|
|
c->rim = 0;
|
|
if(c->nrd - c->rdfree >= 16)
|
|
replenish(c, rdh);
|
|
r = c->rdba + rdh;
|
|
if(!(r->status & Rdd))
|
|
goto loop; /* UGH */
|
|
b = c->rb[rdh];
|
|
c->rb[rdh] = 0;
|
|
b->wp += r->length;
|
|
if((r->status & 1)){
|
|
if(r->status & Ipcs)
|
|
b->flag |= Bipck;
|
|
b->checksum = r->cksum;
|
|
}
|
|
// r->status = 0;
|
|
etheriq(e, b);
|
|
c->rdfree--;
|
|
rdh = Next(rdh, m);
|
|
goto loop1; /* UGH */
|
|
}
|
|
|
|
static void
|
|
promiscuous(void *a, int on)
|
|
{
|
|
Ctlr *c;
|
|
Ether *e;
|
|
|
|
e = a;
|
|
c = e->ctlr;
|
|
if(on)
|
|
c->reg[Fctrl] |= Upe | Mpe;
|
|
else
|
|
c->reg[Fctrl] &= ~(Upe | Mpe);
|
|
}
|
|
|
|
static void
|
|
multicast(void *a, uchar *ea, int on)
|
|
{
|
|
int b, i;
|
|
Ctlr *c;
|
|
Ether *e;
|
|
|
|
e = a;
|
|
c = e->ctlr;
|
|
|
|
/*
|
|
* multiple ether addresses can hash to the same filter bit,
|
|
* so it's never safe to clear a filter bit.
|
|
* if we want to clear filter bits, we need to keep track of
|
|
* all the multicast addresses in use, clear all the filter bits,
|
|
* then set the ones corresponding to in-use addresses.
|
|
*
|
|
* Extracts the 12 bits, from a multicast address, to determine which
|
|
* bit-vector to set in the multicast table. The hardware uses 12 bits, from
|
|
* incoming rx multicast addresses, to determine the bit-vector to check in
|
|
* the MTA. Which of the 4 combination, of 12-bits, the hardware uses is set
|
|
* by the MO field of the MCSTCTRL. The MO field is set during initialization
|
|
* to mc_filter_type.
|
|
*
|
|
* The MTA is a register array of 128 32-bit registers. It is treated
|
|
* like an array of 4096 bits. We want to set bit
|
|
* BitArray[vector_value]. So we figure out what register the bit is
|
|
* in, read it, OR in the new bit, then write back the new value. The
|
|
* register is determined by the upper 7 bits of the vector value and
|
|
* the bit within that register are determined by the lower 5 bits of
|
|
* the value.
|
|
*
|
|
* when Mcstctrl.Mo == 0, use bits [47:36] of the address
|
|
* register index = bits [47:41]
|
|
* which bit in the above register = bits [40:36]
|
|
*/
|
|
i = ea[5] >> 1; /* register index = 47:41 (7 bits) */
|
|
b = (ea[5]&1)<<4 | ea[4]>>4; /* which bit in the above register = 40:36 (5 bits) */
|
|
b = 1 << b;
|
|
if(on)
|
|
c->mta[i] |= b;
|
|
// else
|
|
// c->mta[i] &= ~b;
|
|
c->reg[Mta+i] = c->mta[i];
|
|
c->reg[Mcstctrl] = Mfe;
|
|
/* for(i = 0; i < 128; i++) c->reg[Mta + i] = -1; brute force it to work for testing */
|
|
}
|
|
|
|
static int
|
|
detach(Ctlr *c)
|
|
{
|
|
int i;
|
|
u32int l, h;
|
|
|
|
l = c->reg[Ral];
|
|
h = c->reg[Rah];
|
|
if (h & Av) {
|
|
c->ra[0] = l & 0xFF;
|
|
c->ra[1] = l>>8 & 0xFF;
|
|
c->ra[2] = l>>16 & 0xFF;
|
|
c->ra[3] = l>>24 & 0xFF;
|
|
c->ra[4] = h & 0xFF;
|
|
c->ra[5] = h>>8 & 0xFF;
|
|
}
|
|
c->reg[Imc] = ~0;
|
|
c->reg[Ctrl] |= Rst;
|
|
for(i = 0; i < 100; i++){
|
|
delay(1);
|
|
if((c->reg[Ctrl] & Rst) == 0)
|
|
break;
|
|
}
|
|
if (i >= 100)
|
|
return -1;
|
|
delay(10);
|
|
|
|
/* not cleared by reset; kill it manually. */
|
|
for(i = 1; i < 16; i++)
|
|
c->reg[Rah + i] &= ~(1 << 31);
|
|
for(i = 0; i < 128; i++)
|
|
c->reg[Mta + i] = 0;
|
|
for(i = 1; i < 640; i++)
|
|
c->reg[Vfta + i] = 0;
|
|
c->reg[Ctrlext] &= ~Drvload; /* driver works without this */
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
shutdown(Ether *e)
|
|
{
|
|
detach(e->ctlr);
|
|
}
|
|
|
|
static int
|
|
reset(Ctlr *c)
|
|
{
|
|
int i;
|
|
|
|
while((c->reg[Eec] & AutoRd) == 0)
|
|
;
|
|
while((c->reg[Eemngctl] & CfgDone0) == 0)
|
|
;
|
|
while((c->reg[Eemngctl] & CfgDone1) == 0)
|
|
;
|
|
while((c->reg[Rdrxctl] & Dmaidone) == 0)
|
|
;
|
|
if(detach(c)){
|
|
print("iX550: reset timeout\n");
|
|
return -1;
|
|
}
|
|
while((c->reg[Eec] & AutoRd) == 0)
|
|
;
|
|
while((c->reg[Eemngctl] & CfgDone0) == 0)
|
|
;
|
|
while((c->reg[Eemngctl] & CfgDone1) == 0)
|
|
;
|
|
while((c->reg[Rdrxctl] & Dmaidone) == 0)
|
|
;
|
|
readstats(c);
|
|
for(i = 0; i<nelem(c->stats); i++)
|
|
c->stats[i] = 0;
|
|
|
|
/* configure interrupt mapping */
|
|
c->reg[Ivar] = Intalloc0 | Intallocval0 | intalloc1 | Intallocval1;
|
|
|
|
/* interrupt throttling goes here. */
|
|
for(i = Itr; i < Itr + 20; i++)
|
|
c->reg[i] = 1<<3; /* 1 interval */
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
txinit(Ctlr *c)
|
|
{
|
|
Block *b;
|
|
int i;
|
|
|
|
c->reg[Txdctl] = 16<<Wthresh | 16<<Pthresh;
|
|
for(i = 0; i < c->ntd; i++){
|
|
b = c->tb[i];
|
|
c->tb[i] = 0;
|
|
if(b)
|
|
freeb(b);
|
|
}
|
|
memset(c->tdba, 0, c->ntd * sizeof(Td));
|
|
c->reg[Tdbal] = PCIWADDR(c->tdba);
|
|
c->reg[Tdbah] = 0;
|
|
c->reg[Tdlen] = c->ntd*sizeof(Td);
|
|
c->reg[Tdh] = 0;
|
|
c->reg[Tdt] = 0;
|
|
c->tdh = c->ntd - 1;
|
|
c->tdt = 0;
|
|
|
|
c->reg[Txdctl] |= Ten;
|
|
c->reg[Dmatxctl] |= Txen;
|
|
|
|
}
|
|
|
|
static void
|
|
attach(Ether *e)
|
|
{
|
|
Ctlr *c;
|
|
int t;
|
|
char buf[KNAMELEN];
|
|
|
|
c = e->ctlr;
|
|
c->edev = e; /* point back to Ether* */
|
|
lock(&c->alock);
|
|
if(c->alloc){
|
|
unlock(&c->alock);
|
|
return;
|
|
}
|
|
|
|
c->nrd = Nrd;
|
|
c->ntd = Ntd;
|
|
t = c->nrd * sizeof *c->rdba + 255;
|
|
t += c->ntd * sizeof *c->tdba + 255;
|
|
t += (c->ntd + c->nrd) * sizeof(Block*);
|
|
c->alloc = malloc(t);
|
|
unlock(&c->alock);
|
|
if(c->alloc == nil)
|
|
error(Enomem);
|
|
|
|
c->rdba = (Rd*)ROUNDUP((uintptr)c->alloc, 256);
|
|
c->tdba = (Td*)ROUNDUP((uintptr)(c->rdba + c->nrd), 256);
|
|
c->rb = (Block**)(c->tdba + c->ntd);
|
|
c->tb = (Block**)(c->rb + c->nrd);
|
|
|
|
rxinit(c);
|
|
txinit(c);
|
|
|
|
c->reg[Ctrlext] |= Drvload; /* driver works without this */
|
|
snprint(buf, sizeof buf, "#l%dl", e->ctlrno);
|
|
kproc(buf, lproc, e);
|
|
snprint(buf, sizeof buf, "#l%dr", e->ctlrno);
|
|
kproc(buf, rproc, e);
|
|
snprint(buf, sizeof buf, "#l%dt", e->ctlrno);
|
|
kproc(buf, tproc, e);
|
|
}
|
|
|
|
static void
|
|
interrupt(Ureg*, void *v)
|
|
{
|
|
int icr, im;
|
|
Ctlr *c;
|
|
Ether *e;
|
|
|
|
e = v;
|
|
c = e->ctlr;
|
|
ilock(&c->imlock);
|
|
c->reg[Imc] = ~0;
|
|
im = c->im;
|
|
while((icr = c->reg[Icr] & c->im) != 0){
|
|
if(icr & Lsc){
|
|
im &= ~Lsc;
|
|
c->lim = icr & Lsc;
|
|
wakeup(&c->lrendez);
|
|
}
|
|
if(icr & Irx0){
|
|
im &= ~Irx0;
|
|
c->rim = icr & Irx0;
|
|
wakeup(&c->rrendez);
|
|
}
|
|
if(icr & Itx0){
|
|
im &= ~Itx0;
|
|
c->tim = icr & Itx0;
|
|
wakeup(&c->trendez);
|
|
}
|
|
}
|
|
c->reg[Ims] = c->im = im;
|
|
iunlock(&c->imlock);
|
|
}
|
|
|
|
static void
|
|
scan(void)
|
|
{
|
|
uvlong io, iomsi;
|
|
void *mem, *memmsi;
|
|
int pciregs, pcimsix;
|
|
Ctlr *c;
|
|
Pcidev *p;
|
|
|
|
p = 0;
|
|
while(p = pcimatch(p, 0x8086, 0x15c8)){ /* X553/X550-AT 10GBASE-T */
|
|
pcimsix = 4;
|
|
pciregs = 0;
|
|
if((p->mem[pciregs].bar & 1) != 0
|
|
|| (p->mem[pcimsix].bar & 1) != 0)
|
|
continue;
|
|
if(nctlr == nelem(ctlrtab)){
|
|
print("iX550: too many controllers\n");
|
|
return;
|
|
}
|
|
c = malloc(sizeof *c);
|
|
if(c == nil){
|
|
print("iX550: can't allocate memory\n");
|
|
continue;
|
|
}
|
|
io = p->mem[pciregs].bar & ~0xF;
|
|
mem = vmap(io, p->mem[pciregs].size);
|
|
if(mem == nil){
|
|
print("iX550: can't map regs %llux\n", io);
|
|
free(c);
|
|
continue;
|
|
}
|
|
iomsi = p->mem[pcimsix].bar & ~0xF;
|
|
memmsi = vmap(iomsi, p->mem[pcimsix].size);
|
|
if(memmsi == nil){
|
|
print("iX550: can't map msi-x regs %llux\n", iomsi);
|
|
vunmap(mem, p->mem[pciregs].size);
|
|
free(c);
|
|
continue;
|
|
}
|
|
pcienable(p);
|
|
c->p = p;
|
|
c->io = io;
|
|
c->reg = (u32int*)mem;
|
|
c->regmsi = (u32int*)memmsi;
|
|
c->rbsz = ROUND(Mtu, 1024);
|
|
if(reset(c)){
|
|
print("iX550: can't reset\n");
|
|
vunmap(mem, p->mem[pciregs].size);
|
|
vunmap(memmsi, p->mem[pcimsix].size);
|
|
free(c);
|
|
continue;
|
|
}
|
|
pcisetbme(p);
|
|
ctlrtab[nctlr++] = c;
|
|
}
|
|
}
|
|
|
|
static int
|
|
pnp(Ether *e)
|
|
{
|
|
static uchar zeros[Eaddrlen];
|
|
int i;
|
|
Ctlr *c = nil;
|
|
uchar *p;
|
|
|
|
if(nctlr == 0)
|
|
scan();
|
|
for(i = 0; i < nctlr; i++){
|
|
c = ctlrtab[i];
|
|
if(c == nil || c->flag & Factive)
|
|
continue;
|
|
if(e->port == 0 || e->port == c->io)
|
|
break;
|
|
}
|
|
if (i >= nctlr)
|
|
return -1;
|
|
|
|
if(memcmp(c->ra, zeros, Eaddrlen) != 0)
|
|
memmove(e->ea, c->ra, Eaddrlen);
|
|
|
|
p = e->ea;
|
|
c->reg[Ral] = p[3]<<24 | p[2]<<16 | p[1]<<8 | p[0];
|
|
c->reg[Rah] = p[5]<<8 | p[4] | 1<<31;
|
|
|
|
c->flag |= Factive;
|
|
e->ctlr = c;
|
|
e->port = (uintptr)c->reg;
|
|
e->irq = c->p->intl;
|
|
e->tbdf = c->p->tbdf;
|
|
e->mbps = 10000;
|
|
e->maxmtu = Mtu;
|
|
|
|
e->arg = e;
|
|
e->attach = attach;
|
|
e->ctl = ctl;
|
|
e->ifstat = ifstat;
|
|
e->multicast = multicast;
|
|
e->promiscuous = promiscuous;
|
|
e->shutdown = shutdown;
|
|
e->transmit = transmit;
|
|
|
|
intrenable(e->irq, interrupt, e, e->tbdf, e->name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
etherx550link(void)
|
|
{
|
|
addethercard("iX550", pnp);
|
|
}
|