rminnichinitial1fcdf7916 years agoHistory
885 lines · 18.8 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 none of them are in the right place. * To do: *	the PCI scanning code could be made common to other adapters; *	PCI code needs rewritten to handle byte, word, dword accesses *	  and using the devno as a bus+dev+function triplet. */#include "u.h"#include "lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "io.h" #include "etherif.h" enum {	Nrfd		= 4,		/* receive frame area */ 	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 */	Pointer		= 0x04,		/* dword */	Port		= 0x08,		/* dword */	Fcr		= 0x0C,		/* Flash control register */	Ecr		= 0x0E,		/* EEPROM control register */	Mcr		= 0x10,		/* MDI control 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; 	Etherpkt;} 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		= 0x00004000,	RfdEOF		= 0x00008000,}; typedef struct Cb {	int	command;	ulong	link;	uchar	data[24];	/* CbIAS + CbConfigure */} Cb; typedef struct TxCB {	int	command;	ulong	link;	ulong	tbd;	ushort	count;	uchar	threshold;	uchar	number;} TxCB; enum {					/* action command */	CbOK		= 0x00002000,	/* DMA completed OK */	CbC		= 0x00008000,	/* execution Complete */ 	CbNOP		= 0x00000000,	CbIAS		= 0x00010000,	/* Indvidual Address Setup */	CbConfigure	= 0x00020000,	CbMAS		= 0x00030000,	/* Multicast Address Setup */	CbTransmit	= 0x00040000,	CbDump		= 0x00060000,	CbDiagnose	= 0x00070000,	CbCommand	= 0x00070000,	/* mask */ 	CbSF		= 0x00080000,	/* CbTransmit */ 	CbI		= 0x20000000,	/* Interrupt after completion */	CbS		= 0x40000000,	/* Suspend after completion */	CbEL		= 0x80000000,	/* End of List */}; enum {					/* CbTransmit count */	CbEOF		= 0x00008000,}; typedef struct Ctlr Ctlr;typedef struct Ctlr {	int	port;	Pcidev*	pcidev;	Ctlr*	next;	int	active; 	int	eepromsz;		/* address size in bits */	ushort*	eeprom; 	int	ctlrno;	char*	type; 	uchar	configdata[24]; 	Rfd	rfd[Nrfd];	int	rfdl;	int	rfdx; 	Block*	cbqhead;	Block*	cbqtail;	int	cbqbusy;} Ctlr; static Ctlr* ctlrhead;static Ctlr* ctlrtail; static uchar configdata[24] = {	0x16,				/* byte count */	0x44,				/* Rx/Tx FIFO limit */	0x00,				/* adaptive IFS */	0x00,		0x04,				/* Rx DMA maximum byte count */	0x84,				/* Tx DMA maximum byte count */	0x33,				/* late SCB, CNA interrupts */	0x01,				/* discard short Rx frames */	0x00,				/* 503/MII */ 	0x00,		0x2E,				/* normal operation, NSAI */	0x00,				/* linear priority */	0x60,				/* inter-frame spacing */	0x00,		0xF2,		0x48,				/* promiscuous mode off */	0x00,		0x40,		0xF2,				/* 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 voidcustart(Ctlr* ctlr){	if(ctlr->cbqhead == 0){		ctlr->cbqbusy = 0;		return;	}	ctlr->cbqbusy = 1; 	csr32w(ctlr, Pointer, PADDR(ctlr->cbqhead->rp));	while(csr8r(ctlr, CommandR))		;	csr8w(ctlr, CommandR, CUstart);} static voidaction(Ctlr* ctlr, Block* bp){	Cb *cb; 	cb = (Cb*)bp->rp;	cb->command |= CbEL; 	if(ctlr->cbqhead){		ctlr->cbqtail->next = bp;		cb = (Cb*)ctlr->cbqtail->rp;		cb->link = PADDR(bp->rp);		cb->command &= ~CbEL;	}	else		ctlr->cbqhead = bp;	ctlr->cbqtail = bp; 	if(ctlr->cbqbusy == 0)		custart(ctlr);} static voidattach(Ether* ether){	int status;	Ctlr *ctlr; 	ctlr = ether->ctlr;	status = csr16r(ctlr, Status);	if((status & RUstatus) == RUidle){		csr32w(ctlr, Pointer, PADDR(&ctlr->rfd[ctlr->rfdx]));		while(csr8r(ctlr, CommandR))			;		csr8w(ctlr, CommandR, RUstart);	}} static voidconfigure(void* arg, int promiscuous){	Ctlr *ctlr;	Block *bp;	Cb *cb; 	ctlr = ((Ether*)arg)->ctlr; 	bp = allocb(sizeof(Cb));	cb = (Cb*)bp->rp;	bp->wp += sizeof(Cb); 	cb->command = CbConfigure;	cb->link = NullPointer;	memmove(cb->data, ctlr->configdata, sizeof(ctlr->configdata));	if(promiscuous)		cb->data[15] |= 0x01;	action(ctlr, bp);} static voidtransmit(Ether* ether){	Block *bp;	TxCB *txcb;	RingBuf *tb; 	for(tb = &ether->tb[ether->ti]; tb->owner == Interface; tb = &ether->tb[ether->ti]){		bp = allocb(tb->len+sizeof(TxCB));		txcb = (TxCB*)bp->wp;		bp->wp += sizeof(TxCB); 		txcb->command = CbTransmit;		txcb->link = NullPointer;		txcb->tbd = NullPointer;		txcb->count = CbEOF|tb->len;		txcb->threshold = 2;		txcb->number = 0; 		memmove(bp->wp, tb->pkt, tb->len);		memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen);		bp->wp += tb->len; 		action(ether->ctlr, bp); 		tb->owner = Host;		ether->ti = NEXT(ether->ti, ether->ntb);	}} static voidinterrupt(Ureg*, void* arg){	Rfd *rfd;	Block *bp;	Ctlr *ctlr;	Ether *ether;	int status;	RingBuf *rb; 	ether = arg;	ctlr = ether->ctlr; 	for(;;){		status = csr16r(ctlr, Status);		csr8w(ctlr, Ack, (status>>8) & 0xFF); 		if((status & (StatCX|StatFR|StatCNA|StatRNR)) == 0)			return; 		if(status & StatFR){			rfd = &ctlr->rfd[ctlr->rfdx];			while(rfd->field & RfdC){				rb = &ether->rb[ether->ri];				if(rb->owner == Interface){					rb->owner = Host;					rb->len = rfd->count & 0x3FFF;					memmove(rb->pkt, rfd->d, rfd->count & 0x3FFF);					ether->ri = NEXT(ether->ri, ether->nrb);				} 				/*				 * Reinitialise the frame for reception and bump				 * the receive frame processing index;				 * bump the sentinel index, mark the new sentinel				 * and clear the old sentinel suspend bit;				 * set bp and rfd for the next receive frame to				 * process.				 */				rfd->field = 0;				rfd->count = 0;				ctlr->rfdx = NEXT(ctlr->rfdx, Nrfd); 				rfd = &ctlr->rfd[ctlr->rfdl];				ctlr->rfdl = NEXT(ctlr->rfdl, Nrfd);				ctlr->rfd[ctlr->rfdl].field |= RfdS;				rfd->field &= ~RfdS; 				rfd = &ctlr->rfd[ctlr->rfdx];			}			status &= ~StatFR;		} 		if(status & StatRNR){			while(csr8r(ctlr, CommandR))				;			csr8w(ctlr, CommandR, RUresume); 			status &= ~StatRNR;		} 		if(status & StatCNA){			while(bp = ctlr->cbqhead){				if((((Cb*)bp->rp)->command & CbC) == 0)					break;				ctlr->cbqhead = bp->next;				freeb(bp);			}			custart(ctlr); 			status &= ~StatCNA;		} 		if(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI))			panic("%s#%d: status %uX", ctlr->type,  ctlr->ctlrno, status);	}} static voidctlrinit(Ctlr* ctlr){	int i;	Rfd *rfd;	ulong link; 	link = NullPointer;	for(i = Nrfd-1; i >= 0; i--){		rfd = &ctlr->rfd[i]; 		rfd->field = 0;		rfd->link = link;		link = PADDR(rfd);		rfd->rbd = NullPointer;		rfd->count = 0;		rfd->size = sizeof(Etherpkt);	}	ctlr->rfd[Nrfd-1].link = PADDR(&ctlr->rfd[0]); 	ctlr->rfdl = 0;	ctlr->rfd[0].field |= RfdS;	ctlr->rfdx = 2; 	memmove(ctlr->configdata, configdata, sizeof(configdata));} static intmiir(Ctlr* ctlr, int phyadd, int regadd){	int mcr, timo; 	csr32w(ctlr, Mcr, MDIread|(phyadd<<21)|(regadd<<16));	mcr = 0;	for(timo = 64; timo; timo--){		mcr = csr32r(ctlr, Mcr);		if(mcr & MDIready)			break;		microdelay(1);	} 	if(mcr & MDIready)		return mcr & 0xFFFF; 	return -1;} static intmiiw(Ctlr* ctlr, int phyadd, int regadd, int data){	int mcr, timo; 	csr32w(ctlr, Mcr, MDIwrite|(phyadd<<21)|(regadd<<16)|(data & 0xFFFF));	mcr = 0;	for(timo = 64; timo; timo--){		mcr = csr32r(ctlr, Mcr);		if(mcr & MDIready)			break;		microdelay(1);	} 	if(mcr & MDIready)		return 0; 	return -1;} static inthy93c46r(Ctlr* ctlr, int r){	int data, i, op, size; 	/*	 * Hyundai HY93C46 or equivalent serial EEPROM.	 * This sequence for reading a 16-bit register 'r'	 * in the EEPROM is taken straight from Section	 * 3.3.4.2 of the Intel 82557 User's Guide.	 */reread:	csr16w(ctlr, Ecr, EEcs);	op = EEstart|EEread;	for(i = 2; i >= 0; i--){		data = (((op>>i) & 0x01)<<2)|EEcs;		csr16w(ctlr, Ecr, data);		csr16w(ctlr, Ecr, data|EEsk);		microdelay(1);

Lines 1–500 of 885.