xref: /freebsd/sys/dev/dpaa/if_dtsec.c (revision acc1a9ef)
1 /*-
2  * Copyright (c) 2011-2012 Semihalf.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/module.h>
34 #include <sys/bus.h>
35 #include <sys/rman.h>
36 #include <sys/malloc.h>
37 #include <sys/mbuf.h>
38 #include <sys/socket.h>
39 #include <sys/sysctl.h>
40 #include <sys/sockio.h>
41 
42 #include <machine/bus.h>
43 #include <machine/resource.h>
44 
45 #include <net/ethernet.h>
46 #include <net/if.h>
47 #include <net/if_dl.h>
48 #include <net/if_media.h>
49 #include <net/if_types.h>
50 #include <net/if_arp.h>
51 
52 #include <dev/fdt/fdt_common.h>
53 #include <dev/mii/mii.h>
54 #include <dev/mii/miivar.h>
55 #include <dev/ofw/ofw_bus.h>
56 #include <dev/ofw/ofw_bus_subr.h>
57 #include <dev/ofw/openfirm.h>
58 
59 #include "miibus_if.h"
60 
61 #include <contrib/ncsw/inc/Peripherals/fm_mac_ext.h>
62 #include <contrib/ncsw/inc/Peripherals/fm_port_ext.h>
63 #include <contrib/ncsw/inc/xx_ext.h>
64 
65 #include "fman.h"
66 #include "if_dtsec.h"
67 #include "if_dtsec_im.h"
68 #include "if_dtsec_rm.h"
69 
70 
71 /**
72  * @group dTSEC private defines.
73  * @{
74  */
75 /**
76  * dTSEC FMan MAC exceptions info struct.
77  */
78 struct dtsec_fm_mac_ex_str {
79 	const int num;
80 	const char *str;
81 };
82 
83 /* XXX: Handle to FM_MAC instance of dTSEC0 */
84 /* From QorIQ Data Path Acceleration Architecture Reference Manual, Rev 2, page
85  * 3-37, "The MII management hardware is shared by all dTSECs... only through
86  * the MIIM registers of dTSEC1 can external PHY's be accessed and configured."
87  */
88 static t_Handle dtsec_mdio_mac_handle;
89 /** @} */
90 
91 
92 /**
93  * @group FMan MAC routines.
94  * @{
95  */
96 #define	DTSEC_MAC_EXCEPTIONS_END	(-1)
97 
98 /**
99  * FMan MAC exceptions.
100  */
101 static const struct dtsec_fm_mac_ex_str dtsec_fm_mac_exceptions[] = {
102 	{ e_FM_MAC_EX_10G_MDIO_SCAN_EVENTMDIO, "MDIO scan event" },
103 	{ e_FM_MAC_EX_10G_MDIO_CMD_CMPL, "MDIO command completion" },
104 	{ e_FM_MAC_EX_10G_REM_FAULT, "Remote fault" },
105 	{ e_FM_MAC_EX_10G_LOC_FAULT, "Local fault" },
106 	{ e_FM_MAC_EX_10G_1TX_ECC_ER, "Transmit frame ECC error" },
107 	{ e_FM_MAC_EX_10G_TX_FIFO_UNFL, "Transmit FIFO underflow" },
108 	{ e_FM_MAC_EX_10G_TX_FIFO_OVFL, "Receive FIFO overflow" },
109 	{ e_FM_MAC_EX_10G_TX_ER, "Transmit frame error" },
110 	{ e_FM_MAC_EX_10G_RX_FIFO_OVFL, "Receive FIFO overflow" },
111 	{ e_FM_MAC_EX_10G_RX_ECC_ER, "Receive frame ECC error" },
112 	{ e_FM_MAC_EX_10G_RX_JAB_FRM, "Receive jabber frame" },
113 	{ e_FM_MAC_EX_10G_RX_OVRSZ_FRM, "Receive oversized frame" },
114 	{ e_FM_MAC_EX_10G_RX_RUNT_FRM, "Receive runt frame" },
115 	{ e_FM_MAC_EX_10G_RX_FRAG_FRM, "Receive fragment frame" },
116 	{ e_FM_MAC_EX_10G_RX_LEN_ER, "Receive payload length error" },
117 	{ e_FM_MAC_EX_10G_RX_CRC_ER, "Receive CRC error" },
118 	{ e_FM_MAC_EX_10G_RX_ALIGN_ER, "Receive alignment error" },
119 	{ e_FM_MAC_EX_1G_BAB_RX, "Babbling receive error" },
120 	{ e_FM_MAC_EX_1G_RX_CTL, "Receive control (pause frame) interrupt" },
121 	{ e_FM_MAC_EX_1G_GRATEFUL_TX_STP_COMPLET, "Graceful transmit stop "
122 	    "complete" },
123 	{ e_FM_MAC_EX_1G_BAB_TX, "Babbling transmit error" },
124 	{ e_FM_MAC_EX_1G_TX_CTL, "Transmit control (pause frame) interrupt" },
125 	{ e_FM_MAC_EX_1G_TX_ERR, "Transmit error" },
126 	{ e_FM_MAC_EX_1G_LATE_COL, "Late collision" },
127 	{ e_FM_MAC_EX_1G_COL_RET_LMT, "Collision retry limit" },
128 	{ e_FM_MAC_EX_1G_TX_FIFO_UNDRN, "Transmit FIFO underrun" },
129 	{ e_FM_MAC_EX_1G_MAG_PCKT, "Magic Packet detected when dTSEC is in "
130 	    "Magic Packet detection mode" },
131 	{ e_FM_MAC_EX_1G_MII_MNG_RD_COMPLET, "MII management read completion" },
132 	{ e_FM_MAC_EX_1G_MII_MNG_WR_COMPLET, "MII management write completion" },
133 	{ e_FM_MAC_EX_1G_GRATEFUL_RX_STP_COMPLET, "Graceful receive stop "
134 	    "complete" },
135 	{ e_FM_MAC_EX_1G_TX_DATA_ERR, "Internal data error on transmit" },
136 	{ e_FM_MAC_EX_1G_RX_DATA_ERR, "Internal data error on receive" },
137 	{ e_FM_MAC_EX_1G_1588_TS_RX_ERR, "Time-Stamp Receive Error" },
138 	{ e_FM_MAC_EX_1G_RX_MIB_CNT_OVFL, "MIB counter overflow" },
139 	{ DTSEC_MAC_EXCEPTIONS_END, "" }
140 };
141 
142 static const char *
143 dtsec_fm_mac_ex_to_str(e_FmMacExceptions exception)
144 {
145 	int i;
146 
147 	for (i = 0; dtsec_fm_mac_exceptions[i].num != exception &&
148 	    dtsec_fm_mac_exceptions[i].num != DTSEC_MAC_EXCEPTIONS_END; ++i)
149 		;
150 
151 	if (dtsec_fm_mac_exceptions[i].num == DTSEC_MAC_EXCEPTIONS_END)
152 		return ("<Unknown Exception>");
153 
154 	return (dtsec_fm_mac_exceptions[i].str);
155 }
156 
157 static void
158 dtsec_fm_mac_mdio_event_callback(t_Handle h_App,
159     e_FmMacExceptions exception)
160 {
161 	struct dtsec_softc *sc;
162 
163 	sc = h_App;
164 	device_printf(sc->sc_dev, "MDIO event %i: %s.\n", exception,
165 	    dtsec_fm_mac_ex_to_str(exception));
166 }
167 
168 static void
169 dtsec_fm_mac_exception_callback(t_Handle app, e_FmMacExceptions exception)
170 {
171 	struct dtsec_softc *sc;
172 
173 	sc = app;
174 	device_printf(sc->sc_dev, "MAC exception %i: %s.\n", exception,
175 	    dtsec_fm_mac_ex_to_str(exception));
176 }
177 
178 static void
179 dtsec_fm_mac_free(struct dtsec_softc *sc)
180 {
181 	if (sc->sc_mach == NULL)
182 		return;
183 
184 	FM_MAC_Disable(sc->sc_mach, e_COMM_MODE_RX_AND_TX);
185 	FM_MAC_Free(sc->sc_mach);
186 	sc->sc_mach = NULL;
187 }
188 
189 static int
190 dtsec_fm_mac_init(struct dtsec_softc *sc, uint8_t *mac)
191 {
192 	t_FmMacParams params;
193 	t_Error error;
194 
195 	memset(&params, 0, sizeof(params));
196 	memcpy(&params.addr, mac, sizeof(params.addr));
197 
198 	params.baseAddr = sc->sc_fm_base + sc->sc_mac_mem_offset;
199 	params.enetMode = sc->sc_mac_enet_mode;
200 	params.macId = sc->sc_eth_id;
201 	params.mdioIrq = sc->sc_mac_mdio_irq;
202 	params.f_Event = dtsec_fm_mac_mdio_event_callback;
203 	params.f_Exception = dtsec_fm_mac_exception_callback;
204 	params.h_App = sc;
205 	params.h_Fm = sc->sc_fmh;
206 
207 	sc->sc_mach = FM_MAC_Config(&params);
208 	if (sc->sc_hidden)
209 		return (0);
210 	if (sc->sc_mach == NULL) {
211 		device_printf(sc->sc_dev, "couldn't configure FM_MAC module.\n"
212 		    );
213 		return (ENXIO);
214 	}
215 
216 	error = FM_MAC_ConfigResetOnInit(sc->sc_mach, TRUE);
217 	if (error != E_OK) {
218 		device_printf(sc->sc_dev, "couldn't enable reset on init "
219 		    "feature.\n");
220 		dtsec_fm_mac_free(sc);
221 		return (ENXIO);
222 	}
223 
224 	/* Do not inform about pause frames */
225 	error = FM_MAC_ConfigException(sc->sc_mach, e_FM_MAC_EX_1G_RX_CTL,
226 	    FALSE);
227 	if (error != E_OK) {
228 		device_printf(sc->sc_dev, "couldn't disable pause frames "
229 			"exception.\n");
230 		dtsec_fm_mac_free(sc);
231 		return (ENXIO);
232 	}
233 
234 	error = FM_MAC_Init(sc->sc_mach);
235 	if (error != E_OK) {
236 		device_printf(sc->sc_dev, "couldn't initialize FM_MAC module."
237 		    "\n");
238 		dtsec_fm_mac_free(sc);
239 		return (ENXIO);
240 	}
241 
242 	return (0);
243 }
244 /** @} */
245 
246 
247 /**
248  * @group FMan PORT routines.
249  * @{
250  */
251 static const char *
252 dtsec_fm_port_ex_to_str(e_FmPortExceptions exception)
253 {
254 
255 	switch (exception) {
256 	case e_FM_PORT_EXCEPTION_IM_BUSY:
257 		return ("IM: RX busy");
258 	default:
259 		return ("<Unknown Exception>");
260 	}
261 }
262 
263 void
264 dtsec_fm_port_rx_exception_callback(t_Handle app,
265     e_FmPortExceptions exception)
266 {
267 	struct dtsec_softc *sc;
268 
269 	sc = app;
270 	device_printf(sc->sc_dev, "RX exception: %i: %s.\n", exception,
271 	    dtsec_fm_port_ex_to_str(exception));
272 }
273 
274 void
275 dtsec_fm_port_tx_exception_callback(t_Handle app,
276     e_FmPortExceptions exception)
277 {
278 	struct dtsec_softc *sc;
279 
280 	sc = app;
281 	device_printf(sc->sc_dev, "TX exception: %i: %s.\n", exception,
282 	    dtsec_fm_port_ex_to_str(exception));
283 }
284 
285 e_FmPortType
286 dtsec_fm_port_rx_type(enum eth_dev_type type)
287 {
288 	switch (type) {
289 	case ETH_DTSEC:
290 		return (e_FM_PORT_TYPE_RX);
291 	case ETH_10GSEC:
292 		return (e_FM_PORT_TYPE_RX_10G);
293 	default:
294 		return (e_FM_PORT_TYPE_DUMMY);
295 	}
296 }
297 
298 e_FmPortType
299 dtsec_fm_port_tx_type(enum eth_dev_type type)
300 {
301 
302 	switch (type) {
303 	case ETH_DTSEC:
304 		return (e_FM_PORT_TYPE_TX);
305 	case ETH_10GSEC:
306 		return (e_FM_PORT_TYPE_TX_10G);
307 	default:
308 		return (e_FM_PORT_TYPE_DUMMY);
309 	}
310 }
311 
312 static void
313 dtsec_fm_port_free_both(struct dtsec_softc *sc)
314 {
315 	if (sc->sc_rxph) {
316 		FM_PORT_Free(sc->sc_rxph);
317 		sc->sc_rxph = NULL;
318 	}
319 
320 	if (sc->sc_txph) {
321 		FM_PORT_Free(sc->sc_txph);
322 		sc->sc_txph = NULL;
323 	}
324 }
325 /** @} */
326 
327 
328 /**
329  * @group IFnet routines.
330  * @{
331  */
332 static int
333 dtsec_if_enable_locked(struct dtsec_softc *sc)
334 {
335 	int error;
336 
337 	DTSEC_LOCK_ASSERT(sc);
338 
339 	error = FM_MAC_Enable(sc->sc_mach, e_COMM_MODE_RX_AND_TX);
340 	if (error != E_OK)
341 		return (EIO);
342 
343 	error = FM_PORT_Enable(sc->sc_rxph);
344 	if (error != E_OK)
345 		return (EIO);
346 
347 	error = FM_PORT_Enable(sc->sc_txph);
348 	if (error != E_OK)
349 		return (EIO);
350 
351 	sc->sc_ifnet->if_drv_flags |= IFF_DRV_RUNNING;
352 
353 	/* Refresh link state */
354 	dtsec_miibus_statchg(sc->sc_dev);
355 
356 	return (0);
357 }
358 
359 static int
360 dtsec_if_disable_locked(struct dtsec_softc *sc)
361 {
362 	int error;
363 
364 	DTSEC_LOCK_ASSERT(sc);
365 
366 	error = FM_MAC_Disable(sc->sc_mach, e_COMM_MODE_RX_AND_TX);
367 	if (error != E_OK)
368 		return (EIO);
369 
370 	error = FM_PORT_Disable(sc->sc_rxph);
371 	if (error != E_OK)
372 		return (EIO);
373 
374 	error = FM_PORT_Disable(sc->sc_txph);
375 	if (error != E_OK)
376 		return (EIO);
377 
378 	sc->sc_ifnet->if_drv_flags &= ~IFF_DRV_RUNNING;
379 
380 	return (0);
381 }
382 
383 static int
384 dtsec_if_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
385 {
386 	struct dtsec_softc *sc;
387 	struct ifreq *ifr;
388 	int error;
389 
390 	sc = ifp->if_softc;
391 	ifr = (struct ifreq *)data;
392 	error = 0;
393 
394 	/* Basic functionality to achieve media status reports */
395 	switch (command) {
396 	case SIOCSIFFLAGS:
397 		DTSEC_LOCK(sc);
398 
399 		if (sc->sc_ifnet->if_flags & IFF_UP)
400 			error = dtsec_if_enable_locked(sc);
401 		else
402 			error = dtsec_if_disable_locked(sc);
403 
404 		DTSEC_UNLOCK(sc);
405 		break;
406 
407 	case SIOCGIFMEDIA:
408 	case SIOCSIFMEDIA:
409 		error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii->mii_media,
410 		    command);
411 		break;
412 
413 	default:
414 		error = ether_ioctl(ifp, command, data);
415 	}
416 
417 	return (error);
418 }
419 
420 static void
421 dtsec_if_tick(void *arg)
422 {
423 	struct dtsec_softc *sc;
424 
425 	sc = arg;
426 
427 	/* TODO */
428 	DTSEC_LOCK(sc);
429 
430 	mii_tick(sc->sc_mii);
431 	callout_reset(&sc->sc_tick_callout, hz, dtsec_if_tick, sc);
432 
433 	DTSEC_UNLOCK(sc);
434 }
435 
436 static void
437 dtsec_if_deinit_locked(struct dtsec_softc *sc)
438 {
439 
440 	DTSEC_LOCK_ASSERT(sc);
441 
442 	DTSEC_UNLOCK(sc);
443 	callout_drain(&sc->sc_tick_callout);
444 	DTSEC_LOCK(sc);
445 }
446 
447 static void
448 dtsec_if_init_locked(struct dtsec_softc *sc)
449 {
450 	int error;
451 
452 	DTSEC_LOCK_ASSERT(sc);
453 
454 	/* Set MAC address */
455 	error = FM_MAC_ModifyMacAddr(sc->sc_mach,
456 	    (t_EnetAddr *)IF_LLADDR(sc->sc_ifnet));
457 	if (error != E_OK) {
458 		device_printf(sc->sc_dev, "couldn't set MAC address.\n");
459 		goto err;
460 	}
461 
462 	/* Start MII polling */
463 	if (sc->sc_mii)
464 		callout_reset(&sc->sc_tick_callout, hz, dtsec_if_tick, sc);
465 
466 	if (sc->sc_ifnet->if_flags & IFF_UP) {
467 		error = dtsec_if_enable_locked(sc);
468 		if (error != 0)
469 			goto err;
470 	} else {
471 		error = dtsec_if_disable_locked(sc);
472 		if (error != 0)
473 			goto err;
474 	}
475 
476 	return;
477 
478 err:
479 	dtsec_if_deinit_locked(sc);
480 	device_printf(sc->sc_dev, "initialization error.\n");
481 	return;
482 }
483 
484 static void
485 dtsec_if_init(void *data)
486 {
487 	struct dtsec_softc *sc;
488 
489 	sc = data;
490 
491 	DTSEC_LOCK(sc);
492 	dtsec_if_init_locked(sc);
493 	DTSEC_UNLOCK(sc);
494 }
495 
496 static void
497 dtsec_if_start(struct ifnet *ifp)
498 {
499 	struct dtsec_softc *sc;
500 
501 	sc = ifp->if_softc;
502 	DTSEC_LOCK(sc);
503 	sc->sc_start_locked(sc);
504 	DTSEC_UNLOCK(sc);
505 }
506 
507 static void
508 dtsec_if_watchdog(struct ifnet *ifp)
509 {
510 	/* TODO */
511 }
512 /** @} */
513 
514 
515 /**
516  * @group IFmedia routines.
517  * @{
518  */
519 static int
520 dtsec_ifmedia_upd(struct ifnet *ifp)
521 {
522 	struct dtsec_softc *sc = ifp->if_softc;
523 
524 	DTSEC_LOCK(sc);
525 	mii_mediachg(sc->sc_mii);
526 	DTSEC_UNLOCK(sc);
527 
528 	return (0);
529 }
530 
531 static void
532 dtsec_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
533 {
534 	struct dtsec_softc *sc = ifp->if_softc;
535 
536 	DTSEC_LOCK(sc);
537 
538 	mii_pollstat(sc->sc_mii);
539 
540 	ifmr->ifm_active = sc->sc_mii->mii_media_active;
541 	ifmr->ifm_status = sc->sc_mii->mii_media_status;
542 
543 	DTSEC_UNLOCK(sc);
544 }
545 /** @} */
546 
547 
548 /**
549  * @group dTSEC bus interface.
550  * @{
551  */
552 static void
553 dtsec_configure_mode(struct dtsec_softc *sc)
554 {
555 	char tunable[64];
556 
557 	snprintf(tunable, sizeof(tunable), "%s.independent_mode",
558 	    device_get_nameunit(sc->sc_dev));
559 
560 	sc->sc_mode = DTSEC_MODE_REGULAR;
561 	TUNABLE_INT_FETCH(tunable, &sc->sc_mode);
562 
563 	if (sc->sc_mode == DTSEC_MODE_REGULAR) {
564 		sc->sc_port_rx_init = dtsec_rm_fm_port_rx_init;
565 		sc->sc_port_tx_init = dtsec_rm_fm_port_tx_init;
566 		sc->sc_start_locked = dtsec_rm_if_start_locked;
567 	} else {
568 		sc->sc_port_rx_init = dtsec_im_fm_port_rx_init;
569 		sc->sc_port_tx_init = dtsec_im_fm_port_tx_init;
570 		sc->sc_start_locked = dtsec_im_if_start_locked;
571 	}
572 
573 	device_printf(sc->sc_dev, "Configured for %s mode.\n",
574 	    (sc->sc_mode == DTSEC_MODE_REGULAR) ? "regular" : "independent");
575 }
576 
577 int
578 dtsec_attach(device_t dev)
579 {
580 	struct dtsec_softc *sc;
581 	int error;
582 	struct ifnet *ifp;
583 
584 	sc = device_get_softc(dev);
585 
586 	sc->sc_dev = dev;
587 	sc->sc_mac_mdio_irq = NO_IRQ;
588 	sc->sc_eth_id = device_get_unit(dev);
589 
590 
591 	/* Check if MallocSmart allocator is ready */
592 	if (XX_MallocSmartInit() != E_OK)
593 		return (ENXIO);
594 
595 	XX_TrackInit();
596 
597 	/* Init locks */
598 	mtx_init(&sc->sc_lock, device_get_nameunit(dev),
599 	    "DTSEC Global Lock", MTX_DEF);
600 
601 	mtx_init(&sc->sc_mii_lock, device_get_nameunit(dev),
602 	    "DTSEC MII Lock", MTX_DEF);
603 
604 	/* Init callouts */
605 	callout_init(&sc->sc_tick_callout, CALLOUT_MPSAFE);
606 
607 	/* Read configuraton */
608 	if ((error = fman_get_handle(&sc->sc_fmh)) != 0)
609 		return (error);
610 
611 	if ((error = fman_get_muram_handle(&sc->sc_muramh)) != 0)
612 		return (error);
613 
614 	if ((error = fman_get_bushandle(&sc->sc_fm_base)) != 0)
615 		return (error);
616 
617 	/* Configure working mode */
618 	dtsec_configure_mode(sc);
619 
620 	/* If we are working in regular mode configure BMAN and QMAN */
621 	if (sc->sc_mode == DTSEC_MODE_REGULAR) {
622 		/* Create RX buffer pool */
623 		error = dtsec_rm_pool_rx_init(sc);
624 		if (error != 0)
625 			return (EIO);
626 
627 		/* Create RX frame queue range */
628 		error = dtsec_rm_fqr_rx_init(sc);
629 		if (error != 0)
630 			return (EIO);
631 
632 		/* Create frame info pool */
633 		error = dtsec_rm_fi_pool_init(sc);
634 		if (error != 0)
635 			return (EIO);
636 
637 		/* Create TX frame queue range */
638 		error = dtsec_rm_fqr_tx_init(sc);
639 		if (error != 0)
640 			return (EIO);
641 	}
642 
643 	/* Init FMan MAC module. */
644 	error = dtsec_fm_mac_init(sc, sc->sc_mac_addr);
645 	if (error != 0) {
646 		dtsec_detach(dev);
647 		return (ENXIO);
648 	}
649 
650 	/*
651 	 * XXX: All phys are connected to MDIO interface of the first dTSEC
652 	 * device (dTSEC0). We have to save handle to the FM_MAC instance of
653 	 * dTSEC0, which is used later during phy's registers accesses. Another
654 	 * option would be adding new property to DTS pointing to correct dTSEC
655 	 * instance, of which FM_MAC handle has to be used for phy's registers
656 	 * accesses. We did not want to add new properties to DTS, thus this
657 	 * quite ugly hack.
658 	 */
659 	if (sc->sc_eth_id == 0)
660 		dtsec_mdio_mac_handle = sc->sc_mach;
661 	if (sc->sc_hidden)
662 		return (0);
663 
664 	/* Init FMan TX port */
665 	error = sc->sc_port_tx_init(sc, device_get_unit(sc->sc_dev));
666 	if (error != 0) {
667 		dtsec_detach(dev);
668 		return (ENXIO);
669 	}
670 
671 	/* Init FMan RX port */
672 	error = sc->sc_port_rx_init(sc, device_get_unit(sc->sc_dev));
673 	if (error != 0) {
674 		dtsec_detach(dev);
675 		return (ENXIO);
676 	}
677 
678 	/* Create network interface for upper layers */
679 	ifp = sc->sc_ifnet = if_alloc(IFT_ETHER);
680 	if (ifp == NULL) {
681 		device_printf(sc->sc_dev, "if_alloc() failed.\n");
682 		dtsec_detach(dev);
683 		return (ENOMEM);
684 	}
685 
686 	ifp->if_softc = sc;
687 	ifp->if_mtu = ETHERMTU;	/* TODO: Configure */
688 	ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST;
689 	ifp->if_init = dtsec_if_init;
690 	ifp->if_start = dtsec_if_start;
691 	ifp->if_ioctl = dtsec_if_ioctl;
692 	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
693 
694 	if (sc->sc_phy_addr >= 0)
695 		if_initname(ifp, device_get_name(sc->sc_dev),
696 		    device_get_unit(sc->sc_dev));
697 	else
698 		if_initname(ifp, "dtsec_phy", device_get_unit(sc->sc_dev));
699 
700 	/* TODO */
701 #if 0
702 	IFQ_SET_MAXLEN(&ifp->if_snd, TSEC_TX_NUM_DESC - 1);
703 	ifp->if_snd.ifq_drv_maxlen = TSEC_TX_NUM_DESC - 1;
704 	IFQ_SET_READY(&ifp->if_snd);
705 #endif
706 	ifp->if_capabilities = 0; /* TODO: Check */
707 	ifp->if_capenable = ifp->if_capabilities;
708 
709 	/* Attach PHY(s) */
710 	error = mii_attach(sc->sc_dev, &sc->sc_mii_dev, ifp, dtsec_ifmedia_upd,
711 	    dtsec_ifmedia_sts, BMSR_DEFCAPMASK, sc->sc_phy_addr,
712 	    MII_OFFSET_ANY, 0);
713 	if (error) {
714 		device_printf(sc->sc_dev, "attaching PHYs failed: %d\n", error);
715 		dtsec_detach(sc->sc_dev);
716 		return (error);
717 	}
718 	sc->sc_mii = device_get_softc(sc->sc_mii_dev);
719 
720 	/* Attach to stack */
721 	ether_ifattach(ifp, sc->sc_mac_addr);
722 
723 	return (0);
724 }
725 
726 int
727 dtsec_detach(device_t dev)
728 {
729 	struct dtsec_softc *sc;
730 	if_t ifp;
731 
732 	sc = device_get_softc(dev);
733 	ifp = sc->sc_ifnet;
734 
735 	if (device_is_attached(dev)) {
736 		ether_ifdetach(ifp);
737 		/* Shutdown interface */
738 		DTSEC_LOCK(sc);
739 		dtsec_if_deinit_locked(sc);
740 		DTSEC_UNLOCK(sc);
741 	}
742 
743 	if (sc->sc_ifnet) {
744 		if_free(sc->sc_ifnet);
745 		sc->sc_ifnet = NULL;
746 	}
747 
748 	if (sc->sc_mode == DTSEC_MODE_REGULAR) {
749 		/* Free RX/TX FQRs */
750 		dtsec_rm_fqr_rx_free(sc);
751 		dtsec_rm_fqr_tx_free(sc);
752 
753 		/* Free frame info pool */
754 		dtsec_rm_fi_pool_free(sc);
755 
756 		/* Free RX buffer pool */
757 		dtsec_rm_pool_rx_free(sc);
758 	}
759 
760 	dtsec_fm_mac_free(sc);
761 	dtsec_fm_port_free_both(sc);
762 
763 	/* Destroy lock */
764 	mtx_destroy(&sc->sc_lock);
765 
766 	return (0);
767 }
768 
769 int
770 dtsec_suspend(device_t dev)
771 {
772 
773 	return (0);
774 }
775 
776 int
777 dtsec_resume(device_t dev)
778 {
779 
780 	return (0);
781 }
782 
783 int
784 dtsec_shutdown(device_t dev)
785 {
786 
787 	return (0);
788 }
789 /** @} */
790 
791 
792 /**
793  * @group MII bus interface.
794  * @{
795  */
796 int
797 dtsec_miibus_readreg(device_t dev, int phy, int reg)
798 {
799 	struct dtsec_softc *sc;
800 	uint16_t data;
801 	t_Error error;
802 
803 	sc = device_get_softc(dev);
804 
805 	if (phy != sc->sc_phy_addr)
806 		return (0xFFFF);
807 
808 	DTSEC_MII_LOCK(sc);
809 	error = FM_MAC_MII_ReadPhyReg(dtsec_mdio_mac_handle, phy, reg, &data);
810 	DTSEC_MII_UNLOCK(sc);
811 	if (error != E_OK) {
812 		device_printf(dev, "Error while reading from PHY (NetCommSw "
813 		    "error: %d)\n", error);
814 		return (0xFFFF);
815 	}
816 
817 	return ((int)data);
818 }
819 
820 int
821 dtsec_miibus_writereg(device_t dev, int phy, int reg, int value)
822 {
823 	struct dtsec_softc *sc;
824 	t_Error error;
825 
826 	sc = device_get_softc(dev);
827 
828 	if (phy != sc->sc_phy_addr)
829 		return (EINVAL);
830 
831 	DTSEC_MII_LOCK(sc);
832 	error = FM_MAC_MII_WritePhyReg(dtsec_mdio_mac_handle, phy, reg, value);
833 	DTSEC_MII_UNLOCK(sc);
834 	if (error != E_OK) {
835 		device_printf(dev, "Error while writing to PHY (NetCommSw "
836 		    "error: %d).\n", error);
837 		return (EIO);
838 	}
839 
840 	return (0);
841 }
842 
843 void
844 dtsec_miibus_statchg(device_t dev)
845 {
846 	struct dtsec_softc *sc;
847 	e_EnetSpeed speed;
848 	bool duplex;
849 	int error;
850 
851 	sc = device_get_softc(dev);
852 
853 	DTSEC_LOCK_ASSERT(sc);
854 
855 	duplex = ((sc->sc_mii->mii_media_active & IFM_GMASK) == IFM_FDX);
856 
857 	switch (IFM_SUBTYPE(sc->sc_mii->mii_media_active)) {
858 	case IFM_1000_T:
859 	case IFM_1000_SX:
860 		speed = e_ENET_SPEED_1000;
861 		break;
862 
863         case IFM_100_TX:
864 		speed = e_ENET_SPEED_100;
865 		break;
866 
867         case IFM_10_T:
868 		speed = e_ENET_SPEED_10;
869 		break;
870 
871 	default:
872 		speed = e_ENET_SPEED_10;
873 	}
874 
875 	error = FM_MAC_AdjustLink(sc->sc_mach, speed, duplex);
876 	if (error != E_OK)
877 		device_printf(sc->sc_dev, "error while adjusting MAC speed.\n");
878 }
879 /** @} */
880