summaryrefslogtreecommitdiff
path: root/os/mpc/etherscc.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/mpc/etherscc.c
parent46439007cf417cbd9ac8049bb4122c890097a0fa (diff)
20060303
Diffstat (limited to 'os/mpc/etherscc.c')
-rw-r--r--os/mpc/etherscc.c528
1 files changed, 528 insertions, 0 deletions
diff --git a/os/mpc/etherscc.c b/os/mpc/etherscc.c
new file mode 100644
index 00000000..8180140e
--- /dev/null
+++ b/os/mpc/etherscc.c
@@ -0,0 +1,528 @@
+/*
+ * SCCn 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 "etherif.h"
+
+enum {
+ Nrdre = 16, /* receive descriptor ring entries */
+ Ntdre = 16, /* 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= 1<<8,
+ RxeLG= 1<<5,
+ RxeNO= 1<<4,
+ RxeSH= 1<<3,
+ RxeCR= 1<<2,
+ RxeOV= 1<<1,
+ RxeCL= 1<<0,
+ RxError= (RxeLG|RxeNO|RxeSH|RxeCR|RxeOV|RxeCL), /* various error flags */
+
+ /* ether-specific Tx BD bits */
+ TxPad= 1<<14, /* pad short frames */
+ TxTC= 1<<10, /* transmit CRC */
+ TxeDEF= 1<<9,
+ TxeHB= 1<<8,
+ TxeLC= 1<<7,
+ TxeRL= 1<<6,
+ TxeUN= 1<<1,
+ TxeCSL= 1<<0,
+
+ /* scce */
+ RXB= 1<<0,
+ TXB= 1<<1,
+ BSY= 1<<2,
+ RXF= 1<<3,
+ TXE= 1<<4,
+
+ /* psmr */
+ PRO= 1<<9, /* promiscuous mode */
+
+ /* gsmrl */
+ ENR= 1<<5,
+ ENT= 1<<4,
+
+ /* port A */
+ RXD1= SIBIT(15),
+ TXD1= SIBIT(14),
+
+ /* port B */
+ RTS1= IBIT(19),
+
+ /* port C */
+ CTS1= SIBIT(11),
+ CD1= SIBIT(10),
+};
+
+typedef struct Etherparam Etherparam;
+struct Etherparam {
+ SCCparam;
+ ulong c_pres; /* preset CRC */
+ ulong c_mask; /* constant mask for CRC */
+ ulong crcec; /* CRC error counter */
+ ulong alec; /* alighnment error counter */
+ ulong disfc; /* discard frame counter */
+ ushort pads; /* short frame PAD characters */
+ ushort ret_lim; /* retry limit threshold */
+ ushort ret_cnt; /* retry limit counter */
+ ushort mflr; /* maximum frame length reg */
+ ushort minflr; /* minimum frame length reg */
+ ushort maxd1; /* maximum DMA1 length reg */
+ ushort maxd2; /* maximum DMA2 length reg */
+ ushort maxd; /* rx max DMA */
+ ushort dma_cnt; /* rx dma counter */
+ ushort max_b; /* max bd byte count */
+ ushort gaddr[4]; /* group address filter */
+ ulong tbuf0_data0; /* save area 0 - current frm */
+ ulong tbuf0_data1; /* save area 1 - current frm */
+ ulong tbuf0_rba0;
+ ulong tbuf0_crc;
+ ushort tbuf0_bcnt;
+ ushort paddr[3]; /* physical address LSB to MSB increasing */
+ ushort p_per; /* persistence */
+ ushort rfbd_ptr; /* rx first bd pointer */
+ ushort tfbd_ptr; /* tx first bd pointer */
+ ushort tlbd_ptr; /* tx last bd pointer */
+ ulong tbuf1_data0; /* save area 0 - next frame */
+ ulong tbuf1_data1; /* save area 1 - next frame */
+ ulong tbuf1_rba0;
+ ulong tbuf1_crc;
+ ushort tbuf1_bcnt;
+ ushort tx_len; /* tx frame length counter */
+ ushort iaddr[4]; /* individual address filter*/
+ ushort boff_cnt; /* back-off counter */
+ ushort taddr[3]; /* temp address */
+};
+
+typedef struct {
+ Lock;
+ int port;
+ int init;
+ int active;
+ SCC* scc;
+ CPMdev* cpm;
+
+ Ring;
+
+ ulong interrupts; /* statistics */
+ ulong deferred;
+ ulong heartbeat;
+ ulong latecoll;
+ ulong retrylim;
+ ulong underrun;
+ ulong overrun;
+ ulong carrierlost;
+ ulong retrycount;
+} Ctlr;
+
+static int sccid[] = {-1, CPscc1, CPscc2, CPscc3, CPscc4};
+
+static void
+attach(Ether *ether)
+{
+ Ctlr *ctlr;
+
+ ctlr = ether->ctlr;
+ ctlr->active = 1;
+ ctlr->scc->gsmrl |= ENR|ENT;
+ eieio();
+}
+
+static void
+closed(Ether *ether)
+{
+ Ctlr *ctlr;
+
+ ctlr = ether->ctlr;
+ if(ctlr->active){
+ sccxstop(ctlr->cpm);
+ ilock(ctlr);
+ 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->scc->psmr |= PRO;
+ else
+ ctlr->scc->psmr &= ~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->scc->psmr |= PRO;
+ else
+ ctlr->scc->psmr &= ~PRO;
+ iunlock(ctlr);
+}
+
+static void
+txstart(Ether *ether)
+{
+ int len;
+ Ctlr *ctlr;
+ Block *b;
+ BD *dre;
+
+ ctlr = ether->ctlr;
+ while(ctlr->ntq < 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);
+ dcflush(b->rp, len);
+ if(ctlr->txb[ctlr->tdrh] != nil)
+ panic("scc/ether: txstart");
+ ctlr->txb[ctlr->tdrh] = b;
+ if((ulong)b->rp&1)
+ panic("scc/ether: txstart align"); /* TO DO: ensure alignment */
+ dre->addr = PADDR(b->rp);
+ dre->length = len;
+ eieio();
+ dre->status = (dre->status & BDWrap) | BDReady|TxPad|BDInt|BDLast|TxTC;
+ eieio();
+ ctlr->scc->todr = 1<<15; /* transmit now */
+ eieio();
+ 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)
+{
+ Ether *ether;
+ int len, events, status;
+ Ctlr *ctlr;
+ BD *dre;
+ Block *b;
+
+ ether = arg;
+ ctlr = ether->ctlr;
+ if(!ctlr->active)
+ return; /* not ours */
+
+ /*
+ * Acknowledge all interrupts and whine about those that shouldn't
+ * happen.
+ */
+ events = ctlr->scc->scce;
+ eieio();
+ ctlr->scc->scce = events;
+ eieio();
+ ctlr->interrupts++;
+
+ if(events & (TXE|BSY|RXB)){
+ if(events & RXB)
+ ctlr->overrun++;
+ if(events & TXE)
+ ether->oerrs++;
+ if(0 || events & TXE)
+ print("ETHER.SCC#%d: scce = 0x%uX\n", ether->ctlrno, events);
+ }
+ /*
+ * 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) || 1){
+ dre = &ctlr->rdr[ctlr->rdrx];
+ 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;
+ if((b = iallocb(len)) != 0){
+ dcinval(KADDR(dre->addr), len);
+ 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;
+ eieio();
+
+ ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre);
+ dre = &ctlr->rdr[ctlr->rdrx];
+ }
+ }
+
+ /*
+ * Transmitter interrupt: handle anything queued for a free descriptor.
+ */
+ if(events & TXB){
+ lock(ctlr);
+ while(ctlr->ntq){
+ dre = &ctlr->tdr[ctlr->tdri];
+ 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("scce/interrupt: bufp");
+ ctlr->txb[ctlr->tdri] = nil;
+ freeb(b);
+ ctlr->ntq--;
+ ctlr->tdri = NEXT(ctlr->tdri, Ntdre);
+ }
+ txstart(ether);
+ unlock(ctlr);
+ }
+ if(events & TXE)
+ cpmop(ctlr->cpm, RestartTx, 0);
+}
+
+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 MPC823 user guide: section16.9.23.7's initialisation sequence,
+ * except that it sets the right bits for the MPC823ADS board when SCC2 is used,
+ * and those for the 860/821 development board for SCC1.
+ */
+static void
+sccsetup(Ctlr *ctlr, SCC *scc, Ether *ether)
+{
+ int i, rcs, tcs, w;
+ Etherparam *p;
+ IMM *io;
+
+
+ i = 2*(ctlr->port-1);
+ io = ioplock();
+ w = (TXD1|RXD1)<<i; /* TXDn and RXDn in port A */
+ io->papar |= w; /* enable TXDn and RXDn pins */
+ io->padir &= ~w;
+ io->paodr &= ~w; /* not open drain */
+
+ w = (CD1|CTS1)<<i; /* CLSN and RENA: CDn and CTSn in port C */
+ io->pcpar &= ~w; /* enable CLSN (CTSn) and RENA (CDn) */
+ io->pcdir &= ~w;
+ io->pcso |= w;
+ iopunlock();
+
+ /* clocks and transceiver control: details depend on the board's wiring */
+ archetherenable(sccid[ctlr->port], &rcs, &tcs, ether->mbps, ether->fullduplex);
+
+ sccnmsi(ctlr->port, rcs, tcs); /* connect the clocks */
+
+ p = ctlr->cpm->param;
+ memset(p, 0, sizeof(*p));
+ p->rfcr = 0x18;
+ p->tfcr = 0x18;
+ p->mrblr = Bufsize;
+ p->rbase = PADDR(ctlr->rdr);
+ p->tbase = PADDR(ctlr->tdr);
+
+ cpmop(ctlr->cpm, InitRxTx, 0);
+
+ p->c_pres = ~0;
+ p->c_mask = 0xDEBB20E3;
+ p->crcec = 0;
+ p->alec = 0;
+ p->disfc = 0;
+ p->pads = 0x8888;
+ p->ret_lim = 0xF;
+ p->mflr = Rbsize;
+ p->minflr = ETHERMINTU+4;
+ p->maxd1 = Bufsize;
+ p->maxd2 = Bufsize;
+ p->p_per = 0; /* only moderate aggression */
+
+ for(i=0; i<Eaddrlen; i+=2)
+ p->paddr[2-i/2] = (ether->ea[i+1]<<8)|ether->ea[i]; /* it's not the obvious byte order */
+
+ scc->psmr = (2<<10)|(5<<1); /* 32-bit CRC, ignore 22 bits before SFD */
+ scc->dsr = 0xd555;
+ scc->gsmrh = 0; /* normal operation */
+ scc->gsmrl = (1<<28)|(4<<21)|(1<<19)|0xC; /* transmit clock invert, 48 bit preamble, repetitive 10 preamble, ethernet */
+ eieio();
+ scc->scce = ~0; /* clear all events */
+ eieio();
+ scc->sccm = TXE | RXF | TXB; /* enable interrupts */
+ eieio();
+
+ io = ioplock();
+ w = RTS1<<(ctlr->port-1); /* enable TENA pin (RTSn) */
+ io->pbpar |= w;
+ io->pbdir |= w;
+ iopunlock();
+
+ /* gsmrl enable is deferred until attach */
+}
+
+static int
+reset(Ether* ether)
+{
+ uchar ea[Eaddrlen];
+ CPMdev *cpm;
+ Ctlr *ctlr;
+ SCC *scc;
+
+ if(m->speed < 24){
+ print("%s ether: system speed must be >= 24MHz for ether use\n", ether->type);
+ return -1;
+ }
+
+ if(!(ether->port >= 1 && ether->port <= 4)){
+ print("%s ether: no SCC port %lud\n", ether->type, ether->port);
+ return -1;
+ }
+
+ /*
+ * 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;
+ }
+
+ cpm = cpmdev(sccid[ether->port]);
+ if(cpm == nil)
+ return -1;
+ ether->irq = VectorCPIC + cpm->irq;
+ scc = cpm->regs;
+ ctlr = malloc(sizeof(*ctlr));
+ ether->ctlr = ctlr;
+ memset(ctlr, 0, sizeof(*ctlr));
+ ctlr->cpm = cpm;
+ ctlr->scc = scc;
+ ctlr->port = ether->port;
+
+ if(ioringinit(ctlr, Nrdre, Ntdre, Bufsize) < 0)
+ panic("etherscc init");
+
+ sccsetup(ctlr, scc, ether);
+
+ ether->attach = attach;
+ ether->closed = closed;
+ ether->transmit = transmit;
+ ether->interrupt = interrupt;
+ ether->ifstat = ifstat;
+
+ ether->arg = ether;
+ ether->promiscuous = promiscuous;
+ ether->multicast = multicast;
+
+ return 0;
+}
+
+void
+etherscclink(void)
+{
+ addethercard("SCC", reset);
+}