Plan 9 from Bell Labs’s /usr/web/sources/patch/applied/82598/ether82598.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


/*
 * intel 10gbe pcie driver
 * copyright © 2007, coraid, inc.
 */
#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"

/*
 * // comments note conflicts with 82563-style drivers
 *
 */
enum{
	/* general */
	Ctrl		= 0x00000/4,	/* Device Control */
	Status		= 0x00008/4,	/* Device Status */
	Ctrlext		= 0x00018/4,	/* Extended Device Control */
	Esdp		= 0x00020/4,	// extended sdp control
	Esodp		= 0x00028/4,	// extended od sdp control
	Ledctl		= 0x00200/4,	// led control
	Tcptimer		= 0x0004c/4,	// tcp timer
	Ecc		= 0x110b0/4,	// errata ecc control magic

	/* nvm */
	Eec		= 0x10010/4,	// eeprom/flash control
	Eerd		= 0x10014/4,	// eeprom read
	Fla		= 0x1001c/4,	// flash access
	Flop		= 0x1013c/4,	// flash opcode
	Grc		= 0x10200/4,	// general rx 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,	// " ayto clear
	Iam		= 0x00890/4,	// " auto mask enable
	Itr		= 0x00820/4,	// " throttling rate (0-19)
	Ivar		= 0x00900/4,	// " vector allocation regs.
	/*msi interrupt */
	Msixt		= 0x0000/4,	// msix table (bar3)
	Msipba		= 0x2000/4,	// msix pending bit array (bar3)
	Pbacl		= 0x11068/4,	// pba clear
	Gpie		= 0x00898/4,	// general purpose int enable

	/* flow control */
	Pfctop		= 0x03008/4,	// priority flow ctl type opcode
	Fcttv		= 0x03200/4,	// " transmit timer value (0-3)
	Fcrtl		= 0x03220/4,	// " rx threshold low (0-7) +8n
	Fcrth		= 0x03260/4,	// " rx threshold high (0-7) +8n
	Rcrtv		= 0x032a0/4,	// " refresh value threshold
	Tfcs		= 0x0ce00/4,	// " tx status

	/* rx dma */
	Rbal		= 0x01000/4,	// rx desc base low (0-63) +0x40n
	Rbah		= 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.
	Dcarxctl		= 0x02200/4,	// rx dca control
	Rdrxctl		= 0x02f00/4,	// rx dma control
	Rxpbsize		= 0x03c00/4,	// rx packet buffer size
	Rxctl		= 0x03000/4,	// rx control
	Dropen		= 0x03d04/4,	// drop enable control

	/* rx */
	Rxcsum		= 0x05000/4,	// rx checksum control
	Rfctl		= 0x04008/4,	// rx filter control
	Mta		= 0x05200/4,	// multicast table array (0-127)
	Ral		= 0x05400/4,	// rx address low
	Rah		= 0x05404/4,
	Psrtype		= 0x05480/4,	// packet split rx type.
	Vfta		= 0x0a000/4,	// vlan filter table array.
	Fctrl		= 0x05080/4,	// filter control
	Vlnctrl		= 0x05088/4,	// vlan control
	Msctctrl		= 0x05090/4,	// multicast control
	Mrqc		= 0x05818/4,	// multiple rx queues cmd
	Vmdctl		= 0x0581c/4,	// vmdq control
	Imir		= 0x05a80/4,	// immediate irq rx (0-7)
	Imirext		= 0x05aa0/4,	// immediate irq rx ext
	Imirvp		= 0x05ac0/4,	// immediate irq vlan priority
	Reta		= 0x05c00/4,	// redirection table
	Rssrk		= 0x05c80/4,	// rss random key

