shithub: purgatorio

ref: b5bc6572b0a82c52ab04bcf25e1ed76700deb246
dir: /os/boot/mpc/etherscc.c/

View raw version
/*
 * SCCn ethernet
 */

#include "u.h"
#include "lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"

#include "etherif.h"

enum {
	Nrdre		= 32,	/* receive descriptor ring entries */
	Ntdre		= 4,	/* 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,

	/* 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 {
	SCC*	scc;
	int	port;
	int	cpm;

	BD*	rdr;				/* receive descriptor ring */
	void*	rrb;				/* receive ring buffers */
	int	rdrx;				/* index into rdr */

	BD*	tdr;				/* transmit descriptor ring */
	void*	trb;				/* transmit ring buffers */
	int	tdrx;				/* index into tdr */
} Mot;
static Mot mot[MaxEther];

static	int	sccid[] = {-1, SCC1ID, SCC2ID, SCC3ID, SCC4ID};
static	int	sccparam[] = {-1, SCC1P, SCC2P, SCC3P, SCC4P};
static	int	sccreg[] = {-1, 0xA00, 0xA20, 0xA40, 0xA60};
static	int	sccirq[] = {-1, 0x1E, 0x1D, 0x1C, 0x1B};

static void
attach(Ctlr *ctlr)
{
	mot[ctlr->ctlrno].scc->gsmrl |= ENR|ENT;
	eieio();
}

static void
transmit(Ctlr *ctlr)
{
	int len;
	Mot *motp;
	Block *b;
	BD *tdre;

	motp = &mot[ctlr->ctlrno];
	while(((tdre = &motp->tdr[motp->tdrx])->status & BDReady) == 0){
		b = qget(ctlr->oq);
		if(b == 0)
			break;

		/*
		 * Copy the packet to the transmit buffer.
		 */
		len = BLEN(b);
		memmove(KADDR(tdre->addr), b->rp, len);
	
		/*
		 * Give ownership of the descriptor to the chip, increment the
		 * software ring descriptor pointer and tell the chip to poll.
		 */
		tdre->length = len;
		eieio();
		tdre->status = (tdre->status & BDWrap) | BDReady|TxPad|BDInt|BDLast|TxTC;
		eieio();
		motp->scc->todr = 1<<15;	/* transmit now */
		eieio();
		motp->tdrx = NEXT(motp->tdrx, Ntdre);

		freeb(b);
	
	}
}

static void
interrupt(Ureg*, void *ap)
{
	int len, events, status;
	Mot *motp;
	BD *rdre;
	Block *b;
	Ctlr *ctlr;

	ctlr = ap;
	motp = &mot[ctlr->ctlrno];

	/*
	 * Acknowledge all interrupts and whine about those that shouldn't
	 * happen.
	 */
	events = motp->scc->scce;
	eieio();
	motp->scc->scce = events;
	eieio();
	if(events & (TXE|BSY|RXB))
		print("ETHER.SCC#%d: scce = 0x%uX\n", ctlr->ctlrno, events);
	//print(" %ux|", 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){
		rdre = &motp->rdr[motp->rdrx];
		while(((status = rdre->status) & BDEmpty) == 0){
			if(status & RxError || (status & (BDFirst|BDLast)) != (BDFirst|BDLast)){
				//if(status & RxBuff)
				//	ctlr->buffs++;
				if(status & (1<<2))
					ctlr->crcs++;
				if(status & (1<<1))
					ctlr->overflows++;
				//print("eth rx: %ux\n", status);
				if(status & RxError)
					print("~");
				else if((status & BDLast) == 0)
					print("@");
			}
			else{
				/*
				 * We have a packet. Read it into the next
				 * free ring buffer, if any.
				 */
				len = rdre->length-4;
				if((b = iallocb(len)) != 0){
					memmove(b->wp, KADDR(rdre->addr), len);
					b->wp += len;
					etheriq(ctlr, b, 1);
				}
			}

			/*
			 * Finished with this descriptor, reinitialise it,
			 * give it back to the chip, then on to the next...
			 */
			rdre->length = 0;
			rdre->status = (rdre->status & BDWrap) | BDEmpty | BDInt;
			eieio();

			motp->rdrx = NEXT(motp->rdrx, Nrdre);
			rdre = &motp->rdr[motp->rdrx];
		}
	}

	/*
	 * Transmitter interrupt: handle anything queued for a free descriptor.
	 */
	if(events & TXB)
		transmit(ctlr);
	if(events & TXE)
		cpmop(RestartTx, motp->cpm, 0);
}

