1*da917039SDavid du Colombier /*
2*da917039SDavid du Colombier * Xilinx Temacs Ethernet driver.
3*da917039SDavid du Colombier * It uses the Local Link FIFOs.
4*da917039SDavid du Colombier * There are two interfaces per Temacs controller.
5*da917039SDavid du Colombier * Half-duplex is not supported by hardware.
6*da917039SDavid du Colombier */
7*da917039SDavid du Colombier #include "include.h"
8*da917039SDavid du Colombier
9*da917039SDavid du Colombier enum {
10*da917039SDavid du Colombier /* fixed by hardware */
11*da917039SDavid du Colombier Nifcs = 2,
12*da917039SDavid du Colombier
13*da917039SDavid du Colombier /* tunable parameters; see below for more */
14*da917039SDavid du Colombier Nrde = 64,
15*da917039SDavid du Colombier Ntde = 4,
16*da917039SDavid du Colombier };
17*da917039SDavid du Colombier enum { /* directly-addressible registers' bits */
18*da917039SDavid du Colombier /* raf register */
19*da917039SDavid du Colombier Htrst = 1<<0, /* hard temac reset (both ifcs) */
20*da917039SDavid du Colombier Mcstrej = 1<<1, /* reject received multicast dest addr */
21*da917039SDavid du Colombier Bcstrej = 1<<2, /* reject received broadcast dest addr */
22*da917039SDavid du Colombier
23*da917039SDavid du Colombier /* is, ip, ie register */
24*da917039SDavid du Colombier Hardacscmplt = 1<<0, /* hard register access complete */
25*da917039SDavid du Colombier Autoneg = 1<<1, /* auto-negotiation complete */
26*da917039SDavid du Colombier Rxcmplt = 1<<2, /* receive complete */
27*da917039SDavid du Colombier Rxrject = 1<<3, /* receive frame rejected */
28*da917039SDavid du Colombier Rxfifoovr = 1<<4, /* receive fifo overrun */
29*da917039SDavid du Colombier Txcmplt = 1<<5, /* transmit complete */
30*da917039SDavid du Colombier Rxdcmlock = 1<<6, /* receive DCM lock (ready for use) */
31*da917039SDavid du Colombier Mgtrdy = 1<<7, /* mgt ready (new in 1.01b)
32*da917039SDavid du Colombier
33*da917039SDavid du Colombier /* ctl register */
34*da917039SDavid du Colombier Wen = 1<<15, /* write instead of read */
35*da917039SDavid du Colombier
36*da917039SDavid du Colombier /* ctl register address codes; select other registers */
37*da917039SDavid du Colombier Rcw0 = 0x200, /* receive configuration */
38*da917039SDavid du Colombier Rcw1 = 0x240,
39*da917039SDavid du Colombier Tc = 0x280, /* tx config */
40*da917039SDavid du Colombier Fcc = 0x2c0, /* flow control */
41*da917039SDavid du Colombier Emmc = 0x300, /* ethernet mac mode config */
42*da917039SDavid du Colombier Phyc = 0x320, /* rgmii/sgmii config */
43*da917039SDavid du Colombier Mc = 0x340, /* mgmt config */
44*da917039SDavid du Colombier Uaw0 = 0x380, /* unicast addr word 0 (low-order) */
45*da917039SDavid du Colombier Uaw1 = 0x384, /* unicast addr word 1 (high-order) */
46*da917039SDavid du Colombier Maw0 = 0x388, /* multicast addr word 0 (low) */
47*da917039SDavid du Colombier Maw1 = 0x38c, /* multicast addr word 1 (high + more) */
48*da917039SDavid du Colombier Afm = 0x390, /* addr filter mode */
49*da917039SDavid du Colombier Tis = 0x3a0, /* intr status */
50*da917039SDavid du Colombier Tie = 0x3a4, /* intr enable */
51*da917039SDavid du Colombier Miimwd = 0x3b0, /* mii mgmt write data */
52*da917039SDavid du Colombier Miimai = 0x3b4, /* mii mgmt access initiate */
53*da917039SDavid du Colombier
54*da917039SDavid du Colombier /* rdy register */
55*da917039SDavid du Colombier Fabrrr = 1<<0, /* fabric read ready */
56*da917039SDavid du Colombier Miimrr = 1<<1, /* mii mgmt read ready */
57*da917039SDavid du Colombier Miimwr = 1<<2, /* mii mgmt write ready */
58*da917039SDavid du Colombier Afrr = 1<<3, /* addr filter read ready */
59*da917039SDavid du Colombier Afwr = 1<<4, /* addr filter write ready */
60*da917039SDavid du Colombier Cfgrr = 1<<5, /* config reg read ready */
61*da917039SDavid du Colombier Cfgwr = 1<<6, /* config reg write ready */
62*da917039SDavid du Colombier Hardacsrdy = 1<<16, /* hard reg access ready */
63*da917039SDavid du Colombier };
64*da917039SDavid du Colombier enum { /* indirectly-addressible registers' bits */
65*da917039SDavid du Colombier /* Rcw1 register */
66*da917039SDavid du Colombier Rst = 1<<31, /* reset */
67*da917039SDavid du Colombier Jum = 1<<30, /* jumbo frame enable */
68*da917039SDavid du Colombier Fcs = 1<<29, /* in-band fcs enable */
69*da917039SDavid du Colombier Rx = 1<<28, /* rx enable */
70*da917039SDavid du Colombier Vlan = 1<<27, /* vlan frame enable */
71*da917039SDavid du Colombier Hd = 1<<26, /* half-duplex mode (must be 0) */
72*da917039SDavid du Colombier Ltdis = 1<<25, /* length/type field valid check disable */
73*da917039SDavid du Colombier
74*da917039SDavid du Colombier /* Tc register. same as Rcw1 but Rx->Tx, Ltdis->Ifg */
75*da917039SDavid du Colombier Tx = Rx, /* tx enable */
76*da917039SDavid du Colombier Ifg = Ltdis, /* inter-frame gap adjustment enable */
77*da917039SDavid du Colombier
78*da917039SDavid du Colombier /* Fcc register */
79*da917039SDavid du Colombier Fctx = 1<<30, /* tx flow control enable */
80*da917039SDavid du Colombier Fcrx = 1<<29, /* rx flow control enable */
81*da917039SDavid du Colombier
82*da917039SDavid du Colombier /* Emmc register */
83*da917039SDavid du Colombier Linkspeed = 3<<30, /* field */
84*da917039SDavid du Colombier Ls1000 = 2<<30, /* Gb */
85*da917039SDavid du Colombier Ls100 = 1<<30, /* 100Mb */
86*da917039SDavid du Colombier Ls10 = 0<<30, /* 10Mb */
87*da917039SDavid du Colombier Rgmii = 1<<29, /* rgmii mode enable */
88*da917039SDavid du Colombier Sgmii = 1<<28, /* sgmii mode enable */
89*da917039SDavid du Colombier Gpcs = 1<<27, /* 1000base-x mode enable */
90*da917039SDavid du Colombier Hostifen= 1<<26, /* host interface enable */
91*da917039SDavid du Colombier Tx16 = 1<<25, /* tx 16-bit (vs 8-bit) data ifc enable (0) */
92*da917039SDavid du Colombier Rx16 = 1<<24, /* rx 16-bit (vs 8-bit) data ifc enable (0) */
93*da917039SDavid du Colombier
94*da917039SDavid du Colombier /* Phyc register. sgmii link speed is Emmc's Linkspeed. */
95*da917039SDavid du Colombier Rgmiills = 3<<2, /* field */
96*da917039SDavid du Colombier Rls1000 = 2<<2, /* Gb */
97*da917039SDavid du Colombier Rls100 = 1<<2, /* 100Mb */
98*da917039SDavid du Colombier Rls10 = 0<<2, /* 10Mb */
99*da917039SDavid du Colombier Rgmiihd = 1<<1, /* half-duplex */
100*da917039SDavid du Colombier Rgmiilink = 1<<0, /* rgmii link (is up) */
101*da917039SDavid du Colombier
102*da917039SDavid du Colombier /* Mc register */
103*da917039SDavid du Colombier Mdioen = 1<<6, /* mdio (mii mgmt) enable */
104*da917039SDavid du Colombier
105*da917039SDavid du Colombier /* Maw1 register */
106*da917039SDavid du Colombier Rnw = 1<<23, /* multicast addr table reg read (vs write) */
107*da917039SDavid du Colombier Addr = 3<<16, /* field */
108*da917039SDavid du Colombier
109*da917039SDavid du Colombier /* Afm register */
110*da917039SDavid du Colombier Pm = 1<<31, /* promiscuous mode */
111*da917039SDavid du Colombier
112*da917039SDavid du Colombier /* Tis, Tie register (*rst->*en) */
113*da917039SDavid du Colombier Fabrrst = 1<<0, /* fabric read intr sts (read done) */
114*da917039SDavid du Colombier Miimrst = 1<<1, /* mii mgmt read intr sts (read done) */
115*da917039SDavid du Colombier Miimwst = 1<<2, /* mii mgmt write intr sts (write done) */
116*da917039SDavid du Colombier Afrst = 1<<3, /* addr filter read intr sts (read done) */
117*da917039SDavid du Colombier Afwst = 1<<4, /* addr filter write intr sts (write done) */
118*da917039SDavid du Colombier Cfgrst = 1<<5, /* config read intr sts (read done) */
119*da917039SDavid du Colombier Cfgwst = 1<<6, /* config write intr sts (write done) */
120*da917039SDavid du Colombier };
121*da917039SDavid du Colombier
122*da917039SDavid du Colombier enum {
123*da917039SDavid du Colombier /* tunable parameters */
124*da917039SDavid du Colombier Defmbps = 1000, /* default Mb/s */
125*da917039SDavid du Colombier Defls = Ls1000, /* must match Defmbps */
126*da917039SDavid du Colombier };
127*da917039SDavid du Colombier
128*da917039SDavid du Colombier typedef struct Temacsw Temacsw;
129*da917039SDavid du Colombier typedef struct Temacregs Temacregs;
130*da917039SDavid du Colombier struct Temacregs {
131*da917039SDavid du Colombier ulong raf; /* reset & addr filter */
132*da917039SDavid du Colombier ulong tpf; /* tx pause frame */
133*da917039SDavid du Colombier ulong ifgp; /* tx inter-frame gap adjustment */
134*da917039SDavid du Colombier ulong is; /* intr status */
135*da917039SDavid du Colombier ulong ip; /* intr pending */
136*da917039SDavid du Colombier ulong ie; /* intr enable */
137*da917039SDavid du Colombier ulong pad[2];
138*da917039SDavid du Colombier
139*da917039SDavid du Colombier ulong msw; /* msw data; shared by ifcs */
140*da917039SDavid du Colombier ulong lsw; /* lsw data; shared */
141*da917039SDavid du Colombier ulong ctl; /* control; shared */
142*da917039SDavid du Colombier ulong rdy; /* ready status */
143*da917039SDavid du Colombier ulong pad2[4];
144*da917039SDavid du Colombier };
145*da917039SDavid du Colombier struct Temacsw {
146*da917039SDavid du Colombier Temacregs *regs;
147*da917039SDavid du Colombier };
148*da917039SDavid du Colombier
149*da917039SDavid du Colombier extern uchar mymac[Eaddrlen];
150*da917039SDavid du Colombier
151*da917039SDavid du Colombier static Ether *ethers[1]; /* only first ether is connected to a fifo */
152*da917039SDavid du Colombier static Lock shreglck; /* protects shared registers */
153*da917039SDavid du Colombier
154*da917039SDavid du Colombier static void transmit(Ether *ether);
155*da917039SDavid du Colombier
156*da917039SDavid du Colombier static void
getready(Temacregs * trp)157*da917039SDavid du Colombier getready(Temacregs *trp)
158*da917039SDavid du Colombier {
159*da917039SDavid du Colombier while ((trp->rdy & Hardacsrdy) == 0)
160*da917039SDavid du Colombier ;
161*da917039SDavid du Colombier }
162*da917039SDavid du Colombier
163*da917039SDavid du Colombier static ulong
rdindir(Temacregs * trp,unsigned code)164*da917039SDavid du Colombier rdindir(Temacregs *trp, unsigned code)
165*da917039SDavid du Colombier {
166*da917039SDavid du Colombier ulong val;
167*da917039SDavid du Colombier
168*da917039SDavid du Colombier ilock(&shreglck);
169*da917039SDavid du Colombier getready(trp);
170*da917039SDavid du Colombier trp->ctl = code;
171*da917039SDavid du Colombier coherence();
172*da917039SDavid du Colombier
173*da917039SDavid du Colombier getready(trp);
174*da917039SDavid du Colombier val = trp->lsw;
175*da917039SDavid du Colombier iunlock(&shreglck);
176*da917039SDavid du Colombier return val;
177*da917039SDavid du Colombier }
178*da917039SDavid du Colombier
179*da917039SDavid du Colombier static int
wrindir(Temacregs * trp,unsigned code,ulong val)180*da917039SDavid du Colombier wrindir(Temacregs *trp, unsigned code, ulong val)
181*da917039SDavid du Colombier {
182*da917039SDavid du Colombier ilock(&shreglck);
183*da917039SDavid du Colombier getready(trp);
184*da917039SDavid du Colombier trp->lsw = val;
185*da917039SDavid du Colombier coherence();
186*da917039SDavid du Colombier trp->ctl = Wen | code;
187*da917039SDavid du Colombier coherence();
188*da917039SDavid du Colombier
189*da917039SDavid du Colombier getready(trp);
190*da917039SDavid du Colombier iunlock(&shreglck);
191*da917039SDavid du Colombier return 0;
192*da917039SDavid du Colombier }
193*da917039SDavid du Colombier
194*da917039SDavid du Colombier static int
interrupt(ulong bit)195*da917039SDavid du Colombier interrupt(ulong bit)
196*da917039SDavid du Colombier {
197*da917039SDavid du Colombier int e, r, sts;
198*da917039SDavid du Colombier Ether *ether;
199*da917039SDavid du Colombier Temacsw *ctlr;
200*da917039SDavid du Colombier
201*da917039SDavid du Colombier r = 0;
202*da917039SDavid du Colombier for (e = 0; e < MaxEther; e++) {
203*da917039SDavid du Colombier ether = ethers[e];
204*da917039SDavid du Colombier if (ether == nil)
205*da917039SDavid du Colombier continue;
206*da917039SDavid du Colombier ctlr = ether->ctlr;
207*da917039SDavid du Colombier sts = ctlr->regs->is;
208*da917039SDavid du Colombier if (sts)
209*da917039SDavid du Colombier r = 1;
210*da917039SDavid du Colombier ctlr->regs->is = sts; /* extinguish intr source */
211*da917039SDavid du Colombier coherence();
212*da917039SDavid du Colombier sts &= ~(Rxcmplt | Txcmplt | Rxdcmlock | Mgtrdy);
213*da917039SDavid du Colombier if (sts)
214*da917039SDavid du Colombier iprint("ethertemac: sts %#ux\n", sts);
215*da917039SDavid du Colombier }
216*da917039SDavid du Colombier if (r)
217*da917039SDavid du Colombier intrack(bit);
218*da917039SDavid du Colombier return r;
219*da917039SDavid du Colombier }
220*da917039SDavid du Colombier
221*da917039SDavid du Colombier static void
reset(Ether * ether)222*da917039SDavid du Colombier reset(Ether *ether)
223*da917039SDavid du Colombier {
224*da917039SDavid du Colombier Temacsw *ctlr;
225*da917039SDavid du Colombier Temacregs *trp;
226*da917039SDavid du Colombier
227*da917039SDavid du Colombier ctlr = ether->ctlr;
228*da917039SDavid du Colombier trp = ctlr->regs;
229*da917039SDavid du Colombier trp->ie = 0;
230*da917039SDavid du Colombier coherence();
231*da917039SDavid du Colombier /* don't use raf to reset: that resets both interfaces */
232*da917039SDavid du Colombier wrindir(trp, Tc, Rst);
233*da917039SDavid du Colombier while (rdindir(trp, Tc) & Rst)
234*da917039SDavid du Colombier ;
235*da917039SDavid du Colombier wrindir(trp, Rcw1, Rst);
236*da917039SDavid du Colombier while (rdindir(trp, Rcw1) & Rst)
237*da917039SDavid du Colombier ;
238*da917039SDavid du Colombier llfiforeset();
239*da917039SDavid du Colombier }
240*da917039SDavid du Colombier
241*da917039SDavid du Colombier static void
attach(Ether *)242*da917039SDavid du Colombier attach(Ether *)
243*da917039SDavid du Colombier {
244*da917039SDavid du Colombier }
245*da917039SDavid du Colombier
246*da917039SDavid du Colombier static void
transmit(Ether * ether)247*da917039SDavid du Colombier transmit(Ether *ether)
248*da917039SDavid du Colombier {
249*da917039SDavid du Colombier RingBuf *tb;
250*da917039SDavid du Colombier
251*da917039SDavid du Colombier if (ether->tbusy)
252*da917039SDavid du Colombier return;
253*da917039SDavid du Colombier tb = ðer->tb[ether->ti];
254*da917039SDavid du Colombier if (tb->owner != Interface)
255*da917039SDavid du Colombier return;
256*da917039SDavid du Colombier llfifotransmit(tb->pkt, tb->len);
257*da917039SDavid du Colombier coherence();
258*da917039SDavid du Colombier tb->owner = Host;
259*da917039SDavid du Colombier coherence();
260*da917039SDavid du Colombier ether->ti = NEXT(ether->ti, ether->ntb);
261*da917039SDavid du Colombier coherence();
262*da917039SDavid du Colombier }
263*da917039SDavid du Colombier
264*da917039SDavid du Colombier static void
detach(Ether * ether)265*da917039SDavid du Colombier detach(Ether *ether)
266*da917039SDavid du Colombier {
267*da917039SDavid du Colombier reset(ether);
268*da917039SDavid du Colombier }
269*da917039SDavid du Colombier
270*da917039SDavid du Colombier int
temacreset(Ether * ether)271*da917039SDavid du Colombier temacreset(Ether* ether)
272*da917039SDavid du Colombier {
273*da917039SDavid du Colombier int i;
274*da917039SDavid du Colombier ulong ealo, eahi;
275*da917039SDavid du Colombier uvlong ea;
276*da917039SDavid du Colombier Temacsw *ctlr;
277*da917039SDavid du Colombier Temacregs *trp;
278*da917039SDavid du Colombier
279*da917039SDavid du Colombier if ((unsigned)ether->ctlrno >= nelem(ethers) || ethers[ether->ctlrno])
280*da917039SDavid du Colombier return -1; /* already probed & found */
281*da917039SDavid du Colombier trp = (Temacregs *)Temac + ether->ctlrno;
282*da917039SDavid du Colombier if (probeaddr((uintptr)trp) < 0)
283*da917039SDavid du Colombier return -1;
284*da917039SDavid du Colombier
285*da917039SDavid du Colombier ethers[ether->ctlrno] = ether;
286*da917039SDavid du Colombier ether->ctlr = ctlr = malloc(sizeof *ctlr);
287*da917039SDavid du Colombier ctlr->regs = trp;
288*da917039SDavid du Colombier
289*da917039SDavid du Colombier /*
290*da917039SDavid du Colombier * Determine media.
291*da917039SDavid du Colombier */
292*da917039SDavid du Colombier ether->mbps = Defmbps;
293*da917039SDavid du Colombier // ether->mbps = media(ether, 1);
294*da917039SDavid du Colombier
295*da917039SDavid du Colombier /*
296*da917039SDavid du Colombier * Initialise descriptor rings, ethernet address.
297*da917039SDavid du Colombier */
298*da917039SDavid du Colombier ether->nrb = Nrde;
299*da917039SDavid du Colombier ether->ntb = Ntde;
300*da917039SDavid du Colombier ether->rb = malloc(Nrde * sizeof(RingBuf));
301*da917039SDavid du Colombier ether->tb = malloc(Ntde * sizeof(RingBuf));
302*da917039SDavid du Colombier ether->port = Temac;
303*da917039SDavid du Colombier
304*da917039SDavid du Colombier reset(ether);
305*da917039SDavid du Colombier delay(1);
306*da917039SDavid du Colombier
307*da917039SDavid du Colombier llfifoinit(ether);
308*da917039SDavid du Colombier
309*da917039SDavid du Colombier wrindir(trp, Mc, Mdioen | 29); /* 29 is divisor; see p.47 of ds537 */
310*da917039SDavid du Colombier delay(100); /* guess */
311*da917039SDavid du Colombier
312*da917039SDavid du Colombier /*
313*da917039SDavid du Colombier * mac addr is stored little-endian in longs in Uaw[01].
314*da917039SDavid du Colombier * default address is rubbish.
315*da917039SDavid du Colombier */
316*da917039SDavid du Colombier memmove(ether->ea, mymac, Eaddrlen);
317*da917039SDavid du Colombier ea = 0;
318*da917039SDavid du Colombier for (i = 0; i < Eaddrlen; i++)
319*da917039SDavid du Colombier ea |= (uvlong)mymac[i] << (i * 8);
320*da917039SDavid du Colombier wrindir(trp, Uaw0, (ulong)ea);
321*da917039SDavid du Colombier wrindir(trp, Uaw1, (ulong)(ea >> 32));
322*da917039SDavid du Colombier ealo = rdindir(trp, Uaw0);
323*da917039SDavid du Colombier eahi = rdindir(trp, Uaw1) & 0xffff;
324*da917039SDavid du Colombier if (ealo != (ulong)ea || eahi != (ulong)(ea >> 32))
325*da917039SDavid du Colombier panic("temac mac address wouldn't set, got %lux %lux",
326*da917039SDavid du Colombier eahi, ealo);
327*da917039SDavid du Colombier
328*da917039SDavid du Colombier /*
329*da917039SDavid du Colombier * admit broadcast packets too
330*da917039SDavid du Colombier */
331*da917039SDavid du Colombier wrindir(trp, Maw0, ~0ul);
332*da917039SDavid du Colombier wrindir(trp, Maw1, 0xffff); /* write to mat reg 0 */
333*da917039SDavid du Colombier
334*da917039SDavid du Colombier wrindir(trp, Afm, 0); /* not promiscuous */
335*da917039SDavid du Colombier wrindir(trp, Tc, Tx);
336*da917039SDavid du Colombier wrindir(trp, Emmc, Defls);
337*da917039SDavid du Colombier
338*da917039SDavid du Colombier /* intrenable(Inttemac, interrupt); /* done by ether.c */
339*da917039SDavid du Colombier trp->ie = Rxrject | Rxfifoovr; /* just errors */
340*da917039SDavid du Colombier coherence();
341*da917039SDavid du Colombier
342*da917039SDavid du Colombier wrindir(trp, Tc, Tx);
343*da917039SDavid du Colombier wrindir(trp, Rcw1, Rx);
344*da917039SDavid du Colombier
345*da917039SDavid du Colombier /*
346*da917039SDavid du Colombier * Linkage to the generic ethernet driver.
347*da917039SDavid du Colombier */
348*da917039SDavid du Colombier ether->attach = attach;
349*da917039SDavid du Colombier ether->transmit = transmit;
350*da917039SDavid du Colombier ether->interrupt = interrupt;
351*da917039SDavid du Colombier ether->detach = detach;
352*da917039SDavid du Colombier
353*da917039SDavid du Colombier return 0;
354*da917039SDavid du Colombier }
355