123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628 |
- /*
- * FCCn ethernet
- */
- #include "u.h"
- #include "../port/lib.h"
- #include "mem.h"
- #include "dat.h"
- #include "fns.h"
- #include "io.h"
- #include "m8260.h"
- #include "../port/error.h"
- #include "../port/netif.h"
- #include "etherif.h"
- enum {
- Nrdre = 128, /* receive descriptor ring entries */
- Ntdre = 128, /* transmit descriptor ring entries */
- Rbsize = ETHERMAXTU+4, /* ring buffer size (+4 for CRC) */
- Bufsize = (Rbsize+7)&~7, /* aligned */
- };
- 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 */
- PRO= BIT(9), /* promiscuous mode */
- LPB= BIT(3), /* local protect bit */
- FDE= BIT(5), /* full duplex ethernet */
- CRCE= BIT(24), /* Ethernet CRC */
- /* 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),
- };
- 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 {
- Lock;
- int fccid;
- int port;
- ulong pmdio;
- ulong pmdck;
- int init;
- int active;
- FCC* fcc;
- Ring;
- ulong interrupts; /* statistics */
- ulong deferred;
- ulong heartbeat;
- ulong latecoll;
- ulong retrylim;
- ulong underrun;
- ulong overrun;
- ulong carrierlost;
- ulong retrycount;
- } Ctlr;
- static int fccirq[] = {0x20, 0x21, 0x22};
- static int fccid[] = {FCC1ID, FCC2ID, FCC3ID};
- int ioringinit(Ring* r, int nrdre, int ntdre, int bufsize);
- static void
- attach(Ether *ether)
- {
- Ctlr *ctlr;
- ctlr = ether->ctlr;
- ctlr->active = 1;
- ctlr->fcc->gfmr |= ENR|ENT;
- eieio();
- }
- 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 */
- 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;
- ushort events;
- Ctlr *ctlr;
- BD *dre;
- Block *b;
- 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;
- eieio();
- ctlr->fcc->fcce = events; /* clear events */
- eieio();
- ctlr->interrupts++;
- if(events & RXB)
- ctlr->overrun++;
- if(events & TXE)
- ether->oerrs++;
- /*
- * 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|RXB)){
- dre = &ctlr->rdr[ctlr->rdrx];
- dczap(dre, sizeof(BD));
- while(((status = dre->status) & BDEmpty) == 0){
- 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;
- dczap(KADDR(dre->addr), len);
- if((b = iallocb(len)) != 0){
- memmove(b->wp, KADDR(dre->addr), len);
- b->wp += len;
- etheriq(ether, b, 1);
- }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)){
- lock(ctlr);
- 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++;
- 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);
- }
- if(events & TXE)
- cpmop(RestartTx, ctlr->fccid, 0xc);
- txstart(ether);
- unlock(ctlr);
- }
- }
- 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;
- }
- /*
- * This follows the MPC8260 user guide: section28.9's initialisation sequence.
- */
- static void
- fccsetup(Ctlr *ctlr, FCC *fcc, uchar *ea)
- {
- int i;
- Etherparam *p;
- /* Turn Ethernet off */
- fcc->gfmr &= ~(ENR | ENT);
- ioplock();
- switch(ctlr->port) {
- default:
- iopunlock();
- return;
- case 0:
- /* Step 1 (Section 28.9), write the parallel ports */
- ctlr->pmdio = 0x01000000;
- ctlr->pmdck = 0x08000000;
- iomem->port[0].pdir &= ~A1dir0;
- iomem->port[0].pdir |= A1dir1;
- iomem->port[0].psor &= ~A1psor0;
- iomem->port[0].psor |= A1psor1;
- iomem->port[0].ppar |= (A1dir0 | A1dir1);
- /* Step 2, Port C clocks */
- iomem->port[2].psor &= ~0x00000c00;
- iomem->port[2].pdir &= ~0x00000c00;
- iomem->port[2].ppar |= 0x00000c00;
- iomem->port[3].pdat |= (ctlr->pmdio | ctlr->pmdck);
- iomem->port[3].podr |= ctlr->pmdio;
- iomem->port[3].pdir |= (ctlr->pmdio | ctlr->pmdck);
- iomem->port[3].ppar &= ~(ctlr->pmdio | ctlr->pmdck);
- eieio();
- /* Step 3, Serial Interface clock routing */
- iomem->cmxfcr &= ~0xff000000; /* Clock mask */
- iomem->cmxfcr |= 0x37000000; /* Clock route */
- break;
- case 1:
- /* Step 1 (Section 28.9), write the parallel ports */
- ctlr->pmdio = 0x00400000;
- ctlr->pmdck = 0x00200000;
- iomem->port[1].pdir &= ~B2dir0;
- iomem->port[1].pdir |= B2dir1;
- iomem->port[1].psor &= ~B2psor0;
- iomem->port[1].psor |= B2psor1;
- iomem->port[1].ppar |= (B2dir0 | B2dir1);
- /* Step 2, Port C clocks */
- iomem->port[2].psor &= ~0x00003000;
- iomem->port[2].pdir &= ~0x00003000;
- iomem->port[2].ppar |= 0x00003000;
- iomem->port[2].pdat |= (ctlr->pmdio | ctlr->pmdck);
- iomem->port[2].podr |= ctlr->pmdio;
- iomem->port[2].pdir |= (ctlr->pmdio | ctlr->pmdck);
- iomem->port[2].ppar &= ~(ctlr->pmdio | ctlr->pmdck);
- eieio();
- /* Step 3, Serial Interface clock routing */
- iomem->cmxfcr &= ~0x00ff0000;
- iomem->cmxfcr |= 0x00250000;
- break;
- case 2:
- /* Step 1 (Section 28.9), write the parallel ports */
- iomem->port[1].pdir &= ~B3dir0;
- iomem->port[1].pdir |= B3dir1;
- iomem->port[1].psor &= ~B3psor0;
- iomem->port[1].psor |= B3psor1;
- iomem->port[1].ppar |= (B3dir0 | B3dir1);
- /* Step 2, Port C clocks */
- iomem->port[2].psor &= ~0x0000c000;
- iomem->port[2].pdir &= ~0x0000c000;
- iomem->port[2].ppar |= 0x0000c000;
- iomem->port[3].pdat |= (ctlr->pmdio | ctlr->pmdck);
- iomem->port[3].podr |= ctlr->pmdio;
- iomem->port[3].pdir |= (ctlr->pmdio | ctlr->pmdck);
- iomem->port[3].ppar &= ~(ctlr->pmdio | ctlr->pmdck);
- eieio();
- /* Step 3, Serial Interface clock routing */
- iomem->cmxfcr &= ~0x0000ff00;
- iomem->cmxfcr |= 0x00003700;
- break;
- }
- iopunlock();
- p = (Etherparam*)(m->imap->prmfcc + ctlr->port);
- memset(p, 0, sizeof(Etherparam));
- /* Step 4 */
- // fcc->gfmr |= (TCI | ENET);
- fcc->gfmr |= ENET;
- /* Step 5 */
- fcc->fpsmr = CRCE | FDE | LPB; /* full duplex operation */
- // fcc->fpsmr = CRCE; /* half duplex operation */
- /* 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 = Bufsize;
- p->mflr = Rbsize;
- p->minflr = ETHERMINTU + 4;
- p->maxd1 = (Rbsize+3) & ~3;
- p->maxd2 = (Rbsize+3) & ~3;
- 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->imap->fccextra[ctlr->port].ri - (uchar*)INTMEM;
- p->tiptr = m->imap->fccextra[ctlr->port].ti - (uchar*)INTMEM;
- p->padptr = m->imap->fccextra[ctlr->port].pad - (uchar*)INTMEM;
- memset(m->imap->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, Enable ethernet: done at attach time
- }
- static int
- reset(Ether* ether)
- {
- uchar ea[Eaddrlen];
- Ctlr *ctlr;
- FCC *fcc;
- if(m->cpuhz < 24000000){
- print("%s ether: system speed must be >= 24MHz for ether use\n", ether->type);
- return -1;
- }
- if(!(ether->port >= 0 && 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 = iomem->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, Bufsize) < 0)
- panic("etherfcc init");
- 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);
- }
|