ref: 2e369f0d1fad5f38c44ed13109f74cdd6ab535f2
dir: /sys/src/9/mtx/ether2114x.c/
/* * Digital Semiconductor DECchip 2114x PCI Fast Ethernet LAN Controller. * To do: * thresholds; * ring sizing; * handle more error conditions; * tidy setup packet mess; * push initialisation back to attach; * full SROM decoding. */ #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" #define DEBUG (0) #define debug if(DEBUG)print enum { Nrde = 64, Ntde = 64, }; #define Rbsz ROUNDUP(sizeof(Etherpkt)+4, 4) enum { /* CRS0 - Bus Mode */ Swr = 0x00000001, /* Software Reset */ Bar = 0x00000002, /* Bus Arbitration */ Dsl = 0x0000007C, /* Descriptor Skip Length (field) */ Ble = 0x00000080, /* Big/Little Endian */ Pbl = 0x00003F00, /* Programmable Burst Length (field) */ Cal = 0x0000C000, /* Cache Alignment (field) */ Cal8 = 0x00004000, /* 8 longword boundary alignment */ Cal16 = 0x00008000, /* 16 longword boundary alignment */ Cal32 = 0x0000C000, /* 32 longword boundary alignment */ Tap = 0x000E0000, /* Transmit Automatic Polling (field) */ Dbo = 0x00100000, /* Descriptor Byte Ordering Mode */ Rml = 0x00200000, /* Read Multiple */ }; enum { /* CSR[57] - Status and Interrupt Enable */ Ti = 0x00000001, /* Transmit Interrupt */ Tps = 0x00000002, /* Transmit Process Stopped */ Tu = 0x00000004, /* Transmit buffer Unavailable */ Tjt = 0x00000008, /* Transmit Jabber Timeout */ Unf = 0x00000020, /* transmit UNderFlow */ Ri = 0x00000040, /* Receive Interrupt */ Ru = 0x00000080, /* Receive buffer Unavailable */ Rps = 0x00000100, /* Receive Process Stopped */ Rwt = 0x00000200, /* Receive Watchdog Timeout */ Eti = 0x00000400, /* Early Transmit Interrupt */ Gte = 0x00000800, /* General purpose Timer Expired */ Fbe = 0x00002000, /* Fatal Bus Error */ Ais = 0x00008000, /* Abnormal Interrupt Summary */ Nis = 0x00010000, /* Normal Interrupt Summary */ Rs = 0x000E0000, /* Receive process State (field) */ Ts = 0x00700000, /* Transmit process State (field) */ Eb = 0x03800000, /* Error bits */ }; enum { /* CSR6 - Operating Mode */ Hp = 0x00000001, /* Hash/Perfect receive filtering mode */ Sr = 0x00000002, /* Start/stop Receive */ Ho = 0x00000004, /* Hash-Only filtering mode */ Pb = 0x00000008, /* Pass Bad frames */ If = 0x00000010, /* Inverse Filtering */ Sb = 0x00000020, /* Start/stop Backoff counter */ Pr = 0x00000040, /* Promiscuous Mode */ Pm = 0x00000080, /* Pass all Multicast */ Fd = 0x00000200, /* Full Duplex mode */ Om = 0x00000C00, /* Operating Mode (field) */ Fc = 0x00001000, /* Force Collision */ St = 0x00002000, /* Start/stop Transmission Command */ Tr = 0x0000C000, /* ThReshold control bits (field) */ Tr128 = 0x00000000, Tr256 = 0x00004000, Tr512 = 0x00008000, Tr1024 = 0x0000C000, Ca = 0x00020000, /* CApture effect enable */ Ps = 0x00040000, /* Port Select */ Hbd = 0x00080000, /* HeartBeat Disable */ Imm = 0x00100000, /* IMMediate mode */ Sf = 0x00200000, /* Store and Forward */ Ttm = 0x00400000, /* Transmit Threshold Mode */ Pcs = 0x00800000, /* PCS function */ Scr = 0x01000000, /* SCRambler mode */ Mbo = 0x02000000, /* Must Be One */ Ra = 0x40000000, /* Receive All */ Sc = 0x80000000, /* Special Capture effect enable */ TrMODE = Tr512, /* default transmission threshold */ }; enum { /* CSR9 - ROM and MII Management */ Scs = 0x00000001, /* serial ROM chip select */ Sclk = 0x00000002, /* serial ROM clock */ Sdi = 0x00000004, /* serial ROM data in */ Sdo = 0x00000008, /* serial ROM data out */ Ss = 0x00000800, /* serial ROM select */ Wr = 0x00002000, /* write */ Rd = 0x00004000, /* read */ Mdc = 0x00010000, /* MII management clock */ Mdo = 0x00020000, /* MII management write data */ Mii = 0x00040000, /* MII management operation mode (W) */ Mdi = 0x00080000, /* MII management data in */ }; enum { /* CSR12 - General-Purpose Port */ Gpc = 0x00000100, /* General Purpose Control */ }; typedef struct Des { int status; int control; ulong addr; Block* bp; } Des; enum { /* status */ Of = 0x00000001, /* Rx: OverFlow */ Ce = 0x00000002, /* Rx: CRC Error */ Db = 0x00000004, /* Rx: Dribbling Bit */ Re = 0x00000008, /* Rx: Report on MII Error */ Rw = 0x00000010, /* Rx: Receive Watchdog */ Ft = 0x00000020, /* Rx: Frame Type */ Cs = 0x00000040, /* Rx: Collision Seen */ Tl = 0x00000080, /* Rx: Frame too Long */ Ls = 0x00000100, /* Rx: Last deScriptor */ Fs = 0x00000200, /* Rx: First deScriptor */ Mf = 0x00000400, /* Rx: Multicast Frame */ Rf = 0x00000800, /* Rx: Runt Frame */ Dt = 0x00003000, /* Rx: Data Type (field) */ De = 0x00004000, /* Rx: Descriptor Error */ Fl = 0x3FFF0000, /* Rx: Frame Length (field) */ Ff = 0x40000000, /* Rx: Filtering Fail */ Def = 0x00000001, /* Tx: DEFerred */ Uf = 0x00000002, /* Tx: UnderFlow error */ Lf = 0x00000004, /* Tx: Link Fail report */ Cc = 0x00000078, /* Tx: Collision Count (field) */ Hf = 0x00000080, /* Tx: Heartbeat Fail */ Ec = 0x00000100, /* Tx: Excessive Collisions */ Lc = 0x00000200, /* Tx: Late Collision */ Nc = 0x00000400, /* Tx: No Carrier */ Lo = 0x00000800, /* Tx: LOss of carrier */ To = 0x00004000, /* Tx: Transmission jabber timeOut */ Es = 0x00008000, /* [RT]x: Error Summary */ Own = 0x80000000, /* [RT]x: OWN bit */ }; enum { /* control */ Bs1 = 0x000007FF, /* [RT]x: Buffer 1 Size */ Bs2 = 0x003FF800, /* [RT]x: Buffer 2 Size */ Ch = 0x01000000, /* [RT]x: second address CHained */ Er = 0x02000000, /* [RT]x: End of Ring */ Ft0 = 0x00400000, /* Tx: Filtering Type 0 */ Dpd = 0x00800000, /* Tx: Disabled PaDding */ Ac = 0x04000000, /* Tx: Add CRC disable */ Set = 0x08000000, /* Tx: SETup packet */ Ft1 = 0x10000000, /* Tx: Filtering Type 1 */ Fseg = 0x20000000, /* Tx: First SEGment */ Lseg = 0x40000000, /* Tx: Last SEGment */ Ic = 0x80000000, /* Tx: Interrupt on Completion */ }; enum { /* PHY registers */ Bmcr = 0, /* Basic Mode Control */ Bmsr = 1, /* Basic Mode Status */ Phyidr1 = 2, /* PHY Identifier #1 */ Phyidr2 = 3, /* PHY Identifier #2 */ Anar = 4, /* Auto-Negotiation Advertisment */ Anlpar = 5, /* Auto-Negotiation Link Partner Ability */ Aner = 6, /* Auto-Negotiation Expansion */ }; enum { /* Variants */ Tulip0 = (0x0009<<16)|0x1011, Tulip3 = (0x0019<<16)|0x1011, Pnic = (0x0002<<16)|0x11AD, Pnic2 = (0xC115<<16)|0x11AD, }; typedef struct Ctlr Ctlr; typedef struct Ctlr { int port; Pcidev* pcidev; Ctlr* next; int active; int id; /* (pcidev->did<<16)|pcidev->vid */ uchar* srom; int sromsz; /* address size in bits */ uchar* sromea; /* MAC address */ uchar* leaf; int sct; /* selected connection type */ int k; /* info block count */ uchar* infoblock[16]; int sctk; /* sct block index */ int curk; /* current block index */ uchar* type5block; int phy[32]; /* logical to physical map */ int phyreset; /* reset bitmap */ int curphyad; int fdx; int ttm; uchar fd; /* option */ int medium; /* option */ int csr6; /* CSR6 - operating mode */ int mask; /* CSR[57] - interrupt mask */ int mbps; Lock lock; Des* rdr; /* receive descriptor ring */ int nrdr; /* size of rdr */ int rdrx; /* index into rdr */ Lock tlock; Des* tdr; /* transmit descriptor ring */ int ntdr; /* size of tdr */ int tdrh; /* host index into tdr */ int tdri; /* interface index into tdr */ int ntq; /* descriptors active */ int ntqmax; Block* setupbp; ulong of; /* receive statistics */ ulong ce; ulong cs; ulong tl; ulong rf; ulong de; ulong ru; ulong rps; ulong rwt; ulong uf; /* transmit statistics */ ulong ec; ulong lc; ulong nc; ulong lo; ulong to; ulong tps; ulong tu; ulong tjt; ulong unf; } Ctlr; static Ctlr* ctlrhead; static Ctlr* ctlrtail; #define csr32r(c, r) (inl((c)->port+((r)*8))) #define csr32w(c, r, l) (outl((c)->port+((r)*8), (ulong)(l))) static void promiscuous(void* arg, int on) { Ctlr *ctlr; ctlr = ((Ether*)arg)->ctlr; ilock(&ctlr->lock); if(on) ctlr->csr6 |= Pr; else ctlr->csr6 &= ~Pr; csr32w(ctlr, 6, ctlr->csr6); iunlock(&ctlr->lock); } static void attach(Ether* ether) { Ctlr *ctlr; ctlr = ether->ctlr; ilock(&ctlr->lock); if(!(ctlr->csr6 & Sr)){ ctlr->csr6 |= Sr; csr32w(ctlr, 6, ctlr->csr6); } iunlock(&ctlr->lock); } static long ifstat(Ether* ether, void* a, long n, ulong offset) { Ctlr *ctlr; char *buf, *p; int i, l, len; ctlr = ether->ctlr; ether->crcs = ctlr->ce; ether->frames = ctlr->rf+ctlr->cs; ether->buffs = ctlr->de+ctlr->tl; ether->overflows = ctlr->of; if(n == 0) return 0; p = malloc(READSTR); l = snprint(p, READSTR, "Overflow: %lud\n", ctlr->of); l += snprint(p+l, READSTR-l, "Ru: %lud\n", ctlr->ru); l += snprint(p+l, READSTR-l, "Rps: %lud\n", ctlr->rps); l += snprint(p+l, READSTR-l, "Rwt: %lud\n", ctlr->rwt); l += snprint(p+l, READSTR-l, "Tps: %lud\n", ctlr->tps); l += snprint(p+l, READSTR-l, "Tu: %lud\n", ctlr->tu); l += snprint(p+l, READSTR-l, "Tjt: %lud\n", ctlr->tjt); l += snprint(p+l, READSTR-l, "Unf: %lud\n", ctlr->unf); l += snprint(p+l, READSTR-l, "CRC Error: %lud\n", ctlr->ce); l += snprint(p+l, READSTR-l, "Collision Seen: %lud\n", ctlr->cs); l += snprint(p+l, READSTR-l, "Frame Too Long: %lud\n", ctlr->tl); l += snprint(p+l, READSTR-l, "Runt Frame: %lud\n", ctlr->rf); l += snprint(p+l, READSTR-l, "Descriptor Error: %lud\n", ctlr->de); l += snprint(p+l, READSTR-l, "Underflow Error: %lud\n", ctlr->uf); l += snprint(p+l, READSTR-l, "Excessive Collisions: %lud\n", ctlr->ec); l += snprint(p+l, READSTR-l, "Late Collision: %lud\n", ctlr->lc); l += snprint(p+l, READSTR-l, "No Carrier: %lud\n", ctlr->nc); l += snprint(p+l, READSTR-l, "Loss of Carrier: %lud\n", ctlr->lo); l += snprint(p+l, READSTR-l, "Transmit Jabber Timeout: %lud\n", ctlr->to); l += snprint(p+l, READSTR-l, "csr6: %luX %uX\n", csr32r(ctlr, 6), ctlr->csr6); snprint(p+l, READSTR-l, "ntqmax: %d\n", ctlr->ntqmax); ctlr->ntqmax = 0; buf = a; len = readstr(offset, buf, n, p); if(offset > l) offset -= l; else offset = 0; buf += len; n -= len; l = snprint(p, READSTR, "srom:"); for(i = 0; i < (1<<(ctlr->sromsz)*sizeof(ushort)); i++){ if(i && ((i & 0x0F) == 0)) l += snprint(p+l, READSTR-l, "\n "); l += snprint(p+l, READSTR-l, " %2.2uX", ctlr->srom[i]); } snprint(p+l, READSTR-l, "\n"); len += readstr(offset, buf, n, p); free(p); return len; } static void txstart(Ether* ether) { Ctlr *ctlr; Block *bp; Des *des; int control; ctlr = ether->ctlr; while(ctlr->ntq < (ctlr->ntdr-1)){ if(ctlr->setupbp){ bp = ctlr->setupbp; ctlr->setupbp = 0; control = Ic|Set|BLEN(bp); } else{ bp = qget(ether->oq); if(bp == nil) break; control = Ic|Lseg|Fseg|BLEN(bp); } ctlr->tdr[PREV(ctlr->tdrh, ctlr->ntdr)].control &= ~Ic; des = &ctlr->tdr[ctlr->tdrh]; des->bp = bp; des->addr = PCIWADDR(bp->rp); des->control |= control; ctlr->ntq++; coherence(); des->status = Own; csr32w(ctlr, 1, 0); ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr); } if(ctlr->ntq > ctlr->ntqmax) ctlr->ntqmax = ctlr->ntq; } static void transmit(Ether* ether) { Ctlr *ctlr; ctlr = ether->ctlr; ilock(&ctlr->tlock); txstart(ether); iunlock(&ctlr->tlock); } static void interrupt(Ureg*, void* arg) { Ctlr *ctlr; Ether *ether; int len, status; Des *des; Block *bp; ether = arg; ctlr = ether->ctlr; while((status = csr32r(ctlr, 5)) & (Nis|Ais)){ /* * Acknowledge the interrupts and mask-out * the ones that are implicitly handled. */ csr32w(ctlr, 5, status); status &= (ctlr->mask & ~(Nis|Ti)); if(status & Ais){ if(status & Tps) ctlr->tps++; if(status & Tu) ctlr->tu++; if(status & Tjt) ctlr->tjt++; if(status & Ru) ctlr->ru++; if(status & Rps) ctlr->rps++; if(status & Rwt) ctlr->rwt++; status &= ~(Ais|Rwt|Rps|Ru|Tjt|Tu|Tps); } /* * Received packets. */ if(status & Ri){ des = &ctlr->rdr[ctlr->rdrx]; while(!(des->status & Own)){ if(des->status & Es){ if(des->status & Of) ctlr->of++; if(des->status & Ce) ctlr->ce++; if(des->status & Cs) ctlr->cs++; if(des->status & Tl) ctlr->tl++; if(des->status & Rf) ctlr->rf++; if(des->status & De) ctlr->de++; } else if(bp = iallocb(Rbsz)){ len = ((des->status & Fl)>>16)-4; des->bp->wp = des->bp->rp+len; etheriq(ether, des->bp); des->bp = bp; des->addr = PCIWADDR(bp->rp); } des->control &= Er; des->control |= Rbsz; coherence(); des->status = Own; ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr); des = &ctlr->rdr[ctlr->rdrx]; } status &= ~Ri; } /* * Check the transmit side: * check for Transmit Underflow and Adjust * the threshold upwards; * free any transmitted buffers and try to * top-up the ring. */ if(status & Unf){ ctlr->unf++; ilock(&ctlr->lock); csr32w(ctlr, 6, ctlr->csr6 & ~St); switch(ctlr->csr6 & Tr){ case Tr128: len = Tr256; break; case Tr256: len = Tr512; break; case Tr512: len = Tr1024; break; default: case Tr1024: len = Sf; break; } ctlr->csr6 = (ctlr->csr6 & ~Tr)|len; csr32w(ctlr, 6, ctlr->csr6); iunlock(&ctlr->lock); csr32w(ctlr, 5, Tps); status &= ~(Unf|Tps); } ilock(&ctlr->tlock); while(ctlr->ntq){ des = &ctlr->tdr[ctlr->tdri]; if(des->status & Own) break; if(des->status & Es){ if(des->status & Uf) ctlr->uf++; if(des->status & Ec) ctlr->ec++; if(des->status & Lc) ctlr->lc++; if(des->status & Nc) ctlr->nc++; if(des->status & Lo) ctlr->lo++; if(des->status & To) ctlr->to++; ether->oerrs++; } freeb(des->bp); des->control &= Er; ctlr->ntq--; ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr); } txstart(ether); iunlock(&ctlr->tlock); /* * Anything left not catered for? */ if(status) panic("#l%d: status %8.8uX\n", ether->ctlrno, status); } } static void ctlrinit(Ether* ether) { Ctlr *ctlr; Des *des; Block *bp; int i; uchar bi[Eaddrlen*2]; ctlr = ether->ctlr; /* * Allocate and initialise the receive ring; * allocate and initialise the transmit ring; * unmask interrupts and start the transmit side; * create and post a setup packet to initialise * the physical ethernet address. */ ctlr->rdr = xspanalloc(ctlr->nrdr*sizeof(Des), 8*sizeof(ulong), 0); for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){ des->bp = iallocb(Rbsz); if(des->bp == nil) panic("can't allocate ethernet receive ring\n"); des->status = Own; des->control = Rbsz; des->addr = PCIWADDR(des->bp->rp); } ctlr->rdr[ctlr->nrdr-1].control |= Er; ctlr->rdrx = 0; csr32w(ctlr, 3, PCIWADDR(ctlr->rdr)); ctlr->tdr = xspanalloc(ctlr->ntdr*sizeof(Des), 8*sizeof(ulong), 0); ctlr->tdr[ctlr->ntdr-1].control |= Er; ctlr->tdrh = 0; ctlr->tdri = 0; csr32w(ctlr, 4, PCIWADDR(ctlr->tdr)); /* * Clear any bits in the Status Register (CSR5) as * the PNIC has a different reset value from a true 2114x. */ ctlr->mask = Nis|Ais|Fbe|Rwt|Rps|Ru|Ri|Unf|Tjt|Tps|Ti; csr32w(ctlr, 5, ctlr->mask); csr32w(ctlr, 7, ctlr->mask); ctlr->csr6 |= St; csr32w(ctlr, 6, ctlr->csr6); for(i = 0; i < Eaddrlen/2; i++){ bi[i*4] = ether->ea[i*2]; bi[i*4+1] = ether->ea[i*2+1]; bi[i*4+2] = ether->ea[i*2+1]; bi[i*4+3] = ether->ea[i*2]; } bp = iallocb(Eaddrlen*2*16); if(bp == nil) panic("can't allocate ethernet setup buffer\n"); memset(bp->rp, 0xFF, sizeof(bi)); for(i = sizeof(bi); i < sizeof(bi)*16; i += sizeof(bi)) memmove(bp->rp+i, bi, sizeof(bi)); bp->wp += sizeof(bi)*16; ctlr->setupbp = bp; ether->oq = qopen(256*1024, Qmsg, 0, 0); transmit(ether); } static void csr9w(Ctlr* ctlr, int data) { csr32w(ctlr, 9, data); microdelay(1); } static int miimdi(Ctlr* ctlr, int n) { int data, i; /* * Read n bits from the MII Management Register. */ data = 0; for(i = n-1; i >= 0; i--){ if(csr32r(ctlr, 9) & Mdi) data |= (1<<i); csr9w(ctlr, Mii|Mdc); csr9w(ctlr, Mii); } csr9w(ctlr, 0); return data; } static void miimdo(Ctlr* ctlr, int bits, int n) { int i, mdo; /* * Write n bits to the MII Management Register. */ for(i = n-1; i >= 0; i--){ if(bits & (1<<i)) mdo = Mdo; else mdo = 0; csr9w(ctlr, mdo); csr9w(ctlr, mdo|Mdc); csr9w(ctlr, mdo); } } static int miir(Ctlr* ctlr, int phyad, int regad) { int data, i; if(ctlr->id == Pnic){ i = 1000; csr32w(ctlr, 20, 0x60020000|(phyad<<23)|(regad<<18)); do{ microdelay(1); data = csr32r(ctlr, 20); }while((data & 0x80000000) && --i); if(i == 0) return -1; return data & 0xFFFF; } /* * Preamble; * ST+OP+PHYAD+REGAD; * TA + 16 data bits. */ miimdo(ctlr, 0xFFFFFFFF, 32); miimdo(ctlr, 0x1800|(phyad<<5)|regad, 14); data = miimdi(ctlr, 18); if(data & 0x10000) return -1; return data & 0xFFFF; } static void miiw(Ctlr* ctlr, int phyad, int regad, int data) { /* * Preamble; * ST+OP+PHYAD+REGAD+TA + 16 data bits; * Z. */ miimdo(ctlr, 0xFFFFFFFF, 32); data &= 0xFFFF; data |= (0x05<<(5+5+2+16))|(phyad<<(5+2+16))|(regad<<(2+16))|(0x02<<16); miimdo(ctlr, data, 32); csr9w(ctlr, Mdc); csr9w(ctlr, 0); } static int sromr(Ctlr* ctlr, int r) { int i, op, data, size; if(ctlr->id == Pnic){ i = 1000; csr32w(ctlr, 19, 0x600|r); do{ microdelay(1); data = csr32r(ctlr, 19); }while((data & 0x80000000) && --i); if(ctlr->sromsz == 0) ctlr->sromsz = 6; return csr32r(ctlr, 9) & 0xFFFF; } /* * This sequence for reading a 16-bit register 'r' * in the EEPROM is taken straight from Section * 7.4 of the 21140 Hardware Reference Manual. */ reread: csr9w(ctlr, Rd|Ss); csr9w(ctlr, Rd|Ss|Scs); csr9w(ctlr, Rd|Ss|Sclk|Scs); csr9w(ctlr, Rd|Ss); op = 0x06; for(i = 3-1; i >= 0; i--){ data = Rd|Ss|(((op>>i) & 0x01)<<2)|Scs; csr9w(ctlr, data); csr9w(ctlr, data|Sclk); csr9w(ctlr, data); } /* * First time through must work out the EEPROM size. */ if((size = ctlr->sromsz) == 0) size = 8; for(size = size-1; size >= 0; size--){ data = Rd|Ss|(((r>>size) & 0x01)<<2)|Scs; csr9w(ctlr, data); csr9w(ctlr, data|Sclk); csr9w(ctlr, data); microdelay(1); if(!(csr32r(ctlr, 9) & Sdo)) break; } data = 0; for(i = 16-1; i >= 0; i--){ csr9w(ctlr, Rd|Ss|Sclk|Scs); if(csr32r(ctlr, 9) & Sdo) data |= (1<<i); csr9w(ctlr, Rd|Ss|Scs); } csr9w(ctlr, 0); if(ctlr->sromsz == 0){ ctlr->sromsz = 8-size; goto reread; } return data & 0xFFFF; } static void softreset(Ctlr* ctlr) { /* * Soft-reset the controller and initialise bus mode. * Delay should be >= 50 PCI cycles (2×S @ 25MHz). */ csr32w(ctlr, 0, Swr); microdelay(10); csr32w(ctlr, 0, Rml|Cal16|Dbo); delay(1); } static int type5block(Ctlr* ctlr, uchar* block) { int csr15, i, len; /* * Reset or GPR sequence. Reset should be once only, * before the GPR sequence. * Note 'block' is not a pointer to the block head but * a pointer to the data in the block starting at the * reset length value so type5block can be used for the * sequences contained in type 1 and type 3 blocks. * The SROM docs state the 21140 type 5 block is the * same as that for the 21143, but the two controllers * use different registers and sequence-element lengths * so the 21140 code here is a guess for a real type 5 * sequence. */ len = *block++; if(ctlr->id != Tulip3){ for(i = 0; i < len; i++){ csr32w(ctlr, 12, *block); block++; } return len; } for(i = 0; i < len; i++){ csr15 = *block++<<16; csr15 |= *block++<<24; csr32w(ctlr, 15, csr15); debug("%8.8uX ", csr15); } return 2*len; } static int typephylink(Ctlr* ctlr, uchar*) { int an, bmcr, bmsr, csr6, x; /* * Fail if * auto-negotiataion enabled but not complete; * no valid link established. */ bmcr = miir(ctlr, ctlr->curphyad, Bmcr); miir(ctlr, ctlr->curphyad, Bmsr); bmsr = miir(ctlr, ctlr->curphyad, Bmsr); debug("bmcr 0x%2.2uX bmsr 0x%2.2uX\n", bmcr, bmsr); if(((bmcr & 0x1000) && !(bmsr & 0x0020)) || !(bmsr & 0x0004)) return 0; if(bmcr & 0x1000){ an = miir(ctlr, ctlr->curphyad, Anar); an &= miir(ctlr, ctlr->curphyad, Anlpar) & 0x3E0; debug("an 0x%2.uX 0x%2.2uX 0x%2.2uX\n", miir(ctlr, ctlr->curphyad, Anar), miir(ctlr, ctlr->curphyad, Anlpar), an); if(an & 0x0100) x = 0x4000; else if(an & 0x0080) x = 0x2000; else if(an & 0x0040) x = 0x1000; else if(an & 0x0020) x = 0x0800; else x = 0; } else if((bmcr & 0x2100) == 0x2100) x = 0x4000; else if(bmcr & 0x2000){ /* * If FD capable, force it if necessary. */ if((bmsr & 0x4000) && ctlr->fd){ miiw(ctlr, ctlr->curphyad, Bmcr, 0x2100); x = 0x4000; } else x = 0x2000; } else if(bmcr & 0x0100) x = 0x1000; else x = 0x0800; csr6 = Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE; if(ctlr->fdx & x) csr6 |= Fd; if(ctlr->ttm & x) csr6 |= Ttm; debug("csr6 0x%8.8uX 0x%8.8uX 0x%8.8luX\n", csr6, ctlr->csr6, csr32r(ctlr, 6)); if(csr6 != ctlr->csr6){ ctlr->csr6 = csr6; csr32w(ctlr, 6, csr6); } return 1; } static int typephymode(Ctlr* ctlr, uchar* block, int wait) { uchar *p; int len, mc, nway, phyx, timeo; if(DEBUG){ int i; len = (block[0] & ~0x80)+1; for(i = 0; i < len; i++) debug("%2.2uX ", block[i]); debug("\n"); } if(block[1] == 1) len = 1; else if(block[1] == 3) len = 2; else return -1; /* * Snarf the media capabilities, nway advertisment, * FDX and TTM bitmaps. */ p = &block[5+len*block[3]+len*block[4+len*block[3]]]; mc = *p++; mc |= *p++<<8; nway = *p++; nway |= *p++<<8; ctlr->fdx = *p++; ctlr->fdx |= *p++<<8; ctlr->ttm = *p++; ctlr->ttm |= *p<<8; debug("mc %4.4uX nway %4.4uX fdx %4.4uX ttm %4.4uX\n", mc, nway, ctlr->fdx, ctlr->ttm); USED(mc); phyx = block[2]; ctlr->curphyad = ctlr->phy[phyx]; ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE; //csr32w(ctlr, 6, ctlr->csr6); if(typephylink(ctlr, block)) return 0; if(!(ctlr->phyreset & (1<<phyx))){ debug("reset seq: len %d: ", block[3]); if(ctlr->type5block) type5block(ctlr, &ctlr->type5block[2]); else type5block(ctlr, &block[4+len*block[3]]); debug("\n"); ctlr->phyreset |= (1<<phyx); } /* * GPR sequence. */ debug("gpr seq: len %d: ", block[3]); type5block(ctlr, &block[3]); debug("\n"); ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE; //csr32w(ctlr, 6, ctlr->csr6); if(typephylink(ctlr, block)) return 0; /* * Turn off auto-negotiation, set the auto-negotiation * advertisment register then start the auto-negotiation * process again. */ miiw(ctlr, ctlr->curphyad, Bmcr, 0); miiw(ctlr, ctlr->curphyad, Anar, nway|1); miiw(ctlr, ctlr->curphyad, Bmcr, 0x1000); if(!wait) return 0; for(timeo = 0; timeo < 30; timeo++){ if(typephylink(ctlr, block)) return 0; delay(100); } return -1; } static int typesymmode(Ctlr *ctlr, uchar *block, int wait) { uint gpmode, gpdata, command; USED(wait); gpmode = block[3] | ((uint) block[4] << 8); gpdata = block[5] | ((uint) block[6] << 8); command = (block[7] | ((uint) block[8] << 8)) & 0x71; if (command & 0x8000) { print("ether2114x.c: FIXME: handle type 4 mode blocks where cmd.active_invalid != 0\n"); return -1; } csr32w(ctlr, 15, gpmode); csr32w(ctlr, 15, gpdata); ctlr->csr6 = (command & 0x71) << 18; csr32w(ctlr, 6, ctlr->csr6); return 0; } static int type2mode(Ctlr* ctlr, uchar* block, int) { uchar *p; int csr6, csr13, csr14, csr15, gpc, gpd; csr6 = Sc|Mbo|Ca|Sb|TrMODE; debug("type2mode: medium 0x%2.2uX\n", block[2]); /* * Don't attempt full-duplex * unless explicitly requested. */ if((block[2] & 0x3F) == 0x04){ /* 10BASE-TFD */ if(!ctlr->fd) return -1; csr6 |= Fd; } /* * Operating mode programming values from the datasheet * unless media specific data is explicitly given. */ p = &block[3]; if(block[2] & 0x40){ csr13 = (block[4]<<8)|block[3]; csr14 = (block[6]<<8)|block[5]; csr15 = (block[8]<<8)|block[7]; p += 6; } else switch(block[2] & 0x3F){ default: return -1; case 0x00: /* 10BASE-T */ csr13 = 0x00000001; csr14 = 0x00007F3F; csr15 = 0x00000008; break; case 0x01: /* 10BASE-2 */ csr13 = 0x00000009; csr14 = 0x00000705; csr15 = 0x00000006; break; case 0x02: /* 10BASE-5 (AUI) */ csr13 = 0x00000009; csr14 = 0x00000705; csr15 = 0x0000000E; break; case 0x04: /* 10BASE-TFD */ csr13 = 0x00000001; csr14 = 0x00007F3D; csr15 = 0x00000008; break; } gpc = *p++<<16; gpc |= *p++<<24; gpd = *p++<<16; gpd |= *p<<24; csr32w(ctlr, 13, 0); csr32w(ctlr, 14, csr14); csr32w(ctlr, 15, gpc|csr15); delay(10); csr32w(ctlr, 15, gpd|csr15); csr32w(ctlr, 13, csr13); ctlr->csr6 = csr6; csr32w(ctlr, 6, ctlr->csr6); debug("type2mode: csr13 %8.8uX csr14 %8.8uX csr15 %8.8uX\n", csr13, csr14, csr15); debug("type2mode: gpc %8.8uX gpd %8.8uX csr6 %8.8uX\n", gpc, gpd, csr6); return 0; } static int type0link(Ctlr* ctlr, uchar* block) { int m, polarity, sense; m = (block[3]<<8)|block[2]; sense = 1<<((m & 0x000E)>>1); if(m & 0x0080) polarity = sense; else polarity = 0; return (csr32r(ctlr, 12) & sense)^polarity; } static int type0mode(Ctlr* ctlr, uchar* block, int wait) { int csr6, m, timeo; csr6 = Sc|Mbo|Hbd|Ca|Sb|TrMODE; debug("type0: medium 0x%uX, fd %d: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n", ctlr->medium, ctlr->fd, block[0], block[1], block[2], block[3]); switch(block[0]){ default: break; case 0x04: /* 10BASE-TFD */ case 0x05: /* 100BASE-TXFD */ case 0x08: /* 100BASE-FXFD */ /* * Don't attempt full-duplex * unless explicitly requested. */ if(!ctlr->fd) return -1; csr6 |= Fd; break; } m = (block[3]<<8)|block[2]; if(m & 0x0001) csr6 |= Ps; if(m & 0x0010) csr6 |= Ttm; if(m & 0x0020) csr6 |= Pcs; if(m & 0x0040) csr6 |= Scr; csr32w(ctlr, 12, block[1]); microdelay(10); csr32w(ctlr, 6, csr6); ctlr->csr6 = csr6; if(!wait) return 0; for(timeo = 0; timeo < 30; timeo++){ if(type0link(ctlr, block)) return 0; delay(100); } return -1; } static int mediaxx(Ether* ether, int wait) { Ctlr* ctlr; uchar *block; ctlr = ether->ctlr; block = ctlr->infoblock[ctlr->curk]; if(block[0] & 0x80){ switch(block[1]){ default: return -1; case 0: if(ctlr->medium >= 0 && block[2] != ctlr->medium) return 0; /* need this test? */ if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[2]) return 0; if(type0mode(ctlr, block+2, wait)) return 0; break; case 1: if(typephymode(ctlr, block, wait)) return 0; break; case 2: debug("type2: medium %d block[2] %d\n", ctlr->medium, block[2]); if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium)) return 0; if(type2mode(ctlr, block, wait)) return 0; break; case 3: if(typephymode(ctlr, block, wait)) return 0; break; case 4: debug("type4: medium %d block[2] %d\n", ctlr->medium, block[2]); if(ctlr->medium >= 0 && ((block[2] & 0x3F) != ctlr->medium)) return 0; if(typesymmode(ctlr, block, wait)) return 0; break; } } else{ if(ctlr->medium >= 0 && block[0] != ctlr->medium) return 0; /* need this test? */if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[0]) return 0; if(type0mode(ctlr, block, wait)) return 0; } if(ctlr->csr6){ if(!(ctlr->csr6 & Ps) || (ctlr->csr6 & Ttm)) return 10; return 100; } return 0; } static int media(Ether* ether, int wait) { Ctlr* ctlr; int k, mbps; ctlr = ether->ctlr; for(k = 0; k < ctlr->k; k++){ mbps = mediaxx(ether, wait); if(mbps > 0) return mbps; if(ctlr->curk == 0) ctlr->curk = ctlr->k-1; else ctlr->curk--; } return 0; } static char* mediatable[9] = { "10BASE-T", /* TP */ "10BASE-2", /* BNC */ "10BASE-5", /* AUI */ "100BASE-TX", "10BASE-TFD", "100BASE-TXFD", "100BASE-T4", "100BASE-FX", "100BASE-FXFD", }; static uchar en1207[] = { /* Accton EN1207-COMBO */ 0x00, 0x00, 0xE8, /* [0] vendor ethernet code */ 0x00, /* [3] spare */ 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */ 0x1F, /* [6] general purpose control */ 2, /* [7] block count */ 0x00, /* [8] media code (10BASE-TX) */ 0x0B, /* [9] general purpose port data */ 0x9E, 0x00, /* [10] command (LSB+MSB = 0x009E) */ 0x03, /* [8] media code (100BASE-TX) */ 0x1B, /* [9] general purpose port data */ 0x6D, 0x00, /* [10] command (LSB+MSB = 0x006D) */ /* There is 10BASE-2 as well, but... */ }; static uchar ana6910fx[] = { /* Adaptec (Cogent) ANA-6910FX */ 0x00, 0x00, 0x92, /* [0] vendor ethernet code */ 0x00, /* [3] spare */ 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */ 0x3F, /* [6] general purpose control */ 1, /* [7] block count */ 0x07, /* [8] media code (100BASE-FX) */ 0x03, /* [9] general purpose port data */ 0x2D, 0x00 /* [10] command (LSB+MSB = 0x000D) */ }; static uchar smc9332[] = { /* SMC 9332 */ 0x00, 0x00, 0xC0, /* [0] vendor ethernet code */ 0x00, /* [3] spare */ 0x00, 0x08, /* [4] connection (LSB+MSB = 0x0800) */ 0x1F, /* [6] general purpose control */ 2, /* [7] block count */ 0x00, /* [8] media code (10BASE-TX) */ 0x00, /* [9] general purpose port data */ 0x9E, 0x00, /* [10] command (LSB+MSB = 0x009E) */ 0x03, /* [8] media code (100BASE-TX) */ 0x09, /* [9] general purpose port data */ 0x6D, 0x00, /* [10] command (LSB+MSB = 0x006D) */ }; static uchar* leaf21140[] = { en1207, /* Accton EN1207-COMBO */ ana6910fx, /* Adaptec (Cogent) ANA-6910FX */ smc9332, /* SMC 9332 */ nil, }; /* * Copied to ctlr->srom at offset 20. */ static uchar leafpnic[] = { 0x00, 0x00, 0x00, 0x00, /* MAC address */ 0x00, 0x00, 0x00, /* controller 0 device number */ 0x1E, 0x00, /* controller 0 info leaf offset */ 0x00, /* reserved */ 0x00, 0x08, /* selected connection type */ 0x00, /* general purpose control */ 0x01, /* block count */ 0x8C, /* format indicator and count */ 0x01, /* block type */ 0x00, /* PHY number */ 0x00, /* GPR sequence length */ 0x00, /* reset sequence length */ 0x00, 0x78, /* media capabilities */ 0xE0, 0x01, /* Nway advertisment */ 0x00, 0x50, /* FDX bitmap */ 0x00, 0x18, /* TTM bitmap */ }; static int srom(Ctlr* ctlr) { int i, k, oui, phy, x; uchar *p; /* * This is a partial decoding of the SROM format described in * 'Digital Semiconductor 21X4 Serial ROM Format, Version 4.05, * 2-Mar-98'. Only the 2114[03] are handled, support for other * controllers can be added as needed. * Do a dummy read first to get the size and allocate ctlr->srom. */ sromr(ctlr, 0); if(ctlr->srom == nil) ctlr->srom = malloc((1<<ctlr->sromsz)*sizeof(ushort)); for(i = 0; i < (1<<ctlr->sromsz); i++){ x = sromr(ctlr, i); ctlr->srom[2*i] = x; ctlr->srom[2*i+1] = x>>8; } /* * There are 2 SROM layouts: * e.g. Digital EtherWORKS station address at offset 20; * this complies with the 21140A SROM * application note from Digital; * e.g. SMC9332 station address at offset 0 followed by * 2 additional bytes, repeated at offset * 6; the 8 bytes are also repeated in * reverse order at offset 8. * To check which it is, read the SROM and check for the repeating * patterns of the non-compliant cards; if that fails use the one at * offset 20. */ ctlr->sromea = ctlr->srom; for(i = 0; i < 8; i++){ x = ctlr->srom[i]; if(x != ctlr->srom[15-i] || x != ctlr->srom[16+i]){ ctlr->sromea = &ctlr->srom[20]; break; } } /* * Fake up the SROM for the PNIC. * It looks like a 21140 with a PHY. * The MAC address is byte-swapped in the orginal SROM data. */ if(ctlr->id == Pnic){ memmove(&ctlr->srom[20], leafpnic, sizeof(leafpnic)); for(i = 0; i < Eaddrlen; i += 2){ ctlr->srom[20+i] = ctlr->srom[i+1]; ctlr->srom[20+i+1] = ctlr->srom[i]; } } /* * Next, try to find the info leaf in the SROM for media detection. * If it's a non-conforming card try to match the vendor ethernet code * and point p at a fake info leaf with compact 21140 entries. */ if(ctlr->sromea == ctlr->srom){ p = nil; for(i = 0; leaf21140[i] != nil; i++){ if(memcmp(leaf21140[i], ctlr->sromea, 3) == 0){ p = &leaf21140[i][4]; break; } } if(p == nil) return -1; } else p = &ctlr->srom[(ctlr->srom[28]<<8)|ctlr->srom[27]]; /* * Set up the info needed for later media detection. * For the 21140, set the general-purpose mask in CSR12. * The info block entries are stored in order of increasing * precedence, so detection will work backwards through the * stored indexes into ctlr->srom. * If an entry is found which matches the selected connection * type, save the index. Otherwise, start at the last entry. * If any MII entries are found (type 1 and 3 blocks), scan * for PHYs. */ ctlr->leaf = p; ctlr->sct = *p++; ctlr->sct |= *p++<<8; if(ctlr->id != Tulip3){ csr32w(ctlr, 12, Gpc|*p++); delay(200); } ctlr->k = *p++; if(ctlr->k >= nelem(ctlr->infoblock)) ctlr->k = nelem(ctlr->infoblock)-1; ctlr->sctk = ctlr->k-1; phy = 0; for(k = 0; k < ctlr->k; k++){ ctlr->infoblock[k] = p; /* * The RAMIX PMC665 has a badly-coded SROM, * hence the test for 21143 and type 3. */ if((*p & 0x80) || (ctlr->id == Tulip3 && *(p+1) == 3)){ *p |= 0x80; if(*(p+1) == 1 || *(p+1) == 3) phy = 1; if(*(p+1) == 5) ctlr->type5block = p; p += (*p & ~0x80)+1; } else{ debug("type0: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n", p[0], p[1], p[2], p[3]); if(ctlr->sct != 0x0800 && *p == (ctlr->sct & 0xFF)) ctlr->sctk = k; p += 4; } } ctlr->curk = ctlr->sctk; debug("sct 0x%uX medium 0x%uX k %d curk %d phy %d\n", ctlr->sct, ctlr->medium, ctlr->k, ctlr->curk, phy); if(phy){ x = 0; for(k = 0; k < nelem(ctlr->phy); k++){ if((oui = miir(ctlr, k, 2)) == -1 || oui == 0) continue; if(DEBUG){ oui = (oui & 0x3FF)<<6; oui |= miir(ctlr, k, 3)>>10; miir(ctlr, k, 1); debug("phy%d: index %d oui %uX reg1 %uX\n", x, k, oui, miir(ctlr, k, 1)); USED(oui); } ctlr->phy[x] = k; } } ctlr->fd = 0; ctlr->medium = -1; return 0; } static void dec2114xpci(void) { Ctlr *ctlr; Pcidev *p; int x; p = nil; while(p = pcimatch(p, 0, 0)){ if(p->ccrb != 0x02 || p->ccru != 0) continue; switch((p->did<<16)|p->vid){ default: continue; case Tulip3: /* 21143 */ /* * Exit sleep mode. */ x = pcicfgr32(p, 0x40); x &= ~0xc0000000; pcicfgw32(p, 0x40, x); /*FALLTHROUGH*/ case Pnic: /* PNIC */ case Pnic2: /* PNIC-II */ case Tulip0: /* 21140 */ break; } /* * bar[0] is the I/O port register address and * bar[1] is the memory-mapped register address. */ ctlr = malloc(sizeof(Ctlr)); ctlr->port = p->mem[0].bar & ~0x01; ctlr->pcidev = p; ctlr->id = (p->did<<16)|p->vid; if(ioalloc(ctlr->port, p->mem[0].size, 0, "dec2114x") < 0){ print("dec2114x: port 0x%uX in use\n", ctlr->port); free(ctlr); continue; } /* * Some cards (e.g. ANA-6910FX) seem to need the Ps bit * set or they don't always work right after a hardware * reset. */ csr32w(ctlr, 6, Mbo|Ps); softreset(ctlr); if(srom(ctlr)){ iofree(ctlr->port); free(ctlr); continue; } switch(ctlr->id){ default: break; case Pnic: /* PNIC */ /* * Turn off the jabber timer. */ csr32w(ctlr, 15, 0x00000001); break; } if(ctlrhead != nil) ctlrtail->next = ctlr; else ctlrhead = ctlr; ctlrtail = ctlr; } } static int reset(Ether* ether) { Ctlr *ctlr; int i, x; uchar ea[Eaddrlen]; static int scandone; if(scandone == 0){ dec2114xpci(); scandone = 1; } /* * Any adapter matches if no ether->port is supplied, * otherwise the ports must match. */ for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ if(ctlr->active) continue; if(ether->port == 0 || ether->port == ctlr->port){ ctlr->active = 1; break; } } if(ctlr == nil) return -1; ether->ctlr = ctlr; ether->port = ctlr->port; // ether->irq = ctlr->pcidev->intl; ether->irq = 2; /* arrrrrgh */ ether->tbdf = ctlr->pcidev->tbdf; /* * Check if the adapter's station address is to be overridden. * If not, read it from the EEPROM and set in ether->ea prior to * loading the station address in the hardware. */ memset(ea, 0, Eaddrlen); if(memcmp(ea, ether->ea, Eaddrlen) == 0) memmove(ether->ea, ctlr->sromea, Eaddrlen); /* * Look for a medium override in case there's no autonegotiation * (no MII) or the autonegotiation fails. */ for(i = 0; i < ether->nopt; i++){ if(cistrcmp(ether->opt[i], "FD") == 0){ ctlr->fd = 1; continue; } for(x = 0; x < nelem(mediatable); x++){ debug("compare <%s> <%s>\n", mediatable[x], ether->opt[i]); if(cistrcmp(mediatable[x], ether->opt[i])) continue; ctlr->medium = x; switch(ctlr->medium){ default: ctlr->fd = 0; break; case 0x04: /* 10BASE-TFD */ case 0x05: /* 100BASE-TXFD */ case 0x08: /* 100BASE-FXFD */ ctlr->fd = 1; break; } break; } } ether->mbps = media(ether, 1); /* * Initialise descriptor rings, ethernet address. */ ctlr->nrdr = Nrde; ctlr->ntdr = Ntde; pcisetbme(ctlr->pcidev); ctlrinit(ether); /* * Linkage to the generic ethernet driver. */ ether->attach = attach; ether->transmit = transmit; ether->ifstat = ifstat; ether->arg = ether; ether->promiscuous = promiscuous; intrenable(ether->irq, interrupt, ether, ether->tbdf, ether->name); return 0; } void ether2114xlink(void) { addethercard("21140", reset); addethercard("2114x", reset); }