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