xref: /plan9/sys/src/9/pc/ethersmc.c (revision 6520663fb7ebac8d17e4a1dbc55d10bf525f7b14)
180ee5cbfSDavid du Colombier /*
280ee5cbfSDavid du Colombier  * SMC EtherEZ (SMC91cXX chip) PCMCIA card support.
380ee5cbfSDavid du Colombier  */
480ee5cbfSDavid du Colombier 
580ee5cbfSDavid du Colombier #include "u.h"
680ee5cbfSDavid du Colombier #include "../port/lib.h"
780ee5cbfSDavid du Colombier #include "mem.h"
880ee5cbfSDavid du Colombier #include "dat.h"
980ee5cbfSDavid du Colombier #include "fns.h"
1080ee5cbfSDavid du Colombier #include "io.h"
1180ee5cbfSDavid du Colombier #include "../port/error.h"
1280ee5cbfSDavid du Colombier #include "../port/netif.h"
1380ee5cbfSDavid du Colombier #include "etherif.h"
1480ee5cbfSDavid du Colombier 
1580ee5cbfSDavid du Colombier enum {
1680ee5cbfSDavid du Colombier 	IoSize		= 0x10,		/* port pool size */
1780ee5cbfSDavid du Colombier 	TxTimeout	= 150,
1880ee5cbfSDavid du Colombier };
1980ee5cbfSDavid du Colombier 
2080ee5cbfSDavid du Colombier enum {	/* PCMCIA related */
2180ee5cbfSDavid du Colombier 	TupleFunce	= 0x22,
2280ee5cbfSDavid du Colombier 	TfNodeId	= 0x04,
2380ee5cbfSDavid du Colombier };
2480ee5cbfSDavid du Colombier 
2580ee5cbfSDavid du Colombier enum {	/* bank 0 registers */
2680ee5cbfSDavid du Colombier 	Tcr		= 0x0000,	/* transmit control */
2780ee5cbfSDavid du Colombier 	Eph		= 0x0002,	/* ethernet protocol handler */
2880ee5cbfSDavid du Colombier 	Rcr		= 0x0004,	/* receiver control */
2980ee5cbfSDavid du Colombier 	Counter		= 0x0006,	/* statistics counter */
3080ee5cbfSDavid du Colombier 	MemInfo		= 0x0008,
3180ee5cbfSDavid du Colombier 	MemCfg		= 0x000A,
3280ee5cbfSDavid du Colombier };
3380ee5cbfSDavid du Colombier 
3480ee5cbfSDavid du Colombier enum {	/* bank 1 registers */
3580ee5cbfSDavid du Colombier 	Config		= 0x0000,
3680ee5cbfSDavid du Colombier 	BaseAddr	= 0x0002,
3780ee5cbfSDavid du Colombier 	Addr0		= 0x0004,	/* ethernet address */
3880ee5cbfSDavid du Colombier 	Addr1		= 0x0006,
3980ee5cbfSDavid du Colombier 	Addr2		= 0x0008,
4080ee5cbfSDavid du Colombier 	General		= 0x000A,
4180ee5cbfSDavid du Colombier 	Control		= 0x000C,
4280ee5cbfSDavid du Colombier };
4380ee5cbfSDavid du Colombier 
4480ee5cbfSDavid du Colombier enum {	/* bank 2 registers */
4580ee5cbfSDavid du Colombier 	MmuCmd		= 0x0000,
4680ee5cbfSDavid du Colombier 	PktNo		= 0x0002,
4780ee5cbfSDavid du Colombier 	AllocRes	= 0x0003,
4880ee5cbfSDavid du Colombier 	FifoPorts	= 0x0004,
4980ee5cbfSDavid du Colombier 	Pointer		= 0x0006,
5080ee5cbfSDavid du Colombier 	Data1		= 0x0008,
5180ee5cbfSDavid du Colombier 	Interrupt	= 0x000C,
5280ee5cbfSDavid du Colombier 	IntrMask	= 0x000D,
5380ee5cbfSDavid du Colombier };
5480ee5cbfSDavid du Colombier 
5580ee5cbfSDavid du Colombier enum {	/* bank 3 registers */
5680ee5cbfSDavid du Colombier 	Mcast0		= 0x0000,
5780ee5cbfSDavid du Colombier 	Mcast2		= 0x0002,
5880ee5cbfSDavid du Colombier 	Mcast4		= 0x0004,
5980ee5cbfSDavid du Colombier 	Mcast6		= 0x0006,
6080ee5cbfSDavid du Colombier 	Revision	= 0x000A,
6180ee5cbfSDavid du Colombier };
6280ee5cbfSDavid du Colombier 
6380ee5cbfSDavid du Colombier enum {
6480ee5cbfSDavid du Colombier 	BankSelect	= 0x000E	/* bank select register */
6580ee5cbfSDavid du Colombier };
6680ee5cbfSDavid du Colombier 
6780ee5cbfSDavid du Colombier enum {
6880ee5cbfSDavid du Colombier 	BsrMask		= 0xFF00,	/* mask for chip identification */
6980ee5cbfSDavid du Colombier 	BsrId		= 0x3300,
7080ee5cbfSDavid du Colombier };
7180ee5cbfSDavid du Colombier 
7280ee5cbfSDavid du Colombier 
7380ee5cbfSDavid du Colombier enum {	/* Tcr values */
7480ee5cbfSDavid du Colombier 	TcrClear	= 0x0000,
7580ee5cbfSDavid du Colombier 	TcrEnable	= 0x0001,	/* enable transmit */
7680ee5cbfSDavid du Colombier 	TcrLoop		= 0x0002,	/* enable internal analogue loopback */
7780ee5cbfSDavid du Colombier 	TcrForceCol	= 0x0004,	/* force collision on next tx */
7880ee5cbfSDavid du Colombier 	TcrPadEn	= 0x0080,	/* pad short packets to 64 bytes */
7980ee5cbfSDavid du Colombier 	TcrNoCrc	= 0x0100,	/* do not append CRC */
8080ee5cbfSDavid du Colombier 	TcrMonCns	= 0x0400,	/* monitor carrier status */
8180ee5cbfSDavid du Colombier 	TcrFduplx	= 0x0800,
8280ee5cbfSDavid du Colombier 	TcrStpSqet	= 0x1000,
8380ee5cbfSDavid du Colombier 	TcrEphLoop	= 0x2000,
8480ee5cbfSDavid du Colombier 	TcrNormal	= TcrEnable,
8580ee5cbfSDavid du Colombier };
8680ee5cbfSDavid du Colombier 
8780ee5cbfSDavid du Colombier enum {	/* Eph values */
8880ee5cbfSDavid du Colombier 	EphTxOk		= 0x0001,
8980ee5cbfSDavid du Colombier 	Eph1Col		= 0x0002,	/* single collision */
9080ee5cbfSDavid du Colombier 	EphMCol		= 0x0004,	/* multiple collisions */
9180ee5cbfSDavid du Colombier 	EphTxMcast	= 0x0008,	/* multicast transmit */
9280ee5cbfSDavid du Colombier 	Eph16Col	= 0x0010,	/* 16 collisions, tx disabled */
9380ee5cbfSDavid du Colombier 	EphSqet		= 0x0020,	/* SQE test failed, tx disabled */
9480ee5cbfSDavid du Colombier 	EphTxBcast	= 0x0040,	/* broadcast tx */
9580ee5cbfSDavid du Colombier 	EphDefr		= 0x0080,	/* deffered tx */
9680ee5cbfSDavid du Colombier 	EphLatCol	= 0x0200,	/* late collision, tx disabled */
9780ee5cbfSDavid du Colombier 	EphLostCarr	= 0x0400,	/* lost carrier, tx disabled */
9880ee5cbfSDavid du Colombier 	EphExcDefr	= 0x0800,	/* excessive defferals */
9980ee5cbfSDavid du Colombier 	EphCntRol	= 0x1000,	/* ECR counter(s) rolled over */
10080ee5cbfSDavid du Colombier 	EphRxOvrn	= 0x2000,	/* receiver overrun, packets dropped */
10180ee5cbfSDavid du Colombier 	EphLinkOk	= 0x4000,
10280ee5cbfSDavid du Colombier 	EphTxUnrn	= 0x8000,	/* tx underrun */
10380ee5cbfSDavid du Colombier };
10480ee5cbfSDavid du Colombier 
10580ee5cbfSDavid du Colombier enum {	/* Rcr values */
10680ee5cbfSDavid du Colombier 	RcrClear	= 0x0000,
10780ee5cbfSDavid du Colombier 	RcrPromisc	= 0x0002,
10880ee5cbfSDavid du Colombier 	RcrAllMcast	= 0x0004,
10980ee5cbfSDavid du Colombier 	RcrEnable	= 0x0100,
11080ee5cbfSDavid du Colombier 	RcrStripCrc	= 0x0200,
11180ee5cbfSDavid du Colombier 	RcrSoftReset	= 0x8000,
11280ee5cbfSDavid du Colombier 	RcrNormal	= RcrStripCrc | RcrEnable,
11380ee5cbfSDavid du Colombier };
11480ee5cbfSDavid du Colombier 
11580ee5cbfSDavid du Colombier enum { /* Counter value masks */
11680ee5cbfSDavid du Colombier 	CntColMask	= 0x000F,	/* collisions */
11780ee5cbfSDavid du Colombier 	CntMColMask	= 0x00F0,	/* multiple collisions */
11880ee5cbfSDavid du Colombier 	CntDtxMask	= 0x0F00,	/* deferred transmits */
11980ee5cbfSDavid du Colombier 	CntExDtxMask	= 0xF000,	/* excessively deferred transmits */
12080ee5cbfSDavid du Colombier 
12180ee5cbfSDavid du Colombier 	CntColShr	= 1,
12280ee5cbfSDavid du Colombier 	CntMColShr	= 4,
12380ee5cbfSDavid du Colombier 	CntDtxShr	= 8,
12480ee5cbfSDavid du Colombier };
12580ee5cbfSDavid du Colombier 
12680ee5cbfSDavid du Colombier enum { /* MemInfo value masks */
12780ee5cbfSDavid du Colombier 	MirTotalMask	= 0x00FF,
12880ee5cbfSDavid du Colombier 	MirFreeMask	= 0xFF00,
12980ee5cbfSDavid du Colombier };
13080ee5cbfSDavid du Colombier 
13180ee5cbfSDavid du Colombier enum {	/* Config values */
13280ee5cbfSDavid du Colombier 	CfgIrqSel0	= 0x0002,
13380ee5cbfSDavid du Colombier 	CfgIrqSel1	= 0x0004,
13480ee5cbfSDavid du Colombier 	CfgDisLink	= 0x0040,	/* disable 10BaseT link test */
13580ee5cbfSDavid du Colombier 	Cfg16Bit	= 0x0080,
13680ee5cbfSDavid du Colombier 	CfgAuiSelect	= 0x0100,
13780ee5cbfSDavid du Colombier 	CfgSetSqlch	= 0x0200,
13880ee5cbfSDavid du Colombier 	CfgFullStep	= 0x0400,
13980ee5cbfSDavid du Colombier 	CfgNoWait	= 0x1000,
14080ee5cbfSDavid du Colombier 	CfgMiiSelect	= 0x8000,
14180ee5cbfSDavid du Colombier };
14280ee5cbfSDavid du Colombier 
14380ee5cbfSDavid du Colombier enum {	/* Control values */
14480ee5cbfSDavid du Colombier 	CtlStore	= 0x0001,	/* store to EEPROM */
14580ee5cbfSDavid du Colombier 	CtlReload	= 0x0002,	/* reload EEPROM into registers */
14680ee5cbfSDavid du Colombier 	CtlEeSelect	= 0x0004,	/* select registers for reload/store */
14780ee5cbfSDavid du Colombier 	CtlTeEnable	= 0x0020,	/* tx error detection via eph irq */
14880ee5cbfSDavid du Colombier 	CtlCrEnable	= 0x0040,	/* counter rollover via eph irq */
14980ee5cbfSDavid du Colombier 	CtlLeEnable	= 0x0080,	/* link error detection via eph irq*/
15080ee5cbfSDavid du Colombier 	CtlAutoRls	= 0x0800,	/* auto release mode */
15180ee5cbfSDavid du Colombier 	CtlPowerDn	= 0x2000,
15280ee5cbfSDavid du Colombier };
15380ee5cbfSDavid du Colombier 
15480ee5cbfSDavid du Colombier enum {	/* MmuCmd values */
15580ee5cbfSDavid du Colombier 	McBusy		= 0x0001,
15680ee5cbfSDavid du Colombier 	McAlloc		= 0x0020,	/* | with number of 256 byte packets - 1 */
15780ee5cbfSDavid du Colombier 	McReset		= 0x0040,
15880ee5cbfSDavid du Colombier 	McRelease	= 0x0080,	/* dequeue (but not free) current rx packet */
15980ee5cbfSDavid du Colombier 	McFreePkt	= 0x00A0,	/* dequeue and free current rx packet */
16080ee5cbfSDavid du Colombier 	McEnqueue	= 0x00C0,	/* enqueue the packet for tx */
16180ee5cbfSDavid du Colombier 	McTxReset	= 0x00E0,	/* reset transmit queues */
16280ee5cbfSDavid du Colombier };
16380ee5cbfSDavid du Colombier 
16480ee5cbfSDavid du Colombier enum { /* AllocRes values */
16580ee5cbfSDavid du Colombier 	ArFailed	= 0x80,
16680ee5cbfSDavid du Colombier };
16780ee5cbfSDavid du Colombier 
16880ee5cbfSDavid du Colombier enum {	/* FifoPorts values */
16980ee5cbfSDavid du Colombier 	FpTxEmpty	= 0x0080,
17080ee5cbfSDavid du Colombier 	FpRxEmpty	= 0x8000,
17180ee5cbfSDavid du Colombier 	FpTxMask	= 0x007F,
17280ee5cbfSDavid du Colombier 	FpRxMask	= 0x7F00,
17380ee5cbfSDavid du Colombier };
17480ee5cbfSDavid du Colombier 
17580ee5cbfSDavid du Colombier enum {	/* Pointer values */
17680ee5cbfSDavid du Colombier 	PtrRead		= 0x2000,
17780ee5cbfSDavid du Colombier 	PtrAutoInc	= 0x4000,
17880ee5cbfSDavid du Colombier 	PtrRcv		= 0x8000,
17980ee5cbfSDavid du Colombier };
18080ee5cbfSDavid du Colombier 
18180ee5cbfSDavid du Colombier enum {	/* Interrupt values */
18280ee5cbfSDavid du Colombier 	IntRcv		= 0x0001,
18380ee5cbfSDavid du Colombier 	IntTxError	= 0x0002,
18480ee5cbfSDavid du Colombier 	IntTxEmpty	= 0x0004,
18580ee5cbfSDavid du Colombier 	IntAlloc	= 0x0008,
18680ee5cbfSDavid du Colombier 	IntRxOvrn	= 0x0010,
18780ee5cbfSDavid du Colombier 	IntEph		= 0x0020,
18880ee5cbfSDavid du Colombier };
18980ee5cbfSDavid du Colombier 
19080ee5cbfSDavid du Colombier enum { /* transmit status bits */
19180ee5cbfSDavid du Colombier 	TsSuccess	= 0x0001,
19280ee5cbfSDavid du Colombier 	Ts16Col		= 0x00A0,
19380ee5cbfSDavid du Colombier 	TsLatCol	= 0x0200,
19480ee5cbfSDavid du Colombier 	TsLostCar	= 0x0400,
19580ee5cbfSDavid du Colombier };
19680ee5cbfSDavid du Colombier 
19780ee5cbfSDavid du Colombier enum { /* receive status bits */
19880ee5cbfSDavid du Colombier 	RsMcast		= 0x0001,
19980ee5cbfSDavid du Colombier 	RsTooShort	= 0x0400,
20080ee5cbfSDavid du Colombier 	RsTooLong	= 0x0800,
20180ee5cbfSDavid du Colombier 	RsOddFrame	= 0x1000,
20280ee5cbfSDavid du Colombier 	RsBadCrc	= 0x2000,
20380ee5cbfSDavid du Colombier 	RsAlgnErr	= 0x8000,
20480ee5cbfSDavid du Colombier 	RsError		= RsAlgnErr | RsBadCrc | RsTooLong | RsTooShort,
20580ee5cbfSDavid du Colombier };
20680ee5cbfSDavid du Colombier 
20780ee5cbfSDavid du Colombier enum {
20880ee5cbfSDavid du Colombier 	RxLenMask	= 0x07FF,	/* significant rx len bits */
20980ee5cbfSDavid du Colombier 	HdrSize		= 6,		/* packet header length */
21080ee5cbfSDavid du Colombier 	PageSize	= 256,		/* page length */
21180ee5cbfSDavid du Colombier };
21280ee5cbfSDavid du Colombier 
21359c21d95SDavid du Colombier typedef struct Smc91xx Smc91xx;
21459c21d95SDavid du Colombier struct Smc91xx {
21580ee5cbfSDavid du Colombier 	Lock;
21680ee5cbfSDavid du Colombier 	ushort rev;
21780ee5cbfSDavid du Colombier 	int attached;
21880ee5cbfSDavid du Colombier 	Block *txbp;
21980ee5cbfSDavid du Colombier 	ulong txtime;
22080ee5cbfSDavid du Colombier 
22180ee5cbfSDavid du Colombier 	ulong rovrn;
22280ee5cbfSDavid du Colombier 	ulong lcar;
22380ee5cbfSDavid du Colombier 	ulong col;
22480ee5cbfSDavid du Colombier 	ulong scol;
22580ee5cbfSDavid du Colombier 	ulong mcol;
22680ee5cbfSDavid du Colombier 	ulong lcol;
22780ee5cbfSDavid du Colombier 	ulong dfr;
22859c21d95SDavid du Colombier };
22980ee5cbfSDavid du Colombier 
23080ee5cbfSDavid du Colombier #define SELECT_BANK(x) outs(port + BankSelect, x)
23180ee5cbfSDavid du Colombier 
23280ee5cbfSDavid du Colombier static int
readnodeid(int slot,Ether * ether)23380ee5cbfSDavid du Colombier readnodeid(int slot, Ether* ether)
23480ee5cbfSDavid du Colombier {
23580ee5cbfSDavid du Colombier 	uchar data[Eaddrlen + 1];
23680ee5cbfSDavid du Colombier 	int len;
23780ee5cbfSDavid du Colombier 
23880ee5cbfSDavid du Colombier 	len = sizeof(data);
23980ee5cbfSDavid du Colombier 	if (pcmcistuple(slot, TupleFunce, TfNodeId, data, len) != len)
24080ee5cbfSDavid du Colombier 		return -1;
24180ee5cbfSDavid du Colombier 
24280ee5cbfSDavid du Colombier 	if (data[0] != Eaddrlen)
24380ee5cbfSDavid du Colombier 		return -1;
24480ee5cbfSDavid du Colombier 
24580ee5cbfSDavid du Colombier 	memmove(ether->ea, &data[1], Eaddrlen);
24680ee5cbfSDavid du Colombier 	return 0;
24780ee5cbfSDavid du Colombier }
24880ee5cbfSDavid du Colombier 
24980ee5cbfSDavid du Colombier static void
chipreset(Ether * ether)25080ee5cbfSDavid du Colombier chipreset(Ether* ether)
25180ee5cbfSDavid du Colombier {
25280ee5cbfSDavid du Colombier 	int port;
25380ee5cbfSDavid du Colombier 	int i;
25480ee5cbfSDavid du Colombier 
25580ee5cbfSDavid du Colombier 	port = ether->port;
25680ee5cbfSDavid du Colombier 
25780ee5cbfSDavid du Colombier 	/* reset the chip */
25880ee5cbfSDavid du Colombier 	SELECT_BANK(0);
25980ee5cbfSDavid du Colombier 	outs(port + Rcr, RcrSoftReset);
26080ee5cbfSDavid du Colombier 	delay(1);
26180ee5cbfSDavid du Colombier 	outs(port + Rcr, RcrClear);
26280ee5cbfSDavid du Colombier 	outs(port + Tcr, TcrClear);
26380ee5cbfSDavid du Colombier 	SELECT_BANK(1);
26480ee5cbfSDavid du Colombier 	outs(port + Control, CtlAutoRls | CtlTeEnable |
26580ee5cbfSDavid du Colombier 		CtlCrEnable);
26680ee5cbfSDavid du Colombier 
26780ee5cbfSDavid du Colombier 	for(i = 0; i < 6; i++) {
26880ee5cbfSDavid du Colombier 		outb(port + Addr0 +  i, ether->ea[i]);
26980ee5cbfSDavid du Colombier 	}
27080ee5cbfSDavid du Colombier 
27180ee5cbfSDavid du Colombier 	SELECT_BANK(2);
27280ee5cbfSDavid du Colombier 	outs(port + MmuCmd, McReset);
27380ee5cbfSDavid du Colombier }
27480ee5cbfSDavid du Colombier 
27580ee5cbfSDavid du Colombier static void
chipenable(Ether * ether)27680ee5cbfSDavid du Colombier chipenable(Ether* ether)
27780ee5cbfSDavid du Colombier {
27880ee5cbfSDavid du Colombier 	int port;
27980ee5cbfSDavid du Colombier 
28080ee5cbfSDavid du Colombier 	port = ether->port;
28180ee5cbfSDavid du Colombier 	SELECT_BANK(0);
28280ee5cbfSDavid du Colombier 	outs(port + Tcr, TcrNormal);
28380ee5cbfSDavid du Colombier 	outs(port + Rcr, RcrNormal);
28480ee5cbfSDavid du Colombier 	SELECT_BANK(2);
28580ee5cbfSDavid du Colombier 	outb(port + IntrMask, IntEph | IntRxOvrn | IntRcv);
28680ee5cbfSDavid du Colombier }
28780ee5cbfSDavid du Colombier 
28880ee5cbfSDavid du Colombier static void
attach(Ether * ether)28980ee5cbfSDavid du Colombier attach(Ether *ether)
29080ee5cbfSDavid du Colombier {
29180ee5cbfSDavid du Colombier 	Smc91xx* ctlr;
29280ee5cbfSDavid du Colombier 
29380ee5cbfSDavid du Colombier 	ctlr = ether->ctlr;
29480ee5cbfSDavid du Colombier 	ilock(ctlr);
29580ee5cbfSDavid du Colombier 
29680ee5cbfSDavid du Colombier 	if (ctlr->attached) {
29780ee5cbfSDavid du Colombier 		iunlock(ctlr);
29880ee5cbfSDavid du Colombier 		return;
29980ee5cbfSDavid du Colombier 	}
30080ee5cbfSDavid du Colombier 
30180ee5cbfSDavid du Colombier 	chipenable(ether);
30280ee5cbfSDavid du Colombier 	ctlr->attached = 1;
30380ee5cbfSDavid du Colombier 	iunlock(ctlr);
30480ee5cbfSDavid du Colombier }
30580ee5cbfSDavid du Colombier 
30680ee5cbfSDavid du Colombier static void
txstart(Ether * ether)30780ee5cbfSDavid du Colombier txstart(Ether* ether)
30880ee5cbfSDavid du Colombier {
30980ee5cbfSDavid du Colombier 	int port;
31080ee5cbfSDavid du Colombier 	Smc91xx* ctlr;
31180ee5cbfSDavid du Colombier 	Block* bp;
31280ee5cbfSDavid du Colombier 	int len, npages;
31380ee5cbfSDavid du Colombier 	int pno;
31480ee5cbfSDavid du Colombier 
31580ee5cbfSDavid du Colombier 	/* assumes ctlr is locked and bank 2 is selected */
31680ee5cbfSDavid du Colombier 	/* leaves bank 2 selected on return */
31780ee5cbfSDavid du Colombier 	port = ether->port;
31880ee5cbfSDavid du Colombier 	ctlr = ether->ctlr;
31980ee5cbfSDavid du Colombier 
32080ee5cbfSDavid du Colombier 	if (ctlr->txbp) {
32180ee5cbfSDavid du Colombier 		bp = ctlr->txbp;
32280ee5cbfSDavid du Colombier 		ctlr->txbp = 0;
32380ee5cbfSDavid du Colombier 	} else {
32480ee5cbfSDavid du Colombier 		bp = qget(ether->oq);
32580ee5cbfSDavid du Colombier 		if (bp == 0)
32680ee5cbfSDavid du Colombier 			return;
32780ee5cbfSDavid du Colombier 
32880ee5cbfSDavid du Colombier 		len = BLEN(bp);
32980ee5cbfSDavid du Colombier 		npages = (len + HdrSize) / PageSize;
33080ee5cbfSDavid du Colombier 		outs(port + MmuCmd, McAlloc | npages);
33180ee5cbfSDavid du Colombier 	}
33280ee5cbfSDavid du Colombier 
33380ee5cbfSDavid du Colombier 	pno = inb(port + AllocRes);
33480ee5cbfSDavid du Colombier 	if (pno & ArFailed) {
33580ee5cbfSDavid du Colombier 		outb(port + IntrMask, inb(port + IntrMask) | IntAlloc);
33680ee5cbfSDavid du Colombier 		ctlr->txbp = bp;
33780ee5cbfSDavid du Colombier 		ctlr->txtime = MACHP(0)->ticks;
33880ee5cbfSDavid du Colombier 		return;
33980ee5cbfSDavid du Colombier 	}
34080ee5cbfSDavid du Colombier 
34180ee5cbfSDavid du Colombier 	outb(port + PktNo, pno);
34280ee5cbfSDavid du Colombier 	outs(port + Pointer, PtrAutoInc);
34380ee5cbfSDavid du Colombier 
34480ee5cbfSDavid du Colombier 	len = BLEN(bp);
34580ee5cbfSDavid du Colombier 	outs(port + Data1, 0);
34680ee5cbfSDavid du Colombier 	outb(port + Data1, (len + HdrSize) & 0xFF);
34780ee5cbfSDavid du Colombier 	outb(port + Data1, (len + HdrSize) >> 8);
34880ee5cbfSDavid du Colombier 	outss(port + Data1, bp->rp, len / 2);
34980ee5cbfSDavid du Colombier 	if ((len & 1) == 0) {
35080ee5cbfSDavid du Colombier 		outs(port + Data1, 0);
35180ee5cbfSDavid du Colombier 	} else {
35280ee5cbfSDavid du Colombier 		outb(port + Data1, bp->rp[len - 1]);
35380ee5cbfSDavid du Colombier 		outb(port + Data1, 0x20);	/* no info what 0x20 means */
35480ee5cbfSDavid du Colombier 	}
35580ee5cbfSDavid du Colombier 
35680ee5cbfSDavid du Colombier 	outb(port + IntrMask, inb(port + IntrMask) |
35780ee5cbfSDavid du Colombier 			IntTxError | IntTxEmpty);
35880ee5cbfSDavid du Colombier 
35980ee5cbfSDavid du Colombier 	outs(port + MmuCmd, McEnqueue);
36080ee5cbfSDavid du Colombier 	freeb(bp);
36180ee5cbfSDavid du Colombier }
36280ee5cbfSDavid du Colombier 
36380ee5cbfSDavid du Colombier static void
receive(Ether * ether)36480ee5cbfSDavid du Colombier receive(Ether* ether)
36580ee5cbfSDavid du Colombier {
36680ee5cbfSDavid du Colombier 	int port;
36780ee5cbfSDavid du Colombier 	Block* bp;
36880ee5cbfSDavid du Colombier 	int pktno, status, len;
36980ee5cbfSDavid du Colombier 
37080ee5cbfSDavid du Colombier 	/* assumes ctlr is locked and bank 2 is selected */
37180ee5cbfSDavid du Colombier 	/* leaves bank 2 selected on return */
37280ee5cbfSDavid du Colombier 	port = ether->port;
37380ee5cbfSDavid du Colombier 
37480ee5cbfSDavid du Colombier 	pktno = ins(port + FifoPorts);
37580ee5cbfSDavid du Colombier 	if (pktno & FpRxEmpty) {
37680ee5cbfSDavid du Colombier 		return;
37780ee5cbfSDavid du Colombier 	}
37880ee5cbfSDavid du Colombier 
37980ee5cbfSDavid du Colombier 	outs(port + Pointer, PtrRead | PtrRcv | PtrAutoInc);
38080ee5cbfSDavid du Colombier 	status = ins(port + Data1);
38180ee5cbfSDavid du Colombier 	len = ins(port + Data1) & RxLenMask - HdrSize;
38280ee5cbfSDavid du Colombier 
38380ee5cbfSDavid du Colombier 	if (status & RsOddFrame)
38480ee5cbfSDavid du Colombier 		len++;
38580ee5cbfSDavid du Colombier 
38680ee5cbfSDavid du Colombier 	if ((status & RsError) || (bp = iallocb(len)) == 0) {
38780ee5cbfSDavid du Colombier 
38880ee5cbfSDavid du Colombier 		if (status & RsAlgnErr)
38980ee5cbfSDavid du Colombier 			ether->frames++;
39080ee5cbfSDavid du Colombier 		if (status & (RsTooShort | RsTooLong))
39180ee5cbfSDavid du Colombier 			ether->buffs++;
39280ee5cbfSDavid du Colombier 		if (status & RsBadCrc)
39380ee5cbfSDavid du Colombier 			ether->crcs++;
39480ee5cbfSDavid du Colombier 
39580ee5cbfSDavid du Colombier 		outs(port + MmuCmd, McRelease);
39680ee5cbfSDavid du Colombier 		return;
39780ee5cbfSDavid du Colombier 	}
39880ee5cbfSDavid du Colombier 
39980ee5cbfSDavid du Colombier 	/* packet length is padded to word */
40080ee5cbfSDavid du Colombier 	inss(port + Data1, bp->rp, len / 2);
40180ee5cbfSDavid du Colombier 	bp->wp = bp->rp + (len & ~1);
40280ee5cbfSDavid du Colombier 
40380ee5cbfSDavid du Colombier 	if (len & 1) {
40480ee5cbfSDavid du Colombier 		*bp->wp = inb(port + Data1);
40580ee5cbfSDavid du Colombier 		bp->wp++;
40680ee5cbfSDavid du Colombier 	}
40780ee5cbfSDavid du Colombier 
40880ee5cbfSDavid du Colombier 	etheriq(ether, bp, 1);
40980ee5cbfSDavid du Colombier 	ether->inpackets++;
41080ee5cbfSDavid du Colombier 	outs(port + MmuCmd, McRelease);
41180ee5cbfSDavid du Colombier }
41280ee5cbfSDavid du Colombier 
41380ee5cbfSDavid du Colombier static void
txerror(Ether * ether)41480ee5cbfSDavid du Colombier txerror(Ether* ether)
41580ee5cbfSDavid du Colombier {
41680ee5cbfSDavid du Colombier 	int port;
41780ee5cbfSDavid du Colombier 	Smc91xx* ctlr;
41880ee5cbfSDavid du Colombier 	int save_pkt;
41980ee5cbfSDavid du Colombier 	int pktno, status;
42080ee5cbfSDavid du Colombier 
42180ee5cbfSDavid du Colombier 	/* assumes ctlr is locked and bank 2 is selected */
42280ee5cbfSDavid du Colombier 	/* leaves bank 2 selected on return */
42380ee5cbfSDavid du Colombier 	port = ether->port;
42480ee5cbfSDavid du Colombier 	ctlr = ether->ctlr;
42580ee5cbfSDavid du Colombier 
42680ee5cbfSDavid du Colombier 	save_pkt = inb(port + PktNo);
42780ee5cbfSDavid du Colombier 
42880ee5cbfSDavid du Colombier 	pktno = ins(port + FifoPorts) & FpTxMask;
42980ee5cbfSDavid du Colombier 	outb(port + PktNo, pktno);
43080ee5cbfSDavid du Colombier 	outs(port + Pointer, PtrAutoInc | PtrRead);
43180ee5cbfSDavid du Colombier 	status = ins(port + Data1);
43280ee5cbfSDavid du Colombier 
43380ee5cbfSDavid du Colombier 	if (status & TsLostCar)
43480ee5cbfSDavid du Colombier 		ctlr->lcar++;
43580ee5cbfSDavid du Colombier 
43680ee5cbfSDavid du Colombier 	if (status & TsLatCol)
43780ee5cbfSDavid du Colombier 		ctlr->lcol++;
43880ee5cbfSDavid du Colombier 
43980ee5cbfSDavid du Colombier 	if (status & Ts16Col)
44080ee5cbfSDavid du Colombier 		ctlr->scol++;
44180ee5cbfSDavid du Colombier 
44280ee5cbfSDavid du Colombier 	ether->oerrs++;
44380ee5cbfSDavid du Colombier 
44480ee5cbfSDavid du Colombier 	SELECT_BANK(0);
44580ee5cbfSDavid du Colombier 	outs(port + Tcr, ins(port + Tcr) | TcrEnable);
44680ee5cbfSDavid du Colombier 
44780ee5cbfSDavid du Colombier 	SELECT_BANK(2);
44880ee5cbfSDavid du Colombier 	outs(port + MmuCmd, McFreePkt);
44980ee5cbfSDavid du Colombier 
45080ee5cbfSDavid du Colombier 	outb(port + PktNo, save_pkt);
45180ee5cbfSDavid du Colombier }
45280ee5cbfSDavid du Colombier 
45380ee5cbfSDavid du Colombier static void
eph_irq(Ether * ether)45480ee5cbfSDavid du Colombier eph_irq(Ether* ether)
45580ee5cbfSDavid du Colombier {
45680ee5cbfSDavid du Colombier 	int port;
45780ee5cbfSDavid du Colombier 	Smc91xx* ctlr;
45880ee5cbfSDavid du Colombier 	ushort status;
45980ee5cbfSDavid du Colombier 	int n;
46080ee5cbfSDavid du Colombier 
46180ee5cbfSDavid du Colombier 	/* assumes ctlr is locked and bank 2 is selected */
46280ee5cbfSDavid du Colombier 	/* leaves bank 2 selected on return */
46380ee5cbfSDavid du Colombier 	port = ether->port;
46480ee5cbfSDavid du Colombier 	ctlr = ether->ctlr;
46580ee5cbfSDavid du Colombier 
46680ee5cbfSDavid du Colombier 	SELECT_BANK(0);
46780ee5cbfSDavid du Colombier 	status = ins(port + Eph);
46880ee5cbfSDavid du Colombier 
46980ee5cbfSDavid du Colombier 	if (status & EphCntRol) {
47080ee5cbfSDavid du Colombier 		/* read the counter register even if we don't need it */
47180ee5cbfSDavid du Colombier 		/* otherwise we will keep getting this interrupt */
47280ee5cbfSDavid du Colombier 		n = ins(port + Counter);
47380ee5cbfSDavid du Colombier 		ctlr->col += (n & CntColMask) >> CntColShr;
47480ee5cbfSDavid du Colombier 		ctlr->mcol += (n & CntMColMask) >> CntMColShr;
47580ee5cbfSDavid du Colombier 		ctlr->dfr += (n & CntDtxMask) >> CntDtxShr;
47680ee5cbfSDavid du Colombier 	}
47780ee5cbfSDavid du Colombier 
47880ee5cbfSDavid du Colombier 	/* if there was a transmit error, Tcr is disabled */
47980ee5cbfSDavid du Colombier 	outs(port + Tcr, ins(port + Tcr) | TcrEnable);
48080ee5cbfSDavid du Colombier 
48180ee5cbfSDavid du Colombier 	/* clear a link error interrupt */
48280ee5cbfSDavid du Colombier 	SELECT_BANK(1);
48380ee5cbfSDavid du Colombier 	outs(port + Control, CtlAutoRls);
48480ee5cbfSDavid du Colombier 	outs(port + Control, CtlAutoRls | CtlTeEnable | CtlCrEnable);
48580ee5cbfSDavid du Colombier 
48680ee5cbfSDavid du Colombier 	SELECT_BANK(2);
48780ee5cbfSDavid du Colombier }
48880ee5cbfSDavid du Colombier 
48980ee5cbfSDavid du Colombier static void
transmit(Ether * ether)49080ee5cbfSDavid du Colombier transmit(Ether* ether)
49180ee5cbfSDavid du Colombier {
49280ee5cbfSDavid du Colombier 	Smc91xx* ctlr;
49380ee5cbfSDavid du Colombier 	int port, n;
49480ee5cbfSDavid du Colombier 
49580ee5cbfSDavid du Colombier 	ctlr = ether->ctlr;
49680ee5cbfSDavid du Colombier 	port = ether->port;
49780ee5cbfSDavid du Colombier 	ilock(ctlr);
49880ee5cbfSDavid du Colombier 
49980ee5cbfSDavid du Colombier 	if (ctlr->txbp) {
50080ee5cbfSDavid du Colombier 		n = TK2MS(MACHP(0)->ticks - ctlr->txtime);
50180ee5cbfSDavid du Colombier 		if (n > TxTimeout) {
50280ee5cbfSDavid du Colombier 			chipreset(ether);
50380ee5cbfSDavid du Colombier 			chipenable(ether);
50480ee5cbfSDavid du Colombier 			freeb(ctlr->txbp);
50580ee5cbfSDavid du Colombier 			ctlr->txbp = 0;
50680ee5cbfSDavid du Colombier 		}
50780ee5cbfSDavid du Colombier 		iunlock(ctlr);
50880ee5cbfSDavid du Colombier 		return;
50980ee5cbfSDavid du Colombier 	}
51080ee5cbfSDavid du Colombier 
51180ee5cbfSDavid du Colombier 	SELECT_BANK(2);
51280ee5cbfSDavid du Colombier 	txstart(ether);
51380ee5cbfSDavid du Colombier 	iunlock(ctlr);
51480ee5cbfSDavid du Colombier }
51580ee5cbfSDavid du Colombier 
51680ee5cbfSDavid du Colombier static void
interrupt(Ureg *,void * arg)51780ee5cbfSDavid du Colombier interrupt(Ureg*, void *arg)
51880ee5cbfSDavid du Colombier {
51980ee5cbfSDavid du Colombier 	int port;
52080ee5cbfSDavid du Colombier 	Smc91xx* ctlr;
52180ee5cbfSDavid du Colombier 	Ether* ether;
52280ee5cbfSDavid du Colombier 	int save_bank;
52380ee5cbfSDavid du Colombier 	int save_pointer;
52480ee5cbfSDavid du Colombier 	int mask, status;
52580ee5cbfSDavid du Colombier 
52680ee5cbfSDavid du Colombier 	ether = arg;
52780ee5cbfSDavid du Colombier 	port = ether->port;
52880ee5cbfSDavid du Colombier 	ctlr = ether->ctlr;
52980ee5cbfSDavid du Colombier 
53080ee5cbfSDavid du Colombier 	ilock(ctlr);
53180ee5cbfSDavid du Colombier 	save_bank = ins(port + BankSelect);
53280ee5cbfSDavid du Colombier 	SELECT_BANK(2);
53380ee5cbfSDavid du Colombier 	save_pointer = ins(port + Pointer);
53480ee5cbfSDavid du Colombier 
53580ee5cbfSDavid du Colombier 	mask = inb(port + IntrMask);
53680ee5cbfSDavid du Colombier 	outb(port + IntrMask, 0);
53780ee5cbfSDavid du Colombier 
53880ee5cbfSDavid du Colombier 	while ((status = inb(port + Interrupt) & mask) != 0) {
53980ee5cbfSDavid du Colombier 		if (status & IntRcv) {
54080ee5cbfSDavid du Colombier 			receive(ether);
54180ee5cbfSDavid du Colombier 		}
54280ee5cbfSDavid du Colombier 
54380ee5cbfSDavid du Colombier 		if (status & IntTxError) {
54480ee5cbfSDavid du Colombier 			txerror(ether);
54580ee5cbfSDavid du Colombier 		}
54680ee5cbfSDavid du Colombier 
54780ee5cbfSDavid du Colombier 		if (status & IntTxEmpty) {
54880ee5cbfSDavid du Colombier 			outb(port + Interrupt, IntTxEmpty);
54980ee5cbfSDavid du Colombier 			outb(port + IntrMask, mask & ~IntTxEmpty);
55080ee5cbfSDavid du Colombier 			txstart(ether);
55180ee5cbfSDavid du Colombier 			mask = inb(port + IntrMask);
55280ee5cbfSDavid du Colombier 		}
55380ee5cbfSDavid du Colombier 
55480ee5cbfSDavid du Colombier 		if (status & IntAlloc) {
55580ee5cbfSDavid du Colombier 			outb(port + IntrMask, mask & ~IntAlloc);
556*6520663fSDavid du Colombier 			txstart(ether);
55780ee5cbfSDavid du Colombier 			mask = inb(port + IntrMask);
55880ee5cbfSDavid du Colombier 		}
55980ee5cbfSDavid du Colombier 
56080ee5cbfSDavid du Colombier 		if (status & IntRxOvrn) {
56180ee5cbfSDavid du Colombier 			ctlr->rovrn++;
56280ee5cbfSDavid du Colombier 			ether->misses++;
56380ee5cbfSDavid du Colombier 			outb(port + Interrupt,IntRxOvrn);
56480ee5cbfSDavid du Colombier 		}
56580ee5cbfSDavid du Colombier 
56680ee5cbfSDavid du Colombier 		if (status & IntEph)
56780ee5cbfSDavid du Colombier 			eph_irq(ether);
56880ee5cbfSDavid du Colombier 	}
56980ee5cbfSDavid du Colombier 
57080ee5cbfSDavid du Colombier 	outb(port + IntrMask, mask);
57180ee5cbfSDavid du Colombier 	outs(port + Pointer, save_pointer);
57280ee5cbfSDavid du Colombier 	outs(port + BankSelect, save_bank);
57380ee5cbfSDavid du Colombier 	iunlock(ctlr);
57480ee5cbfSDavid du Colombier }
57580ee5cbfSDavid du Colombier 
57680ee5cbfSDavid du Colombier static void
promiscuous(void * arg,int on)57780ee5cbfSDavid du Colombier promiscuous(void* arg, int on)
57880ee5cbfSDavid du Colombier {
57980ee5cbfSDavid du Colombier 	int port;
58080ee5cbfSDavid du Colombier 	Smc91xx *ctlr;
58180ee5cbfSDavid du Colombier 	Ether* ether;
58280ee5cbfSDavid du Colombier 	ushort x;
58380ee5cbfSDavid du Colombier 
58480ee5cbfSDavid du Colombier 	ether = arg;
58580ee5cbfSDavid du Colombier 	port = ether->port;
58680ee5cbfSDavid du Colombier 	ctlr = ether->ctlr;
58780ee5cbfSDavid du Colombier 
58880ee5cbfSDavid du Colombier 	ilock(ctlr);
58980ee5cbfSDavid du Colombier 	SELECT_BANK(0);
59080ee5cbfSDavid du Colombier 	x = ins(port + Rcr);
59180ee5cbfSDavid du Colombier 	if (on)
59280ee5cbfSDavid du Colombier 		x |= RcrPromisc;
59380ee5cbfSDavid du Colombier 	else
59480ee5cbfSDavid du Colombier 		x &= ~RcrPromisc;
59580ee5cbfSDavid du Colombier 
59680ee5cbfSDavid du Colombier 	outs(port + Rcr, x);
59780ee5cbfSDavid du Colombier 	iunlock(ctlr);
59880ee5cbfSDavid du Colombier }
59980ee5cbfSDavid du Colombier 
60080ee5cbfSDavid du Colombier static void
multicast(void * arg,uchar * addr,int on)60180ee5cbfSDavid du Colombier multicast(void* arg, uchar *addr, int on)
60280ee5cbfSDavid du Colombier {
60380ee5cbfSDavid du Colombier 	int port;
60480ee5cbfSDavid du Colombier 	Smc91xx*ctlr;
60580ee5cbfSDavid du Colombier 	Ether *ether;
60680ee5cbfSDavid du Colombier 	ushort x;
60780ee5cbfSDavid du Colombier 
60880ee5cbfSDavid du Colombier 	USED(addr, on);
60980ee5cbfSDavid du Colombier 
61080ee5cbfSDavid du Colombier 	ether = arg;
61180ee5cbfSDavid du Colombier 	port = ether->port;
61280ee5cbfSDavid du Colombier 	ctlr = ether->ctlr;
61380ee5cbfSDavid du Colombier 	ilock(ctlr);
61480ee5cbfSDavid du Colombier 
61580ee5cbfSDavid du Colombier 	SELECT_BANK(0);
61680ee5cbfSDavid du Colombier 	x = ins(port + Rcr);
61780ee5cbfSDavid du Colombier 
61880ee5cbfSDavid du Colombier 	if (ether->nmaddr)
61980ee5cbfSDavid du Colombier 		x |= RcrAllMcast;
62080ee5cbfSDavid du Colombier 	else
62180ee5cbfSDavid du Colombier 		x &= ~RcrAllMcast;
62280ee5cbfSDavid du Colombier 
62380ee5cbfSDavid du Colombier 	outs(port + Rcr, x);
62480ee5cbfSDavid du Colombier 	iunlock(ctlr);
62580ee5cbfSDavid du Colombier }
62680ee5cbfSDavid du Colombier 
62780ee5cbfSDavid du Colombier static long
ifstat(Ether * ether,void * a,long n,ulong offset)62880ee5cbfSDavid du Colombier ifstat(Ether* ether, void* a, long n, ulong offset)
62980ee5cbfSDavid du Colombier {
63080ee5cbfSDavid du Colombier 	static char *chiprev[] = {
63180ee5cbfSDavid du Colombier 		[3] 	"92",
63280ee5cbfSDavid du Colombier 		[5]	"95",
63380ee5cbfSDavid du Colombier 		[7]	"100",
63480ee5cbfSDavid du Colombier 		[8]	"100-FD",
63580ee5cbfSDavid du Colombier 		[9]	"110",
63680ee5cbfSDavid du Colombier 	};
63780ee5cbfSDavid du Colombier 	Smc91xx* ctlr;
638aa72973aSDavid du Colombier 	char *p, *s;
63980ee5cbfSDavid du Colombier 	int r, len;
64080ee5cbfSDavid du Colombier 
64180ee5cbfSDavid du Colombier 	if (n == 0)
64280ee5cbfSDavid du Colombier 		return 0;
64380ee5cbfSDavid du Colombier 
64480ee5cbfSDavid du Colombier 	ctlr = ether->ctlr;
64580ee5cbfSDavid du Colombier 	p = malloc(READSTR);
646aa72973aSDavid du Colombier 	if(p == nil)
647aa72973aSDavid du Colombier 		error(Enomem);
64880ee5cbfSDavid du Colombier 
64980ee5cbfSDavid du Colombier 	s = 0;
65080ee5cbfSDavid du Colombier 	if (ctlr->rev > 0) {
65180ee5cbfSDavid du Colombier 		r = ctlr->rev >> 4;
65280ee5cbfSDavid du Colombier 		if (r < nelem(chiprev))
65380ee5cbfSDavid du Colombier 			s = chiprev[r];
65480ee5cbfSDavid du Colombier 
65580ee5cbfSDavid du Colombier 		if (r == 4) {
65680ee5cbfSDavid du Colombier 			if ((ctlr->rev & 0x0F) >= 6)
65780ee5cbfSDavid du Colombier 				s = "96";
65880ee5cbfSDavid du Colombier 			else
65980ee5cbfSDavid du Colombier 				s = "94";
66080ee5cbfSDavid du Colombier 		}
66180ee5cbfSDavid du Colombier 	}
66280ee5cbfSDavid du Colombier 
66380ee5cbfSDavid du Colombier 	len = snprint(p, READSTR, "rev: 91c%s\n", (s) ? s : "???");
66480ee5cbfSDavid du Colombier 	len += snprint(p + len, READSTR - len, "rxovrn: %uld\n", ctlr->rovrn);
66580ee5cbfSDavid du Colombier 	len += snprint(p + len, READSTR - len, "lcar: %uld\n", ctlr->lcar);
66680ee5cbfSDavid du Colombier 	len += snprint(p + len, READSTR - len, "col: %uld\n", ctlr->col);
66780ee5cbfSDavid du Colombier 	len += snprint(p + len, READSTR - len, "16col: %uld\n", ctlr->scol);
66880ee5cbfSDavid du Colombier 	len += snprint(p + len, READSTR - len, "mcol: %uld\n", ctlr->mcol);
66980ee5cbfSDavid du Colombier 	len += snprint(p + len, READSTR - len, "lcol: %uld\n", ctlr->lcol);
67080ee5cbfSDavid du Colombier 	len += snprint(p + len, READSTR - len, "dfr: %uld\n", ctlr->dfr);
67180ee5cbfSDavid du Colombier 	USED(len);
67280ee5cbfSDavid du Colombier 
67380ee5cbfSDavid du Colombier 	n = readstr(offset, a, n, p);
67480ee5cbfSDavid du Colombier 	free(p);
67580ee5cbfSDavid du Colombier 
67680ee5cbfSDavid du Colombier 	return n;
67780ee5cbfSDavid du Colombier }
67880ee5cbfSDavid du Colombier 
67980ee5cbfSDavid du Colombier static int
reset(Ether * ether)68080ee5cbfSDavid du Colombier reset(Ether* ether)
68180ee5cbfSDavid du Colombier {
68280ee5cbfSDavid du Colombier 	int port;
68380ee5cbfSDavid du Colombier 	int i, x;
68480ee5cbfSDavid du Colombier 	char* type;
68580ee5cbfSDavid du Colombier 	Smc91xx* ctlr;
68680ee5cbfSDavid du Colombier 	int slot;
68780ee5cbfSDavid du Colombier 	uchar ea[Eaddrlen];
68880ee5cbfSDavid du Colombier 
68980ee5cbfSDavid du Colombier 	if (ether->irq == 0)
69080ee5cbfSDavid du Colombier 		ether->irq = 9;
69180ee5cbfSDavid du Colombier 
69280ee5cbfSDavid du Colombier 	if (ether->port == 0)
69380ee5cbfSDavid du Colombier 		ether->port = 0x100;
69480ee5cbfSDavid du Colombier 
69580ee5cbfSDavid du Colombier 	type = "8020";
69680ee5cbfSDavid du Colombier 	for(i = 0; i < ether->nopt; i++) {
69780ee5cbfSDavid du Colombier 		if (cistrncmp(ether->opt[i], "id=", 3))
69880ee5cbfSDavid du Colombier 			continue;
69980ee5cbfSDavid du Colombier 		type = &ether->opt[i][3];
70080ee5cbfSDavid du Colombier 		break;
70180ee5cbfSDavid du Colombier 	}
70280ee5cbfSDavid du Colombier 
70380ee5cbfSDavid du Colombier 	if ((slot = pcmspecial(type, ether)) < 0)
70480ee5cbfSDavid du Colombier 		return -1;
70580ee5cbfSDavid du Colombier 
70680ee5cbfSDavid du Colombier 	if (ioalloc(ether->port, IoSize, 0, "smc91cXX") < 0) {
70780ee5cbfSDavid du Colombier 		pcmspecialclose(slot);
70880ee5cbfSDavid du Colombier 		return -1;
70980ee5cbfSDavid du Colombier 	}
71080ee5cbfSDavid du Colombier 
71180ee5cbfSDavid du Colombier 	ether->ctlr = malloc(sizeof(Smc91xx));
71280ee5cbfSDavid du Colombier 	ctlr = ether->ctlr;
71380ee5cbfSDavid du Colombier 	if (ctlr == 0) {
71480ee5cbfSDavid du Colombier 		iofree(ether->port);
71580ee5cbfSDavid du Colombier 		pcmspecialclose(slot);
716aedc1c01SDavid du Colombier 		return -1;
71780ee5cbfSDavid du Colombier 	}
71880ee5cbfSDavid du Colombier 
71980ee5cbfSDavid du Colombier 	ilock(ctlr);
72080ee5cbfSDavid du Colombier 	ctlr->rev = 0;
72180ee5cbfSDavid du Colombier 	ctlr->txbp = nil;
72280ee5cbfSDavid du Colombier 	ctlr->attached = 0;
72380ee5cbfSDavid du Colombier 	ctlr->rovrn = 0;
72480ee5cbfSDavid du Colombier 	ctlr->lcar = 0;
72580ee5cbfSDavid du Colombier 	ctlr->col = 0;
72680ee5cbfSDavid du Colombier 	ctlr->scol = 0;
72780ee5cbfSDavid du Colombier 	ctlr->mcol = 0;
72880ee5cbfSDavid du Colombier 	ctlr->lcol = 0;
72980ee5cbfSDavid du Colombier 	ctlr->dfr = 0;
73080ee5cbfSDavid du Colombier 
73180ee5cbfSDavid du Colombier 	port = ether->port;
73280ee5cbfSDavid du Colombier 
73380ee5cbfSDavid du Colombier 	SELECT_BANK(1);
73480ee5cbfSDavid du Colombier 	if ((ins(port + BankSelect) & BsrMask) != BsrId) {
73580ee5cbfSDavid du Colombier 		outs(port + Control, 0);	/* try powering up the chip */
73680ee5cbfSDavid du Colombier 		delay(55);
73780ee5cbfSDavid du Colombier 	}
73880ee5cbfSDavid du Colombier 
73980ee5cbfSDavid du Colombier 	outs(port + Config, ins(port + Config) | Cfg16Bit);
74080ee5cbfSDavid du Colombier 	x = ins(port + BaseAddr);
74180ee5cbfSDavid du Colombier 
74280ee5cbfSDavid du Colombier 	if (((ins(port + BankSelect) & BsrMask) != BsrId) ||
74380ee5cbfSDavid du Colombier 		((x >> 8) == (x & 0xFF))) {
74480ee5cbfSDavid du Colombier 		iunlock(ctlr);
74580ee5cbfSDavid du Colombier 		iofree(port);
74680ee5cbfSDavid du Colombier 		pcmspecialclose(slot);
74780ee5cbfSDavid du Colombier 		return -1;
74880ee5cbfSDavid du Colombier 	}
74980ee5cbfSDavid du Colombier 
75080ee5cbfSDavid du Colombier 	SELECT_BANK(3);
75180ee5cbfSDavid du Colombier 	ctlr->rev = ins(port + Revision) & 0xFF;
75280ee5cbfSDavid du Colombier 
75380ee5cbfSDavid du Colombier 	memset(ea, 0, Eaddrlen);
75480ee5cbfSDavid du Colombier 	if (memcmp(ea, ether->ea, Eaddrlen) == 0) {
75580ee5cbfSDavid du Colombier 		if (readnodeid(slot, ether) < 0) {
75680ee5cbfSDavid du Colombier 			print("Smc91cXX: cannot find ethernet address\n");
75780ee5cbfSDavid du Colombier 			iunlock(ctlr);
75880ee5cbfSDavid du Colombier 			iofree(port);
75980ee5cbfSDavid du Colombier 			pcmspecialclose(slot);
76080ee5cbfSDavid du Colombier 			return -1;
76180ee5cbfSDavid du Colombier 		}
76280ee5cbfSDavid du Colombier 	}
76380ee5cbfSDavid du Colombier 
76480ee5cbfSDavid du Colombier 	chipreset(ether);
76580ee5cbfSDavid du Colombier 
76680ee5cbfSDavid du Colombier 	ether->attach = attach;
76780ee5cbfSDavid du Colombier 	ether->transmit = transmit;
76880ee5cbfSDavid du Colombier 	ether->interrupt = interrupt;
76980ee5cbfSDavid du Colombier 	ether->ifstat = ifstat;
77080ee5cbfSDavid du Colombier 	ether->promiscuous = promiscuous;
77180ee5cbfSDavid du Colombier 	ether->multicast = multicast;
77298bee55eSDavid du Colombier 	ether->shutdown = chipreset;
77380ee5cbfSDavid du Colombier 	ether->arg = ether;
77480ee5cbfSDavid du Colombier 	iunlock(ctlr);
77580ee5cbfSDavid du Colombier 	return 0;
77680ee5cbfSDavid du Colombier }
77780ee5cbfSDavid du Colombier 
77880ee5cbfSDavid du Colombier void
ethersmclink(void)77980ee5cbfSDavid du Colombier ethersmclink(void)
78080ee5cbfSDavid du Colombier {
78180ee5cbfSDavid du Colombier 	addethercard("smc91cXX", reset);
78280ee5cbfSDavid du Colombier }
783