diff options
Diffstat (limited to 'os/cerf405/etheremac.c')
| -rw-r--r-- | os/cerf405/etheremac.c | 829 |
1 files changed, 829 insertions, 0 deletions
diff --git a/os/cerf405/etheremac.c b/os/cerf405/etheremac.c new file mode 100644 index 00000000..484c4205 --- /dev/null +++ b/os/cerf405/etheremac.c @@ -0,0 +1,829 @@ +/* + * 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 */ +} |
