669 lines · 14.4 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * AMD79C970 * PCnet-PCI Single-Chip Ethernet Controller for PCI Local Bus * To do: *	finish this rewrite */#include "u.h"#include "../port/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 {	Lognrdre	= 6,	Nrdre		= (1<<Lognrdre),/* receive descriptor ring entries */	Logntdre	= 4,	Ntdre		= (1<<Logntdre),/* transmit descriptor ring entries */ 	Rbsize		= ETHERMAXTU+4,	/* ring buffer size (+4 for CRC) */}; enum {					/* DWIO I/O resource map */	Aprom		= 0x0000,	/* physical address */	Rdp		= 0x0010,	/* register data port */	Rap		= 0x0014,	/* register address port */	Sreset		= 0x0018,	/* software reset */	Bdp		= 0x001C,	/* bus configuration register data port */}; enum {					/* CSR0 */	Init		= 0x0001,	/* begin initialisation */	Strt		= 0x0002,	/* enable chip */	Stop		= 0x0004,	/* disable chip */	Tdmd		= 0x0008,	/* transmit demand */	Txon		= 0x0010,	/* transmitter on */	Rxon		= 0x0020,	/* receiver on */	Iena		= 0x0040,	/* interrupt enable */	Intr		= 0x0080,	/* interrupt flag */	Idon		= 0x0100,	/* initialisation done */	Tint		= 0x0200,	/* transmit interrupt */	Rint		= 0x0400,	/* receive interrupt */	Merr		= 0x0800,	/* memory error */	Miss		= 0x1000,	/* missed frame */	Cerr		= 0x2000,	/* collision */	Babl		= 0x4000,	/* transmitter timeout */	Err		= 0x8000,	/* Babl|Cerr|Miss|Merr */};	enum {					/* CSR3 */	Bswp		= 0x0004,	/* byte swap */	Emba		= 0x0008,	/* enable modified back-off algorithm */	Dxmt2pd		= 0x0010,	/* disable transmit two part deferral */	Lappen		= 0x0020,	/* look-ahead packet processing enable */}; enum {					/* CSR4 */	ApadXmt		= 0x0800,	/* auto pad transmit */}; enum {					/* CSR15 */	Prom		= 0x8000,	/* promiscuous mode */}; typedef struct Iblock Iblock;struct Iblock {			/* Initialisation Block */	ushort	mode;	uchar	rlen;			/* upper 4 bits */	uchar	tlen;			/* upper 4 bits */	uchar	padr[6];	uchar	res[2];	uchar	ladr[8];	ulong	rdra;	ulong	tdra;}; typedef struct Dre Dre;struct Dre {			/* descriptor ring entry */	ulong	addr;	ulong	md1;			/* status|bcnt */	ulong	md2;			/* rcc|rpc|mcnt */	Block*	bp;}; enum {					/* md1 */	Enp		= 0x01000000,	/* end of packet */	Stp		= 0x02000000,	/* start of packet */	RxBuff		= 0x04000000,	/* buffer error */	Def		= 0x04000000,	/* deferred */	Crc		= 0x08000000,	/* CRC error */	One		= 0x08000000,	/* one retry needed */	Oflo		= 0x10000000,	/* overflow error */	More		= 0x10000000,	/* more than one retry needed */	Fram		= 0x20000000,	/* framing error */	RxErr		= 0x40000000,	/* Fram|Oflo|Crc|RxBuff */	TxErr		= 0x40000000,	/* Uflo|Lcol|Lcar|Rtry */	Own		= 0x80000000,}; enum {					/* md2 */	Rtry		= 0x04000000,	/* failed after repeated retries */	Lcar		= 0x08000000,	/* loss of carrier */	Lcol		= 0x10000000,	/* late collision */	Uflo		= 0x40000000,	/* underflow error */	TxBuff		= 0x80000000,	/* buffer error */}; typedef struct Ctlr Ctlr;struct Ctlr {	Lock;	int	port;	Pcidev*	pcidev;	Ctlr*	next;	int	active; 	int	init;			/* initialisation in progress */	Iblock	iblock; 	Dre*	rdr;			/* receive descriptor ring */	int	rdrx; 	Dre*	tdr;			/* transmit descriptor ring */	int	tdrh;			/* host index into tdr */	int	tdri;			/* interface index into tdr */	int	ntq;			/* descriptors active */ 	ulong	rxbuff;			/* receive statistics */	ulong	crc;	ulong	oflo;	ulong	fram; 	ulong	rtry;			/* transmit statistics */	ulong	lcar;	ulong	lcol;	ulong	uflo;	ulong	txbuff; 	ulong	merr;			/* bobf is such a whiner */	ulong	miss;	ulong	babl; 	int	(*ior)(Ctlr*, int);	void	(*iow)(Ctlr*, int, int);}; static Ctlr* ctlrhead;static Ctlr* ctlrtail; /* * The Rdp, Rap, Sreset, Bdp ports are 32-bit port offset in the enumeration above. * To get to 16-bit offsets, scale down with 0x10 staying the same. */static intio16r(Ctlr *c, int r){	if(r >= Rdp)		r = (r-Rdp)/2+Rdp;	return ins(c->port+r);} static voidio16w(Ctlr *c, int r, int v){	if(r >= Rdp)		r = (r-Rdp)/2+Rdp;	outs(c->port+r, v);} static intio32r(Ctlr *c, int r){	return inl(c->port+r);} static voidio32w(Ctlr *c, int r, int v){	outl(c->port+r, v);} static voidattach(Ether*){} static longifstat(Ether* ether, void* a, long n, ulong offset){	char *p;	int len;	Ctlr *ctlr; 	ctlr = ether->ctlr; 	ether->crcs = ctlr->crc;	ether->frames = ctlr->fram;	ether->buffs = ctlr->rxbuff+ctlr->txbuff;	ether->overflows = ctlr->oflo; 	if(n == 0)		return 0; 	p = malloc(READSTR);	if(p == nil)		error(Enomem);	len = snprint(p, READSTR, "Rxbuff: %ld\n", ctlr->rxbuff);	len += snprint(p+len, READSTR-len, "Crc: %ld\n", ctlr->crc);	len += snprint(p+len, READSTR-len, "Oflo: %ld\n", ctlr->oflo);	len += snprint(p+len, READSTR-len, "Fram: %ld\n", ctlr->fram);	len += snprint(p+len, READSTR-len, "Rtry: %ld\n", ctlr->rtry);	len += snprint(p+len, READSTR-len, "Lcar: %ld\n", ctlr->lcar);	len += snprint(p+len, READSTR-len, "Lcol: %ld\n", ctlr->lcol);	len += snprint(p+len, READSTR-len, "Uflo: %ld\n", ctlr->uflo);	len += snprint(p+len, READSTR-len, "Txbuff: %ld\n", ctlr->txbuff);	len += snprint(p+len, READSTR-len, "Merr: %ld\n", ctlr->merr);	len += snprint(p+len, READSTR-len, "Miss: %ld\n", ctlr->miss);	snprint(p+len, READSTR-len, "Babl: %ld\n", ctlr->babl); 	n = readstr(offset, a, n, p);	free(p); 	return n;} static voidringinit(Ctlr* ctlr){	Dre *dre; 	/*	 * Initialise the receive and transmit buffer rings.	 * The ring entries must be aligned on 16-byte boundaries.	 *	 * This routine is protected by ctlr->init.	 */	if(ctlr->rdr == 0){		ctlr->rdr = xspanalloc(Nrdre*sizeof(Dre), 0x10, 0);		for(dre = ctlr->rdr; dre < &ctlr->rdr[Nrdre]; dre++){			dre->bp = iallocb(Rbsize);			if(dre->bp == nil)				panic("can't allocate ethernet receive ring\n");			dre->addr = PADDR(dre->bp->rp);			dre->md2 = 0;			dre->md1 = Own|(-Rbsize & 0xFFFF);		}	}	ctlr->rdrx = 0; 	if(ctlr->tdr == 0)		ctlr->tdr = xspanalloc(Ntdre*sizeof(Dre), 0x10, 0);	memset(ctlr->tdr, 0, Ntdre*sizeof(Dre));	ctlr->tdrh = ctlr->tdri = 0;} static voidpromiscuous(void* arg, int on){	Ether *ether;	int x;	Ctlr *ctlr; 	ether = arg;	ctlr = ether->ctlr; 	/*	 * Put the chip into promiscuous mode. First must wait until	 * anyone transmitting is done, then stop the chip and put	 * it in promiscuous mode. Restarting is made harder by the chip	 * reloading the transmit and receive descriptor pointers with their	 * base addresses when Strt is set (unlike the older Lance chip),	 * so the rings must be re-initialised.	 */	ilock(ctlr);	if(ctlr->init){		iunlock(ctlr);		return;	}	ctlr->init = 1;	iunlock(ctlr); 	while(ctlr->ntq)		; 	ctlr->iow(ctlr, Rdp, Stop); 	ctlr->iow(ctlr, Rap, 15);	x = ctlr->ior(ctlr, Rdp) & ~Prom;	if(on)		x |= Prom;	ctlr->iow(ctlr, Rdp, x);	ctlr->iow(ctlr, Rap, 0); 	ringinit(ctlr); 	ilock(ctlr);	ctlr->init = 0;	ctlr->iow(ctlr, Rdp, Iena|Strt);	iunlock(ctlr);} static voidmulticast(void* arg, uchar*, int){	promiscuous(arg, 1);} static voidshutdown(Ether *ether){	Ctlr *ctlr; 	ctlr = ether->ctlr;	ilock(ctlr);	io32r(ctlr, Sreset);	io16r(ctlr, Sreset);	iunlock(ctlr);} static voidtxstart(Ether* ether){	Ctlr *ctlr;	Block *bp;	Dre *dre; 	ctlr = ether->ctlr; 	if(ctlr->init)		return; 	while(ctlr->ntq < (Ntdre-1)){		bp = qget(ether->oq);		if(bp == nil)			break; 		/*		 * Give ownership of the descriptor to the chip,		 * increment the software ring descriptor pointer		 * and tell the chip to poll.		 * There's no need to pad to ETHERMINTU		 * here as ApadXmt is set in CSR4.		 */		dre = &ctlr->tdr[ctlr->tdrh];		dre->bp = bp;		dre->addr = PADDR(bp->rp);		dre->md2 = 0;		dre->md1 = Own|Stp|Enp|(-BLEN(bp) & 0xFFFF);		ctlr->ntq++;		ctlr->iow(ctlr, Rdp, Iena|Tdmd);		ctlr->tdrh = NEXT(ctlr->tdrh, Ntdre);	}} static voidtransmit(Ether* ether){	Ctlr *ctlr; 	ctlr = ether->ctlr;	ilock(ctlr);	txstart(ether);	iunlock(ctlr);} static voidinterrupt(Ureg*, void* arg){	Ctlr *ctlr;	Ether *ether;	int csr0, len;	Dre *dre;	Block *bp; 	ether = arg;	ctlr = ether->ctlr; 	/*	 * Acknowledge all interrupts and whine about those that shouldn't	 * happen.	 */intrloop:	csr0 = ctlr->ior(ctlr, Rdp) & 0xFFFF;	ctlr->iow(ctlr, Rdp, Babl|Cerr|Miss|Merr|Rint|Tint|Iena);	if(csr0 & Merr)		ctlr->merr++;	if(csr0 & Miss)		ctlr->miss++;	if(csr0 & Babl)		ctlr->babl++;	//if(csr0 & (Babl|Miss|Merr))	//	print("#l%d: csr0 = 0x%uX\n", ether->ctlrno, csr0);	if(!(csr0 & (Rint|Tint)))		return; 	/*	 * Receiver interrupt: run round the descriptor ring logging	 * errors and passing valid receive data up to the higher levels	 * until a descriptor is encountered still owned by the chip.	 */	if(csr0 & Rint){		dre = &ctlr->rdr[ctlr->rdrx];		while(!(dre->md1 & Own)){			if(dre->md1 & RxErr){				if(dre->md1 & RxBuff)					ctlr->rxbuff++;				if(dre->md1 & Crc)					ctlr->crc++;				if(dre->md1 & Oflo)					ctlr->oflo++;				if(dre->md1 & Fram)					ctlr->fram++;			}			else if(bp = iallocb(Rbsize)){				len = (dre->md2 & 0x0FFF)-4;				dre->bp->wp = dre->bp->rp+len;				etheriq(ether, dre->bp, 1);				dre->bp = bp;				dre->addr = PADDR(bp->rp);			} 			/*			 * Finished with this descriptor, reinitialise it,			 * give it back to the chip, then on to the next...			 */			dre->md2 = 0;			dre->md1 = Own|(-Rbsize & 0xFFFF); 			ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre);			dre = &ctlr->rdr[ctlr->rdrx];		}	} 	/*	 * Transmitter interrupt: wakeup anyone waiting for a free descriptor.	 */	if(csr0 & Tint){		lock(ctlr);		while(ctlr->ntq){			dre = &ctlr->tdr[ctlr->tdri];			if(dre->md1 & Own)				break;				if(dre->md1 & TxErr){				if(dre->md2 & Rtry)					ctlr->rtry++;				if(dre->md2 & Lcar)					ctlr->lcar++;				if(dre->md2 & Lcol)					ctlr->lcol++;				if(dre->md2 & Uflo)					ctlr->uflo++;				if(dre->md2 & TxBuff)					ctlr->txbuff++;				ether->oerrs++;			}				freeb(dre->bp);				ctlr->ntq--;			ctlr->tdri = NEXT(ctlr->tdri, Ntdre);		}		txstart(ether);		unlock(ctlr);	}	goto intrloop;} static voidamd79c970pci(void){	int port;	Ctlr *ctlr;	Pcidev *p; 	p = nil;	while(p = pcimatch(p, 0x1022, 0x2000)){		port = p->mem[0].bar & ~0x01;		if(ioalloc(port, p->mem[0].size, 0, "amd79c970") < 0){			print("amd79c970: port 0x%uX in use\n", port);			continue;		}		ctlr = malloc(sizeof(Ctlr));		if(ctlr == nil)			error(Enomem);		ctlr->port = p->mem[0].bar & ~0x01;		ctlr->pcidev = p; 

Lines 1–500 of 669.