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