summaryrefslogtreecommitdiff
path: root/os/cerf405/etheremac.c
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 21:39:35 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 21:39:35 +0000
commit74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a (patch)
treec6e220ba61db3a6ea4052e6841296d829654e664 /os/cerf405/etheremac.c
parent46439007cf417cbd9ac8049bb4122c890097a0fa (diff)
20060303
Diffstat (limited to 'os/cerf405/etheremac.c')
-rw-r--r--os/cerf405/etheremac.c829
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 */
+}