ref: d843bc8e22a7db269867fdc702bd0043e4f499a0
parent: 426c989317108ace29978920e99efb0ff1a1d02a
author: cinap_lenrek <[email protected]>
date: Sun Dec 23 12:48:11 EST 2018
etherx550: add intel 10GB ethernet controlller x550 driver (thanks joe9)
--- /dev/null
+++ b/sys/src/9/pc/etherx550.c
@@ -1,0 +1,921 @@
+/*
+ * 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/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 {
+ Rbsz = 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 + 1023)/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);
+}
+
+extern void addvgaseg(char*, ulong, ulong);
+
+static void
+scan(void)
+{
+ uintptr 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(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 %#p\n", io);
+ free(c);
+ continue;
+ }
+ if (nctlr == 0)
+ addvgaseg("pci.ctlr0.bar0", p->mem[pciregs].bar & ~0xf, p->mem[pciregs].size);
+ else if (nctlr == 1)
+ addvgaseg("pci.ctlr1.bar0", p->mem[pciregs].bar & ~0xf, p->mem[pciregs].size);
+ iomsi = p->mem[pcimsix].bar & ~0xf;
+ memmsi = vmap(iomsi, p->mem[pcimsix].size);
+ if(memmsi == nil){
+ print("iX550: can't map msi-x regs %#p\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 = Rbsz;
+ if(reset(c)){
+ print("iX550: can't reset\n");
+ free(c);
+ vunmap(mem, p->mem[pciregs].size);
+ vunmap(memmsi, p->mem[pcimsix].size);
+ 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 = c->rbsz;
+
+ 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);
+}
--- a/sys/src/9/pc/pc
+++ b/sys/src/9/pc/pc
@@ -62,6 +62,7 @@
ether82563 pci
ether82598 pci
ether83815 pci
+ etherx550 pci
etherbcm pci
etherdp83820 pci ethermii
etherec2t ether8390
--- a/sys/src/9/pc64/pc64
+++ b/sys/src/9/pc64/pc64
@@ -59,6 +59,7 @@
# ether82557 pci
ether82563 pci
ether82598 pci
+ etherx550 pci
# ether83815 pci
etherbcm pci
# etherdp83820 pci ethermii