812 lines · 17.3 KB · C
RawDownload
/* * National Semiconductor DP8390 and clone * Network Interface Controller. */#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"#include "ether8390.h" enum {					/* NIC core registers */	Cr		= 0x00,		/* command register, all pages */ 					/* Page 0, read */	Clda0		= 0x01,		/* current local DMA address 0 */	Clda1		= 0x02,		/* current local DMA address 1 */	Bnry		= 0x03,		/* boundary pointer (R/W) */	Tsr		= 0x04,		/* transmit status register */	Ncr		= 0x05,		/* number of collisions register */	Fifo		= 0x06,		/* FIFO */	Isr		= 0x07,		/* interrupt status register (R/W) */	Crda0		= 0x08,		/* current remote DMA address 0 */	Crda1		= 0x09,		/* current remote DMA address 1 */	Rsr		= 0x0C,		/* receive status register */	Ref0		= 0x0D,		/* frame alignment errors */	Ref1		= 0x0E,		/* CRC errors */	Ref2		= 0x0F,		/* missed packet errors */ 					/* Page 0, write */	Pstart		= 0x01,		/* page start register */	Pstop		= 0x02,		/* page stop register */	Tpsr		= 0x04,		/* transmit page start address */	Tbcr0		= 0x05,		/* transmit byte count register 0 */	Tbcr1		= 0x06,		/* transmit byte count register 1 */	Rsar0		= 0x08,		/* remote start address register 0 */	Rsar1		= 0x09,		/* remote start address register 1 */	Rbcr0		= 0x0A,		/* remote byte count register 0 */	Rbcr1		= 0x0B,		/* remote byte count register 1 */	Rcr		= 0x0C,		/* receive configuration register */	Tcr		= 0x0D,		/* transmit configuration register */	Dcr		= 0x0E,		/* data configuration register */	Imr		= 0x0F,		/* interrupt mask */ 					/* Page 1, read/write */	Par0		= 0x01,		/* physical address register 0 */	Curr		= 0x07,		/* current page register */	Mar0		= 0x08,		/* multicast address register 0 */}; enum {					/* Cr */	Stp		= 0x01,		/* stop */	Sta		= 0x02,		/* start */	Txp		= 0x04,		/* transmit packet */	Rd0		= 0x08,		/* remote DMA command */	Rd1		= 0x10,		Rd2		= 0x20,	RdREAD		= Rd0,		/* remote read */	RdWRITE		= Rd1,		/* remote write */	RdSEND		= Rd1|Rd0,	/* send packet */	RdABORT		= Rd2,		/* abort/complete remote DMA */	Ps0		= 0x40,		/* page select */	Ps1		= 0x80,	Page0		= 0x00,	Page1		= Ps0,	Page2		= Ps1,}; enum {					/* Isr/Imr */	Prx		= 0x01,		/* packet received */	Ptx		= 0x02,		/* packet transmitted */	Rxe		= 0x04,		/* receive error */	Txe		= 0x08,		/* transmit error */	Ovw		= 0x10,		/* overwrite warning */	Cnt		= 0x20,		/* counter overflow */	Rdc		= 0x40,		/* remote DMA complete */	Rst		= 0x80,		/* reset status */}; enum {					/* Dcr */	Wts		= 0x01,		/* word transfer select */	Bos		= 0x02,		/* byte order select */	Las		= 0x04,		/* long address select */	Ls		= 0x08,		/* loopback select */	Arm		= 0x10,		/* auto-initialise remote */	Ft0		= 0x20,		/* FIFO threshold select */	Ft1		= 0x40,	Ft1WORD		= 0x00,	Ft2WORD		= Ft0,	Ft4WORD		= Ft1,	Ft6WORD		= Ft1|Ft0,}; enum {					/* Tcr */	Crc		= 0x01,		/* inhibit CRC */	Lb0		= 0x02,		/* encoded loopback control */	Lb1		= 0x04,	LpbkNORMAL	= 0x00,		/* normal operation */	LpbkNIC		= Lb0,		/* internal NIC module loopback */	LpbkENDEC	= Lb1,		/* internal ENDEC module loopback */	LpbkEXTERNAL	= Lb1|Lb0,	/* external loopback */	Atd		= 0x08,		/* auto transmit disable */	Ofst		= 0x10,		/* collision offset enable */}; enum {					/* Tsr */	Ptxok		= 0x01,		/* packet transmitted */	Col		= 0x04,		/* transmit collided */	Abt		= 0x08,		/* tranmit aborted */	Crs		= 0x10,		/* carrier sense lost */	Fu		= 0x20,		/* FIFO underrun */	Cdh		= 0x40,		/* CD heartbeat */	Owc		= 0x80,		/* out of window collision */}; enum {					/* Rcr */	Sep		= 0x01,		/* save errored packets */	Ar		= 0x02,		/* accept runt packets */	Ab		= 0x04,		/* accept broadcast */	Am		= 0x08,		/* accept multicast */	Pro		= 0x10,		/* promiscuous physical */	Mon		= 0x20,		/* monitor mode */}; enum {					/* Rsr */	Prxok		= 0x01,		/* packet received intact */	Crce		= 0x02,		/* CRC error */	Fae		= 0x04,		/* frame alignment error */	Fo		= 0x08,		/* FIFO overrun */	Mpa		= 0x10,		/* missed packet */	Phy		= 0x20,		/* physical/multicast address */	Dis		= 0x40,		/* receiver disabled */	Dfr		= 0x80,		/* deferring */}; typedef struct Hdr Hdr;struct Hdr {	uchar	status;	uchar	next;	uchar	len0;	uchar	len1;}; voiddp8390getea(Ether* ether, uchar* ea){	Dp8390 *ctlr;	uchar cr;	int i; 	ctlr = ether->ctlr; 	/*	 * Get the ethernet address from the chip.	 * Take care to restore the command register	 * afterwards.	 */	ilock(ctlr);	cr = regr(ctlr, Cr) & ~Txp;	regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr));	for(i = 0; i < Eaddrlen; i++)		ea[i] = regr(ctlr, Par0+i);	regw(ctlr, Cr, cr);	iunlock(ctlr);} voiddp8390setea(Ether* ether){	int i;	uchar cr;	Dp8390 *ctlr; 	ctlr = ether->ctlr; 	/*	 * Set the ethernet address into the chip.	 * Take care to restore the command register	 * afterwards. Don't care about multicast	 * addresses as multicast is never enabled	 * (currently).	 */	ilock(ctlr);	cr = regr(ctlr, Cr) & ~Txp;	regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr));	for(i = 0; i < Eaddrlen; i++)		regw(ctlr, Par0+i, ether->ea[i]);	regw(ctlr, Cr, cr);	iunlock(ctlr);} static void*_dp8390read(Dp8390* ctlr, void* to, ulong from, ulong len){	uchar cr;	int timo; 	/*	 * Read some data at offset 'from' in the card's memory	 * using the DP8390 remote DMA facility, and place it at	 * 'to' in main memory, via the I/O data port.	 */	cr = regr(ctlr, Cr) & ~Txp;	regw(ctlr, Cr, Page0|RdABORT|Sta);	regw(ctlr, Isr, Rdc); 	/*	 * Set up the remote DMA address and count.	 */	len = ROUNDUP(len, ctlr->width);	regw(ctlr, Rbcr0, len & 0xFF);	regw(ctlr, Rbcr1, (len>>8) & 0xFF);	regw(ctlr, Rsar0, from & 0xFF);	regw(ctlr, Rsar1, (from>>8) & 0xFF); 	/*	 * Start the remote DMA read and suck the data	 * out of the I/O port.	 */	regw(ctlr, Cr, Page0|RdREAD|Sta);	rdread(ctlr, to, len); 	/*	 * Wait for the remote DMA to complete. The timeout	 * is necessary because this routine may be called on	 * a non-existent chip during initialisation and, due	 * to the miracles of the bus, it's possible to get this	 * far and still be talking to a slot full of nothing.	 */	for(timo = 10000; (regr(ctlr, Isr) & Rdc) == 0 && timo; timo--)			; 	regw(ctlr, Isr, Rdc);	regw(ctlr, Cr, cr); 	return to;} void*dp8390read(Dp8390* ctlr, void* to, ulong from, ulong len){	void *v; 	ilock(ctlr);	v = _dp8390read(ctlr, to, from, len);	iunlock(ctlr); 	return v;} static void*dp8390write(Dp8390* ctlr, ulong to, void* from, ulong len){	ulong crda;	uchar cr;	int timo, width; top:	/*	 * Write some data to offset 'to' in the card's memory	 * using the DP8390 remote DMA facility, reading it at	 * 'from' in main memory, via the I/O data port.	 */	cr = regr(ctlr, Cr) & ~Txp;	regw(ctlr, Cr, Page0|RdABORT|Sta);	regw(ctlr, Isr, Rdc); 	len = ROUNDUP(len, ctlr->width); 	/*	 * Set up the remote DMA address and count.	 * This is straight from the DP8390[12D] datasheet,	 * hence the initial set up for read.	 * Assumption here that the A7000 EtherV card will	 * never need a dummyrr.	 */	if(ctlr->dummyrr && (ctlr->width == 1 || ctlr->width == 2)){		if(ctlr->width == 2)			width = 1;		else			width = 0;		crda = to-1-width;		regw(ctlr, Rbcr0, (len+1+width) & 0xFF);		regw(ctlr, Rbcr1, ((len+1+width)>>8) & 0xFF);		regw(ctlr, Rsar0, crda & 0xFF);		regw(ctlr, Rsar1, (crda>>8) & 0xFF);		regw(ctlr, Cr, Page0|RdREAD|Sta);			for(timo=0;; timo++){			if(timo > 10000){				print("ether8390: dummyrr timeout; assuming nodummyrr\n");				ctlr->dummyrr = 0;				goto top;			}			crda = regr(ctlr, Crda0);			crda |= regr(ctlr, Crda1)<<8;			if(crda == to){				/*				 * Start the remote DMA write and make sure				 * the registers are correct.				 */				regw(ctlr, Cr, Page0|RdWRITE|Sta);					crda = regr(ctlr, Crda0);				crda |= regr(ctlr, Crda1)<<8;				if(crda != to)					panic("crda write %lud to %lud\n", crda, to);					break;			}		}	}	else{		regw(ctlr, Rsar0, to & 0xFF);		regw(ctlr, Rsar1, (to>>8) & 0xFF);		regw(ctlr, Rbcr0, len & 0xFF);		regw(ctlr, Rbcr1, (len>>8) & 0xFF);		regw(ctlr, Cr, Page0|RdWRITE|Sta);	} 	/*	 * Pump the data into the I/O port	 * then wait for the remote DMA to finish.	 */	rdwrite(ctlr, from, len);	for(timo = 10000; (regr(ctlr, Isr) & Rdc) == 0 && timo; timo--)			; 	regw(ctlr, Isr, Rdc);	regw(ctlr, Cr, cr); 	return (void*)to;} static voidringinit(Dp8390* ctlr){	regw(ctlr, Pstart, ctlr->pstart);	regw(ctlr, Pstop, ctlr->pstop);	regw(ctlr, Bnry, ctlr->pstop-1); 	regw(ctlr, Cr, Page1|RdABORT|Stp);	regw(ctlr, Curr, ctlr->pstart);	regw(ctlr, Cr, Page0|RdABORT|Stp); 	ctlr->nxtpkt = ctlr->pstart;} static uchargetcurr(Dp8390* ctlr){	uchar cr, curr; 	cr = regr(ctlr, Cr) & ~Txp;	regw(ctlr, Cr, Page1|(~(Ps1|Ps0) & cr));	curr = regr(ctlr, Curr);	regw(ctlr, Cr, cr); 	return curr;} static voidreceive(Ether* ether){	Dp8390 *ctlr;	uchar curr, *p;	Hdr hdr;	ulong count, data, len;	Block *bp; 	ctlr = ether->ctlr;	for(curr = getcurr(ctlr); ctlr->nxtpkt != curr; curr = getcurr(ctlr)){		data = ctlr->nxtpkt*Dp8390BufSz;		if(ctlr->ram)			memmove(&hdr, (void*)(ether->mem+data), sizeof(Hdr));		else			_dp8390read(ctlr, &hdr, data, sizeof(Hdr)); 		/*		 * Don't believe the upper byte count, work it		 * out from the software next-page pointer and		 * the current next-page pointer.		 */		if(hdr.next > ctlr->nxtpkt)			len = hdr.next - ctlr->nxtpkt - 1;		else			len = (ctlr->pstop-ctlr->nxtpkt) + (hdr.next-ctlr->pstart) - 1;		if(hdr.len0 > (Dp8390BufSz-sizeof(Hdr)))			len--; 		len = ((len<<8)|hdr.len0)-4; 		/*		 * Chip is badly scrogged, reinitialise the ring.		 */		if(hdr.next < ctlr->pstart || hdr.next >= ctlr->pstop		  || len < 60 || len > sizeof(Etherpkt)){			print("dp8390: H%2.2ux+%2.2ux+%2.2ux+%2.2ux,%lud\n",				hdr.status, hdr.next, hdr.len0, hdr.len1, len);			regw(ctlr, Cr, Page0|RdABORT|Stp);			ringinit(ctlr);			regw(ctlr, Cr, Page0|RdABORT|Sta); 			return;		} 		/*		 * If it's a good packet read it in to the software buffer.		 * If the packet wraps round the hardware ring, read it in		 * two pieces.		 */		if((hdr.status & (Fo|Fae|Crce|Prxok)) == Prxok && (bp = iallocb(len))){			p = bp->rp;			bp->wp = p+len;			data += sizeof(Hdr); 			if((data+len) >= ctlr->pstop*Dp8390BufSz){				count = ctlr->pstop*Dp8390BufSz - data;				if(ctlr->ram)					memmove(p, (void*)(ether->mem+data), count);				else					_dp8390read(ctlr, p, data, count);				p += count;				data = ctlr->pstart*Dp8390BufSz;				len -= count;			}			if(len){				if(ctlr->ram)					memmove(p, (void*)(ether->mem+data), len);				else					_dp8390read(ctlr, p, data, len);			} 			/*			 * Copy the packet to whoever wants it.			 */			etheriq(ether, bp, 1);		} 		/*		 * Finished with this packet, update the		 * hardware and software ring pointers.		 */		ctlr->nxtpkt = hdr.next; 		hdr.next--;		if(hdr.next < ctlr->pstart)			hdr.next = ctlr->pstop-1;		regw(ctlr, Bnry, hdr.next);	}} static voidtxstart(Ether* ether){	int len;	Dp8390 *ctlr;	Block *bp;	uchar minpkt[ETHERMINTU], *rp; 	ctlr = ether->ctlr; 	/*	 * This routine is called both from the top level and from interrupt	 * level and expects to be called with ctlr already locked.	 */	if(ctlr->txbusy)		return;	bp = qget(ether->oq);	if(bp == nil)		return; 	/*	 * Make sure the packet is of minimum length;	 * copy it to the card's memory by the appropriate means;	 * start the transmission.	 */	len = BLEN(bp);	rp = bp->rp;	if(len < ETHERMINTU){		rp = minpkt;		memmove(rp, bp->rp, len);		memset(rp+len, 0, ETHERMINTU-len);		len = ETHERMINTU;	} 	if(ctlr->ram)		memmove((void*)(ether->mem+ctlr->tstart*Dp8390BufSz), rp, len);	else		dp8390write(ctlr, ctlr->tstart*Dp8390BufSz, rp, len);	freeb(bp); 	regw(ctlr, Tbcr0, len & 0xFF);	regw(ctlr, Tbcr1, (len>>8) & 0xFF);	regw(ctlr, Cr, Page0|RdABORT|Txp|Sta);

Lines 1–500 of 812.