1*da917039SDavid du Colombier /*
2*da917039SDavid du Colombier * Xilinx Local Link FIFOs for Temac, in pairs (rx and tx).
3*da917039SDavid du Colombier */
4*da917039SDavid du Colombier #include "include.h"
5*da917039SDavid du Colombier
6*da917039SDavid du Colombier enum {
7*da917039SDavid du Colombier Reset = 0xa5, /* magic [tr]dfr & llr value */
8*da917039SDavid du Colombier
9*da917039SDavid du Colombier /* dmacr; copied from dma.c */
10*da917039SDavid du Colombier Sinc = 1<<31, /* source increment */
11*da917039SDavid du Colombier Dinc = 1<<30, /* dest increment */
12*da917039SDavid du Colombier
13*da917039SDavid du Colombier /* field masks */
14*da917039SDavid du Colombier
15*da917039SDavid du Colombier Bytecnt = (1<<11) - 1,
16*da917039SDavid du Colombier Wordcnt = (1<<9) - 1,
17*da917039SDavid du Colombier };
18*da917039SDavid du Colombier
19*da917039SDavid du Colombier enum { /* register's bits */
20*da917039SDavid du Colombier /* isr, ier registers (*?e->*ee) */
21*da917039SDavid du Colombier Rpure = 1<<31, /* rx packet underrun read error */
22*da917039SDavid du Colombier Rpore = 1<<30, /* rx packet overrun read error */
23*da917039SDavid du Colombier Rpue = 1<<29, /* rx packet underrun error */
24*da917039SDavid du Colombier Tpoe = 1<<28, /* tx packet overrun error */
25*da917039SDavid du Colombier Tc = 1<<27, /* tx complete */
26*da917039SDavid du Colombier Rc = 1<<26, /* rx complete */
27*da917039SDavid du Colombier Tse = 1<<25, /* tx size error */
28*da917039SDavid du Colombier Trc = 1<<24, /* tx reset complete */
29*da917039SDavid du Colombier Rrc = 1<<23, /* rx reset complete */
30*da917039SDavid du Colombier };
31*da917039SDavid du Colombier
32*da917039SDavid du Colombier typedef struct Llfiforegs Llfiforegs;
33*da917039SDavid du Colombier typedef struct Llfifosw Llfifosw;
34*da917039SDavid du Colombier
35*da917039SDavid du Colombier struct Llfiforegs {
36*da917039SDavid du Colombier ulong isr; /* intr status */
37*da917039SDavid du Colombier ulong ier; /* intr enable */
38*da917039SDavid du Colombier
39*da917039SDavid du Colombier ulong tdfr; /* tx data fifo reset */
40*da917039SDavid du Colombier ulong tdfv; /* tx data fifo vacancy (words free) */
41*da917039SDavid du Colombier ulong tdfd; /* tx data fifo write port */
42*da917039SDavid du Colombier ulong tlf; /* tx length fifo */
43*da917039SDavid du Colombier
44*da917039SDavid du Colombier ulong rdfr; /* rx data fifo reset */
45*da917039SDavid du Colombier ulong rdfo; /* rx data fifo occupancy */
46*da917039SDavid du Colombier ulong rdfd; /* rx data fifo read port */
47*da917039SDavid du Colombier ulong rlf; /* tx length fifo */
48*da917039SDavid du Colombier
49*da917039SDavid du Colombier ulong llr; /* locallink reset */
50*da917039SDavid du Colombier };
51*da917039SDavid du Colombier struct Llfifosw {
52*da917039SDavid du Colombier Llfiforegs *regs;
53*da917039SDavid du Colombier };
54*da917039SDavid du Colombier
55*da917039SDavid du Colombier static Llfiforegs *frp = (Llfiforegs *)Llfifo;
56*da917039SDavid du Colombier static Ether *llether;
57*da917039SDavid du Colombier
58*da917039SDavid du Colombier /*
59*da917039SDavid du Colombier * as of dma controller v2, keyhole operations are on ulongs,
60*da917039SDavid du Colombier * but otherwise it's as if memmove were used.
61*da917039SDavid du Colombier * addresses need not be word-aligned, though registers are.
62*da917039SDavid du Colombier */
63*da917039SDavid du Colombier static void
fifocpy(void * vdest,void * vsrc,uint bytes,ulong flags)64*da917039SDavid du Colombier fifocpy(void *vdest, void *vsrc, uint bytes, ulong flags)
65*da917039SDavid du Colombier {
66*da917039SDavid du Colombier int words;
67*da917039SDavid du Colombier ulong *dest, *dstbuf, *src;
68*da917039SDavid du Colombier /* +2*BY2WD is slop for alignment */
69*da917039SDavid du Colombier static uchar buf[ETHERMAXTU+8+2*BY2WD];
70*da917039SDavid du Colombier
71*da917039SDavid du Colombier dest = vdest;
72*da917039SDavid du Colombier src = vsrc;
73*da917039SDavid du Colombier assert(bytes <= sizeof buf);
74*da917039SDavid du Colombier words = bytes / BY2WD;
75*da917039SDavid du Colombier if (bytes % BY2WD != 0)
76*da917039SDavid du Colombier words++;
77*da917039SDavid du Colombier
78*da917039SDavid du Colombier switch (flags & (Sinc | Dinc)) {
79*da917039SDavid du Colombier case Sinc | Dinc:
80*da917039SDavid du Colombier memmove(vdest, vsrc, bytes);
81*da917039SDavid du Colombier break;
82*da917039SDavid du Colombier case Sinc: /* mem to register */
83*da917039SDavid du Colombier src = (ulong *)ROUNDUP((uvlong)buf, BY2WD);
84*da917039SDavid du Colombier memmove(src, vsrc, bytes); /* ensure src alignment */
85*da917039SDavid du Colombier assert((uintptr)src % BY2WD == 0);
86*da917039SDavid du Colombier assert((uintptr)dest % BY2WD == 0);
87*da917039SDavid du Colombier while (words-- > 0)
88*da917039SDavid du Colombier *dest = *src++;
89*da917039SDavid du Colombier break;
90*da917039SDavid du Colombier case Dinc: /* register to mem */
91*da917039SDavid du Colombier dest = dstbuf = (ulong *)ROUNDUP((uvlong)buf, BY2WD);
92*da917039SDavid du Colombier assert((uintptr)src % BY2WD == 0);
93*da917039SDavid du Colombier assert((uintptr)dest % BY2WD == 0);
94*da917039SDavid du Colombier while (words-- > 0)
95*da917039SDavid du Colombier *dest++ = *src;
96*da917039SDavid du Colombier memmove(vdest, dstbuf, bytes); /* ensure dest alignment */
97*da917039SDavid du Colombier break;
98*da917039SDavid du Colombier case 0: /* register-to-null or vice versa */
99*da917039SDavid du Colombier while (words-- > 0)
100*da917039SDavid du Colombier *dest = *src;
101*da917039SDavid du Colombier break;
102*da917039SDavid du Colombier }
103*da917039SDavid du Colombier }
104*da917039SDavid du Colombier
105*da917039SDavid du Colombier static void
discardinpkt(int len)106*da917039SDavid du Colombier discardinpkt(int len) /* discard the rx fifo's packet */
107*da917039SDavid du Colombier {
108*da917039SDavid du Colombier ulong null;
109*da917039SDavid du Colombier
110*da917039SDavid du Colombier fifocpy(&null, &frp->rdfd, len, 0);
111*da917039SDavid du Colombier coherence();
112*da917039SDavid du Colombier }
113*da917039SDavid du Colombier
114*da917039SDavid du Colombier int
llfifointr(ulong bit)115*da917039SDavid du Colombier llfifointr(ulong bit)
116*da917039SDavid du Colombier {
117*da917039SDavid du Colombier ulong len, sts;
118*da917039SDavid du Colombier Ether *ether;
119*da917039SDavid du Colombier RingBuf *rb;
120*da917039SDavid du Colombier static uchar zaddrs[Eaddrlen * 2];
121*da917039SDavid du Colombier
122*da917039SDavid du Colombier sts = frp->isr;
123*da917039SDavid du Colombier if (sts == 0)
124*da917039SDavid du Colombier return 0; /* not for me */
125*da917039SDavid du Colombier ether = llether;
126*da917039SDavid du Colombier /* it's important to drain all packets in the rx fifo */
127*da917039SDavid du Colombier while ((frp->rdfo & Wordcnt) != 0) {
128*da917039SDavid du Colombier assert((frp->rdfo & ~Wordcnt) == 0);
129*da917039SDavid du Colombier len = frp->rlf & Bytecnt; /* read rlf from fifo */
130*da917039SDavid du Colombier assert((len & ~Bytecnt) == 0);
131*da917039SDavid du Colombier assert(len > 0 && len <= ETHERMAXTU);
132*da917039SDavid du Colombier rb = ðer->rb[ether->ri];
133*da917039SDavid du Colombier if (rb->owner == Interface) {
134*da917039SDavid du Colombier /* from rx fifo into ring buffer */
135*da917039SDavid du Colombier fifocpy(rb->pkt, &frp->rdfd, len, Dinc);
136*da917039SDavid du Colombier if (memcmp(rb->pkt, zaddrs, sizeof zaddrs) == 0) {
137*da917039SDavid du Colombier iprint("ether header with all-zero mac "
138*da917039SDavid du Colombier "addresses\n");
139*da917039SDavid du Colombier continue;
140*da917039SDavid du Colombier }
141*da917039SDavid du Colombier rb->len = len;
142*da917039SDavid du Colombier rb->owner = Host;
143*da917039SDavid du Colombier coherence();
144*da917039SDavid du Colombier ether->ri = NEXT(ether->ri, ether->nrb);
145*da917039SDavid du Colombier coherence();
146*da917039SDavid du Colombier } else {
147*da917039SDavid du Colombier discardinpkt(len);
148*da917039SDavid du Colombier /* not too informative during booting */
149*da917039SDavid du Colombier iprint("llfifo: no buffer for input pkt\n");
150*da917039SDavid du Colombier }
151*da917039SDavid du Colombier }
152*da917039SDavid du Colombier
153*da917039SDavid du Colombier if (sts & Tc)
154*da917039SDavid du Colombier ether->tbusy = 0;
155*da917039SDavid du Colombier ether->transmit(ether);
156*da917039SDavid du Colombier
157*da917039SDavid du Colombier frp->isr = sts; /* extinguish intr source */
158*da917039SDavid du Colombier coherence();
159*da917039SDavid du Colombier
160*da917039SDavid du Colombier intrack(bit);
161*da917039SDavid du Colombier sts &= ~(Tc | Rc);
162*da917039SDavid du Colombier if (sts)
163*da917039SDavid du Colombier iprint("llfifo isr %#lux\n", sts);
164*da917039SDavid du Colombier return 1;
165*da917039SDavid du Colombier }
166*da917039SDavid du Colombier
167*da917039SDavid du Colombier void
llfiforeset(void)168*da917039SDavid du Colombier llfiforeset(void)
169*da917039SDavid du Colombier {
170*da917039SDavid du Colombier frp->tdfr = Reset;
171*da917039SDavid du Colombier frp->rdfr = Reset;
172*da917039SDavid du Colombier coherence();
173*da917039SDavid du Colombier while ((frp->isr & (Trc | Rrc)) != (Trc | Rrc))
174*da917039SDavid du Colombier ;
175*da917039SDavid du Colombier }
176*da917039SDavid du Colombier
177*da917039SDavid du Colombier void
llfifoinit(Ether * ether)178*da917039SDavid du Colombier llfifoinit(Ether *ether)
179*da917039SDavid du Colombier {
180*da917039SDavid du Colombier llether = ether;
181*da917039SDavid du Colombier frp->ier = 0;
182*da917039SDavid du Colombier frp->isr = frp->isr; /* extinguish intr source */
183*da917039SDavid du Colombier coherence();
184*da917039SDavid du Colombier
185*da917039SDavid du Colombier intrenable(Intllfifo, llfifointr);
186*da917039SDavid du Colombier coherence();
187*da917039SDavid du Colombier frp->ier = Rc | Tc;
188*da917039SDavid du Colombier coherence();
189*da917039SDavid du Colombier }
190*da917039SDavid du Colombier
191*da917039SDavid du Colombier void
llfifotransmit(uchar * ubuf,unsigned len)192*da917039SDavid du Colombier llfifotransmit(uchar *ubuf, unsigned len)
193*da917039SDavid du Colombier {
194*da917039SDavid du Colombier int wds;
195*da917039SDavid du Colombier
196*da917039SDavid du Colombier llether->tbusy = 1;
197*da917039SDavid du Colombier
198*da917039SDavid du Colombier assert(len <= ETHERMAXTU);
199*da917039SDavid du Colombier wds = ROUNDUP(len, BY2WD) / BY2WD;
200*da917039SDavid du Colombier
201*da917039SDavid du Colombier /* wait for tx fifo to drain */
202*da917039SDavid du Colombier while ((frp->tdfv & Wordcnt) < wds)
203*da917039SDavid du Colombier ;
204*da917039SDavid du Colombier
205*da917039SDavid du Colombier /* to tx fifo */
206*da917039SDavid du Colombier assert((frp->tdfv & ~Wordcnt) == 0);
207*da917039SDavid du Colombier fifocpy(&frp->tdfd, ubuf, len, Sinc);
208*da917039SDavid du Colombier coherence();
209*da917039SDavid du Colombier frp->tlf = len; /* send packet in tx fifo to ether */
210*da917039SDavid du Colombier coherence();
211*da917039SDavid du Colombier }
212