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