static void
ringinit(Mot* motp)
{
	int i, x;

	/*
	 * Initialise the receive and transmit buffer rings. The ring
	 * entries must be aligned on 16-byte boundaries.
	 */
	if(motp->rdr == 0)
		motp->rdr = bdalloc(Nrdre);
	if(motp->rrb == 0)
		motp->rrb = ialloc(Nrdre*Bufsize, 0);
	x = PADDR(motp->rrb);
	for(i = 0; i < Nrdre; i++){
		motp->rdr[i].length = 0;
		motp->rdr[i].addr = x;
		motp->rdr[i].status = BDEmpty|BDInt;
		x += Bufsize;
	}
	motp->rdr[i-1].status |= BDWrap;
	motp->rdrx = 0;

	if(motp->tdr == 0)
		motp->tdr = bdalloc(Ntdre);
	if(motp->trb == 0)
		motp->trb = ialloc(Ntdre*Bufsize, 0);
	x = PADDR(motp->trb);
	for(i = 0; i < Ntdre; i++){
		motp->tdr[i].addr = x;
		motp->tdr[i].length = 0;
		motp->tdr[i].status = TxPad|BDInt|BDLast|TxTC;
		x += Bufsize;
	}
	motp->tdr[i-1].status |= BDWrap;
	motp->tdrx = 0;
}

/*
 * 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(Mot *ctlr, SCC *scc, uchar *ea)
{
	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(ctlr->cpm, &rcs, &tcs);

	sccnmsi(ctlr->port, rcs, tcs);	/* connect the clocks */

	p = (Etherparam*)KADDR(sccparam[ctlr->port]);
	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(InitRxTx, ctlr->cpm, 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] = (ea[i+1]<<8)|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 */
}

/*
 * Prepare the SCCx ethernet for booting.
 */
int
sccethreset(Ctlr* ctlr)
{
	uchar ea[Eaddrlen];
	Mot *motp;
	SCC *scc;
	char line[50], def[50];

	/*
	 * Since there's no EPROM, insist that the configuration entry
	 * (see conf.c and flash.c) holds the Ethernet address.
	 */
	memset(ea, 0, Eaddrlen);
	if(memcmp(ea, ctlr->card.ea, Eaddrlen) == 0){
		print("no preset Ether address\n");
		for(;;){
			strcpy(def, "00108bf12900");	/* valid MAC address to be used only for initial configuration */
			if(getstr("ether MAC address", line, sizeof(line), def) < 0)
				return -1;
			if(parseether(ctlr->card.ea, line) >= 0 || ctlr->card.ea[0] == 0xFF)
				break;
			print("invalid MAC address\n");
		}
	}

	scc = IOREGS(sccreg[ctlr->card.port], SCC);
	ctlr->card.irq = VectorCPIC+sccirq[ctlr->card.port];

	motp = &mot[ctlr->ctlrno];
	motp->scc = scc;
	motp->port = ctlr->card.port;
	motp->cpm = sccid[ctlr->card.port];

	ringinit(motp);

	sccsetup(motp, scc, ctlr->card.ea);

	/* enable is deferred until attach */

	ctlr->card.reset = sccethreset;
	ctlr->card.attach = attach;
	ctlr->card.transmit = transmit;
	ctlr->card.intr = interrupt;

	return 0;
}