xref: /plan9/sys/src/9/ppc/etherfcc.c (revision 5d9de2d38d2503efca29e12e0e32036368a7a75f)
1458db832SDavid du Colombier /*
2458db832SDavid du Colombier  * FCCn ethernet
3458db832SDavid du Colombier  */
4458db832SDavid du Colombier 
5458db832SDavid du Colombier #include "u.h"
6458db832SDavid du Colombier #include "../port/lib.h"
7458db832SDavid du Colombier #include "mem.h"
8458db832SDavid du Colombier #include "dat.h"
9458db832SDavid du Colombier #include "fns.h"
10458db832SDavid du Colombier #include "io.h"
114de34a7eSDavid du Colombier #include "imm.h"
12458db832SDavid du Colombier #include "../port/error.h"
13458db832SDavid du Colombier #include "../port/netif.h"
14458db832SDavid du Colombier 
15458db832SDavid du Colombier #include "etherif.h"
164de34a7eSDavid du Colombier #include "../ppc/ethermii.h"
1706578a4fSDavid du Colombier 
1806578a4fSDavid du Colombier #define DBG 1
19458db832SDavid du Colombier 
20458db832SDavid du Colombier enum {
21458db832SDavid du Colombier 	Nrdre		= 128,			/* receive descriptor ring entries */
22458db832SDavid du Colombier 	Ntdre		= 128,			/* transmit descriptor ring entries */
23458db832SDavid du Colombier 
24458db832SDavid du Colombier 	Rbsize		= ETHERMAXTU+4,		/* ring buffer size (+4 for CRC) */
2506578a4fSDavid du Colombier 	Bufsize		= Rbsize+CACHELINESZ,	/* extra room for alignment */
26458db832SDavid du Colombier };
27458db832SDavid du Colombier 
28458db832SDavid du Colombier enum {
29458db832SDavid du Colombier 
30458db832SDavid du Colombier 	/* ether-specific Rx BD bits */
31458db832SDavid du Colombier 	RxMiss=		SBIT(7),
32458db832SDavid du Colombier 	RxeLG=		SBIT(10),
33458db832SDavid du Colombier 	RxeNO=		SBIT(11),
34458db832SDavid du Colombier 	RxeSH=		SBIT(12),
35458db832SDavid du Colombier 	RxeCR=		SBIT(13),
36458db832SDavid du Colombier 	RxeOV=		SBIT(14),
37458db832SDavid du Colombier 	RxeCL=		SBIT(15),
38458db832SDavid du Colombier 	RxError=	(RxeLG|RxeNO|RxeSH|RxeCR|RxeOV|RxeCL),	/* various error flags */
39458db832SDavid du Colombier 
40458db832SDavid du Colombier 	/* ether-specific Tx BD bits */
41458db832SDavid du Colombier 	TxPad=		SBIT(1),	/* pad short frames */
42458db832SDavid du Colombier 	TxTC=		SBIT(5),	/* transmit CRC */
43458db832SDavid du Colombier 	TxeDEF=		SBIT(6),
44458db832SDavid du Colombier 	TxeHB=		SBIT(7),
45458db832SDavid du Colombier 	TxeLC=		SBIT(8),
46458db832SDavid du Colombier 	TxeRL=		SBIT(9),
47458db832SDavid du Colombier 	TxeUN=		SBIT(14),
48458db832SDavid du Colombier 	TxeCSL=		SBIT(15),
49458db832SDavid du Colombier 
50458db832SDavid du Colombier 	/* psmr */
51458db832SDavid du Colombier 	CRCE=		BIT(24),	/* Ethernet CRC */
5206578a4fSDavid du Colombier 	FCE=		BIT(10),	/* flow control */
5306578a4fSDavid du Colombier 	PRO=		BIT(9),		/* promiscuous mode */
5406578a4fSDavid du Colombier 	FDE=		BIT(5),		/* full duplex ethernet */
5506578a4fSDavid du Colombier 	LPB=		BIT(3),		/* local protect bit */
56458db832SDavid du Colombier 
57458db832SDavid du Colombier 	/* gfmr */
58458db832SDavid du Colombier 	ENET=		0xc,		/* ethernet mode */
59458db832SDavid du Colombier 	ENT=		BIT(27),
60458db832SDavid du Colombier 	ENR=		BIT(26),
61458db832SDavid du Colombier 	TCI=		BIT(2),
62458db832SDavid du Colombier 
63458db832SDavid du Colombier 	/* FCC function code register */
64458db832SDavid du Colombier 	GBL=		0x20,
65458db832SDavid du Colombier 	BO=		0x18,
66458db832SDavid du Colombier 	EB=		0x10,		/* Motorola byte order */
67458db832SDavid du Colombier 	TC2=		0x04,
68458db832SDavid du Colombier 	DTB=		0x02,
69458db832SDavid du Colombier 	BDB=		0x01,
70458db832SDavid du Colombier 
71458db832SDavid du Colombier 	/* FCC Event/Mask bits */
72458db832SDavid du Colombier 	GRA=		SBIT(8),
73458db832SDavid du Colombier 	RXC=		SBIT(9),
74458db832SDavid du Colombier 	TXC=		SBIT(10),
75458db832SDavid du Colombier 	TXE=		SBIT(11),
76458db832SDavid du Colombier 	RXF=		SBIT(12),
77458db832SDavid du Colombier 	BSY=		SBIT(13),
78458db832SDavid du Colombier 	TXB=		SBIT(14),
79458db832SDavid du Colombier 	RXB=		SBIT(15),
80458db832SDavid du Colombier };
81458db832SDavid du Colombier 
8206578a4fSDavid du Colombier enum {		/* Mcr */
8306578a4fSDavid du Colombier 	MDIread	=	0x60020000,	/* read opcode */
8406578a4fSDavid du Colombier 	MDIwrite =	0x50020000,	/* write opcode */
8506578a4fSDavid du Colombier };
8606578a4fSDavid du Colombier 
87458db832SDavid du Colombier typedef struct Etherparam Etherparam;
88458db832SDavid du Colombier struct Etherparam {
89458db832SDavid du Colombier /*0x00*/	FCCparam;
90458db832SDavid du Colombier /*0x3c*/	ulong	stat_buf;
91458db832SDavid du Colombier /*0x40*/	ulong	cam_ptr;
92458db832SDavid du Colombier /*0x44*/	ulong	cmask;
93458db832SDavid du Colombier /*0x48*/	ulong	cpres;
94458db832SDavid du Colombier /*0x4c*/	ulong	crcec;
95458db832SDavid du Colombier /*0x50*/	ulong	alec;
96458db832SDavid du Colombier /*0x54*/	ulong	disfc;
97458db832SDavid du Colombier /*0x58*/	ushort	retlim;
98458db832SDavid du Colombier /*0x5a*/	ushort	retcnt;
99458db832SDavid du Colombier /*0x5c*/	ushort	p_per;
100458db832SDavid du Colombier /*0x5e*/	ushort	boff_cnt;
101458db832SDavid du Colombier /*0x60*/	ulong	gaddr[2];
102458db832SDavid du Colombier /*0x68*/	ushort	tfcstat;
103458db832SDavid du Colombier /*0x6a*/	ushort	tfclen;
104458db832SDavid du Colombier /*0x6c*/	ulong	tfcptr;
105458db832SDavid du Colombier /*0x70*/	ushort	mflr;
106458db832SDavid du Colombier /*0x72*/	ushort	paddr[3];
107458db832SDavid du Colombier /*0x78*/	ushort	ibd_cnt;
108458db832SDavid du Colombier /*0x7a*/	ushort	ibd_start;
109458db832SDavid du Colombier /*0x7c*/	ushort	ibd_end;
110458db832SDavid du Colombier /*0x7e*/	ushort	tx_len;
111458db832SDavid du Colombier /*0x80*/	uchar	ibd_base[32];
112458db832SDavid du Colombier /*0xa0*/	ulong	iaddr[2];
113458db832SDavid du Colombier /*0xa8*/	ushort	minflr;
114458db832SDavid du Colombier /*0xaa*/	ushort	taddr[3];
115458db832SDavid du Colombier /*0xb0*/	ushort	padptr;
116458db832SDavid du Colombier /*0xb2*/	ushort	Rsvdb2;
117458db832SDavid du Colombier /*0xb4*/	ushort	cf_range;
118458db832SDavid du Colombier /*0xb6*/	ushort	max_b;
119458db832SDavid du Colombier /*0xb8*/	ushort	maxd1;
120458db832SDavid du Colombier /*0xba*/	ushort	maxd2;
121458db832SDavid du Colombier /*0xbc*/	ushort	maxd;
122458db832SDavid du Colombier /*0xbe*/	ushort	dma_cnt;
123458db832SDavid du Colombier /*0xc0*/	ulong	octc;
124458db832SDavid du Colombier /*0xc4*/	ulong	colc;
125458db832SDavid du Colombier /*0xc8*/	ulong	broc;
126458db832SDavid du Colombier /*0xcc*/	ulong	mulc;
127458db832SDavid du Colombier /*0xd0*/	ulong	uspc;
128458db832SDavid du Colombier /*0xd4*/	ulong	frgc;
129458db832SDavid du Colombier /*0xd8*/	ulong	ospc;
130458db832SDavid du Colombier /*0xdc*/	ulong	jbrc;
131458db832SDavid du Colombier /*0xe0*/	ulong	p64c;
132458db832SDavid du Colombier /*0xe4*/	ulong	p65c;
133458db832SDavid du Colombier /*0xe8*/	ulong	p128c;
134458db832SDavid du Colombier /*0xec*/	ulong	p256c;
135458db832SDavid du Colombier /*0xf0*/	ulong	p512c;
136458db832SDavid du Colombier /*0xf4*/	ulong	p1024c;
137458db832SDavid du Colombier /*0xf8*/	ulong	cam_buf;
138458db832SDavid du Colombier /*0xfc*/	ulong	Rsvdfc;
139458db832SDavid du Colombier /*0x100*/
140458db832SDavid du Colombier };
141458db832SDavid du Colombier 
14206578a4fSDavid du Colombier typedef struct Ctlr Ctlr;
14306578a4fSDavid du Colombier struct Ctlr {
144458db832SDavid du Colombier 	Lock;
145458db832SDavid du Colombier 	int	fccid;
146458db832SDavid du Colombier 	int	port;
147458db832SDavid du Colombier 	ulong	pmdio;
148458db832SDavid du Colombier 	ulong	pmdck;
149458db832SDavid du Colombier 	int	init;
150458db832SDavid du Colombier 	int	active;
15106578a4fSDavid du Colombier 	int	duplex;		/* 1 == full */
152458db832SDavid du Colombier 	FCC*	fcc;
153458db832SDavid du Colombier 
154458db832SDavid du Colombier 	Ring;
15506578a4fSDavid du Colombier 	Block*	rcvbufs[Nrdre];
15606578a4fSDavid du Colombier 	Mii*	mii;
15706578a4fSDavid du Colombier 	Timer;
158458db832SDavid du Colombier 
159458db832SDavid du Colombier 	ulong	interrupts;	/* statistics */
160458db832SDavid du Colombier 	ulong	deferred;
161458db832SDavid du Colombier 	ulong	heartbeat;
162458db832SDavid du Colombier 	ulong	latecoll;
163458db832SDavid du Colombier 	ulong	retrylim;
164458db832SDavid du Colombier 	ulong	underrun;
165458db832SDavid du Colombier 	ulong	overrun;
166458db832SDavid du Colombier 	ulong	carrierlost;
167458db832SDavid du Colombier 	ulong	retrycount;
16806578a4fSDavid du Colombier };
169458db832SDavid du Colombier 
170458db832SDavid du Colombier static	int	fccirq[] = {0x20, 0x21, 0x22};
171458db832SDavid du Colombier static	int	fccid[] = {FCC1ID, FCC2ID, FCC3ID};
172458db832SDavid du Colombier 
17306578a4fSDavid du Colombier #ifdef DBG
17406578a4fSDavid du Colombier ulong fccrhisto[16];
17506578a4fSDavid du Colombier ulong fccthisto[16];
17606578a4fSDavid du Colombier ulong fccrthisto[16];
17706578a4fSDavid du Colombier ulong fcctrhisto[16];
17806578a4fSDavid du Colombier ulong ehisto[0x80];
17906578a4fSDavid du Colombier #endif
18006578a4fSDavid du Colombier 
18106578a4fSDavid du Colombier static int fccmiimir(Mii*, int, int);
18206578a4fSDavid du Colombier static int fccmiimiw(Mii*, int, int, int);
18306578a4fSDavid du Colombier static void fccltimer(Ureg*, Timer*);
184458db832SDavid du Colombier 
185458db832SDavid du Colombier static void
attach(Ether * ether)186458db832SDavid du Colombier attach(Ether *ether)
187458db832SDavid du Colombier {
188458db832SDavid du Colombier 	Ctlr *ctlr;
189458db832SDavid du Colombier 
190458db832SDavid du Colombier 	ctlr = ether->ctlr;
19106578a4fSDavid du Colombier 	ilock(ctlr);
192458db832SDavid du Colombier 	ctlr->active = 1;
193458db832SDavid du Colombier 	ctlr->fcc->gfmr |= ENR|ENT;
19406578a4fSDavid du Colombier 	iunlock(ctlr);
19506578a4fSDavid du Colombier 	ctlr->tmode = Tperiodic;
19606578a4fSDavid du Colombier 	ctlr->tf = fccltimer;
19706578a4fSDavid du Colombier 	ctlr->ta = ether;
19806578a4fSDavid du Colombier 	ctlr->tns = 5000000000LL;	/* 5 seconds */
19906578a4fSDavid du Colombier 	timeradd(ctlr);
200458db832SDavid du Colombier }
201458db832SDavid du Colombier 
202458db832SDavid du Colombier static void
closed(Ether * ether)203458db832SDavid du Colombier closed(Ether *ether)
204458db832SDavid du Colombier {
205458db832SDavid du Colombier 	Ctlr *ctlr;
206458db832SDavid du Colombier 
207458db832SDavid du Colombier 	ctlr = ether->ctlr;
208458db832SDavid du Colombier 	ilock(ctlr);
209458db832SDavid du Colombier 	ctlr->active = 0;
210458db832SDavid du Colombier 	ctlr->fcc->gfmr &= ~(ENR|ENT);
211458db832SDavid du Colombier 	iunlock(ctlr);
212458db832SDavid du Colombier 	print("Ether closed\n");
213458db832SDavid du Colombier }
214458db832SDavid du Colombier 
215458db832SDavid du Colombier static void
promiscuous(void * arg,int on)216458db832SDavid du Colombier promiscuous(void* arg, int on)
217458db832SDavid du Colombier {
218458db832SDavid du Colombier 	Ether *ether;
219458db832SDavid du Colombier 	Ctlr *ctlr;
220458db832SDavid du Colombier 
221458db832SDavid du Colombier 	ether = (Ether*)arg;
222458db832SDavid du Colombier 	ctlr = ether->ctlr;
223458db832SDavid du Colombier 
224458db832SDavid du Colombier 	ilock(ctlr);
225458db832SDavid du Colombier 	if(on || ether->nmaddr)
226458db832SDavid du Colombier 		ctlr->fcc->fpsmr |= PRO;
227458db832SDavid du Colombier 	else
228458db832SDavid du Colombier 		ctlr->fcc->fpsmr &= ~PRO;
229458db832SDavid du Colombier 	iunlock(ctlr);
230458db832SDavid du Colombier }
231458db832SDavid du Colombier 
232458db832SDavid du Colombier static void
multicast(void * arg,uchar * addr,int on)233458db832SDavid du Colombier multicast(void* arg, uchar *addr, int on)
234458db832SDavid du Colombier {
235458db832SDavid du Colombier 	Ether *ether;
236458db832SDavid du Colombier 	Ctlr *ctlr;
237458db832SDavid du Colombier 
238458db832SDavid du Colombier 	USED(addr, on);	/* if on, could SetGroupAddress; if !on, it's hard */
239458db832SDavid du Colombier 
240458db832SDavid du Colombier 	ether = (Ether*)arg;
241458db832SDavid du Colombier 	ctlr = ether->ctlr;
242458db832SDavid du Colombier 
243458db832SDavid du Colombier 	ilock(ctlr);
244458db832SDavid du Colombier 	if(ether->prom || ether->nmaddr)
245458db832SDavid du Colombier 		ctlr->fcc->fpsmr |= PRO;
246458db832SDavid du Colombier 	else
247458db832SDavid du Colombier 		ctlr->fcc->fpsmr &= ~PRO;
248458db832SDavid du Colombier 	iunlock(ctlr);
249458db832SDavid du Colombier }
250458db832SDavid du Colombier 
251458db832SDavid du Colombier static void
txstart(Ether * ether)252458db832SDavid du Colombier txstart(Ether *ether)
253458db832SDavid du Colombier {
254458db832SDavid du Colombier 	int len;
255458db832SDavid du Colombier 	Ctlr *ctlr;
256458db832SDavid du Colombier 	Block *b;
257458db832SDavid du Colombier 	BD *dre;
258458db832SDavid du Colombier 
259458db832SDavid du Colombier 	ctlr = ether->ctlr;
260458db832SDavid du Colombier 	if(ctlr->init)
261458db832SDavid du Colombier 		return;
262458db832SDavid du Colombier 	while(ctlr->ntq < Ntdre-1){
263458db832SDavid du Colombier 		b = qget(ether->oq);
264458db832SDavid du Colombier 		if(b == 0)
265458db832SDavid du Colombier 			break;
266458db832SDavid du Colombier 
267458db832SDavid du Colombier 		dre = &ctlr->tdr[ctlr->tdrh];
268458db832SDavid du Colombier 		dczap(dre, sizeof(BD));
269458db832SDavid du Colombier 		if(dre->status & BDReady)
270458db832SDavid du Colombier 			panic("ether: txstart");
271458db832SDavid du Colombier 
272458db832SDavid du Colombier 		/*
273458db832SDavid du Colombier 		 * Give ownership of the descriptor to the chip, increment the
274458db832SDavid du Colombier 		 * software ring descriptor pointer and tell the chip to poll.
275458db832SDavid du Colombier 		 */
276458db832SDavid du Colombier 		len = BLEN(b);
277458db832SDavid du Colombier 		if(ctlr->txb[ctlr->tdrh] != nil)
278458db832SDavid du Colombier 			panic("fcc/ether: txstart");
279458db832SDavid du Colombier 		ctlr->txb[ctlr->tdrh] = b;
280458db832SDavid du Colombier 		if((ulong)b->rp&1)
281458db832SDavid du Colombier 			panic("fcc/ether: txstart align");	/* TO DO: ensure alignment */
282458db832SDavid du Colombier 		dre->addr = PADDR(b->rp);
283458db832SDavid du Colombier 		dre->length = len;
284458db832SDavid du Colombier 		dcflush(b->rp, len);
285458db832SDavid du Colombier 		dcflush(dre, sizeof(BD));
286458db832SDavid du Colombier 		dre->status = (dre->status & BDWrap) | BDReady|TxPad|BDInt|BDLast|TxTC;
287458db832SDavid du Colombier 		dcflush(dre, sizeof(BD));
28806578a4fSDavid du Colombier /*		ctlr->fcc->ftodr = 1<<15;	/* transmit now; Don't do this according to errata */
289458db832SDavid du Colombier 		ctlr->ntq++;
290458db832SDavid du Colombier 		ctlr->tdrh = NEXT(ctlr->tdrh, Ntdre);
291458db832SDavid du Colombier 	}
292458db832SDavid du Colombier }
293458db832SDavid du Colombier 
294458db832SDavid du Colombier static void
transmit(Ether * ether)295458db832SDavid du Colombier transmit(Ether* ether)
296458db832SDavid du Colombier {
297458db832SDavid du Colombier 	Ctlr *ctlr;
298458db832SDavid du Colombier 
299458db832SDavid du Colombier 	ctlr = ether->ctlr;
300458db832SDavid du Colombier 	ilock(ctlr);
301458db832SDavid du Colombier 	txstart(ether);
302458db832SDavid du Colombier 	iunlock(ctlr);
303458db832SDavid du Colombier }
304458db832SDavid du Colombier 
305458db832SDavid du Colombier static void
interrupt(Ureg *,void * arg)306458db832SDavid du Colombier interrupt(Ureg*, void *arg)
307458db832SDavid du Colombier {
30806578a4fSDavid du Colombier 	int len, status, rcvd, xmtd, restart;
309458db832SDavid du Colombier 	ushort events;
310458db832SDavid du Colombier 	Ctlr *ctlr;
311458db832SDavid du Colombier 	BD *dre;
31206578a4fSDavid du Colombier 	Block *b, *nb;
313458db832SDavid du Colombier 	Ether *ether = arg;
314458db832SDavid du Colombier 
315458db832SDavid du Colombier 	ctlr = ether->ctlr;
316458db832SDavid du Colombier 	if(!ctlr->active)
317458db832SDavid du Colombier 		return;	/* not ours */
318458db832SDavid du Colombier 
319458db832SDavid du Colombier 	/*
320458db832SDavid du Colombier 	 * Acknowledge all interrupts and whine about those that shouldn't
321458db832SDavid du Colombier 	 * happen.
322458db832SDavid du Colombier 	 */
323458db832SDavid du Colombier 	events = ctlr->fcc->fcce;
324458db832SDavid du Colombier 	ctlr->fcc->fcce = events;		/* clear events */
32506578a4fSDavid du Colombier 
32606578a4fSDavid du Colombier #ifdef DBG
32706578a4fSDavid du Colombier 	ehisto[events & 0x7f]++;
32806578a4fSDavid du Colombier #endif
32906578a4fSDavid du Colombier 
330458db832SDavid du Colombier 	ctlr->interrupts++;
331458db832SDavid du Colombier 
33206578a4fSDavid du Colombier 	if(events & BSY)
333458db832SDavid du Colombier 		ctlr->overrun++;
334458db832SDavid du Colombier 	if(events & TXE)
335458db832SDavid du Colombier 		ether->oerrs++;
336458db832SDavid du Colombier 
33706578a4fSDavid du Colombier #ifdef DBG
33806578a4fSDavid du Colombier 	rcvd = xmtd = 0;
33906578a4fSDavid du Colombier #endif
340458db832SDavid du Colombier 	/*
341458db832SDavid du Colombier 	 * Receiver interrupt: run round the descriptor ring logging
342458db832SDavid du Colombier 	 * errors and passing valid receive data up to the higher levels
343458db832SDavid du Colombier 	 * until we encounter a descriptor still owned by the chip.
344458db832SDavid du Colombier 	 */
34506578a4fSDavid du Colombier 	if(events & RXF){
346458db832SDavid du Colombier 		dre = &ctlr->rdr[ctlr->rdrx];
347458db832SDavid du Colombier 		dczap(dre, sizeof(BD));
348458db832SDavid du Colombier 		while(((status = dre->status) & BDEmpty) == 0){
34906578a4fSDavid du Colombier 			rcvd++;
350458db832SDavid du Colombier 			if(status & RxError || (status & (BDFirst|BDLast)) != (BDFirst|BDLast)){
351458db832SDavid du Colombier 				if(status & (RxeLG|RxeSH))
352458db832SDavid du Colombier 					ether->buffs++;
353458db832SDavid du Colombier 				if(status & RxeNO)
354458db832SDavid du Colombier 					ether->frames++;
355458db832SDavid du Colombier 				if(status & RxeCR)
356458db832SDavid du Colombier 					ether->crcs++;
357458db832SDavid du Colombier 				if(status & RxeOV)
358458db832SDavid du Colombier 					ether->overflows++;
359458db832SDavid du Colombier 				print("eth rx: %ux\n", status);
36006578a4fSDavid du Colombier 			}else{
361458db832SDavid du Colombier 				/*
362458db832SDavid du Colombier 				 * We have a packet. Read it in.
363458db832SDavid du Colombier 				 */
364458db832SDavid du Colombier 				len = dre->length-4;
36506578a4fSDavid du Colombier 				b = ctlr->rcvbufs[ctlr->rdrx];
36606578a4fSDavid du Colombier 				assert(dre->addr == PADDR(b->rp));
36706578a4fSDavid du Colombier 				dczap(b->rp, len);
36806578a4fSDavid du Colombier 				if(nb = iallocb(Bufsize)){
369458db832SDavid du Colombier 					b->wp += len;
370458db832SDavid du Colombier 					etheriq(ether, b, 1);
37106578a4fSDavid du Colombier 					b = nb;
37206578a4fSDavid du Colombier 					b->rp = (uchar*)(((ulong)b->rp + CACHELINESZ-1) & ~(CACHELINESZ-1));
37306578a4fSDavid du Colombier 					b->wp = b->rp;
37406578a4fSDavid du Colombier 					ctlr->rcvbufs[ctlr->rdrx] = b;
37506578a4fSDavid du Colombier 					ctlr->rdr[ctlr->rdrx].addr = PADDR(b->wp);
376458db832SDavid du Colombier 				}else
377458db832SDavid du Colombier 					ether->soverflows++;
378458db832SDavid du Colombier 			}
379458db832SDavid du Colombier 
380458db832SDavid du Colombier 			/*
381458db832SDavid du Colombier 			 * Finished with this descriptor, reinitialise it,
382458db832SDavid du Colombier 			 * give it back to the chip, then on to the next...
383458db832SDavid du Colombier 			 */
384458db832SDavid du Colombier 			dre->length = 0;
385458db832SDavid du Colombier 			dre->status = (status & BDWrap) | BDEmpty | BDInt;
386458db832SDavid du Colombier 			dcflush(dre, sizeof(BD));
387458db832SDavid du Colombier 
388458db832SDavid du Colombier 			ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre);
389458db832SDavid du Colombier 			dre = &ctlr->rdr[ctlr->rdrx];
390458db832SDavid du Colombier 			dczap(dre, sizeof(BD));
391458db832SDavid du Colombier 		}
392458db832SDavid du Colombier 	}
393458db832SDavid du Colombier 
394458db832SDavid du Colombier 	/*
395458db832SDavid du Colombier 	 * Transmitter interrupt: handle anything queued for a free descriptor.
396458db832SDavid du Colombier 	 */
397458db832SDavid du Colombier 	if(events & (TXB|TXE)){
39806578a4fSDavid du Colombier 		ilock(ctlr);
39906578a4fSDavid du Colombier 		restart = 0;
400458db832SDavid du Colombier 		while(ctlr->ntq){
401458db832SDavid du Colombier 			dre = &ctlr->tdr[ctlr->tdri];
402458db832SDavid du Colombier 			dczap(dre, sizeof(BD));
403458db832SDavid du Colombier 			status = dre->status;
404458db832SDavid du Colombier 			if(status & BDReady)
405458db832SDavid du Colombier 				break;
406458db832SDavid du Colombier 			if(status & TxeDEF)
407458db832SDavid du Colombier 				ctlr->deferred++;
408458db832SDavid du Colombier 			if(status & TxeHB)
409458db832SDavid du Colombier 				ctlr->heartbeat++;
410458db832SDavid du Colombier 			if(status & TxeLC)
411458db832SDavid du Colombier 				ctlr->latecoll++;
412458db832SDavid du Colombier 			if(status & TxeRL)
413458db832SDavid du Colombier 				ctlr->retrylim++;
414458db832SDavid du Colombier 			if(status & TxeUN)
415458db832SDavid du Colombier 				ctlr->underrun++;
416458db832SDavid du Colombier 			if(status & TxeCSL)
417458db832SDavid du Colombier 				ctlr->carrierlost++;
41806578a4fSDavid du Colombier 			if(status & (TxeLC|TxeRL|TxeUN))
41906578a4fSDavid du Colombier 				restart = 1;
420458db832SDavid du Colombier 			ctlr->retrycount += (status>>2)&0xF;
421458db832SDavid du Colombier 			b = ctlr->txb[ctlr->tdri];
422458db832SDavid du Colombier 			if(b == nil)
423458db832SDavid du Colombier 				panic("fcce/interrupt: bufp");
424458db832SDavid du Colombier 			ctlr->txb[ctlr->tdri] = nil;
425458db832SDavid du Colombier 			freeb(b);
426458db832SDavid du Colombier 			ctlr->ntq--;
427458db832SDavid du Colombier 			ctlr->tdri = NEXT(ctlr->tdri, Ntdre);
42806578a4fSDavid du Colombier 			xmtd++;
429458db832SDavid du Colombier 		}
430458db832SDavid du Colombier 
43106578a4fSDavid du Colombier 		if(restart){
43206578a4fSDavid du Colombier 			ctlr->fcc->gfmr &= ~ENT;
43306578a4fSDavid du Colombier 			delay(10);
43406578a4fSDavid du Colombier 			ctlr->fcc->gfmr |= ENT;
435458db832SDavid du Colombier 			cpmop(RestartTx, ctlr->fccid, 0xc);
436458db832SDavid du Colombier 		}
43706578a4fSDavid du Colombier 		txstart(ether);
43806578a4fSDavid du Colombier 		iunlock(ctlr);
43906578a4fSDavid du Colombier 	}
44006578a4fSDavid du Colombier #ifdef DBG
44106578a4fSDavid du Colombier 	if(rcvd >= nelem(fccrhisto))
44206578a4fSDavid du Colombier 		rcvd = nelem(fccrhisto) - 1;
44306578a4fSDavid du Colombier 	if(xmtd >= nelem(fccthisto))
44406578a4fSDavid du Colombier 		xmtd = nelem(fccthisto) - 1;
44506578a4fSDavid du Colombier 	if(rcvd)
44606578a4fSDavid du Colombier 		fcctrhisto[xmtd]++;
44706578a4fSDavid du Colombier 	else
44806578a4fSDavid du Colombier 		fccthisto[xmtd]++;
44906578a4fSDavid du Colombier 	if(xmtd)
45006578a4fSDavid du Colombier 		fccrthisto[rcvd]++;
45106578a4fSDavid du Colombier 	else
45206578a4fSDavid du Colombier 		fccrhisto[rcvd]++;
45306578a4fSDavid du Colombier #endif
454458db832SDavid du Colombier }
455458db832SDavid du Colombier 
456458db832SDavid du Colombier static long
ifstat(Ether * ether,void * a,long n,ulong offset)457458db832SDavid du Colombier ifstat(Ether* ether, void* a, long n, ulong offset)
458458db832SDavid du Colombier {
459458db832SDavid du Colombier 	char *p;
46006578a4fSDavid du Colombier 	int len, i, r;
461458db832SDavid du Colombier 	Ctlr *ctlr;
46206578a4fSDavid du Colombier 	MiiPhy *phy;
463458db832SDavid du Colombier 
464458db832SDavid du Colombier 	if(n == 0)
465458db832SDavid du Colombier 		return 0;
466458db832SDavid du Colombier 
467458db832SDavid du Colombier 	ctlr = ether->ctlr;
468458db832SDavid du Colombier 
46946136019SDavid du Colombier 	p = malloc(READSTR);
47046136019SDavid du Colombier 	len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts);
47146136019SDavid du Colombier 	len += snprint(p+len, READSTR-len, "carrierlost: %lud\n", ctlr->carrierlost);
47246136019SDavid du Colombier 	len += snprint(p+len, READSTR-len, "heartbeat: %lud\n", ctlr->heartbeat);
47346136019SDavid du Colombier 	len += snprint(p+len, READSTR-len, "retrylimit: %lud\n", ctlr->retrylim);
47446136019SDavid du Colombier 	len += snprint(p+len, READSTR-len, "retrycount: %lud\n", ctlr->retrycount);
47546136019SDavid du Colombier 	len += snprint(p+len, READSTR-len, "latecollisions: %lud\n", ctlr->latecoll);
47646136019SDavid du Colombier 	len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n", ctlr->overrun);
47746136019SDavid du Colombier 	len += snprint(p+len, READSTR-len, "txunderruns: %lud\n", ctlr->underrun);
47846136019SDavid du Colombier 	len += snprint(p+len, READSTR-len, "framesdeferred: %lud\n", ctlr->deferred);
47906578a4fSDavid du Colombier 	miistatus(ctlr->mii);
48006578a4fSDavid du Colombier 	phy = ctlr->mii->curphy;
48146136019SDavid du Colombier 	len += snprint(p+len, READSTR-len, "phy: link=%d, tfc=%d, rfc=%d, speed=%d, fd=%d\n",
48206578a4fSDavid du Colombier 		phy->link, phy->tfc, phy->rfc, phy->speed, phy->fd);
48306578a4fSDavid du Colombier 
48406578a4fSDavid du Colombier #ifdef DBG
48506578a4fSDavid du Colombier 	if(ctlr->mii != nil && ctlr->mii->curphy != nil){
48646136019SDavid du Colombier 		len += snprint(p+len, READSTR, "phy:   ");
48706578a4fSDavid du Colombier 		for(i = 0; i < NMiiPhyr; i++){
48806578a4fSDavid du Colombier 			if(i && ((i & 0x07) == 0))
48946136019SDavid du Colombier 				len += snprint(p+len, READSTR-len, "\n       ");
49006578a4fSDavid du Colombier 			r = miimir(ctlr->mii, i);
49146136019SDavid du Colombier 			len += snprint(p+len, READSTR-len, " %4.4uX", r);
49206578a4fSDavid du Colombier 		}
49346136019SDavid du Colombier 		snprint(p+len, READSTR-len, "\n");
49406578a4fSDavid du Colombier 	}
49506578a4fSDavid du Colombier #endif
49646136019SDavid du Colombier 	snprint(p+len, READSTR-len, "\n");
49706578a4fSDavid du Colombier 
498458db832SDavid du Colombier 	n = readstr(offset, a, n, p);
499458db832SDavid du Colombier 	free(p);
500458db832SDavid du Colombier 
501458db832SDavid du Colombier 	return n;
502458db832SDavid du Colombier }
503458db832SDavid du Colombier 
504*5d9de2d3SDavid du Colombier IMM* imm;
505*5d9de2d3SDavid du Colombier 
506458db832SDavid du Colombier /*
507458db832SDavid du Colombier  * This follows the MPC8260 user guide: section28.9's initialisation sequence.
508458db832SDavid du Colombier  */
50906578a4fSDavid du Colombier static int
fccsetup(Ctlr * ctlr,FCC * fcc,uchar * ea)510458db832SDavid du Colombier fccsetup(Ctlr *ctlr, FCC *fcc, uchar *ea)
511458db832SDavid du Colombier {
512458db832SDavid du Colombier 	int i;
513458db832SDavid du Colombier 	Etherparam *p;
51406578a4fSDavid du Colombier 	MiiPhy *phy;
515458db832SDavid du Colombier 
516458db832SDavid du Colombier 	/* Turn Ethernet off */
517458db832SDavid du Colombier 	fcc->gfmr &= ~(ENR | ENT);
518458db832SDavid du Colombier 
51928495efeSDavid du Colombier 	ioplock();
520458db832SDavid du Colombier 	switch(ctlr->port) {
521458db832SDavid du Colombier 	default:
522458db832SDavid du Colombier 		iopunlock();
52306578a4fSDavid du Colombier 		return -1;
524458db832SDavid du Colombier 	case 0:
525458db832SDavid du Colombier 		/* Step 1 (Section 28.9), write the parallel ports */
526458db832SDavid du Colombier 		ctlr->pmdio = 0x01000000;
527458db832SDavid du Colombier 		ctlr->pmdck = 0x08000000;
5284de34a7eSDavid du Colombier 		imm->port[0].pdir &= ~A1dir0;
5294de34a7eSDavid du Colombier 		imm->port[0].pdir |= A1dir1;
5304de34a7eSDavid du Colombier 		imm->port[0].psor &= ~A1psor0;
5314de34a7eSDavid du Colombier 		imm->port[0].psor |= A1psor1;
5324de34a7eSDavid du Colombier 		imm->port[0].ppar |= (A1dir0 | A1dir1);
533458db832SDavid du Colombier 		/* Step 2, Port C clocks */
5344de34a7eSDavid du Colombier 		imm->port[2].psor &= ~0x00000c00;
5354de34a7eSDavid du Colombier 		imm->port[2].pdir &= ~0x00000c00;
5364de34a7eSDavid du Colombier 		imm->port[2].ppar |= 0x00000c00;
5374de34a7eSDavid du Colombier 		imm->port[3].pdat |= (ctlr->pmdio | ctlr->pmdck);
5384de34a7eSDavid du Colombier 		imm->port[3].podr |= ctlr->pmdio;
5394de34a7eSDavid du Colombier 		imm->port[3].pdir |= (ctlr->pmdio | ctlr->pmdck);
5404de34a7eSDavid du Colombier 		imm->port[3].ppar &= ~(ctlr->pmdio | ctlr->pmdck);
541458db832SDavid du Colombier 		eieio();
542458db832SDavid du Colombier 		/* Step 3, Serial Interface clock routing */
5434de34a7eSDavid du Colombier 		imm->cmxfcr &= ~0xff000000;	/* Clock mask */
5444de34a7eSDavid du Colombier 		imm->cmxfcr |= 0x37000000;	/* Clock route */
545458db832SDavid du Colombier 		break;
546458db832SDavid du Colombier 
547458db832SDavid du Colombier 	case 1:
548458db832SDavid du Colombier 		/* Step 1 (Section 28.9), write the parallel ports */
549458db832SDavid du Colombier 		ctlr->pmdio = 0x00400000;
550458db832SDavid du Colombier 		ctlr->pmdck = 0x00200000;
5514de34a7eSDavid du Colombier 		imm->port[1].pdir &= ~B2dir0;
5524de34a7eSDavid du Colombier 		imm->port[1].pdir |= B2dir1;
5534de34a7eSDavid du Colombier 		imm->port[1].psor &= ~B2psor0;
5544de34a7eSDavid du Colombier 		imm->port[1].psor |= B2psor1;
5554de34a7eSDavid du Colombier 		imm->port[1].ppar |= (B2dir0 | B2dir1);
556458db832SDavid du Colombier 		/* Step 2, Port C clocks */
5574de34a7eSDavid du Colombier 		imm->port[2].psor &= ~0x00003000;
5584de34a7eSDavid du Colombier 		imm->port[2].pdir &= ~0x00003000;
5594de34a7eSDavid du Colombier 		imm->port[2].ppar |= 0x00003000;
560458db832SDavid du Colombier 
5614de34a7eSDavid du Colombier 		imm->port[2].pdat |= (ctlr->pmdio | ctlr->pmdck);
5624de34a7eSDavid du Colombier 		imm->port[2].podr |= ctlr->pmdio;
5634de34a7eSDavid du Colombier 		imm->port[2].pdir |= (ctlr->pmdio | ctlr->pmdck);
5644de34a7eSDavid du Colombier 		imm->port[2].ppar &= ~(ctlr->pmdio | ctlr->pmdck);
565458db832SDavid du Colombier 		eieio();
566458db832SDavid du Colombier 		/* Step 3, Serial Interface clock routing */
5674de34a7eSDavid du Colombier 		imm->cmxfcr &= ~0x00ff0000;
5684de34a7eSDavid du Colombier 		imm->cmxfcr |= 0x00250000;
569458db832SDavid du Colombier 		break;
570458db832SDavid du Colombier 
571458db832SDavid du Colombier 	case 2:
572458db832SDavid du Colombier 		/* Step 1 (Section 28.9), write the parallel ports */
5734de34a7eSDavid du Colombier 		imm->port[1].pdir &= ~B3dir0;
5744de34a7eSDavid du Colombier 		imm->port[1].pdir |= B3dir1;
5754de34a7eSDavid du Colombier 		imm->port[1].psor &= ~B3psor0;
5764de34a7eSDavid du Colombier 		imm->port[1].psor |= B3psor1;
5774de34a7eSDavid du Colombier 		imm->port[1].ppar |= (B3dir0 | B3dir1);
578458db832SDavid du Colombier 		/* Step 2, Port C clocks */
5794de34a7eSDavid du Colombier 		imm->port[2].psor &= ~0x0000c000;
5804de34a7eSDavid du Colombier 		imm->port[2].pdir &= ~0x0000c000;
5814de34a7eSDavid du Colombier 		imm->port[2].ppar |= 0x0000c000;
5824de34a7eSDavid du Colombier 		imm->port[3].pdat |= (ctlr->pmdio | ctlr->pmdck);
5834de34a7eSDavid du Colombier 		imm->port[3].podr |= ctlr->pmdio;
5844de34a7eSDavid du Colombier 		imm->port[3].pdir |= (ctlr->pmdio | ctlr->pmdck);
5854de34a7eSDavid du Colombier 		imm->port[3].ppar &= ~(ctlr->pmdio | ctlr->pmdck);
586458db832SDavid du Colombier 		eieio();
587458db832SDavid du Colombier 		/* Step 3, Serial Interface clock routing */
5884de34a7eSDavid du Colombier 		imm->cmxfcr &= ~0x0000ff00;
5894de34a7eSDavid du Colombier 		imm->cmxfcr |= 0x00003700;
590458db832SDavid du Colombier 		break;
591458db832SDavid du Colombier 	}
592458db832SDavid du Colombier 	iopunlock();
593458db832SDavid du Colombier 
5944de34a7eSDavid du Colombier 	p = (Etherparam*)(m->immr->prmfcc + ctlr->port);
595458db832SDavid du Colombier 	memset(p, 0, sizeof(Etherparam));
596458db832SDavid du Colombier 
597458db832SDavid du Colombier 	/* Step 4 */
598458db832SDavid du Colombier 	fcc->gfmr |= ENET;
599458db832SDavid du Colombier 
600458db832SDavid du Colombier 	/* Step 5 */
601458db832SDavid du Colombier 	fcc->fpsmr = CRCE | FDE | LPB;	/* full duplex operation */
60206578a4fSDavid du Colombier 	ctlr->duplex = ~0;
603458db832SDavid du Colombier 
604458db832SDavid du Colombier 	/* Step 6 */
605458db832SDavid du Colombier 	fcc->fdsr = 0xd555;
606458db832SDavid du Colombier 
607458db832SDavid du Colombier 	/* Step 7, initialize parameter ram */
608458db832SDavid du Colombier 	p->rbase = PADDR(ctlr->rdr);
609458db832SDavid du Colombier 	p->tbase = PADDR(ctlr->tdr);
610458db832SDavid du Colombier 	p->rstate = (GBL | EB) << 24;
611458db832SDavid du Colombier 	p->tstate = (GBL | EB) << 24;
612458db832SDavid du Colombier 
613458db832SDavid du Colombier 	p->cmask = 0xdebb20e3;
614458db832SDavid du Colombier 	p->cpres = 0xffffffff;
615458db832SDavid du Colombier 
616458db832SDavid du Colombier 	p->retlim = 15;	/* retry limit */
617458db832SDavid du Colombier 
61806578a4fSDavid du Colombier 	p->mrblr = (Rbsize+0x1f)&~0x1f;		/* multiple of 32 */
619458db832SDavid du Colombier 	p->mflr = Rbsize;
62006578a4fSDavid du Colombier 	p->minflr = ETHERMINTU;
62106578a4fSDavid du Colombier 	p->maxd1 = (Rbsize+7) & ~7;
62206578a4fSDavid du Colombier 	p->maxd2 = (Rbsize+7) & ~7;
623458db832SDavid du Colombier 
624458db832SDavid du Colombier 	for(i=0; i<Eaddrlen; i+=2)
625458db832SDavid du Colombier 		p->paddr[2-i/2] = (ea[i+1]<<8)|ea[i];
626458db832SDavid du Colombier 
627458db832SDavid du Colombier 	/* Step 7, initialize parameter ram, configuration-dependent values */
6284de34a7eSDavid du Colombier 	p->riptr = m->immr->fccextra[ctlr->port].ri - (uchar*)IMMR;
6294de34a7eSDavid du Colombier 	p->tiptr = m->immr->fccextra[ctlr->port].ti - (uchar*)IMMR;
6304de34a7eSDavid du Colombier 	p->padptr = m->immr->fccextra[ctlr->port].pad - (uchar*)IMMR;
6314de34a7eSDavid du Colombier 	memset(m->immr->fccextra[ctlr->port].pad, 0x88, 0x20);
632458db832SDavid du Colombier 
633458db832SDavid du Colombier 	/* Step 8, clear out events */
634458db832SDavid du Colombier 	fcc->fcce = ~0;
635458db832SDavid du Colombier 
636458db832SDavid du Colombier 	/* Step 9, Interrupt enable */
637458db832SDavid du Colombier 	fcc->fccm = TXE | RXF | TXB;
638458db832SDavid du Colombier 
639458db832SDavid du Colombier 	/* Step 10, Configure interrupt priority (not done here) */
640458db832SDavid du Colombier 	/* Step 11, Clear out current events */
641458db832SDavid du Colombier 	/* Step 12, Enable interrupts to the CP interrupt controller */
642458db832SDavid du Colombier 
643458db832SDavid du Colombier 	/* Step 13, Issue the Init Tx and Rx command, specifying 0xc for ethernet*/
644458db832SDavid du Colombier 	cpmop(InitRxTx, fccid[ctlr->port], 0xc);
645458db832SDavid du Colombier 
64606578a4fSDavid du Colombier 	/* Step 14, Link management */
64706578a4fSDavid du Colombier 	if((ctlr->mii = malloc(sizeof(Mii))) == nil)
64806578a4fSDavid du Colombier 		return -1;
64906578a4fSDavid du Colombier 	ctlr->mii->mir = fccmiimir;
65006578a4fSDavid du Colombier 	ctlr->mii->miw = fccmiimiw;
65106578a4fSDavid du Colombier 	ctlr->mii->ctlr = ctlr;
65206578a4fSDavid du Colombier 
65306578a4fSDavid du Colombier 	if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){
65406578a4fSDavid du Colombier 		free(ctlr->mii);
65506578a4fSDavid du Colombier 		ctlr->mii = nil;
65606578a4fSDavid du Colombier 		return -1;
65706578a4fSDavid du Colombier 	}
65806578a4fSDavid du Colombier 	miiane(ctlr->mii, ~0, ~0, ~0);
65906578a4fSDavid du Colombier #ifdef DBG
66006578a4fSDavid du Colombier 	print("oui=%X, phyno=%d, ", phy->oui, phy->phyno);
66106578a4fSDavid du Colombier 	print("anar=%ux, ", phy->anar);
66206578a4fSDavid du Colombier 	print("fc=%ux, ", phy->fc);
66306578a4fSDavid du Colombier 	print("mscr=%ux, ", phy->mscr);
66406578a4fSDavid du Colombier 
66506578a4fSDavid du Colombier 	print("link=%ux, ", phy->link);
66606578a4fSDavid du Colombier 	print("speed=%ux, ", phy->speed);
66706578a4fSDavid du Colombier 	print("fd=%ux, ", phy->fd);
66806578a4fSDavid du Colombier 	print("rfc=%ux, ", phy->rfc);
66906578a4fSDavid du Colombier 	print("tfc=%ux\n", phy->tfc);
67006578a4fSDavid du Colombier #endif
67106578a4fSDavid du Colombier 	/* Step 15, Enable ethernet: done at attach time */
67206578a4fSDavid du Colombier 	return 0;
673458db832SDavid du Colombier }
674458db832SDavid du Colombier 
675458db832SDavid du Colombier static int
reset(Ether * ether)676458db832SDavid du Colombier reset(Ether* ether)
677458db832SDavid du Colombier {
678458db832SDavid du Colombier 	uchar ea[Eaddrlen];
679458db832SDavid du Colombier 	Ctlr *ctlr;
680458db832SDavid du Colombier 	FCC *fcc;
68106578a4fSDavid du Colombier 	Block *b;
68206578a4fSDavid du Colombier 	int i;
683458db832SDavid du Colombier 
684458db832SDavid du Colombier 	if(m->cpuhz < 24000000){
685458db832SDavid du Colombier 		print("%s ether: system speed must be >= 24MHz for ether use\n", ether->type);
686458db832SDavid du Colombier 		return -1;
687458db832SDavid du Colombier 	}
688458db832SDavid du Colombier 
689499069deSDavid du Colombier 	if(ether->port > 3){
690458db832SDavid du Colombier 		print("%s ether: no FCC port %ld\n", ether->type, ether->port);
691458db832SDavid du Colombier 		return -1;
692458db832SDavid du Colombier 	}
693458db832SDavid du Colombier 	ether->irq = fccirq[ether->port];
694458db832SDavid du Colombier 	ether->tbdf = BusPPC;
6954de34a7eSDavid du Colombier 	fcc = imm->fcc + ether->port;
696458db832SDavid du Colombier 
697458db832SDavid du Colombier 	ctlr = malloc(sizeof(*ctlr));
698458db832SDavid du Colombier 	ether->ctlr = ctlr;
699458db832SDavid du Colombier 	memset(ctlr, 0, sizeof(*ctlr));
700458db832SDavid du Colombier 	ctlr->fcc = fcc;
701458db832SDavid du Colombier 	ctlr->port = ether->port;
702458db832SDavid du Colombier 	ctlr->fccid = fccid[ether->port];
703458db832SDavid du Colombier 
704458db832SDavid du Colombier 	/* Ioringinit will allocate the buffer descriptors in normal memory
705458db832SDavid du Colombier 	 * and NOT in Dual-Ported Ram, as prescribed by the MPC8260
706458db832SDavid du Colombier 	 * PowerQUICC II manual (Section 28.6).  When they are allocated
707458db832SDavid du Colombier 	 * in DPram and the Dcache is enabled, the processor will hang
708458db832SDavid du Colombier 	 */
70906578a4fSDavid du Colombier 	if(ioringinit(ctlr, Nrdre, Ntdre, 0) < 0)
710458db832SDavid du Colombier 		panic("etherfcc init");
71106578a4fSDavid du Colombier 	for(i = 0; i < Nrdre; i++){
71206578a4fSDavid du Colombier 		b = iallocb(Bufsize);
71306578a4fSDavid du Colombier 		b->rp = (uchar*)(((ulong)b->rp + CACHELINESZ-1) & ~(CACHELINESZ-1));
71406578a4fSDavid du Colombier 		b->wp = b->rp;
71506578a4fSDavid du Colombier 		ctlr->rcvbufs[i] = b;
71606578a4fSDavid du Colombier 		ctlr->rdr[i].addr = PADDR(b->wp);
71706578a4fSDavid du Colombier 	}
718458db832SDavid du Colombier 
719458db832SDavid du Colombier 	fccsetup(ctlr, fcc, ether->ea);
720458db832SDavid du Colombier 
721458db832SDavid du Colombier 	ether->mbps = 100;	/* TO DO: could be 10mbps */
722458db832SDavid du Colombier 	ether->attach = attach;
723458db832SDavid du Colombier 	ether->transmit = transmit;
724458db832SDavid du Colombier 	ether->interrupt = interrupt;
725458db832SDavid du Colombier 	ether->ifstat = ifstat;
726458db832SDavid du Colombier 
727458db832SDavid du Colombier 	ether->arg = ether;
728458db832SDavid du Colombier 	ether->promiscuous = promiscuous;
729458db832SDavid du Colombier 	ether->multicast = multicast;
730458db832SDavid du Colombier 
731458db832SDavid du Colombier 	/*
732458db832SDavid du Colombier 	 * Until we know where to find it, insist that the plan9.ini
733458db832SDavid du Colombier 	 * entry holds the Ethernet address.
734458db832SDavid du Colombier 	 */
735458db832SDavid du Colombier 	memset(ea, 0, Eaddrlen);
736458db832SDavid du Colombier 	if(memcmp(ea, ether->ea, Eaddrlen) == 0){
737458db832SDavid du Colombier 		print("no ether address");
738458db832SDavid du Colombier 		return -1;
739458db832SDavid du Colombier 	}
740458db832SDavid du Colombier 
741458db832SDavid du Colombier 	return 0;
742458db832SDavid du Colombier }
743458db832SDavid du Colombier 
744458db832SDavid du Colombier void
etherfcclink(void)745458db832SDavid du Colombier etherfcclink(void)
746458db832SDavid du Colombier {
747458db832SDavid du Colombier 	addethercard("fcc", reset);
748458db832SDavid du Colombier }
74906578a4fSDavid du Colombier 
75006578a4fSDavid du Colombier static void
nanodelay(void)75106578a4fSDavid du Colombier nanodelay(void)
75206578a4fSDavid du Colombier {
75306578a4fSDavid du Colombier 	static int count;
75406578a4fSDavid du Colombier 	int i;
75506578a4fSDavid du Colombier 
75606578a4fSDavid du Colombier 	for(i = 0; i < 500; i++)
75706578a4fSDavid du Colombier 		count++;
75806578a4fSDavid du Colombier 	return;
75906578a4fSDavid du Colombier }
76006578a4fSDavid du Colombier 
76106578a4fSDavid du Colombier static
miiwriteloop(Ctlr * ctlr,Port * port,int cnt,ulong cmd)76206578a4fSDavid du Colombier void miiwriteloop(Ctlr *ctlr, Port *port, int cnt, ulong cmd)
76306578a4fSDavid du Colombier {
76406578a4fSDavid du Colombier 	int i;
76506578a4fSDavid du Colombier 
76606578a4fSDavid du Colombier 	for(i = 0; i < cnt; i++){
76706578a4fSDavid du Colombier 		port->pdat &= ~ctlr->pmdck;
76806578a4fSDavid du Colombier 		if(cmd & BIT(i))
76906578a4fSDavid du Colombier 			port->pdat |= ctlr->pmdio;
77006578a4fSDavid du Colombier 		else
77106578a4fSDavid du Colombier 			port->pdat &= ~ctlr->pmdio;
77206578a4fSDavid du Colombier 		nanodelay();
77306578a4fSDavid du Colombier 		port->pdat |= ctlr->pmdck;
77406578a4fSDavid du Colombier 		nanodelay();
77506578a4fSDavid du Colombier 	}
77606578a4fSDavid du Colombier }
77706578a4fSDavid du Colombier 
77806578a4fSDavid du Colombier static int
fccmiimiw(Mii * mii,int pa,int ra,int data)77906578a4fSDavid du Colombier fccmiimiw(Mii *mii, int pa, int ra, int data)
78006578a4fSDavid du Colombier {
78106578a4fSDavid du Colombier 	int x;
78206578a4fSDavid du Colombier 	Port *port;
78306578a4fSDavid du Colombier 	ulong cmd;
78406578a4fSDavid du Colombier 	Ctlr *ctlr;
78506578a4fSDavid du Colombier 
78606578a4fSDavid du Colombier 	/*
78706578a4fSDavid du Colombier 	 * MII Management Interface Write.
78806578a4fSDavid du Colombier 	 */
78906578a4fSDavid du Colombier 
79006578a4fSDavid du Colombier 	ctlr = mii->ctlr;
7914de34a7eSDavid du Colombier 	port = imm->port + 3;
79206578a4fSDavid du Colombier 	cmd = MDIwrite | (pa<<(5+2+16))| (ra<<(2+16)) | (data & 0xffff);
79306578a4fSDavid du Colombier 
79406578a4fSDavid du Colombier 	x = splhi();
79506578a4fSDavid du Colombier 
79606578a4fSDavid du Colombier 	port->pdir |= (ctlr->pmdio|ctlr->pmdck);
79706578a4fSDavid du Colombier 	nanodelay();
79806578a4fSDavid du Colombier 
79906578a4fSDavid du Colombier 	miiwriteloop(ctlr, port, 32, ~0);
80006578a4fSDavid du Colombier 	miiwriteloop(ctlr, port, 32, cmd);
80106578a4fSDavid du Colombier 
80206578a4fSDavid du Colombier 	port->pdir |= (ctlr->pmdio|ctlr->pmdck);
80306578a4fSDavid du Colombier 	nanodelay();
80406578a4fSDavid du Colombier 
80506578a4fSDavid du Colombier 	miiwriteloop(ctlr, port, 32, ~0);
80606578a4fSDavid du Colombier 
80706578a4fSDavid du Colombier 	splx(x);
80806578a4fSDavid du Colombier 	return 1;
80906578a4fSDavid du Colombier }
81006578a4fSDavid du Colombier 
81106578a4fSDavid du Colombier static int
fccmiimir(Mii * mii,int pa,int ra)81206578a4fSDavid du Colombier fccmiimir(Mii *mii, int pa, int ra)
81306578a4fSDavid du Colombier {
81406578a4fSDavid du Colombier 	int data, i, x;
81506578a4fSDavid du Colombier 	Port *port;
81606578a4fSDavid du Colombier 	ulong cmd;
81706578a4fSDavid du Colombier 	Ctlr *ctlr;
81806578a4fSDavid du Colombier 
81906578a4fSDavid du Colombier 	ctlr = mii->ctlr;
8204de34a7eSDavid du Colombier 	port = imm->port + 3;
82106578a4fSDavid du Colombier 
82206578a4fSDavid du Colombier 	cmd = MDIread | pa<<(5+2+16) | ra<<(2+16);
82306578a4fSDavid du Colombier 
82406578a4fSDavid du Colombier 	x = splhi();
82506578a4fSDavid du Colombier 	port->pdir |= (ctlr->pmdio|ctlr->pmdck);
82606578a4fSDavid du Colombier 	nanodelay();
82706578a4fSDavid du Colombier 
82806578a4fSDavid du Colombier 	miiwriteloop(ctlr, port, 32, ~0);
82906578a4fSDavid du Colombier 
83006578a4fSDavid du Colombier 	/* Clock out the first 14 MS bits of the command */
83106578a4fSDavid du Colombier 	miiwriteloop(ctlr, port, 14, cmd);
83206578a4fSDavid du Colombier 
83306578a4fSDavid du Colombier 	/* Turn-around */
83406578a4fSDavid du Colombier 	port->pdat &= ~ctlr->pmdck;
83506578a4fSDavid du Colombier 	port->pdir &= ~ctlr->pmdio;
83606578a4fSDavid du Colombier 	nanodelay();
83706578a4fSDavid du Colombier 
83806578a4fSDavid du Colombier 	/* For read, clock in 18 bits, use 16 */
83906578a4fSDavid du Colombier 	data = 0;
84006578a4fSDavid du Colombier 	for(i=0; i<18; i++){
84106578a4fSDavid du Colombier 		data <<= 1;
84206578a4fSDavid du Colombier 		if(port->pdat & ctlr->pmdio)
84306578a4fSDavid du Colombier 			data |= 1;
84406578a4fSDavid du Colombier 		port->pdat |= ctlr->pmdck;
84506578a4fSDavid du Colombier 		nanodelay();
84606578a4fSDavid du Colombier 		port->pdat &= ~ctlr->pmdck;
84706578a4fSDavid du Colombier 		nanodelay();
84806578a4fSDavid du Colombier 	}
84906578a4fSDavid du Colombier 	port->pdir |= (ctlr->pmdio|ctlr->pmdck);
85006578a4fSDavid du Colombier 	nanodelay();
85106578a4fSDavid du Colombier 	miiwriteloop(ctlr, port, 32, ~0);
85206578a4fSDavid du Colombier 	splx(x);
85306578a4fSDavid du Colombier 	return data & 0xffff;
85406578a4fSDavid du Colombier }
85506578a4fSDavid du Colombier 
85606578a4fSDavid du Colombier static void
fccltimer(Ureg *,Timer * t)85706578a4fSDavid du Colombier fccltimer(Ureg*, Timer *t)
85806578a4fSDavid du Colombier {
85906578a4fSDavid du Colombier 	Ether *ether;
86006578a4fSDavid du Colombier 	Ctlr *ctlr;
86106578a4fSDavid du Colombier 	MiiPhy *phy;
86206578a4fSDavid du Colombier 	ulong gfmr;
86306578a4fSDavid du Colombier 
86406578a4fSDavid du Colombier 	ether = t->ta;
86506578a4fSDavid du Colombier 	ctlr = ether->ctlr;
86606578a4fSDavid du Colombier 	if(ctlr->mii == nil || ctlr->mii->curphy == nil)
86706578a4fSDavid du Colombier 		return;
86806578a4fSDavid du Colombier 	phy = ctlr->mii->curphy;
86906578a4fSDavid du Colombier 	if(miistatus(ctlr->mii) < 0){
87006578a4fSDavid du Colombier 		print("miistatus failed\n");
87106578a4fSDavid du Colombier 		return;
87206578a4fSDavid du Colombier 	}
87306578a4fSDavid du Colombier 	if(phy->link == 0){
87406578a4fSDavid du Colombier 		print("link lost\n");
87506578a4fSDavid du Colombier 		return;
87606578a4fSDavid du Colombier 	}
87706578a4fSDavid du Colombier 	ether->mbps = phy->speed;
87806578a4fSDavid du Colombier 
87906578a4fSDavid du Colombier 	if(phy->fd != ctlr->duplex)
88006578a4fSDavid du Colombier 		print("set duplex\n");
88106578a4fSDavid du Colombier 	ilock(ctlr);
88206578a4fSDavid du Colombier 	gfmr = ctlr->fcc->gfmr;
88306578a4fSDavid du Colombier 	if(phy->fd != ctlr->duplex){
88406578a4fSDavid du Colombier 		ctlr->fcc->gfmr &= ~(ENR|ENT);
88506578a4fSDavid du Colombier 		if(phy->fd)
88606578a4fSDavid du Colombier 			ctlr->fcc->fpsmr |= FDE | LPB;		/* full duplex operation */
88706578a4fSDavid du Colombier 		else
88806578a4fSDavid du Colombier 			ctlr->fcc->fpsmr &= ~(FDE | LPB);	/* half duplex operation */
88906578a4fSDavid du Colombier 		ctlr->duplex = phy->fd;
89006578a4fSDavid du Colombier 	}
89106578a4fSDavid du Colombier 	ctlr->fcc->gfmr = gfmr;
89206578a4fSDavid du Colombier 	iunlock(ctlr);
89306578a4fSDavid du Colombier }
894