/*
* marvell 88e8057 yukon2
* copyright © 2009-10 erik quanstrom
*/
#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"
#define Pciwaddrh(x) 0
#define Pciwaddrl(x) PCIWADDR(x)
#define is64() (sizeof(uintptr) == 8)
#define dprint(...) if(debug) print(__VA_ARGS__); else {}
extern void sfence(void);
enum {
Nctlr = 4,
Nrb = 1024,
Rbalign = 64,
Fprobe = 1<<0,
Sringcnt = 2048,
Tringcnt = 512,
// Rringcnt = Nrb,
Rringcnt = 512,
Rringl = Rringcnt - 8,
};
enum {
/* pci registers */
Pciphy = 0x40,
Pciclk = 0x80,
Pciasp = 0x84,
Pcistate = 0x88,
Pcicf0 = 0x90,
Pcicf1 = 0x94,
/* “csr” registers */
Ctst = 0x0004/2, /* control and status */
Pwrctl = 0x0007, /* power control */
Isr = 0x0008/4, /* interrupt src */
Ism = 0x000c/4, /* interrupt mask */
Hwe = 0x0010/4, /* hw error */
Hwem = 0x0014/4, /* hw error mask*/
Isrc2 = 0x001c/4,
Eisr = 0x0024/4,
Lisr = 0x0028/4, /* leave isr */
Macadr = 0x0100, /* mac address 2ports*3 */
Pmd = 0x0119,
Maccfg = 0x011a,
Chip = 0x011b,
Ramcnt = 0x011c, /* # of 4k blocks */
Hres = 0x011e,
Clkgate = 0x011d,
Clkctl = 0x0120/4,
Tstctl1 = 0x0158,
Tstctl2 = 0x0159,
Gpio = 0x015c/4,
Rictl = 0x01a0, /* ri ram buffer ctl */
Rib = 0x0190, /* ri buffer0 */
/* other unoffset registers */
Asfcs = 0x0e68, /* asf command and status */
Asfhost = 0x0e6c/4,
Statctl = 0x0e80/4, /* status */
Stattl = 0x0e84/2, /* tail (previous) status addr */
Stataddr = 0x0e88/4, /* status address low */
Statth = 0x0e98/2,
Stathd = 0x0e9c/2,
Statwm = 0x0eac, /* stat watermark */
Statiwm = 0x0ead, /* isr stat watermark */
Dpolltm = 0x0e08/4, /* descriptor pool timer */
/* timers */
Tgv = 0x0e14/4, /* gmac timer current value */
Tgc = 0x0e18, /* gmac timer ctl */
Tgt = 0x0e1a, /* gmac timer test */
Tsti = 0x0ec0/4, /* stat tx timer ini */
Tlti = 0x0eb0/4, /* level */
Titi = 0x0ed0/4, /* isr */
Tstc = 0x0ec8, /* stat tx timer ctl */
Tltc = 0x0eb8, /* level timer ctl */
Titc = 0x0ed8, /* isr timer ctl */
/* “gmac” registers */
Stat = 0x000/2,
Ctl = 0x004/2,
Txctl = 0x008/2,
Rxctl = 0x00c/2,
Txflow = 0x010/2,
Txparm = 0x014/2,
Serctl = 0x018/2, /* serial mode */
Mchash = 0x034/2, /* 4 registers; 4 bytes apart */
/* interrupt sources and masks */
Txirq = 0x044/2,
Rxirq = 0x048/2,
Trirq = 0x04c/2, /* tx/rx overflow irq source */
Txmask = 0x050/2,
Rxmask = 0x054/2,
Trmask = 0x058/2,
Smictl = 0x080/2, /* serial mode control */
Smidata = 0x084/2,
Phyaddr = 0x088/2,
Ea0 = 0x01c/2, /* 3 16 bit gmac registers */
Ea1 = 0x028/2,
Stats = 0x0100/4,
/* mac registers */
Txactl = 0x210, /* transmit arbiter ctl */
Grxea = 0x0c40/4, /* rx fifo end address */
Gfrxctl = 0x0c48/4, /* gmac rxfifo ctl */
Grxfm = 0x0c4c/4, /* fifo flush mask */
Grxft = 0x0c50/4, /* fifo flush threshold */
Grxtt = 0x0c54/4, /* rx truncation threshold */
Gmfea = 0x0d40/4, /* end address */
Gmfae = 0x0d44/4, /* almost empty thresh */
Gmfctl = 0x0d48/4, /* tx gmac fifo ctl */
Rxphi = 0x0c58, /* pause high watermark */
Rxplo = 0x0c5c, /* pause low watermark */
Rxwp = 0x0c60/4,
Rxwlev = 0x0c68/4,
Rxrp = 0x0c70/4,
Rxrlev = 0x0c78/4,
Mac = 0x0f00/4, /* global mac control */
Phy = 0x0f04/4, /* phy control register */
Irq = 0x0f08, /* irq source */
Irqm = 0x0f0c, /* irq mask */
Linkctl = 0x0f10,
/* queue registers; all offsets from Qbase*/
Qbase = 0x0400,
Qportsz = 0x0080, /* BOTCH; tx diff is 2x rx diff */
Qr = 0x000,
Qtxs = 0x200,
Qtx = 0x280,
/* queue offsets */
Qd = 0x00,
Qvlan = 0x20,
Qdone = 0x24,
Qaddrl = 0x28,
Qaddrh = 0x2c,
Qbc = 0x30,
Qcsr = 0x34, /* 32bit */
Qtest = 0x38,
Qwm = 0x40,
/* buffer registers; all offsets from Rbase */
Rbase = 0x0800,
Rstart = 0x00,
Rend = 0x04,
Rwp = 0x08,
Rrp = 0x0c,
Rpon = 0x10, /* pause frames on */
Rpoff = 0x14, /* pause frames off */
Rhon = 0x18, /* high-priority frames on */
Rhoff = 0x1c, /* high-priority frames off */
Rctl = 0x28,
/* prefetch */
Pbase = 0x450,
Pctl = 0x00,
Plidx = 0x04, /* last addr; 16 bit */
Paddrl = 0x08,
Paddrh = 0x0c,
Pgetidx = 0x10, /* 16 bit */
Pputidx = 0x14, /* 16 bit */
Pfifow = 0x20, /* 8 bit */
Pfifor = 0x24, /* 8 bit */
Pfifowm = 0x20, /* 8 bit */
/* indirect phy registers */
Phyctl = 0x000,
Phystat = 0x001,
Phyid0 = 0x002,
Phyid1 = 0x003,
Phyana = 0x004, /* auto neg advertisement */
Phylpa = 0x005, /* link partner ability */
Phyanee = 0x006, /* auto neg adv expansion */
Phynp = 0x007, /* next page */
Phylnp = 0x008, /* link partner next page */
Gbectl = 0x009,
Gbestat = 0x00a,
Phyphy = 0x010, /* phy specific ctl */
Phylstat = 0x011,
Phyintm = 0x012, /* phy interrupt mask */
Phyint = 0x013,
Phyextctl = 0x014,
Phyrxe = 0x015, /* rx error counter */
Phypage = 0x016, /* external address */
Phypadr = 0x01d, /* phy page address */
};
enum {
/* Pciasp */
Aspforce = 1<<15,
Aspglinkdn = 1<<14, /* gphy link down */
Aspfempty = 1<<13,
Aspclkrun = 1<<12,
Aspmsk = Aspforce | Aspglinkdn | Aspfempty | Aspclkrun,
/* Pcistate */
Vmain = 3<<27,
/* Stat */
Sfast = 1<<15, /* 100mbit */
Duplex = 1<<14,
Txnofc = 1<<13, /* tx flow control disabled */
Link = 1<<12, /* link up */
Pausest = 1<<11, /* pause state */
Txactive = 1<<10,
Excesscol = 1<<9,
Latecol = 1<<8,
Physc = 1<<5, /* phy status change */
Sgbe = 1<<4, /* gbe speed */
Rxnofc = 1<<2, /* rx flow control disabled */
Promisc = 1<<1, /* promiscuous mode enabled */
/* Ctl */
Promiscen = 1<<14,
Txfcdis = 1<<13,
Txen = 1<<12,
Rxen = 1<<11,
Bursten = 1<<10,
Loopen = 1<<9,
Gbeen = 1<<7,
Fpass = 1<<6, /* "force link pass" ? */
Duplexen = 1<<5,
Rxfcdis = 1<<4,
Fasten = 1<<3, /* enable 100mbit */
Adudis = 1<<2, /* disable auto upd duplex */
Afcdis = 1<<1, /* disable auto upd flow ctl */
Aspddis = 1<<0, /* disable auto upd speed */
/* Rxctl */
Ufilter = 1<<15, /* unicast filter */
Mfilter = 1<<14, /* multicast filter */
Rmcrc = 1<<13, /* remove frame crc */
/* Serctl */
Vlanen = 1<<9,
Jumboen = 1<<8,
/* Txactl */
Txaclr = 1<<1,
Txarst = 1<<0,
/* Asfcs: yukex only */
Asfbrrst = 1<<9, /* bridge reset */
Asfcpurst = 1<<8, /* cpu reset */
Asfucrst = 3<<0, /* µctlr reset */
/* Asfcs */
Asfhvos = 1<<4, /* os present */
Asfrst = 1<<3,
Asfrun = 1<<2,
Asfcirq = 1<<1,
Afsirq = 1<<0,
/* Statctl */
Statirqclr = 1<<4,
Staton = 1<<3,
Statoff = 1<<2,
Statclr = 1<<1,
Statrst = 1<<0,
/* Mac */
Nomacsec = 1<<13 | 1<<11,
Nortx = 1<<9,
Macpause = 1<<3,
Macpauseoff = 1<<2,
Macrstclr = 1<<1,
Macrst = 1<<0,
/* Phy */
Gphyrstclr = 1<<1,
Gphyrst = 1<<0,
/* Irqm */
Txovfl = 1<<5, /* tx counter overflow */
Rxovfl = 1<<4, /* rx counter overflow */
Txurun = 1<<3, /* transmit fifo underrun */
Txdone = 1<<2, /* frame tx done */
Rxorun = 1<<1, /* rx fifo overrun */
Rxdone = 1<<0, /* frame rx done */
/* Linkctl */
Linkclr = 1<<1,
Linkrst = 1<<0,
/* Smictl */
Smiread = 1<<5,
Smiwrite = 0<<5,
Smirdone = 1<<4,
Smibusy = 1<<3,
/* Phyaddr */
Mibclear = 1<<5,
/* Ctst */
Asfdis = 1<<12, /* asf disable */
Clken = 1<<11, /* enable clock */
Swirq = 1<<7,
Swirqclr = 1<<6,
Mstopped = 1<<5, /* master is stopped */
Mstop = 1<<4, /* stop master */
Mstrclr = 1<<3, /* master reset clear */
Mstrrset = 1<<2, /* master reset */
Swclr = 1<<1,
Swrst = 1<<0,
/* Pwrctl */
Vauxen = 1<<7,
Vauxdis = 1<<6,
Vccen = 1<<5,
Vccdis = 1<<4,
Vauxon = 1<<3,
Vauxoff = 1<<2,
Vccon = 1<<1,
Vccoff = 1<<0,
/* timers */
Tstart = 1<<2,
Tstop = 1<<1,
Tclrirq = 1<<0,
/* Dpolltm */
Pollstart = 1<<1,
Pollstop = 1<<0,
/* csr interrupts: Isrc2, Eisr, etc. */
Ihwerr = 1<<31,
Ibmu = 1<<30, /* sring irq */
Isoftware = 1<<25,
Iphy = 1<<4,
Imac = 1<<3,
Irx = 1<<2,
Itxs = 1<<1, /* descriptor error */
Itx = 1<<0, /* descriptor error */
Iport = 0x1f,
Iphy2base = 8,
Ierror = (Imac | Itx | Irx)*(1 | 1<<Iphy2base),
/* hwe interrupts: Hwe Hwem */
Htsof = 1<<29, /* timer stamp overflow */
Hsensor = 1<<28,
Hmerr = 1<<27, /* master error */
Hstatus = 1<<26, /* status exception */
Hpcie = 1<<25, /* pcie error */
Hpcie2 = 1<<24, /* " */
Hrparity = 1<<5, /* ram read parity error */
Hwparity = 1<<4, /* ram write parity error */
Hmfault = 1<<3, /* mac fault */
Hrxparity = 1<<2, /* rx parity */
Htcptxs = 1<<1, /* tcp length mismatch */
Htcptxa = 1<<0, /* tcp length mismatch */
H1base = 1<<0,
H2base = 1<<8,
Hmask = 0x3f,
Hdflt = Htsof | Hmerr | Hstatus | Hmask*(H1base | H2base),
/* Clkctl */
Clkdiven = 1<<1,
Clkdivdis = 1<<0,
/* Clkgate */
Link2inactive = 1<<7,
/* Phyctl */
Phyrst = 1<<15,
Phy100 = 1<<14, /* manual enable 100mbit */
Aneen = 1<<12, /* auto negotiation enable */
Phyoff = 1<<11, /* turn phy off */
Anerst = 1<<9, /* auto neg reset */
Phydpx = 1<<8,
Phy1000 = 1<<5, /* manual enable gbe */
/* Phyana */
Annp = 1<<15, /* request next page */
Anack = 1<<14, /* ack rx (read only) */
Anrf = 1<<13, /* remote fault */
Anpa = 1<<11, /* try asymmetric pause */
Anp = 1<<10, /* try pause */
An100f = 1<<8,
An100h = 1<<7,
An10f = 1<<6,
An10h = 1<<5,
Anonly = 1<<0,
Anall = An100f | An100h | An10f | An10h | Anonly,
/* Gbectl */
Gbef = 1<<9, /* auto neg gbe full */
Gbeh = 1<<8, /* auto neg gbe half */
Gbexf = 1<<6, /* auto neg gbe full fiber */
Gbexh = 1<<5, /* auto neg gbe full fiber */
/* Phyphy */
Pptf = 3<<14, /* tx fifo depth */
Pprf = 3<<12, /* rx fifo depth */
Pped = 3<<8, /* energy detect */
Ppmdix = 3<<5, /* mdix conf */
Ppmdixa = 3<<5, /* automdix */
Ppengy = 1<<14, /* fe+ enable energy detect */
Ppscrdis = 1<<9, /* fe+ scrambler disable */
Ppnpe = 1<<12, /* fe+ enable next page */
/* Phylstat */
Physpd = 3<<14,
Phydupx = 1<<13,
Phypr = 1<<12, /* page rx */
Phydone = 1<<11, /* speed and duplex neg. done */
Plink = 1<<10,
Pwirelen = 7<<7,
Pmdi = 1<<6,
Pdwnsh = 1<<5, /* downshift */
Penergy = 1<<4, /* energy detect */
Ptxpause = 1<<3, /* tx pause enabled */
Prxpause = 1<<2, /* rx pause enabled */
Ppol = 1<<2, /* polarity */
Pjarjar = 1<<1, /* mesa no understasa */
/* Phyintm */
Anerr = 1<<15, /* an error */
Lsp = 1<<14, /* link speed change */
Andc = 1<<13, /* an duplex change */
Anok = 1<<11,
Lsc = 1<<10, /* link status change */
Symerr = 1<<9, /* symbol error */
Fcarr = 1<<8, /* false carrier */
Fifoerr = 1<<7,
Mdich = 1<<6,
Downsh = 1<<5,
Engych = 1<<4, /* energy change */
Dtech = 1<<2, /* dte power det status */
Polch = 1<<1, /* polarity change */
Jabber = 1<<0,
/* Phyextctl */
Dnmstr = 1<<9, /* master downshift; 0: 1x; 1: 2x; 2: 3x */
Dnslv = 1<<8,
/* Tgc */
Tgstart = 1<<2,
Tgstop = 1<<1,
Tgclr = 1<<0, /* clear irq */
/* Tstctl1 */
Tstwen = 1<<1, /* enable config reg r/w */
Tstwdis = 1<<0, /* disable config reg r/w */
/* Gpio */
Norace = 1<<13,
/* Rictl */
Rirpclr = 1<<9,
Riwpclr = 1<<8,
Riclr = 1<<1,
Rirst = 1<<0,
/* Rbase opcodes */
Rsfon = 1<<5, /* enable store/fwd */
Rsfoff = 1<<4,
Renable = 1<<3,
Rdisable = 1<<2,
Rrstclr = 1<<1,
Rrst = 1<<0,
/* Qbase opcodes */
Qidle = 1<<31,
Qtcprx = 1<<30,
Qiprx = 1<<29,
Qrssen = 1<<15,
Qrssdis = 1<<14,
Qsumen = 1<<13, /* tcp/ip cksum */
Qsumdis = 1<<12,
Qcirqpar = 1<<11, /* clear irq on parity errors */
Qcirqck = 1<<10,
Qstop = 1<<9,
Qstart = 1<<8,
Qfifoon = 1<<7,
Qfifooff = 1<<6,
Qfifoen = 1<<5,
Qfiforst = 1<<4,
Qenable = 1<<3,
Qdisable = 1<<2,
Qrstclr = 1<<1,
Qrst = 1<<0,
Qallclr = Qfiforst | Qfifooff | Qrstclr,
Qgo = Qcirqpar | Qcirqck | Qstart | Qfifoen | Qenable,
/* Qtest bits */
Qckoff = 1<<31, /* tx: auto checksum off */
Qckon = 1<<30,
Qramdis = 1<<24, /* rx: ram disable */
/* Pbase opcodes */
Prefon = 1<<3, /* prefetch on */
Prefoff = 1<<2,
Prefrstclr = 1<<1,
Prefrst = 1<<0,
/* ring opcodes */
Hw = 0x80, /* bitmask */
Ock = 0x12, /* tcp checksum start */
Oaddr64 = 0x21,
Obuf = 0x40,
Opkt = 0x41,
Orxstat = 0x60,
Orxts = 0x61, /* rx timestamp */
Orxvlan = 0x62,
Orxchks = 0x64,
Otxidx = 0x68,
Omacs = 0x6c, /* macsec */
Oputidx = 0x70,
/* ring status */
Eop = 0x80,
/* Gfrxctl */
Gftrunc = 1<<27,
Gftroff = 1<<26,
Gfroon = 1<<19, /* flush on rx overrun */
Gfrooff = 1<<18,
Gffon = 1<<7, /* rx fifo flush mode on */
Gffoff = 1<<6,
Gfon = 1<<3,
Gfoff = 1<<2,
Gfrstclr = 1<<1,
Gfrst = 1<<0,
/* Gmfctl */
Gmfsfoff = 1<<31, /* disable store-forward (ec ultra) */
Gmfsfon = 1<<30, /* able store-forward (ec ultra) */
Gmfvon = 1<<25, /* vlan tag on */
Gmfvoff = 1<<24, /* vlan off */
Gmfjon = 1<<23, /* jumbo on (ec ultra) */
Gmfjoff = 1<<22, /* jumbo off */
Gmfcfu = 1<<6, /* clear fifio underrun irq */
Gmfcfc = 1<<5, /* clear frame complete irq */
Gmfcpe = 1<<4, /* clear parity error irq */
Gmfon = 1<<3,
Gmfoff = 1<<2,
Gmfclr = 1<<1,
Gmfrst = 1<<0,
/* rx frame */
Flen = 0x7fff<<17,
Fvlan = 1<<13,
Fjabbr = 1<<12,
Ftoosm = 1<<11,
Fmc = 1<<10, /* multicast */
Fbc = 1<<9,
Fok = 1<<8, /* good frame */
Fokfc = 1<<7,
Fbadfc = 1<<6,
Fmiierr = 1<<5,
Ftoobg = 1<<4, /* oversized */
Ffrag = 1<<3, /* fragment */
Fcrcerr = 1<<1,
Ffifoof = 1<<0, /* fifo overflow */
Ferror = Ffifoof | Fcrcerr | Ffrag | Ftoobg
| Fmiierr | Fbadfc | Ftoosm | Fjabbr,
/* rx checksum bits in Status.ctl */
Badck = 5, /* arbitrary bad checksum */
Ctcpok = 1<<7, /* tcp or udp cksum ok */
Cisip6 = 1<<3,
Cisip4 = 1<<1,
/* more status ring rx bits */
Rxvlan = 1<<13,
Rxjab = 1<<12, /* jabber */
Rxsmall = 1<<11, /* too small */
Rxmc = 1<<10, /* multicast */
Rxbc = 1<<9, /* bcast */
Rxok = 1<<8,
Rxfcok = 1<<7, /* flow control pkt */
Rxfcbad = 1<<6,
Rxmiierr = 1<<5,
Rxbig = 1<<4, /* too big */
Rxfrag = 1<<3,
Rxcrcerr = 1<<1,
Rxfov = 1<<0, /* fifo overflow */
Rxerror = Rxfov | Rxcrcerr | Rxfrag | Rxbig | Rxmiierr
| Rxfcbad | Rxsmall | Rxjab,
};
enum {
Ffiber = 1<<0,
Fgbe = 1<<1,
Fnewphy = 1<<2,
Fapwr = 1<<3,
Fnewle = 1<<4,
Fram = 1<<5,
Fancy =Fgbe | Fnewphy | Fapwr,
Yukxl = 0,
Yukecu,
Yukex,
Yukec,
Yukfe,
Yukfep,
Yuksup,
Yukul2,
Yukba, /* doesn't exist */
Yukopt,
Nyuk,
};
typedef struct Chipid Chipid;
typedef struct Ctlr Ctlr;
typedef void (*Freefn)(Block*);
typedef struct Kproc Kproc;
typedef struct Mc Mc;
typedef struct Stattab Stattab;
typedef struct Status Status;
typedef struct Sring Sring;
typedef struct Vtab Vtab;
struct Chipid {
uchar feat;
uchar okrev;
uchar mhz;
char *name;
};
struct Kproc {
Rendez;
uint event;
};
struct Sring {
uint wp;
uint rp;
uint cnt;
uint m;
Status *r;
};
struct Ctlr {
Pcidev *p;
Ctlr *oport; /* port 2 */
uchar qno;
uchar attach;
uchar rxinit;
uchar txinit;
uchar flag;
uchar feat;
uchar type;
uchar rev;
uchar nports;
uchar portno;
uintptr io;
uchar *reg8;
ushort *reg16;
uint *reg;
uint rbsz;
uchar ra[Eaddrlen];
uint mca;
uint nmc;
Mc *mc;
void *alloc;
Sring status;
Sring tx;
Block *tbring[Tringcnt];
Sring rx;
Block *rbring[Rringcnt];
Kproc txmit;
Kproc rxmit;
Kproc iproc;
};
struct Mc {
Mc *next;
uchar ea[Eaddrlen];
};
struct Stattab {
uint offset;
char *name;
};
struct Status {
uchar status[4];
uchar l[2];
uchar ctl;
uchar op;
};
struct Vtab {
int vid;
int did;
int mtu;
char *name;
};
static Chipid idtab[] = {
[Yukxl] Fgbe | Fnewphy, 0xff, 156, "yukon-2 xl",
[Yukecu] Fancy, 0xff, 125, "yukon-2 ec ultra",
[Yukex] Fancy | Fnewle, 0xff, 125, "yukon-2 extreme",
[Yukec] Fgbe, 2, 125, "yukon-2 ec",
[Yukfe] 0, 0xff, 100, "yukon-2 fe",
[Yukfep] Fnewphy|Fapwr | Fnewle, 0xff, 50, "yukon-2 fe+",
[Yuksup] Fgbe | Fnewphy | Fnewle, 0xff, 125, "yukon-2 supreme",
[Yukul2] Fgbe |Fapwr, 0xff, 125, "yukon-2 ultra2",
[Yukba] 0, 0, 0, "??",
[Yukopt] Fancy, 0xff, 125, "yukon-2 optima",
};
static Vtab vtab[] = {
0x11ab, 0x4354, 1514, "88e8040", /* unsure on mtu */
0x11ab, 0x4362, 1514, "88e8053",
0x11ab, 0x4364, 1514, "88e8056",
0x11ab, 0x4380, 1514, "88e8057",
0x11ab, 0x436b, 1514, "88e8071", /* unsure on mtu */
0x1186, 0x4b00, 9000, "dge-560t",
0x1186, 0x4b02, 1514, "dge-550sx",
0x1186, 0x4b03, 1514, "dge-550t",
};
static Stattab stattab[] = {
0, "rx ucast",
8, "rx bcast",
16, "rx pause",
24, "rx mcast",
32, "rx chk seq",
48, "rx ok low",
56, "rx ok high",
64, "rx bad low",
72, "rx bad high",
80, "rx frames < 64",
88, "rx frames < 64 fcs",
96, "rx frames 64",
104, "rx frames 65-127",
112, "rx frames 128-255",
120, "rx frames 256-511",
128, "rx frames 512-1023",
136, "rx frames 1024-1518",
144, "rx frames 1519-mtu",
152, "rx frames too long",
160, "rx jabber",
176, "rx fifo oflow",
192, "tx ucast",
200, "tx bcast",
208, "tx pause",
216, "tx mcast",
224, "tx ok low",
232, "tx ok hi",
240, "tx frames 64",
248, "tx frames 65-127",
256, "tx frames 128-255",
264, "tx frames 256-511",
272, "tx frames 512-1023",
280, "tx frames 1024-1518",
288, "tx frames 1519-mtu",
304, "tx coll",
312, "tx late coll",
320, "tx excess coll",
328, "tx mul col",
336, "tx single col",
344, "tx underrun",
};
static uint phypwr[] = {1<<26, 1<<27};
static uint coma[] = {1<<28, 1<<29};
static uchar nilea[Eaddrlen];
static int debug;
static Ctlr *ctlrtab[Nctlr];
static int nctlr;
static struct {
union {
struct {
Lock;
Block *b;
uint starve;
};
uchar pad[128]; /* cacheline */
};
Kproc *k;
Block *x;
uint nfast;
uint nslow;
} rbtab[Nctlr];
static int
icansleep(void *v)
{
Kproc *k;
k = v;
return k->event != 0;
}
static void
unstarve(Kproc *k)
{
k->event = 1;
wakeup(k);
}
static void
starve(Kproc *k)
{
sleep(k, icansleep, k);
k->event = 0;
}
static Status*
getslot(Sring *r, Kproc *k)
{
if(r->rp + r->m - r->wp & ~r->m)
starve(k);
return r->r + (r->wp++ & r->m);
}
static int
getnslot(Sring *r, uint *wp, Status **t, uint n)
{
int i;
if(r->rp + r->m - (n - 1) - wp[0] & ~r->m)
return -1;
for(i = 0; i < n; i++)
t[i] = r->r + (wp[0]++ & r->m);
return 0;
}
/* assume allocs come from a single thread; 30*0.999x speedup */
static Block*
rballoc(int t)
{
Block *b;
if((b = rbtab[t].x) != nil){
rbtab[t].nfast++;
rbtab[t].x = b->next;
b->next = nil;
b->ref = 1;
return b;
}
ilock(&rbtab[t]);
b = rbtab[t].x = rbtab[t].b;
rbtab[t].b = nil;
if(b == nil)
rbtab[t].starve = 1;
iunlock(&rbtab[t]);
rbtab[t].nslow++;
if(b != nil){
rbtab[t].x = b->next;
b->next = nil;
b->ref = 1;
}
return b;
}
static void
rbfree(Block *b, int t)
{
b->rp = b->wp = (uchar*)ROUND((uintptr)b->base, Rbalign);
b->flag &= ~(Bipck | Budpck | Btcpck | Bpktck);
ilock(&rbtab[t]);
b->next = rbtab[t].b;
if(b->next == nil && rbtab[t].starve){
rbtab[t].starve = 0;
unstarve(rbtab[t].k);
}
rbtab[t].b = b;
iunlock(&rbtab[t]);
}
static void
rbfree0(Block *b)
{
rbfree(b, 0);
}
static void
rbfree1(Block *b)
{
rbfree(b, 1);
}
static void
rbfree2(Block *b)
{
rbfree(b, 2);
}
static void
rbfree3(Block *b)
{
rbfree(b, 3);
}
static Freefn freetab[Nctlr] = {
rbfree0,
rbfree1,
rbfree2,
rbfree3,
};
static uint
macread32(Ctlr *c, uint r)
{
return c->reg[c->portno*0x20 + r];
}
static void
macwrite32(Ctlr *c, uint r, uint v)
{
c->reg[c->portno*0x20 + r] = v;
}
static uint
macread16(Ctlr *c, uint r)
{
return c->reg16[c->portno*0x40 + r];
}
static void
macwrite16(Ctlr *c, uint r, uint v)
{
c->reg16[c->portno*0x40 + r] = v;
}
static uint
macread8(Ctlr *c, uint r)
{
return c->reg8[c->portno*0x80 + r];
}
static void
macwrite8(Ctlr *c, uint r, uint v)
{
c->reg8[c->portno*0x80 + r] = v;
}
static uint gmac32[2] = {
0x2800/4,
0x3800/4,
};
static ushort
gmacread32(Ctlr *c, uint r)
{
return c->reg[gmac32[c->portno] + r];
}
static void
gmacwrite32(Ctlr *c, uint r, uint v)
{
c->reg[gmac32[c->portno] + r] = v;
}
static uint gmac[2] = {
0x2800/2,
0x3800/2,
};
static ushort
gmacread(Ctlr *c, uint r)
{
return c->reg16[gmac[c->portno] + r];
}
static void
gmacwrite(Ctlr *c, uint r, ushort v)
{
c->reg16[gmac[c->portno] + r] = v;
}
static uint
qrread(Ctlr *c, uint r)
{
return c->reg[Qbase + c->portno*Qportsz + r>>2];
}
static void
qrwrite(Ctlr *c, uint r, uint v)
{
c->reg[Qbase + c->portno*Qportsz + r>>2] = v;
}
static uint
qrread16(Ctlr *c, uint r)
{
return c->reg16[Qbase + c->portno*Qportsz + r>>1];
}
static void
qrwrite16(Ctlr *c, uint r, uint v)
{
c->reg16[Qbase + c->portno*Qportsz + r>>1] = v;
}
static uint
qrread8(Ctlr *c, uint r)
{
return c->reg8[Qbase + c->portno*Qportsz + r>>0];
}
static void
qrwrite8(Ctlr *c, uint r, uint v)
{
c->reg8[Qbase + c->portno*Qportsz + r>>0] = v;
}
static uint
rrread32(Ctlr *c, uint r)
{
return c->reg[Rbase + c->portno*Qportsz + r>>2];
}
static void
rrwrite32(Ctlr *c, uint r, uint v)
{
c->reg[Rbase + c->portno*Qportsz + r>>2] = v;
}
static void
rrwrite8(Ctlr *c, uint r, uint v)
{
c->reg8[Rbase + c->portno*Qportsz + r] = v;
}
static uint
rrread8(Ctlr *c, uint r)
{
return c->reg8[Rbase + c->portno*Qportsz + r];
}
static uint
prread32(Ctlr *c, uint r)
{
return c->reg[Pbase + c->portno*Qportsz + r>>2];
}
static void
prwrite32(Ctlr *c, uint r, uint v)
{
c->reg[Pbase + c->portno*Qportsz + r>>2] = v;
}
static uint
prread16(Ctlr *c, uint r)
{
return c->reg16[Pbase + c->portno*Qportsz + r>>1];
}
static void
prwrite16(Ctlr *c, uint r, uint v)
{
c->reg16[Pbase + c->portno*Qportsz + r>>1] = v;
}
static ushort
phyread(Ctlr *c, uint r)
{
ushort v;
gmacwrite(c, Smictl, Smiread | r<<6);
for(;;){
v = gmacread(c, Smictl);
if(v == 0xffff)
error("phy read");
if(v & Smirdone)
return gmacread(c, Smidata);
microdelay(10);
}
}
static ushort
phywrite(Ctlr *c, uint r, ushort v)
{
gmacwrite(c, Smidata, v);
gmacwrite(c, Smictl, Smiwrite | r<<6);
for(;;){
v = gmacread(c, Smictl);
if(v == 0xffff)
error("phy write");
if((v & Smibusy) == 0)
return gmacread(c, Smidata);
microdelay(10);
}
}
static uvlong lorder = 0x0706050403020100ull;
static uvlong
getle(uchar *t, int w)
{
uint i;
uvlong r;
r = 0;
for(i = w; i != 0; )
r = r<<8 | t[--i];
return r;
}
static void
putle(uchar *t, uvlong r, int w)
{
uchar *o, *f;
uint i;
f = (uchar*)&r;
o = (uchar*)&lorder;
for(i = 0; i < w; i++)
t[o[i]] = f[i];
}
static void
bufinit(Ctlr *c, uint q, uint start, uint end)
{
uint t;
rrwrite8(c, q + Rctl, Rrstclr);
rrwrite32(c, q + Rstart, start);
rrwrite32(c, q + Rend, end-1);
rrwrite32(c, q + Rwp, start);
rrwrite32(c, q + Rrp, start);
if(q == Qr || q == Qr + Qportsz){
t = start-end;
rrwrite32(c, q + Rpon, t - 8192/8);
rrwrite32(c, q + Rpoff, t - 16384/8);
} else
rrwrite8(c, q + Rctl, Rsfon);
rrwrite8(c, q + Rctl, Renable);
rrread8(c, q + Rctl);
}
static void
qinit(Ctlr *c, uint queue)
{
qrwrite(c, queue + Qcsr, Qallclr);
qrwrite(c, queue + Qcsr, Qgo);
qrwrite(c, queue + Qcsr, Qfifoon);
qrwrite16(c, queue + Qwm, 0x600); /* magic */
// qrwrite16(c, queue + Qwm, 0x80); /* pcie magic; assume pcie; no help */
}
/* initialized prefetching */
static void
pinit(Ctlr *c, uint queue, Sring *r)
{
union {
uchar u[4];
uint l;
} u;
prwrite32(c, queue + Pctl, Prefrst);
prwrite32(c, queue + Pctl, Prefrstclr);
putle(u.u, Pciwaddrh(r->r), 4);
prwrite32(c, queue + Paddrh, u.l);
putle(u.u, Pciwaddrl(r->r), 4);
prwrite32(c, queue + Paddrl, u.l);
prwrite16(c, queue + Plidx, r->m);
prwrite32(c, queue + Pctl, Prefon);
prread32(c, queue + Pctl);
}
static void
txinit(Ether *e)
{
Ctlr *c;
Sring *r;
c = e->ctlr;
r = &c->tx;
if(c->txinit == 1)
return;
c->txinit = 1;
r->wp = 0;
r->rp = 0;
qinit(c, Qtx);
pinit(c, Qtx, &c->tx);
}
static void
linkup(Ctlr *c, uint w)
{
static Lock l;
lock(&l);
gmacwrite(c, Ctl, w|gmacread(c, Ctl));
unlock(&l);
}
static void
tproc(void *v)
{
Block *b;
Ctlr *c;
Ether *e;
Kproc *k;
Sring *r;
Status *t;
e = v;
c = e->ctlr;
k = &c->txmit;
r = &c->tx;
txinit(e);
linkup(c, Txen);
for(;;){
if((b = qbread(e->oq, 100000)) == nil)
break;
if(Pciwaddrh(b->rp) != 0){
t = getslot(r, k);
t->ctl = 0;
t->op = Oaddr64 | Hw;
putle(t->status, Pciwaddrh(b->rp), 4);
}
t = getslot(r, k);
c->tbring[t - r->r] = b;
putle(t->status, Pciwaddrl(b->rp), 4);
putle(t->l, BLEN(b), 2);
t->op = Opkt | Hw;
t->ctl = Eop;
sfence();
prwrite16(c, Qtx + Pputidx, r->wp & r->m);
}
print("#l%d: tproc: queue closed\n", e->ctlrno);
pexit("queue closed", 1);
}
static void
rxinit(Ether *e)
{
int i;
Ctlr *c;
Block *b;
Sring *r;
Status *t;
c = e->ctlr;
r = &c->rx;
if(c->rxinit == 1)
return;
c->rxinit = 1;
for(i = 0; i < Nrb; i++){
b = allocb(c->rbsz + Rbalign);
b->free = freetab[c->qno];
freeb(b);
}
qinit(c, Qr);
if(c->type == Yukecu && (c->rev == 2 || c->rev == 3))
qrwrite(c, Qr + Qtest, Qramdis);
pinit(c, Qr, &c->rx);
if((c->flag & Fnewle) == 0){
t = getslot(r, &c->rxmit);
putle(t->status, 14<<16 | 14, 4);
t->ctl = 0;
t->op = Ock | Hw;
qrwrite(c, Qr + Qcsr, Qsumen);
}
macwrite32(c, Gfrxctl, Gftroff);
}
/* debug; remove */
#include "yukdump.h"
static int
rxscrew(Ether *e, Sring *r, Status *t, uint wp)
{
Ctlr *c;
c = e->ctlr;
if(wp - r->rp > r->cnt){
print("rxscrew1 wp %ud(%ud) rp %ud %lud\n", wp, r->wp, r->rp, t-r->r);
return -1;
}
if(c->rbring[t - r->r]){
print("rxscrew2 wp %ud rp %ud %lud\n", wp, r->rp, t-r->r);
descriptorfu(e, Qr);
return -1;
}
return 0;
}
static int
replenish(Ether *e, Ctlr *c)
{
int req, n, lim;
uint wp;
Block *b;
Sring *r;
Status *tab[2], *t;
r = &c->rx;
wp = r->wp;
req = 1 + is64();
lim = r->cnt/2;
if(lim > 128)
lim = 128; /* hw limit? */
for(n = 0; n < lim; n++){
b = rballoc(c->qno);
if(b == nil || getnslot(r, &wp, tab, req) == -1){
freeb(b);
break;
}
t = tab[0];
if(is64()){
putle(t->status, Pciwaddrh(b->wp), 4);
t->ctl = 0;
t->op = Oaddr64 | Hw;
t = tab[1];
}
if(rxscrew(e, r, t, wp) == -1)
break;
assert(c->rbring[t - r->r] == nil);
c->rbring[t - r->r] = b;
putle(t->status, Pciwaddrl(b->wp), 4);
putle(t->l, c->rbsz, 2);
t->ctl = 0;
t->op = Opkt | Hw;
}
if(n>0){
sfence();
prwrite16(c, Qr + Pputidx, wp & r->m);
r->wp = wp;
dprint("yuk: replenish %d %ud-%ud [%d-%d]\n", n, r->rp, wp, r->rp&r->m, wp&r->m);
}
return lim - n == 0;
}
static void
rproc(void *v)
{
Ctlr *c;
Ether *e;
Kproc *k;
e = v;
c = e->ctlr;
k = &c->rxmit;
rxinit(e);
linkup(c, Rxen);
for(;;)
if(replenish(e, c) == 0){
starve(k);
print("yuk: rx unstarve?\n");
}
}
static void
promiscuous(void *a, int on)
{
uint r;
Ether *e;
Ctlr *c;
e = a;
c = e->ctlr;
r = gmacread(c, Rxctl);
if(on)
r &= ~(Ufilter|Mfilter);
else
r |= Ufilter|Mfilter;
gmacwrite(c, Rxctl, r);
}
static uchar pauseea[] = {1, 0x80, 0xc2, 0, 0, 1};
static void
multicast(void *a, uchar *ea, int on)
{
uchar f[8];
uint i, r, b;
Ctlr *c;
Ether *e;
Mc **ll, *l, *p;
e = a;
c = e->ctlr;
r = gmacread(c, Rxctl);
if(on){
for(ll = &c->mc; *ll != nil; ll = &(*ll)->next)
if(memcmp((*ll)->ea, ea, Eaddrlen) == 0)
return;
*ll = malloc(sizeof **ll);
memmove((*ll)->ea, ea, Eaddrlen);
}else{
for(p = nil, l = c->mc; l != nil; p = l, l = l->next)
if(memcmp(l->ea, ea, Eaddrlen) == 0)
break;
if(l == nil)
return;
if(p != nil)
p->next = l->next;
else
c->mc = l->next;
free(l);
}
memset(f, 0, sizeof f);
if(0 /* flow control */){
b = ethercrc(pauseea, Eaddrlen) & 0x3f;
f[b>>3] |= 1 << (b & 7);
}
for(l = c->mc; l != nil; l = l->next){
b = ethercrc(l->ea, Eaddrlen) & 0x3f;
f[b>>3] |= 1 << (b & 7);
}
for(i = 0; i < sizeof f / 2; i++)
gmacwrite(c, Mchash + 2*i, f[i] | f[i+1]<<8);
gmacwrite(c, Rxctl, r | Mfilter);
}
static int spdtab[4] = {
10, 100, 1000, 0,
};
static void
link(Ether *e)
{
uint i, s, spd;
Ctlr *c;
c = e->ctlr;
i = phyread(c, Phyint);
s = phyread(c, Phylstat);
dprint("#l%d: yuk: link %.8ux %.8ux\n", e->ctlrno, i, s);
spd = 0;
e->link = (s & Plink) != 0;
if(e->link && c->feat&Ffiber)
spd = 1000;
else if(e->link){
spd = s & Physpd;
spd >>= 14;
spd = spdtab[spd];
}
e->mbps = spd;
dprint("#l%d: yuk: link %d spd %d\n", e->ctlrno, e->link, e->mbps);
}
static void
txcleanup(Ctlr *c, uint end)
{
uint rp0, rp;
Block *b;
Sring *r;
Status *t;
r = &c->tx;
rp0 = r->rp & r->m;
for(rp = rp0; rp != end; rp = r->rp & r->m){
t = r->r + rp;
r->rp++;
if((t->ctl & Eop) == 0)
continue;
b = c->tbring[rp];
c->tbring[rp] = nil;
if(b != nil)
freeb(b);
}
if(r->wp - r->rp > 16){ /* BOTCH */
print("TX unstarve %ud - %ud \n", r->wp, r->rp);
unstarve(&c->txmit);
}
}
static void
rx(Ether *e, uint l, uint x, uint flag)
{
uint cnt, i, rp;
Block *b;
Ctlr *c;
Sring *r;
c = e->ctlr;
r = &c->rx;
for(rp = r->rp;;){
i = rp++&r->m;
b = c->rbring[i];
c->rbring[i] = nil;
if(b != nil)
break;
}
cnt = x>>16 & 0x7fff;
if(cnt != l || x&Rxerror &&
!(c->type == Yukfep && c->rev == 0)){
print("#l%d: yuk rx error %.4ux\n", e->ctlrno, x&0xffff);
freeb(b);
}else{
b->wp += l;
b->lim = b->wp; /* lie like a dog */
b->flag |= flag;
etheriq(e, b, 1);
}
r->rp = rp;
}
static uint
cksum(Ctlr *c, uint ck, uint css)
{
if(c->flag & Fnewle && css&(Cisip4|Cisip6) && css&Ctcpok)
return Bipck | Btcpck | Budpck;
else if(ck == 0xffff || ck == 0)
return Bipck;
return 0;
}
static void
sring(Ether *e)
{
uint i, p, lim, op, l, x;
Ctlr *c;
Sring *r;
Status *s;
static uint ck = Badck;
c = e->ctlr;
r = &c->status;
lim = r->rp & r->m;
p = 0;
for(;;){
if((r->rp & r->m) == lim){
lim = c->reg16[Stathd];
if((r->rp & r->m) == lim)
break;
}
i = r->rp & r->m;
s = r->r + i;
op = s->op;
if((op & Hw) == 0)
break;
op &= ~Hw;
switch(op){
case Orxchks:
ck = getle(s->status, 4) & 0xffff;
break;
case Orxstat:
l = getle(s->l, 2);
x = getle(s->status, 4);
rx(e, l, x, cksum(c, ck, s->ctl));
ck = Badck;
p++;
break;
case Otxidx:
l = getle(s->l, 2);
x = getle(s->status, 4);
txcleanup(c, x & 0xfff);
x = l>>24 & 0xff | l<< 8;
x &= 0xfff;
if(x != 0 && c->oport)
txcleanup(c->oport, x);
break;
default:
print("#l%d: yuk: funny opcode %.2ux\n", e->ctlrno, op);
break;
}
s->op = 0;
r->rp++;
}
while(p && replenish(e, c) != 0)
;
c->reg[Statctl] = Statirqclr;
}
enum {
Pciaer = 0x1d00,
Pciunc = 0x0004,
};
static void
hwerror(Ether *e, uint cause)
{
uint u;
Ctlr *c;
c = e->ctlr;
cause = c->reg[Hwe];
if(cause == 0)
print("hwe: no cause\n");
if(cause & Htsof){
c->reg8[Tgc] = Tgclr;
cause &= ~Htsof;
}
if(cause & (Hmerr | Hstatus)){
c->reg8[Tstctl1] = Tstwen;
u = pcicfgr16(c->p, PciPSR) | 0x7800;
pcicfgw16(c->p, PciPSR, u);
c->reg8[Tstctl1] = Tstwdis;
cause &= ~(Hmerr | Hstatus);
}
if(cause & Hpcie){
c->reg8[Tstctl1] = Tstwen;
c->reg[Pciaer + Pciunc>>2] = ~0;
u = c->reg[Pciaer + Pciunc>>2];
USED(u);
print("#l%d: pcierror %.8ux\n", e->ctlrno, u);
c->reg8[Tstctl1] = Tstwdis;
cause &= ~Hpcie;
}
if(cause & Hrxparity){
print("#l%d: ram parity read error. bug? ca %.8ux\n", e->ctlrno, cause);
qrwrite(c, Qtx + Qcsr, Qcirqpar);
cause &= ~Hrxparity;
}
if(cause & Hrparity){
print("#l%d: ram parity read error. bug? ca %.8ux\n", e->ctlrno, cause);
descriptorfu(e, Qr);
descriptorfu(e, Qtx);
c->reg16[Rictl + c->portno*0x40>>1] = Rirpclr;
cause &= ~Hrparity;
}
if(cause & Hwparity){
print("#l%d: ram parity write error. bug? ca %.8ux\n", e->ctlrno, cause);
descriptorfu(e, Qr);
descriptorfu(e, Qtx);
c->reg16[Rictl + c->portno*0x40>>1] = Riwpclr;
cause &= ~Hwparity;
}
if(cause & Hmfault){
print("#l%d: mac parity error\n", e->ctlrno);
macwrite32(c, Gmfctl, Gmfcpe);
cause &= ~Hmfault;
}
if(cause)
print("#l%d: leftover hwe %.8ux\n", e->ctlrno, cause);
}
static void
macintr(Ether *e)
{
uint cause;
Ctlr *c;
c = e->ctlr;
cause = macread8(c, Irq);
cause &= ~(Rxdone | Txdone);
if(cause == 0)
return;
print("#l%d: mac error %.8ux\n", e->ctlrno, cause);
if(cause & Txovfl){
gmacread32(c, Txirq);
cause &= ~Txovfl;
}
if(cause & Rxovfl){
gmacread32(c, Rxirq);
cause &= ~Rxovfl;
}
if(cause & Rxorun){
macwrite32(c, Gfrxctl, Gmfcfu);
cause &= ~Rxorun;
}
if(cause & Txurun){
macwrite32(c, Gmfctl, Gmfcfu);
cause &= ~Txurun;
}
if(cause)
print("#l%d: leftover mac error %.8ux\n", e->ctlrno, cause);
}
static struct {
uint i;
uint q;
char *s;
} emap[] = {
Irx, Qr, "qr",
Itxs, Qtxs, "qtxs",
Itx, Qtx, "qtx",
Irx<<Iphy2base, Qr + 0x80, "qr1",
Itxs<<Iphy2base, Qtxs + 0x100, "qtxs1",
Itx<<Iphy2base, Qtx + 0x100, "qtx1",
};
static void
eerror(Ether *e, uint cause)
{
uint i, o, q;
Ctlr *c;
c = e->ctlr;
if(cause & Imac){
macintr(e);
cause &= ~Imac;
}
if(cause & (Irx | Itxs | Itx)*(1 | 1<<Iphy2base))
for(i = 0; i < nelem(emap); i++){
if((cause & emap[i].i) == 0)
continue;
q = emap[i].q;
o = prread16(c, q + Pgetidx);
print("#l%d: yuk: bug: %s: @%d ca=%.8ux\n",
e->ctlrno, emap[i].s, o, cause);
descriptorfu(e, q);
qrwrite(c, emap[i].q + Qcsr, Qcirqck);
cause &= ~emap[i].i;
}
if(cause)
print("#l%d: leftover error %.8ux\n", e->ctlrno, cause);
}
static void
iproc(void *v)
{
uint cause, d;
Ether *e;
Ctlr *c;
Kproc *k;
e = v;
c = e->ctlr;
k = &c->iproc;
for(;;){
starve(k);
cause = c->reg[Eisr];
if(cause & Iphy)
link(e);
if(cause & Ihwerr)
hwerror(e, cause);
if(cause & Ierror)
eerror(e, cause & Ierror);
if(cause & Ibmu)
sring(e);
d = c->reg[Lisr];
USED(d);
}
}
static void
interrupt(Ureg*, void *v)
{
uint cause;
Ctlr *c;
Ether *e;
e = v;
c = e->ctlr;
cause = c->reg[Isrc2];
if(cause != 0 && cause != ~0)
unstarve(&c->iproc);
}
static void
storefw(Ctlr *c)
{
if(c->type == Yukex && c->rev != 1
|| c->type == Yukfep
|| c->type == Yuksup)
macwrite32(c, Gmfctl, Gmfjon | Gmfsfon);
else{
macwrite32(c, Gmfae, 0x8000 | 0x70); /* tx gmac fifo */
macwrite32(c, Gmfctl, Gmfsfoff);
}
}
static void
raminit(Ctlr *c)
{
uint ram, rx;
if(ram = c->reg8[Ramcnt] * 4096/8){ /* in qwords */
c->flag |= Fram;
rx = ROUNDUP((2*ram)/3, 1024/8);
bufinit(c, Qr, 0, rx);
bufinit(c, Qtx, rx, ram);
rrwrite8(c, Qtxs + Rctl, Rrst); /* sync tx off */
}else{
macwrite8(c, Rxplo, 768/8);
macwrite8(c, Rxphi, 1024/8);
storefw(c);
}
}
static void
attach(Ether *e)
{
char buf[KNAMELEN];
Ctlr *c;
static Lock l;
c = e->ctlr;
if(c->attach == 1)
return;
lock(&l);
if(c->attach == 1){
unlock(&l);
return;
}
c->attach = 1;
unlock(&l);
snprint(buf, sizeof buf, "#l%dtproc", e->ctlrno);
kproc(buf, tproc, e);
snprint(buf, sizeof buf, "#l%drproc", e->ctlrno);
kproc(buf, rproc, e);
snprint(buf, sizeof buf, "#l%diproc", e->ctlrno);
kproc(buf, iproc, e);
c->reg[Ism] |= Ibmu | Iport<<Iphy2base*c->portno;
}
static long
ifstat(Ether *e0, void *a, long n, ulong offset)
{
char *s, *e, *p;
int i;
uint u;
Ctlr *c;
c = e0->ctlr;
p = s = malloc(READSTR);
e = p + READSTR;
for(i = 0; i < nelem(stattab); i++){
u = gmacread32(c, Stats + stattab[i].offset/4);
if(u > 0)
p = seprint(p, e, "%s\t%ud\n", stattab[i].name, u);
}
p = seprint(p, e, "stat %.4ux ctl %.3ux\n", gmacread(c, Stat), gmacread(c, Ctl));
p = seprint(p, e, "irq %.8ux\n", c->reg[Isrc2]);
p = seprint(p, e, "pref %.8ux %.4ux\n", prread32(c, Qr + Pctl), prread16(c, Qr + Pgetidx));
p = seprint(p, e, "nfast %ud nslow %ud\n", rbtab[c->qno].nfast, rbtab[c->qno].nslow);
if(debug){
p = dumppci(c, p, e);
p = dumpgmac(c, p, e);
p = dumpmac(c, p, e);
p = dumpreg(c, p, e);
}
seprint(p, e, "%s rev %d phy %s\n", idtab[c->type].name,
c->rev, c->feat&Ffiber? "fiber": "copper");
n = readstr(offset, a, n, s);
free(s);
return n;
}
static Cmdtab ctltab[] = {
1, "debug", 1,
2, "descriptorfu", 1,
};
static long
ctl(Ether *e, void *buf, long n)
{
Cmdbuf *cb;
Cmdtab *t;
cb = parsecmd(buf, n);
if(waserror()){
free(cb);
nexterror();
}
t = lookupcmd(cb, ctltab, nelem(ctltab));
switch(t->index){
case 0:
debug ^= 1;
break;
case 1:
descriptorfu(e, Qr);
break;
}
free(cb);
poperror();
return n;
}
static uint
yukpcicfgr32(Ctlr *c, uint r)
{
return c->reg[r + 0x1c00>>2];
}
static void
yukpcicfgw32(Ctlr *c, uint r, uint v)
{
c->reg[r + 0x1c00>>2] = v;
}
static void
phypower(Ctlr *c)
{
uint u, u0;
u = u0 = yukpcicfgr32(c, Pciphy);
u &= ~phypwr[c->portno];
if(c->type == Yukxl && c->rev > 1)
u |= coma[c->portno];
if(u != u0 || 1){
c->reg8[Tstctl1] = Tstwen;
yukpcicfgw32(c, Pciphy, u);
c->reg8[Tstctl1] = Tstwdis;
}
if(c->type == Yukfe)
c->reg8[Phyctl] = Aneen;
else if(c->flag & Fapwr)
macwrite32(c, Phy, Gphyrstclr);
}
static void
phyinit(Ctlr *c)
{
uint u;
if((c->feat & Fnewphy) == 0){
u = phyread(c, Phyextctl);
u &= ~0xf70; /* clear downshift counters */
u |= 0x7<<4; /* mac tx clock = 25mhz */
if(c->type == Yukec)
u |= 2*Dnmstr | Dnslv;
else
u |= Dnslv;
phywrite(c, Phyextctl, u);
}
u = phyread(c, Phyphy);
/* questionable value */
if(c->feat & Ffiber)
u &= ~Ppmdix;
else if(c->feat & Fgbe){
u &= ~Pped;
u |= Ppmdixa;
if(c->flag & Fnewphy){
// u &= ~(7<<12);
// u |= 2*(1<<12) | 1<<11; /* like 2*Dnmstr | Dnslv */
u |= 2*(1<<9) | 1<<11;
}
}else{
u |= Ppmdixa >> 1; /* why the shift? */
if(c->type == Yukfep && c->rev == 0){
}
}
phywrite(c, Phyphy, u);
/* copper/fiber specific stuff gmacwrite(c, Ctl, 0); */
gmacwrite(c, Ctl, 0);
if(c->feat & Fgbe)
if(c->feat & Ffiber)
phywrite(c, Gbectl, Gbexf | Gbexh);
else
phywrite(c, Gbectl, Gbef | Gbeh);
phywrite(c, Phyana, Anall);
phywrite(c, Phyctl, Phyrst | Anerst | Aneen);
/* chip specific stuff? */
if (c->type == Yukfep){
u = phyread(c, Phyphy) | Ppnpe;
u &= ~(Ppengy | Ppscrdis);
phywrite(c, Phyphy, u);
// phywrite(c, 0x16, 0x0b54); /* write to fe_led_par */
/* yukfep and rev 0: apply workaround for integrated resistor calibration */
phywrite(c, Phypadr, 17);
phywrite(c, 0x1e, 0x3f60);
}
phywrite(c, Phyintm, Anok | Anerr | Lsc);
dprint("phyid %.4ux step %.4ux\n", phyread(c, 2), phyread(c, 3));
}
static int
identify(Ctlr *c)
{
char t;
pcicfgw32(c->p, Pciclk, 0);
c->reg16[Ctst] = Swclr;
c->type = c->reg8[Chip] - 0xb3;
c->rev = c->reg8[Maccfg]>>4 & 0xf;
if(c->type >= Nyuk)
return -1;
if(idtab[c->type].okrev != 0xff)
if(c->rev != idtab[c->type].okrev)
return -1;
c->feat |= idtab[c->type].feat;
t = c->reg8[Pmd];
if(t == 'L' || t == 'S' || t == 'P')
c->feat |= Ffiber;
c->portno = 0;
/* check second port ... whatever */
return 0;
}
static uint
µ2clk(Ctlr *c, int µs)
{
return idtab[c->type].mhz * µs;
}
static void
gmacsetea(Ctlr *c, uint r)
{
uchar *ra;
int i;
ra = c->ra;
for(i = 0; i < Eaddrlen; i += 2)
gmacwrite(c, r + i, ra[i + 0] | ra[i + 1]<<8);
}
static int
reset(Ctlr *c)
{
uint i, j;
Block *b;
identify(c);
if(c->type == Yukex)
c->reg16[Asfcs/2] &= ~(Asfbrrst | Asfcpurst | Asfucrst);
else
c->reg8[Asfcs] = Asfrst;
c->reg16[Ctst] = Asfdis;
c->reg16[Ctst] = Swrst;
c->reg16[Ctst] = Swclr;
c->reg8[Tstctl1] = Tstwen;
pcicfgw16(c->p, PciPSR, pcicfgr16(c->p, PciPSR) | 0xf100);
c->reg16[Ctst] = Mstrclr;
/* fixup pcie extended error goes here */
c->reg8[Pwrctl] = Vauxen | Vccen | Vauxoff | Vccon;
c->reg[Clkctl] = Clkdivdis;
if(c->type == Yukxl && c->rev > 1)
c->reg8[Clkgate] = ~Link2inactive;
else
c->reg8[Clkgate] = 0;
if(c->flag & Fapwr){
pcicfgw32(c->p, Pciclk, 0);
pcicfgw32(c->p, Pciasp, pcicfgr32(c->p, Pciasp) & Aspmsk);
pcicfgw32(c->p, Pcistate, pcicfgr32(c->p, Pcistate) & Vmain);
pcicfgw32(c->p, Pcicf1, 0);
c->reg[Gpio] |= Norace;
print("yuk2: advanced power %.8ux\n", c->reg[Gpio]);
}
c->reg8[Tstctl1] = Tstwdis;
for(i = 0; i < c->nports; i++){
macwrite8(c, Linkctl, Linkrst);
macwrite8(c, Linkctl, Linkclr);
if(c->type == Yukex || c->type == Yuksup)
macwrite16(c, Mac, Nomacsec | Nortx);
}
c->reg[Dpolltm] = Pollstop;
for(i = 0; i < c->nports; i++)
macwrite8(c, Txactl, Txaclr);
for(i = 0; i < c->nports; i++){
c->reg8[i*64 + Rictl] = Riclr;
for(j = 0; j < 12; j++)
c->reg8[i*64 + Rib + j] = 36; /* qword times */
}
c->reg[Hwem] = Hdflt;
macwrite8(c, Irqm, 0);
for(i = 0; i < 4; i++)
gmacwrite(c, Mchash + 2*i, 0);
gmacwrite(c, Rxctl, Ufilter | Mfilter | Rmcrc);
for(i = 0; i < nelem(c->tbring); i++)
if(b = c->tbring[i]){
c->tbring[i] = nil;
freeb(b);
}
for(i = 0; i < nelem(c->rbring); i++)
if(b = c->rbring[i]){
c->rbring[i] = nil;
freeb(b);
}
memset(c->tbring, 0, sizeof c->tbring[0] * nelem(c->tbring));
memset(c->rbring, 0, sizeof c->rbring[0] * nelem(c->rbring));
memset(c->tx.r, 0, sizeof c->tx.r[0] * c->tx.cnt);
memset(c->rx.r, 0, sizeof c->rx.r[0] * c->rx.cnt);
memset(c->status.r, 0, sizeof c->status.r[0] * c->status.cnt);
c->reg[Statctl] = Statrst;
c->reg[Statctl] = Statclr;
c->reg[Stataddr + 0] = Pciwaddrl(c->status.r);
c->reg[Stataddr + 4] = Pciwaddrh(c->status.r);
c->reg16[Stattl] = c->status.m;
c->reg16[Statth] = 10;
c->reg8[Statwm] = 16;
if(c->type == Yukxl && c->rev == 0)
c->reg8[Statiwm] = 4;
else
c->reg8[Statiwm] = 4; //16;
/* set transmit, isr, level timers */
c->reg[Tsti] = µ2clk(c, 1000);
c->reg[Titi] = µ2clk(c, 20);
c->reg[Tlti] = µ2clk(c, 100);
c->reg[Statctl] = Staton;
c->reg8[Tstc] = Tstart;
c->reg8[Tltc] = Tstart;
c->reg8[Titc] = Tstart;
return 0;
}
static void
macinit(Ctlr *c)
{
uint r, i;
r = macread32(c, Phy) & ~(Gphyrst | Gphyrstclr);
macwrite32(c, Phy, r | Gphyrst);
macwrite32(c, Phy, r | Gphyrstclr);
/* macwrite32(c, Mac, Macrst); ? */
macwrite32(c, Mac, Macrstclr);
if(c->type == Yukxl && c->rev == 0 && c->portno == 1){
}
macread8(c, Irq);
macwrite8(c, Irqm, Txurun);
phypower(c);
phyinit(c);
gmacwrite(c, Phyaddr, (r = gmacread(c, Phyaddr)) | Mibclear);
for(i = 0; i < nelem(stattab); i++)
gmacread32(c, Stats + stattab[i].offset/4);
gmacwrite(c, Phyaddr, r);
gmacwrite(c, Txctl, 4<<10); /* collision distance */
gmacwrite(c, Txflow, 0xffff); /* flow control */
gmacwrite(c, Txparm, 3<<14 | 0xb<<9 | 0x1c<<4 | 4);
gmacwrite(c, Rxctl, Ufilter | Mfilter | Rmcrc);
gmacwrite(c, Serctl, 0x04<<11 /* blind */ | Jumboen | 0x1e /* ipig */);
gmacsetea(c, Ea0);
gmacsetea(c, Ea1);
gmacwrite(c, Txmask, 0);
gmacwrite(c, Rxmask, 0);
gmacwrite(c, Trmask, 0);
macwrite32(c, Gfrxctl, Gfrstclr);
r = Gfon | Gffon;
if(c->type == Yukex || c->type == Yukfep)
r |= Gfroon;
macwrite32(c, Gfrxctl, r);
if(c->type == Yukxl)
macwrite32(c, Grxfm, 0);
else
macwrite32(c, Grxfm, Ferror);
if(c->type == Yukfep && c->rev == 0)
macwrite32(c, Grxft, 0x178);
else
macwrite32(c, Grxft, 0xb);
macwrite32(c, Gmfctl, Gmfclr); /* clear reset */
macwrite32(c, Gmfctl, Gmfon); /* on */
raminit(c);
if(c->type == Yukfep && c->rev == 0)
c->reg[Gmfea] = c->reg[Gmfea] & ~3;
c->rxinit = 0;
c->txinit = 0;
}
static void*
slice(void **v, uint r, uint sz)
{
uintptr a;
a = (uintptr)*v;
a = ROUNDUP(a, r);
*v = (void*)(a + sz);
return (void*)a;
}
static void
setupr(Sring *r, uint cnt)
{
r->rp = 0;
r->wp = 0;
r->cnt = cnt;
r->m = cnt - 1;
}
static int
setup(Ctlr *c)
{
uint n;
void *v, *mem;
Pcidev *p;
p = c->p;
c->io = p->mem[0].bar&~0xf;
mem = vmap(c->io, p->mem[0].size);
if(mem == nil){
print("yuk: cant map %#p\n", c->io);
return -1;
}
c->p = p;
c->reg = (uint*)mem;
c->reg8 = (uchar*)mem;
c->reg16 = (ushort*)mem;
if(memcmp(c->ra, nilea, sizeof c->ra) == 0)
memmove(c->ra, c->reg8 + Macadr + 8*c->portno, Eaddrlen);
setupr(&c->status, Sringcnt);
setupr(&c->tx, Tringcnt);
setupr(&c->rx, Rringcnt);
n = sizeof c->status.r[0] * (c->status.cnt + c->tx.cnt + c->rx.cnt);
n += 16*4096*2; /* rounding slop */
c->alloc = xspanalloc(n, 16*4096, 0); /* unknown alignment constraints */
memset(c->alloc, 0, n);
v = c->alloc;
c->status.r = slice(&v, 16*4096, sizeof c->status.r[0] * c->status.cnt);
c->tx.r = slice(&v, 16*4096, sizeof c->tx.r[0] * c->tx.cnt);
c->rx.r = slice(&v, 16*4096, sizeof c->rx.r[0] * c->rx.cnt);
c->nports = 1; /* BOTCH */
pcisetbme(p);
if(reset(c)){
print("yuk: cant reset\n");
pciclrbme(p);
free(c->alloc);
vunmap(mem, p->mem[0].size);
return -1;
}
macinit(c);
return 0;
}
static void
shutdown(Ether *e)
{
Ctlr *c;
Pcidev *p;
c = e->ctlr;
reset(c);
if(0){
p = c->p;
vunmap(c->reg, p->mem[0].size);
free(c->alloc);
}
}
static void
scan(void)
{
int i;
Pcidev *p;
Ctlr *c;
for(p = nil; p = pcimatch(p, 0, 0); ){
for(i = 0; i < nelem(vtab); i++)
if(vtab[i].vid == p->vid)
if(vtab[i].did == p->did)
break;
if(i == nelem(vtab))
continue;
if(nctlr == nelem(ctlrtab)){
print("yuk: too many controllers\n");
return;
}
c = malloc(sizeof *c);
c->p = p;
c->qno = nctlr;
rbtab[c->qno].k = &c->rxmit;
c->rbsz = vtab[i].mtu;
ctlrtab[nctlr++] = c;
}
}
static int
pnp(Ether *e)
{
int i;
Ctlr *c;
if(nctlr == 0)
scan();
for(i = 0;; i++){
if(i == nctlr)
return -1;
c = ctlrtab[i];
if(c == nil || c->flag&Fprobe)
continue;
if(e->port != 0 && e->port != (ulong)c->reg)
continue;
c->flag |= Fprobe;
if(setup(c) != 0)
continue;
break;
}
e->ctlr = c;
e->port = c->io;
e->irq = c->p->intl;
e->tbdf = c->p->tbdf;
e->mbps = 1000;
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 = nil;
return 0;
}
void
etheryuklink(void)
{
addethercard("yuk", pnp);
}
|