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