xref: /plan9-contrib/sys/src/9/vt4/etherlltemac.c (revision d6dfd9ef91cf0fa8514a249d5f2a550978c19369)
1 /*
2  * Xilinx XPS LL Temac Ethernet v1.00b + XPS LL Local Link FIFOs v1.00a driver.
3  *
4  * It uses the Central DMA controller.
5  * There are two interfaces per Temac controller.
6  * Each Temac interface is connected to a pair of FIFOs (tx and rx).
7  * Half-duplex is not supported by hardware.
8  *
9  * NB: The LL FIFO requires that we copy the data for a single packet
10  * into the FIFO, then write the frp->tlr register before writing any
11  * more data into the FIFO.  It forbids copying the data for multiple
12  * packets into the FIFO and then writing frp->tlr with the length of each.
13  */
14 #include "u.h"
15 #include "../port/lib.h"
16 #include "mem.h"
17 #include "dat.h"
18 #include "fns.h"
19 #include "../ip/ip.h"		/* to declare ethermedium */
20 
21 #include "ethermii.h"
22 #include "../port/netif.h"
23 
24 #include "etherif.h"
25 #include "io.h"
26 
27 enum {				/* directly-addressible registers' bits */
28 	/* raf register */
29 	Htrst	= 1<<0,		/* hard temac reset (both ifcs) */
30 	Mcstrej	= 1<<1,		/* reject received multicast dest addr */
31 	Bcstrej	= 1<<2,		/* reject received broadcast dest addr */
32 
33 	/* is, ip, ie register */
34 	Hardacscmplt = 1<<0,	/* hard register access complete */
35 	Autoneg	= 1<<1,		/* auto-negotiation complete */
36 	Rxcmplt	= 1<<2,		/* receive complete */
37 	Rxrject	= 1<<3,		/* receive frame rejected (unknown m'cast?) */
38 	Rxfifoovr = 1<<4,	/* receive fifo overrun */
39 	Txcmplt	= 1<<5,		/* transmit complete */
40 	Rxdcmlock = 1<<6,	/* receive DCM lock (ready for use) */
41 
42 	/* ctl register */
43 	Wen	= 1<<15,	/* write instead of read */
44 
45 	/* ctl register address codes; select other registers */
46 	Rcw0	= 0x200,	/* receive configuration: pause addr low */
47 	Rcw1	= 0x240,	/* ": misc bits + pause addr high */
48 	Tc	= 0x280,	/* tx config */
49 	Fcc	= 0x2c0,	/* flow control */
50 	Emmc	= 0x300,	/* ethernet mac mode config */
51 	Phyc	= 0x320,	/* rgmii/sgmii config */
52 	Mc	= 0x340,	/* mgmt config */
53 	Uaw0	= 0x380,	/* unicast addr word 0 (low-order) */
54 	Uaw1	= 0x384,	/* unicast addr word 1 (high-order) */
55 	Maw0	= 0x388,	/* multicast addr word 0 (low) */
56 	Maw1	= 0x38c,	/* multicast addr word 1 (high + more) */
57 	Afm	= 0x390,	/* addr filter mode */
58 	Tis	= 0x3a0,	/* intr status */
59 	Tie	= 0x3a4,	/* intr enable */
60 	Miimwd	= 0x3b0,	/* mii mgmt write data */
61 	Miimai	= 0x3b4,	/* mii mgmt access initiate */
62 
63 	/* rdy register */
64 	Fabrrr	= 1<<0,		/* fabric read ready */
65 	Miimrr	= 1<<1,		/* mii mgmt read ready */
66 	Miimwr	= 1<<2,		/* mii mgmt write ready */
67 	Afrr	= 1<<3,		/* addr filter read ready */
68 	Afwr	= 1<<4,		/* addr filter write ready */
69 	Cfgrr	= 1<<5,		/* config reg read ready */
70 	Cfgwr	= 1<<6,		/* config reg write ready */
71 	Hardacsrdy = 1<<16,	/* hard reg access ready */
72 };
73 enum {				/* indirectly-addressible registers' bits */
74 	/* Rcw1 register */
75 	Rst	= 1<<31,	/* reset */
76 	Jum	= 1<<30,	/* jumbo frame enable */
77 	Fcs	= 1<<29,	/* in-band fcs enable */
78 	Rx	= 1<<28,	/* rx enable */
79 	Vlan	= 1<<27,	/* vlan frame enable */
80 	Hd	= 1<<26,	/* half-duplex mode (must be 0) */
81 	Ltdis	= 1<<25,	/* length/type field valid check disable */
82 
83 	/* Tc register.  same as Rcw1 but Rx->Tx, Ltdis->Ifg */
84 	Tx	= Rx,		/* tx enable */
85 	Ifg	= Ltdis,	/* inter-frame gap adjustment enable */
86 
87 	/* Fcc register */
88 	Fctx	= 1<<30,	/* tx flow control enable */
89 	Fcrx	= 1<<29,	/* rx flow control enable */
90 
91 	/* Emmc register */
92 	Linkspeed = 3<<30,	/* field */
93 	Ls1000	= 2<<30,	/* Gb */
94 	Ls100	= 1<<30,	/* 100Mb */
95 	Ls10	= 0<<30,	/* 10Mb */
96 	Rgmii	= 1<<29,	/* rgmii mode enable */
97 	Sgmii	= 1<<28,	/* sgmii mode enable */
98 	Gpcs	= 1<<27,	/* 1000base-x mode enable */
99 	Hostifen= 1<<26,	/* host interface enable */
100 	Tx16	= 1<<25,	/* tx 16-bit (vs 8-bit) data ifc enable (0) */
101 	Rx16	= 1<<24,	/* rx 16-bit (vs 8-bit) data ifc enable (0) */
102 
103 	/* Phyc register.  sgmii link speed is Emmc's Linkspeed. */
104 	Rgmiills = 3<<2,	/* field */
105 	Rls1000	= 2<<2,		/* Gb */
106 	Rls100	= 1<<2,		/* 100Mb */
107 	Rls10	= 0<<2,		/* 10Mb */
108 	Rgmiihd	= 1<<1,		/* half-duplex */
109 	Rgmiilink = 1<<0,	/* rgmii link (is up) */
110 
111 	/* Mc register */
112 	Mdioen	= 1<<6,		/* mdio (mii mgmt) enable */
113 
114 	/* Maw1 register */
115 	Rnw	= 1<<23,	/* multicast addr table reg read (vs write) */
116 	Addr	= 3<<16,	/* field */
117 
118 	/* Afm register */
119 	Pm	= 1<<31,	/* promiscuous mode */
120 
121 	/* Tis, Tie register (*rst->*en) */
122 	Fabrrst	= 1<<0,		/* fabric read intr sts (read done) */
123 	Miimrst	= 1<<1,		/* mii mgmt read intr sts (read done) */
124 	Miimwst	= 1<<2,		/* mii mgmt write intr sts (write done) */
125 	Afrst	= 1<<3,		/* addr filter read intr sts (read done) */
126 	Afwst	= 1<<4,		/* addr filter write intr sts (write done) */
127 	Cfgrst	= 1<<5,		/* config read intr sts (read done) */
128 	Cfgwst	= 1<<6,		/* config write intr sts (write done) */
129 };
130 
131 enum {
132 	/* tunable parameters */
133 	Defmbps	= 1000,		/* default Mb/s */
134 	Defls	= Ls1000,	/* must match Defmbps */
135 };
136 
137 enum {				/* fifo's registers' bits */
138 	/* isr, ier registers (*?e -> *ee) */
139 	Rpure	= 1<<31,	/* rx packet underrun read error */
140 	Rpore	= 1<<30,	/* rx packet overrun read error */
141 	Rpue	= 1<<29,	/* rx packet underrun error */
142 	Tpoe	= 1<<28,	/* tx packet overrun error */
143 	FifoTc	= 1<<27,	/* tx complete */
144 	Rc	= 1<<26,	/* rx complete */
145 	Tse	= 1<<25,	/* tx size error */
146 	Trc	= 1<<24,	/* tx reset complete */
147 	Rrc	= 1<<23,	/* rx reset complete */
148 
149 	Errors	= Rpure | Rpore | Rpue | Tpoe | Tse,
150 };
151 enum {				/* fifo constants */
152 	Chan0,			/* dedicated to input */
153 	Chan1,			/* dedicated to output */
154 
155 	Reset	= 0xa5,		/* magic [tr]dfr & llr value */
156 
157 	/* dmacr; copied from dma.c */
158 	Sinc	= 1<<31,	/* source increment */
159 	Dinc	= 1<<30,	/* dest increment */
160 
161 	/* field masks */
162 	Bytecnt	= (1<<11) - 1,
163 	Wordcnt	= (1<<9) - 1,
164 };
165 
166 typedef struct Temacctlr Temacctlr;
167 typedef struct Temacregs Temacregs;
168 typedef struct Llfiforegs Llfiforegs;
169 
170 struct Temacregs {
171 	ulong	raf;		/* reset & addr filter */
172 	ulong	tpf;		/* tx pause frame */
173 	ulong	ifgp;		/* tx inter-frame gap adjustment */
174 	ulong	is;		/* intr status */
175 	ulong	ip;		/* intr pending */
176 	ulong	ie;		/* intr enable */
177 	ulong	pad[2];
178 
179 	ulong	msw;		/* msw data; shared by ifcs */
180 	ulong	lsw;		/* lsw data; shared */
181 	ulong	ctl;		/* control; shared */
182 	ulong	rdy;		/* ready status */
183 	ulong	pad2[4];
184 };
185 struct Temacctlr {		/* software state */
186 	Temacregs *regs;
187 	Lock;
188 	ushort	active;
189 
190 	/* tx state, for Block being sent */
191 	int	txdma;
192 	Block	*tbp;		/* non-nil if dma to fifo in progress */
193 
194 	/* rx state, for packet being read */
195 	int	rxdma;
196 	long	rlf;		/* read from frp->rlf iff non-negative */
197 	ulong	fifoier;
198 	Block	*rbp;		/* non-nil if dma from fifo in progress */
199 };
200 
201 struct Llfiforegs {
202 	ulong	isr;		/* intr status */
203 	ulong	ier;		/* intr enable */
204 
205 	ulong	tdfr;		/* tx data fifo reset */
206 	ulong	tdfv;		/* tx data fifo vacancy (words free) */
207 	ulong	tdfd;		/* tx data fifo write port */
208 	ulong	tlf;		/* tx length fifo */
209 
210 	ulong	rdfr;		/* rx data fifo reset */
211 	ulong	rdfo;		/* rx data fifo occupancy */
212 	ulong	rdfd;		/* rx data fifo read port */
213 	ulong	rlf;		/* rx length fifo */
214 
215 	ulong	llr;		/* locallink reset */
216 };
217 
218 typedef struct {
219 	ulong	bit;
220 	char	*msg;
221 } Error;
222 
223 extern int dmaready;		/* flag: ready for general use? */
224 extern uchar mymac[Eaddrlen];
225 
226 static Error errs[] = {
227 	Rpure,	"rx pkt underrun read",
228 	Rpore,	"rx pkt overrun read",
229 	Rpue,	"rx pkt underrun",
230 	Tpoe,	"tx pkt overrun",
231 	Tse,	"tx size",
232 };
233 
234 static Ether *ethers[1];	/* only first ether is connected to a fifo */
235 static Llfiforegs *frp = (Llfiforegs *)Llfifo;
236 
237 int	fifointr(ulong bit);
238 
239 static	void	fiforeset(Ether *ether);
240 static	int	dmatxstart(Ether *);
241 
242 static void
getready(Temacregs * trp)243 getready(Temacregs *trp)
244 {
245 	while ((trp->rdy & Hardacsrdy) == 0)
246 		;
247 }
248 
249 static ulong
rdindir(Temacregs * trp,unsigned code)250 rdindir(Temacregs *trp, unsigned code)
251 {
252 	ulong val;
253 
254 	getready(trp);
255 	trp->ctl = code;
256 	barriers();
257 
258 	getready(trp);
259 	val = trp->lsw;
260 	return val;
261 }
262 
263 static int
wrindir(Temacregs * trp,unsigned code,ulong val)264 wrindir(Temacregs *trp, unsigned code, ulong val)
265 {
266 	getready(trp);
267 	trp->lsw = val;
268 	barriers();
269 	trp->ctl = Wen | code;
270 	barriers();
271 
272 	getready(trp);
273 	return 0;
274 }
275 
276 /*
277  * we're only interested in temac errors; completion interrupts come
278  * from the fifo.
279  */
280 static int
temacintr(ulong bit)281 temacintr(ulong bit)
282 {
283 	int e, forme, sts;
284 	Ether *ether;
285 	Temacctlr *ctlr;
286 	Temacregs *trp;
287 
288 	forme = 0;
289 	for (e = 0; e < nelem(ethers); e++) {
290 		ether = ethers[e];
291 		if (ether == nil)
292 			continue;
293 		ether->interrupts++;
294 		ctlr = ether->ctlr;
295 		trp = ctlr->regs;
296 		while ((sts = trp->is) & (Autoneg | Rxfifoovr)) {
297 			forme = 1;
298 			if (sts & Rxfifoovr) {
299 				iprint("temac: receive fifo overrun.  ");
300 				whackether(ether);
301 			}
302 			if (sts & Autoneg) {
303 				iprint("temac: autoneg done\n");
304 				trp->is = Autoneg;  /* extinguish intr source */
305 				barriers();
306 			}
307 		}
308 	}
309 	if (forme)
310 		intrack(bit);
311 	return forme;
312 }
313 
314 static void
resetsw(Ether * ether)315 resetsw(Ether *ether)			/* reset software state */
316 {
317 	Temacctlr *ctlr;
318 
319 	ctlr = ether->ctlr;
320 
321 	delay(20);		/* let any dma in progress complete */
322 	ctlr->active = ctlr->txdma = ctlr->rxdma = 0;
323 	ctlr->rlf = -1;
324 	ctlr->rbp = ctlr->tbp = nil;
325 	barriers();
326 }
327 
328 static void
reset(Ether * ether)329 reset(Ether *ether)
330 {
331 	Temacctlr *ctlr;
332 	Temacregs *trp;
333 
334 	ctlr = ether->ctlr;
335 	trp = ctlr->regs;
336 	assert(trp);
337 
338 	trp->raf = Htrst;	/* resets both ether interfaces */
339 	barriers();
340 	while (trp->raf & Htrst)
341 		;
342 
343 	fiforeset(ether);
344 	barriers();
345 
346 	resetsw(ether);
347 }
348 
349 static void
attach(Ether *)350 attach(Ether *)
351 {
352 }
353 
354 static void
closed(Ether * ether)355 closed(Ether *ether)
356 {
357 	Temacctlr *ctlr;
358 
359 	ctlr = ether->ctlr;
360 	if(ctlr->active){
361 		reset(ether);
362 		ctlr->active = 0;
363 	}
364 }
365 
366 static void
temacshutdown(Ether * ether)367 temacshutdown(Ether* ether)
368 {
369 	reset(ether);		/* stop dma at the very least */
370 }
371 
372 static int promon, multion;
373 
374 static void
promiscuous(void * arg,int on)375 promiscuous(void* arg, int on)
376 {
377 	Ether *ether;
378 	Temacctlr *ctlr;
379 
380 	ether = (Ether*)arg;
381 	ctlr = ether->ctlr;
382 	ilock(ctlr);
383 	promon = on;
384 	if(on) {
385 		ether->promisc = 1;
386 		wrindir(ctlr->regs, Afm, Pm);
387 	} else if (!multion) {
388 		ether->promisc = 0;
389 		wrindir(ctlr->regs, Afm, 0);
390 	}
391 	iunlock(ctlr);
392 }
393 
394 static void
multicast(void * arg,uchar * addr,int on)395 multicast(void* arg, uchar *addr, int on)
396 {
397 	Ether *ether;
398 	Temacctlr *ctlr;
399 
400 	ether = (Ether*)arg;
401 	ctlr = ether->ctlr;
402 	multion = on;
403 	ilock(ctlr);
404 	/*
405 	 * could do better: set Maw[01] & Rnw since ipv6 needs multicast;
406 	 * see netmulti() in netif.c.
407 	 */
408 	USED(addr);
409 	if(on) {
410 		ether->promisc = 1;
411 		wrindir(ctlr->regs, Afm, Pm);		/* overkill */
412 	} else if (!promon) {
413 		ether->promisc = 0;
414 		wrindir(ctlr->regs, Afm, 0);
415 	}
416 	iunlock(ctlr);
417 }
418 
419 /*
420  * start next dma into tx fifo, if there's a pkt in the output queue,
421  * room in the tx fifo, and the dma channel is idle.
422  *
423  * called for each new packet, but okay to send
424  * any and all packets in output queue.
425  */
426 void
temactransmit(Ether * ether)427 temactransmit(Ether *ether)
428 {
429 	Temacctlr *ctlr;
430 
431 	ctlr = ether->ctlr;
432 	ilock(ctlr);
433 	dmatxstart(ether);
434 	iunlock(ctlr);
435 }
436 
437 /*
438  * allocate receive buffer space on cache-line boundaries
439  */
440 Block*
clallocb(void)441 clallocb(void)
442 {
443 	Block *bp;
444 
445 	/* round up for sake of word-at-a-time dma */
446 	bp = iallocb(ROUNDUP(ETHERMAXTU, BY2WD) + DCACHELINESZ-1);
447 	if(bp == nil)
448 		return bp;
449 	dcflush(PTR2UINT(bp->base), BALLOC(bp));
450 	bp->wp = bp->rp = (uchar*)
451 		((PTR2UINT(bp->base) + DCACHELINESZ - 1) & ~(DCACHELINESZ-1));
452 	return bp;
453 }
454 
455 static long
ifstat(Ether * ether,void * a,long n,ulong offset)456 ifstat(Ether* ether, void* a, long n, ulong offset)
457 {
458 	char *p;
459 	int len;
460 
461 	if(n == 0)
462 		return 0;
463 
464 	p = malloc(READSTR);
465 
466 	len = snprint(p, READSTR, "interrupts: %lud\n", ether->interrupts);
467 	len += snprint(p+len, READSTR-len, "dma rx intrs: %lud\n",
468 		ether->dmarxintr);
469 	len += snprint(p+len, READSTR-len, "dma tx intrs: %lud\n",
470 		ether->dmatxintr);
471 	len += snprint(p+len, READSTR-len, "broadcasts rcvd: %lud\n",
472 		ether->bcasts);
473 	len += snprint(p+len, READSTR-len, "multicasts rcvd: %lud\n",
474 		ether->mcasts);
475 	len += snprint(p+len, READSTR-len, "promiscuous: %lud\n",
476 		ether->promisc);
477 	len += snprint(p+len, READSTR-len, "dropped pkts: %lud\n",
478 		ether->pktsdropped);
479 	len += snprint(p+len, READSTR-len, "resets: %lud\n", ether->resets);
480 	snprint(p+len, READSTR-len, "misaligned buffers: %lud\n",
481 		ether->pktsmisaligned);
482 
483 	n = readstr(offset, a, n, p);
484 	free(p);
485 
486 	return n;
487 }
488 
489 static void
init(Ether * ether)490 init(Ether *ether)
491 {
492 	int i;
493 	ulong ealo, eahi;
494 	uvlong ea;
495 	Temacctlr *ctlr;
496 	Temacregs *trp;
497 	static uchar pausemac[Eaddrlen] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x01 };
498 
499 	trp = (Temacregs *)Temac + ether->ctlrno;
500 	ctlr = ether->ctlr;
501 
502 	wrindir(trp, Mc, Mdioen | 29);	/* 29 is divisor; see p.47 of ds537 */
503 	delay(100);			/* guess */
504 
505 	/*
506 	 * mac addr is stored little-endian in longs in Uaw[01].
507 	 * default address is rubbish.
508 	 */
509 	memmove(ether->ea, mymac, Eaddrlen);
510 	ea = 0;
511 	for (i = 0; i < Eaddrlen; i++)
512 		ea |= (uvlong)mymac[i] << (i * 8);
513 	wrindir(trp, Uaw0, (ulong)ea);
514 	wrindir(trp, Uaw1, (ulong)(ea >> 32));
515 	ealo = rdindir(trp, Uaw0);
516 	eahi = rdindir(trp, Uaw1) & 0xffff;
517 	if (ealo != (ulong)ea || eahi != (ulong)(ea >> 32))
518 		panic("temac mac address wouldn't set, got %lux %lux",
519 			eahi, ealo);
520 
521 	/*
522 	 * admit broadcast packets too
523 	 */
524 	wrindir(trp, Maw0, ~0ul);
525 	wrindir(trp, Maw1, 0xffff);	/* write to mat reg 0 */
526 
527 	wrindir(trp, Afm, 0);		/* not promiscuous */
528 	wrindir(trp, Tc, Tx);
529 	ether->promisc = 0;
530 
531 if (0) {
532 	ea = 0;
533 	for (i = 0; i < Eaddrlen; i++)
534 		ea |= (uvlong)pausemac[i] << (i * 8);
535 	wrindir(trp, Rcw0, (ulong)ea);
536 	wrindir(trp, Rcw1, (ulong)(ea >> 32) | Rx);
537 	wrindir(trp, Fcc, Fcrx);	/* honour pause frames sent to us */
538 } else
539 	wrindir(trp, Fcc, 0);		/* no flow control */
540 
541 	wrindir(trp, Emmc, Defls);
542 
543 	ctlr->active = 1;
544 	barriers();
545 
546 	intrenable(Inttemac, temacintr, "lltemac");
547 	trp->ie = Autoneg | Rxfifoovr;
548 	barriers();
549 
550 	fifoinit(ether);
551 }
552 
553 int
temacreset(Ether * ether)554 temacreset(Ether* ether)
555 {
556 	Temacctlr *ctlr;
557 	Temacregs *trp;
558 
559 	if ((unsigned)ether->ctlrno >= nelem(ethers) || ethers[ether->ctlrno])
560 		return -1;		/* already probed & found */
561 	trp = (Temacregs *)Temac + ether->ctlrno;
562 
563 	/* too early to probe on virtex 4 at least; up must be set first */
564 //	if (probeaddr((uintptr)trp) < 0)
565 //		return -1;
566 
567 	ethers[ether->ctlrno] = ether;
568 	ether->ctlr = ctlr = malloc(sizeof *ctlr);
569 	ctlr->regs = trp;
570 	ether->port = (uintptr)trp;
571 	trp->ie = 0;
572 	barriers();
573 	ether->interrupts = 0;		/* should be redundant */
574 
575 	/* we can't tell, so assert what we hope for */
576 	ether->mbps = Defmbps;
577 	ether->fullduplex = ether->link = 1;
578 
579 	/*
580 	 * whatever loaded this kernel (9load or another kernel)
581 	 * must have configured the ethernet in order to use it to load
582 	 * this kernel.  so try not to reset the hardware, which could
583 	 * force a seconds-long link negotiation.
584 	 */
585 	reset(ether);
586 //	resetsw(ether);
587 	init(ether);
588 	/*
589 	 * Linkage to the generic ethernet driver.
590 	 */
591 	ether->attach = attach;
592 	ether->closed = closed;
593 	ether->shutdown = temacshutdown;
594 	ether->transmit = temactransmit;
595 	ether->interrupt = temacintr;
596 	ether->ifstat = ifstat;
597 
598 	ether->arg = ether;
599 	ether->promiscuous = promiscuous;
600 	ether->multicast = multicast;
601 	return 0;
602 }
603 
604 void
etherlltemaclink(void)605 etherlltemaclink(void)
606 {
607 	addethercard("lltemac", temacreset);
608 }
609 
610 /*
611  * Xilinx Local Link FIFOs for Temac, in pairs (rx and tx).
612  */
613 
614 /*
615  * as of dma controller v2, keyhole operations are on ulongs,
616  * but otherwise it's as if memmove were used.
617  * addresses need not be word-aligned, though registers are.
618  */
619 static void
fifocpy(void * vdest,void * vsrc,uint bytes,ulong flags)620 fifocpy(void *vdest, void *vsrc, uint bytes, ulong flags)
621 {
622 	int words;
623 	ulong *dest, *src;
624 	uchar buf[ETHERMAXTU + 2*BY2WD];
625 
626 	dest = vdest;
627 	src = vsrc;
628 	words = bytes / BY2WD;
629 	if (bytes % BY2WD != 0)
630 		words++;
631 
632 	switch (flags & (Sinc | Dinc)) {
633 	case Sinc | Dinc:
634 		memmove(vdest, vsrc, bytes);
635 		break;
636 	case Sinc:				/* mem to register */
637 		memmove(buf, vsrc, bytes);	/* ensure src alignment */
638 		src = (ulong *)buf;
639 		while (words-- > 0)
640 			*dest = *src++;
641 		break;
642 	case Dinc:				/* register to mem */
643 		dest = (ulong *)buf;
644 		while (words-- > 0)
645 			*dest++ = *src;
646 		memmove(vdest, buf, bytes);	/* ensure dest alignment */
647 		break;
648 	case 0:				/* register-to-null or vice versa */
649 		while (words-- > 0)
650 			*dest = *src;
651 		break;
652 	}
653 }
654 
655 /* returns true iff we whacked the ether */
656 static int
whackiferr(Ether * ether,int whack,int sts)657 whackiferr(Ether *ether, int whack, int sts)
658 {
659 	Error *ep;
660 
661 	/*
662 	 * these require a reset of the receive logic: Rpure, Rpore, Rpue.
663 	 * same for transmit logic: Tpoe, Tse.
664 	 */
665 	if (whack || sts & Errors) {
666 		iprint("fifo: errors:");
667 		for (ep = errs; ep < errs + nelem(errs); ep++)
668 			if (sts & ep->bit)
669 				iprint(" %s;", ep->msg);
670 		iprint("\n");
671 		whackether(ether);
672 		return 1;
673 	}
674 	return 0;
675 }
676 
677 static int dmarecv(Ether *);
678 
679 static void
fifointrset(Temacctlr * ctlr)680 fifointrset(Temacctlr *ctlr)
681 {
682 	frp->ier = ctlr->fifoier;
683 	barriers();
684 }
685 
686 static void
enafifointr(Temacctlr * ctlr,int bits)687 enafifointr(Temacctlr *ctlr, int bits)
688 {
689 	ctlr->fifoier |= bits;
690 	fifointrset(ctlr);
691 }
692 
693 static void
disfifointr(Temacctlr * ctlr,int bits)694 disfifointr(Temacctlr *ctlr, int bits)
695 {
696 	ctlr->fifoier &= ~bits;
697 	fifointrset(ctlr);
698 }
699 
700 static long
getrdfo(void)701 getrdfo(void)
702 {
703 	ulong rdfo;
704 
705 	rdfo = frp->rdfo;
706 	if (rdfo & ~Wordcnt) {
707 		iprint("fifo: impossible rdfo value\n");
708 		return -1;
709 	}
710 	return rdfo;
711 }
712 
713 static void
dmarxdone(int)714 dmarxdone(int)
715 {
716 	int whack, multi;
717 	long rdfo;
718 	Block *bp;
719 	Ether *ether;
720 	Etherpkt *pkt;
721 	Temacctlr *ctlr;
722 
723 	ether = ethers[0];
724 	ctlr = ether->ctlr;
725 
726 	ilock(ctlr);
727 	ether->dmarxintr++;
728 	bp = ctlr->rbp;
729 	ctlr->rbp = nil;			/* prevent accidents */
730 	if (ctlr->rxdma == 0 || bp == nil) {
731 		if (bp != nil)
732 			freeb(bp);
733 		if (ctlr->rxdma == 0)
734 			iprint("dmarxdone: no rx dma in progress\n");
735 		else
736 			iprint("dmarxdone: no block for rx dma just finished!\n");
737 		lightbitoff(Ledethinwait);
738 		enafifointr(ctlr, Rc);
739 		iunlock(ctlr);
740 		return;
741 	}
742 	ctlr->rxdma = 0;
743 	barriers();
744 
745 	/*
746 	 * rx Step 2: packet is in Block, pass it upstream.
747 	 */
748 	/* could check for dma errors */
749 	pkt = (Etherpkt*)bp->rp;
750 	assert(pkt != nil);
751 	multi = pkt->d[0] & 1;
752 	if (multi)
753 		if(memcmp(pkt->d, ether->bcast, sizeof pkt->d) == 0)
754 			ether->bcasts++;
755 		else
756 			ether->mcasts++;
757 
758 	etheriq(ether, bp, 1);
759 	lightbitoff(Ledethinwait);
760 
761 	/*
762 	 * rx Step 3/0: if there's another packet in the rx fifo,
763 	 * start dma into a new Block, else reenable recv intrs.
764 	 */
765 	whack = 0;
766 	rdfo = getrdfo();
767 	if (rdfo < 0)
768 		whack = 1;		/* ether is buggered */
769 	else if (rdfo > 0)		/* more packets in rx fifo? */
770 		whack = dmarecv(ether);	/* if dma starts, disables Rc */
771 	else {
772 		if (frp->isr & Rc)
773 			wave('|');	/* isr Rc was set when fifo was empty */
774 		enafifointr(ctlr, Rc);
775 	}
776 	/* if this whacks the ctlr, all the intr enable bits will be set */
777 	whackiferr(ether, whack, frp->isr);
778 	iunlock(ctlr);
779 }
780 
781 static void
discard(Ether * ether,unsigned ruplen)782 discard(Ether *ether, unsigned ruplen)
783 {
784 	ulong null;
785 
786 	/* discard the rx fifo's packet */
787 	fifocpy(&null, &frp->rdfd, ruplen, 0);
788 	ether->pktsdropped++;
789 }
790 
791 /*
792  * called when interrupt cause Rc is set (the rx fifo has at least one packet)
793  * to begin dma to memory of the next packet in the input fifo.
794  *
795  * returns true iff the ether ctlr needs to be whacked.
796  * may be called from interrupt routine; must be called with
797  * interrupts masked.
798  */
799 static int
dmarecv(Ether * ether)800 dmarecv(Ether *ether)
801 {
802 	long rdfo;
803 	ulong len, ruplen;
804 	Block *bp;
805 	Temacctlr *ctlr;
806 
807 	ctlr = ether->ctlr;
808 	if (ctlr->rxdma)
809 		return 0;  /* next rx dma interrupt should process this packet*/
810 
811 	/* ignore frp->isr & Rc; just look at rx fifo occupancy */
812 	rdfo = getrdfo();
813 	if (rdfo < 0) {
814 		iprint("dmarecv: negative rdfo\n");
815 		return 1;		/* ether is buggered */
816 	}
817 	if (rdfo == 0)
818 		return 0;		/* no packets in the rx fifo */
819 
820 	if (!(frp->isr & Rc))
821 		wave('@');  /* isr Rc wasn't set when fifo had stuff in it */
822 
823 	/*
824 	 * We have at least one packet in the rx fifo.  Read the length
825 	 * of the first one, if not already known.
826 	 */
827 	if (ctlr->rlf >= 0)
828 		len = ctlr->rlf;	/* from a previous call */
829 	else {
830 		assert(frp != nil);
831 		/* read length word from rx fifo */
832 		len = frp->rlf;
833 		if (len & ~Bytecnt) {
834 			iprint("fifo: impossible rlf value\n");
835 			return 1;
836 		}
837 		if (len == 0) {
838 			iprint("fifo: rdfo %lud > 0 but rlf == 0\n", rdfo);
839 			return 1;
840 		}
841 		ctlr->rlf = len;	/* save in case dma is busy below */
842 	}
843 
844 	ruplen = ROUNDUP(len, BY2WD);
845 	if (len > ETHERMAXTU) {
846 		iprint("fifo: jumbo pkt tossed\n");
847 		discard(ether, ruplen);
848 		return 0;
849 	}
850 
851 	if (!dmaready)			/* too early, dma not really set up */
852 		return 0;
853 	bp = clallocb();
854 	if(bp == nil){
855 		iprint("fifo: no buffer for input pkt\n");
856 		discard(ether, ruplen);
857 		ether->soverflows++;
858 		return 0;
859 	}
860 
861 	/*
862 	 * rx Step 1: dma from rx fifo into Block, turn off recv interrupts.
863 	 * wait for dmarxdone (interrupt) to pass the Block upstream.
864 	 */
865 	if (!dmastart(Chan0, bp->rp, &frp->rdfd, ruplen, Dinc, dmarxdone)) {
866 		/* should never get here */
867 		iprint("dmarecv: dmastart failed for Chan0\n");
868 		freeb(bp);
869 		enafifointr(ctlr, Rc);
870 		/* we'll try again next time we're called */
871 		return 0;
872 	}
873 	ctlr->rlf = -1;			/* we're committed now */
874 	lightbiton(Ledethinwait);
875 	bp->wp = bp->rp + len;
876 	assert(ctlr->rbp == nil);
877 	ctlr->rbp = bp;
878 	ctlr->rxdma = 1;
879 	barriers();
880 	/*
881 	 * we're waiting for dma and can't start dma of another
882 	 * incoming packet until the current dma is finished.
883 	 */
884 	disfifointr(ctlr, Rc);
885 	return 0;
886 }
887 
888 void
whackether(Ether * ether)889 whackether(Ether *ether)
890 {
891 	int s = splhi();
892 
893 	iprint("resetting fifo+temac...");
894 	reset(ether);
895 	init(ether);
896 	iprint("\n");
897 	ether->resets++;
898 	splx(s);
899 }
900 
901 /*
902  * we've had trouble transmitting 60-byte packets and
903  * 77-byte packets, so round up the length to make it even
904  * and enforce a minimum of 64 bytes (ETHERMINTU+4).
905  * rounding to a multiple of 4 (rather than 2) will make 1514
906  * into 1516, which is a jumbo packet, so take care.
907  * it's a bit sleazy, but this will just pick up a few junk
908  * bytes beyond the packet for padding.
909  */
910 static uint
padpktlen(uint len)911 padpktlen(uint len)
912 {
913 	len = ROUNDUP(len, BY2WD);
914 	if (len > ethermedium.maxtu)
915 		len = ethermedium.maxtu;
916 	return len;
917 }
918 
919 int
fifointr(ulong bit)920 fifointr(ulong bit)
921 {
922 	int r, whack, ic;
923 	ulong sts;
924 	Ether *ether;
925 
926 	ether = ethers[0];
927 	if (ether == nil)
928 		return 0;			/* not for me */
929 	ether->interrupts++;
930 	r = 0;
931 	while ((sts = frp->isr) != 0) {
932 		ic = sts & (Rc | FifoTc);	/* interrupt causes */
933 		r |= ic;
934 		whack = 0;
935 		if (sts & Rc)
936 			whack = dmarecv(ether);
937 		else
938 			if (getrdfo() != 0)
939 				wave('~');  /* isr Rc off, yet fifo !empty */
940 		if (sts & FifoTc) {
941 			/*
942 			 * not much to do, really.  turn out the light and
943 			 * attempt another dma transfer anyway.
944 			 */
945 			lightbitoff(Ledethoutwait);
946 			temactransmit(ether);
947 		}
948 		frp->isr = ic;			/* extinguish intr sources */
949 		barriers();
950 		sts &= ~ic;
951 		if (sts)
952 			iprint("fifointr: sts %#lux\n", sts);
953 		r |= whackiferr(ether, whack, sts);
954 	}
955 	if (r) {
956 		intrack(bit);
957 		return 1;
958 	}
959 	return 0;
960 }
961 
962 static void
fiforeset(Ether * ether)963 fiforeset(Ether *ether)
964 {
965 	Temacctlr *ctlr;
966 
967 	barriers();
968 	dma0init();	/* could be dma in progress, so shut that down */
969 
970 	ctlr = ether->ctlr;
971 	ctlr->fifoier = 0;
972 	fifointrset(ctlr);
973 
974 	/* should put a timeout on this and do a hard reset if it fails */
975 	frp->tdfr = Reset;	/* try it the graceful way first */
976 	frp->rdfr = Reset;
977 	barriers();
978 	while ((frp->isr & (Trc | Rrc)) != (Trc | Rrc))
979 		;
980 	frp->isr = Trc | Rrc;	/* reset the `reset done' bits */
981 	barriers();
982 
983 if (0) {
984 	frp->llr = Reset;
985 	barriers();
986 	while ((frp->isr & (Trc | Rrc)) != (Trc | Rrc))
987 		;
988 }
989 }
990 
991 void
fifoinit(Ether * ether)992 fifoinit(Ether *ether)
993 {
994 	if (ethers[0] != nil)
995 		assert(ethers[0] == ether);
996 //	fiforeset(ether);
997 	frp->isr = frp->isr;		/* extinguish intr source */
998 	barriers();
999 
1000 	intrenable(Intllfifo, fifointr, "fifo");
1001 	barriers();
1002 	enafifointr(ether->ctlr, Rc | FifoTc | Rpure | Rpore | Rpue | Tpoe | Tse);
1003 }
1004 
1005 static void
tmoutreset(ulong bit)1006 tmoutreset(ulong bit)
1007 {
1008 	USED(bit);
1009 	whackether(ethers[0]);
1010 }
1011 
1012 /*
1013  * tx Step 2: write frp->tlr, thus initiating ethernet transmission of
1014  * the Block just copied into the tx fifo, and free that Block.
1015  */
1016 static void
dmatxdone(int)1017 dmatxdone(int)
1018 {
1019 	Block *bp;
1020 	Ether *ether;
1021 	Temacctlr *ctlr;
1022 
1023 	ether = ethers[0];
1024 	ctlr = ether->ctlr;
1025 
1026 	ilock(ctlr);
1027 	ether->dmatxintr++;
1028 	ctlr->txdma = 0;
1029 
1030 	/*
1031 	 * start transmitting this packet from the output fifo.
1032 	 * contrary to DS568 Table 5's description of TSE, it seems
1033 	 * to be legal to write an odd value into tlf (unless the word
1034 	 * `match' implies ±1), but it may be necessary to provide a
1035 	 * padding byte in the fifo if you do.
1036 	 */
1037 	bp = ctlr->tbp;
1038 	if (bp != nil) {
1039 		ctlr->tbp = nil;
1040 		frp->tlf = padpktlen(BLEN(bp));
1041 		barriers();
1042 		freeb(bp);
1043 		ether->tbusy = 0;
1044 	}
1045 
1046 	dmatxstart(ether);		/* attempt another dma to tx fifo */
1047 	iunlock(ctlr);
1048 }
1049 
1050 /*
1051  * if possible, start dma of the first packet of the output queue into
1052  * the tx fifo.
1053  *
1054  * must be called with ether->ctlr ilocked,
1055  * thus we cannot sleep nor qlock.
1056  *
1057  * output buffers are always misaligned, but that doesn't matter
1058  * as of v2 of the dma controller.
1059  */
1060 static int
dmatxstart(Ether * ether)1061 dmatxstart(Ether *ether)
1062 {
1063 	unsigned len, ruplen;
1064 	Block *bp;
1065 	Temacctlr *ctlr;
1066 
1067 	if (ether == nil || ether->oq == nil || ether->tbusy)
1068 		return 0;
1069 	ctlr = ether->ctlr;
1070 	if (ctlr->txdma)
1071 		return 0;
1072 	SET(len);
1073 	while ((bp = qget(ether->oq)) != nil) {
1074 		len = padpktlen(BLEN(bp));
1075 		if (len > ETHERMAXTU) {
1076 			iprint("fifo: dropping outgoing jumbo (%ud) pkt\n",
1077 				len);
1078 			freeb(bp);
1079 		} else
1080 			break;
1081 	}
1082 	if (bp == nil)
1083 		return 0;
1084 	if (!dmaready) {		/* too early? */
1085 		iprint("dmatxstart: dma not ready\n");
1086 		freeb(bp);
1087 		disfifointr(ctlr, FifoTc);
1088 		return 0;
1089 	}
1090 
1091 	if (len == 0)
1092 		print("idiot sending zero-byte packet\n");
1093 	ruplen = ROUNDUP(len, BY2WD);	/* must be multiple of 4 for dma */
1094 
1095 	/*
1096 	 * if there isn't enough room in the fifo for this
1097 	 * packet, return and assume that the next transmit
1098 	 * interrupt will resume transmission of it.
1099 	 */
1100 	if ((frp->tdfv & Wordcnt) < ruplen / BY2WD) {
1101 		iprint("dmatxstart: no room in tx fifo\n");
1102 		return 0;
1103 	}
1104 
1105 	/* tx Step 1: dma to tx fifo, wait for dma tx interrupt */
1106 	lightbiton(Ledethoutwait);
1107 	if (dmastart(Chan1, &frp->tdfd, bp->rp, ruplen, Sinc, dmatxdone)) {
1108 		ether->tbusy = 1;
1109 		ctlr->txdma = 1;
1110 		barriers();
1111 		/* Rc may be off if we got Rc intrs too early */
1112 //		enafifointr(ctlr, FifoTc | Rc);
1113 		ctlr->tbp = bp;			/* remember this block */
1114 	} else
1115 		iprint("dmatxstart: dmastart failed for Chan1\n");
1116 	return 1;
1117 }
1118