	/* 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
	Tdwbal		= 0x06038/4,	// " write-back address low
	Tdwbah		= 0x0603c/4,

	Dtxctl		= 0x07e00/4,	// tx dma control
	Tdcatxctrl	= 0x07200/4,	// tx dca register (0-15)
	Tipg		= 0x0cb00/4,	// tx inter-packet gap
	Txpbsize		= 0x0cc00/4,	// tx packet-buffer size (0-15)

	/* mac */
	Hlreg0		= 0x04240/4,	// highlander control reg 0
	Hlreg1		= 0x04244/4,	// highlander control reg 1 (ro)
	Msca		= 0x0425c/4,	// mdi signal cmd & addr
	Msrwd		= 0x04260/4,	// mdi single rw data
	Mhadd		= 0x04268/4,	// mac addr high & max frame
	Pcss1		= 0x04288/4,	// xgxs status 1
	Pcss2		= 0x0428c/4,
	Xpcss		= 0x04290/4,	// 10gb-x pcs status
	Serdesc		= 0x04298/4,	// serdes control
	Macs		= 0x0429c/4,	// fifo control & report
	Autoc		= 0x042a0/4,	// autodetect control & status
	Links		= 0x042a4/4,	// link status
	Autoc2		= 0x042a8/4,
};

enum{
	/* Ctrl */
	Rst		= 1<<26,	/* full nic reset */

	/* Txdctl */
	Ten		= 1<<25,

	/* Fctrl */
	Bam		= 1<<10,	/* broadcast accept mode */
	Upe 		= 1<<9,	/* unicast promiscuous */
	Mpe 		= 1<<8,	/* multicast promiscuous */

	/* Rxdctl */
	Pthresh		= 0,		/* prefresh threshold shift in bits */
	Hthresh		= 8,		/* host buffer minimum threshold " */
	Wthresh		= 16,		/* writeback threshold */
	Renable		= 1<<25,

	/* Rxctl */
	Rxen		= 1<<0,
	Dmbyps		= 1<<1,

	/* Rdrxctl */
	Rdmt½		= 0,
	Rdmt¼		= 1,
	Rdmt⅛		= 2,

	/* Rxcsum */
	Ippcse		= 1<<12,	/* ip payload checksum enable */

	/* Eerd */
	EEstart		= 1<<0,	/* Start Read */
	EEdone		= 1<<1,	/* Read done */

	/* interrupts */
	Irx0		= 1<<0,	/* driver defined */
	Itx0		= 1<<1,	/* driver defined */
	Lsc		= 1<<20,	/* link status change */

	/* Links */
	Lnkup	= 1<<30,
	Lnkspd	= 1<<29,

	/* Hlreg0 */
	Jumboen	= 1<<2,
};

typedef struct{
	uint	reg;
	char	*name;
}Stat;

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",
	0x3f60,	"xon tx",
	0xcf60,	"xon rx",
	0x3f68,	"xoff tx",
	0xcf68,	"xoff rx",
	0x405c,	"rx 040",
	0x4060,	"rx 07f",
	0x4064,	"rx 100",
	0x4068,	"rx 200",
	0x406c,	"rx 3ff",
	0x4070,	"rx big",
	0x4074,	"rx ok",
	0x4078,	"rx bcast",
	0x3fc0,	"rx no buf0",
	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",
	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 */
	Tcpcs	= 1<<4,	/* tcp checksum calcuated */
	Vp	= 1<<3,	/* 802.1q packet matched vet */
	Ixsm	= 1<<2,	/* ignore checksum */
	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];
	ushort	length;
	uchar	cso;
	uchar	cmd;
	uchar	status;
	uchar	css;
	ushort	vlan;
}Td;

enum{
	Factive	= 1<<0,
	Fstarted	= 1<<1,
};

typedef struct{
	Pcidev	*p;
	u32int	*reg;
	u32int	*reg3;
	uchar	flag;
	int	nrd, ntd, nrb, rbsz;
	QLock	slock, alock, tlock;
	Rendez	lrendez, trendez, rrendez;
	uint	im, lim, rim, tim;
	Lock	imlock;
	char	*alloc;
	Rd	*rdba;
	Block	**rb;
	uint	rdt, rdfree;
	Td	*tdba;
	uint	tdh, tdt;
	Block	**tb;
	uchar	ra[Eaddrlen];
	uchar	mta[128];
	ulong	stats[nelem(stattab)];
	uint	speeds[3];
}Ctlr;

