/*
* marvell 88e8057 yukon2 boot driver
* copyright © 2009-10 erik quanstrom
*/
#include "u.h"
#include "lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.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 {}
#define sfence() coherence()
#define debug _debug
#define qinit _qinit
#define Fprobe _Fprobe
#define Nrb _Nrb
enum {
Nctlr = 2,
Nrb = 1024,
Rbalign = 64,
Fprobe = 1<<0,
Sringcnt = 2048,
Tringcnt = 512,
// Rringcnt = Nrbyuk,
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 Mc Mc;
typedef struct Status Status;
typedef struct Sring Sring;
typedef struct Vtab Vtab;
struct Chipid {
uchar feat;
uchar okrev;
uchar mhz;
char *name;
};
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];
};
struct Mc {
Mc *next;
uchar ea[Eaddrlen];
};
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 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 Status*
getslot(Sring *r, void *)
{
if(r->rp + r->m - r->wp & ~r->m)
panic("ring full");
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;
}
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)
print("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)
print("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
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);
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, nil);
putle(t->status, 14<<16 | 14, 4);
t->ctl = 0;
t->op = Ock | Hw;
qrwrite(c, Qr + Qcsr, Qsumen);
}
macwrite32(c, Gfrxctl, Gftroff);
}
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);
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 = allocb(2048);
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;
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 int spdtab[4] = {
10, 100, 1000, 0,
};
static void
link(Ether *e)
{
uint i, s, spd;
Ctlr *c;
int link;
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;
link = (s & Plink) != 0;
if(link && c->feat&Ffiber)
spd = 1000;
else if(link){
spd = s & Physpd;
spd >>= 14;
spd = spdtab[spd];
}
e->mbps = spd;
dprint("#l%d: yuk: link %d spd %d\n", e->ctlrno, 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 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;
toringbuf(e, b, BLEN(b));
freeb(b);
}
r->rp = rp;
}
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);
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);
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);
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);
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
interrupt(Ureg*, void *v)
{
uint cause, d;
Ctlr *c;
Ether *e;
e = v;
c = e->ctlr;
cause = c->reg[Isrc2];
if(cause != 0 && cause != ~0)
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
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
transmit(Ether* edev)
{
Block *b;
Status *t;
Sring *r;
Ctlr *c;
RingBuf *tb;
c = edev->ctlr;
r = &c->tx;
while((tb = &edev->tb[edev->ti])->owner == Interface){
b = fromringbuf(edev);
if(Pciwaddrh(b->rp) != 0){
t = getslot(r, nil);
t->ctl = 0;
t->op = Oaddr64 | Hw;
putle(t->status, Pciwaddrh(b->rp), 4);
}
t = getslot(r, nil);
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);
tb->owner = Host; /* give descriptor back */
edev->ti = NEXT(edev->ti, edev->ntb);
}
}
static void
tproc(Ether *e)
{
Ctlr *c;
c = e->ctlr;
txinit(e);
linkup(c, Txen);
}
static void
rproc(Ether *e)
{
Ctlr *c;
c = e->ctlr;
rxinit(e);
linkup(c, Rxen);
if(replenish(e, c) == 0){
// starve(k);
print("yuk: rx unstarve?\n");
}
}
static void
attach(Ether *e)
{
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);
tproc(e);
rproc(e);
c->reg[Ism] |= Ibmu | Iport<<Iphy2base*c->portno;
}
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? */
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);
/* yukfep and rev 0: apply workaround for integrated resistor calibration */
phywrite(c, 0x16, 0xb54); /* write to fe_led_par */
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;
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);
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;
ulong mem;
Pcidev *p;
p = c->p;
c->io = p->mem[0].bar&~0xf;
mem = upamalloc(c->io, p->mem[0].size,0);
if(mem == 0){
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);
return -1;
}
macinit(c);
return 0;
}
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;
c->rbsz = vtab[i].mtu;
ctlrtab[nctlr++] = c;
}
}
int
yukpnp(Ether *e)
{
int i;
Ctlr *c;
debug = 0;
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;
memmove(e->ea, c->ra, Eaddrlen);
e->attach = attach;
e->interrupt = interrupt;
e->transmit = transmit;
return 0;
}
|