xref: /plan9/sys/src/9/kw/ether1116.c (revision 7ae8f4532bb599d9c475208824b53a0495a01a84)
1897ae9c1SDavid du Colombier /*
2c8a340cdSDavid du Colombier  * marvell kirkwood gigabit ethernet (88e1116 and 88e1121) driver
328620197SDavid du Colombier  * (as found in the sheevaplug, openrd and guruplug).
428620197SDavid du Colombier  * the main difference is the flavour of phy kludgery necessary.
528620197SDavid du Colombier  *
628620197SDavid du Colombier  * from /public/doc/marvell/88f61xx.kirkwood.pdf,
728620197SDavid du Colombier  *	/public/doc/marvell/88e1116.pdf, and
828620197SDavid du Colombier  *	/public/doc/marvell/88e1121r.pdf.
9897ae9c1SDavid du Colombier  */
10897ae9c1SDavid du Colombier 
11897ae9c1SDavid du Colombier #include "u.h"
12897ae9c1SDavid du Colombier #include "../port/lib.h"
13897ae9c1SDavid du Colombier #include "mem.h"
14897ae9c1SDavid du Colombier #include "dat.h"
15897ae9c1SDavid du Colombier #include "fns.h"
16897ae9c1SDavid du Colombier #include "io.h"
17897ae9c1SDavid du Colombier #include "../port/error.h"
18897ae9c1SDavid du Colombier #include "../port/netif.h"
19897ae9c1SDavid du Colombier 
20897ae9c1SDavid du Colombier #include "etherif.h"
21897ae9c1SDavid du Colombier #include "ethermii.h"
22897ae9c1SDavid du Colombier #include "../ip/ip.h"
23897ae9c1SDavid du Colombier 
24897ae9c1SDavid du Colombier #define	MIIDBG	if(0)iprint
25897ae9c1SDavid du Colombier 
2628620197SDavid du Colombier #define WINATTR(v)      (((v) & MASK(8)) << 8)
2728620197SDavid du Colombier #define WINSIZE(v)      (((v)/(64*1024) - 1) << 16)
2828620197SDavid du Colombier 
29897ae9c1SDavid du Colombier enum {
30897ae9c1SDavid du Colombier 	Nrx		= 512,
311ecc8ef2SDavid du Colombier 	Ntx		= 32,
32897ae9c1SDavid du Colombier 	Nrxblks		= 1024,
33897ae9c1SDavid du Colombier 	Rxblklen	= 2+1522,  /* ifc. supplies first 2 bytes as padding */
34897ae9c1SDavid du Colombier 
35897ae9c1SDavid du Colombier 	Maxrxintrsec	= 20*1000,	/* max. rx intrs. / sec */
3628620197SDavid du Colombier 	Etherstuck	= 70,	/* must send or receive a packet in this many sec.s */
37897ae9c1SDavid du Colombier 
38897ae9c1SDavid du Colombier 	Descralign	= 16,
39897ae9c1SDavid du Colombier 	Bufalign	= 8,
40897ae9c1SDavid du Colombier 
41897ae9c1SDavid du Colombier 	Pass		= 1,		/* accept packets */
42897ae9c1SDavid du Colombier 
43897ae9c1SDavid du Colombier 	Qno		= 0,		/* do everything on queue zero */
44897ae9c1SDavid du Colombier };
45897ae9c1SDavid du Colombier 
46897ae9c1SDavid du Colombier typedef struct Ctlr Ctlr;
47897ae9c1SDavid du Colombier typedef struct Gbereg Gbereg;
48897ae9c1SDavid du Colombier typedef struct Mibstats Mibstats;
49897ae9c1SDavid du Colombier typedef struct Rx Rx;
50897ae9c1SDavid du Colombier typedef struct Tx Tx;
51897ae9c1SDavid du Colombier 
52897ae9c1SDavid du Colombier static struct {
53897ae9c1SDavid du Colombier 	Lock;
54897ae9c1SDavid du Colombier 	Block	*head;
55897ae9c1SDavid du Colombier } freeblocks;
56897ae9c1SDavid du Colombier 
57897ae9c1SDavid du Colombier /* hardware receive buffer descriptor */
58897ae9c1SDavid du Colombier struct Rx {
59897ae9c1SDavid du Colombier 	ulong	cs;
60897ae9c1SDavid du Colombier 	ulong	countsize;	/* bytes, buffer size */
61897ae9c1SDavid du Colombier 	ulong	buf;		/* phys. addr. of packet buffer */
62897ae9c1SDavid du Colombier 	ulong	next;		/* phys. addr. of next Rx */
63897ae9c1SDavid du Colombier };
64897ae9c1SDavid du Colombier 
65897ae9c1SDavid du Colombier /* hardware transmit buffer descriptor */
66897ae9c1SDavid du Colombier struct Tx {
67897ae9c1SDavid du Colombier 	ulong	cs;
68897ae9c1SDavid du Colombier 	ulong	countchk;	/* bytes, checksum */
69897ae9c1SDavid du Colombier 	ulong	buf;		/* phys. addr. of packet buffer */
70897ae9c1SDavid du Colombier 	ulong	next;		/* phys. addr. of next Tx */
71897ae9c1SDavid du Colombier };
72897ae9c1SDavid du Colombier 
73897ae9c1SDavid du Colombier /* fixed by hw; part of Gberegs */
74897ae9c1SDavid du Colombier struct Mibstats {
75897ae9c1SDavid du Colombier 	union {
76897ae9c1SDavid du Colombier 		uvlong	rxby;		/* good bytes rcv'd */
77897ae9c1SDavid du Colombier 		struct {
78897ae9c1SDavid du Colombier 			ulong	rxbylo;
79897ae9c1SDavid du Colombier 			ulong	rxbyhi;
80897ae9c1SDavid du Colombier 		};
81897ae9c1SDavid du Colombier 	};
82897ae9c1SDavid du Colombier 	ulong	badrxby;		/* bad bytes rcv'd */
83897ae9c1SDavid du Colombier 	ulong	mactxerr;		/* tx err pkts */
84897ae9c1SDavid du Colombier 	ulong	rxpkt;			/* good pkts rcv'd */
85897ae9c1SDavid du Colombier 	ulong	badrxpkt;		/* bad pkts rcv'd */
86897ae9c1SDavid du Colombier 	ulong	rxbcastpkt;		/* b'cast pkts rcv'd */
87897ae9c1SDavid du Colombier 	ulong	rxmcastpkt;		/* m'cast pkts rcv'd */
88897ae9c1SDavid du Colombier 
89897ae9c1SDavid du Colombier 	ulong	rx64;			/* pkts <= 64 bytes */
90897ae9c1SDavid du Colombier 	ulong	rx65_127;		/* pkts 65—127 bytes */
91897ae9c1SDavid du Colombier 	ulong	rx128_255;		/* pkts 128—255 bytes */
92897ae9c1SDavid du Colombier 	ulong	rx256_511;		/* pkts 256—511 bytes */
93897ae9c1SDavid du Colombier 	ulong	rx512_1023;		/* pkts 512—1023 bytes */
94897ae9c1SDavid du Colombier 	ulong	rx1024_max;		/* pkts >= 1024 bytes */
95897ae9c1SDavid du Colombier 
96897ae9c1SDavid du Colombier 	union {
97897ae9c1SDavid du Colombier 		uvlong	txby;		/* good bytes sent */
98897ae9c1SDavid du Colombier 		struct {
99897ae9c1SDavid du Colombier 			ulong	txbylo;
100897ae9c1SDavid du Colombier 			ulong	txbyhi;
101897ae9c1SDavid du Colombier 		};
102897ae9c1SDavid du Colombier 	};
103897ae9c1SDavid du Colombier 	ulong	txpkt;			/* good pkts sent */
104897ae9c1SDavid du Colombier 	/* half-duplex: pkts dropped due to excessive collisions */
105897ae9c1SDavid du Colombier 	ulong	txcollpktdrop;
106897ae9c1SDavid du Colombier 	ulong	txmcastpkt;		/* m'cast pkts sent */
107897ae9c1SDavid du Colombier 	ulong	txbcastpkt;		/* b'cast pkts sent */
108897ae9c1SDavid du Colombier 
109897ae9c1SDavid du Colombier 	ulong	badmacctlpkts;		/* bad mac ctl pkts */
110897ae9c1SDavid du Colombier 	ulong	txflctl;		/* flow-control pkts sent */
111897ae9c1SDavid du Colombier 	ulong	rxflctl;		/* good flow-control pkts rcv'd */
112897ae9c1SDavid du Colombier 	ulong	badrxflctl;		/* bad flow-control pkts rcv'd */
113897ae9c1SDavid du Colombier 
114897ae9c1SDavid du Colombier 	ulong	rxundersized;		/* runts */
115897ae9c1SDavid du Colombier 	ulong	rxfrags;		/* fragments rcv'd */
116897ae9c1SDavid du Colombier 	ulong	rxtoobig;		/* oversized pkts rcv'd */
117897ae9c1SDavid du Colombier 	ulong	rxjabber;		/* jabber pkts rcv'd */
118897ae9c1SDavid du Colombier 	ulong	rxerr;			/* rx error events */
119897ae9c1SDavid du Colombier 	ulong	crcerr;			/* crc error events */
120897ae9c1SDavid du Colombier 	ulong	collisions;		/* collision events */
121897ae9c1SDavid du Colombier 	ulong	latecoll;		/* late collisions */
122897ae9c1SDavid du Colombier };
123897ae9c1SDavid du Colombier 
124897ae9c1SDavid du Colombier struct Ctlr {
125897ae9c1SDavid du Colombier 	Lock;
12628620197SDavid du Colombier 	Ether	*ether;
127897ae9c1SDavid du Colombier 	Gbereg	*reg;
128897ae9c1SDavid du Colombier 
129897ae9c1SDavid du Colombier 	Lock	initlock;
130897ae9c1SDavid du Colombier 	int	init;
131897ae9c1SDavid du Colombier 
132897ae9c1SDavid du Colombier 	Rx	*rx;		/* receive descriptors */
133897ae9c1SDavid du Colombier 	Block	*rxb[Nrx];	/* blocks belonging to the descriptors */
134897ae9c1SDavid du Colombier 	int	rxhead;		/* descr ethernet will write to next */
135897ae9c1SDavid du Colombier 	int	rxtail;		/* next descr that might need a buffer */
136897ae9c1SDavid du Colombier 	Rendez	rrendez;	/* interrupt wakes up read process */
137897ae9c1SDavid du Colombier 	int	haveinput;
138897ae9c1SDavid du Colombier 
139897ae9c1SDavid du Colombier 	Tx	*tx;
140897ae9c1SDavid du Colombier 	Block	*txb[Ntx];
141897ae9c1SDavid du Colombier 	int	txhead;		/* next descr we can use for new packet */
142897ae9c1SDavid du Colombier 	int	txtail;		/* next descr to reclaim on tx complete */
143897ae9c1SDavid du Colombier 
144897ae9c1SDavid du Colombier 	Mii	*mii;
145897ae9c1SDavid du Colombier 	int	port;
146897ae9c1SDavid du Colombier 
147897ae9c1SDavid du Colombier 	/* stats */
148897ae9c1SDavid du Colombier 	ulong	intrs;
149897ae9c1SDavid du Colombier 	ulong	newintrs;
150897ae9c1SDavid du Colombier 	ulong	txunderrun;
151897ae9c1SDavid du Colombier 	ulong	txringfull;
152897ae9c1SDavid du Colombier 	ulong	rxdiscard;
153897ae9c1SDavid du Colombier 	ulong	rxoverrun;
154897ae9c1SDavid du Colombier 	ulong	nofirstlast;
155897ae9c1SDavid du Colombier 
156897ae9c1SDavid du Colombier 	Mibstats;
157897ae9c1SDavid du Colombier };
158897ae9c1SDavid du Colombier 
159897ae9c1SDavid du Colombier #define	Rxqon(q)	(1<<(q))
160897ae9c1SDavid du Colombier #define	Txqon(q)	(1<<(q))
161897ae9c1SDavid du Colombier 
162897ae9c1SDavid du Colombier enum {
16328620197SDavid du Colombier 	/* euc bits */
16428620197SDavid du Colombier 	Portreset	= 1 << 20,
16528620197SDavid du Colombier 
16628620197SDavid du Colombier 	/* sdma config, sdc bits */
167897ae9c1SDavid du Colombier 	Burst1		= 0,
168897ae9c1SDavid du Colombier 	Burst2,
169897ae9c1SDavid du Colombier 	Burst4,
170897ae9c1SDavid du Colombier 	Burst8,
171897ae9c1SDavid du Colombier 	Burst16,
172897ae9c1SDavid du Colombier 	SDCrifb		= 1<<0,		/* rx intr on pkt boundaries */
173897ae9c1SDavid du Colombier #define SDCrxburst(v)	((v)<<1)
174897ae9c1SDavid du Colombier 	SDCrxnobyteswap	= 1<<4,
175897ae9c1SDavid du Colombier 	SDCtxnobyteswap	= 1<<5,
176897ae9c1SDavid du Colombier 	SDCswap64byte	= 1<<6,
177897ae9c1SDavid du Colombier #define SDCtxburst(v)	((v)<<22)
178897ae9c1SDavid du Colombier 	/* rx intr ipg (inter packet gap) */
179897ae9c1SDavid du Colombier #define SDCipgintrx(v)	((((v)>>15) & 1)<<25) | (((v) & MASK(15))<<7)
180897ae9c1SDavid du Colombier 
18128620197SDavid du Colombier 	/* portcfg bits */
182897ae9c1SDavid du Colombier 	PCFGupromisc		= 1<<0,	/* unicast promiscuous mode */
183897ae9c1SDavid du Colombier #define Rxqdefault(q)	((q)<<1)
184897ae9c1SDavid du Colombier #define Rxqarp(q)	((q)<<4)
185897ae9c1SDavid du Colombier 	PCFGbcrejectnoiparp	= 1<<7,
186897ae9c1SDavid du Colombier 	PCFGbcrejectip		= 1<<8,
187897ae9c1SDavid du Colombier 	PCFGbcrejectarp		= 1<<9,
188897ae9c1SDavid du Colombier 	PCFGamnotxes		= 1<<12, /* auto mode, no summary update on tx */
18928620197SDavid du Colombier 	PCFGtcpq	= 1<<14,	/* capture tcp frames to tcpq */
19028620197SDavid du Colombier 	PCFGudpq	= 1<<15,	/* capture udp frames to udpq */
191897ae9c1SDavid du Colombier #define	Rxqtcp(q)	((q)<<16)
192897ae9c1SDavid du Colombier #define	Rxqudp(q)	((q)<<19)
193897ae9c1SDavid du Colombier #define	Rxqbpdu(q)	((q)<<22)
194897ae9c1SDavid du Colombier 	PCFGrxcs	= 1<<25,	/* rx tcp checksum mode with header */
195897ae9c1SDavid du Colombier 
19628620197SDavid du Colombier 	/* portcfgx bits */
197897ae9c1SDavid du Colombier 	PCFGXspanq	= 1<<1,
198897ae9c1SDavid du Colombier 	PCFGXcrcoff	= 1<<2,		/* no ethernet crc */
199897ae9c1SDavid du Colombier 
20028620197SDavid du Colombier 	/* port serial control0, psc0 bits */
201897ae9c1SDavid du Colombier 	PSC0porton		= 1<<0,
202897ae9c1SDavid du Colombier 	PSC0forcelinkup		= 1<<1,
203897ae9c1SDavid du Colombier 	PSC0an_dplxoff		= 1<<2,	/* an_ = auto. negotiate */
204897ae9c1SDavid du Colombier 	PSC0an_flctloff		= 1<<3,
205897ae9c1SDavid du Colombier 	PSC0an_pauseadv		= 1<<4,
206897ae9c1SDavid du Colombier 	PSC0nofrclinkdown	= 1<<10,
207897ae9c1SDavid du Colombier 	PSC0an_spdoff		= 1<<13,
20828620197SDavid du Colombier 	PSC0dteadv		= 1<<14,	/* dte advertise */
209897ae9c1SDavid du Colombier 
210897ae9c1SDavid du Colombier 	/* max. input pkt size */
211897ae9c1SDavid du Colombier #define PSC0mru(v)	((v)<<17)
212897ae9c1SDavid du Colombier 	PSC0mrumask	= PSC0mru(MASK(3)),
213897ae9c1SDavid du Colombier 	PSC0mru1518	= 0,		/* 1500+2* 6(addrs) +2 + 4(crc) */
214897ae9c1SDavid du Colombier 	PSC0mru1522,			/* 1518 + 4(vlan tags) */
215897ae9c1SDavid du Colombier 	PSC0mru1552,			/* `baby giant' */
216897ae9c1SDavid du Colombier 	PSC0mru9022,			/* `jumbo' */
217897ae9c1SDavid du Colombier 	PSC0mru9192,			/* bigger jumbo */
218897ae9c1SDavid du Colombier 	PSC0mru9700,			/* still bigger jumbo */
219897ae9c1SDavid du Colombier 
220897ae9c1SDavid du Colombier 	PSC0fd_frc		= 1<<21,	/* force full duplex */
221897ae9c1SDavid du Colombier 	PSC0flctlfrc		= 1<<22,
222897ae9c1SDavid du Colombier 	PSC0gmiispd_gbfrc	= 1<<23,
223897ae9c1SDavid du Colombier 	PSC0miispdfrc100mbps	= 1<<24,
224897ae9c1SDavid du Colombier 
22528620197SDavid du Colombier 	/* port status 0, ps0 bits */
226897ae9c1SDavid du Colombier 	PS0linkup	= 1<<1,
227897ae9c1SDavid du Colombier 	PS0fd		= 1<<2,			/* full duplex */
228897ae9c1SDavid du Colombier 	PS0flctl	= 1<<3,
229897ae9c1SDavid du Colombier 	PS0gmii_gb	= 1<<4,
230897ae9c1SDavid du Colombier 	PS0mii100mbps	= 1<<5,
231897ae9c1SDavid du Colombier 	PS0txbusy	= 1<<7,
232897ae9c1SDavid du Colombier 	PS0txfifoempty	= 1<<10,
233897ae9c1SDavid du Colombier 	PS0rxfifo1empty	= 1<<11,
234897ae9c1SDavid du Colombier 	PS0rxfifo2empty	= 1<<12,
235897ae9c1SDavid du Colombier 
23628620197SDavid du Colombier 	/* port serial control 1, psc1 bits */
237897ae9c1SDavid du Colombier 	PSC1loopback	= 1<<1,
238897ae9c1SDavid du Colombier 	PSC1mii		= 0<<2,
239897ae9c1SDavid du Colombier 	PSC1rgmii	= 1<<3,			/* enable RGMII */
240897ae9c1SDavid du Colombier 	PSC1portreset	= 1<<4,
241897ae9c1SDavid du Colombier 	PSC1clockbypass	= 1<<5,
242897ae9c1SDavid du Colombier 	PSC1iban	= 1<<6,
243897ae9c1SDavid du Colombier 	PSC1iban_bypass	= 1<<7,
244897ae9c1SDavid du Colombier 	PSC1iban_restart= 1<<8,
245897ae9c1SDavid du Colombier 	PSC1_gbonly	= 1<<11,
246897ae9c1SDavid du Colombier 	PSC1encolonbp	= 1<<15, /* "collision during back-pressure mib counting" */
247897ae9c1SDavid du Colombier 	PSC1coldomlimmask= MASK(6)<<16,
248897ae9c1SDavid du Colombier #define PSC1coldomlim(v) (((v) & MASK(6))<<16)
249897ae9c1SDavid du Colombier 	PSC1miiallowoddpreamble	= 1<<22,
250897ae9c1SDavid du Colombier 
25128620197SDavid du Colombier 	/* port status 1, ps1 bits */
252897ae9c1SDavid du Colombier 	PS1rxpause	= 1<<0,
253897ae9c1SDavid du Colombier 	PS1txpause	= 1<<1,
254897ae9c1SDavid du Colombier 	PS1pressure	= 1<<2,
255897ae9c1SDavid du Colombier 	PS1syncfail10ms	= 1<<3,
256897ae9c1SDavid du Colombier 	PS1an_done	= 1<<4,
257897ae9c1SDavid du Colombier 	PS1inbandan_bypassed	= 1<<5,
258897ae9c1SDavid du Colombier 	PS1serdesplllocked	= 1<<6,
259897ae9c1SDavid du Colombier 	PS1syncok	= 1<<7,
260897ae9c1SDavid du Colombier 	PS1nosquelch	= 1<<8,
261897ae9c1SDavid du Colombier 
26228620197SDavid du Colombier 	/* irq bits */
26328620197SDavid du Colombier 	/* rx buf returned to cpu ownership, or frame reception finished */
264897ae9c1SDavid du Colombier 	Irx		= 1<<0,
26528620197SDavid du Colombier 	Iextend		= 1<<1,		/* IEsum of irqe set */
26628620197SDavid du Colombier #define Irxbufferq(q)	(1<<((q)+2))	/* rx buf returned to cpu ownership */
26728620197SDavid du Colombier 	Irxerr		= 1<<10,	/* input ring full, usually */
268897ae9c1SDavid du Colombier #define Irxerrq(q)	(1<<((q)+11))
26928620197SDavid du Colombier #define Itxendq(q)	(1<<((q)+19))	/* tx dma stopped for q */
270897ae9c1SDavid du Colombier 	Isum		= 1<<31,
271897ae9c1SDavid du Colombier 
27228620197SDavid du Colombier 	/* irq extended, irqe bits */
27328620197SDavid du Colombier #define	IEtxbufferq(q)	(1<<((q)+0))	/* tx buf returned to cpu ownership */
274897ae9c1SDavid du Colombier #define	IEtxerrq(q)	(1<<((q)+8))
275897ae9c1SDavid du Colombier 	IEphystschg	= 1<<16,
276897ae9c1SDavid du Colombier 	IEptp		= 1<<17,
277897ae9c1SDavid du Colombier 	IErxoverrun	= 1<<18,
278897ae9c1SDavid du Colombier 	IEtxunderrun	= 1<<19,
279897ae9c1SDavid du Colombier 	IElinkchg	= 1<<20,
280897ae9c1SDavid du Colombier 	IEintaddrerr	= 1<<23,
281897ae9c1SDavid du Colombier 	IEprbserr	= 1<<25,
282897ae9c1SDavid du Colombier 	IEsum		= 1<<31,
283897ae9c1SDavid du Colombier 
284897ae9c1SDavid du Colombier 	/* tx fifo urgent threshold (tx interrupt coalescing), pxtfut */
285897ae9c1SDavid du Colombier #define TFUTipginttx(v)	(((v) & MASK(16))<<4);
286897ae9c1SDavid du Colombier 
287897ae9c1SDavid du Colombier 	/* minimal frame size, mfs */
288897ae9c1SDavid du Colombier 	MFS40by	= 10<<2,
289897ae9c1SDavid du Colombier 	MFS44by	= 11<<2,
290897ae9c1SDavid du Colombier 	MFS48by	= 12<<2,
291897ae9c1SDavid du Colombier 	MFS52by	= 13<<2,
292897ae9c1SDavid du Colombier 	MFS56by	= 14<<2,
293897ae9c1SDavid du Colombier 	MFS60by	= 15<<2,
294897ae9c1SDavid du Colombier 	MFS64by	= 16<<2,
295897ae9c1SDavid du Colombier 
296897ae9c1SDavid du Colombier 	/* receive descriptor status */
297897ae9c1SDavid du Colombier 	RCSmacerr	= 1<<0,
298897ae9c1SDavid du Colombier 	RCSmacmask	= 3<<1,
299897ae9c1SDavid du Colombier 	RCSmacce	= 0<<1,
300897ae9c1SDavid du Colombier 	RCSmacor	= 1<<1,
301897ae9c1SDavid du Colombier 	RCSmacmf	= 2<<1,
302897ae9c1SDavid du Colombier 	RCSl4chkshift	= 3,
303897ae9c1SDavid du Colombier 	RCSl4chkmask	= MASK(16),
304897ae9c1SDavid du Colombier 	RCSvlan		= 1<<17,
305897ae9c1SDavid du Colombier 	RCSbpdu		= 1<<18,
306897ae9c1SDavid du Colombier 	RCSl4mask	= 3<<21,
307897ae9c1SDavid du Colombier 	RCSl4tcp4	= 0<<21,
308897ae9c1SDavid du Colombier 	RCSl4udp4	= 1<<21,
309897ae9c1SDavid du Colombier 	RCSl4other	= 2<<21,
310897ae9c1SDavid du Colombier 	RCSl4rsvd	= 3<<21,
311897ae9c1SDavid du Colombier 	RCSl2ev2	= 1<<23,
312897ae9c1SDavid du Colombier 	RCSl3ip4	= 1<<24,
313897ae9c1SDavid du Colombier 	RCSip4headok	= 1<<25,
314897ae9c1SDavid du Colombier 	RCSlast		= 1<<26,
315897ae9c1SDavid du Colombier 	RCSfirst	= 1<<27,
316897ae9c1SDavid du Colombier 	RCSunknownaddr	= 1<<28,
317897ae9c1SDavid du Colombier 	RCSenableintr	= 1<<29,
318897ae9c1SDavid du Colombier 	RCSl4chkok	= 1<<30,
319897ae9c1SDavid du Colombier 	RCSdmaown	= 1<<31,
320897ae9c1SDavid du Colombier 
321897ae9c1SDavid du Colombier 	/* transmit descriptor status */
322897ae9c1SDavid du Colombier 	TCSmacerr	= 1<<0,
323897ae9c1SDavid du Colombier 	TCSmacmask	= 3<<1,
324897ae9c1SDavid du Colombier 	TCSmaclc	= 0<<1,
325897ae9c1SDavid du Colombier 	TCSmacur	= 1<<1,
326897ae9c1SDavid du Colombier 	TCSmacrl	= 2<<1,
327897ae9c1SDavid du Colombier 	TCSllc		= 1<<9,
328897ae9c1SDavid du Colombier 	TCSl4chkmode	= 1<<10,
329897ae9c1SDavid du Colombier 	TCSipv4hdlenshift= 11,
330897ae9c1SDavid du Colombier 	TCSvlan		= 1<<15,
331897ae9c1SDavid du Colombier 	TCSl4type	= 1<<16,
332897ae9c1SDavid du Colombier 	TCSgl4chk	= 1<<17,
333897ae9c1SDavid du Colombier 	TCSgip4chk	= 1<<18,
334897ae9c1SDavid du Colombier 	TCSpadding	= 1<<19,
335897ae9c1SDavid du Colombier 	TCSlast		= 1<<20,
336897ae9c1SDavid du Colombier 	TCSfirst	= 1<<21,
337897ae9c1SDavid du Colombier 	TCSenableintr	= 1<<23,
338897ae9c1SDavid du Colombier 	TCSautomode	= 1<<30,
339897ae9c1SDavid du Colombier 	TCSdmaown	= 1<<31,
340897ae9c1SDavid du Colombier };
341897ae9c1SDavid du Colombier 
342897ae9c1SDavid du Colombier enum {
343897ae9c1SDavid du Colombier 	/* SMI regs */
344897ae9c1SDavid du Colombier 	PhysmiTimeout	= 10000,	/* what units? in ms. */
345897ae9c1SDavid du Colombier 	Physmidataoff	= 0,		/* Data */
346897ae9c1SDavid du Colombier 	Physmidatamask	= 0xffff<<Physmidataoff,
347897ae9c1SDavid du Colombier 
348897ae9c1SDavid du Colombier 	Physmiaddroff 	= 16,		/* PHY device addr */
349897ae9c1SDavid du Colombier 	Physmiaddrmask	= 0x1f << Physmiaddroff,
350897ae9c1SDavid du Colombier 
351897ae9c1SDavid du Colombier 	Physmiop	= 26,
352897ae9c1SDavid du Colombier 	Physmiopmask	= 3<<Physmiop,
353897ae9c1SDavid du Colombier 	PhysmiopWr	= 0<<Physmiop,
354897ae9c1SDavid du Colombier 	PhysmiopRd	= 1<<Physmiop,
355897ae9c1SDavid du Colombier 
356897ae9c1SDavid du Colombier 	PhysmiReadok	= 1<<27,
357897ae9c1SDavid du Colombier 	PhysmiBusy	= 1<<28,
358897ae9c1SDavid du Colombier 
359897ae9c1SDavid du Colombier 	SmiRegaddroff	= 21,		/* PHY device register addr */
360897ae9c1SDavid du Colombier 	SmiRegaddrmask	= 0x1f << SmiRegaddroff,
361897ae9c1SDavid du Colombier };
362897ae9c1SDavid du Colombier 
363897ae9c1SDavid du Colombier struct Gbereg {
364897ae9c1SDavid du Colombier 	ulong	phy;			/* PHY address */
365897ae9c1SDavid du Colombier 	ulong	smi;			/* serial mgmt. interface */
366897ae9c1SDavid du Colombier 	ulong	euda;			/* ether default address */
367897ae9c1SDavid du Colombier 	ulong	eudid;			/* ether default id */
368897ae9c1SDavid du Colombier 	uchar	_pad0[0x80-0x10];
369897ae9c1SDavid du Colombier 
37028620197SDavid du Colombier 	/* dma stuff */
371897ae9c1SDavid du Colombier 	ulong	euirq;			/* interrupt cause */
372897ae9c1SDavid du Colombier 	ulong	euirqmask;		/* interrupt mask */
373897ae9c1SDavid du Colombier 	uchar	_pad1[0x94-0x88];
374897ae9c1SDavid du Colombier 	ulong	euea;			/* error address */
375897ae9c1SDavid du Colombier 	ulong	euiae;			/* internal error address */
376897ae9c1SDavid du Colombier 	uchar	_pad2[0xb0-0x9c];
377897ae9c1SDavid du Colombier 	ulong	euc;			/* control */
378897ae9c1SDavid du Colombier 	uchar	_pad3[0x200-0xb4];
379897ae9c1SDavid du Colombier 	struct {
380897ae9c1SDavid du Colombier 		ulong	base;		/* window base */
381897ae9c1SDavid du Colombier 		ulong	size;		/* window size */
382897ae9c1SDavid du Colombier 	} base[6];
383897ae9c1SDavid du Colombier 	uchar	_pad4[0x280-0x230];
384897ae9c1SDavid du Colombier 	ulong	harr[4];		/* high address remap */
385897ae9c1SDavid du Colombier 	ulong	bare;			/* base address enable */
386897ae9c1SDavid du Colombier 	ulong	epap;			/* port access protect */
387897ae9c1SDavid du Colombier 	uchar	_pad5[0x400-0x298];
388897ae9c1SDavid du Colombier 
389897ae9c1SDavid du Colombier 	ulong	portcfg;		/* port configuration */
390897ae9c1SDavid du Colombier 	ulong	portcfgx;		/* port config. extend */
391897ae9c1SDavid du Colombier 	ulong	mii;			/* mii serial parameters */
392897ae9c1SDavid du Colombier 	ulong	_pad6;
393897ae9c1SDavid du Colombier 	ulong	evlane;			/* vlan ether type */
394897ae9c1SDavid du Colombier 	ulong	macal;			/* mac address low */
395897ae9c1SDavid du Colombier 	ulong	macah;			/* mac address high */
396897ae9c1SDavid du Colombier 	ulong	sdc;			/* sdma config. */
397897ae9c1SDavid du Colombier 	ulong	dscp[7];		/* ip diff. serv. code point -> pri */
398897ae9c1SDavid du Colombier 	ulong	psc0;			/* port serial control 0 */
399897ae9c1SDavid du Colombier 	ulong	vpt2p;			/* vlan priority tag -> pri */
400897ae9c1SDavid du Colombier 	ulong	ps0;			/* ether port status 0 */
401897ae9c1SDavid du Colombier 	ulong	tqc;			/* transmit queue command */
402897ae9c1SDavid du Colombier 	ulong	psc1;			/* port serial control 1 */
403897ae9c1SDavid du Colombier 	ulong	ps1;			/* ether port status 1 */
404897ae9c1SDavid du Colombier 	ulong	mvhdr;			/* marvell header */
405897ae9c1SDavid du Colombier 	ulong	_pad8[2];
406897ae9c1SDavid du Colombier 
407897ae9c1SDavid du Colombier 	/* interrupts */
408897ae9c1SDavid du Colombier 	ulong	irq;			/* interrupt cause; some rw0c bits */
409897ae9c1SDavid du Colombier 	ulong	irqe;			/* " " extended; some rw0c bits */
410897ae9c1SDavid du Colombier 	ulong	irqmask;		/* interrupt mask (actually enable) */
411897ae9c1SDavid du Colombier 	ulong	irqemask;		/* " " extended */
412897ae9c1SDavid du Colombier 
413897ae9c1SDavid du Colombier 	ulong	_pad9;
414897ae9c1SDavid du Colombier 	ulong	pxtfut;			/* port tx fifo urgent threshold */
415897ae9c1SDavid du Colombier 	ulong	_pad10;
416897ae9c1SDavid du Colombier 	ulong	pxmfs;			/* port rx minimum frame size */
417897ae9c1SDavid du Colombier 	ulong	_pad11;
418897ae9c1SDavid du Colombier 
419897ae9c1SDavid du Colombier 	/*
420897ae9c1SDavid du Colombier 	 * # of input frames discarded by addr filtering or lack of resources;
421897ae9c1SDavid du Colombier 	 * zeroed upon read.
422897ae9c1SDavid du Colombier 	 */
423897ae9c1SDavid du Colombier 	ulong	pxdfc;			/* port rx discard frame counter */
424897ae9c1SDavid du Colombier 	ulong	pxofc;			/* port overrun frame counter */
425897ae9c1SDavid du Colombier 	ulong	_pad12[2];
426897ae9c1SDavid du Colombier 	ulong	piae;			/* port internal address error */
427897ae9c1SDavid du Colombier 	uchar	_pad13[0x4bc-0x498];
428897ae9c1SDavid du Colombier 	ulong	etherprio;		/* ether type priority */
429897ae9c1SDavid du Colombier 	uchar	_pad14[0x4dc-0x4c0];
430897ae9c1SDavid du Colombier 	ulong	tqfpc;			/* tx queue fixed priority config. */
431897ae9c1SDavid du Colombier 	ulong	pttbrc;			/* port tx token-bucket rate config. */
432897ae9c1SDavid du Colombier 	ulong	tqc1;			/* tx queue command 1 */
433897ae9c1SDavid du Colombier 	ulong	pmtu;			/* port maximum transmit unit */
434897ae9c1SDavid du Colombier 	ulong	pmtbs;			/* port maximum token bucket size */
435897ae9c1SDavid du Colombier 	uchar	_pad15[0x600-0x4f0];
436897ae9c1SDavid du Colombier 
437897ae9c1SDavid du Colombier 	struct {
438897ae9c1SDavid du Colombier 		ulong	_pad[3];
439897ae9c1SDavid du Colombier 		ulong	r;		/* phys. addr.: cur. rx desc. ptrs */
440897ae9c1SDavid du Colombier 	} crdp[8];
441897ae9c1SDavid du Colombier 	ulong	rqc;			/* rx queue command */
442897ae9c1SDavid du Colombier 	ulong	tcsdp;			/* phys. addr.: cur. tx desc. ptr */
443897ae9c1SDavid du Colombier 	uchar	_pad16[0x6c0-0x688];
444897ae9c1SDavid du Colombier 
445897ae9c1SDavid du Colombier 	ulong	tcqdp[8];		/* phys. addr.: cur. tx q. desc. ptr */
446897ae9c1SDavid du Colombier 	uchar	_pad17[0x700-0x6e0];
447897ae9c1SDavid du Colombier 
448897ae9c1SDavid du Colombier 	struct {
449897ae9c1SDavid du Colombier 		ulong	tbctr;		/* queue tx token-bucket counter */
450897ae9c1SDavid du Colombier 		ulong	tbcfg;		/* tx queue token-bucket config. */
451897ae9c1SDavid du Colombier 		ulong	acfg;		/* tx queue arbiter config. */
452897ae9c1SDavid du Colombier 		ulong	_pad;
453897ae9c1SDavid du Colombier 	} tq[8];
454897ae9c1SDavid du Colombier 	ulong	pttbc;			/* port tx token-bucket counter */
455897ae9c1SDavid du Colombier 	uchar	_pad18[0x7a8-0x784];
456897ae9c1SDavid du Colombier 
457897ae9c1SDavid du Colombier 	ulong	ipg2;			/* tx queue ipg */
458897ae9c1SDavid du Colombier 	ulong	_pad19[3];
459897ae9c1SDavid du Colombier 	ulong	ipg3;
460897ae9c1SDavid du Colombier 	ulong	_pad20;
461897ae9c1SDavid du Colombier 	ulong	htlp;			/* high token in low packet */
462897ae9c1SDavid du Colombier 	ulong	htap;			/* high token in async packet */
463897ae9c1SDavid du Colombier 	ulong	ltap;			/* low token in async packet */
464897ae9c1SDavid du Colombier 	ulong	_pad21;
465897ae9c1SDavid du Colombier 	ulong	ts;			/* tx speed */
466897ae9c1SDavid du Colombier 	uchar	_pad22[0x1000-0x7d4];
467897ae9c1SDavid du Colombier 
468897ae9c1SDavid du Colombier 	/* mac mib counters: statistics */
469897ae9c1SDavid du Colombier 	Mibstats;
470897ae9c1SDavid du Colombier 	uchar	_pad23[0x1400-0x1080];
471897ae9c1SDavid du Colombier 
472897ae9c1SDavid du Colombier 	/* multicast filtering; each byte: Qno<<1 | Pass */
473897ae9c1SDavid du Colombier 	ulong	dfsmt[64];	/* dest addr filter special m'cast table */
474897ae9c1SDavid du Colombier 	ulong	dfomt[64];	/* dest addr filter other m'cast table */
475897ae9c1SDavid du Colombier 	/* unicast filtering */
476897ae9c1SDavid du Colombier 	ulong	dfut[4];		/* dest addr filter unicast table */
477897ae9c1SDavid du Colombier };
478897ae9c1SDavid du Colombier 
47928620197SDavid du Colombier static Ctlr *ctlrs[MaxEther];
48028620197SDavid du Colombier static uchar zeroea[Eaddrlen];
48128620197SDavid du Colombier 
482897ae9c1SDavid du Colombier static void getmibstats(Ctlr *);
483897ae9c1SDavid du Colombier 
484897ae9c1SDavid du Colombier static void
rxfreeb(Block * b)485897ae9c1SDavid du Colombier rxfreeb(Block *b)
486897ae9c1SDavid du Colombier {
487897ae9c1SDavid du Colombier 	/* freeb(b) will have previously decremented b->ref to 0; raise to 1 */
488897ae9c1SDavid du Colombier 	_xinc(&b->ref);
489897ae9c1SDavid du Colombier 	b->wp = b->rp =
490897ae9c1SDavid du Colombier 		(uchar*)((uintptr)(b->lim - Rxblklen) & ~(Bufalign - 1));
491897ae9c1SDavid du Colombier 	assert(((uintptr)b->rp & (Bufalign - 1)) == 0);
492897ae9c1SDavid du Colombier 	b->free = rxfreeb;
493897ae9c1SDavid du Colombier 
494897ae9c1SDavid du Colombier 	ilock(&freeblocks);
495897ae9c1SDavid du Colombier 	b->next = freeblocks.head;
496897ae9c1SDavid du Colombier 	freeblocks.head = b;
497897ae9c1SDavid du Colombier 	iunlock(&freeblocks);
498897ae9c1SDavid du Colombier }
499897ae9c1SDavid du Colombier 
500897ae9c1SDavid du Colombier static Block *
rxallocb(void)501897ae9c1SDavid du Colombier rxallocb(void)
502897ae9c1SDavid du Colombier {
503897ae9c1SDavid du Colombier 	Block *b;
504897ae9c1SDavid du Colombier 
505897ae9c1SDavid du Colombier 	ilock(&freeblocks);
506897ae9c1SDavid du Colombier 	b = freeblocks.head;
507897ae9c1SDavid du Colombier 	if(b != nil) {
508897ae9c1SDavid du Colombier 		freeblocks.head = b->next;
509897ae9c1SDavid du Colombier 		b->next = nil;
510897ae9c1SDavid du Colombier 		b->free = rxfreeb;
511897ae9c1SDavid du Colombier 	}
512897ae9c1SDavid du Colombier 	iunlock(&freeblocks);
513897ae9c1SDavid du Colombier 	return b;
514897ae9c1SDavid du Colombier }
515897ae9c1SDavid du Colombier 
516897ae9c1SDavid du Colombier static void
rxkick(Ctlr * ctlr)517897ae9c1SDavid du Colombier rxkick(Ctlr *ctlr)
518897ae9c1SDavid du Colombier {
519897ae9c1SDavid du Colombier 	Gbereg *reg = ctlr->reg;
520897ae9c1SDavid du Colombier 
521897ae9c1SDavid du Colombier 	if (reg->crdp[Qno].r == 0)
522897ae9c1SDavid du Colombier 		reg->crdp[Qno].r = PADDR(&ctlr->rx[ctlr->rxhead]);
523897ae9c1SDavid du Colombier 	if ((reg->rqc & 0xff) == 0)		/* all queues are stopped? */
524897ae9c1SDavid du Colombier 		reg->rqc = Rxqon(Qno);		/* restart */
525897ae9c1SDavid du Colombier 	coherence();
526897ae9c1SDavid du Colombier }
527897ae9c1SDavid du Colombier 
528897ae9c1SDavid du Colombier static void
txkick(Ctlr * ctlr)529897ae9c1SDavid du Colombier txkick(Ctlr *ctlr)
530897ae9c1SDavid du Colombier {
531897ae9c1SDavid du Colombier 	Gbereg *reg = ctlr->reg;
532897ae9c1SDavid du Colombier 
533897ae9c1SDavid du Colombier 	if (reg->tcqdp[Qno] == 0)
534897ae9c1SDavid du Colombier 		reg->tcqdp[Qno] = PADDR(&ctlr->tx[ctlr->txhead]);
535897ae9c1SDavid du Colombier 	if ((reg->tqc & 0xff) == 0)		/* all q's stopped? */
536897ae9c1SDavid du Colombier 		reg->tqc = Txqon(Qno);		/* restart */
537897ae9c1SDavid du Colombier 	coherence();
538897ae9c1SDavid du Colombier }
539897ae9c1SDavid du Colombier 
540897ae9c1SDavid du Colombier static void
rxreplenish(Ctlr * ctlr)541897ae9c1SDavid du Colombier rxreplenish(Ctlr *ctlr)
542897ae9c1SDavid du Colombier {
543897ae9c1SDavid du Colombier 	Rx *r;
544897ae9c1SDavid du Colombier 	Block *b;
545897ae9c1SDavid du Colombier 
546897ae9c1SDavid du Colombier 	while(ctlr->rxb[ctlr->rxtail] == nil) {
547897ae9c1SDavid du Colombier 		b = rxallocb();
548897ae9c1SDavid du Colombier 		if(b == nil) {
54928620197SDavid du Colombier 			iprint("#l%d: rxreplenish out of buffers\n",
55028620197SDavid du Colombier 				ctlr->ether->ctlrno);
551897ae9c1SDavid du Colombier 			break;
552897ae9c1SDavid du Colombier 		}
553897ae9c1SDavid du Colombier 
554897ae9c1SDavid du Colombier 		ctlr->rxb[ctlr->rxtail] = b;
555897ae9c1SDavid du Colombier 
5562f205b96SDavid du Colombier 		/* set up uncached receive descriptor */
557897ae9c1SDavid du Colombier 		r = &ctlr->rx[ctlr->rxtail];
558897ae9c1SDavid du Colombier 		assert(((uintptr)r & (Descralign - 1)) == 0);
5592f205b96SDavid du Colombier 		r->countsize = ROUNDUP(Rxblklen, 8);
560897ae9c1SDavid du Colombier 		r->buf = PADDR(b->rp);
5612f205b96SDavid du Colombier 		coherence();
562897ae9c1SDavid du Colombier 
563897ae9c1SDavid du Colombier 		/* and fire */
564897ae9c1SDavid du Colombier 		r->cs = RCSdmaown | RCSenableintr;
5652f205b96SDavid du Colombier 		coherence();
566897ae9c1SDavid du Colombier 
567897ae9c1SDavid du Colombier 		ctlr->rxtail = NEXT(ctlr->rxtail, Nrx);
568897ae9c1SDavid du Colombier 	}
569897ae9c1SDavid du Colombier }
570897ae9c1SDavid du Colombier 
571897ae9c1SDavid du Colombier static void
dump(uchar * bp,long max)572897ae9c1SDavid du Colombier dump(uchar *bp, long max)
573897ae9c1SDavid du Colombier {
574897ae9c1SDavid du Colombier 	if (max > 64)
575897ae9c1SDavid du Colombier 		max = 64;
576897ae9c1SDavid du Colombier 	for (; max > 0; max--, bp++)
577897ae9c1SDavid du Colombier 		iprint("%02.2ux ", *bp);
578897ae9c1SDavid du Colombier 	print("...\n");
579897ae9c1SDavid du Colombier }
580897ae9c1SDavid du Colombier 
581897ae9c1SDavid du Colombier static void
etheractive(Ether * ether)582897ae9c1SDavid du Colombier etheractive(Ether *ether)
583897ae9c1SDavid du Colombier {
584897ae9c1SDavid du Colombier 	ether->starttime = TK2MS(MACHP(0)->ticks)/1000;
585897ae9c1SDavid du Colombier }
586897ae9c1SDavid du Colombier 
587897ae9c1SDavid du Colombier static void
ethercheck(Ether * ether)588897ae9c1SDavid du Colombier ethercheck(Ether *ether)
589897ae9c1SDavid du Colombier {
590897ae9c1SDavid du Colombier 	if (ether->starttime != 0 &&
591897ae9c1SDavid du Colombier 	    TK2MS(MACHP(0)->ticks)/1000 - ether->starttime > Etherstuck) {
592897ae9c1SDavid du Colombier 		etheractive(ether);
593ea9e0a24SDavid du Colombier 		if (ether->ctlrno == 0)	/* only complain about main ether */
594ea9e0a24SDavid du Colombier 			iprint("#l%d: ethernet stuck\n", ether->ctlrno);
595897ae9c1SDavid du Colombier 	}
596897ae9c1SDavid du Colombier }
597897ae9c1SDavid du Colombier 
598897ae9c1SDavid du Colombier static void
receive(Ether * ether)599897ae9c1SDavid du Colombier receive(Ether *ether)
600897ae9c1SDavid du Colombier {
601897ae9c1SDavid du Colombier 	int i;
602897ae9c1SDavid du Colombier 	ulong n;
603897ae9c1SDavid du Colombier 	Block *b;
604897ae9c1SDavid du Colombier 	Ctlr *ctlr = ether->ctlr;
605897ae9c1SDavid du Colombier 	Rx *r;
606897ae9c1SDavid du Colombier 
607897ae9c1SDavid du Colombier 	ethercheck(ether);
608897ae9c1SDavid du Colombier 	for (i = Nrx-2; i > 0; i--) {
6092f205b96SDavid du Colombier 		r = &ctlr->rx[ctlr->rxhead];	/* *r is uncached */
610897ae9c1SDavid du Colombier 		assert(((uintptr)r & (Descralign - 1)) == 0);
6112f205b96SDavid du Colombier 		if(r->cs & RCSdmaown)		/* descriptor busy? */
612897ae9c1SDavid du Colombier 			break;
613897ae9c1SDavid du Colombier 
6142f205b96SDavid du Colombier 		b = ctlr->rxb[ctlr->rxhead];	/* got input buffer? */
615897ae9c1SDavid du Colombier 		if (b == nil)
616897ae9c1SDavid du Colombier 			panic("ether1116: nil ctlr->rxb[ctlr->rxhead] "
617897ae9c1SDavid du Colombier 				"in receive");
618897ae9c1SDavid du Colombier 		ctlr->rxb[ctlr->rxhead] = nil;
619897ae9c1SDavid du Colombier 		ctlr->rxhead = NEXT(ctlr->rxhead, Nrx);
620897ae9c1SDavid du Colombier 
621897ae9c1SDavid du Colombier 		if((r->cs & (RCSfirst|RCSlast)) != (RCSfirst|RCSlast)) {
6222f205b96SDavid du Colombier 			ctlr->nofirstlast++;	/* partial packet */
623897ae9c1SDavid du Colombier 			freeb(b);
624897ae9c1SDavid du Colombier 			continue;
625897ae9c1SDavid du Colombier 		}
626897ae9c1SDavid du Colombier 		if(r->cs & RCSmacerr) {
627897ae9c1SDavid du Colombier 			freeb(b);
628897ae9c1SDavid du Colombier 			continue;
629897ae9c1SDavid du Colombier 		}
630897ae9c1SDavid du Colombier 
63128620197SDavid du Colombier 		n = r->countsize >> 16;		/* TODO includes 2 pad bytes? */
632897ae9c1SDavid du Colombier 		assert(n >= 2 && n < 2048);
633897ae9c1SDavid du Colombier 
6342f205b96SDavid du Colombier 		/* clear any cached packet or part thereof */
63528620197SDavid du Colombier 		l2cacheuinvse(b->rp, n+2);
63628620197SDavid du Colombier 		cachedinvse(b->rp, n+2);
637897ae9c1SDavid du Colombier 		b->wp = b->rp + n;
638897ae9c1SDavid du Colombier 		/*
639897ae9c1SDavid du Colombier 		 * skip hardware padding intended to align ipv4 address
640897ae9c1SDavid du Colombier 		 * in memory (mv-s104860-u0 §8.3.4.1)
641897ae9c1SDavid du Colombier 		 */
642897ae9c1SDavid du Colombier 		b->rp += 2;
643897ae9c1SDavid du Colombier 		etheriq(ether, b, 1);
644897ae9c1SDavid du Colombier 		etheractive(ether);
64528620197SDavid du Colombier 		if (i % (Nrx / 2) == 0) {
646897ae9c1SDavid du Colombier 			rxreplenish(ctlr);
64728620197SDavid du Colombier 			rxkick(ctlr);
64828620197SDavid du Colombier 		}
649897ae9c1SDavid du Colombier 	}
650897ae9c1SDavid du Colombier 	rxreplenish(ctlr);
65128620197SDavid du Colombier 	rxkick(ctlr);
652897ae9c1SDavid du Colombier }
653897ae9c1SDavid du Colombier 
654897ae9c1SDavid du Colombier static void
txreplenish(Ether * ether)655897ae9c1SDavid du Colombier txreplenish(Ether *ether)			/* free transmitted packets */
656897ae9c1SDavid du Colombier {
657897ae9c1SDavid du Colombier 	Ctlr *ctlr;
658897ae9c1SDavid du Colombier 
659897ae9c1SDavid du Colombier 	ctlr = ether->ctlr;
660897ae9c1SDavid du Colombier 	while(ctlr->txtail != ctlr->txhead) {
6612f205b96SDavid du Colombier 		/* ctlr->tx is uncached */
662897ae9c1SDavid du Colombier 		if(ctlr->tx[ctlr->txtail].cs & TCSdmaown)
663897ae9c1SDavid du Colombier 			break;
664897ae9c1SDavid du Colombier 		if(ctlr->txb[ctlr->txtail] == nil)
665897ae9c1SDavid du Colombier 			panic("no block for sent packet?!");
666897ae9c1SDavid du Colombier 		freeb(ctlr->txb[ctlr->txtail]);
667897ae9c1SDavid du Colombier 		ctlr->txb[ctlr->txtail] = nil;
66828620197SDavid du Colombier 
669897ae9c1SDavid du Colombier 		ctlr->txtail = NEXT(ctlr->txtail, Ntx);
670897ae9c1SDavid du Colombier 		etheractive(ether);
671897ae9c1SDavid du Colombier 	}
672897ae9c1SDavid du Colombier }
673897ae9c1SDavid du Colombier 
674897ae9c1SDavid du Colombier /*
675897ae9c1SDavid du Colombier  * transmit strategy: fill the output ring as far as possible,
676897ae9c1SDavid du Colombier  * perhaps leaving a few spare; kick off the output and take
677897ae9c1SDavid du Colombier  * an interrupt only when the transmit queue is empty.
678897ae9c1SDavid du Colombier  */
679897ae9c1SDavid du Colombier static void
transmit(Ether * ether)680897ae9c1SDavid du Colombier transmit(Ether *ether)
681897ae9c1SDavid du Colombier {
682897ae9c1SDavid du Colombier 	int i, kick, len;
683897ae9c1SDavid du Colombier 	Block *b;
684897ae9c1SDavid du Colombier 	Ctlr *ctlr = ether->ctlr;
685897ae9c1SDavid du Colombier 	Gbereg *reg = ctlr->reg;
686897ae9c1SDavid du Colombier 	Tx *t;
687897ae9c1SDavid du Colombier 
688897ae9c1SDavid du Colombier 	ethercheck(ether);
689897ae9c1SDavid du Colombier 	ilock(ctlr);
690897ae9c1SDavid du Colombier 	txreplenish(ether);			/* reap old packets */
691897ae9c1SDavid du Colombier 
6922f205b96SDavid du Colombier 	/* queue new packets; use at most half the tx descs to avoid livelock */
693897ae9c1SDavid du Colombier 	kick = 0;
694897ae9c1SDavid du Colombier 	for (i = Ntx/2 - 2; i > 0; i--) {
6952f205b96SDavid du Colombier 		t = &ctlr->tx[ctlr->txhead];	/* *t is uncached */
696897ae9c1SDavid du Colombier 		assert(((uintptr)t & (Descralign - 1)) == 0);
6972f205b96SDavid du Colombier 		if(t->cs & TCSdmaown) {		/* descriptor busy? */
698897ae9c1SDavid du Colombier 			ctlr->txringfull++;
699897ae9c1SDavid du Colombier 			break;
700897ae9c1SDavid du Colombier 		}
701897ae9c1SDavid du Colombier 
702897ae9c1SDavid du Colombier 		b = qget(ether->oq);		/* outgoing packet? */
703897ae9c1SDavid du Colombier 		if (b == nil)
704897ae9c1SDavid du Colombier 			break;
705897ae9c1SDavid du Colombier 		len = BLEN(b);
706897ae9c1SDavid du Colombier 		if(len < ether->minmtu || len > ether->maxmtu) {
707897ae9c1SDavid du Colombier 			freeb(b);
708897ae9c1SDavid du Colombier 			continue;
709897ae9c1SDavid du Colombier 		}
710897ae9c1SDavid du Colombier 		ctlr->txb[ctlr->txhead] = b;
711897ae9c1SDavid du Colombier 
712897ae9c1SDavid du Colombier 		/* make sure the whole packet is in memory */
713897ae9c1SDavid du Colombier 		cachedwbse(b->rp, len);
714897ae9c1SDavid du Colombier 		l2cacheuwbse(b->rp, len);
715897ae9c1SDavid du Colombier 
716897ae9c1SDavid du Colombier 		/* set up the transmit descriptor */
717897ae9c1SDavid du Colombier 		t->buf = PADDR(b->rp);
718897ae9c1SDavid du Colombier 		t->countchk = len << 16;
7192f205b96SDavid du Colombier 		coherence();
720897ae9c1SDavid du Colombier 
721897ae9c1SDavid du Colombier 		/* and fire */
722897ae9c1SDavid du Colombier 		t->cs = TCSpadding | TCSfirst | TCSlast | TCSdmaown |
723897ae9c1SDavid du Colombier 			TCSenableintr;
7242f205b96SDavid du Colombier 		coherence();
725897ae9c1SDavid du Colombier 
726897ae9c1SDavid du Colombier 		kick++;
727897ae9c1SDavid du Colombier 		ctlr->txhead = NEXT(ctlr->txhead, Ntx);
728897ae9c1SDavid du Colombier 	}
729897ae9c1SDavid du Colombier 	if (kick) {
730897ae9c1SDavid du Colombier 		txkick(ctlr);
731897ae9c1SDavid du Colombier 
732897ae9c1SDavid du Colombier 		reg->irqmask  |= Itxendq(Qno);
733897ae9c1SDavid du Colombier 		reg->irqemask |= IEtxerrq(Qno) | IEtxunderrun;
734897ae9c1SDavid du Colombier 	}
735897ae9c1SDavid du Colombier 	iunlock(ctlr);
736897ae9c1SDavid du Colombier }
737897ae9c1SDavid du Colombier 
738897ae9c1SDavid du Colombier static void
dumprxdescs(Ctlr * ctlr)739897ae9c1SDavid du Colombier dumprxdescs(Ctlr *ctlr)
740897ae9c1SDavid du Colombier {
741897ae9c1SDavid du Colombier 	int i;
742897ae9c1SDavid du Colombier 	Gbereg *reg = ctlr->reg;
743897ae9c1SDavid du Colombier 
744897ae9c1SDavid du Colombier 	iprint("\nrxhead %d rxtail %d; txcdp %#p rxcdp %#p\n",
745897ae9c1SDavid du Colombier 		ctlr->rxhead, ctlr->rxtail, reg->tcqdp[Qno], reg->crdp[Qno].r);
74628620197SDavid du Colombier 	for (i = 0; i < Nrx; i++) {
747897ae9c1SDavid du Colombier 		iprint("rxb %d @ %#p: %#p\n", i, &ctlr->rxb[i], ctlr->rxb[i]);
74828620197SDavid du Colombier 		delay(50);
74928620197SDavid du Colombier 	}
75028620197SDavid du Colombier 	for (i = 0; i < Nrx; i++) {
751897ae9c1SDavid du Colombier 		iprint("rx %d @ %#p: cs %#lux countsize %lud buf %#lux next %#lux\n",
752897ae9c1SDavid du Colombier 			i, &ctlr->rx[i], ctlr->rx[i].cs,
753897ae9c1SDavid du Colombier 			ctlr->rx[i].countsize >> 3, ctlr->rx[i].buf,
754897ae9c1SDavid du Colombier 			ctlr->rx[i].next);
75528620197SDavid du Colombier 		delay(50);
75628620197SDavid du Colombier 	}
757897ae9c1SDavid du Colombier 	delay(1000);
758897ae9c1SDavid du Colombier }
759897ae9c1SDavid du Colombier 
760897ae9c1SDavid du Colombier static int
gotinput(void * ctlr)761897ae9c1SDavid du Colombier gotinput(void* ctlr)
762897ae9c1SDavid du Colombier {
763897ae9c1SDavid du Colombier 	return ((Ctlr*)ctlr)->haveinput != 0;
764897ae9c1SDavid du Colombier }
765897ae9c1SDavid du Colombier 
766897ae9c1SDavid du Colombier /*
767897ae9c1SDavid du Colombier  * process any packets in the input ring.
768897ae9c1SDavid du Colombier  * also sum mib stats frequently to avoid the overflow
769897ae9c1SDavid du Colombier  * mentioned in the errata.
770897ae9c1SDavid du Colombier  */
771897ae9c1SDavid du Colombier static void
rcvproc(void * arg)772897ae9c1SDavid du Colombier rcvproc(void* arg)
773897ae9c1SDavid du Colombier {
774897ae9c1SDavid du Colombier 	Ctlr *ctlr;
775897ae9c1SDavid du Colombier 	Ether *ether;
776897ae9c1SDavid du Colombier 
777897ae9c1SDavid du Colombier 	ether = arg;
778897ae9c1SDavid du Colombier 	ctlr = ether->ctlr;
779897ae9c1SDavid du Colombier 	for(;;){
780897ae9c1SDavid du Colombier 		tsleep(&ctlr->rrendez, gotinput, ctlr, 10*1000);
781897ae9c1SDavid du Colombier 		ilock(ctlr);
782897ae9c1SDavid du Colombier 		getmibstats(ctlr);
783897ae9c1SDavid du Colombier 		if (ctlr->haveinput) {
784897ae9c1SDavid du Colombier 			ctlr->haveinput = 0;
785897ae9c1SDavid du Colombier 			iunlock(ctlr);
786897ae9c1SDavid du Colombier 			receive(ether);
787897ae9c1SDavid du Colombier 		} else
788897ae9c1SDavid du Colombier 			iunlock(ctlr);
789897ae9c1SDavid du Colombier 	}
790897ae9c1SDavid du Colombier }
791897ae9c1SDavid du Colombier 
792897ae9c1SDavid du Colombier static void
interrupt(Ureg *,void * arg)793897ae9c1SDavid du Colombier interrupt(Ureg*, void *arg)
794897ae9c1SDavid du Colombier {
795897ae9c1SDavid du Colombier 	ulong irq, irqe, handled;
796897ae9c1SDavid du Colombier 	Ether *ether = arg;
797897ae9c1SDavid du Colombier 	Ctlr *ctlr = ether->ctlr;
798897ae9c1SDavid du Colombier 	Gbereg *reg = ctlr->reg;
799897ae9c1SDavid du Colombier 
800897ae9c1SDavid du Colombier 	handled = 0;
801897ae9c1SDavid du Colombier 	irq = reg->irq;
802897ae9c1SDavid du Colombier 	irqe = reg->irqe;
80328620197SDavid du Colombier 	reg->irqe = 0;				/* extinguish intr causes */
804897ae9c1SDavid du Colombier 	reg->irq = 0;				/* extinguish intr causes */
805897ae9c1SDavid du Colombier 	ethercheck(ether);
806897ae9c1SDavid du Colombier 
80728620197SDavid du Colombier 	if(irq & (Irx | Irxbufferq(Qno))) {
808897ae9c1SDavid du Colombier 		/*
809897ae9c1SDavid du Colombier 		 * letting a kproc process the input takes far less real time
810897ae9c1SDavid du Colombier 		 * than doing it all at interrupt level.
811897ae9c1SDavid du Colombier 		 */
812897ae9c1SDavid du Colombier 		ctlr->haveinput = 1;
813897ae9c1SDavid du Colombier 		wakeup(&ctlr->rrendez);
81428620197SDavid du Colombier 		irq &= ~(Irx | Irxbufferq(Qno));
815897ae9c1SDavid du Colombier 		handled++;
816897ae9c1SDavid du Colombier 	} else
817897ae9c1SDavid du Colombier 		rxkick(ctlr);
818897ae9c1SDavid du Colombier 
819897ae9c1SDavid du Colombier 	if(irq & Itxendq(Qno)) {		/* transmit ring empty? */
820897ae9c1SDavid du Colombier 		reg->irqmask  &= ~Itxendq(Qno);	/* prevent more interrupts */
821897ae9c1SDavid du Colombier 		reg->irqemask &= ~(IEtxerrq(Qno) | IEtxunderrun);
822897ae9c1SDavid du Colombier 		transmit(ether);
82328620197SDavid du Colombier 		irq &= ~Itxendq(Qno);
824897ae9c1SDavid du Colombier 		handled++;
825897ae9c1SDavid du Colombier 	}
826897ae9c1SDavid du Colombier 
827897ae9c1SDavid du Colombier 	if(irqe & IEsum) {
828897ae9c1SDavid du Colombier 		/*
829897ae9c1SDavid du Colombier 		 * IElinkchg appears to only be set when unplugging.
830897ae9c1SDavid du Colombier 		 * autonegotiation is likely not done yet, so linkup not valid,
831897ae9c1SDavid du Colombier 		 * thus we note the link change here, and check for
832897ae9c1SDavid du Colombier 		 * that and autonegotiation done below.
833897ae9c1SDavid du Colombier 		 */
834897ae9c1SDavid du Colombier 		if(irqe & IEphystschg) {
835897ae9c1SDavid du Colombier 			ether->link = (reg->ps0 & PS0linkup) != 0;
836897ae9c1SDavid du Colombier 			ether->linkchg = 1;
837897ae9c1SDavid du Colombier 		}
838897ae9c1SDavid du Colombier 		if(irqe & IEtxerrq(Qno))
839897ae9c1SDavid du Colombier 			ether->oerrs++;
840897ae9c1SDavid du Colombier 		if(irqe & IErxoverrun)
841897ae9c1SDavid du Colombier 			ether->overflows++;
842897ae9c1SDavid du Colombier 		if(irqe & IEtxunderrun)
843897ae9c1SDavid du Colombier 			ctlr->txunderrun++;
844897ae9c1SDavid du Colombier 		if(irqe & (IEphystschg | IEtxerrq(Qno) | IErxoverrun |
845897ae9c1SDavid du Colombier 		    IEtxunderrun))
846897ae9c1SDavid du Colombier 			handled++;
847897ae9c1SDavid du Colombier 	}
848897ae9c1SDavid du Colombier 	if (irq & Isum) {
8492f205b96SDavid du Colombier 		if (irq & Irxerr) {  /* nil desc. ptr. or desc. owned by cpu */
850897ae9c1SDavid du Colombier 			ether->buffs++;		/* approx. error */
85128620197SDavid du Colombier 
85228620197SDavid du Colombier 			/* if the input ring is full, drain it */
85328620197SDavid du Colombier 			ctlr->haveinput = 1;
85428620197SDavid du Colombier 			wakeup(&ctlr->rrendez);
855897ae9c1SDavid du Colombier 		}
856897ae9c1SDavid du Colombier 		if(irq & (Irxerr | Irxerrq(Qno)))
857897ae9c1SDavid du Colombier 			handled++;
85828620197SDavid du Colombier 		irq  &= ~(Irxerr | Irxerrq(Qno));
859897ae9c1SDavid du Colombier 	}
860897ae9c1SDavid du Colombier 
861897ae9c1SDavid du Colombier 	if(ether->linkchg && (reg->ps1 & PS1an_done)) {
862897ae9c1SDavid du Colombier 		handled++;
863897ae9c1SDavid du Colombier 		ether->link = (reg->ps0 & PS0linkup) != 0;
864897ae9c1SDavid du Colombier 		ether->linkchg = 0;
865897ae9c1SDavid du Colombier 	}
866897ae9c1SDavid du Colombier 	ctlr->newintrs++;
867897ae9c1SDavid du Colombier 
868897ae9c1SDavid du Colombier 	if (!handled) {
869897ae9c1SDavid du Colombier 		irq  &= ~Isum;
870897ae9c1SDavid du Colombier 		irqe &= ~IEtxbufferq(Qno);
871897ae9c1SDavid du Colombier 		if (irq == 0 && irqe == 0) {
872897ae9c1SDavid du Colombier 			/* seems to be triggered by continuous output */
873897ae9c1SDavid du Colombier 			// iprint("ether1116: spurious interrupt\n");
874897ae9c1SDavid du Colombier 		} else
875897ae9c1SDavid du Colombier 			iprint("ether1116: interrupt cause unknown; "
876897ae9c1SDavid du Colombier 				"irq %#lux irqe %#lux\n", irq, irqe);
877897ae9c1SDavid du Colombier 	}
878897ae9c1SDavid du Colombier 	intrclear(Irqlo, ether->irq);
879897ae9c1SDavid du Colombier }
880897ae9c1SDavid du Colombier 
881897ae9c1SDavid du Colombier void
promiscuous(void * arg,int on)882897ae9c1SDavid du Colombier promiscuous(void *arg, int on)
883897ae9c1SDavid du Colombier {
884897ae9c1SDavid du Colombier 	Ether *ether = arg;
885897ae9c1SDavid du Colombier 	Ctlr *ctlr = ether->ctlr;
886897ae9c1SDavid du Colombier 	Gbereg *reg = ctlr->reg;
887897ae9c1SDavid du Colombier 
888897ae9c1SDavid du Colombier 	ilock(ctlr);
889897ae9c1SDavid du Colombier 	ether->prom = on;
890897ae9c1SDavid du Colombier 	if(on)
891897ae9c1SDavid du Colombier 		reg->portcfg |= PCFGupromisc;
892897ae9c1SDavid du Colombier 	else
893897ae9c1SDavid du Colombier 		reg->portcfg &= ~PCFGupromisc;
894897ae9c1SDavid du Colombier 	iunlock(ctlr);
895897ae9c1SDavid du Colombier }
896897ae9c1SDavid du Colombier 
897897ae9c1SDavid du Colombier void
multicast(void *,uchar *,int)898897ae9c1SDavid du Colombier multicast(void *, uchar *, int)
899897ae9c1SDavid du Colombier {
900897ae9c1SDavid du Colombier 	/* nothing to do; we always accept multicast */
901897ae9c1SDavid du Colombier }
902897ae9c1SDavid du Colombier 
903897ae9c1SDavid du Colombier static void quiesce(Gbereg *reg);
904897ae9c1SDavid du Colombier 
905897ae9c1SDavid du Colombier static void
shutdown(Ether * ether)906897ae9c1SDavid du Colombier shutdown(Ether *ether)
907897ae9c1SDavid du Colombier {
90828620197SDavid du Colombier 	int i;
909897ae9c1SDavid du Colombier 	Ctlr *ctlr = ether->ctlr;
910897ae9c1SDavid du Colombier 	Gbereg *reg = ctlr->reg;
911897ae9c1SDavid du Colombier 
912897ae9c1SDavid du Colombier 	ilock(ctlr);
913897ae9c1SDavid du Colombier 	quiesce(reg);
91428620197SDavid du Colombier 	reg->euc |= Portreset;
91528620197SDavid du Colombier 	coherence();
91628620197SDavid du Colombier 	iunlock(ctlr);
91728620197SDavid du Colombier 	delay(100);
91828620197SDavid du Colombier 	ilock(ctlr);
91928620197SDavid du Colombier 	reg->euc &= ~Portreset;
92028620197SDavid du Colombier 	coherence();
92128620197SDavid du Colombier 	delay(20);
92228620197SDavid du Colombier 
923897ae9c1SDavid du Colombier 	reg->psc0 = 0;			/* no PSC0porton */
924897ae9c1SDavid du Colombier 	reg->psc1 |= PSC1portreset;
925897ae9c1SDavid du Colombier 	coherence();
92628620197SDavid du Colombier 	delay(50);
92728620197SDavid du Colombier 	reg->psc1 &= ~PSC1portreset;
92828620197SDavid du Colombier 	coherence();
92928620197SDavid du Colombier 
93028620197SDavid du Colombier 	for (i = 0; i < nelem(reg->tcqdp); i++)
93128620197SDavid du Colombier 		reg->tcqdp[i] = 0;
93228620197SDavid du Colombier 	for (i = 0; i < nelem(reg->crdp); i++)
93328620197SDavid du Colombier 		reg->crdp[i].r = 0;
93428620197SDavid du Colombier 	coherence();
93528620197SDavid du Colombier 
93628620197SDavid du Colombier 	iunlock(ctlr);
937897ae9c1SDavid du Colombier }
938897ae9c1SDavid du Colombier 
939897ae9c1SDavid du Colombier enum {
940897ae9c1SDavid du Colombier 	CMjumbo,
941897ae9c1SDavid du Colombier };
942897ae9c1SDavid du Colombier 
943897ae9c1SDavid du Colombier static Cmdtab ctlmsg[] = {
944897ae9c1SDavid du Colombier 	CMjumbo,	"jumbo",	2,
945897ae9c1SDavid du Colombier };
946897ae9c1SDavid du Colombier 
947897ae9c1SDavid du Colombier long
ctl(Ether * e,void * p,long n)948897ae9c1SDavid du Colombier ctl(Ether *e, void *p, long n)
949897ae9c1SDavid du Colombier {
950897ae9c1SDavid du Colombier 	Cmdbuf *cb;
951897ae9c1SDavid du Colombier 	Cmdtab *ct;
952897ae9c1SDavid du Colombier 	Ctlr *ctlr = e->ctlr;
953897ae9c1SDavid du Colombier 	Gbereg *reg = ctlr->reg;
954897ae9c1SDavid du Colombier 
955897ae9c1SDavid du Colombier 	cb = parsecmd(p, n);
956897ae9c1SDavid du Colombier 	if(waserror()) {
957897ae9c1SDavid du Colombier 		free(cb);
958897ae9c1SDavid du Colombier 		nexterror();
959897ae9c1SDavid du Colombier 	}
960897ae9c1SDavid du Colombier 
961897ae9c1SDavid du Colombier 	ct = lookupcmd(cb, ctlmsg, nelem(ctlmsg));
962897ae9c1SDavid du Colombier 	switch(ct->index) {
963897ae9c1SDavid du Colombier 	case CMjumbo:
964897ae9c1SDavid du Colombier 		if(strcmp(cb->f[1], "on") == 0) {
965897ae9c1SDavid du Colombier 			/* incoming packet queue doesn't expect jumbo frames */
966897ae9c1SDavid du Colombier 			error("jumbo disabled");
967897ae9c1SDavid du Colombier 			reg->psc0 = (reg->psc0 & ~PSC0mrumask) |
968897ae9c1SDavid du Colombier 				PSC0mru(PSC0mru9022);
969897ae9c1SDavid du Colombier 			e->maxmtu = 9022;
970897ae9c1SDavid du Colombier 		} else if(strcmp(cb->f[1], "off") == 0) {
971897ae9c1SDavid du Colombier 			reg->psc0 = (reg->psc0 & ~PSC0mrumask) |
972897ae9c1SDavid du Colombier 				PSC0mru(PSC0mru1522);
973897ae9c1SDavid du Colombier 			e->maxmtu = ETHERMAXTU;
974897ae9c1SDavid du Colombier 		} else
975897ae9c1SDavid du Colombier 			error(Ebadctl);
976897ae9c1SDavid du Colombier 		break;
977897ae9c1SDavid du Colombier 	default:
978897ae9c1SDavid du Colombier 		error(Ebadctl);
979897ae9c1SDavid du Colombier 		break;
980897ae9c1SDavid du Colombier 	}
981897ae9c1SDavid du Colombier 	free(cb);
982897ae9c1SDavid du Colombier 	poperror();
983897ae9c1SDavid du Colombier 	return n;
984897ae9c1SDavid du Colombier }
985897ae9c1SDavid du Colombier 
986897ae9c1SDavid du Colombier /*
987897ae9c1SDavid du Colombier  * phy/mii goo
988897ae9c1SDavid du Colombier  */
989897ae9c1SDavid du Colombier 
990897ae9c1SDavid du Colombier static int
smibusywait(Gbereg * reg,ulong waitbit)991897ae9c1SDavid du Colombier smibusywait(Gbereg *reg, ulong waitbit)
992897ae9c1SDavid du Colombier {
993897ae9c1SDavid du Colombier 	ulong timeout, smi_reg;
994897ae9c1SDavid du Colombier 
995897ae9c1SDavid du Colombier 	timeout = PhysmiTimeout;
996897ae9c1SDavid du Colombier 	/* wait till the SMI is not busy */
997897ae9c1SDavid du Colombier 	do {
998897ae9c1SDavid du Colombier 		/* read smi register */
999897ae9c1SDavid du Colombier 		smi_reg = reg->smi;
1000897ae9c1SDavid du Colombier 		if (timeout-- == 0) {
1001897ae9c1SDavid du Colombier 			MIIDBG("SMI busy timeout\n");
1002897ae9c1SDavid du Colombier 			return -1;
1003897ae9c1SDavid du Colombier 		}
1004897ae9c1SDavid du Colombier //		delay(1);
1005897ae9c1SDavid du Colombier 	} while (smi_reg & waitbit);
1006897ae9c1SDavid du Colombier 	return 0;
1007897ae9c1SDavid du Colombier }
1008897ae9c1SDavid du Colombier 
1009897ae9c1SDavid du Colombier static int
miird(Mii * mii,int pa,int ra)1010897ae9c1SDavid du Colombier miird(Mii *mii, int pa, int ra)
1011897ae9c1SDavid du Colombier {
1012897ae9c1SDavid du Colombier 	ulong smi_reg, timeout;
1013897ae9c1SDavid du Colombier 	Gbereg *reg;
1014897ae9c1SDavid du Colombier 
101528620197SDavid du Colombier 	reg = ((Ctlr*)mii->ctlr)->reg;
1016897ae9c1SDavid du Colombier 
1017897ae9c1SDavid du Colombier 	/* check params */
1018897ae9c1SDavid du Colombier 	if ((pa<<Physmiaddroff) & ~Physmiaddrmask ||
1019897ae9c1SDavid du Colombier 	    (ra<<SmiRegaddroff) & ~SmiRegaddrmask)
1020897ae9c1SDavid du Colombier 		return -1;
1021897ae9c1SDavid du Colombier 
1022897ae9c1SDavid du Colombier 	smibusywait(reg, PhysmiBusy);
1023897ae9c1SDavid du Colombier 
1024897ae9c1SDavid du Colombier 	/* fill the phy address and register offset and read opcode */
1025897ae9c1SDavid du Colombier 	reg->smi = pa << Physmiaddroff | ra << SmiRegaddroff | PhysmiopRd;
1026897ae9c1SDavid du Colombier 	coherence();
1027897ae9c1SDavid du Colombier 
1028897ae9c1SDavid du Colombier 	/* wait til read value is ready */
1029897ae9c1SDavid du Colombier 	timeout = PhysmiTimeout;
1030897ae9c1SDavid du Colombier 	do {
1031897ae9c1SDavid du Colombier 		smi_reg = reg->smi;
1032897ae9c1SDavid du Colombier 		if (timeout-- == 0) {
1033897ae9c1SDavid du Colombier 			MIIDBG("SMI read-valid timeout\n");
1034897ae9c1SDavid du Colombier 			return -1;
1035897ae9c1SDavid du Colombier 		}
1036897ae9c1SDavid du Colombier 	} while (!(smi_reg & PhysmiReadok));
1037897ae9c1SDavid du Colombier 
1038897ae9c1SDavid du Colombier 	/* Wait for the data to update in the SMI register */
1039897ae9c1SDavid du Colombier 	for (timeout = 0; timeout < PhysmiTimeout; timeout++)
1040897ae9c1SDavid du Colombier 		;
1041897ae9c1SDavid du Colombier 	return reg->smi & Physmidatamask;
1042897ae9c1SDavid du Colombier }
1043897ae9c1SDavid du Colombier 
1044897ae9c1SDavid du Colombier static int
miiwr(Mii * mii,int pa,int ra,int v)1045897ae9c1SDavid du Colombier miiwr(Mii *mii, int pa, int ra, int v)
1046897ae9c1SDavid du Colombier {
1047897ae9c1SDavid du Colombier 	Gbereg *reg;
1048897ae9c1SDavid du Colombier 	ulong smi_reg;
1049897ae9c1SDavid du Colombier 
105028620197SDavid du Colombier 	reg = ((Ctlr*)mii->ctlr)->reg;
1051897ae9c1SDavid du Colombier 
1052897ae9c1SDavid du Colombier 	/* check params */
1053897ae9c1SDavid du Colombier 	if (((pa<<Physmiaddroff) & ~Physmiaddrmask) ||
1054897ae9c1SDavid du Colombier 	    ((ra<<SmiRegaddroff) & ~SmiRegaddrmask))
1055897ae9c1SDavid du Colombier 		return -1;
1056897ae9c1SDavid du Colombier 
1057897ae9c1SDavid du Colombier 	smibusywait(reg, PhysmiBusy);
1058897ae9c1SDavid du Colombier 
1059897ae9c1SDavid du Colombier 	/* fill the phy address and register offset and read opcode */
1060897ae9c1SDavid du Colombier 	smi_reg = v << Physmidataoff | pa << Physmiaddroff | ra << SmiRegaddroff;
1061897ae9c1SDavid du Colombier 	reg->smi = smi_reg & ~PhysmiopRd;
1062897ae9c1SDavid du Colombier 	coherence();
1063897ae9c1SDavid du Colombier 	return 0;
1064897ae9c1SDavid du Colombier }
1065897ae9c1SDavid du Colombier 
106628620197SDavid du Colombier #define MIIMODEL(idr2)	(((idr2) >> 4) & MASK(6))
106728620197SDavid du Colombier 
106828620197SDavid du Colombier enum {
106928620197SDavid du Colombier 	Hacknone,
107028620197SDavid du Colombier 	Hackdual,
107128620197SDavid du Colombier 
107228620197SDavid du Colombier 	Ouimarvell	= 0x005043,
107328620197SDavid du Colombier 
107428620197SDavid du Colombier 	/* idr2 mii/phy model numbers */
107528620197SDavid du Colombier 	Phy1000		= 0x00,		/* 88E1000 Gb */
107628620197SDavid du Colombier 	Phy1011		= 0x02,		/* 88E1011 Gb */
107728620197SDavid du Colombier 	Phy1000_3	= 0x03,		/* 88E1000 Gb */
107828620197SDavid du Colombier 	Phy1000s	= 0x04,		/* 88E1000S Gb */
107928620197SDavid du Colombier 	Phy1000_5	= 0x05,		/* 88E1000 Gb */
108028620197SDavid du Colombier 	Phy1000_6	= 0x06,		/* 88E1000 Gb */
108128620197SDavid du Colombier 	Phy3082		= 0x08,		/* 88E3082 10/100 */
108228620197SDavid du Colombier 	Phy1112		= 0x09,		/* 88E1112 Gb */
108328620197SDavid du Colombier 	Phy1121r	= 0x0b,		/* says the 1121r manual */
108428620197SDavid du Colombier 	Phy1149		= 0x0b,		/* 88E1149 Gb */
108528620197SDavid du Colombier 	Phy1111		= 0x0c,		/* 88E1111 Gb */
108628620197SDavid du Colombier 	Phy1116		= 0x21,		/* 88E1116 Gb */
108728620197SDavid du Colombier 	Phy1116r	= 0x24,		/* 88E1116R Gb */
108828620197SDavid du Colombier 	Phy1118		= 0x22,		/* 88E1118 Gb */
108928620197SDavid du Colombier 	Phy3016		= 0x26,		/* 88E3016 10/100 */
109028620197SDavid du Colombier };
109128620197SDavid du Colombier 
109228620197SDavid du Colombier static int hackflavour;
109328620197SDavid du Colombier 
109428620197SDavid du Colombier /*
109528620197SDavid du Colombier  * on openrd, ether0's phy has address 8, ether1's is ether0's 24.
109628620197SDavid du Colombier  * on guruplug, ether0's is phy 0 and ether1's is ether0's phy 1.
109728620197SDavid du Colombier  */
109828620197SDavid du Colombier int
mymii(Mii * mii,int mask)109928620197SDavid du Colombier mymii(Mii* mii, int mask)
110028620197SDavid du Colombier {
110128620197SDavid du Colombier 	Ctlr *ctlr;
110228620197SDavid du Colombier 	MiiPhy *miiphy;
110328620197SDavid du Colombier 	int bit, ctlrno, oui, model, phyno, r, rmask;
110428620197SDavid du Colombier 	static int dualport, phyidx;
110528620197SDavid du Colombier 	static int phynos[NMiiPhy];
110628620197SDavid du Colombier 
110728620197SDavid du Colombier 	ctlr = mii->ctlr;
110828620197SDavid du Colombier 	ctlrno = ctlr->ether->ctlrno;
110928620197SDavid du Colombier 
111028620197SDavid du Colombier 	/* first pass: figure out what kind of phy(s) we have. */
111128620197SDavid du Colombier 	dualport = 0;
111228620197SDavid du Colombier 	if (ctlrno == 0) {
111328620197SDavid du Colombier 		for(phyno = 0; phyno < NMiiPhy; phyno++){
111428620197SDavid du Colombier 			bit = 1<<phyno;
111528620197SDavid du Colombier 			if(!(mask & bit) || mii->mask & bit)
111628620197SDavid du Colombier 				continue;
111728620197SDavid du Colombier 			if(mii->mir(mii, phyno, Bmsr) == -1)
111828620197SDavid du Colombier 				continue;
111928620197SDavid du Colombier 			r = mii->mir(mii, phyno, Phyidr1);
112028620197SDavid du Colombier 			oui = (r & 0x3FFF)<<6;
112128620197SDavid du Colombier 			r = mii->mir(mii, phyno, Phyidr2);
112228620197SDavid du Colombier 			oui |= r>>10;
112328620197SDavid du Colombier 			model = MIIMODEL(r);
112428620197SDavid du Colombier 			if (oui == 0xfffff && model == 0x3f)
112528620197SDavid du Colombier 				continue;
112628620197SDavid du Colombier 			MIIDBG("ctlrno %d phy %d oui %#ux model %#ux\n",
112728620197SDavid du Colombier 				ctlrno, phyno, oui, model);
11284b9994c9SDavid du Colombier 			if (oui == Ouimarvell &&
11294b9994c9SDavid du Colombier 			    (model == Phy1121r || model == Phy1116r))
113028620197SDavid du Colombier 				++dualport;
113128620197SDavid du Colombier 			phynos[phyidx++] = phyno;
113228620197SDavid du Colombier 		}
113328620197SDavid du Colombier 		hackflavour = dualport == 2 && phyidx == 2? Hackdual: Hacknone;
113428620197SDavid du Colombier 		MIIDBG("ether1116: %s-port phy\n",
113528620197SDavid du Colombier 			hackflavour == Hackdual? "dual": "single");
113628620197SDavid du Colombier 	}
113728620197SDavid du Colombier 
113828620197SDavid du Colombier 	/*
113928620197SDavid du Colombier 	 * Probe through mii for PHYs in mask;
114028620197SDavid du Colombier 	 * return the mask of those found in the current probe.
114128620197SDavid du Colombier 	 * If the PHY has not already been probed, update
114228620197SDavid du Colombier 	 * the Mii information.
114328620197SDavid du Colombier 	 */
114428620197SDavid du Colombier 	rmask = 0;
114528620197SDavid du Colombier 	if (hackflavour == Hackdual && ctlrno < phyidx) {
114628620197SDavid du Colombier 		/*
114728620197SDavid du Colombier 		 * openrd, guruplug or the like: use ether0's phys.
114828620197SDavid du Colombier 		 * this is a nasty hack, but so is the hardware.
114928620197SDavid du Colombier 		 */
115028620197SDavid du Colombier 		MIIDBG("ctlrno %d using ctlrno 0's phyno %d\n",
115128620197SDavid du Colombier 			ctlrno, phynos[ctlrno]);
115228620197SDavid du Colombier 		ctlr->mii = mii = ctlrs[0]->mii;
115328620197SDavid du Colombier 		mask = 1 << phynos[ctlrno];
115428620197SDavid du Colombier 		mii->mask = ~mask;
115528620197SDavid du Colombier 	}
115628620197SDavid du Colombier 	for(phyno = 0; phyno < NMiiPhy; phyno++){
115728620197SDavid du Colombier 		bit = 1<<phyno;
115828620197SDavid du Colombier 		if(!(mask & bit))
115928620197SDavid du Colombier 			continue;
116028620197SDavid du Colombier 		if(mii->mask & bit){
116128620197SDavid du Colombier 			rmask |= bit;
116228620197SDavid du Colombier 			continue;
116328620197SDavid du Colombier 		}
116428620197SDavid du Colombier 		if(mii->mir(mii, phyno, Bmsr) == -1)
116528620197SDavid du Colombier 			continue;
116628620197SDavid du Colombier 		r = mii->mir(mii, phyno, Phyidr1);
116728620197SDavid du Colombier 		oui = (r & 0x3FFF)<<6;
116828620197SDavid du Colombier 		r = mii->mir(mii, phyno, Phyidr2);
116928620197SDavid du Colombier 		oui |= r>>10;
117028620197SDavid du Colombier 		if(oui == 0xFFFFF || oui == 0)
117128620197SDavid du Colombier 			continue;
117228620197SDavid du Colombier 
117328620197SDavid du Colombier 		if((miiphy = malloc(sizeof(MiiPhy))) == nil)
117428620197SDavid du Colombier 			continue;
117528620197SDavid du Colombier 		miiphy->mii = mii;
117628620197SDavid du Colombier 		miiphy->oui = oui;
117728620197SDavid du Colombier 		miiphy->phyno = phyno;
117828620197SDavid du Colombier 
117928620197SDavid du Colombier 		miiphy->anar = ~0;
118028620197SDavid du Colombier 		miiphy->fc = ~0;
118128620197SDavid du Colombier 		miiphy->mscr = ~0;
118228620197SDavid du Colombier 
118328620197SDavid du Colombier 		mii->phy[phyno] = miiphy;
118428620197SDavid du Colombier 		if(ctlrno == 0 || hackflavour != Hackdual && mii->curphy == nil)
118528620197SDavid du Colombier 			mii->curphy = miiphy;
118628620197SDavid du Colombier 		mii->mask |= bit;
118728620197SDavid du Colombier 		mii->nphy++;
118828620197SDavid du Colombier 
118928620197SDavid du Colombier 		rmask |= bit;
119028620197SDavid du Colombier 	}
119128620197SDavid du Colombier 	return rmask;
119228620197SDavid du Colombier }
119328620197SDavid du Colombier 
1194897ae9c1SDavid du Colombier static int
kirkwoodmii(Ether * ether)1195897ae9c1SDavid du Colombier kirkwoodmii(Ether *ether)
1196897ae9c1SDavid du Colombier {
1197897ae9c1SDavid du Colombier 	int i;
1198897ae9c1SDavid du Colombier 	Ctlr *ctlr;
1199897ae9c1SDavid du Colombier 	MiiPhy *phy;
1200897ae9c1SDavid du Colombier 
1201897ae9c1SDavid du Colombier 	MIIDBG("mii\n");
1202897ae9c1SDavid du Colombier 	ctlr = ether->ctlr;
1203897ae9c1SDavid du Colombier 	if((ctlr->mii = malloc(sizeof(Mii))) == nil)
1204897ae9c1SDavid du Colombier 		return -1;
1205897ae9c1SDavid du Colombier 	ctlr->mii->ctlr = ctlr;
1206897ae9c1SDavid du Colombier 	ctlr->mii->mir = miird;
1207897ae9c1SDavid du Colombier 	ctlr->mii->miw = miiwr;
1208897ae9c1SDavid du Colombier 
120928620197SDavid du Colombier 	if(mymii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){
1210897ae9c1SDavid du Colombier 		print("#l%d: ether1116: init mii failure\n", ether->ctlrno);
1211897ae9c1SDavid du Colombier 		free(ctlr->mii);
1212897ae9c1SDavid du Colombier 		ctlr->mii = nil;
1213897ae9c1SDavid du Colombier 		return -1;
1214897ae9c1SDavid du Colombier 	}
1215897ae9c1SDavid du Colombier 
1216897ae9c1SDavid du Colombier 	/* oui 005043 is marvell */
1217897ae9c1SDavid du Colombier 	MIIDBG("oui %#X phyno %d\n", phy->oui, phy->phyno);
12184b9994c9SDavid du Colombier 	// TODO: does this make sense? shouldn't each phy be initialised?
121928620197SDavid du Colombier 	if((ctlr->ether->ctlrno == 0 || hackflavour != Hackdual) &&
122028620197SDavid du Colombier 	    miistatus(ctlr->mii) < 0){
1221897ae9c1SDavid du Colombier 		miireset(ctlr->mii);
1222897ae9c1SDavid du Colombier 		MIIDBG("miireset\n");
1223897ae9c1SDavid du Colombier 		if(miiane(ctlr->mii, ~0, 0, ~0) < 0){
1224897ae9c1SDavid du Colombier 			iprint("miiane failed\n");
1225897ae9c1SDavid du Colombier 			return -1;
1226897ae9c1SDavid du Colombier 		}
1227897ae9c1SDavid du Colombier 		MIIDBG("miistatus\n");
1228897ae9c1SDavid du Colombier 		miistatus(ctlr->mii);
1229897ae9c1SDavid du Colombier 		if(miird(ctlr->mii, phy->phyno, Bmsr) & BmsrLs){
1230897ae9c1SDavid du Colombier 			for(i = 0; ; i++){
1231897ae9c1SDavid du Colombier 				if(i > 600){
1232897ae9c1SDavid du Colombier 					iprint("ether1116: autonegotiation failed\n");
1233897ae9c1SDavid du Colombier 					break;
1234897ae9c1SDavid du Colombier 				}
1235897ae9c1SDavid du Colombier 				if(miird(ctlr->mii, phy->phyno, Bmsr) & BmsrAnc)
1236897ae9c1SDavid du Colombier 					break;
1237897ae9c1SDavid du Colombier 				delay(10);
1238897ae9c1SDavid du Colombier 			}
1239897ae9c1SDavid du Colombier 			if(miistatus(ctlr->mii) < 0)
1240897ae9c1SDavid du Colombier 				iprint("miistatus failed\n");
1241897ae9c1SDavid du Colombier 		}else{
1242897ae9c1SDavid du Colombier 			iprint("ether1116: no link\n");
1243897ae9c1SDavid du Colombier 			phy->speed = 10;	/* simple default */
1244897ae9c1SDavid du Colombier 		}
1245897ae9c1SDavid du Colombier 	}
1246897ae9c1SDavid du Colombier 
1247897ae9c1SDavid du Colombier 	ether->mbps = phy->speed;
124828620197SDavid du Colombier 	MIIDBG("#l%d: kirkwoodmii: fd %d speed %d tfc %d rfc %d\n",
124928620197SDavid du Colombier 		ctlr->port, phy->fd, phy->speed, phy->tfc, phy->rfc);
1250897ae9c1SDavid du Colombier 	MIIDBG("mii done\n");
1251897ae9c1SDavid du Colombier 	return 0;
1252897ae9c1SDavid du Colombier }
1253897ae9c1SDavid du Colombier 
1254897ae9c1SDavid du Colombier enum {						/* PHY register pages */
1255897ae9c1SDavid du Colombier 	Pagcopper,
1256897ae9c1SDavid du Colombier 	Pagfiber,
1257897ae9c1SDavid du Colombier 	Pagrgmii,
1258897ae9c1SDavid du Colombier 	Pagled,
1259897ae9c1SDavid du Colombier 	Pagrsvd1,
1260897ae9c1SDavid du Colombier 	Pagvct,
1261897ae9c1SDavid du Colombier 	Pagtest,
1262897ae9c1SDavid du Colombier 	Pagrsvd2,
1263897ae9c1SDavid du Colombier 	Pagfactest,
1264897ae9c1SDavid du Colombier };
1265897ae9c1SDavid du Colombier 
1266897ae9c1SDavid du Colombier static void
miiregpage(Mii * mii,ulong dev,ulong page)1267897ae9c1SDavid du Colombier miiregpage(Mii *mii, ulong dev, ulong page)
1268897ae9c1SDavid du Colombier {
1269897ae9c1SDavid du Colombier 	miiwr(mii, dev, Eadr, page);
1270897ae9c1SDavid du Colombier }
1271897ae9c1SDavid du Colombier 
1272897ae9c1SDavid du Colombier static int
miiphyinit(Mii * mii)1273897ae9c1SDavid du Colombier miiphyinit(Mii *mii)
1274897ae9c1SDavid du Colombier {
1275897ae9c1SDavid du Colombier 	ulong dev;
1276897ae9c1SDavid du Colombier 	Ctlr *ctlr;
1277897ae9c1SDavid du Colombier 	Gbereg *reg;
1278897ae9c1SDavid du Colombier 
1279897ae9c1SDavid du Colombier 	ctlr = (Ctlr*)mii->ctlr;
1280897ae9c1SDavid du Colombier 	reg = ctlr->reg;
1281897ae9c1SDavid du Colombier 	dev = reg->phy;
1282897ae9c1SDavid du Colombier 	MIIDBG("phy dev addr %lux\n", dev);
1283897ae9c1SDavid du Colombier 
1284897ae9c1SDavid du Colombier 	/* leds link & activity */
1285897ae9c1SDavid du Colombier 	miiregpage(mii, dev, Pagled);
1286897ae9c1SDavid du Colombier 	/* low 4 bits == 1: on - link, blink - activity, off - no link */
1287897ae9c1SDavid du Colombier 	miiwr(mii, dev, Scr, (miird(mii, dev, Scr) & ~0xf) | 1);
1288897ae9c1SDavid du Colombier 
1289897ae9c1SDavid du Colombier 	miiregpage(mii, dev, Pagrgmii);
1290897ae9c1SDavid du Colombier 	miiwr(mii, dev, Scr, miird(mii, dev, Scr) | Rgmiipwrup);
129128620197SDavid du Colombier 	/* must now do a software reset, says the manual */
129228620197SDavid du Colombier 	miireset(ctlr->mii);
1293897ae9c1SDavid du Colombier 
1294897ae9c1SDavid du Colombier 	/* enable RGMII delay on Tx and Rx for CPU port */
1295897ae9c1SDavid du Colombier 	miiwr(mii, dev, Recr, miird(mii, dev, Recr) | Rxtiming | Rxtiming);
129628620197SDavid du Colombier 	/* must now do a software reset, says the manual */
129728620197SDavid du Colombier 	miireset(ctlr->mii);
1298897ae9c1SDavid du Colombier 
1299897ae9c1SDavid du Colombier 	miiregpage(mii, dev, Pagcopper);
1300897ae9c1SDavid du Colombier 	miiwr(mii, dev, Scr,
1301897ae9c1SDavid du Colombier 		(miird(mii, dev, Scr) & ~(Pwrdown|Endetect)) | Mdix);
1302897ae9c1SDavid du Colombier 
1303897ae9c1SDavid du Colombier 	return 0;
1304897ae9c1SDavid du Colombier }
1305897ae9c1SDavid du Colombier 
1306897ae9c1SDavid du Colombier /*
1307897ae9c1SDavid du Colombier  * initialisation
1308897ae9c1SDavid du Colombier  */
1309897ae9c1SDavid du Colombier 
1310897ae9c1SDavid du Colombier static void
quiesce(Gbereg * reg)1311897ae9c1SDavid du Colombier quiesce(Gbereg *reg)
1312897ae9c1SDavid du Colombier {
1313897ae9c1SDavid du Colombier 	ulong v;
1314897ae9c1SDavid du Colombier 
1315897ae9c1SDavid du Colombier 	v = reg->tqc;
1316897ae9c1SDavid du Colombier 	if (v & 0xFF)
1317897ae9c1SDavid du Colombier 		reg->tqc = v << 8;		/* stop active channels */
1318897ae9c1SDavid du Colombier 	v = reg->rqc;
1319897ae9c1SDavid du Colombier 	if (v & 0xFF)
1320897ae9c1SDavid du Colombier 		reg->rqc = v << 8;		/* stop active channels */
1321897ae9c1SDavid du Colombier 	/* wait for all queues to stop */
1322897ae9c1SDavid du Colombier 	while (reg->tqc & 0xFF || reg->rqc & 0xFF)
1323897ae9c1SDavid du Colombier 		;
1324897ae9c1SDavid du Colombier }
1325897ae9c1SDavid du Colombier 
1326897ae9c1SDavid du Colombier static void
p16(uchar * p,ulong v)132728620197SDavid du Colombier p16(uchar *p, ulong v)		/* convert big-endian short to bytes */
1328897ae9c1SDavid du Colombier {
1329897ae9c1SDavid du Colombier 	*p++ = v>>8;
1330897ae9c1SDavid du Colombier 	*p   = v;
1331897ae9c1SDavid du Colombier }
1332897ae9c1SDavid du Colombier 
1333897ae9c1SDavid du Colombier static void
p32(uchar * p,ulong v)133428620197SDavid du Colombier p32(uchar *p, ulong v)		/* convert big-endian long to bytes */
1335897ae9c1SDavid du Colombier {
1336897ae9c1SDavid du Colombier 	*p++ = v>>24;
1337897ae9c1SDavid du Colombier 	*p++ = v>>16;
1338897ae9c1SDavid du Colombier 	*p++ = v>>8;
1339897ae9c1SDavid du Colombier 	*p   = v;
1340897ae9c1SDavid du Colombier }
1341897ae9c1SDavid du Colombier 
1342897ae9c1SDavid du Colombier /*
1343897ae9c1SDavid du Colombier  * set ether->ea from hw mac address,
1344897ae9c1SDavid du Colombier  * configure unicast filtering to accept it.
1345897ae9c1SDavid du Colombier  */
1346897ae9c1SDavid du Colombier void
archetheraddr(Ether * ether,Gbereg * reg,int rxqno)1347897ae9c1SDavid du Colombier archetheraddr(Ether *ether, Gbereg *reg, int rxqno)
1348897ae9c1SDavid du Colombier {
134928620197SDavid du Colombier 	uchar *ea;
1350897ae9c1SDavid du Colombier 	ulong nibble, ucreg, tbloff, regoff;
1351897ae9c1SDavid du Colombier 
135228620197SDavid du Colombier 	ea = ether->ea;
135328620197SDavid du Colombier 	p32(ea,   reg->macah);
135428620197SDavid du Colombier 	p16(ea+4, reg->macal);
135528620197SDavid du Colombier 	if (memcmp(ea, zeroea, sizeof zeroea) == 0 && ether->ctlrno > 0) {
135628620197SDavid du Colombier 		/* hack: use ctlr[0]'s + ctlrno */
135728620197SDavid du Colombier 		memmove(ea, ctlrs[0]->ether->ea, Eaddrlen);
135828620197SDavid du Colombier 		ea[Eaddrlen-1] += ether->ctlrno;
135928620197SDavid du Colombier 		reg->macah = ea[0] << 24 | ea[1] << 16 | ea[2] << 8 | ea[3];
136028620197SDavid du Colombier 		reg->macal = ea[4] <<  8 | ea[5];
136128620197SDavid du Colombier 		coherence();
136228620197SDavid du Colombier 	}
1363897ae9c1SDavid du Colombier 
1364897ae9c1SDavid du Colombier 	/* accept frames on ea */
136528620197SDavid du Colombier 	nibble = ea[5] & 0xf;
1366897ae9c1SDavid du Colombier 	tbloff = nibble / 4;
1367897ae9c1SDavid du Colombier 	regoff = nibble % 4;
1368897ae9c1SDavid du Colombier 
1369897ae9c1SDavid du Colombier 	regoff *= 8;
137028620197SDavid du Colombier 	ucreg = reg->dfut[tbloff] & (0xff << regoff);
1371897ae9c1SDavid du Colombier 	ucreg |= (rxqno << 1 | Pass) << regoff;
1372897ae9c1SDavid du Colombier 	reg->dfut[tbloff] = ucreg;
1373897ae9c1SDavid du Colombier 
1374897ae9c1SDavid du Colombier 	/* accept all multicast too.  set up special & other tables. */
1375897ae9c1SDavid du Colombier 	memset(reg->dfsmt, Qno<<1 | Pass, sizeof reg->dfsmt);
1376897ae9c1SDavid du Colombier 	memset(reg->dfomt, Qno<<1 | Pass, sizeof reg->dfomt);
1377897ae9c1SDavid du Colombier 	coherence();
1378897ae9c1SDavid du Colombier }
1379897ae9c1SDavid du Colombier 
1380897ae9c1SDavid du Colombier static void
cfgdramacc(Gbereg * reg)138128620197SDavid du Colombier cfgdramacc(Gbereg *reg)
138228620197SDavid du Colombier {
138328620197SDavid du Colombier 	memset(reg->harr, 0, sizeof reg->harr);
138428620197SDavid du Colombier 	memset(reg->base, 0, sizeof reg->base);
138528620197SDavid du Colombier 
138628620197SDavid du Colombier 	reg->bare = MASK(6) - MASK(2);	/* disable wins 2-5 */
138728620197SDavid du Colombier 	/* this doesn't make any sense, but it's required */
138828620197SDavid du Colombier 	reg->epap = 3 << 2 | 3;		/* full access for wins 0 & 1 */
138928620197SDavid du Colombier //	reg->epap = 0;		/* no access on access violation for all wins */
139028620197SDavid du Colombier 	coherence();
139128620197SDavid du Colombier 
139228620197SDavid du Colombier 	reg->base[0].base = PHYSDRAM | WINATTR(Attrcs0) | Targdram;
139328620197SDavid du Colombier 	reg->base[0].size = WINSIZE(256*MB);
139428620197SDavid du Colombier 	reg->base[1].base = (PHYSDRAM + 256*MB) | WINATTR(Attrcs1) | Targdram;
139528620197SDavid du Colombier 	reg->base[1].size = WINSIZE(256*MB);
139628620197SDavid du Colombier 	coherence();
139728620197SDavid du Colombier }
139828620197SDavid du Colombier 
139928620197SDavid du Colombier static void
ctlralloc(Ctlr * ctlr)140028620197SDavid du Colombier ctlralloc(Ctlr *ctlr)
1401897ae9c1SDavid du Colombier {
1402897ae9c1SDavid du Colombier 	int i;
1403897ae9c1SDavid du Colombier 	Block *b;
1404897ae9c1SDavid du Colombier 	Rx *r;
1405897ae9c1SDavid du Colombier 	Tx *t;
1406897ae9c1SDavid du Colombier 
1407897ae9c1SDavid du Colombier 	ilock(&freeblocks);
1408897ae9c1SDavid du Colombier 	for(i = 0; i < Nrxblks; i++) {
1409897ae9c1SDavid du Colombier 		b = iallocb(Rxblklen+Bufalign-1);
1410897ae9c1SDavid du Colombier 		if(b == nil) {
1411897ae9c1SDavid du Colombier 			iprint("ether1116: no memory for rx buffers\n");
1412897ae9c1SDavid du Colombier 			break;
1413897ae9c1SDavid du Colombier 		}
1414897ae9c1SDavid du Colombier 		assert(b->ref == 1);
1415897ae9c1SDavid du Colombier 		b->wp = b->rp = (uchar*)
1416897ae9c1SDavid du Colombier 			((uintptr)(b->lim - Rxblklen) & ~(Bufalign - 1));
1417897ae9c1SDavid du Colombier 		assert(((uintptr)b->rp & (Bufalign - 1)) == 0);
1418897ae9c1SDavid du Colombier 		b->free = rxfreeb;
1419897ae9c1SDavid du Colombier 		b->next = freeblocks.head;
1420897ae9c1SDavid du Colombier 		freeblocks.head = b;
1421897ae9c1SDavid du Colombier 	}
1422897ae9c1SDavid du Colombier 	iunlock(&freeblocks);
1423897ae9c1SDavid du Colombier 
14242f205b96SDavid du Colombier 	/*
14252f205b96SDavid du Colombier 	 * allocate uncached rx ring descriptors because rings are shared
14262f205b96SDavid du Colombier 	 * with the ethernet controller and more than one fits in a cache line.
14272f205b96SDavid du Colombier 	 */
14282f205b96SDavid du Colombier 	ctlr->rx = ucallocalign(Nrx * sizeof(Rx), Descralign, 0);
1429897ae9c1SDavid du Colombier 	if(ctlr->rx == nil)
1430897ae9c1SDavid du Colombier 		panic("ether1116: no memory for rx ring");
1431897ae9c1SDavid du Colombier 	for(i = 0; i < Nrx; i++) {
1432897ae9c1SDavid du Colombier 		r = &ctlr->rx[i];
1433897ae9c1SDavid du Colombier 		assert(((uintptr)r & (Descralign - 1)) == 0);
14342f205b96SDavid du Colombier 		r->cs = 0;	/* owned by software until r->buf is non-nil */
1435897ae9c1SDavid du Colombier 		r->buf = 0;
1436897ae9c1SDavid du Colombier 		r->next = PADDR(&ctlr->rx[NEXT(i, Nrx)]);
1437897ae9c1SDavid du Colombier 		ctlr->rxb[i] = nil;
1438897ae9c1SDavid du Colombier 	}
1439897ae9c1SDavid du Colombier 	ctlr->rxtail = ctlr->rxhead = 0;
144028620197SDavid du Colombier 	rxreplenish(ctlr);
1441897ae9c1SDavid du Colombier 
14422f205b96SDavid du Colombier 	/* allocate uncached tx ring descriptors */
14432f205b96SDavid du Colombier 	ctlr->tx = ucallocalign(Ntx * sizeof(Tx), Descralign, 0);
1444897ae9c1SDavid du Colombier 	if(ctlr->tx == nil)
1445897ae9c1SDavid du Colombier 		panic("ether1116: no memory for tx ring");
1446897ae9c1SDavid du Colombier 	for(i = 0; i < Ntx; i++) {
1447897ae9c1SDavid du Colombier 		t = &ctlr->tx[i];
1448897ae9c1SDavid du Colombier 		assert(((uintptr)t & (Descralign - 1)) == 0);
1449897ae9c1SDavid du Colombier 		t->cs = 0;
1450897ae9c1SDavid du Colombier 		t->buf = 0;
1451897ae9c1SDavid du Colombier 		t->next = PADDR(&ctlr->tx[NEXT(i, Ntx)]);
1452897ae9c1SDavid du Colombier 		ctlr->txb[i] = nil;
1453897ae9c1SDavid du Colombier 	}
1454897ae9c1SDavid du Colombier 	ctlr->txtail = ctlr->txhead = 0;
145528620197SDavid du Colombier }
145628620197SDavid du Colombier 
145728620197SDavid du Colombier static void
ctlrinit(Ether * ether)145828620197SDavid du Colombier ctlrinit(Ether *ether)
145928620197SDavid du Colombier {
146028620197SDavid du Colombier 	int i;
146128620197SDavid du Colombier 	Ctlr *ctlr = ether->ctlr;
146228620197SDavid du Colombier 	Gbereg *reg = ctlr->reg;
146328620197SDavid du Colombier 	static char name[KNAMELEN];
146428620197SDavid du Colombier 	static Ctlr fakectlr;		/* bigger than 4K; keep off the stack */
146528620197SDavid du Colombier 
146628620197SDavid du Colombier 	for (i = 0; i < nelem(reg->tcqdp); i++)
146728620197SDavid du Colombier 		reg->tcqdp[i] = 0;
146828620197SDavid du Colombier 	for (i = 0; i < nelem(reg->crdp); i++)
146928620197SDavid du Colombier 		reg->crdp[i].r = 0;
147028620197SDavid du Colombier 	coherence();
147128620197SDavid du Colombier 
147228620197SDavid du Colombier 	cfgdramacc(reg);
147328620197SDavid du Colombier 	ctlralloc(ctlr);
147428620197SDavid du Colombier 
147528620197SDavid du Colombier 	reg->tcqdp[Qno]  = PADDR(&ctlr->tx[ctlr->txhead]);
147628620197SDavid du Colombier 	reg->crdp[Qno].r = PADDR(&ctlr->rx[ctlr->rxhead]);
147728620197SDavid du Colombier 	coherence();
147828620197SDavid du Colombier 
147928620197SDavid du Colombier //	dumprxdescs(ctlr);
1480897ae9c1SDavid du Colombier 
1481897ae9c1SDavid du Colombier 	/* clear stats by reading them into fake ctlr */
1482897ae9c1SDavid du Colombier 	getmibstats(&fakectlr);
1483897ae9c1SDavid du Colombier 
148428620197SDavid du Colombier 	reg->pxmfs = MFS40by;			/* allow runts in */
1485897ae9c1SDavid du Colombier 
1486897ae9c1SDavid du Colombier 	/*
1487897ae9c1SDavid du Colombier 	 * ipg's (inter packet gaps) for interrupt coalescing,
1488897ae9c1SDavid du Colombier 	 * values in units of 64 clock cycles.  A full-sized
1489897ae9c1SDavid du Colombier 	 * packet (1514 bytes) takes just over 12µs to transmit.
1490897ae9c1SDavid du Colombier 	 */
1491897ae9c1SDavid du Colombier 	if (CLOCKFREQ/(Maxrxintrsec*64) >= (1<<16))
1492897ae9c1SDavid du Colombier 		panic("rx coalescing value %d too big for short",
1493897ae9c1SDavid du Colombier 			CLOCKFREQ/(Maxrxintrsec*64));
1494897ae9c1SDavid du Colombier 	reg->sdc = SDCrifb | SDCrxburst(Burst16) | SDCtxburst(Burst16) |
1495897ae9c1SDavid du Colombier 		SDCrxnobyteswap | SDCtxnobyteswap |
1496897ae9c1SDavid du Colombier 		SDCipgintrx(CLOCKFREQ/(Maxrxintrsec*64));
1497897ae9c1SDavid du Colombier 	reg->pxtfut = 0;	/* TFUTipginttx(CLOCKFREQ/(Maxrxintrsec*64)) */
1498897ae9c1SDavid du Colombier 
1499897ae9c1SDavid du Colombier 	/* allow just these interrupts */
150028620197SDavid du Colombier 	/* guruplug generates Irxerr interrupts continually */
150128620197SDavid du Colombier 	reg->irqmask = Isum | Irx | Irxbufferq(Qno) | Irxerr | Itxendq(Qno);
150228620197SDavid du Colombier 	reg->irqemask = IEsum | IEtxerrq(Qno) | IEphystschg | IErxoverrun |
150328620197SDavid du Colombier 		IEtxunderrun;
1504897ae9c1SDavid du Colombier 
1505897ae9c1SDavid du Colombier 	reg->irqe = 0;
1506897ae9c1SDavid du Colombier 	reg->euirqmask = 0;
150728620197SDavid du Colombier 	coherence();
150828620197SDavid du Colombier 	reg->irq = 0;
1509897ae9c1SDavid du Colombier 	reg->euirq = 0;
151028620197SDavid du Colombier 	/* send errors to end of memory */
151128620197SDavid du Colombier //	reg->euda = PHYSDRAM + 512*MB - 8*1024;
151228620197SDavid du Colombier 	reg->euda = 0;
151328620197SDavid du Colombier 	reg->eudid = Attrcs1 << 4 | Targdram;
1514897ae9c1SDavid du Colombier 
1515897ae9c1SDavid du Colombier //	archetheraddr(ether, ctlr->reg, Qno);	/* 2nd location */
1516897ae9c1SDavid du Colombier 
1517897ae9c1SDavid du Colombier 	reg->portcfg = Rxqdefault(Qno) | Rxqarp(Qno);
1518897ae9c1SDavid du Colombier 	reg->portcfgx = 0;
151928620197SDavid du Colombier 	coherence();
152028620197SDavid du Colombier 
152128620197SDavid du Colombier 	/*
152228620197SDavid du Colombier 	 * start the controller running.
152328620197SDavid du Colombier 	 * turn the port on, kick the receiver.
152428620197SDavid du Colombier 	 */
1525897ae9c1SDavid du Colombier 
1526897ae9c1SDavid du Colombier 	reg->psc1 = PSC1rgmii | PSC1encolonbp | PSC1coldomlim(0x23);
152728620197SDavid du Colombier 	/* do this only when the controller is quiescent */
1528897ae9c1SDavid du Colombier 	reg->psc0 = PSC0porton | PSC0an_flctloff |
1529897ae9c1SDavid du Colombier 		PSC0an_pauseadv | PSC0nofrclinkdown | PSC0mru(PSC0mru1522);
153028620197SDavid du Colombier 	coherence();
153128620197SDavid du Colombier 	for (i = 0; i < 4000; i++)		/* magic delay */
153228620197SDavid du Colombier 		;
1533897ae9c1SDavid du Colombier 
1534897ae9c1SDavid du Colombier 	ether->link = (reg->ps0 & PS0linkup) != 0;
1535897ae9c1SDavid du Colombier 
1536897ae9c1SDavid du Colombier 	/* set ethernet MTU for leaky bucket mechanism to 0 (disabled) */
1537897ae9c1SDavid du Colombier 	reg->pmtu = 0;
1538897ae9c1SDavid du Colombier 	etheractive(ether);
1539897ae9c1SDavid du Colombier 
1540897ae9c1SDavid du Colombier 	snprint(name, sizeof name, "#l%drproc", ether->ctlrno);
1541897ae9c1SDavid du Colombier 	kproc(name, rcvproc, ether);
154228620197SDavid du Colombier 
154328620197SDavid du Colombier 	reg->rqc = Rxqon(Qno);
154428620197SDavid du Colombier 	coherence();
1545897ae9c1SDavid du Colombier }
1546897ae9c1SDavid du Colombier 
1547897ae9c1SDavid du Colombier static void
attach(Ether * ether)1548897ae9c1SDavid du Colombier attach(Ether* ether)
1549897ae9c1SDavid du Colombier {
1550897ae9c1SDavid du Colombier 	Ctlr *ctlr = ether->ctlr;
1551897ae9c1SDavid du Colombier 
1552897ae9c1SDavid du Colombier 	lock(&ctlr->initlock);
1553897ae9c1SDavid du Colombier 	if(ctlr->init == 0) {
1554897ae9c1SDavid du Colombier 		ctlrinit(ether);
1555897ae9c1SDavid du Colombier 		ctlr->init = 1;
1556897ae9c1SDavid du Colombier 	}
1557897ae9c1SDavid du Colombier 	unlock(&ctlr->initlock);
1558897ae9c1SDavid du Colombier }
1559897ae9c1SDavid du Colombier 
1560897ae9c1SDavid du Colombier /*
1561897ae9c1SDavid du Colombier  * statistics goo.
1562897ae9c1SDavid du Colombier  * mib registers clear on read.
1563897ae9c1SDavid du Colombier  */
1564897ae9c1SDavid du Colombier 
1565897ae9c1SDavid du Colombier static void
getmibstats(Ctlr * ctlr)1566897ae9c1SDavid du Colombier getmibstats(Ctlr *ctlr)
1567897ae9c1SDavid du Colombier {
1568897ae9c1SDavid du Colombier 	Gbereg *reg = ctlr->reg;
1569897ae9c1SDavid du Colombier 
1570897ae9c1SDavid du Colombier 	/*
1571897ae9c1SDavid du Colombier 	 * Marvell 88f6281 errata FE-ETH-120: high long of rxby and txby
1572897ae9c1SDavid du Colombier 	 * can't be read correctly, so read the low long frequently
1573897ae9c1SDavid du Colombier 	 * (every 30 seconds or less), thus avoiding overflow into high long.
1574897ae9c1SDavid du Colombier 	 */
1575897ae9c1SDavid du Colombier 	ctlr->rxby	+= reg->rxbylo;
1576897ae9c1SDavid du Colombier 	ctlr->txby	+= reg->txbylo;
1577897ae9c1SDavid du Colombier 
1578897ae9c1SDavid du Colombier 	ctlr->badrxby	+= reg->badrxby;
1579897ae9c1SDavid du Colombier 	ctlr->mactxerr	+= reg->mactxerr;
1580897ae9c1SDavid du Colombier 	ctlr->rxpkt	+= reg->rxpkt;
1581897ae9c1SDavid du Colombier 	ctlr->badrxpkt	+= reg->badrxpkt;
1582897ae9c1SDavid du Colombier 	ctlr->rxbcastpkt+= reg->rxbcastpkt;
1583897ae9c1SDavid du Colombier 	ctlr->rxmcastpkt+= reg->rxmcastpkt;
1584897ae9c1SDavid du Colombier 	ctlr->rx64	+= reg->rx64;
1585897ae9c1SDavid du Colombier 	ctlr->rx65_127	+= reg->rx65_127;
1586897ae9c1SDavid du Colombier 	ctlr->rx128_255	+= reg->rx128_255;
1587897ae9c1SDavid du Colombier 	ctlr->rx256_511	+= reg->rx256_511;
1588897ae9c1SDavid du Colombier 	ctlr->rx512_1023+= reg->rx512_1023;
1589897ae9c1SDavid du Colombier 	ctlr->rx1024_max+= reg->rx1024_max;
1590897ae9c1SDavid du Colombier 	ctlr->txpkt	+= reg->txpkt;
1591897ae9c1SDavid du Colombier 	ctlr->txcollpktdrop+= reg->txcollpktdrop;
1592897ae9c1SDavid du Colombier 	ctlr->txmcastpkt+= reg->txmcastpkt;
1593897ae9c1SDavid du Colombier 	ctlr->txbcastpkt+= reg->txbcastpkt;
1594897ae9c1SDavid du Colombier 	ctlr->badmacctlpkts+= reg->badmacctlpkts;
1595897ae9c1SDavid du Colombier 	ctlr->txflctl	+= reg->txflctl;
1596897ae9c1SDavid du Colombier 	ctlr->rxflctl	+= reg->rxflctl;
1597897ae9c1SDavid du Colombier 	ctlr->badrxflctl+= reg->badrxflctl;
1598897ae9c1SDavid du Colombier 	ctlr->rxundersized+= reg->rxundersized;
1599897ae9c1SDavid du Colombier 	ctlr->rxfrags	+= reg->rxfrags;
1600897ae9c1SDavid du Colombier 	ctlr->rxtoobig	+= reg->rxtoobig;
1601897ae9c1SDavid du Colombier 	ctlr->rxjabber	+= reg->rxjabber;
1602897ae9c1SDavid du Colombier 	ctlr->rxerr	+= reg->rxerr;
1603897ae9c1SDavid du Colombier 	ctlr->crcerr	+= reg->crcerr;
1604897ae9c1SDavid du Colombier 	ctlr->collisions+= reg->collisions;
1605897ae9c1SDavid du Colombier 	ctlr->latecoll	+= reg->latecoll;
1606897ae9c1SDavid du Colombier }
1607897ae9c1SDavid du Colombier 
1608897ae9c1SDavid du Colombier long
ifstat(Ether * ether,void * a,long n,ulong off)1609897ae9c1SDavid du Colombier ifstat(Ether *ether, void *a, long n, ulong off)
1610897ae9c1SDavid du Colombier {
1611897ae9c1SDavid du Colombier 	Ctlr *ctlr = ether->ctlr;
1612897ae9c1SDavid du Colombier 	Gbereg *reg = ctlr->reg;
1613897ae9c1SDavid du Colombier 	char *buf, *p, *e;
1614897ae9c1SDavid du Colombier 
1615897ae9c1SDavid du Colombier 	buf = p = malloc(READSTR);
1616*7ae8f453SDavid du Colombier 	if(p == nil)
1617*7ae8f453SDavid du Colombier 		panic("ether1116 ifstat: no memory");
1618897ae9c1SDavid du Colombier 	e = p + READSTR;
1619897ae9c1SDavid du Colombier 
1620897ae9c1SDavid du Colombier 	ilock(ctlr);
1621897ae9c1SDavid du Colombier 	getmibstats(ctlr);
1622897ae9c1SDavid du Colombier 
1623897ae9c1SDavid du Colombier 	ctlr->intrs += ctlr->newintrs;
1624897ae9c1SDavid du Colombier 	p = seprint(p, e, "interrupts: %lud\n", ctlr->intrs);
1625897ae9c1SDavid du Colombier 	p = seprint(p, e, "new interrupts: %lud\n", ctlr->newintrs);
1626897ae9c1SDavid du Colombier 	ctlr->newintrs = 0;
1627897ae9c1SDavid du Colombier 	p = seprint(p, e, "tx underrun: %lud\n", ctlr->txunderrun);
1628897ae9c1SDavid du Colombier 	p = seprint(p, e, "tx ring full: %lud\n", ctlr->txringfull);
1629897ae9c1SDavid du Colombier 
1630897ae9c1SDavid du Colombier 	ctlr->rxdiscard += reg->pxdfc;
1631897ae9c1SDavid du Colombier 	ctlr->rxoverrun += reg->pxofc;
1632897ae9c1SDavid du Colombier 	p = seprint(p, e, "rx discarded frames: %lud\n", ctlr->rxdiscard);
1633897ae9c1SDavid du Colombier 	p = seprint(p, e, "rx overrun frames: %lud\n", ctlr->rxoverrun);
1634897ae9c1SDavid du Colombier 	p = seprint(p, e, "no first+last flag: %lud\n", ctlr->nofirstlast);
1635897ae9c1SDavid du Colombier 
1636897ae9c1SDavid du Colombier 	p = seprint(p, e, "duplex: %s\n", (reg->ps0 & PS0fd)? "full": "half");
1637897ae9c1SDavid du Colombier 	p = seprint(p, e, "flow control: %s\n", (reg->ps0 & PS0flctl)? "on": "off");
1638897ae9c1SDavid du Colombier 	/* p = seprint(p, e, "speed: %d mbps\n", ); */
1639897ae9c1SDavid du Colombier 
1640897ae9c1SDavid du Colombier 	p = seprint(p, e, "received bytes: %llud\n", ctlr->rxby);
1641897ae9c1SDavid du Colombier 	p = seprint(p, e, "bad received bytes: %lud\n", ctlr->badrxby);
1642897ae9c1SDavid du Colombier 	p = seprint(p, e, "internal mac transmit errors: %lud\n", ctlr->mactxerr);
1643897ae9c1SDavid du Colombier 	p = seprint(p, e, "total received frames: %lud\n", ctlr->rxpkt);
1644897ae9c1SDavid du Colombier 	p = seprint(p, e, "received broadcast frames: %lud\n", ctlr->rxbcastpkt);
1645897ae9c1SDavid du Colombier 	p = seprint(p, e, "received multicast frames: %lud\n", ctlr->rxmcastpkt);
1646897ae9c1SDavid du Colombier 	p = seprint(p, e, "bad received frames: %lud\n", ctlr->badrxpkt);
1647897ae9c1SDavid du Colombier 	p = seprint(p, e, "received frames 0-64: %lud\n", ctlr->rx64);
1648897ae9c1SDavid du Colombier 	p = seprint(p, e, "received frames 65-127: %lud\n", ctlr->rx65_127);
1649897ae9c1SDavid du Colombier 	p = seprint(p, e, "received frames 128-255: %lud\n", ctlr->rx128_255);
1650897ae9c1SDavid du Colombier 	p = seprint(p, e, "received frames 256-511: %lud\n", ctlr->rx256_511);
1651897ae9c1SDavid du Colombier 	p = seprint(p, e, "received frames 512-1023: %lud\n", ctlr->rx512_1023);
1652897ae9c1SDavid du Colombier 	p = seprint(p, e, "received frames 1024-max: %lud\n", ctlr->rx1024_max);
1653897ae9c1SDavid du Colombier 	p = seprint(p, e, "transmitted bytes: %llud\n", ctlr->txby);
1654897ae9c1SDavid du Colombier 	p = seprint(p, e, "total transmitted frames: %lud\n", ctlr->txpkt);
1655897ae9c1SDavid du Colombier 	p = seprint(p, e, "transmitted broadcast frames: %lud\n", ctlr->txbcastpkt);
1656897ae9c1SDavid du Colombier 	p = seprint(p, e, "transmitted multicast frames: %lud\n", ctlr->txmcastpkt);
1657897ae9c1SDavid du Colombier 	p = seprint(p, e, "transmit frames dropped by collision: %lud\n", ctlr->txcollpktdrop);
1658897ae9c1SDavid du Colombier 	p = seprint(p, e, "misaligned buffers: %lud\n", ether->pktsmisaligned);
1659897ae9c1SDavid du Colombier 
1660897ae9c1SDavid du Colombier 	p = seprint(p, e, "bad mac control frames: %lud\n", ctlr->badmacctlpkts);
1661897ae9c1SDavid du Colombier 	p = seprint(p, e, "transmitted flow control messages: %lud\n", ctlr->txflctl);
1662897ae9c1SDavid du Colombier 	p = seprint(p, e, "received flow control messages: %lud\n", ctlr->rxflctl);
1663897ae9c1SDavid du Colombier 	p = seprint(p, e, "bad received flow control messages: %lud\n", ctlr->badrxflctl);
1664897ae9c1SDavid du Colombier 	p = seprint(p, e, "received undersized packets: %lud\n", ctlr->rxundersized);
1665897ae9c1SDavid du Colombier 	p = seprint(p, e, "received fragments: %lud\n", ctlr->rxfrags);
1666897ae9c1SDavid du Colombier 	p = seprint(p, e, "received oversized packets: %lud\n", ctlr->rxtoobig);
1667897ae9c1SDavid du Colombier 	p = seprint(p, e, "received jabber packets: %lud\n", ctlr->rxjabber);
1668897ae9c1SDavid du Colombier 	p = seprint(p, e, "mac receive errors: %lud\n", ctlr->rxerr);
1669897ae9c1SDavid du Colombier 	p = seprint(p, e, "crc errors: %lud\n", ctlr->crcerr);
1670897ae9c1SDavid du Colombier 	p = seprint(p, e, "collisions: %lud\n", ctlr->collisions);
1671897ae9c1SDavid du Colombier 	p = seprint(p, e, "late collisions: %lud\n", ctlr->latecoll);
1672897ae9c1SDavid du Colombier 	USED(p);
1673897ae9c1SDavid du Colombier 	iunlock(ctlr);
1674897ae9c1SDavid du Colombier 
1675897ae9c1SDavid du Colombier 	n = readstr(off, a, n, buf);
1676897ae9c1SDavid du Colombier 	free(buf);
1677897ae9c1SDavid du Colombier 	return n;
1678897ae9c1SDavid du Colombier }
1679897ae9c1SDavid du Colombier 
1680897ae9c1SDavid du Colombier 
1681897ae9c1SDavid du Colombier static int
reset(Ether * ether)1682897ae9c1SDavid du Colombier reset(Ether *ether)
1683897ae9c1SDavid du Colombier {
1684897ae9c1SDavid du Colombier 	Ctlr *ctlr;
1685897ae9c1SDavid du Colombier 
1686897ae9c1SDavid du Colombier 	ether->ctlr = ctlr = malloc(sizeof *ctlr);
1687*7ae8f453SDavid du Colombier 	if (ctlr == nil)
1688*7ae8f453SDavid du Colombier 		panic("ether1116 reset: no memory");
1689897ae9c1SDavid du Colombier 	switch(ether->ctlrno) {
1690897ae9c1SDavid du Colombier 	case 0:
1691897ae9c1SDavid du Colombier 		ether->irq = IRQ0gbe0sum;
1692897ae9c1SDavid du Colombier 		break;
1693897ae9c1SDavid du Colombier 	case 1:
1694897ae9c1SDavid du Colombier 		ether->irq = IRQ0gbe1sum;
1695897ae9c1SDavid du Colombier 		break;
1696897ae9c1SDavid du Colombier 	default:
1697897ae9c1SDavid du Colombier 		panic("ether1116: bad ether ctlr #%d", ether->ctlrno);
1698897ae9c1SDavid du Colombier 	}
16991ecc8ef2SDavid du Colombier 	ctlr->reg = (Gbereg*)soc.ether[ether->ctlrno];
1700897ae9c1SDavid du Colombier 
170128620197SDavid du Colombier 	/* need this for guruplug, at least */
17027365b686SDavid du Colombier 	*(ulong *)soc.iocfg |= 1 << 7 | 1 << 15;	/* io cfg 0: 1.8v gbe */
1703897ae9c1SDavid du Colombier 	coherence();
1704897ae9c1SDavid du Colombier 
170528620197SDavid du Colombier 	ctlr->ether = ether;
170628620197SDavid du Colombier 	ctlrs[ether->ctlrno] = ctlr;
170728620197SDavid du Colombier 
170828620197SDavid du Colombier 	shutdown(ether);
1709897ae9c1SDavid du Colombier 	/* ensure that both interfaces are set to RGMII before calling mii */
17101ecc8ef2SDavid du Colombier 	((Gbereg*)soc.ether[0])->psc1 |= PSC1rgmii;
17111ecc8ef2SDavid du Colombier 	((Gbereg*)soc.ether[1])->psc1 |= PSC1rgmii;
171228620197SDavid du Colombier 	coherence();
1713897ae9c1SDavid du Colombier 
1714897ae9c1SDavid du Colombier 	/* Set phy address of the port */
1715897ae9c1SDavid du Colombier 	ctlr->port = ether->ctlrno;
1716897ae9c1SDavid du Colombier 	ctlr->reg->phy = ether->ctlrno;
1717897ae9c1SDavid du Colombier 	coherence();
1718897ae9c1SDavid du Colombier 	ether->port = (uintptr)ctlr->reg;
1719897ae9c1SDavid du Colombier 
1720897ae9c1SDavid du Colombier 	if(kirkwoodmii(ether) < 0){
1721897ae9c1SDavid du Colombier 		free(ctlr);
1722897ae9c1SDavid du Colombier 		ether->ctlr = nil;
1723897ae9c1SDavid du Colombier 		return -1;
1724897ae9c1SDavid du Colombier 	}
1725897ae9c1SDavid du Colombier 	miiphyinit(ctlr->mii);
1726897ae9c1SDavid du Colombier 	archetheraddr(ether, ctlr->reg, Qno);	/* original location */
1727897ae9c1SDavid du Colombier 	if (memcmp(ether->ea, zeroea, sizeof zeroea) == 0){
172828620197SDavid du Colombier iprint("ether1116: reset: zero ether->ea\n");
1729897ae9c1SDavid du Colombier 		free(ctlr);
1730897ae9c1SDavid du Colombier 		ether->ctlr = nil;
1731897ae9c1SDavid du Colombier 		return -1;			/* no rj45 for this ether */
1732897ae9c1SDavid du Colombier 	}
173328620197SDavid du Colombier 
1734897ae9c1SDavid du Colombier 	ether->attach = attach;
1735897ae9c1SDavid du Colombier 	ether->transmit = transmit;
1736897ae9c1SDavid du Colombier 	ether->interrupt = interrupt;
1737897ae9c1SDavid du Colombier 	ether->ifstat = ifstat;
1738897ae9c1SDavid du Colombier 	ether->shutdown = shutdown;
1739897ae9c1SDavid du Colombier 	ether->ctl = ctl;
1740897ae9c1SDavid du Colombier 
1741897ae9c1SDavid du Colombier 	ether->arg = ether;
1742897ae9c1SDavid du Colombier 	ether->promiscuous = promiscuous;
1743897ae9c1SDavid du Colombier 	ether->multicast = multicast;
1744897ae9c1SDavid du Colombier 	return 0;
1745897ae9c1SDavid du Colombier }
1746897ae9c1SDavid du Colombier 
1747897ae9c1SDavid du Colombier void
ether1116link(void)1748897ae9c1SDavid du Colombier ether1116link(void)
1749897ae9c1SDavid du Colombier {
1750897ae9c1SDavid du Colombier 	addethercard("88e1116", reset);
1751897ae9c1SDavid du Colombier }
1752