xref: /freebsd/sys/dev/dwc/if_dwc.c (revision afa0f66e81ccd6a946133bb9aceaf1fe59370431)
15d43fd68SRuslan Bukin /*-
25d43fd68SRuslan Bukin  * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
35d43fd68SRuslan Bukin  *
45d43fd68SRuslan Bukin  * This software was developed by SRI International and the University of
55d43fd68SRuslan Bukin  * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
65d43fd68SRuslan Bukin  * ("CTSRD"), as part of the DARPA CRASH research programme.
75d43fd68SRuslan Bukin  *
85d43fd68SRuslan Bukin  * Redistribution and use in source and binary forms, with or without
95d43fd68SRuslan Bukin  * modification, are permitted provided that the following conditions
105d43fd68SRuslan Bukin  * are met:
115d43fd68SRuslan Bukin  * 1. Redistributions of source code must retain the above copyright
125d43fd68SRuslan Bukin  *    notice, this list of conditions and the following disclaimer.
135d43fd68SRuslan Bukin  * 2. Redistributions in binary form must reproduce the above copyright
145d43fd68SRuslan Bukin  *    notice, this list of conditions and the following disclaimer in the
155d43fd68SRuslan Bukin  *    documentation and/or other materials provided with the distribution.
165d43fd68SRuslan Bukin  *
175d43fd68SRuslan Bukin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
185d43fd68SRuslan Bukin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
195d43fd68SRuslan Bukin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
205d43fd68SRuslan Bukin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
215d43fd68SRuslan Bukin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
225d43fd68SRuslan Bukin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
235d43fd68SRuslan Bukin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
245d43fd68SRuslan Bukin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
255d43fd68SRuslan Bukin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
265d43fd68SRuslan Bukin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
275d43fd68SRuslan Bukin  * SUCH DAMAGE.
285d43fd68SRuslan Bukin  */
295d43fd68SRuslan Bukin 
305d43fd68SRuslan Bukin /*
315d43fd68SRuslan Bukin  * Ethernet media access controller (EMAC)
325d43fd68SRuslan Bukin  * Chapter 17, Altera Cyclone V Device Handbook (CV-5V2 2014.07.22)
335d43fd68SRuslan Bukin  *
345d43fd68SRuslan Bukin  * EMAC is an instance of the Synopsys DesignWare 3504-0
355d43fd68SRuslan Bukin  * Universal 10/100/1000 Ethernet MAC (DWC_gmac).
365d43fd68SRuslan Bukin  */
375d43fd68SRuslan Bukin 
385d43fd68SRuslan Bukin #include <sys/cdefs.h>
395d43fd68SRuslan Bukin #include <sys/param.h>
405d43fd68SRuslan Bukin #include <sys/systm.h>
415d43fd68SRuslan Bukin #include <sys/bus.h>
42d7acb49aSJared McNeill #include <sys/gpio.h>
435d43fd68SRuslan Bukin #include <sys/kernel.h>
445d43fd68SRuslan Bukin #include <sys/lock.h>
45da9a326bSLuiz Otavio O Souza #include <sys/malloc.h>
465d43fd68SRuslan Bukin #include <sys/mbuf.h>
47da9a326bSLuiz Otavio O Souza #include <sys/module.h>
485d43fd68SRuslan Bukin #include <sys/mutex.h>
49da9a326bSLuiz Otavio O Souza #include <sys/rman.h>
505d43fd68SRuslan Bukin #include <sys/socket.h>
515d43fd68SRuslan Bukin #include <sys/sockio.h>
525d43fd68SRuslan Bukin 
535d43fd68SRuslan Bukin #include <net/bpf.h>
545d43fd68SRuslan Bukin #include <net/if.h>
555d43fd68SRuslan Bukin #include <net/ethernet.h>
565d43fd68SRuslan Bukin #include <net/if_dl.h>
575d43fd68SRuslan Bukin #include <net/if_media.h>
585d43fd68SRuslan Bukin #include <net/if_types.h>
595d43fd68SRuslan Bukin #include <net/if_var.h>
605d43fd68SRuslan Bukin 
615d43fd68SRuslan Bukin #include <machine/bus.h>
625d43fd68SRuslan Bukin 
6350059a60SEmmanuel Vadot #include <dev/extres/clk/clk.h>
6450059a60SEmmanuel Vadot #include <dev/extres/hwreset/hwreset.h>
6550059a60SEmmanuel Vadot 
665d43fd68SRuslan Bukin #include <dev/mii/mii.h>
675d43fd68SRuslan Bukin #include <dev/mii/miivar.h>
68da9a326bSLuiz Otavio O Souza #include <dev/ofw/ofw_bus.h>
69da9a326bSLuiz Otavio O Souza #include <dev/ofw/ofw_bus_subr.h>
70f77d8d10SEmmanuel Vadot #include <dev/mii/mii_fdt.h>
71da9a326bSLuiz Otavio O Souza 
72c36125f6SEmmanuel Vadot #include <dev/dwc/if_dwcvar.h>
73972adf0fSEmmanuel Vadot #include <dev/dwc/dwc1000_reg.h>
7462519d5aSEmmanuel Vadot #include <dev/dwc/dwc1000_core.h>
75972adf0fSEmmanuel Vadot #include <dev/dwc/dwc1000_dma.h>
76c36125f6SEmmanuel Vadot 
775df53927SLuiz Otavio O Souza #include "if_dwc_if.h"
78d7acb49aSJared McNeill #include "gpio_if.h"
795d43fd68SRuslan Bukin #include "miibus_if.h"
805d43fd68SRuslan Bukin 
81d8e5258dSRuslan Bukin #define	MAC_RESET_TIMEOUT	100
825d43fd68SRuslan Bukin 
835d43fd68SRuslan Bukin static struct resource_spec dwc_spec[] = {
845d43fd68SRuslan Bukin 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
855d43fd68SRuslan Bukin 	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
865d43fd68SRuslan Bukin 	{ -1, 0 }
875d43fd68SRuslan Bukin };
885d43fd68SRuslan Bukin 
895d43fd68SRuslan Bukin static void dwc_stop_locked(struct dwc_softc *sc);
905d43fd68SRuslan Bukin 
912a35d391SEmmanuel Vadot static void dwc_tick(void *arg);
922a35d391SEmmanuel Vadot 
932a35d391SEmmanuel Vadot /*
942a35d391SEmmanuel Vadot  * Media functions
952a35d391SEmmanuel Vadot  */
962a35d391SEmmanuel Vadot 
972a35d391SEmmanuel Vadot static void
98ca018790SMitchell Horne dwc_media_status(if_t ifp, struct ifmediareq *ifmr)
992a35d391SEmmanuel Vadot {
1002a35d391SEmmanuel Vadot 	struct dwc_softc *sc;
1012a35d391SEmmanuel Vadot 	struct mii_data *mii;
1022a35d391SEmmanuel Vadot 
103ca018790SMitchell Horne 	sc = if_getsoftc(ifp);
1042a35d391SEmmanuel Vadot 	mii = sc->mii_softc;
1052a35d391SEmmanuel Vadot 	DWC_LOCK(sc);
1062a35d391SEmmanuel Vadot 	mii_pollstat(mii);
1072a35d391SEmmanuel Vadot 	ifmr->ifm_active = mii->mii_media_active;
1082a35d391SEmmanuel Vadot 	ifmr->ifm_status = mii->mii_media_status;
1092a35d391SEmmanuel Vadot 	DWC_UNLOCK(sc);
1102a35d391SEmmanuel Vadot }
1112a35d391SEmmanuel Vadot 
1122a35d391SEmmanuel Vadot static int
1132a35d391SEmmanuel Vadot dwc_media_change_locked(struct dwc_softc *sc)
1142a35d391SEmmanuel Vadot {
1152a35d391SEmmanuel Vadot 
1162a35d391SEmmanuel Vadot 	return (mii_mediachg(sc->mii_softc));
1172a35d391SEmmanuel Vadot }
1182a35d391SEmmanuel Vadot 
1192a35d391SEmmanuel Vadot static int
120ca018790SMitchell Horne dwc_media_change(if_t ifp)
1212a35d391SEmmanuel Vadot {
1222a35d391SEmmanuel Vadot 	struct dwc_softc *sc;
1232a35d391SEmmanuel Vadot 	int error;
1242a35d391SEmmanuel Vadot 
125ca018790SMitchell Horne 	sc = if_getsoftc(ifp);
1262a35d391SEmmanuel Vadot 
1272a35d391SEmmanuel Vadot 	DWC_LOCK(sc);
1282a35d391SEmmanuel Vadot 	error = dwc_media_change_locked(sc);
1292a35d391SEmmanuel Vadot 	DWC_UNLOCK(sc);
1302a35d391SEmmanuel Vadot 	return (error);
1312a35d391SEmmanuel Vadot }
1322a35d391SEmmanuel Vadot 
1332a35d391SEmmanuel Vadot /*
1342a35d391SEmmanuel Vadot  * if_ functions
1352a35d391SEmmanuel Vadot  */
1362a35d391SEmmanuel Vadot 
137f88e0af6SEmmanuel Vadot static void
1382a35d391SEmmanuel Vadot dwc_txstart_locked(struct dwc_softc *sc)
1395d43fd68SRuslan Bukin {
140ca018790SMitchell Horne 	if_t ifp;
1412a35d391SEmmanuel Vadot 
1422a35d391SEmmanuel Vadot 	DWC_ASSERT_LOCKED(sc);
1432a35d391SEmmanuel Vadot 
1442a35d391SEmmanuel Vadot 	if (!sc->link_is_up)
1452a35d391SEmmanuel Vadot 		return;
1462a35d391SEmmanuel Vadot 
1472a35d391SEmmanuel Vadot 	ifp = sc->ifp;
1482a35d391SEmmanuel Vadot 
1493bbd11eeSEmmanuel Vadot 	if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) !=
1503bbd11eeSEmmanuel Vadot 	    IFF_DRV_RUNNING)
1512a35d391SEmmanuel Vadot 		return;
152*afa0f66eSEmmanuel Vadot 	dma1000_txstart(sc);
1532a35d391SEmmanuel Vadot }
1542a35d391SEmmanuel Vadot 
1552a35d391SEmmanuel Vadot static void
156ca018790SMitchell Horne dwc_txstart(if_t ifp)
1572a35d391SEmmanuel Vadot {
158ca018790SMitchell Horne 	struct dwc_softc *sc = if_getsoftc(ifp);
1592a35d391SEmmanuel Vadot 
1602a35d391SEmmanuel Vadot 	DWC_LOCK(sc);
1612a35d391SEmmanuel Vadot 	dwc_txstart_locked(sc);
1622a35d391SEmmanuel Vadot 	DWC_UNLOCK(sc);
1632a35d391SEmmanuel Vadot }
1642a35d391SEmmanuel Vadot 
1652a35d391SEmmanuel Vadot static void
1662a35d391SEmmanuel Vadot dwc_init_locked(struct dwc_softc *sc)
1672a35d391SEmmanuel Vadot {
168ca018790SMitchell Horne 	if_t ifp = sc->ifp;
1692a35d391SEmmanuel Vadot 
1702a35d391SEmmanuel Vadot 	DWC_ASSERT_LOCKED(sc);
1712a35d391SEmmanuel Vadot 
1723bbd11eeSEmmanuel Vadot 	if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
1732a35d391SEmmanuel Vadot 		return;
1742a35d391SEmmanuel Vadot 
1756501fcdcSJiahao LI 	/*
17662519d5aSEmmanuel Vadot 	 * Call mii_mediachg() which will call back into dwc1000_miibus_statchg()
1776501fcdcSJiahao LI 	 * to set up the remaining config registers based on current media.
1786501fcdcSJiahao LI 	 */
1796501fcdcSJiahao LI 	mii_mediachg(sc->mii_softc);
1806501fcdcSJiahao LI 
18162519d5aSEmmanuel Vadot 	dwc1000_setup_rxfilter(sc);
18262519d5aSEmmanuel Vadot 	dwc1000_core_setup(sc);
18362519d5aSEmmanuel Vadot 	dwc1000_enable_mac(sc, true);
18462519d5aSEmmanuel Vadot 	dwc1000_enable_csum_offload(sc);
185972adf0fSEmmanuel Vadot 	dma1000_start(sc);
1862a35d391SEmmanuel Vadot 
1872a35d391SEmmanuel Vadot 	if_setdrvflagbits(ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE);
1885d43fd68SRuslan Bukin 
1892a35d391SEmmanuel Vadot 	callout_reset(&sc->dwc_callout, hz, dwc_tick, sc);
1902a35d391SEmmanuel Vadot }
1912a35d391SEmmanuel Vadot 
1922a35d391SEmmanuel Vadot static void
1932a35d391SEmmanuel Vadot dwc_init(void *if_softc)
1942a35d391SEmmanuel Vadot {
1952a35d391SEmmanuel Vadot 	struct dwc_softc *sc = if_softc;
1962a35d391SEmmanuel Vadot 
1972a35d391SEmmanuel Vadot 	DWC_LOCK(sc);
1982a35d391SEmmanuel Vadot 	dwc_init_locked(sc);
1992a35d391SEmmanuel Vadot 	DWC_UNLOCK(sc);
2002a35d391SEmmanuel Vadot }
2012a35d391SEmmanuel Vadot 
2022a35d391SEmmanuel Vadot static void
2032a35d391SEmmanuel Vadot dwc_stop_locked(struct dwc_softc *sc)
2042a35d391SEmmanuel Vadot {
205ca018790SMitchell Horne 	if_t ifp;
2062a35d391SEmmanuel Vadot 
2072a35d391SEmmanuel Vadot 	DWC_ASSERT_LOCKED(sc);
2082a35d391SEmmanuel Vadot 
2092a35d391SEmmanuel Vadot 	ifp = sc->ifp;
2103bbd11eeSEmmanuel Vadot 	if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
2112a35d391SEmmanuel Vadot 	sc->tx_watchdog_count = 0;
2122a35d391SEmmanuel Vadot 	sc->stats_harvest_count = 0;
2132a35d391SEmmanuel Vadot 
2142a35d391SEmmanuel Vadot 	callout_stop(&sc->dwc_callout);
2152a35d391SEmmanuel Vadot 
216972adf0fSEmmanuel Vadot 	dma1000_stop(sc);
21762519d5aSEmmanuel Vadot 	dwc1000_enable_mac(sc, false);
2182a35d391SEmmanuel Vadot }
2192a35d391SEmmanuel Vadot 
2202a35d391SEmmanuel Vadot static int
221ca018790SMitchell Horne dwc_ioctl(if_t ifp, u_long cmd, caddr_t data)
2222a35d391SEmmanuel Vadot {
2232a35d391SEmmanuel Vadot 	struct dwc_softc *sc;
2242a35d391SEmmanuel Vadot 	struct mii_data *mii;
2252a35d391SEmmanuel Vadot 	struct ifreq *ifr;
2263bbd11eeSEmmanuel Vadot 	int flags, mask, error;
2272a35d391SEmmanuel Vadot 
228ca018790SMitchell Horne 	sc = if_getsoftc(ifp);
2292a35d391SEmmanuel Vadot 	ifr = (struct ifreq *)data;
2302a35d391SEmmanuel Vadot 
2312a35d391SEmmanuel Vadot 	error = 0;
2322a35d391SEmmanuel Vadot 	switch (cmd) {
2332a35d391SEmmanuel Vadot 	case SIOCSIFFLAGS:
2342a35d391SEmmanuel Vadot 		DWC_LOCK(sc);
2353bbd11eeSEmmanuel Vadot 		if (if_getflags(ifp) & IFF_UP) {
2363bbd11eeSEmmanuel Vadot 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
2373bbd11eeSEmmanuel Vadot 				flags = if_getflags(ifp) ^ sc->if_flags;
2383bbd11eeSEmmanuel Vadot 				if ((flags & (IFF_PROMISC|IFF_ALLMULTI)) != 0)
23962519d5aSEmmanuel Vadot 					dwc1000_setup_rxfilter(sc);
2405d43fd68SRuslan Bukin 			} else {
2412a35d391SEmmanuel Vadot 				if (!sc->is_detaching)
2422a35d391SEmmanuel Vadot 					dwc_init_locked(sc);
2432a35d391SEmmanuel Vadot 			}
2442a35d391SEmmanuel Vadot 		} else {
2453bbd11eeSEmmanuel Vadot 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
2462a35d391SEmmanuel Vadot 				dwc_stop_locked(sc);
2472a35d391SEmmanuel Vadot 		}
2483bbd11eeSEmmanuel Vadot 		sc->if_flags = if_getflags(ifp);
2492a35d391SEmmanuel Vadot 		DWC_UNLOCK(sc);
2502a35d391SEmmanuel Vadot 		break;
2512a35d391SEmmanuel Vadot 	case SIOCADDMULTI:
2522a35d391SEmmanuel Vadot 	case SIOCDELMULTI:
2533bbd11eeSEmmanuel Vadot 		if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
2542a35d391SEmmanuel Vadot 			DWC_LOCK(sc);
25562519d5aSEmmanuel Vadot 			dwc1000_setup_rxfilter(sc);
2562a35d391SEmmanuel Vadot 			DWC_UNLOCK(sc);
2572a35d391SEmmanuel Vadot 		}
2582a35d391SEmmanuel Vadot 		break;
2592a35d391SEmmanuel Vadot 	case SIOCSIFMEDIA:
2602a35d391SEmmanuel Vadot 	case SIOCGIFMEDIA:
2612a35d391SEmmanuel Vadot 		mii = sc->mii_softc;
2622a35d391SEmmanuel Vadot 		error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
2632a35d391SEmmanuel Vadot 		break;
2642a35d391SEmmanuel Vadot 	case SIOCSIFCAP:
2653bbd11eeSEmmanuel Vadot 		mask = ifr->ifr_reqcap ^ if_getcapenable(ifp);
2662a35d391SEmmanuel Vadot 		if (mask & IFCAP_VLAN_MTU) {
2672a35d391SEmmanuel Vadot 			/* No work to do except acknowledge the change took */
2683bbd11eeSEmmanuel Vadot 			if_togglecapenable(ifp, IFCAP_VLAN_MTU);
2692a35d391SEmmanuel Vadot 		}
27098ea5a7bSEmmanuel Vadot 		if (mask & IFCAP_RXCSUM)
27198ea5a7bSEmmanuel Vadot 			if_togglecapenable(ifp, IFCAP_RXCSUM);
27298ea5a7bSEmmanuel Vadot 		if (mask & IFCAP_TXCSUM)
27398ea5a7bSEmmanuel Vadot 			if_togglecapenable(ifp, IFCAP_TXCSUM);
27498ea5a7bSEmmanuel Vadot 		if ((if_getcapenable(ifp) & IFCAP_TXCSUM) != 0)
27598ea5a7bSEmmanuel Vadot 			if_sethwassistbits(ifp, CSUM_IP | CSUM_UDP | CSUM_TCP, 0);
27698ea5a7bSEmmanuel Vadot 		else
27798ea5a7bSEmmanuel Vadot 			if_sethwassistbits(ifp, 0, CSUM_IP | CSUM_UDP | CSUM_TCP);
27835c9edabSJiahao Li 
27935c9edabSJiahao Li 		if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
28035c9edabSJiahao Li 			DWC_LOCK(sc);
28162519d5aSEmmanuel Vadot 			dwc1000_enable_csum_offload(sc);
28235c9edabSJiahao Li 			DWC_UNLOCK(sc);
28335c9edabSJiahao Li 		}
2842a35d391SEmmanuel Vadot 		break;
2852a35d391SEmmanuel Vadot 
2862a35d391SEmmanuel Vadot 	default:
2872a35d391SEmmanuel Vadot 		error = ether_ioctl(ifp, cmd, data);
2882a35d391SEmmanuel Vadot 		break;
2892a35d391SEmmanuel Vadot 	}
2902a35d391SEmmanuel Vadot 
2912a35d391SEmmanuel Vadot 	return (error);
2922a35d391SEmmanuel Vadot }
2932a35d391SEmmanuel Vadot 
2942a35d391SEmmanuel Vadot /*
2952a35d391SEmmanuel Vadot  * Interrupts functions
2962a35d391SEmmanuel Vadot  */
2972a35d391SEmmanuel Vadot 
2982a35d391SEmmanuel Vadot 
2992a35d391SEmmanuel Vadot static void
3002a35d391SEmmanuel Vadot dwc_intr(void *arg)
3012a35d391SEmmanuel Vadot {
3022a35d391SEmmanuel Vadot 	struct dwc_softc *sc;
3032a35d391SEmmanuel Vadot 	uint32_t reg;
3042a35d391SEmmanuel Vadot 
3052a35d391SEmmanuel Vadot 	sc = arg;
3062a35d391SEmmanuel Vadot 
3072a35d391SEmmanuel Vadot 	DWC_LOCK(sc);
3082a35d391SEmmanuel Vadot 
3092a35d391SEmmanuel Vadot 	reg = READ4(sc, INTERRUPT_STATUS);
3102a35d391SEmmanuel Vadot 	if (reg)
3112a35d391SEmmanuel Vadot 		READ4(sc, SGMII_RGMII_SMII_CTRL_STATUS);
3122a35d391SEmmanuel Vadot 
3132a35d391SEmmanuel Vadot 	reg = READ4(sc, DMA_STATUS);
3142a35d391SEmmanuel Vadot 	if (reg & DMA_STATUS_NIS) {
3152a35d391SEmmanuel Vadot 		if (reg & DMA_STATUS_RI)
316972adf0fSEmmanuel Vadot 			dma1000_rxfinish_locked(sc);
3172a35d391SEmmanuel Vadot 
3182a35d391SEmmanuel Vadot 		if (reg & DMA_STATUS_TI) {
319972adf0fSEmmanuel Vadot 			dma1000_txfinish_locked(sc);
3202a35d391SEmmanuel Vadot 			dwc_txstart_locked(sc);
3212a35d391SEmmanuel Vadot 		}
3222a35d391SEmmanuel Vadot 	}
3232a35d391SEmmanuel Vadot 
3242a35d391SEmmanuel Vadot 	if (reg & DMA_STATUS_AIS) {
3252a35d391SEmmanuel Vadot 		if (reg & DMA_STATUS_FBI) {
3262a35d391SEmmanuel Vadot 			/* Fatal bus error */
3272a35d391SEmmanuel Vadot 			device_printf(sc->dev,
3282a35d391SEmmanuel Vadot 			    "Ethernet DMA error, restarting controller.\n");
3292a35d391SEmmanuel Vadot 			dwc_stop_locked(sc);
3302a35d391SEmmanuel Vadot 			dwc_init_locked(sc);
3312a35d391SEmmanuel Vadot 		}
3322a35d391SEmmanuel Vadot 	}
3332a35d391SEmmanuel Vadot 
3342a35d391SEmmanuel Vadot 	WRITE4(sc, DMA_STATUS, reg & DMA_STATUS_INTR_MASK);
3352a35d391SEmmanuel Vadot 	DWC_UNLOCK(sc);
3362a35d391SEmmanuel Vadot }
3372a35d391SEmmanuel Vadot 
3382a35d391SEmmanuel Vadot static void
3392a35d391SEmmanuel Vadot dwc_tick(void *arg)
3402a35d391SEmmanuel Vadot {
3412a35d391SEmmanuel Vadot 	struct dwc_softc *sc;
342ca018790SMitchell Horne 	if_t ifp;
3432a35d391SEmmanuel Vadot 	int link_was_up;
3442a35d391SEmmanuel Vadot 
3452a35d391SEmmanuel Vadot 	sc = arg;
3462a35d391SEmmanuel Vadot 
3472a35d391SEmmanuel Vadot 	DWC_ASSERT_LOCKED(sc);
3482a35d391SEmmanuel Vadot 
3492a35d391SEmmanuel Vadot 	ifp = sc->ifp;
3502a35d391SEmmanuel Vadot 
3513bbd11eeSEmmanuel Vadot 	if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
3522a35d391SEmmanuel Vadot 	    return;
3532a35d391SEmmanuel Vadot 
3542a35d391SEmmanuel Vadot 	/*
3552a35d391SEmmanuel Vadot 	 * Typical tx watchdog.  If this fires it indicates that we enqueued
3562a35d391SEmmanuel Vadot 	 * packets for output and never got a txdone interrupt for them.  Maybe
3572a35d391SEmmanuel Vadot 	 * it's a missed interrupt somehow, just pretend we got one.
3582a35d391SEmmanuel Vadot 	 */
3592a35d391SEmmanuel Vadot 	if (sc->tx_watchdog_count > 0) {
3602a35d391SEmmanuel Vadot 		if (--sc->tx_watchdog_count == 0) {
361972adf0fSEmmanuel Vadot 			dma1000_txfinish_locked(sc);
3622a35d391SEmmanuel Vadot 		}
3632a35d391SEmmanuel Vadot 	}
3642a35d391SEmmanuel Vadot 
3652a35d391SEmmanuel Vadot 	/* Gather stats from hardware counters. */
36662519d5aSEmmanuel Vadot 	dwc1000_harvest_stats(sc);
3672a35d391SEmmanuel Vadot 
3682a35d391SEmmanuel Vadot 	/* Check the media status. */
3692a35d391SEmmanuel Vadot 	link_was_up = sc->link_is_up;
3702a35d391SEmmanuel Vadot 	mii_tick(sc->mii_softc);
3712a35d391SEmmanuel Vadot 	if (sc->link_is_up && !link_was_up)
3722a35d391SEmmanuel Vadot 		dwc_txstart_locked(sc);
3732a35d391SEmmanuel Vadot 
3742a35d391SEmmanuel Vadot 	/* Schedule another check one second from now. */
3752a35d391SEmmanuel Vadot 	callout_reset(&sc->dwc_callout, hz, dwc_tick, sc);
3762a35d391SEmmanuel Vadot }
3772a35d391SEmmanuel Vadot 
378d7acb49aSJared McNeill static int
379f114aaddSEmmanuel Vadot dwc_reset_phy(struct dwc_softc *sc)
380d7acb49aSJared McNeill {
381d7acb49aSJared McNeill 	pcell_t gpio_prop[4];
382d7acb49aSJared McNeill 	pcell_t delay_prop[3];
383f114aaddSEmmanuel Vadot 	phandle_t gpio_node;
384d7acb49aSJared McNeill 	device_t gpio;
385d7acb49aSJared McNeill 	uint32_t pin, flags;
386d7acb49aSJared McNeill 	uint32_t pin_value;
387d7acb49aSJared McNeill 
388f114aaddSEmmanuel Vadot 	/*
389f114aaddSEmmanuel Vadot 	 * All those properties are deprecated but still used in some DTS.
390f114aaddSEmmanuel Vadot 	 * The new way to deal with this is to use the generic bindings
391f114aaddSEmmanuel Vadot 	 * present in the ethernet-phy node.
392f114aaddSEmmanuel Vadot 	 */
393f114aaddSEmmanuel Vadot 	if (OF_getencprop(sc->node, "snps,reset-gpio",
394d7acb49aSJared McNeill 	    gpio_prop, sizeof(gpio_prop)) <= 0)
395d7acb49aSJared McNeill 		return (0);
396d7acb49aSJared McNeill 
397f114aaddSEmmanuel Vadot 	if (OF_getencprop(sc->node, "snps,reset-delays-us",
398d7acb49aSJared McNeill 	    delay_prop, sizeof(delay_prop)) <= 0) {
399f114aaddSEmmanuel Vadot 		device_printf(sc->dev,
400d7acb49aSJared McNeill 		    "Wrong property for snps,reset-delays-us");
401d7acb49aSJared McNeill 		return (ENXIO);
402d7acb49aSJared McNeill 	}
403d7acb49aSJared McNeill 
404d7acb49aSJared McNeill 	gpio_node = OF_node_from_xref(gpio_prop[0]);
405d7acb49aSJared McNeill 	if ((gpio = OF_device_from_xref(gpio_prop[0])) == NULL) {
406f114aaddSEmmanuel Vadot 		device_printf(sc->dev,
407d7acb49aSJared McNeill 		    "Can't find gpio controller for phy reset\n");
408d7acb49aSJared McNeill 		return (ENXIO);
409d7acb49aSJared McNeill 	}
410d7acb49aSJared McNeill 
411f114aaddSEmmanuel Vadot 	if (GPIO_MAP_GPIOS(gpio, sc->node, gpio_node,
41273a1170aSPedro F. Giffuni 	    nitems(gpio_prop) - 1,
413d7acb49aSJared McNeill 	    gpio_prop + 1, &pin, &flags) != 0) {
414f114aaddSEmmanuel Vadot 		device_printf(sc->dev, "Can't map gpio for phy reset\n");
415d7acb49aSJared McNeill 		return (ENXIO);
416d7acb49aSJared McNeill 	}
417d7acb49aSJared McNeill 
418d7acb49aSJared McNeill 	pin_value = GPIO_PIN_LOW;
419f114aaddSEmmanuel Vadot 	if (OF_hasprop(sc->node, "snps,reset-active-low"))
420d7acb49aSJared McNeill 		pin_value = GPIO_PIN_HIGH;
421d7acb49aSJared McNeill 
422d7acb49aSJared McNeill 	GPIO_PIN_SETFLAGS(gpio, pin, GPIO_PIN_OUTPUT);
423d7acb49aSJared McNeill 	GPIO_PIN_SET(gpio, pin, pin_value);
424c069412eSEmmanuel Vadot 	DELAY(delay_prop[0] * 5);
425d7acb49aSJared McNeill 	GPIO_PIN_SET(gpio, pin, !pin_value);
426c069412eSEmmanuel Vadot 	DELAY(delay_prop[1] * 5);
427d7acb49aSJared McNeill 	GPIO_PIN_SET(gpio, pin, pin_value);
428c069412eSEmmanuel Vadot 	DELAY(delay_prop[2] * 5);
429d7acb49aSJared McNeill 
430d7acb49aSJared McNeill 	return (0);
431d7acb49aSJared McNeill }
432d7acb49aSJared McNeill 
4336a05f063SJared McNeill static int
43450059a60SEmmanuel Vadot dwc_clock_init(struct dwc_softc *sc)
4356a05f063SJared McNeill {
43650059a60SEmmanuel Vadot 	int rv;
437824cfb47SOleksandr Tymoshenko 	int64_t freq;
4386a05f063SJared McNeill 
43950059a60SEmmanuel Vadot 	/* Required clock */
44050059a60SEmmanuel Vadot 	rv = clk_get_by_ofw_name(sc->dev, 0, "stmmaceth", &sc->clk_stmmaceth);
44150059a60SEmmanuel Vadot 	if (rv != 0) {
44250059a60SEmmanuel Vadot 		device_printf(sc->dev, "Cannot get GMAC main clock\n");
44350059a60SEmmanuel Vadot 		return (ENXIO);
4446a05f063SJared McNeill 	}
44550059a60SEmmanuel Vadot 	if ((rv = clk_enable(sc->clk_stmmaceth)) != 0) {
44650059a60SEmmanuel Vadot 		device_printf(sc->dev, "could not enable main clock\n");
44750059a60SEmmanuel Vadot 		return (rv);
4486a05f063SJared McNeill 	}
4496a05f063SJared McNeill 
45050059a60SEmmanuel Vadot 	/* Optional clock */
45150059a60SEmmanuel Vadot 	rv = clk_get_by_ofw_name(sc->dev, 0, "pclk", &sc->clk_pclk);
45250059a60SEmmanuel Vadot 	if (rv != 0)
45350059a60SEmmanuel Vadot 		return (0);
45450059a60SEmmanuel Vadot 	if ((rv = clk_enable(sc->clk_pclk)) != 0) {
45550059a60SEmmanuel Vadot 		device_printf(sc->dev, "could not enable peripheral clock\n");
45650059a60SEmmanuel Vadot 		return (rv);
4576a05f063SJared McNeill 	}
45850059a60SEmmanuel Vadot 
45950059a60SEmmanuel Vadot 	if (bootverbose) {
46050059a60SEmmanuel Vadot 		clk_get_freq(sc->clk_stmmaceth, &freq);
46150059a60SEmmanuel Vadot 		device_printf(sc->dev, "MAC clock(%s) freq: %jd\n",
46250059a60SEmmanuel Vadot 		    clk_get_name(sc->clk_stmmaceth), (intmax_t)freq);
46350059a60SEmmanuel Vadot 	}
46450059a60SEmmanuel Vadot 
46550059a60SEmmanuel Vadot 	return (0);
46650059a60SEmmanuel Vadot }
46750059a60SEmmanuel Vadot 
46850059a60SEmmanuel Vadot static int
46950059a60SEmmanuel Vadot dwc_reset_deassert(struct dwc_softc *sc)
47050059a60SEmmanuel Vadot {
47150059a60SEmmanuel Vadot 	int rv;
47250059a60SEmmanuel Vadot 
47350059a60SEmmanuel Vadot 	/* Required reset */
47450059a60SEmmanuel Vadot 	rv = hwreset_get_by_ofw_name(sc->dev, 0, "stmmaceth", &sc->rst_stmmaceth);
47550059a60SEmmanuel Vadot 	if (rv != 0) {
47650059a60SEmmanuel Vadot 		device_printf(sc->dev, "Cannot get GMAC reset\n");
47750059a60SEmmanuel Vadot 		return (ENXIO);
47850059a60SEmmanuel Vadot 	}
47950059a60SEmmanuel Vadot 	rv = hwreset_deassert(sc->rst_stmmaceth);
48050059a60SEmmanuel Vadot 	if (rv != 0) {
48150059a60SEmmanuel Vadot 		device_printf(sc->dev, "could not de-assert GMAC reset\n");
48250059a60SEmmanuel Vadot 		return (rv);
48350059a60SEmmanuel Vadot 	}
48450059a60SEmmanuel Vadot 
48550059a60SEmmanuel Vadot 	/* Optional reset */
48650059a60SEmmanuel Vadot 	rv = hwreset_get_by_ofw_name(sc->dev, 0, "ahb", &sc->rst_ahb);
48750059a60SEmmanuel Vadot 	if (rv != 0)
48850059a60SEmmanuel Vadot 		return (0);
48950059a60SEmmanuel Vadot 	rv = hwreset_deassert(sc->rst_ahb);
49050059a60SEmmanuel Vadot 	if (rv != 0) {
49150059a60SEmmanuel Vadot 		device_printf(sc->dev, "could not de-assert AHB reset\n");
49250059a60SEmmanuel Vadot 		return (rv);
4936a05f063SJared McNeill 	}
4946a05f063SJared McNeill 
4956a05f063SJared McNeill 	return (0);
4966a05f063SJared McNeill }
4976a05f063SJared McNeill 
49827f03f16SEmmanuel Vadot /*
49927f03f16SEmmanuel Vadot  * Probe/Attach functions
50027f03f16SEmmanuel Vadot  */
50127f03f16SEmmanuel Vadot 
5025d43fd68SRuslan Bukin static int
5035d43fd68SRuslan Bukin dwc_probe(device_t dev)
5045d43fd68SRuslan Bukin {
5055d43fd68SRuslan Bukin 
5065d43fd68SRuslan Bukin 	if (!ofw_bus_status_okay(dev))
5075d43fd68SRuslan Bukin 		return (ENXIO);
5085d43fd68SRuslan Bukin 
5095d43fd68SRuslan Bukin 	if (!ofw_bus_is_compatible(dev, "snps,dwmac"))
5105d43fd68SRuslan Bukin 		return (ENXIO);
5115d43fd68SRuslan Bukin 
5125d43fd68SRuslan Bukin 	device_set_desc(dev, "Gigabit Ethernet Controller");
5135d43fd68SRuslan Bukin 	return (BUS_PROBE_DEFAULT);
5145d43fd68SRuslan Bukin }
5155d43fd68SRuslan Bukin 
5165d43fd68SRuslan Bukin static int
5175d43fd68SRuslan Bukin dwc_attach(device_t dev)
5185d43fd68SRuslan Bukin {
5195d43fd68SRuslan Bukin 	uint8_t macaddr[ETHER_ADDR_LEN];
5205d43fd68SRuslan Bukin 	struct dwc_softc *sc;
521ca018790SMitchell Horne 	if_t ifp;
522ff0752c8SLuiz Otavio O Souza 	int error, i;
523ff0752c8SLuiz Otavio O Souza 	uint32_t reg;
5243e5cd548SEmmanuel Vadot 	uint32_t txpbl, rxpbl, pbl;
5255e38d9e4SEmmanuel Vadot 	bool nopblx8 = false;
5265e38d9e4SEmmanuel Vadot 	bool fixed_burst = false;
527f4bb6ea5SEmmanuel Vadot 	bool mixed_burst = false;
528b69c49d1SEmmanuel Vadot 	bool aal = false;
5295d43fd68SRuslan Bukin 
5305d43fd68SRuslan Bukin 	sc = device_get_softc(dev);
5315d43fd68SRuslan Bukin 	sc->dev = dev;
5325d43fd68SRuslan Bukin 	sc->rx_idx = 0;
533e5232621SOleksandr Tymoshenko 	sc->tx_desccount = TX_DESC_COUNT;
534e5232621SOleksandr Tymoshenko 	sc->tx_mapcount = 0;
5355df53927SLuiz Otavio O Souza 	sc->mii_clk = IF_DWC_MII_CLK(dev);
5365df53927SLuiz Otavio O Souza 	sc->mactype = IF_DWC_MAC_TYPE(dev);
5375df53927SLuiz Otavio O Souza 
538f114aaddSEmmanuel Vadot 	sc->node = ofw_bus_get_node(dev);
539f114aaddSEmmanuel Vadot 	switch (mii_fdt_get_contype(sc->node)) {
540f77d8d10SEmmanuel Vadot 	case MII_CONTYPE_RGMII:
541f77d8d10SEmmanuel Vadot 	case MII_CONTYPE_RGMII_ID:
542f77d8d10SEmmanuel Vadot 	case MII_CONTYPE_RGMII_RXID:
543f77d8d10SEmmanuel Vadot 	case MII_CONTYPE_RGMII_TXID:
544824cfb47SOleksandr Tymoshenko 		sc->phy_mode = PHY_MODE_RGMII;
545f77d8d10SEmmanuel Vadot 		break;
546f77d8d10SEmmanuel Vadot 	case MII_CONTYPE_RMII:
547824cfb47SOleksandr Tymoshenko 		sc->phy_mode = PHY_MODE_RMII;
548f77d8d10SEmmanuel Vadot 		break;
549da6252a6SEmmanuel Vadot 	case MII_CONTYPE_MII:
550da6252a6SEmmanuel Vadot 		sc->phy_mode = PHY_MODE_MII;
551da6252a6SEmmanuel Vadot 		break;
552f77d8d10SEmmanuel Vadot 	default:
553f77d8d10SEmmanuel Vadot 		device_printf(dev, "Unsupported MII type\n");
554f77d8d10SEmmanuel Vadot 		return (ENXIO);
555824cfb47SOleksandr Tymoshenko 	}
556824cfb47SOleksandr Tymoshenko 
557f114aaddSEmmanuel Vadot 	if (OF_getencprop(sc->node, "snps,pbl", &pbl, sizeof(uint32_t)) <= 0)
5583e5cd548SEmmanuel Vadot 		pbl = BUS_MODE_DEFAULT_PBL;
559f114aaddSEmmanuel Vadot 	if (OF_getencprop(sc->node, "snps,txpbl", &txpbl, sizeof(uint32_t)) <= 0)
5603e5cd548SEmmanuel Vadot 		txpbl = pbl;
561f114aaddSEmmanuel Vadot 	if (OF_getencprop(sc->node, "snps,rxpbl", &rxpbl, sizeof(uint32_t)) <= 0)
5623e5cd548SEmmanuel Vadot 		rxpbl = pbl;
563f114aaddSEmmanuel Vadot 	if (OF_hasprop(sc->node, "snps,no-pbl-x8") == 1)
5645e38d9e4SEmmanuel Vadot 		nopblx8 = true;
565f114aaddSEmmanuel Vadot 	if (OF_hasprop(sc->node, "snps,fixed-burst") == 1)
5665e38d9e4SEmmanuel Vadot 		fixed_burst = true;
567f4bb6ea5SEmmanuel Vadot 	if (OF_hasprop(sc->node, "snps,mixed-burst") == 1)
568f4bb6ea5SEmmanuel Vadot 		mixed_burst = true;
569b69c49d1SEmmanuel Vadot 	if (OF_hasprop(sc->node, "snps,aal") == 1)
570b69c49d1SEmmanuel Vadot 		aal = true;
5715e38d9e4SEmmanuel Vadot 
57250059a60SEmmanuel Vadot 	error = clk_set_assigned(dev, ofw_bus_get_node(dev));
57350059a60SEmmanuel Vadot 	if (error != 0) {
57450059a60SEmmanuel Vadot 		device_printf(dev, "clk_set_assigned failed\n");
57550059a60SEmmanuel Vadot 		return (error);
57650059a60SEmmanuel Vadot 	}
5775d43fd68SRuslan Bukin 
57850059a60SEmmanuel Vadot 	/* Enable main clock */
57950059a60SEmmanuel Vadot 	if ((error = dwc_clock_init(sc)) != 0)
58050059a60SEmmanuel Vadot 		return (error);
58150059a60SEmmanuel Vadot 	/* De-assert main reset */
58250059a60SEmmanuel Vadot 	if ((error = dwc_reset_deassert(sc)) != 0)
58350059a60SEmmanuel Vadot 		return (error);
58450059a60SEmmanuel Vadot 
58550059a60SEmmanuel Vadot 	if (IF_DWC_INIT(dev) != 0)
5866a05f063SJared McNeill 		return (ENXIO);
5876a05f063SJared McNeill 
5885d43fd68SRuslan Bukin 	if (bus_alloc_resources(dev, dwc_spec, sc->res)) {
5895d43fd68SRuslan Bukin 		device_printf(dev, "could not allocate resources\n");
5905d43fd68SRuslan Bukin 		return (ENXIO);
5915d43fd68SRuslan Bukin 	}
5925d43fd68SRuslan Bukin 
5935d43fd68SRuslan Bukin 	/* Read MAC before reset */
59462519d5aSEmmanuel Vadot 	dwc1000_get_hwaddr(sc, macaddr);
5955d43fd68SRuslan Bukin 
596d7acb49aSJared McNeill 	/* Reset the PHY if needed */
597f114aaddSEmmanuel Vadot 	if (dwc_reset_phy(sc) != 0) {
598d7acb49aSJared McNeill 		device_printf(dev, "Can't reset the PHY\n");
59930f16ad4SEmmanuel Vadot 		bus_release_resources(dev, dwc_spec, sc->res);
600d7acb49aSJared McNeill 		return (ENXIO);
601d7acb49aSJared McNeill 	}
602d7acb49aSJared McNeill 
6035d43fd68SRuslan Bukin 	/* Reset */
6045d43fd68SRuslan Bukin 	reg = READ4(sc, BUS_MODE);
6055d43fd68SRuslan Bukin 	reg |= (BUS_MODE_SWR);
6065d43fd68SRuslan Bukin 	WRITE4(sc, BUS_MODE, reg);
6075d43fd68SRuslan Bukin 
608d8e5258dSRuslan Bukin 	for (i = 0; i < MAC_RESET_TIMEOUT; i++) {
6095d43fd68SRuslan Bukin 		if ((READ4(sc, BUS_MODE) & BUS_MODE_SWR) == 0)
6105d43fd68SRuslan Bukin 			break;
6115d43fd68SRuslan Bukin 		DELAY(10);
6125d43fd68SRuslan Bukin 	}
613d8e5258dSRuslan Bukin 	if (i >= MAC_RESET_TIMEOUT) {
6145d43fd68SRuslan Bukin 		device_printf(sc->dev, "Can't reset DWC.\n");
61530f16ad4SEmmanuel Vadot 		bus_release_resources(dev, dwc_spec, sc->res);
6165d43fd68SRuslan Bukin 		return (ENXIO);
6175d43fd68SRuslan Bukin 	}
6185d43fd68SRuslan Bukin 
6195e38d9e4SEmmanuel Vadot 	reg = BUS_MODE_USP;
6205e38d9e4SEmmanuel Vadot 	if (!nopblx8)
6215e38d9e4SEmmanuel Vadot 		reg |= BUS_MODE_EIGHTXPBL;
6225e38d9e4SEmmanuel Vadot 	reg |= (txpbl << BUS_MODE_PBL_SHIFT);
6235e38d9e4SEmmanuel Vadot 	reg |= (rxpbl << BUS_MODE_RPBL_SHIFT);
6245e38d9e4SEmmanuel Vadot 	if (fixed_burst)
6255e38d9e4SEmmanuel Vadot 		reg |= BUS_MODE_FIXEDBURST;
626f4bb6ea5SEmmanuel Vadot 	if (mixed_burst)
627f4bb6ea5SEmmanuel Vadot 		reg |= BUS_MODE_MIXEDBURST;
628b69c49d1SEmmanuel Vadot 	if (aal)
629b69c49d1SEmmanuel Vadot 		reg |= BUS_MODE_AAL;
6303e5cd548SEmmanuel Vadot 
6315d43fd68SRuslan Bukin 	WRITE4(sc, BUS_MODE, reg);
6325d43fd68SRuslan Bukin 
6335d43fd68SRuslan Bukin 	/*
6345d43fd68SRuslan Bukin 	 * DMA must be stop while changing descriptor list addresses.
6355d43fd68SRuslan Bukin 	 */
6365d43fd68SRuslan Bukin 	reg = READ4(sc, OPERATION_MODE);
6375d43fd68SRuslan Bukin 	reg &= ~(MODE_ST | MODE_SR);
6385d43fd68SRuslan Bukin 	WRITE4(sc, OPERATION_MODE, reg);
6395d43fd68SRuslan Bukin 
640972adf0fSEmmanuel Vadot 	if (dma1000_init(sc)) {
64130f16ad4SEmmanuel Vadot 		bus_release_resources(dev, dwc_spec, sc->res);
6425d43fd68SRuslan Bukin 		return (ENXIO);
64330f16ad4SEmmanuel Vadot 	}
6445d43fd68SRuslan Bukin 
6455d43fd68SRuslan Bukin 	/* Setup addresses */
6465d43fd68SRuslan Bukin 	WRITE4(sc, RX_DESCR_LIST_ADDR, sc->rxdesc_ring_paddr);
6475d43fd68SRuslan Bukin 	WRITE4(sc, TX_DESCR_LIST_ADDR, sc->txdesc_ring_paddr);
6485d43fd68SRuslan Bukin 
649d8e5258dSRuslan Bukin 	mtx_init(&sc->mtx, device_get_nameunit(sc->dev),
650d8e5258dSRuslan Bukin 	    MTX_NETWORK_LOCK, MTX_DEF);
651d8e5258dSRuslan Bukin 
652d8e5258dSRuslan Bukin 	callout_init_mtx(&sc->dwc_callout, &sc->mtx, 0);
653d8e5258dSRuslan Bukin 
654d8e5258dSRuslan Bukin 	/* Setup interrupt handler. */
655d8e5258dSRuslan Bukin 	error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_NET | INTR_MPSAFE,
656d8e5258dSRuslan Bukin 	    NULL, dwc_intr, sc, &sc->intr_cookie);
657d8e5258dSRuslan Bukin 	if (error != 0) {
658d8e5258dSRuslan Bukin 		device_printf(dev, "could not setup interrupt handler.\n");
65930f16ad4SEmmanuel Vadot 		bus_release_resources(dev, dwc_spec, sc->res);
660d8e5258dSRuslan Bukin 		return (ENXIO);
661d8e5258dSRuslan Bukin 	}
662d8e5258dSRuslan Bukin 
6635d43fd68SRuslan Bukin 	/* Set up the ethernet interface. */
6645d43fd68SRuslan Bukin 	sc->ifp = ifp = if_alloc(IFT_ETHER);
6655d43fd68SRuslan Bukin 
666ca018790SMitchell Horne 	if_setsoftc(ifp, sc);
6675d43fd68SRuslan Bukin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
6683bbd11eeSEmmanuel Vadot 	if_setflags(sc->ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
6693bbd11eeSEmmanuel Vadot 	if_setstartfn(ifp, dwc_txstart);
6703bbd11eeSEmmanuel Vadot 	if_setioctlfn(ifp, dwc_ioctl);
6713bbd11eeSEmmanuel Vadot 	if_setinitfn(ifp, dwc_init);
672e5232621SOleksandr Tymoshenko 	if_setsendqlen(ifp, TX_MAP_COUNT - 1);
6733bbd11eeSEmmanuel Vadot 	if_setsendqready(sc->ifp);
67498ea5a7bSEmmanuel Vadot 	if_sethwassist(sc->ifp, CSUM_IP | CSUM_UDP | CSUM_TCP);
67598ea5a7bSEmmanuel Vadot 	if_setcapabilities(sc->ifp, IFCAP_VLAN_MTU | IFCAP_HWCSUM);
6763bbd11eeSEmmanuel Vadot 	if_setcapenable(sc->ifp, if_getcapabilities(sc->ifp));
6775d43fd68SRuslan Bukin 
6785d43fd68SRuslan Bukin 	/* Attach the mii driver. */
6795d43fd68SRuslan Bukin 	error = mii_attach(dev, &sc->miibus, ifp, dwc_media_change,
6805d43fd68SRuslan Bukin 	    dwc_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY,
6815d43fd68SRuslan Bukin 	    MII_OFFSET_ANY, 0);
6825d43fd68SRuslan Bukin 
6835d43fd68SRuslan Bukin 	if (error != 0) {
6845d43fd68SRuslan Bukin 		device_printf(dev, "PHY attach failed\n");
68530f16ad4SEmmanuel Vadot 		bus_teardown_intr(dev, sc->res[1], sc->intr_cookie);
68630f16ad4SEmmanuel Vadot 		bus_release_resources(dev, dwc_spec, sc->res);
6875d43fd68SRuslan Bukin 		return (ENXIO);
6885d43fd68SRuslan Bukin 	}
6895d43fd68SRuslan Bukin 	sc->mii_softc = device_get_softc(sc->miibus);
6905d43fd68SRuslan Bukin 
6915d43fd68SRuslan Bukin 	/* All ready to run, attach the ethernet interface. */
6925d43fd68SRuslan Bukin 	ether_ifattach(ifp, macaddr);
6935d43fd68SRuslan Bukin 	sc->is_attached = true;
6945d43fd68SRuslan Bukin 
6955d43fd68SRuslan Bukin 	return (0);
6965d43fd68SRuslan Bukin }
6975d43fd68SRuslan Bukin 
69827b39e58SMitchell Horne static int
69927b39e58SMitchell Horne dwc_detach(device_t dev)
70027b39e58SMitchell Horne {
70127b39e58SMitchell Horne 	struct dwc_softc *sc;
70227b39e58SMitchell Horne 
70327b39e58SMitchell Horne 	sc = device_get_softc(dev);
70427b39e58SMitchell Horne 
70527b39e58SMitchell Horne 	/*
70627b39e58SMitchell Horne 	 * Disable and tear down interrupts before anything else, so we don't
70727b39e58SMitchell Horne 	 * race with the handler.
70827b39e58SMitchell Horne 	 */
70927b39e58SMitchell Horne 	WRITE4(sc, INTERRUPT_ENABLE, 0);
71027b39e58SMitchell Horne 	if (sc->intr_cookie != NULL) {
71127b39e58SMitchell Horne 		bus_teardown_intr(dev, sc->res[1], sc->intr_cookie);
71227b39e58SMitchell Horne 	}
71327b39e58SMitchell Horne 
71427b39e58SMitchell Horne 	if (sc->is_attached) {
71527b39e58SMitchell Horne 		DWC_LOCK(sc);
71627b39e58SMitchell Horne 		sc->is_detaching = true;
71727b39e58SMitchell Horne 		dwc_stop_locked(sc);
71827b39e58SMitchell Horne 		DWC_UNLOCK(sc);
71927b39e58SMitchell Horne 		callout_drain(&sc->dwc_callout);
72027b39e58SMitchell Horne 		ether_ifdetach(sc->ifp);
72127b39e58SMitchell Horne 	}
72227b39e58SMitchell Horne 
72327b39e58SMitchell Horne 	if (sc->miibus != NULL) {
72427b39e58SMitchell Horne 		device_delete_child(dev, sc->miibus);
72527b39e58SMitchell Horne 		sc->miibus = NULL;
72627b39e58SMitchell Horne 	}
72727b39e58SMitchell Horne 	bus_generic_detach(dev);
72827b39e58SMitchell Horne 
72927b39e58SMitchell Horne 	/* Free DMA descriptors */
730972adf0fSEmmanuel Vadot 	dma1000_free(sc);
73127b39e58SMitchell Horne 
73227b39e58SMitchell Horne 	if (sc->ifp != NULL) {
73327b39e58SMitchell Horne 		if_free(sc->ifp);
73427b39e58SMitchell Horne 		sc->ifp = NULL;
73527b39e58SMitchell Horne 	}
73627b39e58SMitchell Horne 
73727b39e58SMitchell Horne 	bus_release_resources(dev, dwc_spec, sc->res);
73827b39e58SMitchell Horne 
73927b39e58SMitchell Horne 	mtx_destroy(&sc->mtx);
74027b39e58SMitchell Horne 	return (0);
74127b39e58SMitchell Horne }
74227b39e58SMitchell Horne 
7435d43fd68SRuslan Bukin static device_method_t dwc_methods[] = {
7445d43fd68SRuslan Bukin 	DEVMETHOD(device_probe,		dwc_probe),
7455d43fd68SRuslan Bukin 	DEVMETHOD(device_attach,	dwc_attach),
74627b39e58SMitchell Horne 	DEVMETHOD(device_detach,	dwc_detach),
7475d43fd68SRuslan Bukin 
7485d43fd68SRuslan Bukin 	/* MII Interface */
74962519d5aSEmmanuel Vadot 	DEVMETHOD(miibus_readreg,	dwc1000_miibus_read_reg),
75062519d5aSEmmanuel Vadot 	DEVMETHOD(miibus_writereg,	dwc1000_miibus_write_reg),
75162519d5aSEmmanuel Vadot 	DEVMETHOD(miibus_statchg,	dwc1000_miibus_statchg),
7525d43fd68SRuslan Bukin 
7535d43fd68SRuslan Bukin 	{ 0, 0 }
7545d43fd68SRuslan Bukin };
7555d43fd68SRuslan Bukin 
7565df53927SLuiz Otavio O Souza driver_t dwc_driver = {
7575d43fd68SRuslan Bukin 	"dwc",
7585d43fd68SRuslan Bukin 	dwc_methods,
7595d43fd68SRuslan Bukin 	sizeof(struct dwc_softc),
7605d43fd68SRuslan Bukin };
7615d43fd68SRuslan Bukin 
762e55d0536SJohn Baldwin DRIVER_MODULE(dwc, simplebus, dwc_driver, 0, 0);
7633e38757dSJohn Baldwin DRIVER_MODULE(miibus, dwc, miibus_driver, 0, 0);
7645d43fd68SRuslan Bukin 
7655d43fd68SRuslan Bukin MODULE_DEPEND(dwc, ether, 1, 1, 1);
7665d43fd68SRuslan Bukin MODULE_DEPEND(dwc, miibus, 1, 1, 1);
767