1327 lines · 29.5 KB · C
RawDownload
/* * Intel 82557 Fast Ethernet PCI Bus LAN Controller * as found on the Intel EtherExpress PRO/100B. This chip is full * of smarts, unfortunately they're not all in the right place. * To do: *	the PCI scanning code could be made common to other adapters; *	auto-negotiation, full-duplex; *	optionally use memory-mapped registers; *	detach for PCI reset problems (also towards loadable drivers). */#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 {	Nrfd		= 64,		/* receive frame area */	Ncb		= 64,		/* maximum control blocks queued */ 	NullPointer	= 0xFFFFFFFF,	/* 82557 NULL pointer */}; enum {					/* CSR */	Status		= 0x00,		/* byte or word (word includes Ack) */	Ack		= 0x01,		/* byte */	CommandR	= 0x02,		/* byte or word (word includes Interrupt) */	Interrupt	= 0x03,		/* byte */	General		= 0x04,		/* dword */	Port		= 0x08,		/* dword */	Fcr		= 0x0C,		/* Flash control register */	Ecr		= 0x0E,		/* EEPROM control register */	Mcr		= 0x10,		/* MDI control register */	Gstatus		= 0x1D,		/* General status register */}; enum {					/* Status */	RUidle		= 0x0000,	RUsuspended	= 0x0004,	RUnoresources	= 0x0008,	RUready		= 0x0010,	RUrbd		= 0x0020,	/* bit */	RUstatus	= 0x003F,	/* mask */ 	CUidle		= 0x0000,	CUsuspended	= 0x0040,	CUactive	= 0x0080,	CUstatus	= 0x00C0,	/* mask */ 	StatSWI		= 0x0400,	/* SoftWare generated Interrupt */	StatMDI		= 0x0800,	/* MDI r/w done */	StatRNR		= 0x1000,	/* Receive unit Not Ready */	StatCNA		= 0x2000,	/* Command unit Not Active (Active->Idle) */	StatFR		= 0x4000,	/* Finished Receiving */	StatCX		= 0x8000,	/* Command eXecuted */	StatTNO		= 0x8000,	/* Transmit NOT OK */}; enum {					/* Command (byte) */	CUnop		= 0x00,	CUstart		= 0x10,	CUresume	= 0x20,	LoadDCA		= 0x40,		/* Load Dump Counters Address */	DumpSC		= 0x50,		/* Dump Statistical Counters */	LoadCUB		= 0x60,		/* Load CU Base */	ResetSA		= 0x70,		/* Dump and Reset Statistical Counters */ 	RUstart		= 0x01,	RUresume	= 0x02,	RUabort		= 0x04,	LoadHDS		= 0x05,		/* Load Header Data Size */	LoadRUB		= 0x06,		/* Load RU Base */	RBDresume	= 0x07,		/* Resume frame reception */}; enum {					/* Interrupt (byte) */	InterruptM	= 0x01,		/* interrupt Mask */	InterruptSI	= 0x02,		/* Software generated Interrupt */}; enum {					/* Ecr */	EEsk		= 0x01,		/* serial clock */	EEcs		= 0x02,		/* chip select */	EEdi		= 0x04,		/* serial data in */	EEdo		= 0x08,		/* serial data out */ 	EEstart		= 0x04,		/* start bit */	EEread		= 0x02,		/* read opcode */}; enum {					/* Mcr */	MDIread		= 0x08000000,	/* read opcode */	MDIwrite	= 0x04000000,	/* write opcode */	MDIready	= 0x10000000,	/* ready bit */	MDIie		= 0x20000000,	/* interrupt enable */}; typedef struct Rfd {	int	field;	ulong	link;	ulong	rbd;	ushort	count;	ushort	size; 	uchar	data[1700];} Rfd; enum {					/* field */	RfdCollision	= 0x00000001,	RfdIA		= 0x00000002,	/* IA match */	RfdRxerr	= 0x00000010,	/* PHY character error */	RfdType		= 0x00000020,	/* Type frame */	RfdRunt		= 0x00000080,	RfdOverrun	= 0x00000100,	RfdBuffer	= 0x00000200,	RfdAlignment	= 0x00000400,	RfdCRC		= 0x00000800, 	RfdOK		= 0x00002000,	/* frame received OK */	RfdC		= 0x00008000,	/* reception Complete */	RfdSF		= 0x00080000,	/* Simplified or Flexible (1) Rfd */	RfdH		= 0x00100000,	/* Header RFD */ 	RfdI		= 0x20000000,	/* Interrupt after completion */	RfdS		= 0x40000000,	/* Suspend after completion */	RfdEL		= 0x80000000,	/* End of List */}; enum {					/* count */	RfdF		= 0x4000,	RfdEOF		= 0x8000,}; typedef struct Cb Cb;typedef struct Cb {	ushort	status;	ushort	command;	ulong	link;	union {		uchar	data[24];	/* CbIAS + CbConfigure */		struct {			ulong	tbd;			ushort	count;			uchar	threshold;			uchar	number; 			ulong	tba;			ushort	tbasz;			ushort	pad;		};	}; 	Block*	bp;	Cb*	next;} Cb; enum {					/* action command */	CbU		= 0x1000,	/* transmit underrun */	CbOK		= 0x2000,	/* DMA completed OK */	CbC		= 0x8000,	/* execution Complete */ 	CbNOP		= 0x0000,	CbIAS		= 0x0001,	/* Individual Address Setup */	CbConfigure	= 0x0002,	CbMAS		= 0x0003,	/* Multicast Address Setup */	CbTransmit	= 0x0004,	CbDump		= 0x0006,	CbDiagnose	= 0x0007,	CbCommand	= 0x0007,	/* mask */ 	CbSF		= 0x0008,	/* Flexible-mode CbTransmit */ 	CbI		= 0x2000,	/* Interrupt after completion */	CbS		= 0x4000,	/* Suspend after completion */	CbEL		= 0x8000,	/* End of List */}; enum {					/* CbTransmit count */	CbEOF		= 0x8000,}; typedef struct Ctlr Ctlr;typedef struct Ctlr {	Lock	slock;			/* attach */	int	state; 	int	port;	Pcidev*	pcidev;	Ctlr*	next;	int	active; 	int	eepromsz;		/* address size in bits */	ushort*	eeprom; 	Lock	miilock; 	int	tick; 	Lock	rlock;			/* registers */	int	command;		/* last command issued */ 	Block*	rfdhead;		/* receive side */	Block*	rfdtail;	int	nrfd; 	Lock	cblock;			/* transmit side */	int	action;	int	nop;	uchar	configdata[24];	int	threshold;	int	ncb;	Cb*	cbr;	Cb*	cbhead;	Cb*	cbtail;	int	cbq;	int	cbqmax;	int	cbqmaxhw; 	Lock	dlock;			/* dump statistical counters */	ulong	dump[17];} Ctlr; static Ctlr* ctlrhead;static Ctlr* ctlrtail; static uchar configdata[24] = {	0x16,				/* byte count */	0x08,				/* Rx/Tx FIFO limit */	0x00,				/* adaptive IFS */	0x00,		0x00,				/* Rx DMA maximum byte count *///	0x80,				/* Tx DMA maximum byte count */	0x00,				/* Tx DMA maximum byte count */	0x32,				/* !late SCB, CNA interrupts */	0x03,				/* discard short Rx frames */	0x00,				/* 503/MII */ 	0x00,		0x2E,				/* normal operation, NSAI */	0x00,				/* linear priority */	0x60,				/* inter-frame spacing */	0x00,		0xF2,		0xC8,				/* 503, promiscuous mode off */	0x00,		0x40,		0xF3,				/* transmit padding enable */	0x80,				/* full duplex pin enable */	0x3F,				/* no Multi IA */	0x05,				/* no Multi Cast ALL */}; #define csr8r(c, r)	(inb((c)->port+(r)))#define csr16r(c, r)	(ins((c)->port+(r)))#define csr32r(c, r)	(inl((c)->port+(r)))#define csr8w(c, r, b)	(outb((c)->port+(r), (int)(b)))#define csr16w(c, r, w)	(outs((c)->port+(r), (ushort)(w)))#define csr32w(c, r, l)	(outl((c)->port+(r), (ulong)(l))) static voidcommand(Ctlr* ctlr, int c, int v){	int timeo; 	ilock(&ctlr->rlock); 	/*	 * Only back-to-back CUresume can be done	 * without waiting for any previous command to complete.	 * This should be the common case.	 * Unfortunately there's a chip errata where back-to-back	 * CUresumes can be lost, the fix is to always wait.	if(c == CUresume && ctlr->command == CUresume){		csr8w(ctlr, CommandR, c);		iunlock(&ctlr->rlock);		return;	}	 */ 	for(timeo = 0; timeo < 100; timeo++){		if(!csr8r(ctlr, CommandR))			break;		microdelay(1);	}	if(timeo >= 100){		ctlr->command = -1;		iunlock(&ctlr->rlock);		iprint("i82557: command %#ux %#ux timeout\n", c, v);		return;	} 	switch(c){ 	case CUstart:	case LoadDCA:	case LoadCUB:	case RUstart:	case LoadHDS:	case LoadRUB:		csr32w(ctlr, General, v);		break; 	/*	case CUnop:	case CUresume:	case DumpSC:	case ResetSA:	case RUresume:	case RUabort:	 */	default:		break;	}	csr8w(ctlr, CommandR, c);	ctlr->command = c; 	iunlock(&ctlr->rlock);} static Block*rfdalloc(ulong link){	Block *bp;	Rfd *rfd; 	if(bp = iallocb(sizeof(Rfd))){		rfd = (Rfd*)bp->rp;		rfd->field = 0;		rfd->link = link;		rfd->rbd = NullPointer;		rfd->count = 0;		rfd->size = sizeof(Etherpkt);	} 	return bp;} static voidwatchdog(void* arg){	Ether *ether;	Ctlr *ctlr;	static void txstart(Ether*); 	ether = arg;	for(;;){		tsleep(&up->sleep, return0, 0, 4000); 		/*		 * Hmmm. This doesn't seem right. Currently		 * the device can't be disabled but it may be in		 * the future.		 */		ctlr = ether->ctlr;		if(ctlr == nil || ctlr->state == 0){			print("%s: exiting\n", up->text);			pexit("disabled", 0);		} 		ilock(&ctlr->cblock);		if(ctlr->tick++){			ctlr->action = CbMAS;			txstart(ether);		}		iunlock(&ctlr->cblock);	}} static voidattach(Ether* ether){	Ctlr *ctlr;	char name[KNAMELEN]; 	ctlr = ether->ctlr;	lock(&ctlr->slock);	if(ctlr->state == 0){		ilock(&ctlr->rlock);		csr8w(ctlr, Interrupt, 0);		iunlock(&ctlr->rlock);		command(ctlr, RUstart, PADDR(ctlr->rfdhead->rp));		ctlr->state = 1; 		/*		 * Start the watchdog timer for the receive lockup errata		 * unless the EEPROM compatibility word indicates it may be		 * omitted.		 */		if((ctlr->eeprom[0x03] & 0x0003) != 0x0003){			snprint(name, KNAMELEN, "#l%dwatchdog", ether->ctlrno);			kproc(name, watchdog, ether, 0);		}	}	unlock(&ctlr->slock);} static longifstat(Ether* ether, void* a, long n, ulong offset){	char *p;	int i, len, phyaddr;	Ctlr *ctlr;	ulong dump[17]; 	ctlr = ether->ctlr;	lock(&ctlr->dlock); 	/*	 * Start the command then	 * wait for completion status,	 * should be 0xA005.	 */	ctlr->dump[16] = 0;	command(ctlr, DumpSC, 0);	while(ctlr->dump[16] == 0)		; 	ether->oerrs = ctlr->dump[1]+ctlr->dump[2]+ctlr->dump[3];	ether->crcs = ctlr->dump[10];	ether->frames = ctlr->dump[11];	ether->buffs = ctlr->dump[12]+ctlr->dump[15];	ether->overflows = ctlr->dump[13]; 	if(n == 0){		unlock(&ctlr->dlock);		return 0;	} 	memmove(dump, ctlr->dump, sizeof(dump));	unlock(&ctlr->dlock); 	p = malloc(READSTR);	len = snprint(p, READSTR, "transmit good frames: %lud\n", dump[0]);	len += snprint(p+len, READSTR-len, "transmit maximum collisions errors: %lud\n", dump[1]);	len += snprint(p+len, READSTR-len, "transmit late collisions errors: %lud\n", dump[2]);	len += snprint(p+len, READSTR-len, "transmit underrun errors: %lud\n", dump[3]);	len += snprint(p+len, READSTR-len, "transmit lost carrier sense: %lud\n", dump[4]);	len += snprint(p+len, READSTR-len, "transmit deferred: %lud\n", dump[5]);	len += snprint(p+len, READSTR-len, "transmit single collisions: %lud\n", dump[6]);	len += snprint(p+len, READSTR-len, "transmit multiple collisions: %lud\n", dump[7]);	len += snprint(p+len, READSTR-len, "transmit total collisions: %lud\n", dump[8]);	len += snprint(p+len, READSTR-len, "receive good frames: %lud\n", dump[9]);	len += snprint(p+len, READSTR-len, "receive CRC errors: %lud\n", dump[10]);	len += snprint(p+len, READSTR-len, "receive alignment errors: %lud\n", dump[11]);	len += snprint(p+len, READSTR-len, "receive resource errors: %lud\n", dump[12]);	len += snprint(p+len, READSTR-len, "receive overrun errors: %lud\n", dump[13]);	len += snprint(p+len, READSTR-len, "receive collision detect errors: %lud\n", dump[14]);	len += snprint(p+len, READSTR-len, "receive short frame errors: %lud\n", dump[15]);	len += snprint(p+len, READSTR-len, "nop: %d\n", ctlr->nop);	if(ctlr->cbqmax > ctlr->cbqmaxhw)		ctlr->cbqmaxhw = ctlr->cbqmax;	len += snprint(p+len, READSTR-len, "cbqmax: %d\n", ctlr->cbqmax);	ctlr->cbqmax = 0;	len += snprint(p+len, READSTR-len, "threshold: %d\n", ctlr->threshold); 	len += snprint(p+len, READSTR-len, "eeprom:");	for(i = 0; i < (1<<ctlr->eepromsz); i++){		if(i && ((i & 0x07) == 0))			len += snprint(p+len, READSTR-len, "\n       ");		len += snprint(p+len, READSTR-len, " %4.4ux", ctlr->eeprom[i]);	} 	if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000)){		phyaddr = ctlr->eeprom[6] & 0x00FF;		len += snprint(p+len, READSTR-len, "\nphy %2d:", phyaddr);		for(i = 0; i < 6; i++){			static int miir(Ctlr*, int, int); 			len += snprint(p+len, READSTR-len, " %4.4ux",				miir(ctlr, phyaddr, i));		}	} 	snprint(p+len, READSTR-len, "\n");	n = readstr(offset, a, n, p);	free(p); 	return n;} static voidtxstart(Ether* ether){	Ctlr *ctlr;	Block *bp;	Cb *cb; 	ctlr = ether->ctlr;	while(ctlr->cbq < (ctlr->ncb-1)){		cb = ctlr->cbhead->next;		if(ctlr->action == 0){			bp = qget(ether->oq);			if(bp == nil)				break; 

Lines 1–500 of 1327.