ref: 6bb619c8db2867ddd9cd19c0aec05065f5ee0cae
dir: /os/cerf405/etheremac.c/
/* * ethernet */ #include "u.h" #include "lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/error.h" #include "../port/netif.h" #include "ethermii.h" #include "etherif.h" #include "ureg.h" /* * TO DO: * - test EMAC1 */ #define DBG if(0)iprint #define MIIDBG if(0)iprint enum { Nrdre = 64, /* receive descriptor ring entries */ Ntdre = 32, /* transmit descriptor ring entries */ Nrxchan = 2, Ntxchan = 2, /* there are actually 4 but we only use 2 now */ Rbsize = ETHERMAXTU, /* ring buffer size */ Bufsize = (Rbsize+CACHELINESZ-1)&~(CACHELINESZ-1), /* aligned */ }; enum { /* emac-specific Rx BD bits */ RxOverrun= 1<<9, /* not enough empty space in FIFO */ RxPause= 1<<8, /* control pause packet */ RxBad= 1<<7, /* packet error */ RxRunt= 1<<6, RxShort= 1<<5, RxAlign= 1<<4, RxFCS= 1<<3, RxLong= 1<<2, RxRange= 1<<1, /* out of range error */ RxInRange= 1<<0, /* in range error */ RxError= (0x3FF & ~RxPause), /* error flags */ /* emac-specific Tx BD bits */ /* write access */ TxFCS= 1<<9, /* generate FCS */ TxPad= 1<<8, /* pad short frames */ TxInsSA= 1<<7, /* insert source address */ TxRepSA= 1<<6, /* replace source address */ TxInsVLAN= 1<<5, /* insert VLAN tag */ TxRepVLAN= 1<<4, /* replace VLAN tag */ /* read access (status) */ TxBadFCS= 1<<9, TxBadPrev= 1<<8, /* bad previous packet in dependent mode */ TxLostCarrier= 1<<7, TxEDef= 1<<6, /* excessive deferral */ TxECol= 1<<5, /* excessive collisions */ TxLateCol= 1<<4, /* late collision (half-duplex only) */ TxManyCol= 1<<3, /* more than 1 but less than 16 collisions */ TxCollision= 1<<2, /* single collision */ TxUnderrun= 1<<1, /* didn't fill FIFO in time */ TxSQE= 1<<0, /* signal quality test failed (10mbit half-duplex only) */ TxError= 0x3FF, /* error flags */ }; typedef struct Emac Emac; struct Emac { ulong mr0; /* mode register 0 [see 19-48] */ ulong mr1; /* mode register 1 [Reset] */ ulong tmr0; /* transmit mode register 0 [see 19-28] */ ulong tmr1; /* transmit mode register 1 [see 19-28] */ ulong rmr; /* receive mode register [Reset] */ ulong isr; /* interrupt status register [Always] */ ulong iser; /* interrupt status enable register [Reset] */ ulong iahr; /* individual address high [Reset, R, T]*/ ulong ialr; /* individual address low [Reset, R, T] */ ulong vtpid; /* VLAN Tag Protocol Identifier [Reset, R, T] */ ulong vtci; /* VLAN Tag Control Information [Reset, R, T] */ ulong ptr; /* pause timer [Reset, T] */ ulong iaht[4]; /* individual address hash table [Reset, R] */ ulong gaht[4]; /* group address hash table [Reset, R] */ ulong lsah; /* last source address high */ ulong lsal; /* last source address low */ ulong ipgvr; /* inter-packet gap value [Reset, T] */ ulong stacr; /* STA control register [see 19-41] */ ulong trtr; /* transmit request threshold register [see 19-42] */ ulong rwmr; /* receive low/high water mark [Reset] */ ulong octx; /* bytes transmitted */ ulong ocrx; /* bytes received */ }; enum { /* mode register 0 */ Mr0Rxi= 1<<31, /* receive MAC idle */ Mr0Txi= 1<<30, /* transmit MAC idle */ Mr0Srst= 1<<29, /* soft reset; soft reset in progress */ Mr0Txe= 1<<28, /* tx MAC enable */ Mr0Rxe= 1<<27, /* rx MAC enable */ Mr0Wke= 1<<26, /* enable wake-up packets */ /* mode register 1 */ Mr1Fde= 1<<31, /* full-duplex enable */ Mr1Ile= 1<<30, /* internal loop-back enable */ Mr1Vle= 1<<29, /* VLAN enable */ Mr1Eifc= 1<<28, /* enable integrated flow control */ Mr1App= 1<<27, /* allow pause packets */ Mr1Ist= 1<<24, /* ignore sqe test (all but half-duplex 10m/bit) */ Mr1Mf10= 0<<22, /* medium [MII] frequency is 10 mbps */ Mr1Mf100= 1<<22, /* medium frequency is 100 mbps */ Mr1Rfs512= 0<<20, /* RX FIFO size (512 bytes) */ Mr1Rfs1024= 1<<20, Mr1Rfs2048= 2<<20, Mr1Rfs4096= 3<<20, Mr1Tfs1024= 1<<18, /* TX FIFO size (1024 bytes) */ Mr1Tfs2048= 2<<18, Mr1Tr0sp= 0<<15, /* transmit request 0: single packet */ Mr1Tr0mp= 1<<15, /* multiple packets */ Mr1Tr0dm= 2<<15, /* dependent mode */ Mr1Tr1sp= 0<<13, /* transmit request 1: single packet */ Mr1Tr1mp= 1<<13, /* multiple packets */ Mr1Tr1dm= 2<<13, /* dependent mode */ /* transmit mode register 0 */ Tmr0Gnp0= 1<<31, /* get new packet channel 0 */ Tmr0Gnp1= 1<<30, /* get new packet channel 1 */ Tmr0Gnpd= 1<<29, /* get new packet dependent mode */ Tmr0Fc= 1<<28, /* first channel (dependent mode) */ /* transmit mode register 1 */ Tmr1Trl_s= 27, /* transmit low request (shift) */ Tmr1Tur_s= 16, /* transmit urgent request (shift) */ /* receive mode register */ RmrSp= 1<<31, /* strip pad/FCS bytes */ RmrSfcs= 1<<30, /* strip FCS */ RmrRrp= 1<<29, /* receive runt packets */ RmrRfp= 1<<28, /* receive packets with FCS error */ RmrRop= 1<<27, /* receive oversize packets */ RmrRpir= 1<<26, /* receive packets with in range error */ RmrPpp= 1<<25, /* propagate pause packet */ RmrPme= 1<<24, /* promiscuous mode enable */ RmrPmme= 1<<23, /* promiscuous mode multicast enable */ RmrIae= 1<<22, /* individual address enable */ RmrMiae= 1<<21, /* multiple individual address enable */ RmrBae= 1<<20, /* broadcast address enable */ RmrMae= 1<<19, /* multicast address enable */ /* interrupt status register */ IsrOvr= 1<<25, /* overrun error */ IsrPp= 1<<24, /* pause packet */ IsrBp= 1<<23, /* bad packet */ IsrRp= 1<<22, /* runt packet */ IsrSe= 1<<21, /* short event */ IsrAle= 1<<20, /* alignment error */ IsrBfcs= 1<<19, /* bad FCS */ IsrPtle= 1<<18, /* packet too long error */ IsrOre= 1<<17, /* out of range error */ IsrIre= 1<<16, /* in range error */ IsrDbdm= 1<<9, /* dead bit dependent mode */ IsrDb0= 1<<8, /* dead bit 0 */ IsrSe0= 1<<7, /* sqe 0 */ IsrTe0= 1<<6, /* tx error 0 */ IsrDb1= 1<<5, /* dead bit 1 */ IsrSe1= 1<<4, /* sqe 1 */ IsrTe1= 1<<3, /* tx error 1 */ IsrMos= 1<<1, /* MMA operation succeeded */ IsrMof= 1<<0, /* MMA operation failed */ /* STA control register */ StaOc= 1<<15, /* operation complete */ StaPhye= 1<<14, /* PHY error */ StaRead= 1<<12, /* STA read */ StaWrite= 2<<12, /* STA write */ StaOpb50= 0<<10, /* OPB frequency */ StaOpb66= 1<<10, StaOpb83= 2<<10, StaOpb100= 3<<10, /* transmit request threshold */ TrtrTrt_s= 27, /* threshold (shift) -- and the value is (threshold/64)-1 */ /* receive low/high water mark register */ RwmrRlwm_s= 23, /* low water mark (shift) */ RwmrRhwm_s= 7, /* high water mark (shift) */ }; typedef struct { Lock; int port; int init; int active; Emac *regs; Emac *miiregs; Mal* rx; Mal* tx; Mii *mii; Ring; ulong interrupts; /* statistics */ ulong deferred; ulong heartbeat; ulong latecoll; ulong retrylim; ulong underrun; ulong overrun; ulong carrierlost; ulong retrycount; } Ctlr; static void dumpemac(Emac*); static void attach(Ether *ether) { Ctlr *ctlr; ctlr = ether->ctlr; ilock(ctlr); if(!ctlr->active){ malrxenable(ctlr->rx); maltxenable(ctlr->tx); eieio(); ctlr->regs->mr0 = Mr0Txe | Mr0Rxe; eieio(); ctlr->active = 1; } iunlock(ctlr); } static void closed(Ether *ether) { Ctlr *ctlr; ctlr = ether->ctlr; if(ctlr->active){ ilock(ctlr); iprint("ether closed\n"); ctlr->regs->mr0 &= ~(Mr0Txe | Mr0Rxe); /* reset enable bits */ /* TO DO: reset ring */ /* TO DO: could wait */ ctlr->active = 0; iunlock(ctlr); } } static void promiscuous(void* arg, int on) { Ether *ether; Ctlr *ctlr; ether = (Ether*)arg; ctlr = ether->ctlr; ilock(ctlr); if(on || ether->nmaddr) ctlr->regs->rmr |= RmrPme | RmrPmme; else ctlr->regs->rmr &= ~(RmrPme | RmrPmme); iunlock(ctlr); } static void multicast(void* arg, uchar *addr, int on) { Ether *ether; Ctlr *ctlr; USED(addr, on); /* if on, could SetGroupAddress; if !on, it's hard */ ether = (Ether*)arg; ctlr = ether->ctlr; ilock(ctlr); if(ether->prom || ether->nmaddr) ctlr->regs->rmr |= RmrPmme; else ctlr->regs->rmr &= ~RmrPmme; iunlock(ctlr); } static void txstart(Ether *ether) { int len; Ctlr *ctlr; Block *b; BD *dre; ctlr = ether->ctlr; while(ctlr->ntq < ctlr->ntdre-1){ b = qget(ether->oq); if(b == 0) break; dre = &ctlr->tdr[ctlr->tdrh]; if(dre->status & BDReady) panic("ether: txstart"); /* * Give ownership of the descriptor to the chip, increment the * software ring descriptor pointer and tell the chip to poll. */ len = BLEN(b); if(ctlr->txb[ctlr->tdrh] != nil) panic("etheremac: txstart"); ctlr->txb[ctlr->tdrh] = b; dre->addr = PADDR(b->rp); dre->length = len; dcflush(b->rp, len); eieio(); dre->status = (dre->status & BDWrap) | BDReady|BDInt|BDLast|TxFCS|TxPad; eieio(); ctlr->regs->tmr0 = Tmr0Gnp0; /* TO DO: several channels */ eieio(); ctlr->ntq++; ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdre); } } static void transmit(Ether* ether) { Ctlr *ctlr; ctlr = ether->ctlr; ilock(ctlr); txstart(ether); iunlock(ctlr); } /* * allocate receive buffer space on cache-line boundaries */ static Block* clallocb(void) { Block *b; b = iallocb(Bufsize+CACHELINESZ-1); if(b == nil) return b; dcflush(b->base, BALLOC(b)); b->wp = b->rp = (uchar*)(((ulong)b->base + CACHELINESZ - 1) & ~(CACHELINESZ-1)); return b; } static void rxring(Ureg*, void *arg) { Ether *ether; ulong status; Ctlr *ctlr; BD *dre; Block *b, *rb; ether = arg; ctlr = ether->ctlr; ctlr->interrupts++; /* * Receiver interrupt: run round the descriptor ring logging * errors and passing valid receive data up to the higher levels * until we encounter a descriptor still owned by the chip. * We rely on the descriptor accesses being uncached. */ dre = &ctlr->rdr[ctlr->rdrx]; while(((status = dre->status) & BDEmpty) == 0){ if(status & RxError || (status & (BDFirst|BDLast)) != (BDFirst|BDLast)){ if(status & (RxShort|RxLong)) ether->buffs++; if(status & (RxBad|RxAlign|RxRange|RxInRange)) ether->frames++; if(status & RxFCS) ether->crcs++; if(status & RxOverrun) ether->overflows++; iprint("eth rx: %lux\n", status); }else if((status & RxPause) == 0){ /* * We have a packet. Read it in. */ b = clallocb(); if(b != nil){ rb = ctlr->rxb[ctlr->rdrx]; rb->wp += dre->length; ctlr->rxb[ctlr->rdrx] = b; ctlr->rdr[ctlr->rdrx].addr = PADDR(b->wp); etheriq(ether, rb, 1); }else ether->soverflows++; } /* * Finished with this descriptor, reinitialise it, * give it back to the chip, then on to the next... */ dre->status = (status & BDWrap) | BDEmpty | BDInt; eieio(); ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdre); dre = &ctlr->rdr[ctlr->rdrx]; } } static void txring(Ureg*, void *arg) { Ether *ether; ulong status; Ctlr *ctlr; BD *dre; Block *b; ether = arg; ctlr = ether->ctlr; ctlr->interrupts++; /* * Transmitter interrupt: handle anything queued for a free descriptor. */ lock(ctlr); while(ctlr->ntq){ dre = &ctlr->tdr[ctlr->tdri]; status = dre->status; if(status & BDReady) break; if(status & TxEDef) ctlr->deferred++; if(status & TxLateCol) ctlr->latecoll++; if(status & TxECol) ctlr->retrylim++; if(status & TxUnderrun) ctlr->underrun++; if(status & (TxManyCol|TxCollision)) ctlr->retrycount++; b = ctlr->txb[ctlr->tdri]; if(b == nil) panic("etheremac: bufp"); ctlr->txb[ctlr->tdri] = nil; freeb(b); ctlr->ntq--; ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdre); } txstart(ether); unlock(ctlr); } static void interrupt(Ureg*, void *arg) { Ether *ether; ulong events; Ctlr *ctlr; ether = arg; ctlr = ether->ctlr; events = ctlr->regs->isr; eieio(); ctlr->regs->isr = events; eieio(); ctlr->interrupts++; //iprint("eth: %8.8lux\n", events); if(!ctlr->active || events == 0) return; if(events & IsrOvr) ctlr->overrun++; if(events & (IsrTe0|IsrTe1)) ether->oerrs++; rxring(nil, arg); txring(nil, arg); ctlr->interrupts -= 2; /* TO DO: restart tx/rx on error */ } static long ifstat(Ether* ether, void* a, long n, ulong offset) { char *p; int len; Ctlr *ctlr; if(n == 0) return 0; ctlr = ether->ctlr; p = malloc(READSTR); len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts); len += snprint(p+len, READSTR-len, "carrierlost: %lud\n", ctlr->carrierlost); len += snprint(p+len, READSTR-len, "heartbeat: %lud\n", ctlr->heartbeat); len += snprint(p+len, READSTR-len, "retrylimit: %lud\n", ctlr->retrylim); len += snprint(p+len, READSTR-len, "retrycount: %lud\n", ctlr->retrycount); len += snprint(p+len, READSTR-len, "latecollisions: %lud\n", ctlr->latecoll); len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n", ctlr->overrun); len += snprint(p+len, READSTR-len, "txunderruns: %lud\n", ctlr->underrun); snprint(p+len, READSTR-len, "framesdeferred: %lud\n", ctlr->deferred); n = readstr(offset, a, n, p); free(p); return n; } static QLock miilock; /* the PHY are both on EMAC0's MII bus */ static int miird(Mii *mii, int pa, int ra) { Ctlr *ctlr; Emac *em; ulong r; int i; if(up) qlock(&miilock); ctlr = mii->ctlr; em = ctlr->miiregs; MIIDBG("r: %x.%x:", pa, ra); if((em->stacr & StaOc) == 0) iprint("mii-not oc\n"); em->stacr = StaRead | StaOpb66 | (pa<<5) | ra; for(i=0; i<100 && (em->stacr & StaOc) == 0; i++) microdelay(1); r = em->stacr; if(up) qunlock(&miilock); if((r & StaOc) == 0) iprint("mii'-not oc\n"); if(r & StaPhye) return -1; MIIDBG(" %8.8lux\n", r); return r >> 16; } static int miiwr(Mii *mii, int pa, int ra, int v) { Ctlr *ctlr; Emac *em; ulong r; int i; if(up) qlock(&miilock); ctlr = mii->ctlr; em = ctlr->miiregs; if((em->stacr & StaOc) == 0) iprint("miiw-not oc\n"); em->stacr = (v<<16) | StaWrite | StaOpb66 | (pa<<5) | ra; for(i=0; i<100 && (em->stacr & StaOc) == 0; i++) microdelay(1); r = em->stacr; if(up) qunlock(&miilock); if((r & StaOc) == 0) iprint("miiw'-not oc\n"); if(r & StaPhye) return -1; MIIDBG("w: %x.%x: %8.8lux\n", pa, ra, r); return 0; } static int emacmii(Ctlr *ctlr) { MiiPhy *phy; int i; MIIDBG("mii\n"); if((ctlr->mii = malloc(sizeof(Mii))) == nil) return -1; ctlr->mii->ctlr = ctlr; ctlr->mii->mir = miird; ctlr->mii->miw = miiwr; if(mii(ctlr->mii, 1<<(ctlr->port+1)) == 0 || (phy = ctlr->mii->curphy) == nil){ free(ctlr->mii); ctlr->mii = nil; return -1; } iprint("oui %X phyno %d\n", phy->oui, phy->phyno); if(miistatus(ctlr->mii) < 0){ miireset(ctlr->mii); MIIDBG("miireset\n"); if(miiane(ctlr->mii, ~0, 0, ~0) < 0){ iprint("miiane failed\n"); return -1; } MIIDBG("miistatus...\n"); miistatus(ctlr->mii); if(miird(ctlr->mii, phy->phyno, Bmsr) & BmsrLs){ for(i=0;; i++){ if(i > 600){ iprint("emac%d: autonegotiation failed\n", ctlr->port); break; } if(miird(ctlr->mii, phy->phyno, Bmsr) & BmsrAnc) break; delay(10); } if(miistatus(ctlr->mii) < 0) iprint("miistatus failed\n"); }else{ iprint("emac%d: no link\n", ctlr->port); phy->speed = 10; /* simple default */ } } iprint("emac%d mii: fd=%d speed=%d tfc=%d rfc=%d\n", ctlr->port, phy->fd, phy->speed, phy->tfc, phy->rfc); MIIDBG("mii done\n"); return 0; } static void emacsetup(Ctlr *ctlr, Ether *ether) { int i; Emac *em; ulong mode; MiiPhy *phy; /* apparently don't need to set any Alt1 in GPIO */ em = ctlr->regs; /* errata emac_8 */ if(em->mr0 & Mr0Rxe){ /* probably never happens in our config */ em->mr0 &= ~Mr0Rxe; eieio(); for(i=0; (em->mr0 & Mr0Rxi) == 0; i++){ if(i > 100){ iprint("ethermac: Rxe->Rxi timed out\n"); break; /* we'll try soft reset anyway */ } microdelay(100); } } /* soft reset */ em->mr0 = Mr0Srst; eieio(); for(i=0; em->mr0 & Mr0Srst; i++){ if(i > 20){ iprint("ethermac: reset (PHY clocks not running?)"); i=0; } microdelay(100); } iprint("%d: rx=%8.8lux tx=%8.8lux\n", ctlr->port, PADDR(ctlr->rdr), PADDR(ctlr->tdr)); //if(ctlr->port)return; malrxinit(ctlr->rx, ctlr, Bufsize/16); maltxinit(ctlr->tx, ctlr); malrxreset(ctlr->rx); maltxreset(ctlr->tx); em->mr0 = 0; mode = Mr1Rfs4096 | Mr1Tfs2048 | Mr1Tr0mp; if(ctlr->mii != nil && (phy = ctlr->mii->curphy) != nil){ if(phy->speed == 10){ mode |= Mr1Mf10; if(phy->fd) mode |= Mr1Ist; }else mode |= Mr1Mf100 | Mr1Ist; if(phy->fd) mode |= Mr1Fde; /* errata emac_9 suggests not using integrated flow control (it's broken); so don't negotiate it */ if(0 && (phy->rfc || phy->tfc)) mode |= Mr1App | Mr1Eifc; ether->mbps = phy->speed; ether->fullduplex = phy->fd; }else{ iprint("mii: didn't work: default 100FD\n"); mode |= Mr1Mf100 | Mr1Ist | Mr1Fde; ether->mbps = 100; ether->fullduplex = 1; } em->mr1 = mode; em->tmr1 = (9<<Tmr1Trl_s) | (256<<Tmr1Tur_s); /* TO DO: validate these sizes */ em->rmr = RmrSp | RmrSfcs | RmrIae | RmrBae; em->iahr = (ether->ea[0]<<8) | ether->ea[1]; em->ialr = (ether->ea[2]<<24) | (ether->ea[3]<<16) | (ether->ea[4]<<8) | ether->ea[5]; em->vtpid = 0; em->vtci = 0; em->ptr = 1; /* pause timer [Reset, T] */ for(i=0; i<4; i++){ em->iaht[i] = 0; /* individual address hash table */ em->gaht[i] = 0; /* group address hash table */ } em->ipgvr = (96/8)/3; /* minimise bit times between packets */ em->trtr = ((256/64)-1)<<TrtrTrt_s; /* transmission threshold (probably could be smaller) */ em->rwmr = (32<<RwmrRlwm_s) | (128<<RwmrRhwm_s); /* receive low/high water mark (TO DO: check) */ /* 0x0f002000? */ //dumpemac(em); //dumpmal(); eieio(); em->isr = em->isr; /* clear all events */ eieio(); em->iser = IsrOvr | IsrBp | IsrSe | IsrSe0 | IsrTe0 | IsrSe1 | IsrTe1; /* enable various error interrupts */ /* packet tx/rx interrupts come from MAL */ eieio(); /* tx/rx enable is deferred until attach */ } static int reset(Ether* ether) { uchar ea[Eaddrlen]; Ctlr *ctlr; int i; ioringreserve(Nrxchan, Nrdre, Ntxchan, Ntdre); /* * Insist that the platform-specific code provide the Ethernet address */ memset(ea, 0, Eaddrlen); if(memcmp(ea, ether->ea, Eaddrlen) == 0){ print("no ether address"); return -1; } ctlr = malloc(sizeof(*ctlr)); ctlr->port = ether->port; switch(ether->port){ case 0: ctlr->regs = KADDR(PHYSEMAC0); ctlr->miiregs = ctlr->regs; ctlr->rx = malchannel(0, 0, rxring, ether); ctlr->tx = malchannel(0, 1, txring, ether); ether->irq = VectorEMAC0; break; case 1: ctlr->regs = KADDR(PHYSEMAC1); ctlr->miiregs = KADDR(PHYSEMAC0); /* p. 19-41: ``only the MDIO interface for EMAC0 is pinned out'' */ ctlr->rx = malchannel(1, 0, rxring, ether); ctlr->tx = malchannel(2, 1, txring, ether); ether->irq = VectorEMAC1; break; default: print("%s ether: no port %lud\n", ether->type, ether->port); free(ctlr); return -1; } if(emacmii(ctlr) < 0){ free(ctlr); return -1; } ether->ctlr = ctlr; if(ioringinit(ctlr, Nrdre, Ntdre) < 0) /* TO DO: there are two transmit rings*/ panic("etheremac initring"); for(i = 0; i < ctlr->nrdre; i++){ ctlr->rxb[i] = clallocb(); ctlr->rdr[i].addr = PADDR(ctlr->rxb[i]->wp); } emacsetup(ctlr, ether); ether->attach = attach; ether->closed = closed; ether->transmit = transmit; ether->interrupt = interrupt; /* oddly, it's only error interrupts; see malchannel call above for tx/rx */ ether->ifstat = ifstat; ether->arg = ether; ether->promiscuous = promiscuous; ether->multicast = multicast; return 0; } void etheremaclink(void) { addethercard("EMAC", reset); } static void dumpemac(Emac *r) { iprint("mr0=%8.8lux\n", r->mr0); /* mode register 0 [see 19-48] */ iprint("mr1=%8.8lux\n", r->mr1); /* mode register 1 [Reset] */ iprint("tmr0=%8.8lux\n", r->tmr0); /* transmit mode register 0 [see 19-28] */ iprint("tmr1=%8.8lux\n", r->tmr1); /* transmit mode register 1 [see 19-28] */ iprint("rmr=%8.8lux\n", r->rmr); /* receive mode register [Reset] */ iprint("isr=%8.8lux\n", r->isr); /* interrupt status register [Always] */ iprint("iser=%8.8lux\n", r->iser); /* interrupt status enable register [Reset] */ iprint("iahr=%8.8lux\n", r->iahr); /* individual address high [Reset, R, T]*/ iprint("ialr=%8.8lux\n", r->ialr); /* individual address low [Reset, R, T] */ iprint("vtpid=%8.8lux\n", r->vtpid); /* VLAN Tag Protocol Identifier [Reset, R, T] */ iprint("vtci=%8.8lux\n", r->vtci); /* VLAN Tag Control Information [Reset, R, T] */ iprint("ptr=%8.8lux\n", r->ptr); /* pause timer [Reset, T] */ iprint("lsah=%8.8lux\n", r->lsah); /* last source address high */ iprint("lsal=%8.8lux\n", r->lsal); /* last source address low */ iprint("ipgvr=%8.8lux\n", r->ipgvr); /* inter-packet gap value [Reset, T] */ iprint("stacr=%8.8lux\n", r->stacr); /* STA control register [see 19-41] */ iprint("trtr=%8.8lux\n", r->trtr); /* transmit request threshold register [see 19-42] */ iprint("rwmr=%8.8lux\n", r->rwmr); /* receive low/high water mark [Reset] */ iprint("octx=%8.8lux\n", r->octx); /* bytes transmitted */ iprint("ocrx=%8.8lux\n", r->ocrx); /* bytes received */ }