forsyth20121205-174063a80cc13 years agoHistory
781 lines · 14.8 KB · C
RawDownload
/* * SMC EtherEZ (SMC91cXX chip) PCMCIA card support. */ #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 {	IoSize		= 0x10,		/* port pool size */	TxTimeout	= 150,}; enum {	/* PCMCIA related */	TupleFunce	= 0x22,	TfNodeId	= 0x04,}; enum {	/* bank 0 registers */	Tcr		= 0x0000,	/* transmit control */	Eph		= 0x0002,	/* ethernet protocol handler */	Rcr		= 0x0004,	/* receiver control */	Counter		= 0x0006,	/* statistics counter */	MemInfo		= 0x0008,	MemCfg		= 0x000A,}; enum {	/* bank 1 registers */	Config		= 0x0000,	BaseAddr	= 0x0002,	Addr0		= 0x0004,	/* ethernet address */	Addr1		= 0x0006,	Addr2		= 0x0008,	General		= 0x000A,	Control		= 0x000C,}; enum {	/* bank 2 registers */	MmuCmd		= 0x0000,	PktNo		= 0x0002,	AllocRes	= 0x0003,	FifoPorts	= 0x0004,	Pointer		= 0x0006,	Data1		= 0x0008,	Interrupt	= 0x000C,	IntrMask	= 0x000D,}; enum {	/* bank 3 registers */	Mcast0		= 0x0000,	Mcast2		= 0x0002,	Mcast4		= 0x0004,	Mcast6		= 0x0006,	Revision	= 0x000A,}; enum {	BankSelect	= 0x000E	/* bank select register */}; enum {	BsrMask		= 0xFF00,	/* mask for chip identification */	BsrId		= 0x3300,};  enum {	/* Tcr values */	TcrClear	= 0x0000,	TcrEnable	= 0x0001,	/* enable transmit */	TcrLoop		= 0x0002,	/* enable internal analogue loopback */	TcrForceCol	= 0x0004,	/* force collision on next tx */	TcrPadEn	= 0x0080,	/* pad short packets to 64 bytes */	TcrNoCrc	= 0x0100,	/* do not append CRC */	TcrMonCns	= 0x0400,	/* monitor carrier status */	TcrFduplx	= 0x0800,	TcrStpSqet	= 0x1000,	TcrEphLoop	= 0x2000,	TcrNormal	= TcrEnable,}; enum {	/* Eph values */	EphTxOk		= 0x0001,	Eph1Col		= 0x0002,	/* single collision */	EphMCol		= 0x0004,	/* multiple collisions */  	EphTxMcast	= 0x0008,	/* multicast transmit */	Eph16Col	= 0x0010,	/* 16 collisions, tx disabled */	EphSqet		= 0x0020,	/* SQE test failed, tx disabled */	EphTxBcast	= 0x0040,	/* broadcast tx */	EphDefr		= 0x0080,	/* deffered tx */	EphLatCol	= 0x0200,	/* late collision, tx disabled */	EphLostCarr	= 0x0400,	/* lost carrier, tx disabled */	EphExcDefr	= 0x0800,	/* excessive defferals */	EphCntRol	= 0x1000,	/* ECR counter(s) rolled over */	EphRxOvrn	= 0x2000,	/* receiver overrun, packets dropped */	EphLinkOk	= 0x4000,	EphTxUnrn	= 0x8000,	/* tx underrun */}; enum {	/* Rcr values */	RcrClear	= 0x0000,	RcrPromisc	= 0x0002,	RcrAllMcast	= 0x0004,	RcrEnable	= 0x0100,	RcrStripCrc	= 0x0200,	RcrSoftReset	= 0x8000,	RcrNormal	= RcrStripCrc | RcrEnable,}; enum { /* Counter value masks */	CntColMask	= 0x000F,	/* collisions */	CntMColMask	= 0x00F0,	/* multiple collisions */	CntDtxMask	= 0x0F00,	/* deferred transmits */	CntExDtxMask	= 0xF000,	/* excessively deferred transmits */ 	CntColShr	= 1,	CntMColShr	= 4,	CntDtxShr	= 8,}; enum { /* MemInfo value masks */	MirTotalMask	= 0x00FF,	MirFreeMask	= 0xFF00,}; enum {	/* Config values */	CfgIrqSel0	= 0x0002,	CfgIrqSel1	= 0x0004,	CfgDisLink	= 0x0040,	/* disable 10BaseT link test */	Cfg16Bit	= 0x0080,	CfgAuiSelect	= 0x0100,	CfgSetSqlch	= 0x0200,	CfgFullStep	= 0x0400,	CfgNoWait	= 0x1000,	CfgMiiSelect	= 0x8000,}; enum {	/* Control values */	CtlStore	= 0x0001,	/* store to EEPROM */	CtlReload	= 0x0002,	/* reload EEPROM into registers */	CtlEeSelect	= 0x0004,	/* select registers for reload/store */	CtlTeEnable	= 0x0020,	/* tx error detection via eph irq */	CtlCrEnable	= 0x0040,	/* counter rollover via eph irq */	CtlLeEnable	= 0x0080,	/* link error detection via eph irq*/	CtlAutoRls	= 0x0800,	/* auto release mode */	CtlPowerDn	= 0x2000,}; enum {	/* MmuCmd values */	McBusy		= 0x0001,	McAlloc		= 0x0020,	/* | with number of 256 byte packets - 1 */	McReset		= 0x0040,	McRelease	= 0x0080,	/* dequeue (but not free) current rx packet */	McFreePkt	= 0x00A0,	/* dequeue and free current rx packet */	McEnqueue	= 0x00C0,	/* enqueue the packet for tx */	McTxReset	= 0x00E0,	/* reset transmit queues */}; enum { /* AllocRes values */	ArFailed	= 0x80,};	  enum {	/* FifoPorts values */	FpTxEmpty	= 0x0080,	FpRxEmpty	= 0x8000,	FpTxMask	= 0x007F,	FpRxMask	= 0x7F00,}; enum {	/* Pointer values */	PtrRead		= 0x2000,	PtrAutoInc	= 0x4000,	PtrRcv		= 0x8000,}; enum {	/* Interrupt values */	IntRcv		= 0x0001,	IntTxError	= 0x0002,	IntTxEmpty	= 0x0004,	IntAlloc	= 0x0008,	IntRxOvrn	= 0x0010,	IntEph		= 0x0020,}; enum { /* transmit status bits */	TsSuccess	= 0x0001,	Ts16Col		= 0x00A0,	TsLatCol	= 0x0200,	TsLostCar	= 0x0400,}; enum { /* receive status bits */	RsMcast		= 0x0001,	RsTooShort	= 0x0400,	RsTooLong	= 0x0800,	RsOddFrame	= 0x1000,	RsBadCrc	= 0x2000,	RsAlgnErr	= 0x8000,	RsError		= RsAlgnErr | RsBadCrc | RsTooLong | RsTooShort,}; enum {	RxLenMask	= 0x07FF,	/* significant rx len bits */	HdrSize		= 6,		/* packet header length */	PageSize	= 256,		/* page length */}; typedef struct Smc91xx Smc91xx;struct Smc91xx {	Lock;	ushort rev;	int attached;	Block *txbp;	ulong txtime; 	ulong rovrn;	ulong lcar;	ulong col;	ulong scol;	ulong mcol;	ulong lcol;	ulong dfr;}; #define SELECT_BANK(x) outs(port + BankSelect, x) static intreadnodeid(int slot, Ether* ether){	uchar data[Eaddrlen + 1];	int len; 	len = sizeof(data);	if (pcmcistuple(slot, TupleFunce, TfNodeId, data, len) != len)		return -1; 	if (data[0] != Eaddrlen)		return -1; 	memmove(ether->ea, &data[1], Eaddrlen);	return 0;} static voidchipreset(Ether* ether){	int port;	int i; 	port = ether->port; 	/* reset the chip */	SELECT_BANK(0);	outs(port + Rcr, RcrSoftReset);	delay(1);	outs(port + Rcr, RcrClear);	outs(port + Tcr, TcrClear);	SELECT_BANK(1);	outs(port + Control, CtlAutoRls | CtlTeEnable |		CtlCrEnable); 	for(i = 0; i < 6; i++) {		outb(port + Addr0 +  i, ether->ea[i]);	} 	SELECT_BANK(2);	outs(port + MmuCmd, McReset);} static voidchipenable(Ether* ether){	int port; 	port = ether->port;	SELECT_BANK(0);	outs(port + Tcr, TcrNormal);	outs(port + Rcr, RcrNormal);	SELECT_BANK(2);	outb(port + IntrMask, IntEph | IntRxOvrn | IntRcv);} static voidattach(Ether *ether){	Smc91xx* ctlr; 	ctlr = ether->ctlr;	ilock(ctlr);		if (ctlr->attached) {		iunlock(ctlr);		return;	} 	chipenable(ether);	ctlr->attached = 1;	iunlock(ctlr);} static voidtxstart(Ether* ether){	int port;	Smc91xx* ctlr;	Block* bp;	int len, npages;	int pno; 	/* assumes ctlr is locked and bank 2 is selected */	/* leaves bank 2 selected on return */	port = ether->port;	ctlr = ether->ctlr; 	if (ctlr->txbp) {		bp = ctlr->txbp;		ctlr->txbp = 0;	} else {		bp = qget(ether->oq);		if (bp == 0)			return; 		len = BLEN(bp);		npages = (len + HdrSize) / PageSize;		outs(port + MmuCmd, McAlloc | npages);	} 	pno = inb(port + AllocRes);	if (pno & ArFailed) {		outb(port + IntrMask, inb(port + IntrMask) | IntAlloc);		ctlr->txbp = bp;		ctlr->txtime = MACHP(0)->ticks;		return;	} 	outb(port + PktNo, pno);	outs(port + Pointer, PtrAutoInc); 	len = BLEN(bp);	outs(port + Data1, 0);	outb(port + Data1, (len + HdrSize) & 0xFF);	outb(port + Data1, (len + HdrSize) >> 8);	outss(port + Data1, bp->rp, len / 2);	if ((len & 1) == 0) {		outs(port + Data1, 0);	} else {		outb(port + Data1, bp->rp[len - 1]);		outb(port + Data1, 0x20);	/* no info what 0x20 means */	} 	outb(port + IntrMask, inb(port + IntrMask) |			IntTxError | IntTxEmpty); 	outs(port + MmuCmd, McEnqueue);	freeb(bp);} static voidreceive(Ether* ether){	int port;	Block* bp;	int pktno, status, len; 	/* assumes ctlr is locked and bank 2 is selected */	/* leaves bank 2 selected on return */	port = ether->port; 	pktno = ins(port + FifoPorts);	if (pktno & FpRxEmpty) {		return;	} 	outs(port + Pointer, PtrRead | PtrRcv | PtrAutoInc);	status = ins(port + Data1);	len = ins(port + Data1) & RxLenMask - HdrSize;		if (status & RsOddFrame)		len++;		if ((status & RsError) || (bp = iallocb(len)) == 0) { 		if (status & RsAlgnErr)			ether->frames++;		if (status & (RsTooShort | RsTooLong))			ether->buffs++;		if (status & RsBadCrc)			ether->crcs++; 		outs(port + MmuCmd, McRelease);		return;	} 	/* packet length is padded to word */	inss(port + Data1, bp->rp, len / 2);	bp->wp = bp->rp + (len & ~1);		if (len & 1) {		*bp->wp = inb(port + Data1);		bp->wp++;	}	  	etheriq(ether, bp, 1);	ether->inpackets++;	outs(port + MmuCmd, McRelease);} static voidtxerror(Ether* ether){	int port;	Smc91xx* ctlr;	int save_pkt;	int pktno, status; 	/* assumes ctlr is locked and bank 2 is selected */	/* leaves bank 2 selected on return */	port = ether->port;	ctlr = ether->ctlr; 	save_pkt = inb(port + PktNo); 	pktno = ins(port + FifoPorts) & FpTxMask;	outb(port + PktNo, pktno);	outs(port + Pointer, PtrAutoInc | PtrRead);	status = ins(port + Data1);		if (status & TsLostCar)		ctlr->lcar++; 	if (status & TsLatCol)		ctlr->lcol++; 	if (status & Ts16Col)		ctlr->scol++; 	ether->oerrs++;		SELECT_BANK(0);	outs(port + Tcr, ins(port + Tcr) | TcrEnable);		SELECT_BANK(2);	outs(port + MmuCmd, McFreePkt); 	outb(port + PktNo, save_pkt);} static voideph_irq(Ether* ether){	int port;	Smc91xx* ctlr;	ushort status;	int n; 	/* assumes ctlr is locked and bank 2 is selected */	/* leaves bank 2 selected on return */	port = ether->port;	ctlr = ether->ctlr; 	SELECT_BANK(0);	status = ins(port + Eph); 	if (status & EphCntRol) {		/* read the counter register even if we don't need it */		/* otherwise we will keep getting this interrupt */		n = ins(port + Counter);		ctlr->col += (n & CntColMask) >> CntColShr;		ctlr->mcol += (n & CntMColMask) >> CntMColShr;		ctlr->dfr += (n & CntDtxMask) >> CntDtxShr;	} 	/* if there was a transmit error, Tcr is disabled */	outs(port + Tcr, ins(port + Tcr) | TcrEnable); 	/* clear a link error interrupt */	SELECT_BANK(1);	outs(port + Control, CtlAutoRls);	outs(port + Control, CtlAutoRls | CtlTeEnable | CtlCrEnable); 	SELECT_BANK(2);} static voidtransmit(Ether* ether){	Smc91xx* ctlr;	int port, n; 	ctlr = ether->ctlr;	port = ether->port;	ilock(ctlr); 	if (ctlr->txbp) {		n = TK2MS(MACHP(0)->ticks - ctlr->txtime);

Lines 1–500 of 781.