xref: /plan9/sys/src/9/kw/ether1116.c (revision 897ae9c16dffa8596c9f4c4bd772fd900d22612c)
1*897ae9c1SDavid du Colombier /*
2*897ae9c1SDavid du Colombier  * marvell kirkwood ethernet (88e1116) driver
3*897ae9c1SDavid du Colombier  * (as found in the sheevaplug & openrd).
4*897ae9c1SDavid du Colombier  * from /public/doc/marvell/88f61xx.kirkwood.pdf
5*897ae9c1SDavid du Colombier  * and  /public/doc/marvell/88e1116.pdf.
6*897ae9c1SDavid du Colombier  */
7*897ae9c1SDavid du Colombier 
8*897ae9c1SDavid du Colombier #include "u.h"
9*897ae9c1SDavid du Colombier #include "../port/lib.h"
10*897ae9c1SDavid du Colombier #include "mem.h"
11*897ae9c1SDavid du Colombier #include "dat.h"
12*897ae9c1SDavid du Colombier #include "fns.h"
13*897ae9c1SDavid du Colombier #include "io.h"
14*897ae9c1SDavid du Colombier #include "../port/error.h"
15*897ae9c1SDavid du Colombier #include "../port/netif.h"
16*897ae9c1SDavid du Colombier 
17*897ae9c1SDavid du Colombier #include "etherif.h"
18*897ae9c1SDavid du Colombier #include "ethermii.h"
19*897ae9c1SDavid du Colombier #include "../ip/ip.h"
20*897ae9c1SDavid du Colombier 
21*897ae9c1SDavid du Colombier #define	MIIDBG	if(0)iprint
22*897ae9c1SDavid du Colombier 
23*897ae9c1SDavid du Colombier enum {
24*897ae9c1SDavid du Colombier 	Gbe0regs	= PHYSIO + 0x72000,
25*897ae9c1SDavid du Colombier 	Gbe1regs	= PHYSIO + 0x76000,
26*897ae9c1SDavid du Colombier 
27*897ae9c1SDavid du Colombier 	Nrx		= 512,
28*897ae9c1SDavid du Colombier 	Ntx		= 512,
29*897ae9c1SDavid du Colombier 	Nrxblks		= 1024,
30*897ae9c1SDavid du Colombier 	Rxblklen	= 2+1522,  /* ifc. supplies first 2 bytes as padding */
31*897ae9c1SDavid du Colombier 
32*897ae9c1SDavid du Colombier 	Maxrxintrsec	= 20*1000,	/* max. rx intrs. / sec */
33*897ae9c1SDavid du Colombier 	Etherstuck	= 90,	/* must send or receive a packet in this many sec.s */
34*897ae9c1SDavid du Colombier 
35*897ae9c1SDavid du Colombier 	Descralign	= 16,
36*897ae9c1SDavid du Colombier 	Bufalign	= 8,
37*897ae9c1SDavid du Colombier 
38*897ae9c1SDavid du Colombier 	Pass		= 1,		/* accept packets */
39*897ae9c1SDavid du Colombier 
40*897ae9c1SDavid du Colombier 	Qno		= 0,		/* do everything on queue zero */
41*897ae9c1SDavid du Colombier };
42*897ae9c1SDavid du Colombier 
43*897ae9c1SDavid du Colombier typedef struct Ctlr Ctlr;
44*897ae9c1SDavid du Colombier typedef struct Gbereg Gbereg;
45*897ae9c1SDavid du Colombier typedef struct Mibstats Mibstats;
46*897ae9c1SDavid du Colombier typedef struct Rx Rx;
47*897ae9c1SDavid du Colombier typedef struct Tx Tx;
48*897ae9c1SDavid du Colombier 
49*897ae9c1SDavid du Colombier static struct {
50*897ae9c1SDavid du Colombier 	Lock;
51*897ae9c1SDavid du Colombier 	Block	*head;
52*897ae9c1SDavid du Colombier } freeblocks;
53*897ae9c1SDavid du Colombier 
54*897ae9c1SDavid du Colombier /* hardware receive buffer descriptor */
55*897ae9c1SDavid du Colombier struct Rx {
56*897ae9c1SDavid du Colombier 	ulong	cs;
57*897ae9c1SDavid du Colombier 	ulong	countsize;	/* bytes, buffer size */
58*897ae9c1SDavid du Colombier 	ulong	buf;		/* phys. addr. of packet buffer */
59*897ae9c1SDavid du Colombier 	ulong	next;		/* phys. addr. of next Rx */
60*897ae9c1SDavid du Colombier };
61*897ae9c1SDavid du Colombier 
62*897ae9c1SDavid du Colombier /* hardware transmit buffer descriptor */
63*897ae9c1SDavid du Colombier struct Tx {
64*897ae9c1SDavid du Colombier 	ulong	cs;
65*897ae9c1SDavid du Colombier 	ulong	countchk;	/* bytes, checksum */
66*897ae9c1SDavid du Colombier 	ulong	buf;		/* phys. addr. of packet buffer */
67*897ae9c1SDavid du Colombier 	ulong	next;		/* phys. addr. of next Tx */
68*897ae9c1SDavid du Colombier };
69*897ae9c1SDavid du Colombier 
70*897ae9c1SDavid du Colombier /* fixed by hw; part of Gberegs */
71*897ae9c1SDavid du Colombier struct Mibstats {
72*897ae9c1SDavid du Colombier 	union {
73*897ae9c1SDavid du Colombier 		uvlong	rxby;		/* good bytes rcv'd */
74*897ae9c1SDavid du Colombier 		struct {
75*897ae9c1SDavid du Colombier 			ulong rxbylo;
76*897ae9c1SDavid du Colombier 			ulong rxbyhi;
77*897ae9c1SDavid du Colombier 		};
78*897ae9c1SDavid du Colombier 	};
79*897ae9c1SDavid du Colombier 	ulong	badrxby;		/* bad bytes rcv'd */
80*897ae9c1SDavid du Colombier 	ulong	mactxerr;		/* tx err pkts */
81*897ae9c1SDavid du Colombier 	ulong	rxpkt;			/* good pkts rcv'd */
82*897ae9c1SDavid du Colombier 	ulong	badrxpkt;		/* bad pkts rcv'd */
83*897ae9c1SDavid du Colombier 	ulong	rxbcastpkt;		/* b'cast pkts rcv'd */
84*897ae9c1SDavid du Colombier 	ulong	rxmcastpkt;		/* m'cast pkts rcv'd */
85*897ae9c1SDavid du Colombier 
86*897ae9c1SDavid du Colombier 	ulong	rx64;			/* pkts <= 64 bytes */
87*897ae9c1SDavid du Colombier 	ulong	rx65_127;		/* pkts 65—127 bytes */
88*897ae9c1SDavid du Colombier 	ulong	rx128_255;		/* pkts 128—255 bytes */
89*897ae9c1SDavid du Colombier 	ulong	rx256_511;		/* pkts 256—511 bytes */
90*897ae9c1SDavid du Colombier 	ulong	rx512_1023;		/* pkts 512—1023 bytes */
91*897ae9c1SDavid du Colombier 	ulong	rx1024_max;		/* pkts >= 1024 bytes */
92*897ae9c1SDavid du Colombier 
93*897ae9c1SDavid du Colombier 	union {
94*897ae9c1SDavid du Colombier 		uvlong	txby;		/* good bytes sent */
95*897ae9c1SDavid du Colombier 		struct {
96*897ae9c1SDavid du Colombier 			ulong txbylo;
97*897ae9c1SDavid du Colombier 			ulong txbyhi;
98*897ae9c1SDavid du Colombier 		};
99*897ae9c1SDavid du Colombier 	};
100*897ae9c1SDavid du Colombier 	ulong	txpkt;			/* good pkts sent */
101*897ae9c1SDavid du Colombier 	/* half-duplex: pkts dropped due to excessive collisions */
102*897ae9c1SDavid du Colombier 	ulong	txcollpktdrop;
103*897ae9c1SDavid du Colombier 	ulong	txmcastpkt;		/* m'cast pkts sent */
104*897ae9c1SDavid du Colombier 	ulong	txbcastpkt;		/* b'cast pkts sent */
105*897ae9c1SDavid du Colombier 
106*897ae9c1SDavid du Colombier 	ulong	badmacctlpkts;		/* bad mac ctl pkts */
107*897ae9c1SDavid du Colombier 	ulong	txflctl;		/* flow-control pkts sent */
108*897ae9c1SDavid du Colombier 	ulong	rxflctl;		/* good flow-control pkts rcv'd */
109*897ae9c1SDavid du Colombier 	ulong	badrxflctl;		/* bad flow-control pkts rcv'd */
110*897ae9c1SDavid du Colombier 
111*897ae9c1SDavid du Colombier 	ulong	rxundersized;		/* runts */
112*897ae9c1SDavid du Colombier 	ulong	rxfrags;		/* fragments rcv'd */
113*897ae9c1SDavid du Colombier 	ulong	rxtoobig;		/* oversized pkts rcv'd */
114*897ae9c1SDavid du Colombier 	ulong	rxjabber;		/* jabber pkts rcv'd */
115*897ae9c1SDavid du Colombier 	ulong	rxerr;			/* rx error events */
116*897ae9c1SDavid du Colombier 	ulong	crcerr;			/* crc error events */
117*897ae9c1SDavid du Colombier 	ulong	collisions;		/* collision events */
118*897ae9c1SDavid du Colombier 	ulong	latecoll;		/* late collisions */
119*897ae9c1SDavid du Colombier };
120*897ae9c1SDavid du Colombier 
121*897ae9c1SDavid du Colombier struct Ctlr {
122*897ae9c1SDavid du Colombier 	Lock;
123*897ae9c1SDavid du Colombier 	Gbereg	*reg;
124*897ae9c1SDavid du Colombier 
125*897ae9c1SDavid du Colombier 	Lock	initlock;
126*897ae9c1SDavid du Colombier 	int	init;
127*897ae9c1SDavid du Colombier 
128*897ae9c1SDavid du Colombier 	Rx	*rx;		/* receive descriptors */
129*897ae9c1SDavid du Colombier 	Block	*rxb[Nrx];	/* blocks belonging to the descriptors */
130*897ae9c1SDavid du Colombier 	int	rxhead;		/* descr ethernet will write to next */
131*897ae9c1SDavid du Colombier 	int	rxtail;		/* next descr that might need a buffer */
132*897ae9c1SDavid du Colombier 	Rendez	rrendez;	/* interrupt wakes up read process */
133*897ae9c1SDavid du Colombier 	int	haveinput;
134*897ae9c1SDavid du Colombier 
135*897ae9c1SDavid du Colombier 	Tx	*tx;
136*897ae9c1SDavid du Colombier 	Block	*txb[Ntx];
137*897ae9c1SDavid du Colombier 	int	txhead;		/* next descr we can use for new packet */
138*897ae9c1SDavid du Colombier 	int	txtail;		/* next descr to reclaim on tx complete */
139*897ae9c1SDavid du Colombier 
140*897ae9c1SDavid du Colombier 	Mii	*mii;
141*897ae9c1SDavid du Colombier 	int	port;
142*897ae9c1SDavid du Colombier 
143*897ae9c1SDavid du Colombier 	/* stats */
144*897ae9c1SDavid du Colombier 	ulong	intrs;
145*897ae9c1SDavid du Colombier 	ulong	newintrs;
146*897ae9c1SDavid du Colombier 	ulong	txunderrun;
147*897ae9c1SDavid du Colombier 	ulong	txringfull;
148*897ae9c1SDavid du Colombier 	ulong	rxdiscard;
149*897ae9c1SDavid du Colombier 	ulong	rxoverrun;
150*897ae9c1SDavid du Colombier 	ulong	nofirstlast;
151*897ae9c1SDavid du Colombier 
152*897ae9c1SDavid du Colombier 	Mibstats;
153*897ae9c1SDavid du Colombier };
154*897ae9c1SDavid du Colombier 
155*897ae9c1SDavid du Colombier #define	Rxqon(q)	(1<<(q))
156*897ae9c1SDavid du Colombier #define	Txqon(q)	(1<<(q))
157*897ae9c1SDavid du Colombier 
158*897ae9c1SDavid du Colombier enum {
159*897ae9c1SDavid du Colombier 	/* sdma config, sdc */
160*897ae9c1SDavid du Colombier 	Burst1		= 0,
161*897ae9c1SDavid du Colombier 	Burst2,
162*897ae9c1SDavid du Colombier 	Burst4,
163*897ae9c1SDavid du Colombier 	Burst8,
164*897ae9c1SDavid du Colombier 	Burst16,
165*897ae9c1SDavid du Colombier 	SDCrifb		= 1<<0,		/* rx intr on pkt boundaries */
166*897ae9c1SDavid du Colombier #define SDCrxburst(v)	((v)<<1)
167*897ae9c1SDavid du Colombier 	SDCrxnobyteswap	= 1<<4,
168*897ae9c1SDavid du Colombier 	SDCtxnobyteswap	= 1<<5,
169*897ae9c1SDavid du Colombier 	SDCswap64byte	= 1<<6,
170*897ae9c1SDavid du Colombier #define SDCtxburst(v)	((v)<<22)
171*897ae9c1SDavid du Colombier 	/* rx intr ipg (inter packet gap) */
172*897ae9c1SDavid du Colombier #define SDCipgintrx(v)	((((v)>>15) & 1)<<25) | (((v) & MASK(15))<<7)
173*897ae9c1SDavid du Colombier 
174*897ae9c1SDavid du Colombier 	/* portcfg */
175*897ae9c1SDavid du Colombier 	PCFGupromisc		= 1<<0,	/* unicast promiscuous mode */
176*897ae9c1SDavid du Colombier #define Rxqdefault(q)	((q)<<1)
177*897ae9c1SDavid du Colombier #define Rxqarp(q)	((q)<<4)
178*897ae9c1SDavid du Colombier 	PCFGbcrejectnoiparp	= 1<<7,
179*897ae9c1SDavid du Colombier 	PCFGbcrejectip		= 1<<8,
180*897ae9c1SDavid du Colombier 	PCFGbcrejectarp		= 1<<9,
181*897ae9c1SDavid du Colombier 	PCFGamnotxes		= 1<<12, /* auto mode, no summary update on tx */
182*897ae9c1SDavid du Colombier 	PCFGtcpq	= 1<<14,
183*897ae9c1SDavid du Colombier 	PCFGudpq	= 1<<15,
184*897ae9c1SDavid du Colombier #define	Rxqtcp(q)	((q)<<16)
185*897ae9c1SDavid du Colombier #define	Rxqudp(q)	((q)<<19)
186*897ae9c1SDavid du Colombier #define	Rxqbpdu(q)	((q)<<22)
187*897ae9c1SDavid du Colombier 	PCFGrxcs	= 1<<25,	/* rx tcp checksum mode with header */
188*897ae9c1SDavid du Colombier 
189*897ae9c1SDavid du Colombier 	/* portcfgx */
190*897ae9c1SDavid du Colombier 	PCFGXspanq	= 1<<1,
191*897ae9c1SDavid du Colombier 	PCFGXcrcoff	= 1<<2,		/* no ethernet crc */
192*897ae9c1SDavid du Colombier 
193*897ae9c1SDavid du Colombier 	/* port serial control0, psc0 */
194*897ae9c1SDavid du Colombier 	PSC0porton		= 1<<0,
195*897ae9c1SDavid du Colombier 	PSC0forcelinkup		= 1<<1,
196*897ae9c1SDavid du Colombier 	PSC0an_dplxoff		= 1<<2,	/* an_ = auto. negotiate */
197*897ae9c1SDavid du Colombier 	PSC0an_flctloff		= 1<<3,
198*897ae9c1SDavid du Colombier 	PSC0an_pauseadv		= 1<<4,
199*897ae9c1SDavid du Colombier 	PSC0nofrclinkdown	= 1<<10,
200*897ae9c1SDavid du Colombier 	PSC0an_spdoff		= 1<<13,
201*897ae9c1SDavid du Colombier 	PSC0dteadv		= 1<<14,
202*897ae9c1SDavid du Colombier 
203*897ae9c1SDavid du Colombier 	/* max. input pkt size */
204*897ae9c1SDavid du Colombier #define PSC0mru(v)	((v)<<17)
205*897ae9c1SDavid du Colombier 	PSC0mrumask	= PSC0mru(MASK(3)),
206*897ae9c1SDavid du Colombier 	PSC0mru1518	= 0,		/* 1500+2* 6(addrs) +2 + 4(crc) */
207*897ae9c1SDavid du Colombier 	PSC0mru1522,			/* 1518 + 4(vlan tags) */
208*897ae9c1SDavid du Colombier 	PSC0mru1552,			/* `baby giant' */
209*897ae9c1SDavid du Colombier 	PSC0mru9022,			/* `jumbo' */
210*897ae9c1SDavid du Colombier 	PSC0mru9192,			/* bigger jumbo */
211*897ae9c1SDavid du Colombier 	PSC0mru9700,			/* still bigger jumbo */
212*897ae9c1SDavid du Colombier 
213*897ae9c1SDavid du Colombier 	PSC0fd_frc		= 1<<21,	/* force full duplex */
214*897ae9c1SDavid du Colombier 	PSC0flctlfrc		= 1<<22,
215*897ae9c1SDavid du Colombier 	PSC0gmiispd_gbfrc	= 1<<23,
216*897ae9c1SDavid du Colombier 	PSC0miispdfrc100mbps	= 1<<24,
217*897ae9c1SDavid du Colombier 
218*897ae9c1SDavid du Colombier 	/* port status 0, ps0 */
219*897ae9c1SDavid du Colombier 	PS0linkup	= 1<<1,
220*897ae9c1SDavid du Colombier 	PS0fd		= 1<<2,			/* full duplex */
221*897ae9c1SDavid du Colombier 	PS0flctl	= 1<<3,
222*897ae9c1SDavid du Colombier 	PS0gmii_gb	= 1<<4,
223*897ae9c1SDavid du Colombier 	PS0mii100mbps	= 1<<5,
224*897ae9c1SDavid du Colombier 	PS0txbusy	= 1<<7,
225*897ae9c1SDavid du Colombier 	PS0txfifoempty	= 1<<10,
226*897ae9c1SDavid du Colombier 	PS0rxfifo1empty	= 1<<11,
227*897ae9c1SDavid du Colombier 	PS0rxfifo2empty	= 1<<12,
228*897ae9c1SDavid du Colombier 
229*897ae9c1SDavid du Colombier 	/* port serial control 1, psc1 */
230*897ae9c1SDavid du Colombier 	PSC1loopback	= 1<<1,
231*897ae9c1SDavid du Colombier 	PSC1mii		= 0<<2,
232*897ae9c1SDavid du Colombier 	PSC1rgmii	= 1<<3,			/* enable RGMII */
233*897ae9c1SDavid du Colombier 	PSC1portreset	= 1<<4,
234*897ae9c1SDavid du Colombier 	PSC1clockbypass	= 1<<5,
235*897ae9c1SDavid du Colombier 	PSC1iban	= 1<<6,
236*897ae9c1SDavid du Colombier 	PSC1iban_bypass	= 1<<7,
237*897ae9c1SDavid du Colombier 	PSC1iban_restart= 1<<8,
238*897ae9c1SDavid du Colombier 	PSC1_gbonly	= 1<<11,
239*897ae9c1SDavid du Colombier 	PSC1encolonbp	= 1<<15, /* "collision during back-pressure mib counting" */
240*897ae9c1SDavid du Colombier 	PSC1coldomlimmask= MASK(6)<<16,
241*897ae9c1SDavid du Colombier #define PSC1coldomlim(v) (((v) & MASK(6))<<16)
242*897ae9c1SDavid du Colombier 	PSC1miiallowoddpreamble	= 1<<22,
243*897ae9c1SDavid du Colombier 
244*897ae9c1SDavid du Colombier 	/* port status 1, ps1 */
245*897ae9c1SDavid du Colombier 	PS1rxpause	= 1<<0,
246*897ae9c1SDavid du Colombier 	PS1txpause	= 1<<1,
247*897ae9c1SDavid du Colombier 	PS1pressure	= 1<<2,
248*897ae9c1SDavid du Colombier 	PS1syncfail10ms	= 1<<3,
249*897ae9c1SDavid du Colombier 	PS1an_done	= 1<<4,
250*897ae9c1SDavid du Colombier 	PS1inbandan_bypassed	= 1<<5,
251*897ae9c1SDavid du Colombier 	PS1serdesplllocked	= 1<<6,
252*897ae9c1SDavid du Colombier 	PS1syncok	= 1<<7,
253*897ae9c1SDavid du Colombier 	PS1nosquelch	= 1<<8,
254*897ae9c1SDavid du Colombier 
255*897ae9c1SDavid du Colombier 	/* irq */
256*897ae9c1SDavid du Colombier 	Irx		= 1<<0,
257*897ae9c1SDavid du Colombier 	Iextend		= 1<<1,
258*897ae9c1SDavid du Colombier #define Irxbufferq(q)	(1<<((q)+2))
259*897ae9c1SDavid du Colombier 	Irxerr		= 1<<10,
260*897ae9c1SDavid du Colombier #define Irxerrq(q)	(1<<((q)+11))
261*897ae9c1SDavid du Colombier #define Itxendq(q)	(1<<((q)+19))
262*897ae9c1SDavid du Colombier 	Isum		= 1<<31,
263*897ae9c1SDavid du Colombier 
264*897ae9c1SDavid du Colombier 	/* irq extended, irqe */
265*897ae9c1SDavid du Colombier #define	IEtxbufferq(q)	(1<<((q)+0))
266*897ae9c1SDavid du Colombier #define	IEtxerrq(q)	(1<<((q)+8))
267*897ae9c1SDavid du Colombier 	IEphystschg	= 1<<16,
268*897ae9c1SDavid du Colombier 	IEptp		= 1<<17,
269*897ae9c1SDavid du Colombier 	IErxoverrun	= 1<<18,
270*897ae9c1SDavid du Colombier 	IEtxunderrun	= 1<<19,
271*897ae9c1SDavid du Colombier 	IElinkchg	= 1<<20,
272*897ae9c1SDavid du Colombier 	IEintaddrerr	= 1<<23,
273*897ae9c1SDavid du Colombier 	IEprbserr	= 1<<25,
274*897ae9c1SDavid du Colombier 	IEsum		= 1<<31,
275*897ae9c1SDavid du Colombier 
276*897ae9c1SDavid du Colombier 	/* tx fifo urgent threshold (tx interrupt coalescing), pxtfut */
277*897ae9c1SDavid du Colombier #define TFUTipginttx(v)	(((v) & MASK(16))<<4);
278*897ae9c1SDavid du Colombier 
279*897ae9c1SDavid du Colombier 	/* minimal frame size, mfs */
280*897ae9c1SDavid du Colombier 	MFS40by	= 10<<2,
281*897ae9c1SDavid du Colombier 	MFS44by	= 11<<2,
282*897ae9c1SDavid du Colombier 	MFS48by	= 12<<2,
283*897ae9c1SDavid du Colombier 	MFS52by	= 13<<2,
284*897ae9c1SDavid du Colombier 	MFS56by	= 14<<2,
285*897ae9c1SDavid du Colombier 	MFS60by	= 15<<2,
286*897ae9c1SDavid du Colombier 	MFS64by	= 16<<2,
287*897ae9c1SDavid du Colombier 
288*897ae9c1SDavid du Colombier 	/* receive descriptor */
289*897ae9c1SDavid du Colombier #define Bufsize(v)	((v)<<3)
290*897ae9c1SDavid du Colombier 
291*897ae9c1SDavid du Colombier 	/* receive descriptor status */
292*897ae9c1SDavid du Colombier 	RCSmacerr	= 1<<0,
293*897ae9c1SDavid du Colombier 	RCSmacmask	= 3<<1,
294*897ae9c1SDavid du Colombier 	RCSmacce	= 0<<1,
295*897ae9c1SDavid du Colombier 	RCSmacor	= 1<<1,
296*897ae9c1SDavid du Colombier 	RCSmacmf	= 2<<1,
297*897ae9c1SDavid du Colombier 	RCSl4chkshift	= 3,
298*897ae9c1SDavid du Colombier 	RCSl4chkmask	= MASK(16),
299*897ae9c1SDavid du Colombier 	RCSvlan		= 1<<17,
300*897ae9c1SDavid du Colombier 	RCSbpdu		= 1<<18,
301*897ae9c1SDavid du Colombier 	RCSl4mask	= 3<<21,
302*897ae9c1SDavid du Colombier 	RCSl4tcp4	= 0<<21,
303*897ae9c1SDavid du Colombier 	RCSl4udp4	= 1<<21,
304*897ae9c1SDavid du Colombier 	RCSl4other	= 2<<21,
305*897ae9c1SDavid du Colombier 	RCSl4rsvd	= 3<<21,
306*897ae9c1SDavid du Colombier 	RCSl2ev2	= 1<<23,
307*897ae9c1SDavid du Colombier 	RCSl3ip4	= 1<<24,
308*897ae9c1SDavid du Colombier 	RCSip4headok	= 1<<25,
309*897ae9c1SDavid du Colombier 	RCSlast		= 1<<26,
310*897ae9c1SDavid du Colombier 	RCSfirst	= 1<<27,
311*897ae9c1SDavid du Colombier 	RCSunknownaddr	= 1<<28,
312*897ae9c1SDavid du Colombier 	RCSenableintr	= 1<<29,
313*897ae9c1SDavid du Colombier 	RCSl4chkok	= 1<<30,
314*897ae9c1SDavid du Colombier 	RCSdmaown	= 1<<31,
315*897ae9c1SDavid du Colombier 
316*897ae9c1SDavid du Colombier 	/* transmit descriptor status */
317*897ae9c1SDavid du Colombier 	TCSmacerr	= 1<<0,
318*897ae9c1SDavid du Colombier 	TCSmacmask	= 3<<1,
319*897ae9c1SDavid du Colombier 	TCSmaclc	= 0<<1,
320*897ae9c1SDavid du Colombier 	TCSmacur	= 1<<1,
321*897ae9c1SDavid du Colombier 	TCSmacrl	= 2<<1,
322*897ae9c1SDavid du Colombier 	TCSllc		= 1<<9,
323*897ae9c1SDavid du Colombier 	TCSl4chkmode	= 1<<10,
324*897ae9c1SDavid du Colombier 	TCSipv4hdlenshift= 11,
325*897ae9c1SDavid du Colombier 	TCSvlan		= 1<<15,
326*897ae9c1SDavid du Colombier 	TCSl4type	= 1<<16,
327*897ae9c1SDavid du Colombier 	TCSgl4chk	= 1<<17,
328*897ae9c1SDavid du Colombier 	TCSgip4chk	= 1<<18,
329*897ae9c1SDavid du Colombier 	TCSpadding	= 1<<19,
330*897ae9c1SDavid du Colombier 	TCSlast		= 1<<20,
331*897ae9c1SDavid du Colombier 	TCSfirst	= 1<<21,
332*897ae9c1SDavid du Colombier 	TCSenableintr	= 1<<23,
333*897ae9c1SDavid du Colombier 	TCSautomode	= 1<<30,
334*897ae9c1SDavid du Colombier 	TCSdmaown	= 1<<31,
335*897ae9c1SDavid du Colombier };
336*897ae9c1SDavid du Colombier 
337*897ae9c1SDavid du Colombier enum {
338*897ae9c1SDavid du Colombier 	/* SMI regs */
339*897ae9c1SDavid du Colombier 	PhysmiTimeout	= 10000,	/* what units? in ms. */
340*897ae9c1SDavid du Colombier 	Physmidataoff	= 0,		/* Data */
341*897ae9c1SDavid du Colombier 	Physmidatamask	= 0xffff<<Physmidataoff,
342*897ae9c1SDavid du Colombier 
343*897ae9c1SDavid du Colombier 	Physmiaddroff 	= 16,		/* PHY device addr */
344*897ae9c1SDavid du Colombier 	Physmiaddrmask	= 0x1f << Physmiaddroff,
345*897ae9c1SDavid du Colombier 
346*897ae9c1SDavid du Colombier 	Physmiop	= 26,
347*897ae9c1SDavid du Colombier 	Physmiopmask	= 3<<Physmiop,
348*897ae9c1SDavid du Colombier 	PhysmiopWr	= 0<<Physmiop,
349*897ae9c1SDavid du Colombier 	PhysmiopRd	= 1<<Physmiop,
350*897ae9c1SDavid du Colombier 
351*897ae9c1SDavid du Colombier 	PhysmiReadok	= 1<<27,
352*897ae9c1SDavid du Colombier 	PhysmiBusy	= 1<<28,
353*897ae9c1SDavid du Colombier 
354*897ae9c1SDavid du Colombier 	SmiRegaddroff	= 21,		/* PHY device register addr */
355*897ae9c1SDavid du Colombier 	SmiRegaddrmask	= 0x1f << SmiRegaddroff,
356*897ae9c1SDavid du Colombier };
357*897ae9c1SDavid du Colombier 
358*897ae9c1SDavid du Colombier struct Gbereg {
359*897ae9c1SDavid du Colombier 	ulong	phy;			/* PHY address */
360*897ae9c1SDavid du Colombier 	ulong	smi;			/* serial mgmt. interface */
361*897ae9c1SDavid du Colombier 	ulong	euda;			/* ether default address */
362*897ae9c1SDavid du Colombier 	ulong	eudid;			/* ether default id */
363*897ae9c1SDavid du Colombier 	uchar	_pad0[0x80-0x10];
364*897ae9c1SDavid du Colombier 
365*897ae9c1SDavid du Colombier 	ulong	euirq;			/* interrupt cause */
366*897ae9c1SDavid du Colombier 	ulong	euirqmask;		/* interrupt mask */
367*897ae9c1SDavid du Colombier 	uchar	_pad1[0x94-0x88];
368*897ae9c1SDavid du Colombier 
369*897ae9c1SDavid du Colombier 	ulong	euea;			/* error address */
370*897ae9c1SDavid du Colombier 	ulong	euiae;			/* internal error address */
371*897ae9c1SDavid du Colombier 	uchar	_pad2[0xb0-0x9c];
372*897ae9c1SDavid du Colombier 
373*897ae9c1SDavid du Colombier 	ulong	euc;			/* control */
374*897ae9c1SDavid du Colombier 	uchar	_pad3[0x200-0xb4];
375*897ae9c1SDavid du Colombier 
376*897ae9c1SDavid du Colombier 	struct {
377*897ae9c1SDavid du Colombier 		ulong	base;		/* window base */
378*897ae9c1SDavid du Colombier 		ulong	size;		/* window size */
379*897ae9c1SDavid du Colombier 	} base[6];
380*897ae9c1SDavid du Colombier 	uchar	_pad4[0x280-0x230];
381*897ae9c1SDavid du Colombier 
382*897ae9c1SDavid du Colombier 	ulong	harr[4];		/* high address remap */
383*897ae9c1SDavid du Colombier 	ulong	bare;			/* base address enable */
384*897ae9c1SDavid du Colombier 	ulong	epap;			/* port access protect */
385*897ae9c1SDavid du Colombier 	uchar	_pad5[0x400-0x298];
386*897ae9c1SDavid du Colombier 
387*897ae9c1SDavid du Colombier 	ulong	portcfg;		/* port configuration */
388*897ae9c1SDavid du Colombier 	ulong	portcfgx;		/* port config. extend */
389*897ae9c1SDavid du Colombier 	ulong	mii;			/* mii serial parameters */
390*897ae9c1SDavid du Colombier 	ulong	_pad6;
391*897ae9c1SDavid du Colombier 	ulong	evlane;			/* vlan ether type */
392*897ae9c1SDavid du Colombier 	ulong	macal;			/* mac address low */
393*897ae9c1SDavid du Colombier 	ulong	macah;			/* mac address high */
394*897ae9c1SDavid du Colombier 	ulong	sdc;			/* sdma config. */
395*897ae9c1SDavid du Colombier 	ulong	dscp[7];		/* ip diff. serv. code point -> pri */
396*897ae9c1SDavid du Colombier 	ulong	psc0;			/* port serial control 0 */
397*897ae9c1SDavid du Colombier 	ulong	vpt2p;			/* vlan priority tag -> pri */
398*897ae9c1SDavid du Colombier 	ulong	ps0;			/* ether port status 0 */
399*897ae9c1SDavid du Colombier 	ulong	tqc;			/* transmit queue command */
400*897ae9c1SDavid du Colombier 	ulong	psc1;			/* port serial control 1 */
401*897ae9c1SDavid du Colombier 	ulong	ps1;			/* ether port status 1 */
402*897ae9c1SDavid du Colombier 	ulong	mvhdr;			/* marvell header */
403*897ae9c1SDavid du Colombier 	ulong	_pad8[2];
404*897ae9c1SDavid du Colombier 
405*897ae9c1SDavid du Colombier 	/* interrupts */
406*897ae9c1SDavid du Colombier 	ulong	irq;			/* interrupt cause; some rw0c bits */
407*897ae9c1SDavid du Colombier 	ulong	irqe;			/* " " extended; some rw0c bits */
408*897ae9c1SDavid du Colombier 	ulong	irqmask;		/* interrupt mask (actually enable) */
409*897ae9c1SDavid du Colombier 	ulong	irqemask;		/* " " extended */
410*897ae9c1SDavid du Colombier 
411*897ae9c1SDavid du Colombier 	ulong	_pad9;
412*897ae9c1SDavid du Colombier 	ulong	pxtfut;			/* port tx fifo urgent threshold */
413*897ae9c1SDavid du Colombier 	ulong	_pad10;
414*897ae9c1SDavid du Colombier 	ulong	pxmfs;			/* port rx minimum frame size */
415*897ae9c1SDavid du Colombier 	ulong	_pad11;
416*897ae9c1SDavid du Colombier 
417*897ae9c1SDavid du Colombier 	/*
418*897ae9c1SDavid du Colombier 	 * # of input frames discarded by addr filtering or lack of resources;
419*897ae9c1SDavid du Colombier 	 * zeroed upon read.
420*897ae9c1SDavid du Colombier 	 */
421*897ae9c1SDavid du Colombier 	ulong	pxdfc;			/* port rx discard frame counter */
422*897ae9c1SDavid du Colombier 	ulong	pxofc;			/* port overrun frame counter */
423*897ae9c1SDavid du Colombier 	ulong	_pad12[2];
424*897ae9c1SDavid du Colombier 	ulong	piae;			/* port internal address error */
425*897ae9c1SDavid du Colombier 	uchar	_pad13[0x4bc-0x498];
426*897ae9c1SDavid du Colombier 	ulong	etherprio;		/* ether type priority */
427*897ae9c1SDavid du Colombier 	uchar	_pad14[0x4dc-0x4c0];
428*897ae9c1SDavid du Colombier 	ulong	tqfpc;			/* tx queue fixed priority config. */
429*897ae9c1SDavid du Colombier 	ulong	pttbrc;			/* port tx token-bucket rate config. */
430*897ae9c1SDavid du Colombier 	ulong	tqc1;			/* tx queue command 1 */
431*897ae9c1SDavid du Colombier 	ulong	pmtu;			/* port maximum transmit unit */
432*897ae9c1SDavid du Colombier 	ulong	pmtbs;			/* port maximum token bucket size */
433*897ae9c1SDavid du Colombier 	uchar	_pad15[0x600-0x4f0];
434*897ae9c1SDavid du Colombier 
435*897ae9c1SDavid du Colombier 	struct {
436*897ae9c1SDavid du Colombier 		ulong	_pad[3];
437*897ae9c1SDavid du Colombier 		ulong	r;		/* phys. addr.: cur. rx desc. ptrs */
438*897ae9c1SDavid du Colombier 	} crdp[8];
439*897ae9c1SDavid du Colombier 	ulong	rqc;			/* rx queue command */
440*897ae9c1SDavid du Colombier 	ulong	tcsdp;			/* phys. addr.: cur. tx desc. ptr */
441*897ae9c1SDavid du Colombier 	uchar	_pad16[0x6c0-0x688];
442*897ae9c1SDavid du Colombier 
443*897ae9c1SDavid du Colombier 	ulong	tcqdp[8];		/* phys. addr.: cur. tx q. desc. ptr */
444*897ae9c1SDavid du Colombier 	uchar	_pad17[0x700-0x6e0];
445*897ae9c1SDavid du Colombier 
446*897ae9c1SDavid du Colombier 	struct {
447*897ae9c1SDavid du Colombier 		ulong	tbctr;		/* queue tx token-bucket counter */
448*897ae9c1SDavid du Colombier 		ulong	tbcfg;		/* tx queue token-bucket config. */
449*897ae9c1SDavid du Colombier 		ulong	acfg;		/* tx queue arbiter config. */
450*897ae9c1SDavid du Colombier 		ulong	_pad;
451*897ae9c1SDavid du Colombier 	} tq[8];
452*897ae9c1SDavid du Colombier 	ulong	pttbc;			/* port tx token-bucket counter */
453*897ae9c1SDavid du Colombier 	uchar	_pad18[0x7a8-0x784];
454*897ae9c1SDavid du Colombier 
455*897ae9c1SDavid du Colombier 	ulong	ipg2;			/* tx queue ipg */
456*897ae9c1SDavid du Colombier 	ulong	_pad19[3];
457*897ae9c1SDavid du Colombier 	ulong	ipg3;
458*897ae9c1SDavid du Colombier 	ulong	_pad20;
459*897ae9c1SDavid du Colombier 	ulong	htlp;			/* high token in low packet */
460*897ae9c1SDavid du Colombier 	ulong	htap;			/* high token in async packet */
461*897ae9c1SDavid du Colombier 	ulong	ltap;			/* low token in async packet */
462*897ae9c1SDavid du Colombier 	ulong	_pad21;
463*897ae9c1SDavid du Colombier 	ulong	ts;			/* tx speed */
464*897ae9c1SDavid du Colombier 	uchar	_pad22[0x1000-0x7d4];
465*897ae9c1SDavid du Colombier 
466*897ae9c1SDavid du Colombier 	/* mac mib counters: statistics */
467*897ae9c1SDavid du Colombier 	Mibstats;
468*897ae9c1SDavid du Colombier 	uchar	_pad23[0x1400-0x1080];
469*897ae9c1SDavid du Colombier 
470*897ae9c1SDavid du Colombier 	/* multicast filtering; each byte: Qno<<1 | Pass */
471*897ae9c1SDavid du Colombier 	ulong	dfsmt[64];	/* dest addr filter special m'cast table */
472*897ae9c1SDavid du Colombier 	ulong	dfomt[64];	/* dest addr filter other m'cast table */
473*897ae9c1SDavid du Colombier 	/* unicast filtering */
474*897ae9c1SDavid du Colombier 	ulong	dfut[4];		/* dest addr filter unicast table */
475*897ae9c1SDavid du Colombier };
476*897ae9c1SDavid du Colombier 
477*897ae9c1SDavid du Colombier static void getmibstats(Ctlr *);
478*897ae9c1SDavid du Colombier 
479*897ae9c1SDavid du Colombier static void
480*897ae9c1SDavid du Colombier rxfreeb(Block *b)
481*897ae9c1SDavid du Colombier {
482*897ae9c1SDavid du Colombier 	/* freeb(b) will have previously decremented b->ref to 0; raise to 1 */
483*897ae9c1SDavid du Colombier 	_xinc(&b->ref);
484*897ae9c1SDavid du Colombier //iprint("fr %ld ", b->ref);
485*897ae9c1SDavid du Colombier 	b->wp = b->rp =
486*897ae9c1SDavid du Colombier 		(uchar*)((uintptr)(b->lim - Rxblklen) & ~(Bufalign - 1));
487*897ae9c1SDavid du Colombier 	assert(((uintptr)b->rp & (Bufalign - 1)) == 0);
488*897ae9c1SDavid du Colombier 	b->free = rxfreeb;
489*897ae9c1SDavid du Colombier 
490*897ae9c1SDavid du Colombier 	ilock(&freeblocks);
491*897ae9c1SDavid du Colombier 	b->next = freeblocks.head;
492*897ae9c1SDavid du Colombier 	freeblocks.head = b;
493*897ae9c1SDavid du Colombier 	iunlock(&freeblocks);
494*897ae9c1SDavid du Colombier }
495*897ae9c1SDavid du Colombier 
496*897ae9c1SDavid du Colombier static Block *
497*897ae9c1SDavid du Colombier rxallocb(void)
498*897ae9c1SDavid du Colombier {
499*897ae9c1SDavid du Colombier 	Block *b;
500*897ae9c1SDavid du Colombier 
501*897ae9c1SDavid du Colombier 	ilock(&freeblocks);
502*897ae9c1SDavid du Colombier 	b = freeblocks.head;
503*897ae9c1SDavid du Colombier 	if(b != nil) {
504*897ae9c1SDavid du Colombier 		freeblocks.head = b->next;
505*897ae9c1SDavid du Colombier 		b->next = nil;
506*897ae9c1SDavid du Colombier 		b->free = rxfreeb;
507*897ae9c1SDavid du Colombier 	}
508*897ae9c1SDavid du Colombier 	iunlock(&freeblocks);
509*897ae9c1SDavid du Colombier 	return b;
510*897ae9c1SDavid du Colombier }
511*897ae9c1SDavid du Colombier 
512*897ae9c1SDavid du Colombier static void
513*897ae9c1SDavid du Colombier rxkick(Ctlr *ctlr)
514*897ae9c1SDavid du Colombier {
515*897ae9c1SDavid du Colombier 	Gbereg *reg = ctlr->reg;
516*897ae9c1SDavid du Colombier 
517*897ae9c1SDavid du Colombier 	if (reg->crdp[Qno].r == 0)
518*897ae9c1SDavid du Colombier 		reg->crdp[Qno].r = PADDR(&ctlr->rx[ctlr->rxhead]);
519*897ae9c1SDavid du Colombier 	if ((reg->rqc & 0xff) == 0)		/* all queues are stopped? */
520*897ae9c1SDavid du Colombier 		reg->rqc = Rxqon(Qno);		/* restart */
521*897ae9c1SDavid du Colombier 	coherence();
522*897ae9c1SDavid du Colombier }
523*897ae9c1SDavid du Colombier 
524*897ae9c1SDavid du Colombier static void
525*897ae9c1SDavid du Colombier txkick(Ctlr *ctlr)
526*897ae9c1SDavid du Colombier {
527*897ae9c1SDavid du Colombier 	Gbereg *reg = ctlr->reg;
528*897ae9c1SDavid du Colombier 
529*897ae9c1SDavid du Colombier 	if (reg->tcqdp[Qno] == 0)
530*897ae9c1SDavid du Colombier 		reg->tcqdp[Qno] = PADDR(&ctlr->tx[ctlr->txhead]);
531*897ae9c1SDavid du Colombier 	if ((reg->tqc & 0xff) == 0)		/* all q's stopped? */
532*897ae9c1SDavid du Colombier 		reg->tqc = Txqon(Qno);		/* restart */
533*897ae9c1SDavid du Colombier 	coherence();
534*897ae9c1SDavid du Colombier }
535*897ae9c1SDavid du Colombier 
536*897ae9c1SDavid du Colombier static void
537*897ae9c1SDavid du Colombier rxreplenish(Ctlr *ctlr)
538*897ae9c1SDavid du Colombier {
539*897ae9c1SDavid du Colombier 	Rx *r;
540*897ae9c1SDavid du Colombier 	Block *b;
541*897ae9c1SDavid du Colombier 
542*897ae9c1SDavid du Colombier 	while(ctlr->rxb[ctlr->rxtail] == nil) {
543*897ae9c1SDavid du Colombier 		b = rxallocb();
544*897ae9c1SDavid du Colombier 		if(b == nil) {
545*897ae9c1SDavid du Colombier 			iprint("ether1116: rxreplenish out of buffers\n");
546*897ae9c1SDavid du Colombier 			break;
547*897ae9c1SDavid du Colombier 		}
548*897ae9c1SDavid du Colombier 
549*897ae9c1SDavid du Colombier 		ctlr->rxb[ctlr->rxtail] = b;
550*897ae9c1SDavid du Colombier 
551*897ae9c1SDavid du Colombier 		/* set up receive descriptor */
552*897ae9c1SDavid du Colombier 		r = &ctlr->rx[ctlr->rxtail];
553*897ae9c1SDavid du Colombier 		assert(((uintptr)r & (Descralign - 1)) == 0);
554*897ae9c1SDavid du Colombier 		r->countsize = Bufsize(Rxblklen);
555*897ae9c1SDavid du Colombier 		r->buf = PADDR(b->rp);
556*897ae9c1SDavid du Colombier 		cachedwbse(r, sizeof *r);
557*897ae9c1SDavid du Colombier 		l2cacheuwbse(r, sizeof *r);
558*897ae9c1SDavid du Colombier 
559*897ae9c1SDavid du Colombier 		/* and fire */
560*897ae9c1SDavid du Colombier 		r->cs = RCSdmaown | RCSenableintr;
561*897ae9c1SDavid du Colombier 		cachedwbse(&r->cs, BY2SE);
562*897ae9c1SDavid du Colombier 		l2cacheuwbse(&r->cs, BY2SE);
563*897ae9c1SDavid du Colombier 
564*897ae9c1SDavid du Colombier 		ctlr->rxtail = NEXT(ctlr->rxtail, Nrx);
565*897ae9c1SDavid du Colombier 	}
566*897ae9c1SDavid du Colombier 	rxkick(ctlr);
567*897ae9c1SDavid du Colombier }
568*897ae9c1SDavid du Colombier 
569*897ae9c1SDavid du Colombier static void
570*897ae9c1SDavid du Colombier dump(uchar *bp, long max)
571*897ae9c1SDavid du Colombier {
572*897ae9c1SDavid du Colombier 	if (max > 64)
573*897ae9c1SDavid du Colombier 		max = 64;
574*897ae9c1SDavid du Colombier 	for (; max > 0; max--, bp++)
575*897ae9c1SDavid du Colombier 		iprint("%02.2ux ", *bp);
576*897ae9c1SDavid du Colombier 	print("...\n");
577*897ae9c1SDavid du Colombier }
578*897ae9c1SDavid du Colombier 
579*897ae9c1SDavid du Colombier static void
580*897ae9c1SDavid du Colombier etheractive(Ether *ether)
581*897ae9c1SDavid du Colombier {
582*897ae9c1SDavid du Colombier 	ether->starttime = TK2MS(MACHP(0)->ticks)/1000;
583*897ae9c1SDavid du Colombier }
584*897ae9c1SDavid du Colombier 
585*897ae9c1SDavid du Colombier static void
586*897ae9c1SDavid du Colombier ethercheck(Ether *ether)
587*897ae9c1SDavid du Colombier {
588*897ae9c1SDavid du Colombier 	if (ether->starttime != 0 &&
589*897ae9c1SDavid du Colombier 	    TK2MS(MACHP(0)->ticks)/1000 - ether->starttime > Etherstuck) {
590*897ae9c1SDavid du Colombier 		etheractive(ether);
591*897ae9c1SDavid du Colombier 		iprint("ethernet stuck\n");
592*897ae9c1SDavid du Colombier 	}
593*897ae9c1SDavid du Colombier }
594*897ae9c1SDavid du Colombier 
595*897ae9c1SDavid du Colombier static void
596*897ae9c1SDavid du Colombier receive(Ether *ether)
597*897ae9c1SDavid du Colombier {
598*897ae9c1SDavid du Colombier 	int i;
599*897ae9c1SDavid du Colombier 	ulong n;
600*897ae9c1SDavid du Colombier 	Block *b;
601*897ae9c1SDavid du Colombier 	Ctlr *ctlr = ether->ctlr;
602*897ae9c1SDavid du Colombier 	Rx *r;
603*897ae9c1SDavid du Colombier 
604*897ae9c1SDavid du Colombier 	ethercheck(ether);
605*897ae9c1SDavid du Colombier 	for (i = Nrx-2; i > 0; i--) {
606*897ae9c1SDavid du Colombier 		r = &ctlr->rx[ctlr->rxhead];
607*897ae9c1SDavid du Colombier 		assert(((uintptr)r & (Descralign - 1)) == 0);
608*897ae9c1SDavid du Colombier 		l2cacheuinvse(r, sizeof *r);
609*897ae9c1SDavid du Colombier 		cachedinvse(r, sizeof *r);
610*897ae9c1SDavid du Colombier 		if(r->cs & RCSdmaown)
611*897ae9c1SDavid du Colombier 			break;
612*897ae9c1SDavid du Colombier 
613*897ae9c1SDavid du Colombier 		b = ctlr->rxb[ctlr->rxhead];
614*897ae9c1SDavid du Colombier 		if (b == nil)
615*897ae9c1SDavid du Colombier 			panic("ether1116: nil ctlr->rxb[ctlr->rxhead] "
616*897ae9c1SDavid du Colombier 				"in receive");
617*897ae9c1SDavid du Colombier 		ctlr->rxb[ctlr->rxhead] = nil;
618*897ae9c1SDavid du Colombier 		ctlr->rxhead = NEXT(ctlr->rxhead, Nrx);
619*897ae9c1SDavid du Colombier 
620*897ae9c1SDavid du Colombier 		if((r->cs & (RCSfirst|RCSlast)) != (RCSfirst|RCSlast)) {
621*897ae9c1SDavid du Colombier 			ctlr->nofirstlast++;
622*897ae9c1SDavid du Colombier 			freeb(b);
623*897ae9c1SDavid du Colombier 			continue;
624*897ae9c1SDavid du Colombier 		}
625*897ae9c1SDavid du Colombier 		if(r->cs & RCSmacerr) {
626*897ae9c1SDavid du Colombier 			freeb(b);
627*897ae9c1SDavid du Colombier 			continue;
628*897ae9c1SDavid du Colombier 		}
629*897ae9c1SDavid du Colombier 
630*897ae9c1SDavid du Colombier 		n = r->countsize >> 16;
631*897ae9c1SDavid du Colombier 		assert(n >= 2 && n < 2048);
632*897ae9c1SDavid du Colombier 
633*897ae9c1SDavid du Colombier 		l2cacheuinvse(b->rp, n);
634*897ae9c1SDavid du Colombier 		cachedinvse(b->rp, n);
635*897ae9c1SDavid du Colombier 		b->wp = b->rp + n;
636*897ae9c1SDavid du Colombier 		/*
637*897ae9c1SDavid du Colombier 		 * skip hardware padding intended to align ipv4 address
638*897ae9c1SDavid du Colombier 		 * in memory (mv-s104860-u0 §8.3.4.1)
639*897ae9c1SDavid du Colombier 		 */
640*897ae9c1SDavid du Colombier 		b->rp += 2;
641*897ae9c1SDavid du Colombier 		etheriq(ether, b, 1);
642*897ae9c1SDavid du Colombier 		etheractive(ether);
643*897ae9c1SDavid du Colombier 		if (i % (Nrx / 2) == 0)
644*897ae9c1SDavid du Colombier 			rxreplenish(ctlr);
645*897ae9c1SDavid du Colombier 	}
646*897ae9c1SDavid du Colombier 	rxreplenish(ctlr);
647*897ae9c1SDavid du Colombier }
648*897ae9c1SDavid du Colombier 
649*897ae9c1SDavid du Colombier static void
650*897ae9c1SDavid du Colombier txreplenish(Ether *ether)			/* free transmitted packets */
651*897ae9c1SDavid du Colombier {
652*897ae9c1SDavid du Colombier 	Ctlr *ctlr;
653*897ae9c1SDavid du Colombier 
654*897ae9c1SDavid du Colombier 	ctlr = ether->ctlr;
655*897ae9c1SDavid du Colombier 	while(ctlr->txtail != ctlr->txhead) {
656*897ae9c1SDavid du Colombier 		l2cacheuinvse(&ctlr->tx[ctlr->txtail].cs, BY2SE);
657*897ae9c1SDavid du Colombier 		cachedinvse(&ctlr->tx[ctlr->txtail].cs, BY2SE);
658*897ae9c1SDavid du Colombier 		if(ctlr->tx[ctlr->txtail].cs & TCSdmaown)
659*897ae9c1SDavid du Colombier 			break;
660*897ae9c1SDavid du Colombier 		if(ctlr->txb[ctlr->txtail] == nil)
661*897ae9c1SDavid du Colombier 			panic("no block for sent packet?!");
662*897ae9c1SDavid du Colombier 		freeb(ctlr->txb[ctlr->txtail]);
663*897ae9c1SDavid du Colombier 		ctlr->txb[ctlr->txtail] = nil;
664*897ae9c1SDavid du Colombier 		ctlr->txtail = NEXT(ctlr->txtail, Ntx);
665*897ae9c1SDavid du Colombier 		etheractive(ether);
666*897ae9c1SDavid du Colombier 	}
667*897ae9c1SDavid du Colombier }
668*897ae9c1SDavid du Colombier 
669*897ae9c1SDavid du Colombier /*
670*897ae9c1SDavid du Colombier  * transmit strategy: fill the output ring as far as possible,
671*897ae9c1SDavid du Colombier  * perhaps leaving a few spare; kick off the output and take
672*897ae9c1SDavid du Colombier  * an interrupt only when the transmit queue is empty.
673*897ae9c1SDavid du Colombier  */
674*897ae9c1SDavid du Colombier static void
675*897ae9c1SDavid du Colombier transmit(Ether *ether)
676*897ae9c1SDavid du Colombier {
677*897ae9c1SDavid du Colombier 	int i, kick, len;
678*897ae9c1SDavid du Colombier 	Block *b;
679*897ae9c1SDavid du Colombier 	Ctlr *ctlr = ether->ctlr;
680*897ae9c1SDavid du Colombier 	Gbereg *reg = ctlr->reg;
681*897ae9c1SDavid du Colombier 	Tx *t;
682*897ae9c1SDavid du Colombier 
683*897ae9c1SDavid du Colombier 	ethercheck(ether);
684*897ae9c1SDavid du Colombier 	ilock(ctlr);
685*897ae9c1SDavid du Colombier 	txreplenish(ether);			/* reap old packets */
686*897ae9c1SDavid du Colombier 
687*897ae9c1SDavid du Colombier 	/* queue new packets; don't use more than half the tx descs. */
688*897ae9c1SDavid du Colombier 	kick = 0;
689*897ae9c1SDavid du Colombier 	for (i = Ntx/2 - 2; i > 0; i--) {
690*897ae9c1SDavid du Colombier 		t = &ctlr->tx[ctlr->txhead];
691*897ae9c1SDavid du Colombier 		assert(((uintptr)t & (Descralign - 1)) == 0);
692*897ae9c1SDavid du Colombier 		l2cacheuinvse(t, sizeof *t);
693*897ae9c1SDavid du Colombier 		cachedinvse(t, sizeof *t);
694*897ae9c1SDavid du Colombier 		if(t->cs & TCSdmaown) {		/* free descriptor? */
695*897ae9c1SDavid du Colombier 			ctlr->txringfull++;
696*897ae9c1SDavid du Colombier 			break;
697*897ae9c1SDavid du Colombier 		}
698*897ae9c1SDavid du Colombier 
699*897ae9c1SDavid du Colombier 		b = qget(ether->oq);		/* outgoing packet? */
700*897ae9c1SDavid du Colombier 		if (b == nil)
701*897ae9c1SDavid du Colombier 			break;
702*897ae9c1SDavid du Colombier 		len = BLEN(b);
703*897ae9c1SDavid du Colombier 		if(len < ether->minmtu || len > ether->maxmtu) {
704*897ae9c1SDavid du Colombier 			freeb(b);
705*897ae9c1SDavid du Colombier 			continue;
706*897ae9c1SDavid du Colombier 		}
707*897ae9c1SDavid du Colombier 		ctlr->txb[ctlr->txhead] = b;
708*897ae9c1SDavid du Colombier 
709*897ae9c1SDavid du Colombier 		/* make sure the whole packet is in memory */
710*897ae9c1SDavid du Colombier 		cachedwbse(b->rp, len);
711*897ae9c1SDavid du Colombier 		l2cacheuwbse(b->rp, len);
712*897ae9c1SDavid du Colombier 
713*897ae9c1SDavid du Colombier 		/* set up the transmit descriptor */
714*897ae9c1SDavid du Colombier 		t->buf = PADDR(b->rp);
715*897ae9c1SDavid du Colombier 		t->countchk = len << 16;
716*897ae9c1SDavid du Colombier 		cachedwbse(t, sizeof *t);
717*897ae9c1SDavid du Colombier 		l2cacheuwbse(t, sizeof *t);
718*897ae9c1SDavid du Colombier 
719*897ae9c1SDavid du Colombier 		/* and fire */
720*897ae9c1SDavid du Colombier 		t->cs = TCSpadding | TCSfirst | TCSlast | TCSdmaown |
721*897ae9c1SDavid du Colombier 			TCSenableintr;
722*897ae9c1SDavid du Colombier 		cachedwbse(&t->cs, BY2SE);
723*897ae9c1SDavid du Colombier 		l2cacheuwbse(&t->cs, BY2SE);
724*897ae9c1SDavid du Colombier 
725*897ae9c1SDavid du Colombier 		kick++;
726*897ae9c1SDavid du Colombier 		ctlr->txhead = NEXT(ctlr->txhead, Ntx);
727*897ae9c1SDavid du Colombier 	}
728*897ae9c1SDavid du Colombier 	if (kick) {
729*897ae9c1SDavid du Colombier 		txkick(ctlr);
730*897ae9c1SDavid du Colombier 
731*897ae9c1SDavid du Colombier 		reg->irqmask  |= Itxendq(Qno);
732*897ae9c1SDavid du Colombier 		reg->irqemask |= IEtxerrq(Qno) | IEtxunderrun;
733*897ae9c1SDavid du Colombier 	}
734*897ae9c1SDavid du Colombier 	iunlock(ctlr);
735*897ae9c1SDavid du Colombier }
736*897ae9c1SDavid du Colombier 
737*897ae9c1SDavid du Colombier static void
738*897ae9c1SDavid du Colombier dumprxdescs(Ctlr *ctlr)
739*897ae9c1SDavid du Colombier {
740*897ae9c1SDavid du Colombier 	int i;
741*897ae9c1SDavid du Colombier 	Gbereg *reg = ctlr->reg;
742*897ae9c1SDavid du Colombier 
743*897ae9c1SDavid du Colombier 	iprint("\nrxhead %d rxtail %d; txcdp %#p rxcdp %#p\n",
744*897ae9c1SDavid du Colombier 		ctlr->rxhead, ctlr->rxtail, reg->tcqdp[Qno], reg->crdp[Qno].r);
745*897ae9c1SDavid du Colombier 	for (i = 0; i < Nrx; i++)
746*897ae9c1SDavid du Colombier 		iprint("rxb %d @ %#p: %#p\n", i, &ctlr->rxb[i], ctlr->rxb[i]);
747*897ae9c1SDavid du Colombier 	for (i = 0; i < Nrx; i++)
748*897ae9c1SDavid du Colombier 		iprint("rx %d @ %#p: cs %#lux countsize %lud buf %#lux next %#lux\n",
749*897ae9c1SDavid du Colombier 			i, &ctlr->rx[i], ctlr->rx[i].cs,
750*897ae9c1SDavid du Colombier 			ctlr->rx[i].countsize >> 3, ctlr->rx[i].buf,
751*897ae9c1SDavid du Colombier 			ctlr->rx[i].next);
752*897ae9c1SDavid du Colombier 	delay(1000);
753*897ae9c1SDavid du Colombier }
754*897ae9c1SDavid du Colombier 
755*897ae9c1SDavid du Colombier static int
756*897ae9c1SDavid du Colombier gotinput(void* ctlr)
757*897ae9c1SDavid du Colombier {
758*897ae9c1SDavid du Colombier 	return ((Ctlr*)ctlr)->haveinput != 0;
759*897ae9c1SDavid du Colombier }
760*897ae9c1SDavid du Colombier 
761*897ae9c1SDavid du Colombier /*
762*897ae9c1SDavid du Colombier  * process any packets in the input ring.
763*897ae9c1SDavid du Colombier  * also sum mib stats frequently to avoid the overflow
764*897ae9c1SDavid du Colombier  * mentioned in the errata.
765*897ae9c1SDavid du Colombier  */
766*897ae9c1SDavid du Colombier static void
767*897ae9c1SDavid du Colombier rcvproc(void* arg)
768*897ae9c1SDavid du Colombier {
769*897ae9c1SDavid du Colombier 	Ctlr *ctlr;
770*897ae9c1SDavid du Colombier 	Ether *ether;
771*897ae9c1SDavid du Colombier 
772*897ae9c1SDavid du Colombier 	ether = arg;
773*897ae9c1SDavid du Colombier 	ctlr = ether->ctlr;
774*897ae9c1SDavid du Colombier 	for(;;){
775*897ae9c1SDavid du Colombier 		tsleep(&ctlr->rrendez, gotinput, ctlr, 10*1000);
776*897ae9c1SDavid du Colombier 		ilock(ctlr);
777*897ae9c1SDavid du Colombier 		getmibstats(ctlr);
778*897ae9c1SDavid du Colombier 		if (ctlr->haveinput) {
779*897ae9c1SDavid du Colombier 			ctlr->haveinput = 0;
780*897ae9c1SDavid du Colombier 			iunlock(ctlr);
781*897ae9c1SDavid du Colombier 			receive(ether);
782*897ae9c1SDavid du Colombier 		} else
783*897ae9c1SDavid du Colombier 			iunlock(ctlr);
784*897ae9c1SDavid du Colombier 	}
785*897ae9c1SDavid du Colombier }
786*897ae9c1SDavid du Colombier 
787*897ae9c1SDavid du Colombier static void
788*897ae9c1SDavid du Colombier interrupt(Ureg*, void *arg)
789*897ae9c1SDavid du Colombier {
790*897ae9c1SDavid du Colombier 	ulong irq, irqe, handled;
791*897ae9c1SDavid du Colombier 	Ether *ether = arg;
792*897ae9c1SDavid du Colombier 	Ctlr *ctlr = ether->ctlr;
793*897ae9c1SDavid du Colombier 	Gbereg *reg = ctlr->reg;
794*897ae9c1SDavid du Colombier 
795*897ae9c1SDavid du Colombier 	handled = 0;
796*897ae9c1SDavid du Colombier 	irq = reg->irq;
797*897ae9c1SDavid du Colombier 	irqe = reg->irqe;
798*897ae9c1SDavid du Colombier 	reg->irq = 0;				/* extinguish intr causes */
799*897ae9c1SDavid du Colombier 	reg->irqe = 0;				/* " " " */
800*897ae9c1SDavid du Colombier 	ethercheck(ether);
801*897ae9c1SDavid du Colombier 
802*897ae9c1SDavid du Colombier 	if(irq & Irxbufferq(Qno)) {
803*897ae9c1SDavid du Colombier 		/*
804*897ae9c1SDavid du Colombier 		 * letting a kproc process the input takes far less real time
805*897ae9c1SDavid du Colombier 		 * than doing it all at interrupt level.
806*897ae9c1SDavid du Colombier 		 */
807*897ae9c1SDavid du Colombier 		ctlr->haveinput = 1;
808*897ae9c1SDavid du Colombier 		wakeup(&ctlr->rrendez);
809*897ae9c1SDavid du Colombier 		handled++;
810*897ae9c1SDavid du Colombier 	} else
811*897ae9c1SDavid du Colombier 		rxkick(ctlr);
812*897ae9c1SDavid du Colombier 
813*897ae9c1SDavid du Colombier 	if(irq & Itxendq(Qno)) {		/* transmit ring empty? */
814*897ae9c1SDavid du Colombier 		reg->irqmask  &= ~Itxendq(Qno);	/* prevent more interrupts */
815*897ae9c1SDavid du Colombier 		reg->irqemask &= ~(IEtxerrq(Qno) | IEtxunderrun);
816*897ae9c1SDavid du Colombier 		transmit(ether);
817*897ae9c1SDavid du Colombier 		handled++;
818*897ae9c1SDavid du Colombier 	}
819*897ae9c1SDavid du Colombier 
820*897ae9c1SDavid du Colombier 	if(irqe & IEsum) {
821*897ae9c1SDavid du Colombier 		/*
822*897ae9c1SDavid du Colombier 		 * IElinkchg appears to only be set when unplugging.
823*897ae9c1SDavid du Colombier 		 * autonegotiation is likely not done yet, so linkup not valid,
824*897ae9c1SDavid du Colombier 		 * thus we note the link change here, and check for
825*897ae9c1SDavid du Colombier 		 * that and autonegotiation done below.
826*897ae9c1SDavid du Colombier 		 */
827*897ae9c1SDavid du Colombier 		if(irqe & IEphystschg) {
828*897ae9c1SDavid du Colombier 			ether->link = (reg->ps0 & PS0linkup) != 0;
829*897ae9c1SDavid du Colombier 			ether->linkchg = 1;
830*897ae9c1SDavid du Colombier 		}
831*897ae9c1SDavid du Colombier 		if(irqe & IEtxerrq(Qno))
832*897ae9c1SDavid du Colombier 			ether->oerrs++;
833*897ae9c1SDavid du Colombier 		if(irqe & IErxoverrun)
834*897ae9c1SDavid du Colombier 			ether->overflows++;
835*897ae9c1SDavid du Colombier 		if(irqe & IEtxunderrun)
836*897ae9c1SDavid du Colombier 			ctlr->txunderrun++;
837*897ae9c1SDavid du Colombier 		if(irqe & (IEphystschg | IEtxerrq(Qno) | IErxoverrun |
838*897ae9c1SDavid du Colombier 		    IEtxunderrun))
839*897ae9c1SDavid du Colombier 			handled++;
840*897ae9c1SDavid du Colombier 	}
841*897ae9c1SDavid du Colombier 	if (irq & Isum) {
842*897ae9c1SDavid du Colombier 		/* TODO we get these continually on the guruplug */
843*897ae9c1SDavid du Colombier 		if (irq & Irxerrq(Qno)) {
844*897ae9c1SDavid du Colombier 			ether->buffs++;		/* approx. error */
845*897ae9c1SDavid du Colombier 			/* null descriptor pointer or descriptor owned by cpu */
846*897ae9c1SDavid du Colombier //			iprint("ether1116: rx err on queue 0 - input ring full\n");
847*897ae9c1SDavid du Colombier 		}
848*897ae9c1SDavid du Colombier 		if (irq & Irxerr) {
849*897ae9c1SDavid du Colombier 			ether->buffs++;		/* approx. error */
850*897ae9c1SDavid du Colombier 			/* null descriptor pointer or descriptor owned by cpu */
851*897ae9c1SDavid du Colombier //			iprint("ether1116: rx err - input ring full\n");
852*897ae9c1SDavid du Colombier 		}
853*897ae9c1SDavid du Colombier 		if(irq & (Irxerr | Irxerrq(Qno)))
854*897ae9c1SDavid du Colombier 			handled++;
855*897ae9c1SDavid du Colombier 	}
856*897ae9c1SDavid du Colombier 
857*897ae9c1SDavid du Colombier 	if(ether->linkchg && (reg->ps1 & PS1an_done)) {
858*897ae9c1SDavid du Colombier 		handled++;
859*897ae9c1SDavid du Colombier 		ether->link = (reg->ps0 & PS0linkup) != 0;
860*897ae9c1SDavid du Colombier 		ether->linkchg = 0;
861*897ae9c1SDavid du Colombier 	}
862*897ae9c1SDavid du Colombier 	ctlr->newintrs++;
863*897ae9c1SDavid du Colombier 
864*897ae9c1SDavid du Colombier 	if (!handled) {
865*897ae9c1SDavid du Colombier 		irq  &= ~Isum;
866*897ae9c1SDavid du Colombier 		irqe &= ~IEtxbufferq(Qno);
867*897ae9c1SDavid du Colombier 		if (irq == 0 && irqe == 0) {
868*897ae9c1SDavid du Colombier 			/* seems to be triggered by continuous output */
869*897ae9c1SDavid du Colombier 			// iprint("ether1116: spurious interrupt\n");
870*897ae9c1SDavid du Colombier 		} else
871*897ae9c1SDavid du Colombier 			iprint("ether1116: interrupt cause unknown; "
872*897ae9c1SDavid du Colombier 				"irq %#lux irqe %#lux\n", irq, irqe);
873*897ae9c1SDavid du Colombier 	}
874*897ae9c1SDavid du Colombier 	intrclear(Irqlo, ether->irq);
875*897ae9c1SDavid du Colombier }
876*897ae9c1SDavid du Colombier 
877*897ae9c1SDavid du Colombier void
878*897ae9c1SDavid du Colombier promiscuous(void *arg, int on)
879*897ae9c1SDavid du Colombier {
880*897ae9c1SDavid du Colombier 	Ether *ether = arg;
881*897ae9c1SDavid du Colombier 	Ctlr *ctlr = ether->ctlr;
882*897ae9c1SDavid du Colombier 	Gbereg *reg = ctlr->reg;
883*897ae9c1SDavid du Colombier 
884*897ae9c1SDavid du Colombier 	ilock(ctlr);
885*897ae9c1SDavid du Colombier 	ether->prom = on;
886*897ae9c1SDavid du Colombier 	if(on)
887*897ae9c1SDavid du Colombier 		reg->portcfg |= PCFGupromisc;
888*897ae9c1SDavid du Colombier 	else
889*897ae9c1SDavid du Colombier 		reg->portcfg &= ~PCFGupromisc;
890*897ae9c1SDavid du Colombier 	iunlock(ctlr);
891*897ae9c1SDavid du Colombier }
892*897ae9c1SDavid du Colombier 
893*897ae9c1SDavid du Colombier void
894*897ae9c1SDavid du Colombier multicast(void *, uchar *, int)
895*897ae9c1SDavid du Colombier {
896*897ae9c1SDavid du Colombier 	/* nothing to do; we always accept multicast */
897*897ae9c1SDavid du Colombier }
898*897ae9c1SDavid du Colombier 
899*897ae9c1SDavid du Colombier static void quiesce(Gbereg *reg);
900*897ae9c1SDavid du Colombier 
901*897ae9c1SDavid du Colombier static void
902*897ae9c1SDavid du Colombier shutdown(Ether *ether)
903*897ae9c1SDavid du Colombier {
904*897ae9c1SDavid du Colombier 	Ctlr *ctlr = ether->ctlr;
905*897ae9c1SDavid du Colombier 	Gbereg *reg = ctlr->reg;
906*897ae9c1SDavid du Colombier 
907*897ae9c1SDavid du Colombier 	ilock(ctlr);
908*897ae9c1SDavid du Colombier 	quiesce(reg);
909*897ae9c1SDavid du Colombier 	reg->tcqdp[Qno]  = 0;
910*897ae9c1SDavid du Colombier 	reg->crdp[Qno].r = 0;
911*897ae9c1SDavid du Colombier 	reg->psc0 = 0;			/* no PSC0porton */
912*897ae9c1SDavid du Colombier 	reg->psc1 |= PSC1portreset;
913*897ae9c1SDavid du Colombier 	iunlock(ctlr);
914*897ae9c1SDavid du Colombier 	coherence();
915*897ae9c1SDavid du Colombier 	delay(100);
916*897ae9c1SDavid du Colombier }
917*897ae9c1SDavid du Colombier 
918*897ae9c1SDavid du Colombier enum {
919*897ae9c1SDavid du Colombier 	CMjumbo,
920*897ae9c1SDavid du Colombier };
921*897ae9c1SDavid du Colombier 
922*897ae9c1SDavid du Colombier static Cmdtab ctlmsg[] = {
923*897ae9c1SDavid du Colombier 	CMjumbo,	"jumbo",	2,
924*897ae9c1SDavid du Colombier };
925*897ae9c1SDavid du Colombier 
926*897ae9c1SDavid du Colombier long
927*897ae9c1SDavid du Colombier ctl(Ether *e, void *p, long n)
928*897ae9c1SDavid du Colombier {
929*897ae9c1SDavid du Colombier 	Cmdbuf *cb;
930*897ae9c1SDavid du Colombier 	Cmdtab *ct;
931*897ae9c1SDavid du Colombier 	Ctlr *ctlr = e->ctlr;
932*897ae9c1SDavid du Colombier 	Gbereg *reg = ctlr->reg;
933*897ae9c1SDavid du Colombier 
934*897ae9c1SDavid du Colombier 	cb = parsecmd(p, n);
935*897ae9c1SDavid du Colombier 	if(waserror()) {
936*897ae9c1SDavid du Colombier 		free(cb);
937*897ae9c1SDavid du Colombier 		nexterror();
938*897ae9c1SDavid du Colombier 	}
939*897ae9c1SDavid du Colombier 
940*897ae9c1SDavid du Colombier 	ct = lookupcmd(cb, ctlmsg, nelem(ctlmsg));
941*897ae9c1SDavid du Colombier 	switch(ct->index) {
942*897ae9c1SDavid du Colombier 	case CMjumbo:
943*897ae9c1SDavid du Colombier 		if(strcmp(cb->f[1], "on") == 0) {
944*897ae9c1SDavid du Colombier 			/* incoming packet queue doesn't expect jumbo frames */
945*897ae9c1SDavid du Colombier 			error("jumbo disabled");
946*897ae9c1SDavid du Colombier 			reg->psc0 = (reg->psc0 & ~PSC0mrumask) |
947*897ae9c1SDavid du Colombier 				PSC0mru(PSC0mru9022);
948*897ae9c1SDavid du Colombier 			e->maxmtu = 9022;
949*897ae9c1SDavid du Colombier 		} else if(strcmp(cb->f[1], "off") == 0) {
950*897ae9c1SDavid du Colombier 			reg->psc0 = (reg->psc0 & ~PSC0mrumask) |
951*897ae9c1SDavid du Colombier 				PSC0mru(PSC0mru1522);
952*897ae9c1SDavid du Colombier 			e->maxmtu = ETHERMAXTU;
953*897ae9c1SDavid du Colombier 		} else
954*897ae9c1SDavid du Colombier 			error(Ebadctl);
955*897ae9c1SDavid du Colombier 		break;
956*897ae9c1SDavid du Colombier 	default:
957*897ae9c1SDavid du Colombier 		error(Ebadctl);
958*897ae9c1SDavid du Colombier 		break;
959*897ae9c1SDavid du Colombier 	}
960*897ae9c1SDavid du Colombier 	free(cb);
961*897ae9c1SDavid du Colombier 	poperror();
962*897ae9c1SDavid du Colombier 	return n;
963*897ae9c1SDavid du Colombier }
964*897ae9c1SDavid du Colombier 
965*897ae9c1SDavid du Colombier /*
966*897ae9c1SDavid du Colombier  * phy/mii goo
967*897ae9c1SDavid du Colombier  */
968*897ae9c1SDavid du Colombier 
969*897ae9c1SDavid du Colombier static int
970*897ae9c1SDavid du Colombier smibusywait(Gbereg *reg, ulong waitbit)
971*897ae9c1SDavid du Colombier {
972*897ae9c1SDavid du Colombier 	ulong timeout, smi_reg;
973*897ae9c1SDavid du Colombier 
974*897ae9c1SDavid du Colombier 	timeout = PhysmiTimeout;
975*897ae9c1SDavid du Colombier 	/* wait till the SMI is not busy */
976*897ae9c1SDavid du Colombier 	do {
977*897ae9c1SDavid du Colombier 		/* read smi register */
978*897ae9c1SDavid du Colombier 		smi_reg = reg->smi;
979*897ae9c1SDavid du Colombier 		if (timeout-- == 0) {
980*897ae9c1SDavid du Colombier 			MIIDBG("SMI busy timeout\n");
981*897ae9c1SDavid du Colombier 			return -1;
982*897ae9c1SDavid du Colombier 		}
983*897ae9c1SDavid du Colombier //		delay(1);
984*897ae9c1SDavid du Colombier 	} while (smi_reg & waitbit);
985*897ae9c1SDavid du Colombier 	return 0;
986*897ae9c1SDavid du Colombier }
987*897ae9c1SDavid du Colombier 
988*897ae9c1SDavid du Colombier static int
989*897ae9c1SDavid du Colombier miird(Mii *mii, int pa, int ra)
990*897ae9c1SDavid du Colombier {
991*897ae9c1SDavid du Colombier 	ulong smi_reg, timeout;
992*897ae9c1SDavid du Colombier 	Ctlr *ctlr;
993*897ae9c1SDavid du Colombier 	Gbereg *reg;
994*897ae9c1SDavid du Colombier 
995*897ae9c1SDavid du Colombier 	ctlr = (Ctlr*)mii->ctlr;
996*897ae9c1SDavid du Colombier 	reg = ctlr->reg;
997*897ae9c1SDavid du Colombier 
998*897ae9c1SDavid du Colombier 	/* check params */
999*897ae9c1SDavid du Colombier 	if ((pa<<Physmiaddroff) & ~Physmiaddrmask ||
1000*897ae9c1SDavid du Colombier 	    (ra<<SmiRegaddroff) & ~SmiRegaddrmask)
1001*897ae9c1SDavid du Colombier 		return -1;
1002*897ae9c1SDavid du Colombier 
1003*897ae9c1SDavid du Colombier 	smibusywait(reg, PhysmiBusy);
1004*897ae9c1SDavid du Colombier 
1005*897ae9c1SDavid du Colombier 	/* fill the phy address and register offset and read opcode */
1006*897ae9c1SDavid du Colombier 	reg->smi = pa << Physmiaddroff | ra << SmiRegaddroff | PhysmiopRd;
1007*897ae9c1SDavid du Colombier 	coherence();
1008*897ae9c1SDavid du Colombier 
1009*897ae9c1SDavid du Colombier 	/* wait til read value is ready */
1010*897ae9c1SDavid du Colombier //	if (smibusywait(reg, PhysmiReadok) < 0)
1011*897ae9c1SDavid du Colombier //		return -1;
1012*897ae9c1SDavid du Colombier 	timeout = PhysmiTimeout;
1013*897ae9c1SDavid du Colombier 	do {
1014*897ae9c1SDavid du Colombier 		smi_reg = reg->smi;
1015*897ae9c1SDavid du Colombier 		if (timeout-- == 0) {
1016*897ae9c1SDavid du Colombier 			MIIDBG("SMI read-valid timeout\n");
1017*897ae9c1SDavid du Colombier 			return -1;
1018*897ae9c1SDavid du Colombier 		}
1019*897ae9c1SDavid du Colombier 	} while (!(smi_reg & PhysmiReadok));
1020*897ae9c1SDavid du Colombier 
1021*897ae9c1SDavid du Colombier 	/* Wait for the data to update in the SMI register */
1022*897ae9c1SDavid du Colombier 	for (timeout = 0; timeout < PhysmiTimeout; timeout++)
1023*897ae9c1SDavid du Colombier 		;
1024*897ae9c1SDavid du Colombier 	return reg->smi & Physmidatamask;
1025*897ae9c1SDavid du Colombier }
1026*897ae9c1SDavid du Colombier 
1027*897ae9c1SDavid du Colombier static int
1028*897ae9c1SDavid du Colombier miiwr(Mii *mii, int pa, int ra, int v)
1029*897ae9c1SDavid du Colombier {
1030*897ae9c1SDavid du Colombier 	Ctlr *ctlr;
1031*897ae9c1SDavid du Colombier 	Gbereg *reg;
1032*897ae9c1SDavid du Colombier 	ulong smi_reg;
1033*897ae9c1SDavid du Colombier 
1034*897ae9c1SDavid du Colombier 	ctlr = (Ctlr*)mii->ctlr;
1035*897ae9c1SDavid du Colombier 	reg = ctlr->reg;
1036*897ae9c1SDavid du Colombier 
1037*897ae9c1SDavid du Colombier 	/* check params */
1038*897ae9c1SDavid du Colombier 	if (((pa<<Physmiaddroff) & ~Physmiaddrmask) ||
1039*897ae9c1SDavid du Colombier 	    ((ra<<SmiRegaddroff) & ~SmiRegaddrmask))
1040*897ae9c1SDavid du Colombier 		return -1;
1041*897ae9c1SDavid du Colombier 
1042*897ae9c1SDavid du Colombier 	smibusywait(reg, PhysmiBusy);
1043*897ae9c1SDavid du Colombier 
1044*897ae9c1SDavid du Colombier 	/* fill the phy address and register offset and read opcode */
1045*897ae9c1SDavid du Colombier 	smi_reg = v << Physmidataoff | pa << Physmiaddroff | ra << SmiRegaddroff;
1046*897ae9c1SDavid du Colombier 	reg->smi = smi_reg & ~PhysmiopRd;
1047*897ae9c1SDavid du Colombier 	coherence();
1048*897ae9c1SDavid du Colombier 	return 0;
1049*897ae9c1SDavid du Colombier }
1050*897ae9c1SDavid du Colombier 
1051*897ae9c1SDavid du Colombier static int
1052*897ae9c1SDavid du Colombier kirkwoodmii(Ether *ether)
1053*897ae9c1SDavid du Colombier {
1054*897ae9c1SDavid du Colombier 	int i;
1055*897ae9c1SDavid du Colombier 	Ctlr *ctlr;
1056*897ae9c1SDavid du Colombier 	MiiPhy *phy;
1057*897ae9c1SDavid du Colombier 
1058*897ae9c1SDavid du Colombier 	MIIDBG("mii\n");
1059*897ae9c1SDavid du Colombier 	ctlr = ether->ctlr;
1060*897ae9c1SDavid du Colombier 	if((ctlr->mii = malloc(sizeof(Mii))) == nil)
1061*897ae9c1SDavid du Colombier 		return -1;
1062*897ae9c1SDavid du Colombier 	ctlr->mii->ctlr = ctlr;
1063*897ae9c1SDavid du Colombier 	ctlr->mii->mir = miird;
1064*897ae9c1SDavid du Colombier 	ctlr->mii->miw = miiwr;
1065*897ae9c1SDavid du Colombier 
1066*897ae9c1SDavid du Colombier 	if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){
1067*897ae9c1SDavid du Colombier 		print("#l%d: ether1116: init mii failure\n", ether->ctlrno);
1068*897ae9c1SDavid du Colombier 		free(ctlr->mii);
1069*897ae9c1SDavid du Colombier 		ctlr->mii = nil;
1070*897ae9c1SDavid du Colombier 		return -1;
1071*897ae9c1SDavid du Colombier 	}
1072*897ae9c1SDavid du Colombier 
1073*897ae9c1SDavid du Colombier 	/* oui 005043 is marvell */
1074*897ae9c1SDavid du Colombier 	MIIDBG("oui %#X phyno %d\n", phy->oui, phy->phyno);
1075*897ae9c1SDavid du Colombier 	if(miistatus(ctlr->mii) < 0){
1076*897ae9c1SDavid du Colombier 		miireset(ctlr->mii);
1077*897ae9c1SDavid du Colombier 		MIIDBG("miireset\n");
1078*897ae9c1SDavid du Colombier 		if(miiane(ctlr->mii, ~0, 0, ~0) < 0){
1079*897ae9c1SDavid du Colombier 			iprint("miiane failed\n");
1080*897ae9c1SDavid du Colombier 			return -1;
1081*897ae9c1SDavid du Colombier 		}
1082*897ae9c1SDavid du Colombier 		MIIDBG("miistatus\n");
1083*897ae9c1SDavid du Colombier 		miistatus(ctlr->mii);
1084*897ae9c1SDavid du Colombier 		if(miird(ctlr->mii, phy->phyno, Bmsr) & BmsrLs){
1085*897ae9c1SDavid du Colombier 			for(i = 0; ; i++){
1086*897ae9c1SDavid du Colombier 				if(i > 600){
1087*897ae9c1SDavid du Colombier 					iprint("ether1116: autonegotiation failed\n");
1088*897ae9c1SDavid du Colombier 					break;
1089*897ae9c1SDavid du Colombier 				}
1090*897ae9c1SDavid du Colombier 				if(miird(ctlr->mii, phy->phyno, Bmsr) & BmsrAnc)
1091*897ae9c1SDavid du Colombier 					break;
1092*897ae9c1SDavid du Colombier 				delay(10);
1093*897ae9c1SDavid du Colombier 			}
1094*897ae9c1SDavid du Colombier 			if(miistatus(ctlr->mii) < 0)
1095*897ae9c1SDavid du Colombier 				iprint("miistatus failed\n");
1096*897ae9c1SDavid du Colombier 		}else{
1097*897ae9c1SDavid du Colombier 			iprint("ether1116: no link\n");
1098*897ae9c1SDavid du Colombier 			phy->speed = 10;	/* simple default */
1099*897ae9c1SDavid du Colombier 		}
1100*897ae9c1SDavid du Colombier 	}
1101*897ae9c1SDavid du Colombier 
1102*897ae9c1SDavid du Colombier 	ether->mbps = phy->speed;
1103*897ae9c1SDavid du Colombier //	iprint("#l%d: kirkwoodmii: fd %d speed %d tfc %d rfc %d\n",
1104*897ae9c1SDavid du Colombier //		ctlr->port, phy->fd, phy->speed, phy->tfc, phy->rfc);
1105*897ae9c1SDavid du Colombier 	MIIDBG("mii done\n");
1106*897ae9c1SDavid du Colombier 	return 0;
1107*897ae9c1SDavid du Colombier }
1108*897ae9c1SDavid du Colombier 
1109*897ae9c1SDavid du Colombier enum {						/* PHY register pages */
1110*897ae9c1SDavid du Colombier 	Pagcopper,
1111*897ae9c1SDavid du Colombier 	Pagfiber,
1112*897ae9c1SDavid du Colombier 	Pagrgmii,
1113*897ae9c1SDavid du Colombier 	Pagled,
1114*897ae9c1SDavid du Colombier 	Pagrsvd1,
1115*897ae9c1SDavid du Colombier 	Pagvct,
1116*897ae9c1SDavid du Colombier 	Pagtest,
1117*897ae9c1SDavid du Colombier 	Pagrsvd2,
1118*897ae9c1SDavid du Colombier 	Pagfactest,
1119*897ae9c1SDavid du Colombier };
1120*897ae9c1SDavid du Colombier 
1121*897ae9c1SDavid du Colombier static void
1122*897ae9c1SDavid du Colombier miiregpage(Mii *mii, ulong dev, ulong page)
1123*897ae9c1SDavid du Colombier {
1124*897ae9c1SDavid du Colombier 	miiwr(mii, dev, Eadr, page);
1125*897ae9c1SDavid du Colombier }
1126*897ae9c1SDavid du Colombier 
1127*897ae9c1SDavid du Colombier static int
1128*897ae9c1SDavid du Colombier miiphyinit(Mii *mii)
1129*897ae9c1SDavid du Colombier {
1130*897ae9c1SDavid du Colombier 	ulong dev;
1131*897ae9c1SDavid du Colombier 	Ctlr *ctlr;
1132*897ae9c1SDavid du Colombier 	Gbereg *reg;
1133*897ae9c1SDavid du Colombier 
1134*897ae9c1SDavid du Colombier 	ctlr = (Ctlr*)mii->ctlr;
1135*897ae9c1SDavid du Colombier 	reg = ctlr->reg;
1136*897ae9c1SDavid du Colombier 	dev = reg->phy;
1137*897ae9c1SDavid du Colombier 	MIIDBG("phy dev addr %lux\n", dev);
1138*897ae9c1SDavid du Colombier 
1139*897ae9c1SDavid du Colombier 	/* leds link & activity */
1140*897ae9c1SDavid du Colombier 	miiregpage(mii, dev, Pagled);
1141*897ae9c1SDavid du Colombier 	/* low 4 bits == 1: on - link, blink - activity, off - no link */
1142*897ae9c1SDavid du Colombier 	miiwr(mii, dev, Scr, (miird(mii, dev, Scr) & ~0xf) | 1);
1143*897ae9c1SDavid du Colombier 
1144*897ae9c1SDavid du Colombier 	miiregpage(mii, dev, Pagrgmii);
1145*897ae9c1SDavid du Colombier 	miiwr(mii, dev, Scr, miird(mii, dev, Scr) | Rgmiipwrup);
1146*897ae9c1SDavid du Colombier 	/* TODO must now do a software reset, sez the manual */
1147*897ae9c1SDavid du Colombier 
1148*897ae9c1SDavid du Colombier 	/* enable RGMII delay on Tx and Rx for CPU port */
1149*897ae9c1SDavid du Colombier 	miiwr(mii, dev, Recr, miird(mii, dev, Recr) | Rxtiming | Rxtiming);
1150*897ae9c1SDavid du Colombier 
1151*897ae9c1SDavid du Colombier 	miiregpage(mii, dev, Pagcopper);
1152*897ae9c1SDavid du Colombier 	miiwr(mii, dev, Scr,
1153*897ae9c1SDavid du Colombier 		(miird(mii, dev, Scr) & ~(Pwrdown|Endetect)) | Mdix);
1154*897ae9c1SDavid du Colombier 
1155*897ae9c1SDavid du Colombier 	return 0;
1156*897ae9c1SDavid du Colombier }
1157*897ae9c1SDavid du Colombier 
1158*897ae9c1SDavid du Colombier /*
1159*897ae9c1SDavid du Colombier  * initialisation
1160*897ae9c1SDavid du Colombier  */
1161*897ae9c1SDavid du Colombier 
1162*897ae9c1SDavid du Colombier static void
1163*897ae9c1SDavid du Colombier quiesce(Gbereg *reg)
1164*897ae9c1SDavid du Colombier {
1165*897ae9c1SDavid du Colombier 	ulong v;
1166*897ae9c1SDavid du Colombier 
1167*897ae9c1SDavid du Colombier 	v = reg->tqc;
1168*897ae9c1SDavid du Colombier 	if (v & 0xFF)
1169*897ae9c1SDavid du Colombier 		reg->tqc = v << 8;		/* stop active channels */
1170*897ae9c1SDavid du Colombier 	v = reg->rqc;
1171*897ae9c1SDavid du Colombier 	if (v & 0xFF)
1172*897ae9c1SDavid du Colombier 		reg->rqc = v << 8;		/* stop active channels */
1173*897ae9c1SDavid du Colombier 	/* wait for all queues to stop */
1174*897ae9c1SDavid du Colombier 	while (reg->tqc & 0xFF || reg->rqc & 0xFF)
1175*897ae9c1SDavid du Colombier 		;
1176*897ae9c1SDavid du Colombier }
1177*897ae9c1SDavid du Colombier 
1178*897ae9c1SDavid du Colombier static void
1179*897ae9c1SDavid du Colombier portreset(Gbereg *reg)
1180*897ae9c1SDavid du Colombier {
1181*897ae9c1SDavid du Colombier 	ulong i;
1182*897ae9c1SDavid du Colombier 
1183*897ae9c1SDavid du Colombier 	quiesce(reg);
1184*897ae9c1SDavid du Colombier 	reg->psc0 &= ~PSC0porton;		/* disable port */
1185*897ae9c1SDavid du Colombier 	reg->psc1 &= ~(PSC1rgmii|PSC1portreset); /* set port & MII active */
1186*897ae9c1SDavid du Colombier 	coherence();
1187*897ae9c1SDavid du Colombier 	for (i = 0; i < 4000; i++)		/* magic delay */
1188*897ae9c1SDavid du Colombier 		;
1189*897ae9c1SDavid du Colombier }
1190*897ae9c1SDavid du Colombier 
1191*897ae9c1SDavid du Colombier static void
1192*897ae9c1SDavid du Colombier p16(uchar *p, ulong v)
1193*897ae9c1SDavid du Colombier {
1194*897ae9c1SDavid du Colombier 	*p++ = v>>8;
1195*897ae9c1SDavid du Colombier 	*p   = v;
1196*897ae9c1SDavid du Colombier }
1197*897ae9c1SDavid du Colombier 
1198*897ae9c1SDavid du Colombier static void
1199*897ae9c1SDavid du Colombier p32(uchar *p, ulong v)
1200*897ae9c1SDavid du Colombier {
1201*897ae9c1SDavid du Colombier 	*p++ = v>>24;
1202*897ae9c1SDavid du Colombier 	*p++ = v>>16;
1203*897ae9c1SDavid du Colombier 	*p++ = v>>8;
1204*897ae9c1SDavid du Colombier 	*p   = v;
1205*897ae9c1SDavid du Colombier }
1206*897ae9c1SDavid du Colombier 
1207*897ae9c1SDavid du Colombier /*
1208*897ae9c1SDavid du Colombier  * set ether->ea from hw mac address,
1209*897ae9c1SDavid du Colombier  * configure unicast filtering to accept it.
1210*897ae9c1SDavid du Colombier  */
1211*897ae9c1SDavid du Colombier void
1212*897ae9c1SDavid du Colombier archetheraddr(Ether *ether, Gbereg *reg, int rxqno)
1213*897ae9c1SDavid du Colombier {
1214*897ae9c1SDavid du Colombier 	ulong nibble, ucreg, tbloff, regoff;
1215*897ae9c1SDavid du Colombier 
1216*897ae9c1SDavid du Colombier 	p32(ether->ea,   reg->macah);
1217*897ae9c1SDavid du Colombier 	p16(ether->ea+4, reg->macal);
1218*897ae9c1SDavid du Colombier 
1219*897ae9c1SDavid du Colombier 	/* accept frames on ea */
1220*897ae9c1SDavid du Colombier 	nibble = ether->ea[5] & 0xf;
1221*897ae9c1SDavid du Colombier 	tbloff = nibble / 4;
1222*897ae9c1SDavid du Colombier 	regoff = nibble % 4;
1223*897ae9c1SDavid du Colombier 
1224*897ae9c1SDavid du Colombier 	regoff *= 8;
1225*897ae9c1SDavid du Colombier 	ucreg = reg->dfut[tbloff];
1226*897ae9c1SDavid du Colombier 	ucreg &= 0xff << regoff;
1227*897ae9c1SDavid du Colombier 	ucreg |= (rxqno << 1 | Pass) << regoff;
1228*897ae9c1SDavid du Colombier 	reg->dfut[tbloff] = ucreg;
1229*897ae9c1SDavid du Colombier 
1230*897ae9c1SDavid du Colombier 	/* accept all multicast too.  set up special & other tables. */
1231*897ae9c1SDavid du Colombier 	memset(reg->dfsmt, Qno<<1 | Pass, sizeof reg->dfsmt);
1232*897ae9c1SDavid du Colombier 	memset(reg->dfomt, Qno<<1 | Pass, sizeof reg->dfomt);
1233*897ae9c1SDavid du Colombier 	coherence();
1234*897ae9c1SDavid du Colombier }
1235*897ae9c1SDavid du Colombier 
1236*897ae9c1SDavid du Colombier static void
1237*897ae9c1SDavid du Colombier ctlrinit(Ether *ether)
1238*897ae9c1SDavid du Colombier {
1239*897ae9c1SDavid du Colombier 	int i;
1240*897ae9c1SDavid du Colombier 	Block *b;
1241*897ae9c1SDavid du Colombier 	Ctlr *ctlr = ether->ctlr;
1242*897ae9c1SDavid du Colombier 	Gbereg *reg = ctlr->reg;
1243*897ae9c1SDavid du Colombier 	Rx *r;
1244*897ae9c1SDavid du Colombier 	Tx *t;
1245*897ae9c1SDavid du Colombier 	static char name[KNAMELEN];
1246*897ae9c1SDavid du Colombier 	static Ctlr fakectlr;		/* bigger than 4K; keep off the stack */
1247*897ae9c1SDavid du Colombier 
1248*897ae9c1SDavid du Colombier 	ilock(&freeblocks);
1249*897ae9c1SDavid du Colombier 	for(i = 0; i < Nrxblks; i++) {
1250*897ae9c1SDavid du Colombier 		b = iallocb(Rxblklen+Bufalign-1);
1251*897ae9c1SDavid du Colombier 		if(b == nil) {
1252*897ae9c1SDavid du Colombier 			iprint("ether1116: no memory for rx buffers\n");
1253*897ae9c1SDavid du Colombier 			break;
1254*897ae9c1SDavid du Colombier 		}
1255*897ae9c1SDavid du Colombier 		assert(b->ref == 1);
1256*897ae9c1SDavid du Colombier 		b->wp = b->rp = (uchar*)
1257*897ae9c1SDavid du Colombier 			((uintptr)(b->lim - Rxblklen) & ~(Bufalign - 1));
1258*897ae9c1SDavid du Colombier 		assert(((uintptr)b->rp & (Bufalign - 1)) == 0);
1259*897ae9c1SDavid du Colombier 		b->free = rxfreeb;
1260*897ae9c1SDavid du Colombier 		b->next = freeblocks.head;
1261*897ae9c1SDavid du Colombier 		freeblocks.head = b;
1262*897ae9c1SDavid du Colombier 	}
1263*897ae9c1SDavid du Colombier 	iunlock(&freeblocks);
1264*897ae9c1SDavid du Colombier 
1265*897ae9c1SDavid du Colombier 	ctlr->rx = xspanalloc(Nrx * sizeof(Rx), Descralign, 0);
1266*897ae9c1SDavid du Colombier 	if(ctlr->rx == nil)
1267*897ae9c1SDavid du Colombier 		panic("ether1116: no memory for rx ring");
1268*897ae9c1SDavid du Colombier 	for(i = 0; i < Nrx; i++) {
1269*897ae9c1SDavid du Colombier 		r = &ctlr->rx[i];
1270*897ae9c1SDavid du Colombier 		assert(((uintptr)r & (Descralign - 1)) == 0);
1271*897ae9c1SDavid du Colombier 		r->cs = 0;	/* not owned by hardware until r->buf is set */
1272*897ae9c1SDavid du Colombier 		r->buf = 0;
1273*897ae9c1SDavid du Colombier 		r->next = PADDR(&ctlr->rx[NEXT(i, Nrx)]);
1274*897ae9c1SDavid du Colombier 		ctlr->rxb[i] = nil;
1275*897ae9c1SDavid du Colombier 	}
1276*897ae9c1SDavid du Colombier 	ctlr->rxtail = ctlr->rxhead = 0;
1277*897ae9c1SDavid du Colombier 	cachedwb();
1278*897ae9c1SDavid du Colombier 	l2cacheuwb();
1279*897ae9c1SDavid du Colombier 	rxreplenish(ctlr);
1280*897ae9c1SDavid du Colombier 
1281*897ae9c1SDavid du Colombier 	ctlr->tx = xspanalloc(Ntx * sizeof(Tx), Descralign, 0);
1282*897ae9c1SDavid du Colombier 	if(ctlr->tx == nil)
1283*897ae9c1SDavid du Colombier 		panic("ether1116: no memory for tx ring");
1284*897ae9c1SDavid du Colombier 	for(i = 0; i < Ntx; i++) {
1285*897ae9c1SDavid du Colombier 		t = &ctlr->tx[i];
1286*897ae9c1SDavid du Colombier 		assert(((uintptr)t & (Descralign - 1)) == 0);
1287*897ae9c1SDavid du Colombier 		t->cs = 0;
1288*897ae9c1SDavid du Colombier 		t->buf = 0;
1289*897ae9c1SDavid du Colombier 		t->next = PADDR(&ctlr->tx[NEXT(i, Ntx)]);
1290*897ae9c1SDavid du Colombier 		ctlr->txb[i] = nil;
1291*897ae9c1SDavid du Colombier 	}
1292*897ae9c1SDavid du Colombier 	ctlr->txtail = ctlr->txhead = 0;
1293*897ae9c1SDavid du Colombier 	cachedwb();
1294*897ae9c1SDavid du Colombier 	l2cacheuwb();
1295*897ae9c1SDavid du Colombier 
1296*897ae9c1SDavid du Colombier 	/* clear stats by reading them into fake ctlr */
1297*897ae9c1SDavid du Colombier 	getmibstats(&fakectlr);
1298*897ae9c1SDavid du Colombier 
1299*897ae9c1SDavid du Colombier 	reg->pxmfs = MFS64by;
1300*897ae9c1SDavid du Colombier 
1301*897ae9c1SDavid du Colombier 	/*
1302*897ae9c1SDavid du Colombier 	 * ipg's (inter packet gaps) for interrupt coalescing,
1303*897ae9c1SDavid du Colombier 	 * values in units of 64 clock cycles.  A full-sized
1304*897ae9c1SDavid du Colombier 	 * packet (1514 bytes) takes just over 12µs to transmit.
1305*897ae9c1SDavid du Colombier 	 */
1306*897ae9c1SDavid du Colombier 	if (CLOCKFREQ/(Maxrxintrsec*64) >= (1<<16))
1307*897ae9c1SDavid du Colombier 		panic("rx coalescing value %d too big for short",
1308*897ae9c1SDavid du Colombier 			CLOCKFREQ/(Maxrxintrsec*64));
1309*897ae9c1SDavid du Colombier 	reg->sdc = SDCrifb | SDCrxburst(Burst16) | SDCtxburst(Burst16) |
1310*897ae9c1SDavid du Colombier 		SDCrxnobyteswap | SDCtxnobyteswap |
1311*897ae9c1SDavid du Colombier 		SDCipgintrx(CLOCKFREQ/(Maxrxintrsec*64));
1312*897ae9c1SDavid du Colombier 	reg->pxtfut = 0;	/* TFUTipginttx(CLOCKFREQ/(Maxrxintrsec*64)) */
1313*897ae9c1SDavid du Colombier 
1314*897ae9c1SDavid du Colombier 	/* allow just these interrupts */
1315*897ae9c1SDavid du Colombier 	/* no Irxerr interrupts since the guru plug generates them continually */
1316*897ae9c1SDavid du Colombier //	reg->irqmask = Irxbufferq(Qno) | Irxerr | Itxendq(Qno);
1317*897ae9c1SDavid du Colombier 	reg->irqmask = Irxbufferq(Qno) | Itxendq(Qno);
1318*897ae9c1SDavid du Colombier 	reg->irqemask = IEtxerrq(Qno) | IEphystschg | IErxoverrun | IEtxunderrun;
1319*897ae9c1SDavid du Colombier 
1320*897ae9c1SDavid du Colombier 	reg->irq = 0;
1321*897ae9c1SDavid du Colombier 	reg->irqe = 0;
1322*897ae9c1SDavid du Colombier 	reg->euirqmask = 0;
1323*897ae9c1SDavid du Colombier 	reg->euirq = 0;
1324*897ae9c1SDavid du Colombier 
1325*897ae9c1SDavid du Colombier //	archetheraddr(ether, ctlr->reg, Qno);	/* 2nd location */
1326*897ae9c1SDavid du Colombier 
1327*897ae9c1SDavid du Colombier 	reg->tcqdp[Qno]  = PADDR(&ctlr->tx[ctlr->txhead]);
1328*897ae9c1SDavid du Colombier 	for (i = 1; i < nelem(reg->tcqdp); i++)
1329*897ae9c1SDavid du Colombier 		reg->tcqdp[i] = 0;
1330*897ae9c1SDavid du Colombier 	reg->crdp[Qno].r = PADDR(&ctlr->rx[ctlr->rxhead]);
1331*897ae9c1SDavid du Colombier 	for (i = 1; i < nelem(reg->crdp); i++)
1332*897ae9c1SDavid du Colombier 		reg->crdp[i].r = 0;
1333*897ae9c1SDavid du Colombier 	coherence();
1334*897ae9c1SDavid du Colombier 
1335*897ae9c1SDavid du Colombier 	reg->portcfg = Rxqdefault(Qno) | Rxqarp(Qno);
1336*897ae9c1SDavid du Colombier 	reg->portcfgx = 0;
1337*897ae9c1SDavid du Colombier 
1338*897ae9c1SDavid du Colombier 	reg->psc1 = PSC1rgmii | PSC1encolonbp | PSC1coldomlim(0x23);
1339*897ae9c1SDavid du Colombier 	reg->psc0 = PSC0porton | PSC0an_flctloff |
1340*897ae9c1SDavid du Colombier 		PSC0an_pauseadv | PSC0nofrclinkdown | PSC0mru(PSC0mru1522);
1341*897ae9c1SDavid du Colombier 
1342*897ae9c1SDavid du Colombier 	ether->link = (reg->ps0 & PS0linkup) != 0;
1343*897ae9c1SDavid du Colombier 
1344*897ae9c1SDavid du Colombier 	/* set ethernet MTU for leaky bucket mechanism to 0 (disabled) */
1345*897ae9c1SDavid du Colombier 	reg->pmtu = 0;
1346*897ae9c1SDavid du Colombier 
1347*897ae9c1SDavid du Colombier 	reg->rqc = Rxqon(Qno);
1348*897ae9c1SDavid du Colombier 	coherence();
1349*897ae9c1SDavid du Colombier 	etheractive(ether);
1350*897ae9c1SDavid du Colombier 
1351*897ae9c1SDavid du Colombier 	snprint(name, sizeof name, "#l%drproc", ether->ctlrno);
1352*897ae9c1SDavid du Colombier 	kproc(name, rcvproc, ether);
1353*897ae9c1SDavid du Colombier }
1354*897ae9c1SDavid du Colombier 
1355*897ae9c1SDavid du Colombier static void
1356*897ae9c1SDavid du Colombier attach(Ether* ether)
1357*897ae9c1SDavid du Colombier {
1358*897ae9c1SDavid du Colombier 	Ctlr *ctlr = ether->ctlr;
1359*897ae9c1SDavid du Colombier 
1360*897ae9c1SDavid du Colombier 	lock(&ctlr->initlock);
1361*897ae9c1SDavid du Colombier 	if(ctlr->init == 0) {
1362*897ae9c1SDavid du Colombier 		ctlrinit(ether);
1363*897ae9c1SDavid du Colombier 		ctlr->init = 1;
1364*897ae9c1SDavid du Colombier 	}
1365*897ae9c1SDavid du Colombier 	unlock(&ctlr->initlock);
1366*897ae9c1SDavid du Colombier }
1367*897ae9c1SDavid du Colombier 
1368*897ae9c1SDavid du Colombier /*
1369*897ae9c1SDavid du Colombier  * statistics goo.
1370*897ae9c1SDavid du Colombier  * mib registers clear on read.
1371*897ae9c1SDavid du Colombier  */
1372*897ae9c1SDavid du Colombier 
1373*897ae9c1SDavid du Colombier static void
1374*897ae9c1SDavid du Colombier getmibstats(Ctlr *ctlr)
1375*897ae9c1SDavid du Colombier {
1376*897ae9c1SDavid du Colombier 	Gbereg *reg = ctlr->reg;
1377*897ae9c1SDavid du Colombier 
1378*897ae9c1SDavid du Colombier 	/*
1379*897ae9c1SDavid du Colombier 	 * Marvell 88f6281 errata FE-ETH-120: high long of rxby and txby
1380*897ae9c1SDavid du Colombier 	 * can't be read correctly, so read the low long frequently
1381*897ae9c1SDavid du Colombier 	 * (every 30 seconds or less), thus avoiding overflow into high long.
1382*897ae9c1SDavid du Colombier 	 */
1383*897ae9c1SDavid du Colombier 	ctlr->rxby	+= reg->rxbylo;
1384*897ae9c1SDavid du Colombier 	ctlr->txby	+= reg->txbylo;
1385*897ae9c1SDavid du Colombier 
1386*897ae9c1SDavid du Colombier 	ctlr->badrxby	+= reg->badrxby;
1387*897ae9c1SDavid du Colombier 	ctlr->mactxerr	+= reg->mactxerr;
1388*897ae9c1SDavid du Colombier 	ctlr->rxpkt	+= reg->rxpkt;
1389*897ae9c1SDavid du Colombier 	ctlr->badrxpkt	+= reg->badrxpkt;
1390*897ae9c1SDavid du Colombier 	ctlr->rxbcastpkt+= reg->rxbcastpkt;
1391*897ae9c1SDavid du Colombier 	ctlr->rxmcastpkt+= reg->rxmcastpkt;
1392*897ae9c1SDavid du Colombier 	ctlr->rx64	+= reg->rx64;
1393*897ae9c1SDavid du Colombier 	ctlr->rx65_127	+= reg->rx65_127;
1394*897ae9c1SDavid du Colombier 	ctlr->rx128_255	+= reg->rx128_255;
1395*897ae9c1SDavid du Colombier 	ctlr->rx256_511	+= reg->rx256_511;
1396*897ae9c1SDavid du Colombier 	ctlr->rx512_1023+= reg->rx512_1023;
1397*897ae9c1SDavid du Colombier 	ctlr->rx1024_max+= reg->rx1024_max;
1398*897ae9c1SDavid du Colombier 	ctlr->txpkt	+= reg->txpkt;
1399*897ae9c1SDavid du Colombier 	ctlr->txcollpktdrop+= reg->txcollpktdrop;
1400*897ae9c1SDavid du Colombier 	ctlr->txmcastpkt+= reg->txmcastpkt;
1401*897ae9c1SDavid du Colombier 	ctlr->txbcastpkt+= reg->txbcastpkt;
1402*897ae9c1SDavid du Colombier 	ctlr->badmacctlpkts+= reg->badmacctlpkts;
1403*897ae9c1SDavid du Colombier 	ctlr->txflctl	+= reg->txflctl;
1404*897ae9c1SDavid du Colombier 	ctlr->rxflctl	+= reg->rxflctl;
1405*897ae9c1SDavid du Colombier 	ctlr->badrxflctl+= reg->badrxflctl;
1406*897ae9c1SDavid du Colombier 	ctlr->rxundersized+= reg->rxundersized;
1407*897ae9c1SDavid du Colombier 	ctlr->rxfrags	+= reg->rxfrags;
1408*897ae9c1SDavid du Colombier 	ctlr->rxtoobig	+= reg->rxtoobig;
1409*897ae9c1SDavid du Colombier 	ctlr->rxjabber	+= reg->rxjabber;
1410*897ae9c1SDavid du Colombier 	ctlr->rxerr	+= reg->rxerr;
1411*897ae9c1SDavid du Colombier 	ctlr->crcerr	+= reg->crcerr;
1412*897ae9c1SDavid du Colombier 	ctlr->collisions+= reg->collisions;
1413*897ae9c1SDavid du Colombier 	ctlr->latecoll	+= reg->latecoll;
1414*897ae9c1SDavid du Colombier }
1415*897ae9c1SDavid du Colombier 
1416*897ae9c1SDavid du Colombier long
1417*897ae9c1SDavid du Colombier ifstat(Ether *ether, void *a, long n, ulong off)
1418*897ae9c1SDavid du Colombier {
1419*897ae9c1SDavid du Colombier 	Ctlr *ctlr = ether->ctlr;
1420*897ae9c1SDavid du Colombier 	Gbereg *reg = ctlr->reg;
1421*897ae9c1SDavid du Colombier 	char *buf, *p, *e;
1422*897ae9c1SDavid du Colombier 
1423*897ae9c1SDavid du Colombier 	buf = p = malloc(READSTR);
1424*897ae9c1SDavid du Colombier 	e = p + READSTR;
1425*897ae9c1SDavid du Colombier 
1426*897ae9c1SDavid du Colombier 	ilock(ctlr);
1427*897ae9c1SDavid du Colombier 	getmibstats(ctlr);
1428*897ae9c1SDavid du Colombier 
1429*897ae9c1SDavid du Colombier 	ctlr->intrs += ctlr->newintrs;
1430*897ae9c1SDavid du Colombier 	p = seprint(p, e, "interrupts: %lud\n", ctlr->intrs);
1431*897ae9c1SDavid du Colombier 	p = seprint(p, e, "new interrupts: %lud\n", ctlr->newintrs);
1432*897ae9c1SDavid du Colombier 	ctlr->newintrs = 0;
1433*897ae9c1SDavid du Colombier 	p = seprint(p, e, "tx underrun: %lud\n", ctlr->txunderrun);
1434*897ae9c1SDavid du Colombier 	p = seprint(p, e, "tx ring full: %lud\n", ctlr->txringfull);
1435*897ae9c1SDavid du Colombier 
1436*897ae9c1SDavid du Colombier 	ctlr->rxdiscard += reg->pxdfc;
1437*897ae9c1SDavid du Colombier 	ctlr->rxoverrun += reg->pxofc;
1438*897ae9c1SDavid du Colombier 	p = seprint(p, e, "rx discarded frames: %lud\n", ctlr->rxdiscard);
1439*897ae9c1SDavid du Colombier 	p = seprint(p, e, "rx overrun frames: %lud\n", ctlr->rxoverrun);
1440*897ae9c1SDavid du Colombier 	p = seprint(p, e, "no first+last flag: %lud\n", ctlr->nofirstlast);
1441*897ae9c1SDavid du Colombier 
1442*897ae9c1SDavid du Colombier 	p = seprint(p, e, "duplex: %s\n", (reg->ps0 & PS0fd)? "full": "half");
1443*897ae9c1SDavid du Colombier 	p = seprint(p, e, "flow control: %s\n", (reg->ps0 & PS0flctl)? "on": "off");
1444*897ae9c1SDavid du Colombier 	/* p = seprint(p, e, "speed: %d mbps\n", ); */
1445*897ae9c1SDavid du Colombier 
1446*897ae9c1SDavid du Colombier 	p = seprint(p, e, "received bytes: %llud\n", ctlr->rxby);
1447*897ae9c1SDavid du Colombier 	p = seprint(p, e, "bad received bytes: %lud\n", ctlr->badrxby);
1448*897ae9c1SDavid du Colombier 	p = seprint(p, e, "internal mac transmit errors: %lud\n", ctlr->mactxerr);
1449*897ae9c1SDavid du Colombier 	p = seprint(p, e, "total received frames: %lud\n", ctlr->rxpkt);
1450*897ae9c1SDavid du Colombier 	p = seprint(p, e, "received broadcast frames: %lud\n", ctlr->rxbcastpkt);
1451*897ae9c1SDavid du Colombier 	p = seprint(p, e, "received multicast frames: %lud\n", ctlr->rxmcastpkt);
1452*897ae9c1SDavid du Colombier 	p = seprint(p, e, "bad received frames: %lud\n", ctlr->badrxpkt);
1453*897ae9c1SDavid du Colombier 	p = seprint(p, e, "received frames 0-64: %lud\n", ctlr->rx64);
1454*897ae9c1SDavid du Colombier 	p = seprint(p, e, "received frames 65-127: %lud\n", ctlr->rx65_127);
1455*897ae9c1SDavid du Colombier 	p = seprint(p, e, "received frames 128-255: %lud\n", ctlr->rx128_255);
1456*897ae9c1SDavid du Colombier 	p = seprint(p, e, "received frames 256-511: %lud\n", ctlr->rx256_511);
1457*897ae9c1SDavid du Colombier 	p = seprint(p, e, "received frames 512-1023: %lud\n", ctlr->rx512_1023);
1458*897ae9c1SDavid du Colombier 	p = seprint(p, e, "received frames 1024-max: %lud\n", ctlr->rx1024_max);
1459*897ae9c1SDavid du Colombier 	p = seprint(p, e, "transmitted bytes: %llud\n", ctlr->txby);
1460*897ae9c1SDavid du Colombier 	p = seprint(p, e, "total transmitted frames: %lud\n", ctlr->txpkt);
1461*897ae9c1SDavid du Colombier 	p = seprint(p, e, "transmitted broadcast frames: %lud\n", ctlr->txbcastpkt);
1462*897ae9c1SDavid du Colombier 	p = seprint(p, e, "transmitted multicast frames: %lud\n", ctlr->txmcastpkt);
1463*897ae9c1SDavid du Colombier 	p = seprint(p, e, "transmit frames dropped by collision: %lud\n", ctlr->txcollpktdrop);
1464*897ae9c1SDavid du Colombier 	p = seprint(p, e, "misaligned buffers: %lud\n", ether->pktsmisaligned);
1465*897ae9c1SDavid du Colombier 
1466*897ae9c1SDavid du Colombier 	p = seprint(p, e, "bad mac control frames: %lud\n", ctlr->badmacctlpkts);
1467*897ae9c1SDavid du Colombier 	p = seprint(p, e, "transmitted flow control messages: %lud\n", ctlr->txflctl);
1468*897ae9c1SDavid du Colombier 	p = seprint(p, e, "received flow control messages: %lud\n", ctlr->rxflctl);
1469*897ae9c1SDavid du Colombier 	p = seprint(p, e, "bad received flow control messages: %lud\n", ctlr->badrxflctl);
1470*897ae9c1SDavid du Colombier 	p = seprint(p, e, "received undersized packets: %lud\n", ctlr->rxundersized);
1471*897ae9c1SDavid du Colombier 	p = seprint(p, e, "received fragments: %lud\n", ctlr->rxfrags);
1472*897ae9c1SDavid du Colombier 	p = seprint(p, e, "received oversized packets: %lud\n", ctlr->rxtoobig);
1473*897ae9c1SDavid du Colombier 	p = seprint(p, e, "received jabber packets: %lud\n", ctlr->rxjabber);
1474*897ae9c1SDavid du Colombier 	p = seprint(p, e, "mac receive errors: %lud\n", ctlr->rxerr);
1475*897ae9c1SDavid du Colombier 	p = seprint(p, e, "crc errors: %lud\n", ctlr->crcerr);
1476*897ae9c1SDavid du Colombier 	p = seprint(p, e, "collisions: %lud\n", ctlr->collisions);
1477*897ae9c1SDavid du Colombier 	p = seprint(p, e, "late collisions: %lud\n", ctlr->latecoll);
1478*897ae9c1SDavid du Colombier 	USED(p);
1479*897ae9c1SDavid du Colombier 	iunlock(ctlr);
1480*897ae9c1SDavid du Colombier 
1481*897ae9c1SDavid du Colombier 	n = readstr(off, a, n, buf);
1482*897ae9c1SDavid du Colombier 	free(buf);
1483*897ae9c1SDavid du Colombier 	return n;
1484*897ae9c1SDavid du Colombier }
1485*897ae9c1SDavid du Colombier 
1486*897ae9c1SDavid du Colombier 
1487*897ae9c1SDavid du Colombier static int
1488*897ae9c1SDavid du Colombier reset(Ether *ether)
1489*897ae9c1SDavid du Colombier {
1490*897ae9c1SDavid du Colombier 	Ctlr *ctlr;
1491*897ae9c1SDavid du Colombier 	static uchar zeroea[Eaddrlen];
1492*897ae9c1SDavid du Colombier 
1493*897ae9c1SDavid du Colombier 	ether->ctlr = ctlr = malloc(sizeof *ctlr);
1494*897ae9c1SDavid du Colombier 	switch(ether->ctlrno) {
1495*897ae9c1SDavid du Colombier 	case 0:
1496*897ae9c1SDavid du Colombier 		ctlr->reg = (Gbereg*)Gbe0regs;
1497*897ae9c1SDavid du Colombier 		ether->irq = IRQ0gbe0sum;
1498*897ae9c1SDavid du Colombier 		break;
1499*897ae9c1SDavid du Colombier 	case 1:
1500*897ae9c1SDavid du Colombier 		ctlr->reg = (Gbereg*)Gbe1regs;
1501*897ae9c1SDavid du Colombier 		ether->irq = IRQ0gbe1sum;
1502*897ae9c1SDavid du Colombier 		break;
1503*897ae9c1SDavid du Colombier 	default:
1504*897ae9c1SDavid du Colombier 		panic("ether1116: bad ether ctlr #%d", ether->ctlrno);
1505*897ae9c1SDavid du Colombier 	}
1506*897ae9c1SDavid du Colombier 
1507*897ae9c1SDavid du Colombier 	/* TODO need this for guruplug, at least */
1508*897ae9c1SDavid du Colombier 	*(ulong *)AddrIocfg0 |= 1 << 7 | 1 << 15;	/* io cfg 0: 1.8v gbe */
1509*897ae9c1SDavid du Colombier 	coherence();
1510*897ae9c1SDavid du Colombier 
1511*897ae9c1SDavid du Colombier 	portreset(ctlr->reg);
1512*897ae9c1SDavid du Colombier 	/* ensure that both interfaces are set to RGMII before calling mii */
1513*897ae9c1SDavid du Colombier 	((Gbereg*)Gbe0regs)->psc1 |= PSC1rgmii;
1514*897ae9c1SDavid du Colombier 	((Gbereg*)Gbe1regs)->psc1 |= PSC1rgmii;
1515*897ae9c1SDavid du Colombier 
1516*897ae9c1SDavid du Colombier 	/* Set phy address of the port */
1517*897ae9c1SDavid du Colombier 	ctlr->port = ether->ctlrno;
1518*897ae9c1SDavid du Colombier 	ctlr->reg->phy = ether->ctlrno;
1519*897ae9c1SDavid du Colombier 	coherence();
1520*897ae9c1SDavid du Colombier 	ether->port = (uintptr)ctlr->reg;
1521*897ae9c1SDavid du Colombier 
1522*897ae9c1SDavid du Colombier 	if(kirkwoodmii(ether) < 0){
1523*897ae9c1SDavid du Colombier 		free(ctlr);
1524*897ae9c1SDavid du Colombier 		ether->ctlr = nil;
1525*897ae9c1SDavid du Colombier 		return -1;
1526*897ae9c1SDavid du Colombier 	}
1527*897ae9c1SDavid du Colombier 	miiphyinit(ctlr->mii);
1528*897ae9c1SDavid du Colombier 	archetheraddr(ether, ctlr->reg, Qno);	/* original location */
1529*897ae9c1SDavid du Colombier 	if (memcmp(ether->ea, zeroea, sizeof zeroea) == 0){
1530*897ae9c1SDavid du Colombier 		free(ctlr);
1531*897ae9c1SDavid du Colombier 		ether->ctlr = nil;
1532*897ae9c1SDavid du Colombier 		return -1;			/* no rj45 for this ether */
1533*897ae9c1SDavid du Colombier 	}
1534*897ae9c1SDavid du Colombier 	ether->attach = attach;
1535*897ae9c1SDavid du Colombier 	ether->transmit = transmit;
1536*897ae9c1SDavid du Colombier 	ether->interrupt = interrupt;
1537*897ae9c1SDavid du Colombier 	ether->ifstat = ifstat;
1538*897ae9c1SDavid du Colombier 	ether->shutdown = shutdown;
1539*897ae9c1SDavid du Colombier 	ether->ctl = ctl;
1540*897ae9c1SDavid du Colombier 
1541*897ae9c1SDavid du Colombier 	ether->arg = ether;
1542*897ae9c1SDavid du Colombier 	ether->promiscuous = promiscuous;
1543*897ae9c1SDavid du Colombier 	ether->multicast = multicast;
1544*897ae9c1SDavid du Colombier 
1545*897ae9c1SDavid du Colombier 	return 0;
1546*897ae9c1SDavid du Colombier }
1547*897ae9c1SDavid du Colombier 
1548*897ae9c1SDavid du Colombier void
1549*897ae9c1SDavid du Colombier ether1116link(void)
1550*897ae9c1SDavid du Colombier {
1551*897ae9c1SDavid du Colombier 	addethercard("88e1116", reset);
1552*897ae9c1SDavid du Colombier }
1553