fec.c revision 1.1 1 /* $NetBSD: fec.c,v 1.1 2026/06/27 13:28:34 rkujawa Exp $ */
2
3 /*-
4 * Copyright (c) 2008, 2026 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Radoslaw Kujawa and Robert Swindells.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * Driver for the MPC5200B Fast Ethernet Controller (FEC).
34 */
35
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: fec.c,v 1.1 2026/06/27 13:28:34 rkujawa Exp $");
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/device.h>
42 #include <sys/callout.h>
43 #include <sys/mbuf.h>
44 #include <sys/bus.h>
45
46 #include <net/if.h>
47 #include <net/if_dl.h>
48 #include <net/if_ether.h>
49 #include <net/if_media.h>
50 #include <net/bpf.h>
51
52 #include <dev/mii/mii.h>
53 #include <dev/mii/miivar.h>
54
55 #include <dev/ofw/openfirm.h>
56
57 #include <machine/autoconf.h>
58
59 #include <powerpc/mpc5200/obiovar.h>
60 #include <powerpc/mpc5200/mpc5200reg.h>
61 #include <powerpc/mpc5200/fecreg.h>
62 #include <powerpc/mpc5200/fecvar.h>
63 #include <powerpc/mpc5200/cdmvar.h>
64 #include <powerpc/mpc5200/bestcommvar.h>
65
66 #define FEC_READ(sc, r) bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (r))
67 #define FEC_WRITE(sc, r, v) bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (r), (v))
68
69 #define FEC_RX_TASK 3 /* TASK_FEC_RX image slot */
70 #define FEC_TX_TASK 2 /* TASK_FEC_TX image slot */
71 #define FEC_RX_INITIATOR 3 /* INITIATOR_FEC_RX */
72 #define FEC_TX_INITIATOR 4 /* INITIATOR_FEC_TX */
73
74 static const struct bestcomm_bd_layout fec_rx_layout = BESTCOMM_LAYOUT_FEC_RX;
75 static const struct bestcomm_bd_layout fec_tx_layout = BESTCOMM_LAYOUT_FEC_TX;
76
77 static int fec_match(device_t, cfdata_t, void *);
78 static void fec_attach(device_t, device_t, void *);
79
80 static int fec_miibus_readreg(device_t, int, int, uint16_t *);
81 static int fec_miibus_writereg(device_t, int, int, uint16_t);
82 static void fec_miibus_statchg(struct ifnet *);
83
84 static int fec_ioctl(struct ifnet *, u_long, void *);
85 static int fec_init(struct ifnet *);
86 static void fec_stop(struct ifnet *, int);
87 static void fec_start(struct ifnet *);
88 static void fec_tick(void *);
89
90 static void fec_reset(struct fec_softc *);
91 static void fec_mac_setup(struct fec_softc *);
92 static int fec_newbuf(struct fec_softc *, u_int);
93 static int fec_intr(void *);
94 static int fec_rxintr(void *);
95 static int fec_txintr(void *);
96
97 CFATTACH_DECL_NEW(fec, sizeof(struct fec_softc),
98 fec_match, fec_attach, NULL, NULL);
99
100 static int
101 fec_match(device_t parent, cfdata_t cf, void *aux)
102 {
103 struct obio_attach_args *oba = aux;
104 char compat[40];
105 int len;
106
107 if (strcmp(oba->obio_name, "ethernet") == 0)
108 return 1;
109
110 len = OF_getprop(oba->obio_node, "compatible", compat, sizeof(compat));
111 if (len > 0 &&
112 (strcmp(compat, "mpc5200-fec") == 0 ||
113 strcmp(compat, "mpc5200b-fec") == 0))
114 return 1;
115
116 return 0;
117 }
118
119 static void
120 fec_attach(device_t parent, device_t self, void *aux)
121 {
122 struct fec_softc *sc = device_private(self);
123 struct obio_attach_args *oba = aux;
124 struct ifnet *ifp = &sc->sc_if;
125 struct mii_data *mii = &sc->sc_mii;
126 uint8_t enaddr[ETHER_ADDR_LEN];
127 bus_size_t size;
128 int ist;
129
130 sc->sc_dev = self;
131 sc->sc_iot = oba->obio_bst;
132 sc->sc_dmat = oba->obio_dmat;
133 sc->sc_pa = oba->obio_addr;
134 callout_init(&sc->sc_tick_ch, 0);
135 callout_setfunc(&sc->sc_tick_ch, fec_tick, sc);
136
137 size = oba->obio_size != 0 ? oba->obio_size : FEC_REG_SIZE;
138 if (bus_space_map(sc->sc_iot, oba->obio_addr, size, 0,
139 &sc->sc_ioh) != 0) {
140 aprint_error(": can't map registers\n");
141 return;
142 }
143
144 if (OF_getprop(oba->obio_node, "local-mac-address", enaddr,
145 sizeof(enaddr)) != sizeof(enaddr) &&
146 OF_getprop(oba->obio_node, "mac-address", enaddr,
147 sizeof(enaddr)) != sizeof(enaddr)) {
148 aprint_error(": no MAC address\n");
149 return;
150 }
151 memcpy(sc->sc_enaddr, enaddr, ETHER_ADDR_LEN);
152
153 if (obio_decode_interrupt(oba->obio_node, 0, &sc->sc_irq, &ist))
154 sc->sc_ih = intr_establish(sc->sc_irq, ist, IPL_NET,
155 fec_intr, sc);
156
157 aprint_normal(": MPC5200 FEC, address %s\n",
158 ether_sprintf(sc->sc_enaddr));
159
160 /* Reset the controller and program the MII management clock. */
161 sc->sc_ipb_freq = mpc5200_cdm_get_ipb_freq();
162 fec_reset(sc);
163
164 /* MII / PHY. */
165 mii->mii_ifp = ifp;
166 mii->mii_readreg = fec_miibus_readreg;
167 mii->mii_writereg = fec_miibus_writereg;
168 mii->mii_statchg = fec_miibus_statchg;
169 sc->sc_ec.ec_mii = mii;
170 ifmedia_init(&mii->mii_media, 0, ether_mediachange, ether_mediastatus);
171 mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0);
172 if (LIST_FIRST(&mii->mii_phys) == NULL) {
173 aprint_error_dev(self, "no PHY found\n");
174 ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_MANUAL, 0, NULL);
175 ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_MANUAL);
176 } else
177 ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO);
178
179 strlcpy(ifp->if_xname, device_xname(self), IFNAMSIZ);
180 ifp->if_softc = sc;
181 ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
182 ifp->if_init = fec_init;
183 ifp->if_stop = fec_stop;
184 ifp->if_start = fec_start;
185 ifp->if_ioctl = fec_ioctl;
186 IFQ_SET_READY(&ifp->if_snd);
187
188 if_attach(ifp);
189 if_deferred_start_init(ifp, NULL);
190 ether_ifattach(ifp, sc->sc_enaddr);
191 }
192
193 /*
194 * Soft-reset the controller and set the MII management clock divider so MDC
195 * stays at or below 2.5 MHz, derived from the IPB clock.
196 */
197 static void
198 fec_reset(struct fec_softc *sc)
199 {
200 uint32_t div;
201 int i;
202
203 FEC_WRITE(sc, FEC_ECNTRL, FEC_ECNTRL_RESET);
204 for (i = 0; i < 1000; i++) {
205 if ((FEC_READ(sc, FEC_ECNTRL) & FEC_ECNTRL_RESET) == 0)
206 break;
207 DELAY(10);
208 }
209 FEC_WRITE(sc, FEC_IMASK, 0); /* mask all events */
210 FEC_WRITE(sc, FEC_IEVENT, 0xffffffff); /* clear events */
211
212 div = howmany(sc->sc_ipb_freq, 2 * FEC_MII_MAX_CLK);
213 if (div > 0x3f)
214 div = 0x3f;
215 FEC_WRITE(sc, FEC_MII_SPEED, FEC_MII_SPEED_DIV(div));
216 }
217
218 static int
219 fec_mii_wait(struct fec_softc *sc)
220 {
221 int i;
222
223 for (i = 0; i < FEC_PHY_TIMEOUT; i++) {
224 if (FEC_READ(sc, FEC_IEVENT) & FEC_INT_MII) {
225 FEC_WRITE(sc, FEC_IEVENT, FEC_INT_MII);
226 return 0;
227 }
228 DELAY(10);
229 }
230 return ETIMEDOUT;
231 }
232
233 static int
234 fec_miibus_readreg(device_t self, int phy, int reg, uint16_t *val)
235 {
236 struct fec_softc *sc = device_private(self);
237
238 FEC_WRITE(sc, FEC_IEVENT, FEC_INT_MII);
239 FEC_WRITE(sc, FEC_MII_DATA, FEC_MII_READ(phy, reg));
240 if (fec_mii_wait(sc) != 0)
241 return ETIMEDOUT;
242
243 *val = FEC_READ(sc, FEC_MII_DATA) & FEC_MII_DATA_MASK;
244 return 0;
245 }
246
247 static int
248 fec_miibus_writereg(device_t self, int phy, int reg, uint16_t val)
249 {
250 struct fec_softc *sc = device_private(self);
251
252 FEC_WRITE(sc, FEC_IEVENT, FEC_INT_MII);
253 FEC_WRITE(sc, FEC_MII_DATA, FEC_MII_WRITE(phy, reg, val));
254 return fec_mii_wait(sc);
255 }
256
257 static void
258 fec_miibus_statchg(struct ifnet *ifp)
259 {
260 struct fec_softc *sc = ifp->if_softc;
261 uint32_t xcr;
262
263 xcr = FEC_READ(sc, FEC_X_CNTRL);
264 if (sc->sc_mii.mii_media_active & IFM_FDX)
265 xcr |= FEC_X_CNTRL_FDEN;
266 else
267 xcr &= ~FEC_X_CNTRL_FDEN;
268 FEC_WRITE(sc, FEC_X_CNTRL, xcr);
269 }
270
271 static void
272 fec_tick(void *arg)
273 {
274 struct fec_softc *sc = arg;
275 int s;
276
277 s = splnet();
278 mii_tick(&sc->sc_mii);
279 splx(s);
280
281 /*
282 * Meh. Under load the receive FIFO can overrun, work it around.
283 */
284 if ((sc->sc_if.if_flags & IFF_RUNNING) &&
285 (FEC_READ(sc, FEC_ECNTRL) & FEC_ECNTRL_ETHER_EN) == 0) {
286 if_statinc(&sc->sc_if, if_ierrors);
287 aprint_error_dev(sc->sc_dev,
288 "ETHER_EN cleared, resetting\n");
289 s = splnet();
290 (void)fec_init(&sc->sc_if); /* reschedules the callout */
291 splx(s);
292 return;
293 }
294
295 callout_schedule(&sc->sc_tick_ch, hz);
296 }
297
298 /* Program the station address and receive/transmit MAC control. */
299 static void
300 fec_mac_setup(struct fec_softc *sc)
301 {
302 FEC_WRITE(sc, FEC_PADDR1,
303 (sc->sc_enaddr[0] << 24) | (sc->sc_enaddr[1] << 16) |
304 (sc->sc_enaddr[2] << 8) | sc->sc_enaddr[3]);
305 FEC_WRITE(sc, FEC_PADDR2,
306 (sc->sc_enaddr[4] << 24) | (sc->sc_enaddr[5] << 16) | 0x8808);
307 FEC_WRITE(sc, FEC_IADDR1, 0);
308 FEC_WRITE(sc, FEC_IADDR2, 0);
309 /*
310 * XXX: Accept all multicast groups via the hash filter.
311 */
312 FEC_WRITE(sc, FEC_GADDR1, 0xffffffff);
313 FEC_WRITE(sc, FEC_GADDR2, 0xffffffff);
314 FEC_WRITE(sc, FEC_R_CNTRL,
315 FEC_R_CNTRL_MAX_FL(ETHER_MAX_LEN) | FEC_R_CNTRL_MII_MODE);
316 FEC_WRITE(sc, FEC_X_CNTRL, FEC_X_CNTRL_FDEN);
317
318 /*
319 * Enable frame mode on both FIFOs
320 */
321 FEC_WRITE(sc, FEC_RESET_CNTRL, FEC_RESET_CNTRL_FIFO_EN);
322
323 FEC_WRITE(sc, FEC_RFIFO_CONTROL, FEC_FIFO_CTRL_INIT);
324 FEC_WRITE(sc, FEC_TFIFO_CONTROL, FEC_FIFO_CTRL_INIT);
325
326 /*
327 * The transmit FIFO requests data from BestComm only while it holds
328 * fewer than "alarm" bytes!
329 */
330 FEC_WRITE(sc, FEC_TFIFO_ALARM, 0x80);
331 }
332
333 /* Allocate a cluster for receive descriptor idx and hand it to the engine. */
334 static int
335 fec_newbuf(struct fec_softc *sc, u_int idx)
336 {
337 struct mbuf *m;
338 int error;
339
340 MGETHDR(m, M_DONTWAIT, MT_DATA);
341 if (m == NULL)
342 return ENOBUFS;
343 MCLGET(m, M_DONTWAIT);
344 if ((m->m_flags & M_EXT) == 0) {
345 m_freem(m);
346 return ENOBUFS;
347 }
348 m->m_len = m->m_pkthdr.len = MCLBYTES;
349 error = bus_dmamap_load_mbuf(sc->sc_dmat, sc->sc_rxmap[idx], m,
350 BUS_DMA_NOWAIT | BUS_DMA_READ);
351 if (error) {
352 m_freem(m);
353 return error;
354 }
355 bus_dmamap_sync(sc->sc_dmat, sc->sc_rxmap[idx], 0, MCLBYTES,
356 BUS_DMASYNC_PREREAD);
357 sc->sc_rxmbuf[idx] = m;
358 bestcomm_bd_post(&sc->sc_rxring, idx,
359 sc->sc_rxmap[idx]->dm_segs[0].ds_addr, MCLBYTES, 0);
360 return 0;
361 }
362
363 static int
364 fec_init(struct ifnet *ifp)
365 {
366 struct fec_softc *sc = ifp->if_softc;
367 u_int i;
368 int error;
369
370 fec_stop(ifp, 0);
371
372 if (!bestcomm_available()) {
373 aprint_error_ifnet(ifp, "BestComm SDMA not available\n");
374 return ENXIO;
375 }
376
377 fec_reset(sc);
378 fec_mac_setup(sc);
379
380 if (bestcomm_bd_setup(&sc->sc_rxring, FEC_RX_TASK, &fec_rx_layout,
381 sc->sc_pa + FEC_RFIFO_DATA, FEC_NRXDESC, MCLBYTES, 4,
382 FEC_RX_INITIATOR, 6) != 0 ||
383 bestcomm_bd_setup(&sc->sc_txring, FEC_TX_TASK, &fec_tx_layout,
384 sc->sc_pa + FEC_TFIFO_DATA, FEC_NTXDESC, MCLBYTES, 4,
385 FEC_TX_INITIATOR, 5) != 0) {
386 aprint_error_ifnet(ifp, "cannot set up SDMA rings\n");
387 return ENOMEM;
388 }
389
390 for (i = 0; i < FEC_NRXDESC; i++) {
391 if (sc->sc_rxmap[i] == NULL &&
392 bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1, MCLBYTES, 0,
393 BUS_DMA_NOWAIT, &sc->sc_rxmap[i]) != 0)
394 return ENOMEM;
395 if ((error = fec_newbuf(sc, i)) != 0)
396 return error;
397 }
398 for (i = 0; i < FEC_NTXDESC; i++) {
399 if (sc->sc_txmap[i] == NULL &&
400 bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1, MCLBYTES, 0,
401 BUS_DMA_NOWAIT, &sc->sc_txmap[i]) != 0)
402 return ENOMEM;
403 }
404 sc->sc_rxptr = 0;
405 sc->sc_txnext = sc->sc_txdirty = sc->sc_txbusy = 0;
406
407 /* Hook the BestComm task-completion interrupts (via the SIU cascade). */
408 sc->sc_rxih = intr_establish(bestcomm_task_irq(FEC_RX_TASK), IST_LEVEL,
409 IPL_NET, fec_rxintr, sc);
410 sc->sc_txih = intr_establish(bestcomm_task_irq(FEC_TX_TASK), IST_LEVEL,
411 IPL_NET, fec_txintr, sc);
412
413 /*
414 * Bring the link up before enabling the controller.
415 */
416 mii_mediachg(&sc->sc_mii);
417 for (i = 0; i < 500; i++) {
418 mii_pollstat(&sc->sc_mii);
419 if (sc->sc_mii.mii_media_status & IFM_ACTIVE)
420 break;
421 DELAY(10000);
422 }
423
424 FEC_WRITE(sc, FEC_ECNTRL, FEC_ECNTRL_ETHER_EN);
425 bestcomm_task_start(FEC_RX_TASK);
426 bestcomm_task_start(FEC_TX_TASK);
427
428 ifp->if_flags |= IFF_RUNNING;
429 ifp->if_flags &= ~IFF_OACTIVE;
430
431 callout_schedule(&sc->sc_tick_ch, hz);
432 return 0;
433 }
434
435 static void
436 fec_stop(struct ifnet *ifp, int disable)
437 {
438 struct fec_softc *sc = ifp->if_softc;
439 u_int i;
440
441 callout_stop(&sc->sc_tick_ch);
442
443 if (ifp->if_flags & IFF_RUNNING) {
444 bestcomm_task_stop(FEC_RX_TASK);
445 bestcomm_task_stop(FEC_TX_TASK);
446 }
447 FEC_WRITE(sc, FEC_ECNTRL, 0);
448
449 if (sc->sc_rxih != NULL) {
450 intr_disestablish(sc->sc_rxih);
451 sc->sc_rxih = NULL;
452 }
453 if (sc->sc_txih != NULL) {
454 intr_disestablish(sc->sc_txih);
455 sc->sc_txih = NULL;
456 }
457
458 mii_down(&sc->sc_mii);
459
460 for (i = 0; i < FEC_NRXDESC; i++) {
461 if (sc->sc_rxmbuf[i] != NULL) {
462 bus_dmamap_unload(sc->sc_dmat, sc->sc_rxmap[i]);
463 m_freem(sc->sc_rxmbuf[i]);
464 sc->sc_rxmbuf[i] = NULL;
465 }
466 }
467 for (i = 0; i < FEC_NTXDESC; i++) {
468 if (sc->sc_txmbuf[i] != NULL) {
469 bus_dmamap_unload(sc->sc_dmat, sc->sc_txmap[i]);
470 m_freem(sc->sc_txmbuf[i]);
471 sc->sc_txmbuf[i] = NULL;
472 }
473 }
474 ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
475 }
476
477 static void
478 fec_start(struct ifnet *ifp)
479 {
480 struct fec_softc *sc = ifp->if_softc;
481 struct mbuf *m, *mn;
482 u_int idx;
483 int error, posted = 0;
484
485 if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
486 return;
487
488 while (sc->sc_txbusy < FEC_NTXDESC) {
489 IFQ_POLL(&ifp->if_snd, m);
490 if (m == NULL)
491 break;
492 idx = sc->sc_txnext;
493
494 error = bus_dmamap_load_mbuf(sc->sc_dmat, sc->sc_txmap[idx], m,
495 BUS_DMA_NOWAIT | BUS_DMA_WRITE);
496 if (error == EFBIG) {
497 /* The buffer descriptor holds one segment; collapse. */
498 mn = m_defrag(m, M_DONTWAIT);
499 if (mn != NULL) {
500 m = mn;
501 error = bus_dmamap_load_mbuf(sc->sc_dmat,
502 sc->sc_txmap[idx], m,
503 BUS_DMA_NOWAIT | BUS_DMA_WRITE);
504 }
505 }
506 IFQ_DEQUEUE(&ifp->if_snd, m);
507 if (error) {
508 m_freem(m);
509 if_statinc(ifp, if_oerrors);
510 continue;
511 }
512
513 bus_dmamap_sync(sc->sc_dmat, sc->sc_txmap[idx], 0,
514 m->m_pkthdr.len, BUS_DMASYNC_PREWRITE);
515 bpf_mtap(ifp, m, BPF_D_OUT);
516 sc->sc_txmbuf[idx] = m;
517 bestcomm_bd_post(&sc->sc_txring, idx,
518 sc->sc_txmap[idx]->dm_segs[0].ds_addr, m->m_pkthdr.len,
519 BESTCOMM_BD_FLAG_LAST | BESTCOMM_BD_FLAG_INT);
520 sc->sc_txnext = (idx + 1) % FEC_NTXDESC;
521 sc->sc_txbusy++;
522 posted = 1;
523 }
524
525 /*
526 * (Re-)enable the transmit task.
527 */
528 if (posted)
529 bestcomm_task_start(FEC_TX_TASK);
530
531 if (sc->sc_txbusy == FEC_NTXDESC)
532 ifp->if_flags |= IFF_OACTIVE;
533 }
534
535 /* BestComm FEC_RX task completion: hand received frames up, refill the ring. */
536 static int
537 fec_rxintr(void *arg)
538 {
539 struct fec_softc *sc = arg;
540 struct ifnet *ifp = &sc->sc_if;
541 struct mbuf *m;
542 uint32_t st;
543 u_int idx;
544 int len, handled = 0;
545
546 for (;;) {
547 idx = sc->sc_rxptr;
548 st = bestcomm_bd_status(&sc->sc_rxring, idx);
549 if (st & BESTCOMM_BD_READY)
550 break;
551 handled = 1;
552
553 bus_dmamap_sync(sc->sc_dmat, sc->sc_rxmap[idx], 0, MCLBYTES,
554 BUS_DMASYNC_POSTREAD);
555 bus_dmamap_unload(sc->sc_dmat, sc->sc_rxmap[idx]);
556 m = sc->sc_rxmbuf[idx];
557 sc->sc_rxmbuf[idx] = NULL;
558
559 len = st & BESTCOMM_BD_LEN_MASK;
560 if (len > ETHER_CRC_LEN)
561 len -= ETHER_CRC_LEN; /* strip FCS */
562
563 if (fec_newbuf(sc, idx) != 0) {
564 /* No replacement: recycle this buffer, drop the frame. */
565 sc->sc_rxmbuf[idx] = m;
566 (void)bus_dmamap_load_mbuf(sc->sc_dmat,
567 sc->sc_rxmap[idx], m, BUS_DMA_NOWAIT | BUS_DMA_READ);
568 bus_dmamap_sync(sc->sc_dmat, sc->sc_rxmap[idx], 0,
569 MCLBYTES, BUS_DMASYNC_PREREAD);
570 bestcomm_bd_post(&sc->sc_rxring, idx,
571 sc->sc_rxmap[idx]->dm_segs[0].ds_addr, MCLBYTES, 0);
572 if_statinc(ifp, if_ierrors);
573 } else {
574 m_set_rcvif(m, ifp);
575 m->m_pkthdr.len = m->m_len = len;
576 if_percpuq_enqueue(ifp->if_percpuq, m);
577 }
578 sc->sc_rxptr = (idx + 1) % FEC_NRXDESC;
579 }
580
581 return handled;
582 }
583
584 /* BestComm FEC_TX task completion: reap transmitted frames. */
585 static int
586 fec_txintr(void *arg)
587 {
588 struct fec_softc *sc = arg;
589 struct ifnet *ifp = &sc->sc_if;
590 uint32_t st;
591 u_int idx;
592 int handled = 0;
593
594 while (sc->sc_txbusy > 0) {
595 idx = sc->sc_txdirty;
596 st = bestcomm_bd_status(&sc->sc_txring, idx);
597 if (st & BESTCOMM_BD_READY)
598 break;
599 handled = 1;
600
601 bus_dmamap_sync(sc->sc_dmat, sc->sc_txmap[idx], 0,
602 sc->sc_txmbuf[idx]->m_pkthdr.len, BUS_DMASYNC_POSTWRITE);
603 bus_dmamap_unload(sc->sc_dmat, sc->sc_txmap[idx]);
604 m_freem(sc->sc_txmbuf[idx]);
605 sc->sc_txmbuf[idx] = NULL;
606 if_statinc(ifp, if_opackets);
607
608 sc->sc_txdirty = (idx + 1) % FEC_NTXDESC;
609 sc->sc_txbusy--;
610 }
611 if (handled) {
612 ifp->if_flags &= ~IFF_OACTIVE;
613 if_schedule_deferred_start(ifp);
614 }
615 return handled;
616 }
617
618 static int
619 fec_ioctl(struct ifnet *ifp, u_long cmd, void *data)
620 {
621 int s, error;
622
623 s = splnet();
624 error = ether_ioctl(ifp, cmd, data);
625 if (error == ENETRESET) {
626 /*
627 * XXX: Just acknowledge it.
628 */
629 error = 0;
630 }
631 splx(s);
632 return error;
633 }
634
635 static int
636 fec_intr(void *arg)
637 {
638 struct fec_softc *sc = arg;
639 uint32_t ievent;
640
641 ievent = FEC_READ(sc, FEC_IEVENT);
642 FEC_WRITE(sc, FEC_IEVENT, ievent); /* acknowledge */
643 /* Frame rx/tx is serviced by the BestComm task-completion handlers. */
644 return ievent != 0;
645 }
646