/* tweakable paramaters */
enum{
	Rbsz	= 12*1024,
	Nrd	= 256,
	Ntd	= 256,
	Nrb	= 256,
};

static	Ctlr	*ctlrtab[4];
static	int	nctlr;
static	Lock	rblock;
static	Block	*rbpool;

static void
readstats(Ctlr *c)
{
	int i;

	qlock(&c->slock);
	for(i = 0; i < nelem(c->stats); i++)
		c->stats[i] += c->reg[stattab[i].reg>>2];
	qunlock(&c->slock);
}

static int speedtab[] = {
	0,
	1000,
	10000,
};

static long
ifstat(Ether *e, void *a, long n, ulong offset)
{
	Ctlr *c;
	char *s, *p, *q;
	uint i, *t;

	c = e->ctlr;
	p = s = malloc(2*READSTR);
	q = p+2*READSTR;

	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)
{
	Ether *e;
	Ctlr *c;
	int r, i;

	e = v;
	c = e->ctlr;
loop:
	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;
	goto loop;
}

static long
ctl(Ether *, void *, long)
{
	error(Ebadarg);
	return -1;
}

static Block*
rballoc(void)
{
	Block *b;

	ilock(&rblock);
	if(b = rbpool){
		rbpool = b->next;
		b->next = 0;
	}
	iunlock(&rblock);
	return b;
}

void
rbfree(Block *b)
{
	b->rp = b->wp = (uchar*)PGROUND((uintptr)b->base);
	ilock(&rblock);
	b->next = rbpool;
	rbpool = b;
	iunlock(&rblock);
}

#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;
}

void
transmit(Ether *e)
{
	Ctlr *c;
	Block *b;
	Td *t;
	uint i, m, tdt, tdh;

	c = e->ctlr;
//	qlock(&c->tlock);
	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)
{
	Ether *e;
	Ctlr *c;

	e = v;
	c = e->ctlr;
loop:
	sleep(&c->trendez, tim, c);		/* transmit kicks us */
	c->tim = 0;
	transmit(e);
	goto loop;
}

static void
rxinit(Ctlr *c)
{
	Block *b;
	int i;

	c->reg[Rxctl] &= ~Rxen;
	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] |= Ipcs;
	c->reg[Srrctl] = (c->rbsz+1023)/1024;
	c->reg[Mhadd] = c->rbsz<<16;
	c->reg[Hlreg0] |= Jumboen;

	c->reg[Rbal] = PCIWADDR(c->rdba);
	c->reg[Rbah] = 0;
	c->reg[Rdlen] = c->nrd*sizeof(Rd);
	c->reg[Rdh] = 0;
	c->reg[Rdt] = c->rdt = 0;

	c->reg[Rdrxctl] = Rdmt¼;
	c->reg[Rxdctl] = 8<<Wthresh|8<<Pthresh|4<<Hthresh|Renable;
	c->reg[Rxctl] |= Rxen|Dmbyps;
}

