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 */
78struct 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 */
88static 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 */
101static 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
142static const char *
143dtsec_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
157static void
158dtsec_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
168static void
169dtsec_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
178static void
179dtsec_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
189static int
190dtsec_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 */
251static const char *
252dtsec_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
263void
264dtsec_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
274void
275dtsec_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
285e_FmPortType
286dtsec_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
298e_FmPortType
299dtsec_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
312static void
313dtsec_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 */
332static int
333dtsec_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
359static int
360dtsec_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
383static int
384dtsec_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
420static void
421dtsec_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
436static void
437dtsec_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
447static void
448dtsec_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
478err:
479	dtsec_if_deinit_locked(sc);
480	device_printf(sc->sc_dev, "initialization error.\n");
481	return;
482}
483
484static void
485dtsec_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
496static void
497dtsec_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
507static void
508dtsec_if_watchdog(struct ifnet *ifp)
509{
510	/* TODO */
511}
512/** @} */
513
514
515/**
516 * @group IFmedia routines.
517 * @{
518 */
519static int
520dtsec_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
531static void
532dtsec_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 */
552static void
553dtsec_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
577int
578dtsec_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
726int
727dtsec_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
769int
770dtsec_suspend(device_t dev)
771{
772
773	return (0);
774}
775
776int
777dtsec_resume(device_t dev)
778{
779
780	return (0);
781}
782
783int
784dtsec_shutdown(device_t dev)
785{
786
787	return (0);
788}
789/** @} */
790
791
792/**
793 * @group MII bus interface.
794 * @{
795 */
796int
797dtsec_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
820int
821dtsec_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
843void
844dtsec_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