xref: /plan9-contrib/sys/src/9k/386/ether82557.c (revision 9ef1f84b659abcb917c5c090acbce0772e494f21)
1*9ef1f84bSDavid du Colombier /*
2*9ef1f84bSDavid du Colombier  * Intel 82557 Fast Ethernet PCI Bus LAN Controller
3*9ef1f84bSDavid du Colombier  * as found on the Intel EtherExpress PRO/100B. This chip is full
4*9ef1f84bSDavid du Colombier  * of smarts, unfortunately they're not all in the right place.
5*9ef1f84bSDavid du Colombier  * To do:
6*9ef1f84bSDavid du Colombier  *	the PCI scanning code could be made common to other adapters;
7*9ef1f84bSDavid du Colombier  *	auto-negotiation, full-duplex;
8*9ef1f84bSDavid du Colombier  *	optionally use memory-mapped registers;
9*9ef1f84bSDavid du Colombier  *	detach for PCI reset problems (also towards loadable drivers).
10*9ef1f84bSDavid du Colombier  */
11*9ef1f84bSDavid du Colombier #include "u.h"
12*9ef1f84bSDavid du Colombier #include "../port/lib.h"
13*9ef1f84bSDavid du Colombier #include "mem.h"
14*9ef1f84bSDavid du Colombier #include "dat.h"
15*9ef1f84bSDavid du Colombier #include "fns.h"
16*9ef1f84bSDavid du Colombier #include "../port/error.h"
17*9ef1f84bSDavid du Colombier 
18*9ef1f84bSDavid du Colombier #include "../port/netif.h"
19*9ef1f84bSDavid du Colombier 
20*9ef1f84bSDavid du Colombier #include "etherif.h"
21*9ef1f84bSDavid du Colombier #include "io.h"
22*9ef1f84bSDavid du Colombier 
23*9ef1f84bSDavid du Colombier enum {
24*9ef1f84bSDavid du Colombier 	Nrfd		= 64,		/* receive frame area */
25*9ef1f84bSDavid du Colombier 	Ncb		= 64,		/* maximum control blocks queued */
26*9ef1f84bSDavid du Colombier 
27*9ef1f84bSDavid du Colombier 	NullPointer	= 0xFFFFFFFF,	/* 82557 NULL pointer */
28*9ef1f84bSDavid du Colombier };
29*9ef1f84bSDavid du Colombier 
30*9ef1f84bSDavid du Colombier enum {					/* CSR */
31*9ef1f84bSDavid du Colombier 	Status		= 0x00,		/* byte or word (word includes Ack) */
32*9ef1f84bSDavid du Colombier 	Ack		= 0x01,		/* byte */
33*9ef1f84bSDavid du Colombier 	CommandR	= 0x02,		/* byte or word (word includes Interrupt) */
34*9ef1f84bSDavid du Colombier 	Interrupt	= 0x03,		/* byte */
35*9ef1f84bSDavid du Colombier 	General		= 0x04,		/* dword */
36*9ef1f84bSDavid du Colombier 	Port		= 0x08,		/* dword */
37*9ef1f84bSDavid du Colombier 	Fcr		= 0x0C,		/* Flash control register */
38*9ef1f84bSDavid du Colombier 	Ecr		= 0x0E,		/* EEPROM control register */
39*9ef1f84bSDavid du Colombier 	Mcr		= 0x10,		/* MDI control register */
40*9ef1f84bSDavid du Colombier 	Gstatus		= 0x1D,		/* General status register */
41*9ef1f84bSDavid du Colombier };
42*9ef1f84bSDavid du Colombier 
43*9ef1f84bSDavid du Colombier enum {					/* Status */
44*9ef1f84bSDavid du Colombier 	RUidle		= 0x0000,
45*9ef1f84bSDavid du Colombier 	RUsuspended	= 0x0004,
46*9ef1f84bSDavid du Colombier 	RUnoresources	= 0x0008,
47*9ef1f84bSDavid du Colombier 	RUready		= 0x0010,
48*9ef1f84bSDavid du Colombier 	RUrbd		= 0x0020,	/* bit */
49*9ef1f84bSDavid du Colombier 	RUstatus	= 0x003F,	/* mask */
50*9ef1f84bSDavid du Colombier 
51*9ef1f84bSDavid du Colombier 	CUidle		= 0x0000,
52*9ef1f84bSDavid du Colombier 	CUsuspended	= 0x0040,
53*9ef1f84bSDavid du Colombier 	CUactive	= 0x0080,
54*9ef1f84bSDavid du Colombier 	CUstatus	= 0x00C0,	/* mask */
55*9ef1f84bSDavid du Colombier 
56*9ef1f84bSDavid du Colombier 	StatSWI		= 0x0400,	/* SoftWare generated Interrupt */
57*9ef1f84bSDavid du Colombier 	StatMDI		= 0x0800,	/* MDI r/w done */
58*9ef1f84bSDavid du Colombier 	StatRNR		= 0x1000,	/* Receive unit Not Ready */
59*9ef1f84bSDavid du Colombier 	StatCNA		= 0x2000,	/* Command unit Not Active (Active->Idle) */
60*9ef1f84bSDavid du Colombier 	StatFR		= 0x4000,	/* Finished Receiving */
61*9ef1f84bSDavid du Colombier 	StatCX		= 0x8000,	/* Command eXecuted */
62*9ef1f84bSDavid du Colombier 	StatTNO		= 0x8000,	/* Transmit NOT OK */
63*9ef1f84bSDavid du Colombier };
64*9ef1f84bSDavid du Colombier 
65*9ef1f84bSDavid du Colombier enum {					/* Command (byte) */
66*9ef1f84bSDavid du Colombier 	CUnop		= 0x00,
67*9ef1f84bSDavid du Colombier 	CUstart		= 0x10,
68*9ef1f84bSDavid du Colombier 	CUresume	= 0x20,
69*9ef1f84bSDavid du Colombier 	LoadDCA		= 0x40,		/* Load Dump Counters Address */
70*9ef1f84bSDavid du Colombier 	DumpSC		= 0x50,		/* Dump Statistical Counters */
71*9ef1f84bSDavid du Colombier 	LoadCUB		= 0x60,		/* Load CU Base */
72*9ef1f84bSDavid du Colombier 	ResetSA		= 0x70,		/* Dump and Reset Statistical Counters */
73*9ef1f84bSDavid du Colombier 
74*9ef1f84bSDavid du Colombier 	RUstart		= 0x01,
75*9ef1f84bSDavid du Colombier 	RUresume	= 0x02,
76*9ef1f84bSDavid du Colombier 	RUabort		= 0x04,
77*9ef1f84bSDavid du Colombier 	LoadHDS		= 0x05,		/* Load Header Data Size */
78*9ef1f84bSDavid du Colombier 	LoadRUB		= 0x06,		/* Load RU Base */
79*9ef1f84bSDavid du Colombier 	RBDresume	= 0x07,		/* Resume frame reception */
80*9ef1f84bSDavid du Colombier };
81*9ef1f84bSDavid du Colombier 
82*9ef1f84bSDavid du Colombier enum {					/* Interrupt (byte) */
83*9ef1f84bSDavid du Colombier 	InterruptM	= 0x01,		/* interrupt Mask */
84*9ef1f84bSDavid du Colombier 	InterruptSI	= 0x02,		/* Software generated Interrupt */
85*9ef1f84bSDavid du Colombier };
86*9ef1f84bSDavid du Colombier 
87*9ef1f84bSDavid du Colombier enum {					/* Ecr */
88*9ef1f84bSDavid du Colombier 	EEsk		= 0x01,		/* serial clock */
89*9ef1f84bSDavid du Colombier 	EEcs		= 0x02,		/* chip select */
90*9ef1f84bSDavid du Colombier 	EEdi		= 0x04,		/* serial data in */
91*9ef1f84bSDavid du Colombier 	EEdo		= 0x08,		/* serial data out */
92*9ef1f84bSDavid du Colombier 
93*9ef1f84bSDavid du Colombier 	EEstart		= 0x04,		/* start bit */
94*9ef1f84bSDavid du Colombier 	EEread		= 0x02,		/* read opcode */
95*9ef1f84bSDavid du Colombier };
96*9ef1f84bSDavid du Colombier 
97*9ef1f84bSDavid du Colombier enum {					/* Mcr */
98*9ef1f84bSDavid du Colombier 	MDIread		= 0x08000000,	/* read opcode */
99*9ef1f84bSDavid du Colombier 	MDIwrite	= 0x04000000,	/* write opcode */
100*9ef1f84bSDavid du Colombier 	MDIready	= 0x10000000,	/* ready bit */
101*9ef1f84bSDavid du Colombier 	MDIie		= 0x20000000,	/* interrupt enable */
102*9ef1f84bSDavid du Colombier };
103*9ef1f84bSDavid du Colombier 
104*9ef1f84bSDavid du Colombier typedef struct Rfd {
105*9ef1f84bSDavid du Colombier 	int	field;
106*9ef1f84bSDavid du Colombier 	ulong	link;
107*9ef1f84bSDavid du Colombier 	ulong	rbd;
108*9ef1f84bSDavid du Colombier 	ushort	count;
109*9ef1f84bSDavid du Colombier 	ushort	size;
110*9ef1f84bSDavid du Colombier 
111*9ef1f84bSDavid du Colombier 	uchar	data[1700];
112*9ef1f84bSDavid du Colombier } Rfd;
113*9ef1f84bSDavid du Colombier 
114*9ef1f84bSDavid du Colombier enum {					/* field */
115*9ef1f84bSDavid du Colombier 	RfdCollision	= 0x00000001,
116*9ef1f84bSDavid du Colombier 	RfdIA		= 0x00000002,	/* IA match */
117*9ef1f84bSDavid du Colombier 	RfdRxerr	= 0x00000010,	/* PHY character error */
118*9ef1f84bSDavid du Colombier 	RfdType		= 0x00000020,	/* Type frame */
119*9ef1f84bSDavid du Colombier 	RfdRunt		= 0x00000080,
120*9ef1f84bSDavid du Colombier 	RfdOverrun	= 0x00000100,
121*9ef1f84bSDavid du Colombier 	RfdBuffer	= 0x00000200,
122*9ef1f84bSDavid du Colombier 	RfdAlignment	= 0x00000400,
123*9ef1f84bSDavid du Colombier 	RfdCRC		= 0x00000800,
124*9ef1f84bSDavid du Colombier 
125*9ef1f84bSDavid du Colombier 	RfdOK		= 0x00002000,	/* frame received OK */
126*9ef1f84bSDavid du Colombier 	RfdC		= 0x00008000,	/* reception Complete */
127*9ef1f84bSDavid du Colombier 	RfdSF		= 0x00080000,	/* Simplified or Flexible (1) Rfd */
128*9ef1f84bSDavid du Colombier 	RfdH		= 0x00100000,	/* Header RFD */
129*9ef1f84bSDavid du Colombier 
130*9ef1f84bSDavid du Colombier 	RfdI		= 0x20000000,	/* Interrupt after completion */
131*9ef1f84bSDavid du Colombier 	RfdS		= 0x40000000,	/* Suspend after completion */
132*9ef1f84bSDavid du Colombier 	RfdEL		= 0x80000000,	/* End of List */
133*9ef1f84bSDavid du Colombier };
134*9ef1f84bSDavid du Colombier 
135*9ef1f84bSDavid du Colombier enum {					/* count */
136*9ef1f84bSDavid du Colombier 	RfdF		= 0x4000,
137*9ef1f84bSDavid du Colombier 	RfdEOF		= 0x8000,
138*9ef1f84bSDavid du Colombier };
139*9ef1f84bSDavid du Colombier 
140*9ef1f84bSDavid du Colombier typedef struct Cb Cb;
141*9ef1f84bSDavid du Colombier typedef struct Cb {
142*9ef1f84bSDavid du Colombier 	ushort	status;
143*9ef1f84bSDavid du Colombier 	ushort	command;
144*9ef1f84bSDavid du Colombier 	ulong	link;
145*9ef1f84bSDavid du Colombier 	union {
146*9ef1f84bSDavid du Colombier 		uchar	data[24];	/* CbIAS + CbConfigure */
147*9ef1f84bSDavid du Colombier 		struct {
148*9ef1f84bSDavid du Colombier 			ulong	tbd;
149*9ef1f84bSDavid du Colombier 			ushort	count;
150*9ef1f84bSDavid du Colombier 			uchar	threshold;
151*9ef1f84bSDavid du Colombier 			uchar	number;
152*9ef1f84bSDavid du Colombier 
153*9ef1f84bSDavid du Colombier 			ulong	tba;
154*9ef1f84bSDavid du Colombier 			ushort	tbasz;
155*9ef1f84bSDavid du Colombier 			ushort	pad;
156*9ef1f84bSDavid du Colombier 		};
157*9ef1f84bSDavid du Colombier 	};
158*9ef1f84bSDavid du Colombier 
159*9ef1f84bSDavid du Colombier 	Block*	bp;
160*9ef1f84bSDavid du Colombier 	Cb*	next;
161*9ef1f84bSDavid du Colombier } Cb;
162*9ef1f84bSDavid du Colombier 
163*9ef1f84bSDavid du Colombier enum {					/* action command */
164*9ef1f84bSDavid du Colombier 	CbU		= 0x1000,	/* transmit underrun */
165*9ef1f84bSDavid du Colombier 	CbOK		= 0x2000,	/* DMA completed OK */
166*9ef1f84bSDavid du Colombier 	CbC		= 0x8000,	/* execution Complete */
167*9ef1f84bSDavid du Colombier 
168*9ef1f84bSDavid du Colombier 	CbNOP		= 0x0000,
169*9ef1f84bSDavid du Colombier 	CbIAS		= 0x0001,	/* Individual Address Setup */
170*9ef1f84bSDavid du Colombier 	CbConfigure	= 0x0002,
171*9ef1f84bSDavid du Colombier 	CbMAS		= 0x0003,	/* Multicast Address Setup */
172*9ef1f84bSDavid du Colombier 	CbTransmit	= 0x0004,
173*9ef1f84bSDavid du Colombier 	CbDump		= 0x0006,
174*9ef1f84bSDavid du Colombier 	CbDiagnose	= 0x0007,
175*9ef1f84bSDavid du Colombier 	CbCommand	= 0x0007,	/* mask */
176*9ef1f84bSDavid du Colombier 
177*9ef1f84bSDavid du Colombier 	CbSF		= 0x0008,	/* Flexible-mode CbTransmit */
178*9ef1f84bSDavid du Colombier 
179*9ef1f84bSDavid du Colombier 	CbI		= 0x2000,	/* Interrupt after completion */
180*9ef1f84bSDavid du Colombier 	CbS		= 0x4000,	/* Suspend after completion */
181*9ef1f84bSDavid du Colombier 	CbEL		= 0x8000,	/* End of List */
182*9ef1f84bSDavid du Colombier };
183*9ef1f84bSDavid du Colombier 
184*9ef1f84bSDavid du Colombier enum {					/* CbTransmit count */
185*9ef1f84bSDavid du Colombier 	CbEOF		= 0x8000,
186*9ef1f84bSDavid du Colombier };
187*9ef1f84bSDavid du Colombier 
188*9ef1f84bSDavid du Colombier typedef struct Ctlr Ctlr;
189*9ef1f84bSDavid du Colombier typedef struct Ctlr {
190*9ef1f84bSDavid du Colombier 	Lock	slock;			/* attach */
191*9ef1f84bSDavid du Colombier 	int	state;
192*9ef1f84bSDavid du Colombier 
193*9ef1f84bSDavid du Colombier 	int	port;
194*9ef1f84bSDavid du Colombier 	Pcidev*	pcidev;
195*9ef1f84bSDavid du Colombier 	Ctlr*	next;
196*9ef1f84bSDavid du Colombier 	int	active;
197*9ef1f84bSDavid du Colombier 
198*9ef1f84bSDavid du Colombier 	int	eepromsz;		/* address size in bits */
199*9ef1f84bSDavid du Colombier 	ushort*	eeprom;
200*9ef1f84bSDavid du Colombier 
201*9ef1f84bSDavid du Colombier 	Lock	miilock;
202*9ef1f84bSDavid du Colombier 
203*9ef1f84bSDavid du Colombier 	int	tick;
204*9ef1f84bSDavid du Colombier 
205*9ef1f84bSDavid du Colombier 	Lock	rlock;			/* registers */
206*9ef1f84bSDavid du Colombier 	int	command;		/* last command issued */
207*9ef1f84bSDavid du Colombier 
208*9ef1f84bSDavid du Colombier 	Block*	rfdhead;		/* receive side */
209*9ef1f84bSDavid du Colombier 	Block*	rfdtail;
210*9ef1f84bSDavid du Colombier 	int	nrfd;
211*9ef1f84bSDavid du Colombier 
212*9ef1f84bSDavid du Colombier 	Lock	cblock;			/* transmit side */
213*9ef1f84bSDavid du Colombier 	int	action;
214*9ef1f84bSDavid du Colombier 	int	nop;
215*9ef1f84bSDavid du Colombier 	uchar	configdata[24];
216*9ef1f84bSDavid du Colombier 	int	threshold;
217*9ef1f84bSDavid du Colombier 	int	ncb;
218*9ef1f84bSDavid du Colombier 	Cb*	cbr;
219*9ef1f84bSDavid du Colombier 	Cb*	cbhead;
220*9ef1f84bSDavid du Colombier 	Cb*	cbtail;
221*9ef1f84bSDavid du Colombier 	int	cbq;
222*9ef1f84bSDavid du Colombier 	int	cbqmax;
223*9ef1f84bSDavid du Colombier 	int	cbqmaxhw;
224*9ef1f84bSDavid du Colombier 
225*9ef1f84bSDavid du Colombier 	Lock	dlock;			/* dump statistical counters */
226*9ef1f84bSDavid du Colombier 	ulong	dump[17];
227*9ef1f84bSDavid du Colombier } Ctlr;
228*9ef1f84bSDavid du Colombier 
229*9ef1f84bSDavid du Colombier static Ctlr* ctlrhead;
230*9ef1f84bSDavid du Colombier static Ctlr* ctlrtail;
231*9ef1f84bSDavid du Colombier 
232*9ef1f84bSDavid du Colombier static uchar configdata[24] = {
233*9ef1f84bSDavid du Colombier 	0x16,				/* byte count */
234*9ef1f84bSDavid du Colombier 	0x08,				/* Rx/Tx FIFO limit */
235*9ef1f84bSDavid du Colombier 	0x00,				/* adaptive IFS */
236*9ef1f84bSDavid du Colombier 	0x00,
237*9ef1f84bSDavid du Colombier 	0x00,				/* Rx DMA maximum byte count */
238*9ef1f84bSDavid du Colombier //	0x80,				/* Tx DMA maximum byte count */
239*9ef1f84bSDavid du Colombier 	0x00,				/* Tx DMA maximum byte count */
240*9ef1f84bSDavid du Colombier 	0x32,				/* !late SCB, CNA interrupts */
241*9ef1f84bSDavid du Colombier 	0x03,				/* discard short Rx frames */
242*9ef1f84bSDavid du Colombier 	0x00,				/* 503/MII */
243*9ef1f84bSDavid du Colombier 
244*9ef1f84bSDavid du Colombier 	0x00,
245*9ef1f84bSDavid du Colombier 	0x2E,				/* normal operation, NSAI */
246*9ef1f84bSDavid du Colombier 	0x00,				/* linear priority */
247*9ef1f84bSDavid du Colombier 	0x60,				/* inter-frame spacing */
248*9ef1f84bSDavid du Colombier 	0x00,
249*9ef1f84bSDavid du Colombier 	0xF2,
250*9ef1f84bSDavid du Colombier 	0xC8,				/* 503, promiscuous mode off */
251*9ef1f84bSDavid du Colombier 	0x00,
252*9ef1f84bSDavid du Colombier 	0x40,
253*9ef1f84bSDavid du Colombier 	0xF3,				/* transmit padding enable */
254*9ef1f84bSDavid du Colombier 	0x80,				/* full duplex pin enable */
255*9ef1f84bSDavid du Colombier 	0x3F,				/* no Multi IA */
256*9ef1f84bSDavid du Colombier 	0x05,				/* no Multi Cast ALL */
257*9ef1f84bSDavid du Colombier };
258*9ef1f84bSDavid du Colombier 
259*9ef1f84bSDavid du Colombier #define csr8r(c, r)	(inb((c)->port+(r)))
260*9ef1f84bSDavid du Colombier #define csr16r(c, r)	(ins((c)->port+(r)))
261*9ef1f84bSDavid du Colombier #define csr32r(c, r)	(inl((c)->port+(r)))
262*9ef1f84bSDavid du Colombier #define csr8w(c, r, b)	(outb((c)->port+(r), (int)(b)))
263*9ef1f84bSDavid du Colombier #define csr16w(c, r, w)	(outs((c)->port+(r), (ushort)(w)))
264*9ef1f84bSDavid du Colombier #define csr32w(c, r, l)	(outl((c)->port+(r), (ulong)(l)))
265*9ef1f84bSDavid du Colombier 
266*9ef1f84bSDavid du Colombier static void
command(Ctlr * ctlr,int c,int v)267*9ef1f84bSDavid du Colombier command(Ctlr* ctlr, int c, int v)
268*9ef1f84bSDavid du Colombier {
269*9ef1f84bSDavid du Colombier 	int timeo;
270*9ef1f84bSDavid du Colombier 
271*9ef1f84bSDavid du Colombier 	ilock(&ctlr->rlock);
272*9ef1f84bSDavid du Colombier 
273*9ef1f84bSDavid du Colombier 	/*
274*9ef1f84bSDavid du Colombier 	 * Only back-to-back CUresume can be done
275*9ef1f84bSDavid du Colombier 	 * without waiting for any previous command to complete.
276*9ef1f84bSDavid du Colombier 	 * This should be the common case.
277*9ef1f84bSDavid du Colombier 	 * Unfortunately there's a chip errata where back-to-back
278*9ef1f84bSDavid du Colombier 	 * CUresumes can be lost, the fix is to always wait.
279*9ef1f84bSDavid du Colombier 	if(c == CUresume && ctlr->command == CUresume){
280*9ef1f84bSDavid du Colombier 		csr8w(ctlr, CommandR, c);
281*9ef1f84bSDavid du Colombier 		iunlock(&ctlr->rlock);
282*9ef1f84bSDavid du Colombier 		return;
283*9ef1f84bSDavid du Colombier 	}
284*9ef1f84bSDavid du Colombier 	 */
285*9ef1f84bSDavid du Colombier 
286*9ef1f84bSDavid du Colombier 	for(timeo = 0; timeo < 100; timeo++){
287*9ef1f84bSDavid du Colombier 		if(!csr8r(ctlr, CommandR))
288*9ef1f84bSDavid du Colombier 			break;
289*9ef1f84bSDavid du Colombier 		microdelay(1);
290*9ef1f84bSDavid du Colombier 	}
291*9ef1f84bSDavid du Colombier 	if(timeo >= 100){
292*9ef1f84bSDavid du Colombier 		ctlr->command = -1;
293*9ef1f84bSDavid du Colombier 		iunlock(&ctlr->rlock);
294*9ef1f84bSDavid du Colombier 		iprint("i82557: command %#ux %#ux timeout\n", c, v);
295*9ef1f84bSDavid du Colombier 		return;
296*9ef1f84bSDavid du Colombier 	}
297*9ef1f84bSDavid du Colombier 
298*9ef1f84bSDavid du Colombier 	switch(c){
299*9ef1f84bSDavid du Colombier 
300*9ef1f84bSDavid du Colombier 	case CUstart:
301*9ef1f84bSDavid du Colombier 	case LoadDCA:
302*9ef1f84bSDavid du Colombier 	case LoadCUB:
303*9ef1f84bSDavid du Colombier 	case RUstart:
304*9ef1f84bSDavid du Colombier 	case LoadHDS:
305*9ef1f84bSDavid du Colombier 	case LoadRUB:
306*9ef1f84bSDavid du Colombier 		csr32w(ctlr, General, v);
307*9ef1f84bSDavid du Colombier 		break;
308*9ef1f84bSDavid du Colombier 
309*9ef1f84bSDavid du Colombier 	/*
310*9ef1f84bSDavid du Colombier 	case CUnop:
311*9ef1f84bSDavid du Colombier 	case CUresume:
312*9ef1f84bSDavid du Colombier 	case DumpSC:
313*9ef1f84bSDavid du Colombier 	case ResetSA:
314*9ef1f84bSDavid du Colombier 	case RUresume:
315*9ef1f84bSDavid du Colombier 	case RUabort:
316*9ef1f84bSDavid du Colombier 	 */
317*9ef1f84bSDavid du Colombier 	default:
318*9ef1f84bSDavid du Colombier 		break;
319*9ef1f84bSDavid du Colombier 	}
320*9ef1f84bSDavid du Colombier 	csr8w(ctlr, CommandR, c);
321*9ef1f84bSDavid du Colombier 	ctlr->command = c;
322*9ef1f84bSDavid du Colombier 
323*9ef1f84bSDavid du Colombier 	iunlock(&ctlr->rlock);
324*9ef1f84bSDavid du Colombier }
325*9ef1f84bSDavid du Colombier 
326*9ef1f84bSDavid du Colombier static Block*
rfdalloc(ulong link)327*9ef1f84bSDavid du Colombier rfdalloc(ulong link)
328*9ef1f84bSDavid du Colombier {
329*9ef1f84bSDavid du Colombier 	Block *bp;
330*9ef1f84bSDavid du Colombier 	Rfd *rfd;
331*9ef1f84bSDavid du Colombier 
332*9ef1f84bSDavid du Colombier 	if(bp = iallocb(sizeof(Rfd))){
333*9ef1f84bSDavid du Colombier 		rfd = (Rfd*)bp->rp;
334*9ef1f84bSDavid du Colombier 		rfd->field = 0;
335*9ef1f84bSDavid du Colombier 		rfd->link = link;
336*9ef1f84bSDavid du Colombier 		rfd->rbd = NullPointer;
337*9ef1f84bSDavid du Colombier 		rfd->count = 0;
338*9ef1f84bSDavid du Colombier 		rfd->size = sizeof(Etherpkt);
339*9ef1f84bSDavid du Colombier 	}
340*9ef1f84bSDavid du Colombier 
341*9ef1f84bSDavid du Colombier 	return bp;
342*9ef1f84bSDavid du Colombier }
343*9ef1f84bSDavid du Colombier 
344*9ef1f84bSDavid du Colombier static void
ethwatchdog(void * arg)345*9ef1f84bSDavid du Colombier ethwatchdog(void* arg)
346*9ef1f84bSDavid du Colombier {
347*9ef1f84bSDavid du Colombier 	Ether *ether;
348*9ef1f84bSDavid du Colombier 	Ctlr *ctlr;
349*9ef1f84bSDavid du Colombier 	static void txstart(Ether*);
350*9ef1f84bSDavid du Colombier 
351*9ef1f84bSDavid du Colombier 	ether = arg;
352*9ef1f84bSDavid du Colombier 	for(;;){
353*9ef1f84bSDavid du Colombier 		tsleep(&up->sleep, return0, 0, 4000);
354*9ef1f84bSDavid du Colombier 
355*9ef1f84bSDavid du Colombier 		/*
356*9ef1f84bSDavid du Colombier 		 * Hmmm. This doesn't seem right. Currently
357*9ef1f84bSDavid du Colombier 		 * the device can't be disabled but it may be in
358*9ef1f84bSDavid du Colombier 		 * the future.
359*9ef1f84bSDavid du Colombier 		 */
360*9ef1f84bSDavid du Colombier 		ctlr = ether->ctlr;
361*9ef1f84bSDavid du Colombier 		if(ctlr == nil || ctlr->state == 0){
362*9ef1f84bSDavid du Colombier 			print("%s: exiting\n", up->text);
363*9ef1f84bSDavid du Colombier 			pexit("disabled", 0);
364*9ef1f84bSDavid du Colombier 		}
365*9ef1f84bSDavid du Colombier 
366*9ef1f84bSDavid du Colombier 		ilock(&ctlr->cblock);
367*9ef1f84bSDavid du Colombier 		if(ctlr->tick++){
368*9ef1f84bSDavid du Colombier 			ctlr->action = CbMAS;
369*9ef1f84bSDavid du Colombier 			txstart(ether);
370*9ef1f84bSDavid du Colombier 		}
371*9ef1f84bSDavid du Colombier 		iunlock(&ctlr->cblock);
372*9ef1f84bSDavid du Colombier 	}
373*9ef1f84bSDavid du Colombier }
374*9ef1f84bSDavid du Colombier 
375*9ef1f84bSDavid du Colombier static void
attach(Ether * ether)376*9ef1f84bSDavid du Colombier attach(Ether* ether)
377*9ef1f84bSDavid du Colombier {
378*9ef1f84bSDavid du Colombier 	Ctlr *ctlr;
379*9ef1f84bSDavid du Colombier 	char name[KNAMELEN];
380*9ef1f84bSDavid du Colombier 
381*9ef1f84bSDavid du Colombier 	ctlr = ether->ctlr;
382*9ef1f84bSDavid du Colombier 	lock(&ctlr->slock);
383*9ef1f84bSDavid du Colombier 	if(ctlr->state == 0){
384*9ef1f84bSDavid du Colombier 		ilock(&ctlr->rlock);
385*9ef1f84bSDavid du Colombier 		csr8w(ctlr, Interrupt, 0);
386*9ef1f84bSDavid du Colombier 		iunlock(&ctlr->rlock);
387*9ef1f84bSDavid du Colombier 		command(ctlr, RUstart, PADDR(ctlr->rfdhead->rp));
388*9ef1f84bSDavid du Colombier 		ctlr->state = 1;
389*9ef1f84bSDavid du Colombier 
390*9ef1f84bSDavid du Colombier 		/*
391*9ef1f84bSDavid du Colombier 		 * Start the watchdog timer for the receive lockup errata
392*9ef1f84bSDavid du Colombier 		 * unless the EEPROM compatibility word indicates it may be
393*9ef1f84bSDavid du Colombier 		 * omitted.
394*9ef1f84bSDavid du Colombier 		 */
395*9ef1f84bSDavid du Colombier 		if((ctlr->eeprom[0x03] & 0x0003) != 0x0003){
396*9ef1f84bSDavid du Colombier 			snprint(name, KNAMELEN, "#l%dwatchdog", ether->ctlrno);
397*9ef1f84bSDavid du Colombier 			kproc(name, ethwatchdog, ether);
398*9ef1f84bSDavid du Colombier 		}
399*9ef1f84bSDavid du Colombier 	}
400*9ef1f84bSDavid du Colombier 	unlock(&ctlr->slock);
401*9ef1f84bSDavid du Colombier }
402*9ef1f84bSDavid du Colombier 
403*9ef1f84bSDavid du Colombier static long
ifstat(Ether * ether,void * a,long n,ulong offset)404*9ef1f84bSDavid du Colombier ifstat(Ether* ether, void* a, long n, ulong offset)
405*9ef1f84bSDavid du Colombier {
406*9ef1f84bSDavid du Colombier 	char *alloc, *e, *p;
407*9ef1f84bSDavid du Colombier 	int i, phyaddr;
408*9ef1f84bSDavid du Colombier 	Ctlr *ctlr;
409*9ef1f84bSDavid du Colombier 	ulong dump[17];
410*9ef1f84bSDavid du Colombier 
411*9ef1f84bSDavid du Colombier 	ctlr = ether->ctlr;
412*9ef1f84bSDavid du Colombier 	lock(&ctlr->dlock);
413*9ef1f84bSDavid du Colombier 
414*9ef1f84bSDavid du Colombier 	/*
415*9ef1f84bSDavid du Colombier 	 * Start the command then
416*9ef1f84bSDavid du Colombier 	 * wait for completion status,
417*9ef1f84bSDavid du Colombier 	 * should be 0xA005.
418*9ef1f84bSDavid du Colombier 	 */
419*9ef1f84bSDavid du Colombier 	ctlr->dump[16] = 0;
420*9ef1f84bSDavid du Colombier 	command(ctlr, DumpSC, 0);
421*9ef1f84bSDavid du Colombier 	while(ctlr->dump[16] == 0)
422*9ef1f84bSDavid du Colombier 		;
423*9ef1f84bSDavid du Colombier 
424*9ef1f84bSDavid du Colombier 	ether->oerrs = ctlr->dump[1]+ctlr->dump[2]+ctlr->dump[3];
425*9ef1f84bSDavid du Colombier 	ether->crcs = ctlr->dump[10];
426*9ef1f84bSDavid du Colombier 	ether->frames = ctlr->dump[11];
427*9ef1f84bSDavid du Colombier 	ether->buffs = ctlr->dump[12]+ctlr->dump[15];
428*9ef1f84bSDavid du Colombier 	ether->overflows = ctlr->dump[13];
429*9ef1f84bSDavid du Colombier 
430*9ef1f84bSDavid du Colombier 	if(n == 0){
431*9ef1f84bSDavid du Colombier 		unlock(&ctlr->dlock);
432*9ef1f84bSDavid du Colombier 		return 0;
433*9ef1f84bSDavid du Colombier 	}
434*9ef1f84bSDavid du Colombier 
435*9ef1f84bSDavid du Colombier 	memmove(dump, ctlr->dump, sizeof(dump));
436*9ef1f84bSDavid du Colombier 	unlock(&ctlr->dlock);
437*9ef1f84bSDavid du Colombier 
438*9ef1f84bSDavid du Colombier 	if((alloc = malloc(READSTR)) == nil)
439*9ef1f84bSDavid du Colombier 		error(Enomem);
440*9ef1f84bSDavid du Colombier 	p = alloc;
441*9ef1f84bSDavid du Colombier 	e = p + READSTR;
442*9ef1f84bSDavid du Colombier 
443*9ef1f84bSDavid du Colombier 	p = seprint(p, e, "transmit good frames: %lud\n", dump[0]);
444*9ef1f84bSDavid du Colombier 	p = seprint(p, e, "transmit maximum collisions errors: %lud\n", dump[1]);
445*9ef1f84bSDavid du Colombier 	p = seprint(p, e, "transmit late collisions errors: %lud\n", dump[2]);
446*9ef1f84bSDavid du Colombier 	p = seprint(p, e, "transmit underrun errors: %lud\n", dump[3]);
447*9ef1f84bSDavid du Colombier 	p = seprint(p, e, "transmit lost carrier sense: %lud\n", dump[4]);
448*9ef1f84bSDavid du Colombier 	p = seprint(p, e, "transmit deferred: %lud\n", dump[5]);
449*9ef1f84bSDavid du Colombier 	p = seprint(p, e, "transmit single collisions: %lud\n", dump[6]);
450*9ef1f84bSDavid du Colombier 	p = seprint(p, e, "transmit multiple collisions: %lud\n", dump[7]);
451*9ef1f84bSDavid du Colombier 	p = seprint(p, e, "transmit total collisions: %lud\n", dump[8]);
452*9ef1f84bSDavid du Colombier 	p = seprint(p, e, "receive good frames: %lud\n", dump[9]);
453*9ef1f84bSDavid du Colombier 	p = seprint(p, e, "receive CRC errors: %lud\n", dump[10]);
454*9ef1f84bSDavid du Colombier 	p = seprint(p, e, "receive alignment errors: %lud\n", dump[11]);
455*9ef1f84bSDavid du Colombier 	p = seprint(p, e, "receive resource errors: %lud\n", dump[12]);
456*9ef1f84bSDavid du Colombier 	p = seprint(p, e, "receive overrun errors: %lud\n", dump[13]);
457*9ef1f84bSDavid du Colombier 	p = seprint(p, e, "receive collision detect errors: %lud\n", dump[14]);
458*9ef1f84bSDavid du Colombier 	p = seprint(p, e, "receive short frame errors: %lud\n", dump[15]);
459*9ef1f84bSDavid du Colombier 	p = seprint(p, e, "nop: %d\n", ctlr->nop);
460*9ef1f84bSDavid du Colombier 	if(ctlr->cbqmax > ctlr->cbqmaxhw)
461*9ef1f84bSDavid du Colombier 		ctlr->cbqmaxhw = ctlr->cbqmax;
462*9ef1f84bSDavid du Colombier 	p = seprint(p, e, "cbqmax: %d\n", ctlr->cbqmax);
463*9ef1f84bSDavid du Colombier 	ctlr->cbqmax = 0;
464*9ef1f84bSDavid du Colombier 	p = seprint(p, e, "threshold: %d\n", ctlr->threshold);
465*9ef1f84bSDavid du Colombier 
466*9ef1f84bSDavid du Colombier 	p = seprint(p, e, "eeprom:");
467*9ef1f84bSDavid du Colombier 	for(i = 0; i < (1<<ctlr->eepromsz); i++){
468*9ef1f84bSDavid du Colombier 		if(i && ((i & 0x07) == 0))
469*9ef1f84bSDavid du Colombier 			p = seprint(p, e, "\n       ");
470*9ef1f84bSDavid du Colombier 		p = seprint(p, e, " %4.4ux", ctlr->eeprom[i]);
471*9ef1f84bSDavid du Colombier 	}
472*9ef1f84bSDavid du Colombier 
473*9ef1f84bSDavid du Colombier 	if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000)){
474*9ef1f84bSDavid du Colombier 		phyaddr = ctlr->eeprom[6] & 0x00FF;
475*9ef1f84bSDavid du Colombier 		p = seprint(p, e, "\nphy %2d:", phyaddr);
476*9ef1f84bSDavid du Colombier 		for(i = 0; i < 6; i++){
477*9ef1f84bSDavid du Colombier 			static int miir(Ctlr*, int, int);
478*9ef1f84bSDavid du Colombier 
479*9ef1f84bSDavid du Colombier 			p = seprint(p, e, " %4.4ux", miir(ctlr, phyaddr, i));
480*9ef1f84bSDavid du Colombier 		}
481*9ef1f84bSDavid du Colombier 	}
482*9ef1f84bSDavid du Colombier 	seprint(p, e, "\n");
483*9ef1f84bSDavid du Colombier 
484*9ef1f84bSDavid du Colombier 	n = readstr(offset, a, n, alloc);
485*9ef1f84bSDavid du Colombier 	free(alloc);
486*9ef1f84bSDavid du Colombier 
487*9ef1f84bSDavid du Colombier 	return n;
488*9ef1f84bSDavid du Colombier }
489*9ef1f84bSDavid du Colombier 
490*9ef1f84bSDavid du Colombier static void
txstart(Ether * ether)491*9ef1f84bSDavid du Colombier txstart(Ether* ether)
492*9ef1f84bSDavid du Colombier {
493*9ef1f84bSDavid du Colombier 	Ctlr *ctlr;
494*9ef1f84bSDavid du Colombier 	Block *bp;
495*9ef1f84bSDavid du Colombier 	Cb *cb;
496*9ef1f84bSDavid du Colombier 
497*9ef1f84bSDavid du Colombier 	ctlr = ether->ctlr;
498*9ef1f84bSDavid du Colombier 	while(ctlr->cbq < (ctlr->ncb-1)){
499*9ef1f84bSDavid du Colombier 		cb = ctlr->cbhead->next;
500*9ef1f84bSDavid du Colombier 		if(ctlr->action == 0){
501*9ef1f84bSDavid du Colombier 			bp = qget(ether->oq);
502*9ef1f84bSDavid du Colombier 			if(bp == nil)
503*9ef1f84bSDavid du Colombier 				break;
504*9ef1f84bSDavid du Colombier 
505*9ef1f84bSDavid du Colombier 			cb->command = CbS|CbSF|CbTransmit;
506*9ef1f84bSDavid du Colombier 			cb->tbd = PADDR(&cb->tba);
507*9ef1f84bSDavid du Colombier 			cb->count = 0;
508*9ef1f84bSDavid du Colombier 			cb->threshold = ctlr->threshold;
509*9ef1f84bSDavid du Colombier 			cb->number = 1;
510*9ef1f84bSDavid du Colombier 			cb->tba = PADDR(bp->rp);
511*9ef1f84bSDavid du Colombier 			cb->bp = bp;
512*9ef1f84bSDavid du Colombier 			cb->tbasz = BLEN(bp);
513*9ef1f84bSDavid du Colombier 		}
514*9ef1f84bSDavid du Colombier 		else if(ctlr->action == CbConfigure){
515*9ef1f84bSDavid du Colombier 			cb->command = CbS|CbConfigure;
516*9ef1f84bSDavid du Colombier 			memmove(cb->data, ctlr->configdata, sizeof(ctlr->configdata));
517*9ef1f84bSDavid du Colombier 			ctlr->action = 0;
518*9ef1f84bSDavid du Colombier 		}
519*9ef1f84bSDavid du Colombier 		else if(ctlr->action == CbIAS){
520*9ef1f84bSDavid du Colombier 			cb->command = CbS|CbIAS;
521*9ef1f84bSDavid du Colombier 			memmove(cb->data, ether->ea, Eaddrlen);
522*9ef1f84bSDavid du Colombier 			ctlr->action = 0;
523*9ef1f84bSDavid du Colombier 		}
524*9ef1f84bSDavid du Colombier 		else if(ctlr->action == CbMAS){
525*9ef1f84bSDavid du Colombier 			cb->command = CbS|CbMAS;
526*9ef1f84bSDavid du Colombier 			memset(cb->data, 0, sizeof(cb->data));
527*9ef1f84bSDavid du Colombier 			ctlr->action = 0;
528*9ef1f84bSDavid du Colombier 		}
529*9ef1f84bSDavid du Colombier 		else{
530*9ef1f84bSDavid du Colombier 			print("#l%d: action %#ux\n", ether->ctlrno, ctlr->action);
531*9ef1f84bSDavid du Colombier 			ctlr->action = 0;
532*9ef1f84bSDavid du Colombier 			break;
533*9ef1f84bSDavid du Colombier 		}
534*9ef1f84bSDavid du Colombier 		cb->status = 0;
535*9ef1f84bSDavid du Colombier 
536*9ef1f84bSDavid du Colombier 		coherence();
537*9ef1f84bSDavid du Colombier 		ctlr->cbhead->command &= ~CbS;
538*9ef1f84bSDavid du Colombier 		ctlr->cbhead = cb;
539*9ef1f84bSDavid du Colombier 		ctlr->cbq++;
540*9ef1f84bSDavid du Colombier 	}
541*9ef1f84bSDavid du Colombier 
542*9ef1f84bSDavid du Colombier 	/*
543*9ef1f84bSDavid du Colombier 	 * Workaround for some broken HUB chips
544*9ef1f84bSDavid du Colombier 	 * when connected at 10Mb/s half-duplex.
545*9ef1f84bSDavid du Colombier 	 */
546*9ef1f84bSDavid du Colombier 	if(ctlr->nop){
547*9ef1f84bSDavid du Colombier 		command(ctlr, CUnop, 0);
548*9ef1f84bSDavid du Colombier 		microdelay(1);
549*9ef1f84bSDavid du Colombier 	}
550*9ef1f84bSDavid du Colombier 	command(ctlr, CUresume, 0);
551*9ef1f84bSDavid du Colombier 
552*9ef1f84bSDavid du Colombier 	if(ctlr->cbq > ctlr->cbqmax)
553*9ef1f84bSDavid du Colombier 		ctlr->cbqmax = ctlr->cbq;
554*9ef1f84bSDavid du Colombier }
555*9ef1f84bSDavid du Colombier 
556*9ef1f84bSDavid du Colombier static void
configure(Ether * ether,int promiscuous)557*9ef1f84bSDavid du Colombier configure(Ether* ether, int promiscuous)
558*9ef1f84bSDavid du Colombier {
559*9ef1f84bSDavid du Colombier 	Ctlr *ctlr;
560*9ef1f84bSDavid du Colombier 
561*9ef1f84bSDavid du Colombier 	ctlr = ether->ctlr;
562*9ef1f84bSDavid du Colombier 	ilock(&ctlr->cblock);
563*9ef1f84bSDavid du Colombier 	if(promiscuous){
564*9ef1f84bSDavid du Colombier 		ctlr->configdata[6] |= 0x80;		/* Save Bad Frames */
565*9ef1f84bSDavid du Colombier 		//ctlr->configdata[6] &= ~0x40;		/* !Discard Overrun Rx Frames */
566*9ef1f84bSDavid du Colombier 		ctlr->configdata[7] &= ~0x01;		/* !Discard Short Rx Frames */
567*9ef1f84bSDavid du Colombier 		ctlr->configdata[15] |= 0x01;		/* Promiscuous mode */
568*9ef1f84bSDavid du Colombier 		ctlr->configdata[18] &= ~0x01;		/* (!Padding enable?), !stripping enable */
569*9ef1f84bSDavid du Colombier 		ctlr->configdata[21] |= 0x08;		/* Multi Cast ALL */
570*9ef1f84bSDavid du Colombier 	}
571*9ef1f84bSDavid du Colombier 	else{
572*9ef1f84bSDavid du Colombier 		ctlr->configdata[6] &= ~0x80;
573*9ef1f84bSDavid du Colombier 		//ctlr->configdata[6] |= 0x40;
574*9ef1f84bSDavid du Colombier 		ctlr->configdata[7] |= 0x01;
575*9ef1f84bSDavid du Colombier 		ctlr->configdata[15] &= ~0x01;
576*9ef1f84bSDavid du Colombier 		ctlr->configdata[18] |= 0x01;		/* 0x03? */
577*9ef1f84bSDavid du Colombier 		ctlr->configdata[21] &= ~0x08;
578*9ef1f84bSDavid du Colombier 	}
579*9ef1f84bSDavid du Colombier 	ctlr->action = CbConfigure;
580*9ef1f84bSDavid du Colombier 	txstart(ether);
581*9ef1f84bSDavid du Colombier 	iunlock(&ctlr->cblock);
582*9ef1f84bSDavid du Colombier }
583*9ef1f84bSDavid du Colombier 
584*9ef1f84bSDavid du Colombier static void
promiscuous(void * arg,int on)585*9ef1f84bSDavid du Colombier promiscuous(void* arg, int on)
586*9ef1f84bSDavid du Colombier {
587*9ef1f84bSDavid du Colombier 	configure(arg, on);
588*9ef1f84bSDavid du Colombier }
589*9ef1f84bSDavid du Colombier 
590*9ef1f84bSDavid du Colombier static void
multicast(void * ether,uchar * addr,int add)591*9ef1f84bSDavid du Colombier multicast(void* ether, uchar *addr, int add)
592*9ef1f84bSDavid du Colombier {
593*9ef1f84bSDavid du Colombier 	USED(addr);
594*9ef1f84bSDavid du Colombier 	/*
595*9ef1f84bSDavid du Colombier 	 * TODO: if (add) add addr to list of mcast addrs in controller
596*9ef1f84bSDavid du Colombier 	 *	else remove addr from list of mcast addrs in controller
597*9ef1f84bSDavid du Colombier 	 * enable multicast input (see CbMAS) instead of promiscuous mode.
598*9ef1f84bSDavid du Colombier 	 */
599*9ef1f84bSDavid du Colombier 	if (add)
600*9ef1f84bSDavid du Colombier 		configure(ether, 1);
601*9ef1f84bSDavid du Colombier }
602*9ef1f84bSDavid du Colombier 
603*9ef1f84bSDavid du Colombier static void
transmit(Ether * ether)604*9ef1f84bSDavid du Colombier transmit(Ether* ether)
605*9ef1f84bSDavid du Colombier {
606*9ef1f84bSDavid du Colombier 	Ctlr *ctlr;
607*9ef1f84bSDavid du Colombier 
608*9ef1f84bSDavid du Colombier 	ctlr = ether->ctlr;
609*9ef1f84bSDavid du Colombier 	ilock(&ctlr->cblock);
610*9ef1f84bSDavid du Colombier 	txstart(ether);
611*9ef1f84bSDavid du Colombier 	iunlock(&ctlr->cblock);
612*9ef1f84bSDavid du Colombier }
613*9ef1f84bSDavid du Colombier 
614*9ef1f84bSDavid du Colombier static void
receive(Ether * ether)615*9ef1f84bSDavid du Colombier receive(Ether* ether)
616*9ef1f84bSDavid du Colombier {
617*9ef1f84bSDavid du Colombier 	Rfd *rfd;
618*9ef1f84bSDavid du Colombier 	Ctlr *ctlr;
619*9ef1f84bSDavid du Colombier 	int count;
620*9ef1f84bSDavid du Colombier 	Block *bp, *pbp, *xbp;
621*9ef1f84bSDavid du Colombier 
622*9ef1f84bSDavid du Colombier 	ctlr = ether->ctlr;
623*9ef1f84bSDavid du Colombier 	bp = ctlr->rfdhead;
624*9ef1f84bSDavid du Colombier 	for(rfd = (Rfd*)bp->rp; rfd->field & RfdC; rfd = (Rfd*)bp->rp){
625*9ef1f84bSDavid du Colombier 		/*
626*9ef1f84bSDavid du Colombier 		 * If it's an OK receive frame
627*9ef1f84bSDavid du Colombier 		 * 1) save the count
628*9ef1f84bSDavid du Colombier 		 * 2) if it's small, try to allocate a block and copy
629*9ef1f84bSDavid du Colombier 		 *    the data, then adjust the necessary fields for reuse;
630*9ef1f84bSDavid du Colombier 		 * 3) if it's big, try to allocate a new Rfd and if
631*9ef1f84bSDavid du Colombier 		 *    successful
632*9ef1f84bSDavid du Colombier 		 *	adjust the received buffer pointers for the
633*9ef1f84bSDavid du Colombier 		 *	  actual data received;
634*9ef1f84bSDavid du Colombier 		 *	initialise the replacement buffer to point to
635*9ef1f84bSDavid du Colombier 		 *	  the next in the ring;
636*9ef1f84bSDavid du Colombier 		 *	initialise bp to point to the replacement;
637*9ef1f84bSDavid du Colombier 		 * 4) if there's a good packet, pass it on for disposal.
638*9ef1f84bSDavid du Colombier 		 */
639*9ef1f84bSDavid du Colombier 		if(rfd->field & RfdOK){
640*9ef1f84bSDavid du Colombier 			pbp = nil;
641*9ef1f84bSDavid du Colombier 			count = rfd->count & 0x3FFF;
642*9ef1f84bSDavid du Colombier 			if((count < ETHERMAXTU/4) && (pbp = iallocb(count))){
643*9ef1f84bSDavid du Colombier 				memmove(pbp->rp, bp->rp+offsetof(Rfd, data[0]), count);
644*9ef1f84bSDavid du Colombier 				pbp->wp = pbp->rp + count;
645*9ef1f84bSDavid du Colombier 
646*9ef1f84bSDavid du Colombier 				rfd->count = 0;
647*9ef1f84bSDavid du Colombier 				rfd->field = 0;
648*9ef1f84bSDavid du Colombier 			}
649*9ef1f84bSDavid du Colombier 			else if(xbp = rfdalloc(rfd->link)){
650*9ef1f84bSDavid du Colombier 				bp->rp += offsetof(Rfd, data[0]);
651*9ef1f84bSDavid du Colombier 				bp->wp = bp->rp + count;
652*9ef1f84bSDavid du Colombier 
653*9ef1f84bSDavid du Colombier 				xbp->next = bp->next;
654*9ef1f84bSDavid du Colombier 				bp->next = 0;
655*9ef1f84bSDavid du Colombier 
656*9ef1f84bSDavid du Colombier 				pbp = bp;
657*9ef1f84bSDavid du Colombier 				bp = xbp;
658*9ef1f84bSDavid du Colombier 			}
659*9ef1f84bSDavid du Colombier 			if(pbp != nil)
660*9ef1f84bSDavid du Colombier 				etheriq(ether, pbp, 1);
661*9ef1f84bSDavid du Colombier 		}
662*9ef1f84bSDavid du Colombier 		else{
663*9ef1f84bSDavid du Colombier 			rfd->count = 0;
664*9ef1f84bSDavid du Colombier 			rfd->field = 0;
665*9ef1f84bSDavid du Colombier 		}
666*9ef1f84bSDavid du Colombier 
667*9ef1f84bSDavid du Colombier 		/*
668*9ef1f84bSDavid du Colombier 		 * The ring tail pointer follows the head with with one
669*9ef1f84bSDavid du Colombier 		 * unused buffer in between to defeat hardware prefetch;
670*9ef1f84bSDavid du Colombier 		 * once the tail pointer has been bumped on to the next
671*9ef1f84bSDavid du Colombier 		 * and the new tail has the Suspend bit set, it can be
672*9ef1f84bSDavid du Colombier 		 * removed from the old tail buffer.
673*9ef1f84bSDavid du Colombier 		 * As a replacement for the current head buffer may have
674*9ef1f84bSDavid du Colombier 		 * been allocated above, ensure that the new tail points
675*9ef1f84bSDavid du Colombier 		 * to it (next and link).
676*9ef1f84bSDavid du Colombier 		 */
677*9ef1f84bSDavid du Colombier 		rfd = (Rfd*)ctlr->rfdtail->rp;
678*9ef1f84bSDavid du Colombier 		ctlr->rfdtail = ctlr->rfdtail->next;
679*9ef1f84bSDavid du Colombier 		ctlr->rfdtail->next = bp;
680*9ef1f84bSDavid du Colombier 		((Rfd*)ctlr->rfdtail->rp)->link = PADDR(bp->rp);
681*9ef1f84bSDavid du Colombier 		((Rfd*)ctlr->rfdtail->rp)->field |= RfdS;
682*9ef1f84bSDavid du Colombier 		coherence();
683*9ef1f84bSDavid du Colombier 		rfd->field &= ~RfdS;
684*9ef1f84bSDavid du Colombier 
685*9ef1f84bSDavid du Colombier 		/*
686*9ef1f84bSDavid du Colombier 		 * Finally done with the current (possibly replaced)
687*9ef1f84bSDavid du Colombier 		 * head, move on to the next and maintain the sentinel
688*9ef1f84bSDavid du Colombier 		 * between tail and head.
689*9ef1f84bSDavid du Colombier 		 */
690*9ef1f84bSDavid du Colombier 		ctlr->rfdhead = bp->next;
691*9ef1f84bSDavid du Colombier 		bp = ctlr->rfdhead;
692*9ef1f84bSDavid du Colombier 	}
693*9ef1f84bSDavid du Colombier }
694*9ef1f84bSDavid du Colombier 
695*9ef1f84bSDavid du Colombier static void
interrupt(Ureg *,void * arg)696*9ef1f84bSDavid du Colombier interrupt(Ureg*, void* arg)
697*9ef1f84bSDavid du Colombier {
698*9ef1f84bSDavid du Colombier 	Cb* cb;
699*9ef1f84bSDavid du Colombier 	Ctlr *ctlr;
700*9ef1f84bSDavid du Colombier 	Ether *ether;
701*9ef1f84bSDavid du Colombier 	int status;
702*9ef1f84bSDavid du Colombier 
703*9ef1f84bSDavid du Colombier 	ether = arg;
704*9ef1f84bSDavid du Colombier 	ctlr = ether->ctlr;
705*9ef1f84bSDavid du Colombier 
706*9ef1f84bSDavid du Colombier 	for(;;){
707*9ef1f84bSDavid du Colombier 		ilock(&ctlr->rlock);
708*9ef1f84bSDavid du Colombier 		status = csr16r(ctlr, Status);
709*9ef1f84bSDavid du Colombier 		csr8w(ctlr, Ack, (status>>8) & 0xFF);
710*9ef1f84bSDavid du Colombier 		iunlock(&ctlr->rlock);
711*9ef1f84bSDavid du Colombier 
712*9ef1f84bSDavid du Colombier 		if(!(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI)))
713*9ef1f84bSDavid du Colombier 			break;
714*9ef1f84bSDavid du Colombier 
715*9ef1f84bSDavid du Colombier 		/*
716*9ef1f84bSDavid du Colombier 		 * If the watchdog timer for the receiver lockup errata is running,
717*9ef1f84bSDavid du Colombier 		 * let it know the receiver is active.
718*9ef1f84bSDavid du Colombier 		 */
719*9ef1f84bSDavid du Colombier 		if(status & (StatFR|StatRNR)){
720*9ef1f84bSDavid du Colombier 			ilock(&ctlr->cblock);
721*9ef1f84bSDavid du Colombier 			ctlr->tick = 0;
722*9ef1f84bSDavid du Colombier 			iunlock(&ctlr->cblock);
723*9ef1f84bSDavid du Colombier 		}
724*9ef1f84bSDavid du Colombier 
725*9ef1f84bSDavid du Colombier 		if(status & StatFR){
726*9ef1f84bSDavid du Colombier 			receive(ether);
727*9ef1f84bSDavid du Colombier 			status &= ~StatFR;
728*9ef1f84bSDavid du Colombier 		}
729*9ef1f84bSDavid du Colombier 
730*9ef1f84bSDavid du Colombier 		if(status & StatRNR){
731*9ef1f84bSDavid du Colombier 			command(ctlr, RUresume, 0);
732*9ef1f84bSDavid du Colombier 			status &= ~StatRNR;
733*9ef1f84bSDavid du Colombier 		}
734*9ef1f84bSDavid du Colombier 
735*9ef1f84bSDavid du Colombier 		if(status & StatCNA){
736*9ef1f84bSDavid du Colombier 			ilock(&ctlr->cblock);
737*9ef1f84bSDavid du Colombier 
738*9ef1f84bSDavid du Colombier 			cb = ctlr->cbtail;
739*9ef1f84bSDavid du Colombier 			while(ctlr->cbq){
740*9ef1f84bSDavid du Colombier 				if(!(cb->status & CbC))
741*9ef1f84bSDavid du Colombier 					break;
742*9ef1f84bSDavid du Colombier 				if(cb->bp){
743*9ef1f84bSDavid du Colombier 					freeb(cb->bp);
744*9ef1f84bSDavid du Colombier 					cb->bp = nil;
745*9ef1f84bSDavid du Colombier 				}
746*9ef1f84bSDavid du Colombier 				if((cb->status & CbU) && ctlr->threshold < 0xE0)
747*9ef1f84bSDavid du Colombier 					ctlr->threshold++;
748*9ef1f84bSDavid du Colombier 
749*9ef1f84bSDavid du Colombier 				ctlr->cbq--;
750*9ef1f84bSDavid du Colombier 				cb = cb->next;
751*9ef1f84bSDavid du Colombier 			}
752*9ef1f84bSDavid du Colombier 			ctlr->cbtail = cb;
753*9ef1f84bSDavid du Colombier 
754*9ef1f84bSDavid du Colombier 			txstart(ether);
755*9ef1f84bSDavid du Colombier 			iunlock(&ctlr->cblock);
756*9ef1f84bSDavid du Colombier 
757*9ef1f84bSDavid du Colombier 			status &= ~StatCNA;
758*9ef1f84bSDavid du Colombier 		}
759*9ef1f84bSDavid du Colombier 
760*9ef1f84bSDavid du Colombier 		if(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI))
761*9ef1f84bSDavid du Colombier 			panic("#l%d: status %#ux\n", ether->ctlrno, status);
762*9ef1f84bSDavid du Colombier 	}
763*9ef1f84bSDavid du Colombier }
764*9ef1f84bSDavid du Colombier 
765*9ef1f84bSDavid du Colombier static void
ctlrinit(Ctlr * ctlr)766*9ef1f84bSDavid du Colombier ctlrinit(Ctlr* ctlr)
767*9ef1f84bSDavid du Colombier {
768*9ef1f84bSDavid du Colombier 	int i;
769*9ef1f84bSDavid du Colombier 	Block *bp;
770*9ef1f84bSDavid du Colombier 	Rfd *rfd;
771*9ef1f84bSDavid du Colombier 	ulong link;
772*9ef1f84bSDavid du Colombier 
773*9ef1f84bSDavid du Colombier 	/*
774*9ef1f84bSDavid du Colombier 	 * Create the Receive Frame Area (RFA) as a ring of allocated
775*9ef1f84bSDavid du Colombier 	 * buffers.
776*9ef1f84bSDavid du Colombier 	 * A sentinel buffer is maintained between the last buffer in
777*9ef1f84bSDavid du Colombier 	 * the ring (marked with RfdS) and the head buffer to defeat the
778*9ef1f84bSDavid du Colombier 	 * hardware prefetch of the next RFD and allow dynamic buffer
779*9ef1f84bSDavid du Colombier 	 * allocation.
780*9ef1f84bSDavid du Colombier 	 */
781*9ef1f84bSDavid du Colombier 	link = NullPointer;
782*9ef1f84bSDavid du Colombier 	for(i = 0; i < Nrfd; i++){
783*9ef1f84bSDavid du Colombier 		bp = rfdalloc(link);
784*9ef1f84bSDavid du Colombier 		if(ctlr->rfdhead == nil)
785*9ef1f84bSDavid du Colombier 			ctlr->rfdtail = bp;
786*9ef1f84bSDavid du Colombier 		bp->next = ctlr->rfdhead;
787*9ef1f84bSDavid du Colombier 		ctlr->rfdhead = bp;
788*9ef1f84bSDavid du Colombier 		link = PADDR(bp->rp);
789*9ef1f84bSDavid du Colombier 	}
790*9ef1f84bSDavid du Colombier 	ctlr->rfdtail->next = ctlr->rfdhead;
791*9ef1f84bSDavid du Colombier 	rfd = (Rfd*)ctlr->rfdtail->rp;
792*9ef1f84bSDavid du Colombier 	rfd->link = PADDR(ctlr->rfdhead->rp);
793*9ef1f84bSDavid du Colombier 	rfd->field |= RfdS;
794*9ef1f84bSDavid du Colombier 	ctlr->rfdhead = ctlr->rfdhead->next;
795*9ef1f84bSDavid du Colombier 
796*9ef1f84bSDavid du Colombier 	/*
797*9ef1f84bSDavid du Colombier 	 * Create a ring of control blocks for the
798*9ef1f84bSDavid du Colombier 	 * transmit side.
799*9ef1f84bSDavid du Colombier 	 */
800*9ef1f84bSDavid du Colombier 	ilock(&ctlr->cblock);
801*9ef1f84bSDavid du Colombier 	ctlr->cbr = malloc(ctlr->ncb*sizeof(Cb));
802*9ef1f84bSDavid du Colombier 	if(ctlr->cbr == nil) {
803*9ef1f84bSDavid du Colombier 		iunlock(&ctlr->cblock);
804*9ef1f84bSDavid du Colombier 		error(Enomem);
805*9ef1f84bSDavid du Colombier 	}
806*9ef1f84bSDavid du Colombier 	for(i = 0; i < ctlr->ncb; i++){
807*9ef1f84bSDavid du Colombier 		ctlr->cbr[i].status = CbC|CbOK;
808*9ef1f84bSDavid du Colombier 		ctlr->cbr[i].command = CbS|CbNOP;
809*9ef1f84bSDavid du Colombier 		ctlr->cbr[i].link = PADDR(&ctlr->cbr[NEXT(i, ctlr->ncb)].status);
810*9ef1f84bSDavid du Colombier 		ctlr->cbr[i].next = &ctlr->cbr[NEXT(i, ctlr->ncb)];
811*9ef1f84bSDavid du Colombier 	}
812*9ef1f84bSDavid du Colombier 	ctlr->cbhead = ctlr->cbr;
813*9ef1f84bSDavid du Colombier 	ctlr->cbtail = ctlr->cbr;
814*9ef1f84bSDavid du Colombier 	ctlr->cbq = 0;
815*9ef1f84bSDavid du Colombier 
816*9ef1f84bSDavid du Colombier 	memmove(ctlr->configdata, configdata, sizeof(configdata));
817*9ef1f84bSDavid du Colombier 	ctlr->threshold = 80;
818*9ef1f84bSDavid du Colombier 	ctlr->tick = 0;
819*9ef1f84bSDavid du Colombier 
820*9ef1f84bSDavid du Colombier 	iunlock(&ctlr->cblock);
821*9ef1f84bSDavid du Colombier }
822*9ef1f84bSDavid du Colombier 
823*9ef1f84bSDavid du Colombier static int
miir(Ctlr * ctlr,int phyadd,int regadd)824*9ef1f84bSDavid du Colombier miir(Ctlr* ctlr, int phyadd, int regadd)
825*9ef1f84bSDavid du Colombier {
826*9ef1f84bSDavid du Colombier 	int mcr, timo;
827*9ef1f84bSDavid du Colombier 
828*9ef1f84bSDavid du Colombier 	lock(&ctlr->miilock);
829*9ef1f84bSDavid du Colombier 	csr32w(ctlr, Mcr, MDIread|(phyadd<<21)|(regadd<<16));
830*9ef1f84bSDavid du Colombier 	mcr = 0;
831*9ef1f84bSDavid du Colombier 	for(timo = 64; timo; timo--){
832*9ef1f84bSDavid du Colombier 		mcr = csr32r(ctlr, Mcr);
833*9ef1f84bSDavid du Colombier 		if(mcr & MDIready)
834*9ef1f84bSDavid du Colombier 			break;
835*9ef1f84bSDavid du Colombier 		microdelay(1);
836*9ef1f84bSDavid du Colombier 	}
837*9ef1f84bSDavid du Colombier 	unlock(&ctlr->miilock);
838*9ef1f84bSDavid du Colombier 
839*9ef1f84bSDavid du Colombier 	if(mcr & MDIready)
840*9ef1f84bSDavid du Colombier 		return mcr & 0xFFFF;
841*9ef1f84bSDavid du Colombier 
842*9ef1f84bSDavid du Colombier 	return -1;
843*9ef1f84bSDavid du Colombier }
844*9ef1f84bSDavid du Colombier 
845*9ef1f84bSDavid du Colombier static int
miiw(Ctlr * ctlr,int phyadd,int regadd,int data)846*9ef1f84bSDavid du Colombier miiw(Ctlr* ctlr, int phyadd, int regadd, int data)
847*9ef1f84bSDavid du Colombier {
848*9ef1f84bSDavid du Colombier 	int mcr, timo;
849*9ef1f84bSDavid du Colombier 
850*9ef1f84bSDavid du Colombier 	lock(&ctlr->miilock);
851*9ef1f84bSDavid du Colombier 	csr32w(ctlr, Mcr, MDIwrite|(phyadd<<21)|(regadd<<16)|(data & 0xFFFF));
852*9ef1f84bSDavid du Colombier 	mcr = 0;
853*9ef1f84bSDavid du Colombier 	for(timo = 64; timo; timo--){
854*9ef1f84bSDavid du Colombier 		mcr = csr32r(ctlr, Mcr);
855*9ef1f84bSDavid du Colombier 		if(mcr & MDIready)
856*9ef1f84bSDavid du Colombier 			break;
857*9ef1f84bSDavid du Colombier 		microdelay(1);
858*9ef1f84bSDavid du Colombier 	}
859*9ef1f84bSDavid du Colombier 	unlock(&ctlr->miilock);
860*9ef1f84bSDavid du Colombier 
861*9ef1f84bSDavid du Colombier 	if(mcr & MDIready)
862*9ef1f84bSDavid du Colombier 		return 0;
863*9ef1f84bSDavid du Colombier 
864*9ef1f84bSDavid du Colombier 	return -1;
865*9ef1f84bSDavid du Colombier }
866*9ef1f84bSDavid du Colombier 
867*9ef1f84bSDavid du Colombier static int
hy93c46r(Ctlr * ctlr,int r)868*9ef1f84bSDavid du Colombier hy93c46r(Ctlr* ctlr, int r)
869*9ef1f84bSDavid du Colombier {
870*9ef1f84bSDavid du Colombier 	int data, i, op, size;
871*9ef1f84bSDavid du Colombier 
872*9ef1f84bSDavid du Colombier 	/*
873*9ef1f84bSDavid du Colombier 	 * Hyundai HY93C46 or equivalent serial EEPROM.
874*9ef1f84bSDavid du Colombier 	 * This sequence for reading a 16-bit register 'r'
875*9ef1f84bSDavid du Colombier 	 * in the EEPROM is taken straight from Section
876*9ef1f84bSDavid du Colombier 	 * 3.3.4.2 of the Intel 82557 User's Guide.
877*9ef1f84bSDavid du Colombier 	 */
878*9ef1f84bSDavid du Colombier reread:
879*9ef1f84bSDavid du Colombier 	csr16w(ctlr, Ecr, EEcs);
880*9ef1f84bSDavid du Colombier 	op = EEstart|EEread;
881*9ef1f84bSDavid du Colombier 	for(i = 2; i >= 0; i--){
882*9ef1f84bSDavid du Colombier 		data = (((op>>i) & 0x01)<<2)|EEcs;
883*9ef1f84bSDavid du Colombier 		csr16w(ctlr, Ecr, data);
884*9ef1f84bSDavid du Colombier 		csr16w(ctlr, Ecr, data|EEsk);
885*9ef1f84bSDavid du Colombier 		microdelay(1);
886*9ef1f84bSDavid du Colombier 		csr16w(ctlr, Ecr, data);
887*9ef1f84bSDavid du Colombier 		microdelay(1);
888*9ef1f84bSDavid du Colombier 	}
889*9ef1f84bSDavid du Colombier 
890*9ef1f84bSDavid du Colombier 	/*
891*9ef1f84bSDavid du Colombier 	 * First time through must work out the EEPROM size.
892*9ef1f84bSDavid du Colombier 	 */
893*9ef1f84bSDavid du Colombier 	if((size = ctlr->eepromsz) == 0)
894*9ef1f84bSDavid du Colombier 		size = 8;
895*9ef1f84bSDavid du Colombier 
896*9ef1f84bSDavid du Colombier 	for(size = size-1; size >= 0; size--){
897*9ef1f84bSDavid du Colombier 		data = (((r>>size) & 0x01)<<2)|EEcs;
898*9ef1f84bSDavid du Colombier 		csr16w(ctlr, Ecr, data);
899*9ef1f84bSDavid du Colombier 		csr16w(ctlr, Ecr, data|EEsk);
900*9ef1f84bSDavid du Colombier 		delay(1);
901*9ef1f84bSDavid du Colombier 		csr16w(ctlr, Ecr, data);
902*9ef1f84bSDavid du Colombier 		microdelay(1);
903*9ef1f84bSDavid du Colombier 		if(!(csr16r(ctlr, Ecr) & EEdo))
904*9ef1f84bSDavid du Colombier 			break;
905*9ef1f84bSDavid du Colombier 	}
906*9ef1f84bSDavid du Colombier 
907*9ef1f84bSDavid du Colombier 	data = 0;
908*9ef1f84bSDavid du Colombier 	for(i = 15; i >= 0; i--){
909*9ef1f84bSDavid du Colombier 		csr16w(ctlr, Ecr, EEcs|EEsk);
910*9ef1f84bSDavid du Colombier 		microdelay(1);
911*9ef1f84bSDavid du Colombier 		if(csr16r(ctlr, Ecr) & EEdo)
912*9ef1f84bSDavid du Colombier 			data |= (1<<i);
913*9ef1f84bSDavid du Colombier 		csr16w(ctlr, Ecr, EEcs);
914*9ef1f84bSDavid du Colombier 		microdelay(1);
915*9ef1f84bSDavid du Colombier 	}
916*9ef1f84bSDavid du Colombier 
917*9ef1f84bSDavid du Colombier 	csr16w(ctlr, Ecr, 0);
918*9ef1f84bSDavid du Colombier 
919*9ef1f84bSDavid du Colombier 	if(ctlr->eepromsz == 0){
920*9ef1f84bSDavid du Colombier 		ctlr->eepromsz = 8-size;
921*9ef1f84bSDavid du Colombier 		ctlr->eeprom = malloc((1<<ctlr->eepromsz)*sizeof(ushort));
922*9ef1f84bSDavid du Colombier 		if(ctlr->eeprom == nil)
923*9ef1f84bSDavid du Colombier 			error(Enomem);
924*9ef1f84bSDavid du Colombier 		goto reread;
925*9ef1f84bSDavid du Colombier 	}
926*9ef1f84bSDavid du Colombier 
927*9ef1f84bSDavid du Colombier 	return data;
928*9ef1f84bSDavid du Colombier }
929*9ef1f84bSDavid du Colombier 
930*9ef1f84bSDavid du Colombier static void
i82557pci(void)931*9ef1f84bSDavid du Colombier i82557pci(void)
932*9ef1f84bSDavid du Colombier {
933*9ef1f84bSDavid du Colombier 	Pcidev *p;
934*9ef1f84bSDavid du Colombier 	Ctlr *ctlr;
935*9ef1f84bSDavid du Colombier 	int i, nop, port;
936*9ef1f84bSDavid du Colombier 
937*9ef1f84bSDavid du Colombier 	p = nil;
938*9ef1f84bSDavid du Colombier 	nop = 0;
939*9ef1f84bSDavid du Colombier 	while(p = pcimatch(p, 0x8086, 0)){
940*9ef1f84bSDavid du Colombier 		switch(p->did){
941*9ef1f84bSDavid du Colombier 		default:
942*9ef1f84bSDavid du Colombier 			continue;
943*9ef1f84bSDavid du Colombier 		case 0x1031:		/* Intel 82562EM */
944*9ef1f84bSDavid du Colombier 		case 0x103B:		/* Intel 82562EM */
945*9ef1f84bSDavid du Colombier 		case 0x103C:		/* Intel 82562EM */
946*9ef1f84bSDavid du Colombier 		case 0x1050:		/* Intel 82562EZ */
947*9ef1f84bSDavid du Colombier 		case 0x1039:		/* Intel 82801BD PRO/100 VE */
948*9ef1f84bSDavid du Colombier 		case 0x103A:		/* Intel 82562 PRO/100 VE */
949*9ef1f84bSDavid du Colombier 		case 0x103D:		/* Intel 82562 PRO/100 VE */
950*9ef1f84bSDavid du Colombier 		case 0x1064:		/* Intel 82562 PRO/100 VE */
951*9ef1f84bSDavid du Colombier 		case 0x2449:		/* Intel 82562ET */
952*9ef1f84bSDavid du Colombier 		case 0x27DC:		/* Intel 82801G PRO/100 VE */
953*9ef1f84bSDavid du Colombier 			nop = 1;
954*9ef1f84bSDavid du Colombier 			/*FALLTHROUGH*/
955*9ef1f84bSDavid du Colombier 		case 0x1209:		/* Intel 82559ER */
956*9ef1f84bSDavid du Colombier 		case 0x1229:		/* Intel 8255[789] */
957*9ef1f84bSDavid du Colombier 		case 0x1030:		/* Intel 82559 InBusiness 10/100  */
958*9ef1f84bSDavid du Colombier 			break;
959*9ef1f84bSDavid du Colombier 		}
960*9ef1f84bSDavid du Colombier 
961*9ef1f84bSDavid du Colombier 		if(pcigetpms(p) > 0){
962*9ef1f84bSDavid du Colombier 			pcisetpms(p, 0);
963*9ef1f84bSDavid du Colombier 
964*9ef1f84bSDavid du Colombier 			for(i = 0; i < 6; i++)
965*9ef1f84bSDavid du Colombier 				pcicfgw32(p, PciBAR0+i*4, p->mem[i].bar);
966*9ef1f84bSDavid du Colombier 			pcicfgw8(p, PciINTL, p->intl);
967*9ef1f84bSDavid du Colombier 			pcicfgw8(p, PciLTR, p->ltr);
968*9ef1f84bSDavid du Colombier 			pcicfgw8(p, PciCLS, p->cls);
969*9ef1f84bSDavid du Colombier 			pcicfgw16(p, PciPCR, p->pcr);
970*9ef1f84bSDavid du Colombier 		}
971*9ef1f84bSDavid du Colombier 
972*9ef1f84bSDavid du Colombier 		/*
973*9ef1f84bSDavid du Colombier 		 * bar[0] is the memory-mapped register address (4KB),
974*9ef1f84bSDavid du Colombier 		 * bar[1] is the I/O port register address (32 bytes) and
975*9ef1f84bSDavid du Colombier 		 * bar[2] is for the flash ROM (1MB).
976*9ef1f84bSDavid du Colombier 		 */
977*9ef1f84bSDavid du Colombier 		port = p->mem[1].bar & ~0x01;
978*9ef1f84bSDavid du Colombier 		if(ioalloc(port, p->mem[1].size, 0, "i82557") < 0){
979*9ef1f84bSDavid du Colombier 			print("i82557: port %#ux in use\n", port);
980*9ef1f84bSDavid du Colombier 			continue;
981*9ef1f84bSDavid du Colombier 		}
982*9ef1f84bSDavid du Colombier 
983*9ef1f84bSDavid du Colombier 		ctlr = malloc(sizeof(Ctlr));
984*9ef1f84bSDavid du Colombier 		if(ctlr == nil)
985*9ef1f84bSDavid du Colombier 			error(Enomem);
986*9ef1f84bSDavid du Colombier 		ctlr->port = port;
987*9ef1f84bSDavid du Colombier 		ctlr->pcidev = p;
988*9ef1f84bSDavid du Colombier 		ctlr->nop = nop;
989*9ef1f84bSDavid du Colombier 
990*9ef1f84bSDavid du Colombier 		if(ctlrhead != nil)
991*9ef1f84bSDavid du Colombier 			ctlrtail->next = ctlr;
992*9ef1f84bSDavid du Colombier 		else
993*9ef1f84bSDavid du Colombier 			ctlrhead = ctlr;
994*9ef1f84bSDavid du Colombier 		ctlrtail = ctlr;
995*9ef1f84bSDavid du Colombier 
996*9ef1f84bSDavid du Colombier 		pcisetbme(p);
997*9ef1f84bSDavid du Colombier 	}
998*9ef1f84bSDavid du Colombier }
999*9ef1f84bSDavid du Colombier 
1000*9ef1f84bSDavid du Colombier static char* mediatable[9] = {
1001*9ef1f84bSDavid du Colombier 	"10BASE-T",				/* TP */
1002*9ef1f84bSDavid du Colombier 	"10BASE-2",				/* BNC */
1003*9ef1f84bSDavid du Colombier 	"10BASE-5",				/* AUI */
1004*9ef1f84bSDavid du Colombier 	"100BASE-TX",
1005*9ef1f84bSDavid du Colombier 	"10BASE-TFD",
1006*9ef1f84bSDavid du Colombier 	"100BASE-TXFD",
1007*9ef1f84bSDavid du Colombier 	"100BASE-T4",
1008*9ef1f84bSDavid du Colombier 	"100BASE-FX",
1009*9ef1f84bSDavid du Colombier 	"100BASE-FXFD",
1010*9ef1f84bSDavid du Colombier };
1011*9ef1f84bSDavid du Colombier 
1012*9ef1f84bSDavid du Colombier static int
scanphy(Ctlr * ctlr)1013*9ef1f84bSDavid du Colombier scanphy(Ctlr* ctlr)
1014*9ef1f84bSDavid du Colombier {
1015*9ef1f84bSDavid du Colombier 	int i, oui, x;
1016*9ef1f84bSDavid du Colombier 
1017*9ef1f84bSDavid du Colombier 	for(i = 0; i < 32; i++){
1018*9ef1f84bSDavid du Colombier 		if((oui = miir(ctlr, i, 2)) == -1 || oui == 0 || oui == 0xFFFF)
1019*9ef1f84bSDavid du Colombier 			continue;
1020*9ef1f84bSDavid du Colombier 		oui <<= 6;
1021*9ef1f84bSDavid du Colombier 		x = miir(ctlr, i, 3);
1022*9ef1f84bSDavid du Colombier 		oui |= x>>10;
1023*9ef1f84bSDavid du Colombier 		//print("phy%d: oui %#ux reg1 %#ux\n", i, oui, miir(ctlr, i, 1));
1024*9ef1f84bSDavid du Colombier 
1025*9ef1f84bSDavid du Colombier 		ctlr->eeprom[6] = i;
1026*9ef1f84bSDavid du Colombier 		if(oui == 0xAA00)
1027*9ef1f84bSDavid du Colombier 			ctlr->eeprom[6] |= 0x07<<8;
1028*9ef1f84bSDavid du Colombier 		else if(oui == 0x80017){
1029*9ef1f84bSDavid du Colombier 			if(x & 0x01)
1030*9ef1f84bSDavid du Colombier 				ctlr->eeprom[6] |= 0x0A<<8;
1031*9ef1f84bSDavid du Colombier 			else
1032*9ef1f84bSDavid du Colombier 				ctlr->eeprom[6] |= 0x04<<8;
1033*9ef1f84bSDavid du Colombier 		}
1034*9ef1f84bSDavid du Colombier 		return i;
1035*9ef1f84bSDavid du Colombier 	}
1036*9ef1f84bSDavid du Colombier 	return -1;
1037*9ef1f84bSDavid du Colombier }
1038*9ef1f84bSDavid du Colombier 
1039*9ef1f84bSDavid du Colombier static void
shutdown(Ether * ether)1040*9ef1f84bSDavid du Colombier shutdown(Ether* ether)
1041*9ef1f84bSDavid du Colombier {
1042*9ef1f84bSDavid du Colombier 	Ctlr *ctlr = ether->ctlr;
1043*9ef1f84bSDavid du Colombier 
1044*9ef1f84bSDavid du Colombier 	csr32w(ctlr, Port, 0);
1045*9ef1f84bSDavid du Colombier 	delay(1);
1046*9ef1f84bSDavid du Colombier 	csr8w(ctlr, Interrupt, InterruptM);
1047*9ef1f84bSDavid du Colombier }
1048*9ef1f84bSDavid du Colombier 
1049*9ef1f84bSDavid du Colombier 
1050*9ef1f84bSDavid du Colombier static int
reset(Ether * ether)1051*9ef1f84bSDavid du Colombier reset(Ether* ether)
1052*9ef1f84bSDavid du Colombier {
1053*9ef1f84bSDavid du Colombier 	int anar, anlpar, bmcr, bmsr, i, k, medium, phyaddr, x;
1054*9ef1f84bSDavid du Colombier 	unsigned short sum;
1055*9ef1f84bSDavid du Colombier 	uchar ea[Eaddrlen];
1056*9ef1f84bSDavid du Colombier 	Ctlr *ctlr;
1057*9ef1f84bSDavid du Colombier 
1058*9ef1f84bSDavid du Colombier 	if(ctlrhead == nil)
1059*9ef1f84bSDavid du Colombier 		i82557pci();
1060*9ef1f84bSDavid du Colombier 
1061*9ef1f84bSDavid du Colombier 	/*
1062*9ef1f84bSDavid du Colombier 	 * Any adapter matches if no ether->port is supplied,
1063*9ef1f84bSDavid du Colombier 	 * otherwise the ports must match.
1064*9ef1f84bSDavid du Colombier 	 */
1065*9ef1f84bSDavid du Colombier 	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
1066*9ef1f84bSDavid du Colombier 		if(ctlr->active)
1067*9ef1f84bSDavid du Colombier 			continue;
1068*9ef1f84bSDavid du Colombier 		if(ether->port == 0 || ether->port == ctlr->port){
1069*9ef1f84bSDavid du Colombier 			ctlr->active = 1;
1070*9ef1f84bSDavid du Colombier 			break;
1071*9ef1f84bSDavid du Colombier 		}
1072*9ef1f84bSDavid du Colombier 	}
1073*9ef1f84bSDavid du Colombier 	if(ctlr == nil)
1074*9ef1f84bSDavid du Colombier 		return -1;
1075*9ef1f84bSDavid du Colombier 
1076*9ef1f84bSDavid du Colombier 	/*
1077*9ef1f84bSDavid du Colombier 	 * Initialise the Ctlr structure.
1078*9ef1f84bSDavid du Colombier 	 * Perform a software reset after which should ensure busmastering
1079*9ef1f84bSDavid du Colombier 	 * is still enabled. The EtherExpress PRO/100B appears to leave
1080*9ef1f84bSDavid du Colombier 	 * the PCI configuration alone (see the 'To do' list above) so punt
1081*9ef1f84bSDavid du Colombier 	 * for now.
1082*9ef1f84bSDavid du Colombier 	 * Load the RUB and CUB registers for linear addressing (0).
1083*9ef1f84bSDavid du Colombier 	 */
1084*9ef1f84bSDavid du Colombier 	ether->ctlr = ctlr;
1085*9ef1f84bSDavid du Colombier 	ether->port = ctlr->port;
1086*9ef1f84bSDavid du Colombier 	ether->irq = ctlr->pcidev->intl;
1087*9ef1f84bSDavid du Colombier 	ether->tbdf = ctlr->pcidev->tbdf;
1088*9ef1f84bSDavid du Colombier 
1089*9ef1f84bSDavid du Colombier 	ilock(&ctlr->rlock);
1090*9ef1f84bSDavid du Colombier 	csr32w(ctlr, Port, 0);
1091*9ef1f84bSDavid du Colombier 	delay(1);
1092*9ef1f84bSDavid du Colombier 	csr8w(ctlr, Interrupt, InterruptM);
1093*9ef1f84bSDavid du Colombier 	iunlock(&ctlr->rlock);
1094*9ef1f84bSDavid du Colombier 
1095*9ef1f84bSDavid du Colombier 	command(ctlr, LoadRUB, 0);
1096*9ef1f84bSDavid du Colombier 	command(ctlr, LoadCUB, 0);
1097*9ef1f84bSDavid du Colombier 	command(ctlr, LoadDCA, PADDR(ctlr->dump));
1098*9ef1f84bSDavid du Colombier 
1099*9ef1f84bSDavid du Colombier 	/*
1100*9ef1f84bSDavid du Colombier 	 * Initialise the receive frame, transmit ring and configuration areas.
1101*9ef1f84bSDavid du Colombier 	 */
1102*9ef1f84bSDavid du Colombier 	ctlr->ncb = Ncb;
1103*9ef1f84bSDavid du Colombier 	ctlrinit(ctlr);
1104*9ef1f84bSDavid du Colombier 
1105*9ef1f84bSDavid du Colombier 	/*
1106*9ef1f84bSDavid du Colombier 	 * Read the EEPROM.
1107*9ef1f84bSDavid du Colombier 	 * Do a dummy read first to get the size
1108*9ef1f84bSDavid du Colombier 	 * and allocate ctlr->eeprom.
1109*9ef1f84bSDavid du Colombier 	 */
1110*9ef1f84bSDavid du Colombier 	hy93c46r(ctlr, 0);
1111*9ef1f84bSDavid du Colombier 	sum = 0;
1112*9ef1f84bSDavid du Colombier 	for(i = 0; i < (1<<ctlr->eepromsz); i++){
1113*9ef1f84bSDavid du Colombier 		x = hy93c46r(ctlr, i);
1114*9ef1f84bSDavid du Colombier 		ctlr->eeprom[i] = x;
1115*9ef1f84bSDavid du Colombier 		sum += x;
1116*9ef1f84bSDavid du Colombier 	}
1117*9ef1f84bSDavid du Colombier 	if(sum != 0xBABA)
1118*9ef1f84bSDavid du Colombier 		print("#l%d: EEPROM checksum - %#4.4ux\n", ether->ctlrno, sum);
1119*9ef1f84bSDavid du Colombier 
1120*9ef1f84bSDavid du Colombier 	/*
1121*9ef1f84bSDavid du Colombier 	 * Eeprom[6] indicates whether there is a PHY and whether
1122*9ef1f84bSDavid du Colombier 	 * it's not 10Mb-only, in which case use the given PHY address
1123*9ef1f84bSDavid du Colombier 	 * to set any PHY specific options and determine the speed.
1124*9ef1f84bSDavid du Colombier 	 * Unfortunately, sometimes the EEPROM is blank except for
1125*9ef1f84bSDavid du Colombier 	 * the ether address and checksum; in this case look at the
1126*9ef1f84bSDavid du Colombier 	 * controller type and if it's am 82558 or 82559 it has an
1127*9ef1f84bSDavid du Colombier 	 * embedded PHY so scan for that.
1128*9ef1f84bSDavid du Colombier 	 * If no PHY, assume 82503 (serial) operation.
1129*9ef1f84bSDavid du Colombier 	 */
1130*9ef1f84bSDavid du Colombier 	if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000))
1131*9ef1f84bSDavid du Colombier 		phyaddr = ctlr->eeprom[6] & 0x00FF;
1132*9ef1f84bSDavid du Colombier 	else
1133*9ef1f84bSDavid du Colombier 	switch(ctlr->pcidev->rid){
1134*9ef1f84bSDavid du Colombier 	case 0x01:			/* 82557 A-step */
1135*9ef1f84bSDavid du Colombier 	case 0x02:			/* 82557 B-step */
1136*9ef1f84bSDavid du Colombier 	case 0x03:			/* 82557 C-step */
1137*9ef1f84bSDavid du Colombier 	default:
1138*9ef1f84bSDavid du Colombier 		phyaddr = -1;
1139*9ef1f84bSDavid du Colombier 		break;
1140*9ef1f84bSDavid du Colombier 	case 0x04:			/* 82558 A-step */
1141*9ef1f84bSDavid du Colombier 	case 0x05:			/* 82558 B-step */
1142*9ef1f84bSDavid du Colombier 	case 0x06:			/* 82559 A-step */
1143*9ef1f84bSDavid du Colombier 	case 0x07:			/* 82559 B-step */
1144*9ef1f84bSDavid du Colombier 	case 0x08:			/* 82559 C-step */
1145*9ef1f84bSDavid du Colombier 	case 0x09:			/* 82559ER A-step */
1146*9ef1f84bSDavid du Colombier 		phyaddr = scanphy(ctlr);
1147*9ef1f84bSDavid du Colombier 		break;
1148*9ef1f84bSDavid du Colombier 	}
1149*9ef1f84bSDavid du Colombier 	if(phyaddr >= 0){
1150*9ef1f84bSDavid du Colombier 		/*
1151*9ef1f84bSDavid du Colombier 		 * Resolve the highest common ability of the two
1152*9ef1f84bSDavid du Colombier 		 * link partners. In descending order:
1153*9ef1f84bSDavid du Colombier 		 *	0x0100		100BASE-TX Full Duplex
1154*9ef1f84bSDavid du Colombier 		 *	0x0200		100BASE-T4
1155*9ef1f84bSDavid du Colombier 		 *	0x0080		100BASE-TX
1156*9ef1f84bSDavid du Colombier 		 *	0x0040		10BASE-T Full Duplex
1157*9ef1f84bSDavid du Colombier 		 *	0x0020		10BASE-T
1158*9ef1f84bSDavid du Colombier 		 */
1159*9ef1f84bSDavid du Colombier 		anar = miir(ctlr, phyaddr, 0x04);
1160*9ef1f84bSDavid du Colombier 		anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0;
1161*9ef1f84bSDavid du Colombier 		anar &= anlpar;
1162*9ef1f84bSDavid du Colombier 		bmcr = 0;
1163*9ef1f84bSDavid du Colombier 		if(anar & 0x380)
1164*9ef1f84bSDavid du Colombier 			bmcr = 0x2000;
1165*9ef1f84bSDavid du Colombier 		if(anar & 0x0140)
1166*9ef1f84bSDavid du Colombier 			bmcr |= 0x0100;
1167*9ef1f84bSDavid du Colombier 
1168*9ef1f84bSDavid du Colombier 		switch((ctlr->eeprom[6]>>8) & 0x001F){
1169*9ef1f84bSDavid du Colombier 
1170*9ef1f84bSDavid du Colombier 		case 0x04:				/* DP83840 */
1171*9ef1f84bSDavid du Colombier 		case 0x0A:				/* DP83840A */
1172*9ef1f84bSDavid du Colombier 			/*
1173*9ef1f84bSDavid du Colombier 			 * The DP83840[A] requires some tweaking for
1174*9ef1f84bSDavid du Colombier 			 * reliable operation.
1175*9ef1f84bSDavid du Colombier 			 * The manual says bit 10 should be unconditionally
1176*9ef1f84bSDavid du Colombier 			 * set although it supposedly only affects full-duplex
1177*9ef1f84bSDavid du Colombier 			 * operation (an & 0x0140).
1178*9ef1f84bSDavid du Colombier 			 */
1179*9ef1f84bSDavid du Colombier 			x = miir(ctlr, phyaddr, 0x17) & ~0x0520;
1180*9ef1f84bSDavid du Colombier 			x |= 0x0420;
1181*9ef1f84bSDavid du Colombier 			for(i = 0; i < ether->nopt; i++){
1182*9ef1f84bSDavid du Colombier 				if(cistrcmp(ether->opt[i], "congestioncontrol"))
1183*9ef1f84bSDavid du Colombier 					continue;
1184*9ef1f84bSDavid du Colombier 				x |= 0x0100;
1185*9ef1f84bSDavid du Colombier 				break;
1186*9ef1f84bSDavid du Colombier 			}
1187*9ef1f84bSDavid du Colombier 			miiw(ctlr, phyaddr, 0x17, x);
1188*9ef1f84bSDavid du Colombier 
1189*9ef1f84bSDavid du Colombier 			/*
1190*9ef1f84bSDavid du Colombier 			 * If the link partner can't autonegotiate, determine
1191*9ef1f84bSDavid du Colombier 			 * the speed from elsewhere.
1192*9ef1f84bSDavid du Colombier 			 */
1193*9ef1f84bSDavid du Colombier 			if(anlpar == 0){
1194*9ef1f84bSDavid du Colombier 				miir(ctlr, phyaddr, 0x01);
1195*9ef1f84bSDavid du Colombier 				bmsr = miir(ctlr, phyaddr, 0x01);
1196*9ef1f84bSDavid du Colombier 				x = miir(ctlr, phyaddr, 0x19);
1197*9ef1f84bSDavid du Colombier 				if((bmsr & 0x0004) && !(x & 0x0040))
1198*9ef1f84bSDavid du Colombier 					bmcr = 0x2000;
1199*9ef1f84bSDavid du Colombier 			}
1200*9ef1f84bSDavid du Colombier 			break;
1201*9ef1f84bSDavid du Colombier 
1202*9ef1f84bSDavid du Colombier 		case 0x07:				/* Intel 82555 */
1203*9ef1f84bSDavid du Colombier 			/*
1204*9ef1f84bSDavid du Colombier 			 * Auto-negotiation may fail if the other end is
1205*9ef1f84bSDavid du Colombier 			 * a DP83840A and the cable is short.
1206*9ef1f84bSDavid du Colombier 			 */
1207*9ef1f84bSDavid du Colombier 			miir(ctlr, phyaddr, 0x01);
1208*9ef1f84bSDavid du Colombier 			bmsr = miir(ctlr, phyaddr, 0x01);
1209*9ef1f84bSDavid du Colombier 			if((miir(ctlr, phyaddr, 0) & 0x1000) && !(bmsr & 0x0020)){
1210*9ef1f84bSDavid du Colombier 				miiw(ctlr, phyaddr, 0x1A, 0x2010);
1211*9ef1f84bSDavid du Colombier 				x = miir(ctlr, phyaddr, 0);
1212*9ef1f84bSDavid du Colombier 				miiw(ctlr, phyaddr, 0, 0x0200|x);
1213*9ef1f84bSDavid du Colombier 				for(i = 0; i < 3000; i++){
1214*9ef1f84bSDavid du Colombier 					delay(1);
1215*9ef1f84bSDavid du Colombier 					if(miir(ctlr, phyaddr, 0x01) & 0x0020)
1216*9ef1f84bSDavid du Colombier 						break;
1217*9ef1f84bSDavid du Colombier 				}
1218*9ef1f84bSDavid du Colombier 				miiw(ctlr, phyaddr, 0x1A, 0x2000);
1219*9ef1f84bSDavid du Colombier 
1220*9ef1f84bSDavid du Colombier 				anar = miir(ctlr, phyaddr, 0x04);
1221*9ef1f84bSDavid du Colombier 				anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0;
1222*9ef1f84bSDavid du Colombier 				anar &= anlpar;
1223*9ef1f84bSDavid du Colombier 				bmcr = 0;
1224*9ef1f84bSDavid du Colombier 				if(anar & 0x380)
1225*9ef1f84bSDavid du Colombier 					bmcr = 0x2000;
1226*9ef1f84bSDavid du Colombier 				if(anar & 0x0140)
1227*9ef1f84bSDavid du Colombier 					bmcr |= 0x0100;
1228*9ef1f84bSDavid du Colombier 			}
1229*9ef1f84bSDavid du Colombier 			break;
1230*9ef1f84bSDavid du Colombier 		}
1231*9ef1f84bSDavid du Colombier 
1232*9ef1f84bSDavid du Colombier 		/*
1233*9ef1f84bSDavid du Colombier 		 * Force speed and duplex if no auto-negotiation.
1234*9ef1f84bSDavid du Colombier 		 */
1235*9ef1f84bSDavid du Colombier 		if(anlpar == 0){
1236*9ef1f84bSDavid du Colombier 			medium = -1;
1237*9ef1f84bSDavid du Colombier 			for(i = 0; i < ether->nopt; i++){
1238*9ef1f84bSDavid du Colombier 				for(k = 0; k < nelem(mediatable); k++){
1239*9ef1f84bSDavid du Colombier 					if(cistrcmp(mediatable[k], ether->opt[i]))
1240*9ef1f84bSDavid du Colombier 						continue;
1241*9ef1f84bSDavid du Colombier 					medium = k;
1242*9ef1f84bSDavid du Colombier 					break;
1243*9ef1f84bSDavid du Colombier 				}
1244*9ef1f84bSDavid du Colombier 
1245*9ef1f84bSDavid du Colombier 				switch(medium){
1246*9ef1f84bSDavid du Colombier 				default:
1247*9ef1f84bSDavid du Colombier 					break;
1248*9ef1f84bSDavid du Colombier 
1249*9ef1f84bSDavid du Colombier 				case 0x00:			/* 10BASE-T */
1250*9ef1f84bSDavid du Colombier 				case 0x01:			/* 10BASE-2 */
1251*9ef1f84bSDavid du Colombier 				case 0x02:			/* 10BASE-5 */
1252*9ef1f84bSDavid du Colombier 					bmcr &= ~(0x2000|0x0100);
1253*9ef1f84bSDavid du Colombier 					ctlr->configdata[19] &= ~0x40;
1254*9ef1f84bSDavid du Colombier 					break;
1255*9ef1f84bSDavid du Colombier 
1256*9ef1f84bSDavid du Colombier 				case 0x03:			/* 100BASE-TX */
1257*9ef1f84bSDavid du Colombier 				case 0x06:			/* 100BASE-T4 */
1258*9ef1f84bSDavid du Colombier 				case 0x07:			/* 100BASE-FX */
1259*9ef1f84bSDavid du Colombier 					ctlr->configdata[19] &= ~0x40;
1260*9ef1f84bSDavid du Colombier 					bmcr |= 0x2000;
1261*9ef1f84bSDavid du Colombier 					break;
1262*9ef1f84bSDavid du Colombier 
1263*9ef1f84bSDavid du Colombier 				case 0x04:			/* 10BASE-TFD */
1264*9ef1f84bSDavid du Colombier 					bmcr = (bmcr & ~0x2000)|0x0100;
1265*9ef1f84bSDavid du Colombier 					ctlr->configdata[19] |= 0x40;
1266*9ef1f84bSDavid du Colombier 					break;
1267*9ef1f84bSDavid du Colombier 
1268*9ef1f84bSDavid du Colombier 				case 0x05:			/* 100BASE-TXFD */
1269*9ef1f84bSDavid du Colombier 				case 0x08:			/* 100BASE-FXFD */
1270*9ef1f84bSDavid du Colombier 					bmcr |= 0x2000|0x0100;
1271*9ef1f84bSDavid du Colombier 					ctlr->configdata[19] |= 0x40;
1272*9ef1f84bSDavid du Colombier 					break;
1273*9ef1f84bSDavid du Colombier 				}
1274*9ef1f84bSDavid du Colombier 			}
1275*9ef1f84bSDavid du Colombier 			if(medium != -1)
1276*9ef1f84bSDavid du Colombier 				miiw(ctlr, phyaddr, 0x00, bmcr);
1277*9ef1f84bSDavid du Colombier 		}
1278*9ef1f84bSDavid du Colombier 
1279*9ef1f84bSDavid du Colombier 		if(bmcr & 0x2000)
1280*9ef1f84bSDavid du Colombier 			ether->mbps = 100;
1281*9ef1f84bSDavid du Colombier 
1282*9ef1f84bSDavid du Colombier 		ctlr->configdata[8] = 1;
1283*9ef1f84bSDavid du Colombier 		ctlr->configdata[15] &= ~0x80;
1284*9ef1f84bSDavid du Colombier 	}
1285*9ef1f84bSDavid du Colombier 	else{
1286*9ef1f84bSDavid du Colombier 		ctlr->configdata[8] = 0;
1287*9ef1f84bSDavid du Colombier 		ctlr->configdata[15] |= 0x80;
1288*9ef1f84bSDavid du Colombier 	}
1289*9ef1f84bSDavid du Colombier 
1290*9ef1f84bSDavid du Colombier 	/*
1291*9ef1f84bSDavid du Colombier 	 * Workaround for some broken HUB chips when connected at 10Mb/s
1292*9ef1f84bSDavid du Colombier 	 * half-duplex.
1293*9ef1f84bSDavid du Colombier 	 * This is a band-aid, but as there's no dynamic auto-negotiation
1294*9ef1f84bSDavid du Colombier 	 * code at the moment, only deactivate the workaround code in txstart
1295*9ef1f84bSDavid du Colombier 	 * if the link is 100Mb/s.
1296*9ef1f84bSDavid du Colombier 	 */
1297*9ef1f84bSDavid du Colombier 	if(ether->mbps != 10)
1298*9ef1f84bSDavid du Colombier 		ctlr->nop = 0;
1299*9ef1f84bSDavid du Colombier 
1300*9ef1f84bSDavid du Colombier 	/*
1301*9ef1f84bSDavid du Colombier 	 * Load the chip configuration and start it off.
1302*9ef1f84bSDavid du Colombier 	 */
1303*9ef1f84bSDavid du Colombier 	if(ether->oq == 0)
1304*9ef1f84bSDavid du Colombier 		ether->oq = qopen(64*1024, Qmsg, 0, 0);
1305*9ef1f84bSDavid du Colombier 	configure(ether, 0);
1306*9ef1f84bSDavid du Colombier 	command(ctlr, CUstart, PADDR(&ctlr->cbr->status));
1307*9ef1f84bSDavid du Colombier 
1308*9ef1f84bSDavid du Colombier 	/*
1309*9ef1f84bSDavid du Colombier 	 * Check if the adapter's station address is to be overridden.
1310*9ef1f84bSDavid du Colombier 	 * If not, read it from the EEPROM and set in ether->ea prior to loading
1311*9ef1f84bSDavid du Colombier 	 * the station address with the Individual Address Setup command.
1312*9ef1f84bSDavid du Colombier 	 */
1313*9ef1f84bSDavid du Colombier 	memset(ea, 0, Eaddrlen);
1314*9ef1f84bSDavid du Colombier 	if(memcmp(ea, ether->ea, Eaddrlen) == 0){
1315*9ef1f84bSDavid du Colombier 		for(i = 0; i < Eaddrlen/2; i++){
1316*9ef1f84bSDavid du Colombier 			x = ctlr->eeprom[i];
1317*9ef1f84bSDavid du Colombier 			ether->ea[2*i] = x;
1318*9ef1f84bSDavid du Colombier 			ether->ea[2*i+1] = x>>8;
1319*9ef1f84bSDavid du Colombier 		}
1320*9ef1f84bSDavid du Colombier 	}
1321*9ef1f84bSDavid du Colombier 
1322*9ef1f84bSDavid du Colombier 	ilock(&ctlr->cblock);
1323*9ef1f84bSDavid du Colombier 	ctlr->action = CbIAS;
1324*9ef1f84bSDavid du Colombier 	txstart(ether);
1325*9ef1f84bSDavid du Colombier 	iunlock(&ctlr->cblock);
1326*9ef1f84bSDavid du Colombier 
1327*9ef1f84bSDavid du Colombier 	/*
1328*9ef1f84bSDavid du Colombier 	 * Linkage to the generic ethernet driver.
1329*9ef1f84bSDavid du Colombier 	 */
1330*9ef1f84bSDavid du Colombier 	ether->attach = attach;
1331*9ef1f84bSDavid du Colombier 	ether->transmit = transmit;
1332*9ef1f84bSDavid du Colombier 	ether->interrupt = interrupt;
1333*9ef1f84bSDavid du Colombier 	ether->ifstat = ifstat;
1334*9ef1f84bSDavid du Colombier 	ether->shutdown = shutdown;
1335*9ef1f84bSDavid du Colombier 
1336*9ef1f84bSDavid du Colombier 	ether->promiscuous = promiscuous;
1337*9ef1f84bSDavid du Colombier 	ether->multicast = multicast;
1338*9ef1f84bSDavid du Colombier 	ether->arg = ether;
1339*9ef1f84bSDavid du Colombier 
1340*9ef1f84bSDavid du Colombier 	return 0;
1341*9ef1f84bSDavid du Colombier }
1342*9ef1f84bSDavid du Colombier 
1343*9ef1f84bSDavid du Colombier void
ether82557link(void)1344*9ef1f84bSDavid du Colombier ether82557link(void)
1345*9ef1f84bSDavid du Colombier {
1346*9ef1f84bSDavid du Colombier 	addethercard("i82557",  reset);
1347*9ef1f84bSDavid du Colombier }
1348