diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/ppc/etherfcc.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/ppc/etherfcc.c')
-rwxr-xr-x | sys/src/9/ppc/etherfcc.c | 891 |
1 files changed, 891 insertions, 0 deletions
diff --git a/sys/src/9/ppc/etherfcc.c b/sys/src/9/ppc/etherfcc.c new file mode 100755 index 000000000..0b2e1f21e --- /dev/null +++ b/sys/src/9/ppc/etherfcc.c @@ -0,0 +1,891 @@ +/* + * FCCn ethernet + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "imm.h" +#include "../port/error.h" +#include "../port/netif.h" + +#include "etherif.h" +#include "../ppc/ethermii.h" + +#define DBG 1 + +enum { + Nrdre = 128, /* receive descriptor ring entries */ + Ntdre = 128, /* transmit descriptor ring entries */ + + Rbsize = ETHERMAXTU+4, /* ring buffer size (+4 for CRC) */ + Bufsize = Rbsize+CACHELINESZ, /* extra room for alignment */ +}; + +enum { + + /* ether-specific Rx BD bits */ + RxMiss= SBIT(7), + RxeLG= SBIT(10), + RxeNO= SBIT(11), + RxeSH= SBIT(12), + RxeCR= SBIT(13), + RxeOV= SBIT(14), + RxeCL= SBIT(15), + RxError= (RxeLG|RxeNO|RxeSH|RxeCR|RxeOV|RxeCL), /* various error flags */ + + /* ether-specific Tx BD bits */ + TxPad= SBIT(1), /* pad short frames */ + TxTC= SBIT(5), /* transmit CRC */ + TxeDEF= SBIT(6), + TxeHB= SBIT(7), + TxeLC= SBIT(8), + TxeRL= SBIT(9), + TxeUN= SBIT(14), + TxeCSL= SBIT(15), + + /* psmr */ + CRCE= BIT(24), /* Ethernet CRC */ + FCE= BIT(10), /* flow control */ + PRO= BIT(9), /* promiscuous mode */ + FDE= BIT(5), /* full duplex ethernet */ + LPB= BIT(3), /* local protect bit */ + + /* gfmr */ + ENET= 0xc, /* ethernet mode */ + ENT= BIT(27), + ENR= BIT(26), + TCI= BIT(2), + + /* FCC function code register */ + GBL= 0x20, + BO= 0x18, + EB= 0x10, /* Motorola byte order */ + TC2= 0x04, + DTB= 0x02, + BDB= 0x01, + + /* FCC Event/Mask bits */ + GRA= SBIT(8), + RXC= SBIT(9), + TXC= SBIT(10), + TXE= SBIT(11), + RXF= SBIT(12), + BSY= SBIT(13), + TXB= SBIT(14), + RXB= SBIT(15), +}; + +enum { /* Mcr */ + MDIread = 0x60020000, /* read opcode */ + MDIwrite = 0x50020000, /* write opcode */ +}; + +typedef struct Etherparam Etherparam; +struct Etherparam { +/*0x00*/ FCCparam; +/*0x3c*/ ulong stat_buf; +/*0x40*/ ulong cam_ptr; +/*0x44*/ ulong cmask; +/*0x48*/ ulong cpres; +/*0x4c*/ ulong crcec; +/*0x50*/ ulong alec; +/*0x54*/ ulong disfc; +/*0x58*/ ushort retlim; +/*0x5a*/ ushort retcnt; +/*0x5c*/ ushort p_per; +/*0x5e*/ ushort boff_cnt; +/*0x60*/ ulong gaddr[2]; +/*0x68*/ ushort tfcstat; +/*0x6a*/ ushort tfclen; +/*0x6c*/ ulong tfcptr; +/*0x70*/ ushort mflr; +/*0x72*/ ushort paddr[3]; +/*0x78*/ ushort ibd_cnt; +/*0x7a*/ ushort ibd_start; +/*0x7c*/ ushort ibd_end; +/*0x7e*/ ushort tx_len; +/*0x80*/ uchar ibd_base[32]; +/*0xa0*/ ulong iaddr[2]; +/*0xa8*/ ushort minflr; +/*0xaa*/ ushort taddr[3]; +/*0xb0*/ ushort padptr; +/*0xb2*/ ushort Rsvdb2; +/*0xb4*/ ushort cf_range; +/*0xb6*/ ushort max_b; +/*0xb8*/ ushort maxd1; +/*0xba*/ ushort maxd2; +/*0xbc*/ ushort maxd; +/*0xbe*/ ushort dma_cnt; +/*0xc0*/ ulong octc; +/*0xc4*/ ulong colc; +/*0xc8*/ ulong broc; +/*0xcc*/ ulong mulc; +/*0xd0*/ ulong uspc; +/*0xd4*/ ulong frgc; +/*0xd8*/ ulong ospc; +/*0xdc*/ ulong jbrc; +/*0xe0*/ ulong p64c; +/*0xe4*/ ulong p65c; +/*0xe8*/ ulong p128c; +/*0xec*/ ulong p256c; +/*0xf0*/ ulong p512c; +/*0xf4*/ ulong p1024c; +/*0xf8*/ ulong cam_buf; +/*0xfc*/ ulong Rsvdfc; +/*0x100*/ +}; + +typedef struct Ctlr Ctlr; +struct Ctlr { + Lock; + int fccid; + int port; + ulong pmdio; + ulong pmdck; + int init; + int active; + int duplex; /* 1 == full */ + FCC* fcc; + + Ring; + Block* rcvbufs[Nrdre]; + Mii* mii; + Timer; + + ulong interrupts; /* statistics */ + ulong deferred; + ulong heartbeat; + ulong latecoll; + ulong retrylim; + ulong underrun; + ulong overrun; + ulong carrierlost; + ulong retrycount; +}; + +static int fccirq[] = {0x20, 0x21, 0x22}; +static int fccid[] = {FCC1ID, FCC2ID, FCC3ID}; + +#ifdef DBG +ulong fccrhisto[16]; +ulong fccthisto[16]; +ulong fccrthisto[16]; +ulong fcctrhisto[16]; +ulong ehisto[0x80]; +#endif + +static int fccmiimir(Mii*, int, int); +static int fccmiimiw(Mii*, int, int, int); +static void fccltimer(Ureg*, Timer*); + +static void +attach(Ether *ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(ctlr); + ctlr->active = 1; + ctlr->fcc->gfmr |= ENR|ENT; + iunlock(ctlr); + ctlr->tmode = Tperiodic; + ctlr->tf = fccltimer; + ctlr->ta = ether; + ctlr->tns = 5000000000LL; /* 5 seconds */ + timeradd(ctlr); +} + +static void +closed(Ether *ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(ctlr); + ctlr->active = 0; + ctlr->fcc->gfmr &= ~(ENR|ENT); + iunlock(ctlr); + print("Ether closed\n"); +} + +static void +promiscuous(void* arg, int on) +{ + Ether *ether; + Ctlr *ctlr; + + ether = (Ether*)arg; + ctlr = ether->ctlr; + + ilock(ctlr); + if(on || ether->nmaddr) + ctlr->fcc->fpsmr |= PRO; + else + ctlr->fcc->fpsmr &= ~PRO; + 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->fcc->fpsmr |= PRO; + else + ctlr->fcc->fpsmr &= ~PRO; + iunlock(ctlr); +} + +static void +txstart(Ether *ether) +{ + int len; + Ctlr *ctlr; + Block *b; + BD *dre; + + ctlr = ether->ctlr; + if(ctlr->init) + return; + while(ctlr->ntq < Ntdre-1){ + b = qget(ether->oq); + if(b == 0) + break; + + dre = &ctlr->tdr[ctlr->tdrh]; + dczap(dre, sizeof(BD)); + 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("fcc/ether: txstart"); + ctlr->txb[ctlr->tdrh] = b; + if((ulong)b->rp&1) + panic("fcc/ether: txstart align"); /* TO DO: ensure alignment */ + dre->addr = PADDR(b->rp); + dre->length = len; + dcflush(b->rp, len); + dcflush(dre, sizeof(BD)); + dre->status = (dre->status & BDWrap) | BDReady|TxPad|BDInt|BDLast|TxTC; + dcflush(dre, sizeof(BD)); +/* ctlr->fcc->ftodr = 1<<15; /* transmit now; Don't do this according to errata */ + ctlr->ntq++; + ctlr->tdrh = NEXT(ctlr->tdrh, Ntdre); + } +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(ctlr); + txstart(ether); + iunlock(ctlr); +} + +static void +interrupt(Ureg*, void *arg) +{ + int len, status, rcvd, xmtd, restart; + ushort events; + Ctlr *ctlr; + BD *dre; + Block *b, *nb; + Ether *ether = arg; + + ctlr = ether->ctlr; + if(!ctlr->active) + return; /* not ours */ + + /* + * Acknowledge all interrupts and whine about those that shouldn't + * happen. + */ + events = ctlr->fcc->fcce; + ctlr->fcc->fcce = events; /* clear events */ + +#ifdef DBG + ehisto[events & 0x7f]++; +#endif + + ctlr->interrupts++; + + if(events & BSY) + ctlr->overrun++; + if(events & TXE) + ether->oerrs++; + +#ifdef DBG + rcvd = xmtd = 0; +#endif + /* + * 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. + */ + if(events & RXF){ + dre = &ctlr->rdr[ctlr->rdrx]; + dczap(dre, sizeof(BD)); + while(((status = dre->status) & BDEmpty) == 0){ + rcvd++; + if(status & RxError || (status & (BDFirst|BDLast)) != (BDFirst|BDLast)){ + if(status & (RxeLG|RxeSH)) + ether->buffs++; + if(status & RxeNO) + ether->frames++; + if(status & RxeCR) + ether->crcs++; + if(status & RxeOV) + ether->overflows++; + print("eth rx: %ux\n", status); + }else{ + /* + * We have a packet. Read it in. + */ + len = dre->length-4; + b = ctlr->rcvbufs[ctlr->rdrx]; + assert(dre->addr == PADDR(b->rp)); + dczap(b->rp, len); + if(nb = iallocb(Bufsize)){ + b->wp += len; + etheriq(ether, b, 1); + b = nb; + b->rp = (uchar*)(((ulong)b->rp + CACHELINESZ-1) & ~(CACHELINESZ-1)); + b->wp = b->rp; + ctlr->rcvbufs[ctlr->rdrx] = b; + ctlr->rdr[ctlr->rdrx].addr = PADDR(b->wp); + }else + ether->soverflows++; + } + + /* + * Finished with this descriptor, reinitialise it, + * give it back to the chip, then on to the next... + */ + dre->length = 0; + dre->status = (status & BDWrap) | BDEmpty | BDInt; + dcflush(dre, sizeof(BD)); + + ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre); + dre = &ctlr->rdr[ctlr->rdrx]; + dczap(dre, sizeof(BD)); + } + } + + /* + * Transmitter interrupt: handle anything queued for a free descriptor. + */ + if(events & (TXB|TXE)){ + ilock(ctlr); + restart = 0; + while(ctlr->ntq){ + dre = &ctlr->tdr[ctlr->tdri]; + dczap(dre, sizeof(BD)); + status = dre->status; + if(status & BDReady) + break; + if(status & TxeDEF) + ctlr->deferred++; + if(status & TxeHB) + ctlr->heartbeat++; + if(status & TxeLC) + ctlr->latecoll++; + if(status & TxeRL) + ctlr->retrylim++; + if(status & TxeUN) + ctlr->underrun++; + if(status & TxeCSL) + ctlr->carrierlost++; + if(status & (TxeLC|TxeRL|TxeUN)) + restart = 1; + ctlr->retrycount += (status>>2)&0xF; + b = ctlr->txb[ctlr->tdri]; + if(b == nil) + panic("fcce/interrupt: bufp"); + ctlr->txb[ctlr->tdri] = nil; + freeb(b); + ctlr->ntq--; + ctlr->tdri = NEXT(ctlr->tdri, Ntdre); + xmtd++; + } + + if(restart){ + ctlr->fcc->gfmr &= ~ENT; + delay(10); + ctlr->fcc->gfmr |= ENT; + cpmop(RestartTx, ctlr->fccid, 0xc); + } + txstart(ether); + iunlock(ctlr); + } +#ifdef DBG + if(rcvd >= nelem(fccrhisto)) + rcvd = nelem(fccrhisto) - 1; + if(xmtd >= nelem(fccthisto)) + xmtd = nelem(fccthisto) - 1; + if(rcvd) + fcctrhisto[xmtd]++; + else + fccthisto[xmtd]++; + if(xmtd) + fccrthisto[rcvd]++; + else + fccrhisto[rcvd]++; +#endif +} + +static long +ifstat(Ether* ether, void* a, long n, ulong offset) +{ + char *p; + int len, i, r; + Ctlr *ctlr; + MiiPhy *phy; + + 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); + len += snprint(p+len, READSTR-len, "framesdeferred: %lud\n", ctlr->deferred); + miistatus(ctlr->mii); + phy = ctlr->mii->curphy; + len += snprint(p+len, READSTR-len, "phy: link=%d, tfc=%d, rfc=%d, speed=%d, fd=%d\n", + phy->link, phy->tfc, phy->rfc, phy->speed, phy->fd); + +#ifdef DBG + if(ctlr->mii != nil && ctlr->mii->curphy != nil){ + len += snprint(p+len, READSTR, "phy: "); + for(i = 0; i < NMiiPhyr; i++){ + if(i && ((i & 0x07) == 0)) + len += snprint(p+len, READSTR-len, "\n "); + r = miimir(ctlr->mii, i); + len += snprint(p+len, READSTR-len, " %4.4uX", r); + } + snprint(p+len, READSTR-len, "\n"); + } +#endif + snprint(p+len, READSTR-len, "\n"); + + n = readstr(offset, a, n, p); + free(p); + + return n; +} + +/* + * This follows the MPC8260 user guide: section28.9's initialisation sequence. + */ +static int +fccsetup(Ctlr *ctlr, FCC *fcc, uchar *ea) +{ + int i; + Etherparam *p; + MiiPhy *phy; + + /* Turn Ethernet off */ + fcc->gfmr &= ~(ENR | ENT); + + ioplock(); + switch(ctlr->port) { + default: + iopunlock(); + return -1; + case 0: + /* Step 1 (Section 28.9), write the parallel ports */ + ctlr->pmdio = 0x01000000; + ctlr->pmdck = 0x08000000; + imm->port[0].pdir &= ~A1dir0; + imm->port[0].pdir |= A1dir1; + imm->port[0].psor &= ~A1psor0; + imm->port[0].psor |= A1psor1; + imm->port[0].ppar |= (A1dir0 | A1dir1); + /* Step 2, Port C clocks */ + imm->port[2].psor &= ~0x00000c00; + imm->port[2].pdir &= ~0x00000c00; + imm->port[2].ppar |= 0x00000c00; + imm->port[3].pdat |= (ctlr->pmdio | ctlr->pmdck); + imm->port[3].podr |= ctlr->pmdio; + imm->port[3].pdir |= (ctlr->pmdio | ctlr->pmdck); + imm->port[3].ppar &= ~(ctlr->pmdio | ctlr->pmdck); + eieio(); + /* Step 3, Serial Interface clock routing */ + imm->cmxfcr &= ~0xff000000; /* Clock mask */ + imm->cmxfcr |= 0x37000000; /* Clock route */ + break; + + case 1: + /* Step 1 (Section 28.9), write the parallel ports */ + ctlr->pmdio = 0x00400000; + ctlr->pmdck = 0x00200000; + imm->port[1].pdir &= ~B2dir0; + imm->port[1].pdir |= B2dir1; + imm->port[1].psor &= ~B2psor0; + imm->port[1].psor |= B2psor1; + imm->port[1].ppar |= (B2dir0 | B2dir1); + /* Step 2, Port C clocks */ + imm->port[2].psor &= ~0x00003000; + imm->port[2].pdir &= ~0x00003000; + imm->port[2].ppar |= 0x00003000; + + imm->port[2].pdat |= (ctlr->pmdio | ctlr->pmdck); + imm->port[2].podr |= ctlr->pmdio; + imm->port[2].pdir |= (ctlr->pmdio | ctlr->pmdck); + imm->port[2].ppar &= ~(ctlr->pmdio | ctlr->pmdck); + eieio(); + /* Step 3, Serial Interface clock routing */ + imm->cmxfcr &= ~0x00ff0000; + imm->cmxfcr |= 0x00250000; + break; + + case 2: + /* Step 1 (Section 28.9), write the parallel ports */ + imm->port[1].pdir &= ~B3dir0; + imm->port[1].pdir |= B3dir1; + imm->port[1].psor &= ~B3psor0; + imm->port[1].psor |= B3psor1; + imm->port[1].ppar |= (B3dir0 | B3dir1); + /* Step 2, Port C clocks */ + imm->port[2].psor &= ~0x0000c000; + imm->port[2].pdir &= ~0x0000c000; + imm->port[2].ppar |= 0x0000c000; + imm->port[3].pdat |= (ctlr->pmdio | ctlr->pmdck); + imm->port[3].podr |= ctlr->pmdio; + imm->port[3].pdir |= (ctlr->pmdio | ctlr->pmdck); + imm->port[3].ppar &= ~(ctlr->pmdio | ctlr->pmdck); + eieio(); + /* Step 3, Serial Interface clock routing */ + imm->cmxfcr &= ~0x0000ff00; + imm->cmxfcr |= 0x00003700; + break; + } + iopunlock(); + + p = (Etherparam*)(m->immr->prmfcc + ctlr->port); + memset(p, 0, sizeof(Etherparam)); + + /* Step 4 */ + fcc->gfmr |= ENET; + + /* Step 5 */ + fcc->fpsmr = CRCE | FDE | LPB; /* full duplex operation */ + ctlr->duplex = ~0; + + /* Step 6 */ + fcc->fdsr = 0xd555; + + /* Step 7, initialize parameter ram */ + p->rbase = PADDR(ctlr->rdr); + p->tbase = PADDR(ctlr->tdr); + p->rstate = (GBL | EB) << 24; + p->tstate = (GBL | EB) << 24; + + p->cmask = 0xdebb20e3; + p->cpres = 0xffffffff; + + p->retlim = 15; /* retry limit */ + + p->mrblr = (Rbsize+0x1f)&~0x1f; /* multiple of 32 */ + p->mflr = Rbsize; + p->minflr = ETHERMINTU; + p->maxd1 = (Rbsize+7) & ~7; + p->maxd2 = (Rbsize+7) & ~7; + + for(i=0; i<Eaddrlen; i+=2) + p->paddr[2-i/2] = (ea[i+1]<<8)|ea[i]; + + /* Step 7, initialize parameter ram, configuration-dependent values */ + p->riptr = m->immr->fccextra[ctlr->port].ri - (uchar*)IMMR; + p->tiptr = m->immr->fccextra[ctlr->port].ti - (uchar*)IMMR; + p->padptr = m->immr->fccextra[ctlr->port].pad - (uchar*)IMMR; + memset(m->immr->fccextra[ctlr->port].pad, 0x88, 0x20); + + /* Step 8, clear out events */ + fcc->fcce = ~0; + + /* Step 9, Interrupt enable */ + fcc->fccm = TXE | RXF | TXB; + + /* Step 10, Configure interrupt priority (not done here) */ + /* Step 11, Clear out current events */ + /* Step 12, Enable interrupts to the CP interrupt controller */ + + /* Step 13, Issue the Init Tx and Rx command, specifying 0xc for ethernet*/ + cpmop(InitRxTx, fccid[ctlr->port], 0xc); + + /* Step 14, Link management */ + if((ctlr->mii = malloc(sizeof(Mii))) == nil) + return -1; + ctlr->mii->mir = fccmiimir; + ctlr->mii->miw = fccmiimiw; + ctlr->mii->ctlr = ctlr; + + if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){ + free(ctlr->mii); + ctlr->mii = nil; + return -1; + } + miiane(ctlr->mii, ~0, ~0, ~0); +#ifdef DBG + print("oui=%X, phyno=%d, ", phy->oui, phy->phyno); + print("anar=%ux, ", phy->anar); + print("fc=%ux, ", phy->fc); + print("mscr=%ux, ", phy->mscr); + + print("link=%ux, ", phy->link); + print("speed=%ux, ", phy->speed); + print("fd=%ux, ", phy->fd); + print("rfc=%ux, ", phy->rfc); + print("tfc=%ux\n", phy->tfc); +#endif + /* Step 15, Enable ethernet: done at attach time */ + return 0; +} + +static int +reset(Ether* ether) +{ + uchar ea[Eaddrlen]; + Ctlr *ctlr; + FCC *fcc; + Block *b; + int i; + + if(m->cpuhz < 24000000){ + print("%s ether: system speed must be >= 24MHz for ether use\n", ether->type); + return -1; + } + + if(ether->port > 3){ + print("%s ether: no FCC port %ld\n", ether->type, ether->port); + return -1; + } + ether->irq = fccirq[ether->port]; + ether->tbdf = BusPPC; + fcc = imm->fcc + ether->port; + + ctlr = malloc(sizeof(*ctlr)); + ether->ctlr = ctlr; + memset(ctlr, 0, sizeof(*ctlr)); + ctlr->fcc = fcc; + ctlr->port = ether->port; + ctlr->fccid = fccid[ether->port]; + + /* Ioringinit will allocate the buffer descriptors in normal memory + * and NOT in Dual-Ported Ram, as prescribed by the MPC8260 + * PowerQUICC II manual (Section 28.6). When they are allocated + * in DPram and the Dcache is enabled, the processor will hang + */ + if(ioringinit(ctlr, Nrdre, Ntdre, 0) < 0) + panic("etherfcc init"); + for(i = 0; i < Nrdre; i++){ + b = iallocb(Bufsize); + b->rp = (uchar*)(((ulong)b->rp + CACHELINESZ-1) & ~(CACHELINESZ-1)); + b->wp = b->rp; + ctlr->rcvbufs[i] = b; + ctlr->rdr[i].addr = PADDR(b->wp); + } + + fccsetup(ctlr, fcc, ether->ea); + + ether->mbps = 100; /* TO DO: could be 10mbps */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->ifstat = ifstat; + + ether->arg = ether; + ether->promiscuous = promiscuous; + ether->multicast = multicast; + + /* + * Until we know where to find it, insist that the plan9.ini + * entry holds the Ethernet address. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0){ + print("no ether address"); + return -1; + } + + return 0; +} + +void +etherfcclink(void) +{ + addethercard("fcc", reset); +} + +static void +nanodelay(void) +{ + static int count; + int i; + + for(i = 0; i < 500; i++) + count++; + return; +} + +static +void miiwriteloop(Ctlr *ctlr, Port *port, int cnt, ulong cmd) +{ + int i; + + for(i = 0; i < cnt; i++){ + port->pdat &= ~ctlr->pmdck; + if(cmd & BIT(i)) + port->pdat |= ctlr->pmdio; + else + port->pdat &= ~ctlr->pmdio; + nanodelay(); + port->pdat |= ctlr->pmdck; + nanodelay(); + } +} + +static int +fccmiimiw(Mii *mii, int pa, int ra, int data) +{ + int x; + Port *port; + ulong cmd; + Ctlr *ctlr; + + /* + * MII Management Interface Write. + */ + + ctlr = mii->ctlr; + port = imm->port + 3; + cmd = MDIwrite | (pa<<(5+2+16))| (ra<<(2+16)) | (data & 0xffff); + + x = splhi(); + + port->pdir |= (ctlr->pmdio|ctlr->pmdck); + nanodelay(); + + miiwriteloop(ctlr, port, 32, ~0); + miiwriteloop(ctlr, port, 32, cmd); + + port->pdir |= (ctlr->pmdio|ctlr->pmdck); + nanodelay(); + + miiwriteloop(ctlr, port, 32, ~0); + + splx(x); + return 1; +} + +static int +fccmiimir(Mii *mii, int pa, int ra) +{ + int data, i, x; + Port *port; + ulong cmd; + Ctlr *ctlr; + + ctlr = mii->ctlr; + port = imm->port + 3; + + cmd = MDIread | pa<<(5+2+16) | ra<<(2+16); + + x = splhi(); + port->pdir |= (ctlr->pmdio|ctlr->pmdck); + nanodelay(); + + miiwriteloop(ctlr, port, 32, ~0); + + /* Clock out the first 14 MS bits of the command */ + miiwriteloop(ctlr, port, 14, cmd); + + /* Turn-around */ + port->pdat &= ~ctlr->pmdck; + port->pdir &= ~ctlr->pmdio; + nanodelay(); + + /* For read, clock in 18 bits, use 16 */ + data = 0; + for(i=0; i<18; i++){ + data <<= 1; + if(port->pdat & ctlr->pmdio) + data |= 1; + port->pdat |= ctlr->pmdck; + nanodelay(); + port->pdat &= ~ctlr->pmdck; + nanodelay(); + } + port->pdir |= (ctlr->pmdio|ctlr->pmdck); + nanodelay(); + miiwriteloop(ctlr, port, 32, ~0); + splx(x); + return data & 0xffff; +} + +static void +fccltimer(Ureg*, Timer *t) +{ + Ether *ether; + Ctlr *ctlr; + MiiPhy *phy; + ulong gfmr; + + ether = t->ta; + ctlr = ether->ctlr; + if(ctlr->mii == nil || ctlr->mii->curphy == nil) + return; + phy = ctlr->mii->curphy; + if(miistatus(ctlr->mii) < 0){ + print("miistatus failed\n"); + return; + } + if(phy->link == 0){ + print("link lost\n"); + return; + } + ether->mbps = phy->speed; + + if(phy->fd != ctlr->duplex) + print("set duplex\n"); + ilock(ctlr); + gfmr = ctlr->fcc->gfmr; + if(phy->fd != ctlr->duplex){ + ctlr->fcc->gfmr &= ~(ENR|ENT); + if(phy->fd) + ctlr->fcc->fpsmr |= FDE | LPB; /* full duplex operation */ + else + ctlr->fcc->fpsmr &= ~(FDE | LPB); /* half duplex operation */ + ctlr->duplex = phy->fd; + } + ctlr->fcc->gfmr = gfmr; + iunlock(ctlr); +} |