static void
replenish(Ctlr *c, uint rdh)
{
	Rd *r;
	Block *b;
	int rdt, m, i;

	m = c->nrd-1;
	i = 0;
	for(rdt = c->rdt; Next(rdt, m) != rdh; rdt = Next(rdt, m)){
		r = c->rdba+rdt;
		if(!(b = rballoc())){
			print("82598: no buffers\n");
			break;
		}
		c->rb[rdt] = b;
		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];

void
rproc(void *v)
{
	Ether *e;
	Ctlr *c;
	Block *b;
	Rd *r;
	uint m, rdh;

	e = v;
	c = e->ctlr;
	m = c->nrd-1;
	rdh = 0;
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;
	b = c->rb[rdh];
	c->rb[rdh] = 0;
	b->wp += r->length;
	b->lim = b->wp;		/* lie like a dog */
	if(!(r->status&Ixsm)){
		if(r->status&Ipcs)
			b->flag |= Bipck;
		if(r->status&Tcpcs)
			b->flag |= Btcpck|Budpck;
		b->checksum = r->cksum;
	}
//	r->status = 0;
	etheriq(e, b, 1);
	c->rdfree--;
	rdh = Next(rdh, m);
	goto loop1;
}

static void
promiscuous(void *a, int on)
{
	Ether *e;
	Ctlr *c;

	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)
{
	Ether *e;
	Ctlr *c;
	int b, i;

	e = a;
	c = e->ctlr;

	i = ea[5]>>1;
	b = (ea[5]&1)<<4|ea[4]>>4;
	b = 1<<b;
	if(on)
		c->mta[i] |= b;
	else
		c->mta[i] &= ~b;
	c->reg[Mta+i] = c->mta[i];
}

static int
detach(Ctlr *c)
{
	int i;

	c->reg[Imc] = ~0;
	c->reg[Ctrl] |= Rst;
	for(i = 0; i < 100; i++){
		delay(1);
		if((c->reg[Ctrl]&Rst) == 0)
			goto good;
	}
	return -1;
good:
	/* errata */
	delay(50);
	c->reg[Ecc] &= ~(1<<21|1<<18|1<<9|1<<6);

	/* not cleared by reset; kill it manually. */
	for(i = 1; i<16; i++)
		c->reg[Rah] &= ~(1<<31);
	for(i = 0; i<128; i++)
		c->reg[Mta+i] = 0;
	for(i = 1; i<640; i++)
		c->reg[Vfta+i] = 0;
	return 0;
}

static void
shutdown(Ether *e)
{
	detach(e->ctlr);
}

/* ≤ 20ms */
static ushort
eeread(Ctlr *c, int i)
{
	c->reg[Eerd] = EEstart|i<<2;
	while((c->reg[Eerd]&EEdone) == 0)
		;
	return c->reg[Eerd]>>16;
}

static int
eeload(Ctlr *c)
{
	ushort u, v, p, l, i, j;

	if((eeread(c, 0)&0xc0) != 0x40)
		return -1;
	u = 0;
	for(i = 0; i < 0x40; i++)
		u +=  eeread(c, i);
	for(i = 3; i < 0xf; i++){
		p = eeread(c, i);
		l = eeread(c, p++);
		if((int)p+l+1 > 0xffff)
			continue;
		for(j = p; j < p+l; j++)
			u += eeread(c, j);
	}
	if(u != 0xbaba)
		return -1;
	if(c->reg[Status]&1<<3)
		u = eeread(c, 10);
	else
		u = eeread(c, 9);
	u++;
	for(i = 0; i<Eaddrlen;){
		v = eeread(c, u+i/2);
		c->ra[i++] = v;
		c->ra[i++] = v>>8;
	}
	c->ra[5] += (c->reg[Status]&0xc)>>2;
	return 0;
}

static int
reset(Ctlr *c)
{
	uchar *p;
	int i;

	if(detach(c)){
		print("82598: reset timeout\n");
		return -1;
	}
	if(eeload(c)){
		print("82598: eeprom failure\n");
		return -1;
	}
	p = c->ra;
	c->reg[Ral] = p[3]<<24|p[2]<<16|p[1]<<8|p[0];
	c->reg[Rah] = p[5]<<8|p[4]|1<<31;

	readstats(c);
	for(i = 0; i<nelem(c->stats); i++)
		c->stats[i] = 0;

	c->reg[Ctrlext] |= 1<<16;
	/* make some guesses for flow control */
	c->reg[Fcrtl] = 0x10000|1<<31;
	c->reg[Fcrth] = 0x40000|1<<31;
	c->reg[Rcrtv] = 0x6000;

	/* configure interrupt mapping (don't ask) */
	c->reg[Ivar+0] = 0|1<<7;
	c->reg[Ivar+64/4] = 1|1<<7;
//	c->reg[Ivar+97/4] = (2|1<<7)<<8*(97%4);

	/* interrupt throttling goes here. */
	for(i = Itr; i<Itr+20; i++)
		c->reg[i] = 128;		/* ¼µs intervals */
	c->reg[Itr+Itx0] = 256;
	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;
}

static void
attach(Ether *e)
{
	Block *b;
	Ctlr *c;
	int t;
	char buf[KNAMELEN];

	c = e->ctlr;
	qlock(&c->alock);
	if(c->alloc){
		qunlock(&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);
	qunlock(&c->alock);
	if(c->alloc == 0)
		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);

	if(waserror()){
		while(b = rballoc()){
			b->free = 0;
			freeb(b);
		}
		free(c->alloc);
		c->alloc = 0;
		nexterror();
	}
	for(c->nrb = 0; c->nrb < 2*Nrb; c->nrb++){
		if(!(b = allocb(c->rbsz+BY2PG)))
			error(Enomem);
		b->free = rbfree;
		freeb(b);
	}
	poperror();

	rxinit(c);
	txinit(c);

	sprint(buf, "#l%dl", e->ctlrno);
	kproc(buf, lproc, e);
	sprint(buf, "#l%dr", e->ctlrno);
	kproc(buf, rproc, e);
	sprint(buf, "#l%dt", e->ctlrno);
	kproc(buf, tproc, e);
}

static void
interrupt(Ureg*, void *v)
{
	Ether *e;
	Ctlr *c;
	int icr, im;

	e = v;
	c = e->ctlr;
	ilock(&c->imlock);
	c->reg[Imc] = ~0;
	im = c->im;
	while(icr = c->reg[Icr]&c->im){
		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)
{
	Pcidev *p;
	ulong io, io3;
	void *mem, *mem3;
	Ctlr *c;

	p = 0;
	while(p = pcimatch(p, 0x8086, 0)){
		switch(p->did){
		case 0x10c6:	/* 82598 af dual port */
		case 0x10c7:	/* 82598 af single port */
		case 0x10b6:	/* 82598 backplane */
		case 0x10dd:	/* 82598 at cx4 */
			break;
		default:
			continue;
		}
		if(nctlr == nelem(ctlrtab)){
			print("i82598: too many controllers\n");
			return;
		}
		io = p->mem[0].bar&~0xf;
		mem = vmap(io, p->mem[0].size);
		if(mem == 0){
			print("i82598: cant map %#p\n", p->mem[0].bar);
			continue;
		}
		io3 = p->mem[3].bar&~0xf;
		mem3 = vmap(io3, p->mem[3].size);
		if(mem3 == 0){
			print("i82598: cant map %#p\n", p->mem[3].bar);
			vunmap(mem, p->mem[0].size);
			continue;
		}
		c = malloc(sizeof *c);
		c->p = p;
		c->reg = (u32int*)mem;
		c->reg3 = (u32int*)mem3;
		c->rbsz = Rbsz;
		if(reset(c)){
			print("i82598: cant reset\n");
			free(c);
			vunmap(mem, p->mem[0].size);
			vunmap(mem3, p->mem[3].size);
			continue;
		}
		pcisetbme(p);
		ctlrtab[nctlr++] = c;
	}
}

static int
pnp(Ether *e)
{
	Ctlr *c;
	int i;

	if(nctlr == 0)
		scan();
	for(i = 0; i<nctlr; i++){
		c = ctlrtab[i];
		if(c == 0 || c->flag&Factive)
			continue;
		if(e->port == 0 || e->port == (ulong)c->reg)
			goto found;
	}
	return -1;
found:
	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 = c->rbsz;
	memmove(e->ea, c->ra, Eaddrlen);
	e->arg = e;
	e->attach = attach;
	e->ctl = ctl;
	e->ifstat = ifstat;
	e->interrupt = interrupt;
	e->multicast = multicast;
	e->promiscuous = promiscuous;
	e->shutdown = shutdown;
	e->transmit = transmit;

	return 0;
}

void
ether82598link(void)
{
	addethercard("i82598", pnp);
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.