xref: /plan9-contrib/sys/src/boot/vt4/llfifo.c (revision da917039c7f233c1a27d212bf012c6afa758af39)
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 = &ether->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