xref: /freebsd/sys/dev/dwc/if_dwc.c (revision 972adf0f97ac5e2f3a6024c25623a908403aff9b)
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>
73*972adf0fSEmmanuel Vadot #include <dev/dwc/dwc1000_reg.h>
74*972adf0fSEmmanuel Vadot #include <dev/dwc/dwc1000_dma.h>
75c36125f6SEmmanuel Vadot 
765df53927SLuiz Otavio O Souza #include "if_dwc_if.h"
77d7acb49aSJared McNeill #include "gpio_if.h"
785d43fd68SRuslan Bukin #include "miibus_if.h"
795d43fd68SRuslan Bukin 
80d8e5258dSRuslan Bukin #define	MAC_RESET_TIMEOUT	100
815d43fd68SRuslan Bukin #define	WATCHDOG_TIMEOUT_SECS	5
825d43fd68SRuslan Bukin #define	STATS_HARVEST_INTERVAL	2
835d43fd68SRuslan Bukin 
845d43fd68SRuslan Bukin static struct resource_spec dwc_spec[] = {
855d43fd68SRuslan Bukin 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
865d43fd68SRuslan Bukin 	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
875d43fd68SRuslan Bukin 	{ -1, 0 }
885d43fd68SRuslan Bukin };
895d43fd68SRuslan Bukin 
905d43fd68SRuslan Bukin static void dwc_stop_locked(struct dwc_softc *sc);
915d43fd68SRuslan Bukin static void dwc_setup_rxfilter(struct dwc_softc *sc);
92158ce7baSEmmanuel Vadot static void dwc_setup_core(struct dwc_softc *sc);
93f368f4b1SEmmanuel Vadot static void dwc_enable_mac(struct dwc_softc *sc, bool enable);
945d43fd68SRuslan Bukin 
952a35d391SEmmanuel Vadot static void dwc_tick(void *arg);
962a35d391SEmmanuel Vadot 
972b4a66edSEmmanuel Vadot /* Pause time field in the transmitted control frame */
982b4a66edSEmmanuel Vadot static int dwc_pause_time = 0xffff;
992b4a66edSEmmanuel Vadot TUNABLE_INT("hw.dwc.pause_time", &dwc_pause_time);
1002b4a66edSEmmanuel Vadot 
1012a35d391SEmmanuel Vadot /*
1022a35d391SEmmanuel Vadot  * MIIBUS functions
1032a35d391SEmmanuel Vadot  */
1042a35d391SEmmanuel Vadot 
1052a35d391SEmmanuel Vadot static int
1062a35d391SEmmanuel Vadot dwc_miibus_read_reg(device_t dev, int phy, int reg)
1072a35d391SEmmanuel Vadot {
1082a35d391SEmmanuel Vadot 	struct dwc_softc *sc;
1092a35d391SEmmanuel Vadot 	uint16_t mii;
1102a35d391SEmmanuel Vadot 	size_t cnt;
1112a35d391SEmmanuel Vadot 	int rv = 0;
1122a35d391SEmmanuel Vadot 
1132a35d391SEmmanuel Vadot 	sc = device_get_softc(dev);
1142a35d391SEmmanuel Vadot 
1152a35d391SEmmanuel Vadot 	mii = ((phy & GMII_ADDRESS_PA_MASK) << GMII_ADDRESS_PA_SHIFT)
1162a35d391SEmmanuel Vadot 	    | ((reg & GMII_ADDRESS_GR_MASK) << GMII_ADDRESS_GR_SHIFT)
1172a35d391SEmmanuel Vadot 	    | (sc->mii_clk << GMII_ADDRESS_CR_SHIFT)
1182a35d391SEmmanuel Vadot 	    | GMII_ADDRESS_GB; /* Busy flag */
1192a35d391SEmmanuel Vadot 
1202a35d391SEmmanuel Vadot 	WRITE4(sc, GMII_ADDRESS, mii);
1212a35d391SEmmanuel Vadot 
1222a35d391SEmmanuel Vadot 	for (cnt = 0; cnt < 1000; cnt++) {
1232a35d391SEmmanuel Vadot 		if (!(READ4(sc, GMII_ADDRESS) & GMII_ADDRESS_GB)) {
1242a35d391SEmmanuel Vadot 			rv = READ4(sc, GMII_DATA);
1252a35d391SEmmanuel Vadot 			break;
1262a35d391SEmmanuel Vadot 		}
1272a35d391SEmmanuel Vadot 		DELAY(10);
1282a35d391SEmmanuel Vadot 	}
1292a35d391SEmmanuel Vadot 
1302a35d391SEmmanuel Vadot 	return rv;
1312a35d391SEmmanuel Vadot }
1322a35d391SEmmanuel Vadot 
1332a35d391SEmmanuel Vadot static int
1342a35d391SEmmanuel Vadot dwc_miibus_write_reg(device_t dev, int phy, int reg, int val)
1352a35d391SEmmanuel Vadot {
1362a35d391SEmmanuel Vadot 	struct dwc_softc *sc;
1372a35d391SEmmanuel Vadot 	uint16_t mii;
1382a35d391SEmmanuel Vadot 	size_t cnt;
1392a35d391SEmmanuel Vadot 
1402a35d391SEmmanuel Vadot 	sc = device_get_softc(dev);
1412a35d391SEmmanuel Vadot 
1422a35d391SEmmanuel Vadot 	mii = ((phy & GMII_ADDRESS_PA_MASK) << GMII_ADDRESS_PA_SHIFT)
1432a35d391SEmmanuel Vadot 	    | ((reg & GMII_ADDRESS_GR_MASK) << GMII_ADDRESS_GR_SHIFT)
1442a35d391SEmmanuel Vadot 	    | (sc->mii_clk << GMII_ADDRESS_CR_SHIFT)
1452a35d391SEmmanuel Vadot 	    | GMII_ADDRESS_GB | GMII_ADDRESS_GW;
1462a35d391SEmmanuel Vadot 
1472a35d391SEmmanuel Vadot 	WRITE4(sc, GMII_DATA, val);
1482a35d391SEmmanuel Vadot 	WRITE4(sc, GMII_ADDRESS, mii);
1492a35d391SEmmanuel Vadot 
1502a35d391SEmmanuel Vadot 	for (cnt = 0; cnt < 1000; cnt++) {
1512a35d391SEmmanuel Vadot 		if (!(READ4(sc, GMII_ADDRESS) & GMII_ADDRESS_GB)) {
1522a35d391SEmmanuel Vadot 			break;
1532a35d391SEmmanuel Vadot                 }
1542a35d391SEmmanuel Vadot 		DELAY(10);
1552a35d391SEmmanuel Vadot 	}
1562a35d391SEmmanuel Vadot 
1572a35d391SEmmanuel Vadot 	return (0);
1582a35d391SEmmanuel Vadot }
1592a35d391SEmmanuel Vadot 
1602a35d391SEmmanuel Vadot static void
1612a35d391SEmmanuel Vadot dwc_miibus_statchg(device_t dev)
1622a35d391SEmmanuel Vadot {
1632a35d391SEmmanuel Vadot 	struct dwc_softc *sc;
1642a35d391SEmmanuel Vadot 	struct mii_data *mii;
1652a35d391SEmmanuel Vadot 	uint32_t reg;
1662a35d391SEmmanuel Vadot 
1672a35d391SEmmanuel Vadot 	/*
1682a35d391SEmmanuel Vadot 	 * Called by the MII bus driver when the PHY establishes
1692a35d391SEmmanuel Vadot 	 * link to set the MAC interface registers.
1702a35d391SEmmanuel Vadot 	 */
1712a35d391SEmmanuel Vadot 
1722a35d391SEmmanuel Vadot 	sc = device_get_softc(dev);
1732a35d391SEmmanuel Vadot 
1742a35d391SEmmanuel Vadot 	DWC_ASSERT_LOCKED(sc);
1752a35d391SEmmanuel Vadot 
1762a35d391SEmmanuel Vadot 	mii = sc->mii_softc;
1772a35d391SEmmanuel Vadot 
1782a35d391SEmmanuel Vadot 	if (mii->mii_media_status & IFM_ACTIVE)
1792a35d391SEmmanuel Vadot 		sc->link_is_up = true;
1802a35d391SEmmanuel Vadot 	else
1812a35d391SEmmanuel Vadot 		sc->link_is_up = false;
1822a35d391SEmmanuel Vadot 
1832a35d391SEmmanuel Vadot 	reg = READ4(sc, MAC_CONFIGURATION);
1842a35d391SEmmanuel Vadot 	switch (IFM_SUBTYPE(mii->mii_media_active)) {
1852a35d391SEmmanuel Vadot 	case IFM_1000_T:
1862a35d391SEmmanuel Vadot 	case IFM_1000_SX:
1872a35d391SEmmanuel Vadot 		reg &= ~(CONF_FES | CONF_PS);
1882a35d391SEmmanuel Vadot 		break;
1892a35d391SEmmanuel Vadot 	case IFM_100_TX:
1902a35d391SEmmanuel Vadot 		reg |= (CONF_FES | CONF_PS);
1912a35d391SEmmanuel Vadot 		break;
1922a35d391SEmmanuel Vadot 	case IFM_10_T:
1932a35d391SEmmanuel Vadot 		reg &= ~(CONF_FES);
1942a35d391SEmmanuel Vadot 		reg |= (CONF_PS);
1952a35d391SEmmanuel Vadot 		break;
1962a35d391SEmmanuel Vadot 	case IFM_NONE:
1972a35d391SEmmanuel Vadot 		sc->link_is_up = false;
1982a35d391SEmmanuel Vadot 		return;
1992a35d391SEmmanuel Vadot 	default:
2002a35d391SEmmanuel Vadot 		sc->link_is_up = false;
2012a35d391SEmmanuel Vadot 		device_printf(dev, "Unsupported media %u\n",
2022a35d391SEmmanuel Vadot 		    IFM_SUBTYPE(mii->mii_media_active));
2032a35d391SEmmanuel Vadot 		return;
2042a35d391SEmmanuel Vadot 	}
2052a35d391SEmmanuel Vadot 	if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0)
2062a35d391SEmmanuel Vadot 		reg |= (CONF_DM);
2072a35d391SEmmanuel Vadot 	else
2082a35d391SEmmanuel Vadot 		reg &= ~(CONF_DM);
2092a35d391SEmmanuel Vadot 	WRITE4(sc, MAC_CONFIGURATION, reg);
2102a35d391SEmmanuel Vadot 
2112b4a66edSEmmanuel Vadot 	reg = FLOW_CONTROL_UP;
2122b4a66edSEmmanuel Vadot 	if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0)
2132b4a66edSEmmanuel Vadot 		reg |= FLOW_CONTROL_TX;
2142b4a66edSEmmanuel Vadot 	if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0)
2152b4a66edSEmmanuel Vadot 		reg |= FLOW_CONTROL_RX;
2162b4a66edSEmmanuel Vadot 	if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0)
2172b4a66edSEmmanuel Vadot 		reg |= dwc_pause_time << FLOW_CONTROL_PT_SHIFT;
2182b4a66edSEmmanuel Vadot 	WRITE4(sc, FLOW_CONTROL, reg);
2192b4a66edSEmmanuel Vadot 
2202a35d391SEmmanuel Vadot 	IF_DWC_SET_SPEED(dev, IFM_SUBTYPE(mii->mii_media_active));
2212a35d391SEmmanuel Vadot 
2222a35d391SEmmanuel Vadot }
2232a35d391SEmmanuel Vadot 
2242a35d391SEmmanuel Vadot /*
2252a35d391SEmmanuel Vadot  * Media functions
2262a35d391SEmmanuel Vadot  */
2272a35d391SEmmanuel Vadot 
2282a35d391SEmmanuel Vadot static void
229ca018790SMitchell Horne dwc_media_status(if_t ifp, struct ifmediareq *ifmr)
2302a35d391SEmmanuel Vadot {
2312a35d391SEmmanuel Vadot 	struct dwc_softc *sc;
2322a35d391SEmmanuel Vadot 	struct mii_data *mii;
2332a35d391SEmmanuel Vadot 
234ca018790SMitchell Horne 	sc = if_getsoftc(ifp);
2352a35d391SEmmanuel Vadot 	mii = sc->mii_softc;
2362a35d391SEmmanuel Vadot 	DWC_LOCK(sc);
2372a35d391SEmmanuel Vadot 	mii_pollstat(mii);
2382a35d391SEmmanuel Vadot 	ifmr->ifm_active = mii->mii_media_active;
2392a35d391SEmmanuel Vadot 	ifmr->ifm_status = mii->mii_media_status;
2402a35d391SEmmanuel Vadot 	DWC_UNLOCK(sc);
2412a35d391SEmmanuel Vadot }
2422a35d391SEmmanuel Vadot 
2432a35d391SEmmanuel Vadot static int
2442a35d391SEmmanuel Vadot dwc_media_change_locked(struct dwc_softc *sc)
2452a35d391SEmmanuel Vadot {
2462a35d391SEmmanuel Vadot 
2472a35d391SEmmanuel Vadot 	return (mii_mediachg(sc->mii_softc));
2482a35d391SEmmanuel Vadot }
2492a35d391SEmmanuel Vadot 
2502a35d391SEmmanuel Vadot static int
251ca018790SMitchell Horne dwc_media_change(if_t ifp)
2522a35d391SEmmanuel Vadot {
2532a35d391SEmmanuel Vadot 	struct dwc_softc *sc;
2542a35d391SEmmanuel Vadot 	int error;
2552a35d391SEmmanuel Vadot 
256ca018790SMitchell Horne 	sc = if_getsoftc(ifp);
2572a35d391SEmmanuel Vadot 
2582a35d391SEmmanuel Vadot 	DWC_LOCK(sc);
2592a35d391SEmmanuel Vadot 	error = dwc_media_change_locked(sc);
2602a35d391SEmmanuel Vadot 	DWC_UNLOCK(sc);
2612a35d391SEmmanuel Vadot 	return (error);
2622a35d391SEmmanuel Vadot }
2632a35d391SEmmanuel Vadot 
2642a35d391SEmmanuel Vadot /*
2652a35d391SEmmanuel Vadot  * Core functions
2662a35d391SEmmanuel Vadot  */
2672a35d391SEmmanuel Vadot 
2682a35d391SEmmanuel Vadot static const uint8_t nibbletab[] = {
2692a35d391SEmmanuel Vadot 	/* 0x0 0000 -> 0000 */  0x0,
2702a35d391SEmmanuel Vadot 	/* 0x1 0001 -> 1000 */  0x8,
2712a35d391SEmmanuel Vadot 	/* 0x2 0010 -> 0100 */  0x4,
2722a35d391SEmmanuel Vadot 	/* 0x3 0011 -> 1100 */  0xc,
2732a35d391SEmmanuel Vadot 	/* 0x4 0100 -> 0010 */  0x2,
2742a35d391SEmmanuel Vadot 	/* 0x5 0101 -> 1010 */  0xa,
2752a35d391SEmmanuel Vadot 	/* 0x6 0110 -> 0110 */  0x6,
2762a35d391SEmmanuel Vadot 	/* 0x7 0111 -> 1110 */  0xe,
2772a35d391SEmmanuel Vadot 	/* 0x8 1000 -> 0001 */  0x1,
2782a35d391SEmmanuel Vadot 	/* 0x9 1001 -> 1001 */  0x9,
2792a35d391SEmmanuel Vadot 	/* 0xa 1010 -> 0101 */  0x5,
2802a35d391SEmmanuel Vadot 	/* 0xb 1011 -> 1101 */  0xd,
2812a35d391SEmmanuel Vadot 	/* 0xc 1100 -> 0011 */  0x3,
2822a35d391SEmmanuel Vadot 	/* 0xd 1101 -> 1011 */  0xb,
2832a35d391SEmmanuel Vadot 	/* 0xe 1110 -> 0111 */  0x7,
2842a35d391SEmmanuel Vadot 	/* 0xf 1111 -> 1111 */  0xf, };
2852a35d391SEmmanuel Vadot 
2862a35d391SEmmanuel Vadot static uint8_t
2872a35d391SEmmanuel Vadot bitreverse(uint8_t x)
2882a35d391SEmmanuel Vadot {
2892a35d391SEmmanuel Vadot 
2902a35d391SEmmanuel Vadot 	return (nibbletab[x & 0xf] << 4) | nibbletab[x >> 4];
2912a35d391SEmmanuel Vadot }
2922a35d391SEmmanuel Vadot 
2932a35d391SEmmanuel Vadot static u_int
2942a35d391SEmmanuel Vadot dwc_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
2952a35d391SEmmanuel Vadot {
2962a35d391SEmmanuel Vadot 	struct dwc_hash_maddr_ctx *ctx = arg;
2972a35d391SEmmanuel Vadot 	uint32_t crc, hashbit, hashreg;
2982a35d391SEmmanuel Vadot 	uint8_t val;
2992a35d391SEmmanuel Vadot 
3002a35d391SEmmanuel Vadot 	crc = ether_crc32_le(LLADDR(sdl), ETHER_ADDR_LEN);
3012a35d391SEmmanuel Vadot 	/* Take lower 8 bits and reverse it */
3022a35d391SEmmanuel Vadot 	val = bitreverse(~crc & 0xff);
3032a35d391SEmmanuel Vadot 	if (ctx->sc->mactype != DWC_GMAC_EXT_DESC)
3042a35d391SEmmanuel Vadot 		val >>= 2; /* Only need lower 6 bits */
3052a35d391SEmmanuel Vadot 	hashreg = (val >> 5);
3062a35d391SEmmanuel Vadot 	hashbit = (val & 31);
3072a35d391SEmmanuel Vadot 	ctx->hash[hashreg] |= (1 << hashbit);
3082a35d391SEmmanuel Vadot 
3092a35d391SEmmanuel Vadot 	return (1);
3102a35d391SEmmanuel Vadot }
3112a35d391SEmmanuel Vadot 
3122a35d391SEmmanuel Vadot static void
3132a35d391SEmmanuel Vadot dwc_setup_rxfilter(struct dwc_softc *sc)
3142a35d391SEmmanuel Vadot {
3152a35d391SEmmanuel Vadot 	struct dwc_hash_maddr_ctx ctx;
316ca018790SMitchell Horne 	if_t ifp;
3172a35d391SEmmanuel Vadot 	uint8_t *eaddr;
3182a35d391SEmmanuel Vadot 	uint32_t ffval, hi, lo;
3192a35d391SEmmanuel Vadot 	int nhash, i;
3202a35d391SEmmanuel Vadot 
3212a35d391SEmmanuel Vadot 	DWC_ASSERT_LOCKED(sc);
3222a35d391SEmmanuel Vadot 
3232a35d391SEmmanuel Vadot 	ifp = sc->ifp;
3242a35d391SEmmanuel Vadot 	nhash = sc->mactype != DWC_GMAC_EXT_DESC ? 2 : 8;
3252a35d391SEmmanuel Vadot 
3262a35d391SEmmanuel Vadot 	/*
3272a35d391SEmmanuel Vadot 	 * Set the multicast (group) filter hash.
3282a35d391SEmmanuel Vadot 	 */
329ca018790SMitchell Horne 	if ((if_getflags(ifp) & IFF_ALLMULTI) != 0) {
3302a35d391SEmmanuel Vadot 		ffval = (FRAME_FILTER_PM);
3312a35d391SEmmanuel Vadot 		for (i = 0; i < nhash; i++)
3322a35d391SEmmanuel Vadot 			ctx.hash[i] = ~0;
3332a35d391SEmmanuel Vadot 	} else {
3342a35d391SEmmanuel Vadot 		ffval = (FRAME_FILTER_HMC);
3352a35d391SEmmanuel Vadot 		for (i = 0; i < nhash; i++)
3362a35d391SEmmanuel Vadot 			ctx.hash[i] = 0;
3372a35d391SEmmanuel Vadot 		ctx.sc = sc;
3382a35d391SEmmanuel Vadot 		if_foreach_llmaddr(ifp, dwc_hash_maddr, &ctx);
3392a35d391SEmmanuel Vadot 	}
3402a35d391SEmmanuel Vadot 
3412a35d391SEmmanuel Vadot 	/*
3422a35d391SEmmanuel Vadot 	 * Set the individual address filter hash.
3432a35d391SEmmanuel Vadot 	 */
344ca018790SMitchell Horne 	if ((if_getflags(ifp) & IFF_PROMISC) != 0)
3452a35d391SEmmanuel Vadot 		ffval |= (FRAME_FILTER_PR);
3462a35d391SEmmanuel Vadot 
3472a35d391SEmmanuel Vadot 	/*
3482a35d391SEmmanuel Vadot 	 * Set the primary address.
3492a35d391SEmmanuel Vadot 	 */
350068f2c0eSJustin Hibbits 	eaddr = if_getlladdr(ifp);
3512a35d391SEmmanuel Vadot 	lo = eaddr[0] | (eaddr[1] << 8) | (eaddr[2] << 16) |
3522a35d391SEmmanuel Vadot 	    (eaddr[3] << 24);
3532a35d391SEmmanuel Vadot 	hi = eaddr[4] | (eaddr[5] << 8);
3542a35d391SEmmanuel Vadot 	WRITE4(sc, MAC_ADDRESS_LOW(0), lo);
3552a35d391SEmmanuel Vadot 	WRITE4(sc, MAC_ADDRESS_HIGH(0), hi);
3562a35d391SEmmanuel Vadot 	WRITE4(sc, MAC_FRAME_FILTER, ffval);
3572a35d391SEmmanuel Vadot 	if (sc->mactype != DWC_GMAC_EXT_DESC) {
3582a35d391SEmmanuel Vadot 		WRITE4(sc, GMAC_MAC_HTLOW, ctx.hash[0]);
3592a35d391SEmmanuel Vadot 		WRITE4(sc, GMAC_MAC_HTHIGH, ctx.hash[1]);
3602a35d391SEmmanuel Vadot 	} else {
3612a35d391SEmmanuel Vadot 		for (i = 0; i < nhash; i++)
3622a35d391SEmmanuel Vadot 			WRITE4(sc, HASH_TABLE_REG(i), ctx.hash[i]);
3632a35d391SEmmanuel Vadot 	}
3642a35d391SEmmanuel Vadot }
3652a35d391SEmmanuel Vadot 
3662a35d391SEmmanuel Vadot static void
3672a35d391SEmmanuel Vadot dwc_setup_core(struct dwc_softc *sc)
3682a35d391SEmmanuel Vadot {
3692a35d391SEmmanuel Vadot 	uint32_t reg;
3702a35d391SEmmanuel Vadot 
3712a35d391SEmmanuel Vadot 	DWC_ASSERT_LOCKED(sc);
3722a35d391SEmmanuel Vadot 
3732a35d391SEmmanuel Vadot 	/* Enable core */
3742a35d391SEmmanuel Vadot 	reg = READ4(sc, MAC_CONFIGURATION);
3752a35d391SEmmanuel Vadot 	reg |= (CONF_JD | CONF_ACS | CONF_BE);
3762a35d391SEmmanuel Vadot 	WRITE4(sc, MAC_CONFIGURATION, reg);
3772a35d391SEmmanuel Vadot }
3782a35d391SEmmanuel Vadot 
3792a35d391SEmmanuel Vadot static void
3802a35d391SEmmanuel Vadot dwc_enable_mac(struct dwc_softc *sc, bool enable)
3812a35d391SEmmanuel Vadot {
3822a35d391SEmmanuel Vadot 	uint32_t reg;
3832a35d391SEmmanuel Vadot 
3842a35d391SEmmanuel Vadot 	DWC_ASSERT_LOCKED(sc);
3852a35d391SEmmanuel Vadot 	reg = READ4(sc, MAC_CONFIGURATION);
3862a35d391SEmmanuel Vadot 	if (enable)
3872a35d391SEmmanuel Vadot 		reg |= CONF_TE | CONF_RE;
3882a35d391SEmmanuel Vadot 	else
3892a35d391SEmmanuel Vadot 		reg &= ~(CONF_TE | CONF_RE);
3902a35d391SEmmanuel Vadot 	WRITE4(sc, MAC_CONFIGURATION, reg);
3912a35d391SEmmanuel Vadot }
3922a35d391SEmmanuel Vadot 
3932a35d391SEmmanuel Vadot static void
39435c9edabSJiahao Li dwc_enable_csum_offload(struct dwc_softc *sc)
39535c9edabSJiahao Li {
39635c9edabSJiahao Li 	uint32_t reg;
39735c9edabSJiahao Li 
39835c9edabSJiahao Li 	DWC_ASSERT_LOCKED(sc);
39935c9edabSJiahao Li 	reg = READ4(sc, MAC_CONFIGURATION);
40035c9edabSJiahao Li 	if ((if_getcapenable(sc->ifp) & IFCAP_RXCSUM) != 0)
40135c9edabSJiahao Li 		reg |= CONF_IPC;
40235c9edabSJiahao Li 	else
40335c9edabSJiahao Li 		reg &= ~CONF_IPC;
40435c9edabSJiahao Li 	WRITE4(sc, MAC_CONFIGURATION, reg);
40535c9edabSJiahao Li }
40635c9edabSJiahao Li 
40735c9edabSJiahao Li static void
4082a35d391SEmmanuel Vadot dwc_get_hwaddr(struct dwc_softc *sc, uint8_t *hwaddr)
4092a35d391SEmmanuel Vadot {
4102a35d391SEmmanuel Vadot 	uint32_t hi, lo, rnd;
4112a35d391SEmmanuel Vadot 
4122a35d391SEmmanuel Vadot 	/*
4132a35d391SEmmanuel Vadot 	 * Try to recover a MAC address from the running hardware. If there's
4142a35d391SEmmanuel Vadot 	 * something non-zero there, assume the bootloader did the right thing
4152a35d391SEmmanuel Vadot 	 * and just use it.
4162a35d391SEmmanuel Vadot 	 *
4172a35d391SEmmanuel Vadot 	 * Otherwise, set the address to a convenient locally assigned address,
4182a35d391SEmmanuel Vadot 	 * 'bsd' + random 24 low-order bits.  'b' is 0x62, which has the locally
4192a35d391SEmmanuel Vadot 	 * assigned bit set, and the broadcast/multicast bit clear.
4202a35d391SEmmanuel Vadot 	 */
4212a35d391SEmmanuel Vadot 	lo = READ4(sc, MAC_ADDRESS_LOW(0));
4222a35d391SEmmanuel Vadot 	hi = READ4(sc, MAC_ADDRESS_HIGH(0)) & 0xffff;
4232a35d391SEmmanuel Vadot 	if ((lo != 0xffffffff) || (hi != 0xffff)) {
4242a35d391SEmmanuel Vadot 		hwaddr[0] = (lo >>  0) & 0xff;
4252a35d391SEmmanuel Vadot 		hwaddr[1] = (lo >>  8) & 0xff;
4262a35d391SEmmanuel Vadot 		hwaddr[2] = (lo >> 16) & 0xff;
4272a35d391SEmmanuel Vadot 		hwaddr[3] = (lo >> 24) & 0xff;
4282a35d391SEmmanuel Vadot 		hwaddr[4] = (hi >>  0) & 0xff;
4292a35d391SEmmanuel Vadot 		hwaddr[5] = (hi >>  8) & 0xff;
4302a35d391SEmmanuel Vadot 	} else {
4312a35d391SEmmanuel Vadot 		rnd = arc4random() & 0x00ffffff;
4322a35d391SEmmanuel Vadot 		hwaddr[0] = 'b';
4332a35d391SEmmanuel Vadot 		hwaddr[1] = 's';
4342a35d391SEmmanuel Vadot 		hwaddr[2] = 'd';
4352a35d391SEmmanuel Vadot 		hwaddr[3] = rnd >> 16;
4362a35d391SEmmanuel Vadot 		hwaddr[4] = rnd >>  8;
4372a35d391SEmmanuel Vadot 		hwaddr[5] = rnd >>  0;
4382a35d391SEmmanuel Vadot 	}
4392a35d391SEmmanuel Vadot }
4402a35d391SEmmanuel Vadot 
4412a35d391SEmmanuel Vadot /*
4422a35d391SEmmanuel Vadot  * if_ functions
4432a35d391SEmmanuel Vadot  */
4442a35d391SEmmanuel Vadot 
445f88e0af6SEmmanuel Vadot static void
4462a35d391SEmmanuel Vadot dwc_txstart_locked(struct dwc_softc *sc)
4475d43fd68SRuslan Bukin {
448ca018790SMitchell Horne 	if_t ifp;
4492a35d391SEmmanuel Vadot 	struct mbuf *m;
4502a35d391SEmmanuel Vadot 	int enqueued;
4512a35d391SEmmanuel Vadot 
4522a35d391SEmmanuel Vadot 	DWC_ASSERT_LOCKED(sc);
4532a35d391SEmmanuel Vadot 
4542a35d391SEmmanuel Vadot 	if (!sc->link_is_up)
4552a35d391SEmmanuel Vadot 		return;
4562a35d391SEmmanuel Vadot 
4572a35d391SEmmanuel Vadot 	ifp = sc->ifp;
4582a35d391SEmmanuel Vadot 
4593bbd11eeSEmmanuel Vadot 	if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) !=
4603bbd11eeSEmmanuel Vadot 	    IFF_DRV_RUNNING)
4612a35d391SEmmanuel Vadot 		return;
4622a35d391SEmmanuel Vadot 
4632a35d391SEmmanuel Vadot 	enqueued = 0;
4642a35d391SEmmanuel Vadot 
4652a35d391SEmmanuel Vadot 	for (;;) {
466e5232621SOleksandr Tymoshenko 		if (sc->tx_desccount > (TX_DESC_COUNT - TX_MAP_MAX_SEGS  + 1)) {
467e5232621SOleksandr Tymoshenko 			if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
468e5232621SOleksandr Tymoshenko 			break;
469e5232621SOleksandr Tymoshenko 		}
470e5232621SOleksandr Tymoshenko 
471e5232621SOleksandr Tymoshenko 		if (sc->tx_mapcount == (TX_MAP_COUNT - 1)) {
4723bbd11eeSEmmanuel Vadot 			if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
4732a35d391SEmmanuel Vadot 			break;
4742a35d391SEmmanuel Vadot 		}
4752a35d391SEmmanuel Vadot 
4763bbd11eeSEmmanuel Vadot 		m = if_dequeue(ifp);
4772a35d391SEmmanuel Vadot 		if (m == NULL)
4782a35d391SEmmanuel Vadot 			break;
479*972adf0fSEmmanuel Vadot 		if (dma1000_setup_txbuf(sc, sc->tx_map_head, &m) != 0) {
4803bbd11eeSEmmanuel Vadot 			if_sendq_prepend(ifp, m);
481e5232621SOleksandr Tymoshenko 			if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
4822a35d391SEmmanuel Vadot 			break;
4832a35d391SEmmanuel Vadot 		}
4842a371643SJustin Hibbits 		bpf_mtap_if(ifp, m);
485e5232621SOleksandr Tymoshenko 		sc->tx_map_head = next_txidx(sc, sc->tx_map_head);
486e5232621SOleksandr Tymoshenko 		sc->tx_mapcount++;
4872a35d391SEmmanuel Vadot 		++enqueued;
4882a35d391SEmmanuel Vadot 	}
4892a35d391SEmmanuel Vadot 
4902a35d391SEmmanuel Vadot 	if (enqueued != 0) {
4912a35d391SEmmanuel Vadot 		WRITE4(sc, TRANSMIT_POLL_DEMAND, 0x1);
4922a35d391SEmmanuel Vadot 		sc->tx_watchdog_count = WATCHDOG_TIMEOUT_SECS;
4932a35d391SEmmanuel Vadot 	}
4942a35d391SEmmanuel Vadot }
4952a35d391SEmmanuel Vadot 
4962a35d391SEmmanuel Vadot static void
497ca018790SMitchell Horne dwc_txstart(if_t ifp)
4982a35d391SEmmanuel Vadot {
499ca018790SMitchell Horne 	struct dwc_softc *sc = if_getsoftc(ifp);
5002a35d391SEmmanuel Vadot 
5012a35d391SEmmanuel Vadot 	DWC_LOCK(sc);
5022a35d391SEmmanuel Vadot 	dwc_txstart_locked(sc);
5032a35d391SEmmanuel Vadot 	DWC_UNLOCK(sc);
5042a35d391SEmmanuel Vadot }
5052a35d391SEmmanuel Vadot 
5062a35d391SEmmanuel Vadot static void
5072a35d391SEmmanuel Vadot dwc_init_locked(struct dwc_softc *sc)
5082a35d391SEmmanuel Vadot {
509ca018790SMitchell Horne 	if_t ifp = sc->ifp;
5102a35d391SEmmanuel Vadot 
5112a35d391SEmmanuel Vadot 	DWC_ASSERT_LOCKED(sc);
5122a35d391SEmmanuel Vadot 
5133bbd11eeSEmmanuel Vadot 	if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
5142a35d391SEmmanuel Vadot 		return;
5152a35d391SEmmanuel Vadot 
5166501fcdcSJiahao LI 	/*
5176501fcdcSJiahao LI 	 * Call mii_mediachg() which will call back into dwc_miibus_statchg()
5186501fcdcSJiahao LI 	 * to set up the remaining config registers based on current media.
5196501fcdcSJiahao LI 	 */
5206501fcdcSJiahao LI 	mii_mediachg(sc->mii_softc);
5216501fcdcSJiahao LI 
5222a35d391SEmmanuel Vadot 	dwc_setup_rxfilter(sc);
5232a35d391SEmmanuel Vadot 	dwc_setup_core(sc);
5242a35d391SEmmanuel Vadot 	dwc_enable_mac(sc, true);
52535c9edabSJiahao Li 	dwc_enable_csum_offload(sc);
526*972adf0fSEmmanuel Vadot 	dma1000_start(sc);
5272a35d391SEmmanuel Vadot 
5282a35d391SEmmanuel Vadot 	if_setdrvflagbits(ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE);
5295d43fd68SRuslan Bukin 
5302a35d391SEmmanuel Vadot 	callout_reset(&sc->dwc_callout, hz, dwc_tick, sc);
5312a35d391SEmmanuel Vadot }
5322a35d391SEmmanuel Vadot 
5332a35d391SEmmanuel Vadot static void
5342a35d391SEmmanuel Vadot dwc_init(void *if_softc)
5352a35d391SEmmanuel Vadot {
5362a35d391SEmmanuel Vadot 	struct dwc_softc *sc = if_softc;
5372a35d391SEmmanuel Vadot 
5382a35d391SEmmanuel Vadot 	DWC_LOCK(sc);
5392a35d391SEmmanuel Vadot 	dwc_init_locked(sc);
5402a35d391SEmmanuel Vadot 	DWC_UNLOCK(sc);
5412a35d391SEmmanuel Vadot }
5422a35d391SEmmanuel Vadot 
5432a35d391SEmmanuel Vadot static void
5442a35d391SEmmanuel Vadot dwc_stop_locked(struct dwc_softc *sc)
5452a35d391SEmmanuel Vadot {
546ca018790SMitchell Horne 	if_t ifp;
5472a35d391SEmmanuel Vadot 
5482a35d391SEmmanuel Vadot 	DWC_ASSERT_LOCKED(sc);
5492a35d391SEmmanuel Vadot 
5502a35d391SEmmanuel Vadot 	ifp = sc->ifp;
5513bbd11eeSEmmanuel Vadot 	if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
5522a35d391SEmmanuel Vadot 	sc->tx_watchdog_count = 0;
5532a35d391SEmmanuel Vadot 	sc->stats_harvest_count = 0;
5542a35d391SEmmanuel Vadot 
5552a35d391SEmmanuel Vadot 	callout_stop(&sc->dwc_callout);
5562a35d391SEmmanuel Vadot 
557*972adf0fSEmmanuel Vadot 	dma1000_stop(sc);
5582a35d391SEmmanuel Vadot 	dwc_enable_mac(sc, false);
5592a35d391SEmmanuel Vadot }
5602a35d391SEmmanuel Vadot 
5612a35d391SEmmanuel Vadot static int
562ca018790SMitchell Horne dwc_ioctl(if_t ifp, u_long cmd, caddr_t data)
5632a35d391SEmmanuel Vadot {
5642a35d391SEmmanuel Vadot 	struct dwc_softc *sc;
5652a35d391SEmmanuel Vadot 	struct mii_data *mii;
5662a35d391SEmmanuel Vadot 	struct ifreq *ifr;
5673bbd11eeSEmmanuel Vadot 	int flags, mask, error;
5682a35d391SEmmanuel Vadot 
569ca018790SMitchell Horne 	sc = if_getsoftc(ifp);
5702a35d391SEmmanuel Vadot 	ifr = (struct ifreq *)data;
5712a35d391SEmmanuel Vadot 
5722a35d391SEmmanuel Vadot 	error = 0;
5732a35d391SEmmanuel Vadot 	switch (cmd) {
5742a35d391SEmmanuel Vadot 	case SIOCSIFFLAGS:
5752a35d391SEmmanuel Vadot 		DWC_LOCK(sc);
5763bbd11eeSEmmanuel Vadot 		if (if_getflags(ifp) & IFF_UP) {
5773bbd11eeSEmmanuel Vadot 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
5783bbd11eeSEmmanuel Vadot 				flags = if_getflags(ifp) ^ sc->if_flags;
5793bbd11eeSEmmanuel Vadot 				if ((flags & (IFF_PROMISC|IFF_ALLMULTI)) != 0)
5802a35d391SEmmanuel Vadot 					dwc_setup_rxfilter(sc);
5815d43fd68SRuslan Bukin 			} else {
5822a35d391SEmmanuel Vadot 				if (!sc->is_detaching)
5832a35d391SEmmanuel Vadot 					dwc_init_locked(sc);
5842a35d391SEmmanuel Vadot 			}
5852a35d391SEmmanuel Vadot 		} else {
5863bbd11eeSEmmanuel Vadot 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
5872a35d391SEmmanuel Vadot 				dwc_stop_locked(sc);
5882a35d391SEmmanuel Vadot 		}
5893bbd11eeSEmmanuel Vadot 		sc->if_flags = if_getflags(ifp);
5902a35d391SEmmanuel Vadot 		DWC_UNLOCK(sc);
5912a35d391SEmmanuel Vadot 		break;
5922a35d391SEmmanuel Vadot 	case SIOCADDMULTI:
5932a35d391SEmmanuel Vadot 	case SIOCDELMULTI:
5943bbd11eeSEmmanuel Vadot 		if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
5952a35d391SEmmanuel Vadot 			DWC_LOCK(sc);
5962a35d391SEmmanuel Vadot 			dwc_setup_rxfilter(sc);
5972a35d391SEmmanuel Vadot 			DWC_UNLOCK(sc);
5982a35d391SEmmanuel Vadot 		}
5992a35d391SEmmanuel Vadot 		break;
6002a35d391SEmmanuel Vadot 	case SIOCSIFMEDIA:
6012a35d391SEmmanuel Vadot 	case SIOCGIFMEDIA:
6022a35d391SEmmanuel Vadot 		mii = sc->mii_softc;
6032a35d391SEmmanuel Vadot 		error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
6042a35d391SEmmanuel Vadot 		break;
6052a35d391SEmmanuel Vadot 	case SIOCSIFCAP:
6063bbd11eeSEmmanuel Vadot 		mask = ifr->ifr_reqcap ^ if_getcapenable(ifp);
6072a35d391SEmmanuel Vadot 		if (mask & IFCAP_VLAN_MTU) {
6082a35d391SEmmanuel Vadot 			/* No work to do except acknowledge the change took */
6093bbd11eeSEmmanuel Vadot 			if_togglecapenable(ifp, IFCAP_VLAN_MTU);
6102a35d391SEmmanuel Vadot 		}
61198ea5a7bSEmmanuel Vadot 		if (mask & IFCAP_RXCSUM)
61298ea5a7bSEmmanuel Vadot 			if_togglecapenable(ifp, IFCAP_RXCSUM);
61398ea5a7bSEmmanuel Vadot 		if (mask & IFCAP_TXCSUM)
61498ea5a7bSEmmanuel Vadot 			if_togglecapenable(ifp, IFCAP_TXCSUM);
61598ea5a7bSEmmanuel Vadot 		if ((if_getcapenable(ifp) & IFCAP_TXCSUM) != 0)
61698ea5a7bSEmmanuel Vadot 			if_sethwassistbits(ifp, CSUM_IP | CSUM_UDP | CSUM_TCP, 0);
61798ea5a7bSEmmanuel Vadot 		else
61898ea5a7bSEmmanuel Vadot 			if_sethwassistbits(ifp, 0, CSUM_IP | CSUM_UDP | CSUM_TCP);
61935c9edabSJiahao Li 
62035c9edabSJiahao Li 		if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
62135c9edabSJiahao Li 			DWC_LOCK(sc);
62235c9edabSJiahao Li 			dwc_enable_csum_offload(sc);
62335c9edabSJiahao Li 			DWC_UNLOCK(sc);
62435c9edabSJiahao Li 		}
6252a35d391SEmmanuel Vadot 		break;
6262a35d391SEmmanuel Vadot 
6272a35d391SEmmanuel Vadot 	default:
6282a35d391SEmmanuel Vadot 		error = ether_ioctl(ifp, cmd, data);
6292a35d391SEmmanuel Vadot 		break;
6302a35d391SEmmanuel Vadot 	}
6312a35d391SEmmanuel Vadot 
6322a35d391SEmmanuel Vadot 	return (error);
6332a35d391SEmmanuel Vadot }
6342a35d391SEmmanuel Vadot 
6352a35d391SEmmanuel Vadot /*
6362a35d391SEmmanuel Vadot  * Interrupts functions
6372a35d391SEmmanuel Vadot  */
6382a35d391SEmmanuel Vadot 
6392a35d391SEmmanuel Vadot 
6402a35d391SEmmanuel Vadot static void
6412a35d391SEmmanuel Vadot dwc_intr(void *arg)
6422a35d391SEmmanuel Vadot {
6432a35d391SEmmanuel Vadot 	struct dwc_softc *sc;
6442a35d391SEmmanuel Vadot 	uint32_t reg;
6452a35d391SEmmanuel Vadot 
6462a35d391SEmmanuel Vadot 	sc = arg;
6472a35d391SEmmanuel Vadot 
6482a35d391SEmmanuel Vadot 	DWC_LOCK(sc);
6492a35d391SEmmanuel Vadot 
6502a35d391SEmmanuel Vadot 	reg = READ4(sc, INTERRUPT_STATUS);
6512a35d391SEmmanuel Vadot 	if (reg)
6522a35d391SEmmanuel Vadot 		READ4(sc, SGMII_RGMII_SMII_CTRL_STATUS);
6532a35d391SEmmanuel Vadot 
6542a35d391SEmmanuel Vadot 	reg = READ4(sc, DMA_STATUS);
6552a35d391SEmmanuel Vadot 	if (reg & DMA_STATUS_NIS) {
6562a35d391SEmmanuel Vadot 		if (reg & DMA_STATUS_RI)
657*972adf0fSEmmanuel Vadot 			dma1000_rxfinish_locked(sc);
6582a35d391SEmmanuel Vadot 
6592a35d391SEmmanuel Vadot 		if (reg & DMA_STATUS_TI) {
660*972adf0fSEmmanuel Vadot 			dma1000_txfinish_locked(sc);
6612a35d391SEmmanuel Vadot 			dwc_txstart_locked(sc);
6622a35d391SEmmanuel Vadot 		}
6632a35d391SEmmanuel Vadot 	}
6642a35d391SEmmanuel Vadot 
6652a35d391SEmmanuel Vadot 	if (reg & DMA_STATUS_AIS) {
6662a35d391SEmmanuel Vadot 		if (reg & DMA_STATUS_FBI) {
6672a35d391SEmmanuel Vadot 			/* Fatal bus error */
6682a35d391SEmmanuel Vadot 			device_printf(sc->dev,
6692a35d391SEmmanuel Vadot 			    "Ethernet DMA error, restarting controller.\n");
6702a35d391SEmmanuel Vadot 			dwc_stop_locked(sc);
6712a35d391SEmmanuel Vadot 			dwc_init_locked(sc);
6722a35d391SEmmanuel Vadot 		}
6732a35d391SEmmanuel Vadot 	}
6742a35d391SEmmanuel Vadot 
6752a35d391SEmmanuel Vadot 	WRITE4(sc, DMA_STATUS, reg & DMA_STATUS_INTR_MASK);
6762a35d391SEmmanuel Vadot 	DWC_UNLOCK(sc);
6772a35d391SEmmanuel Vadot }
6782a35d391SEmmanuel Vadot 
6792a35d391SEmmanuel Vadot /*
6802a35d391SEmmanuel Vadot  * Stats
6812a35d391SEmmanuel Vadot  */
6822a35d391SEmmanuel Vadot 
6832a35d391SEmmanuel Vadot static void dwc_clear_stats(struct dwc_softc *sc)
6842a35d391SEmmanuel Vadot {
6852a35d391SEmmanuel Vadot 	uint32_t reg;
6862a35d391SEmmanuel Vadot 
6872a35d391SEmmanuel Vadot 	reg = READ4(sc, MMC_CONTROL);
6882a35d391SEmmanuel Vadot 	reg |= (MMC_CONTROL_CNTRST);
6892a35d391SEmmanuel Vadot 	WRITE4(sc, MMC_CONTROL, reg);
6902a35d391SEmmanuel Vadot }
6912a35d391SEmmanuel Vadot 
6922a35d391SEmmanuel Vadot static void
6932a35d391SEmmanuel Vadot dwc_harvest_stats(struct dwc_softc *sc)
6942a35d391SEmmanuel Vadot {
695ca018790SMitchell Horne 	if_t ifp;
6962a35d391SEmmanuel Vadot 
6972a35d391SEmmanuel Vadot 	/* We don't need to harvest too often. */
6982a35d391SEmmanuel Vadot 	if (++sc->stats_harvest_count < STATS_HARVEST_INTERVAL)
6992a35d391SEmmanuel Vadot 		return;
7002a35d391SEmmanuel Vadot 
7012a35d391SEmmanuel Vadot 	sc->stats_harvest_count = 0;
7022a35d391SEmmanuel Vadot 	ifp = sc->ifp;
7032a35d391SEmmanuel Vadot 
7042a35d391SEmmanuel Vadot 	if_inc_counter(ifp, IFCOUNTER_IERRORS,
7052a35d391SEmmanuel Vadot 	    READ4(sc, RXOVERSIZE_G) + READ4(sc, RXUNDERSIZE_G) +
7062a35d391SEmmanuel Vadot 	    READ4(sc, RXCRCERROR) + READ4(sc, RXALIGNMENTERROR) +
7072a35d391SEmmanuel Vadot 	    READ4(sc, RXRUNTERROR) + READ4(sc, RXJABBERERROR) +
7082a35d391SEmmanuel Vadot 	    READ4(sc, RXLENGTHERROR));
7092a35d391SEmmanuel Vadot 
7102a35d391SEmmanuel Vadot 	if_inc_counter(ifp, IFCOUNTER_OERRORS,
7112a35d391SEmmanuel Vadot 	    READ4(sc, TXOVERSIZE_G) + READ4(sc, TXEXCESSDEF) +
7122a35d391SEmmanuel Vadot 	    READ4(sc, TXCARRIERERR) + READ4(sc, TXUNDERFLOWERROR));
7132a35d391SEmmanuel Vadot 
7142a35d391SEmmanuel Vadot 	if_inc_counter(ifp, IFCOUNTER_COLLISIONS,
7152a35d391SEmmanuel Vadot 	    READ4(sc, TXEXESSCOL) + READ4(sc, TXLATECOL));
7162a35d391SEmmanuel Vadot 
7172a35d391SEmmanuel Vadot 	dwc_clear_stats(sc);
7182a35d391SEmmanuel Vadot }
7192a35d391SEmmanuel Vadot 
7202a35d391SEmmanuel Vadot static void
7212a35d391SEmmanuel Vadot dwc_tick(void *arg)
7222a35d391SEmmanuel Vadot {
7232a35d391SEmmanuel Vadot 	struct dwc_softc *sc;
724ca018790SMitchell Horne 	if_t ifp;
7252a35d391SEmmanuel Vadot 	int link_was_up;
7262a35d391SEmmanuel Vadot 
7272a35d391SEmmanuel Vadot 	sc = arg;
7282a35d391SEmmanuel Vadot 
7292a35d391SEmmanuel Vadot 	DWC_ASSERT_LOCKED(sc);
7302a35d391SEmmanuel Vadot 
7312a35d391SEmmanuel Vadot 	ifp = sc->ifp;
7322a35d391SEmmanuel Vadot 
7333bbd11eeSEmmanuel Vadot 	if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
7342a35d391SEmmanuel Vadot 	    return;
7352a35d391SEmmanuel Vadot 
7362a35d391SEmmanuel Vadot 	/*
7372a35d391SEmmanuel Vadot 	 * Typical tx watchdog.  If this fires it indicates that we enqueued
7382a35d391SEmmanuel Vadot 	 * packets for output and never got a txdone interrupt for them.  Maybe
7392a35d391SEmmanuel Vadot 	 * it's a missed interrupt somehow, just pretend we got one.
7402a35d391SEmmanuel Vadot 	 */
7412a35d391SEmmanuel Vadot 	if (sc->tx_watchdog_count > 0) {
7422a35d391SEmmanuel Vadot 		if (--sc->tx_watchdog_count == 0) {
743*972adf0fSEmmanuel Vadot 			dma1000_txfinish_locked(sc);
7442a35d391SEmmanuel Vadot 		}
7452a35d391SEmmanuel Vadot 	}
7462a35d391SEmmanuel Vadot 
7472a35d391SEmmanuel Vadot 	/* Gather stats from hardware counters. */
7482a35d391SEmmanuel Vadot 	dwc_harvest_stats(sc);
7492a35d391SEmmanuel Vadot 
7502a35d391SEmmanuel Vadot 	/* Check the media status. */
7512a35d391SEmmanuel Vadot 	link_was_up = sc->link_is_up;
7522a35d391SEmmanuel Vadot 	mii_tick(sc->mii_softc);
7532a35d391SEmmanuel Vadot 	if (sc->link_is_up && !link_was_up)
7542a35d391SEmmanuel Vadot 		dwc_txstart_locked(sc);
7552a35d391SEmmanuel Vadot 
7562a35d391SEmmanuel Vadot 	/* Schedule another check one second from now. */
7572a35d391SEmmanuel Vadot 	callout_reset(&sc->dwc_callout, hz, dwc_tick, sc);
7582a35d391SEmmanuel Vadot }
7592a35d391SEmmanuel Vadot 
760d7acb49aSJared McNeill static int
761f114aaddSEmmanuel Vadot dwc_reset_phy(struct dwc_softc *sc)
762d7acb49aSJared McNeill {
763d7acb49aSJared McNeill 	pcell_t gpio_prop[4];
764d7acb49aSJared McNeill 	pcell_t delay_prop[3];
765f114aaddSEmmanuel Vadot 	phandle_t gpio_node;
766d7acb49aSJared McNeill 	device_t gpio;
767d7acb49aSJared McNeill 	uint32_t pin, flags;
768d7acb49aSJared McNeill 	uint32_t pin_value;
769d7acb49aSJared McNeill 
770f114aaddSEmmanuel Vadot 	/*
771f114aaddSEmmanuel Vadot 	 * All those properties are deprecated but still used in some DTS.
772f114aaddSEmmanuel Vadot 	 * The new way to deal with this is to use the generic bindings
773f114aaddSEmmanuel Vadot 	 * present in the ethernet-phy node.
774f114aaddSEmmanuel Vadot 	 */
775f114aaddSEmmanuel Vadot 	if (OF_getencprop(sc->node, "snps,reset-gpio",
776d7acb49aSJared McNeill 	    gpio_prop, sizeof(gpio_prop)) <= 0)
777d7acb49aSJared McNeill 		return (0);
778d7acb49aSJared McNeill 
779f114aaddSEmmanuel Vadot 	if (OF_getencprop(sc->node, "snps,reset-delays-us",
780d7acb49aSJared McNeill 	    delay_prop, sizeof(delay_prop)) <= 0) {
781f114aaddSEmmanuel Vadot 		device_printf(sc->dev,
782d7acb49aSJared McNeill 		    "Wrong property for snps,reset-delays-us");
783d7acb49aSJared McNeill 		return (ENXIO);
784d7acb49aSJared McNeill 	}
785d7acb49aSJared McNeill 
786d7acb49aSJared McNeill 	gpio_node = OF_node_from_xref(gpio_prop[0]);
787d7acb49aSJared McNeill 	if ((gpio = OF_device_from_xref(gpio_prop[0])) == NULL) {
788f114aaddSEmmanuel Vadot 		device_printf(sc->dev,
789d7acb49aSJared McNeill 		    "Can't find gpio controller for phy reset\n");
790d7acb49aSJared McNeill 		return (ENXIO);
791d7acb49aSJared McNeill 	}
792d7acb49aSJared McNeill 
793f114aaddSEmmanuel Vadot 	if (GPIO_MAP_GPIOS(gpio, sc->node, gpio_node,
79473a1170aSPedro F. Giffuni 	    nitems(gpio_prop) - 1,
795d7acb49aSJared McNeill 	    gpio_prop + 1, &pin, &flags) != 0) {
796f114aaddSEmmanuel Vadot 		device_printf(sc->dev, "Can't map gpio for phy reset\n");
797d7acb49aSJared McNeill 		return (ENXIO);
798d7acb49aSJared McNeill 	}
799d7acb49aSJared McNeill 
800d7acb49aSJared McNeill 	pin_value = GPIO_PIN_LOW;
801f114aaddSEmmanuel Vadot 	if (OF_hasprop(sc->node, "snps,reset-active-low"))
802d7acb49aSJared McNeill 		pin_value = GPIO_PIN_HIGH;
803d7acb49aSJared McNeill 
804d7acb49aSJared McNeill 	GPIO_PIN_SETFLAGS(gpio, pin, GPIO_PIN_OUTPUT);
805d7acb49aSJared McNeill 	GPIO_PIN_SET(gpio, pin, pin_value);
806c069412eSEmmanuel Vadot 	DELAY(delay_prop[0] * 5);
807d7acb49aSJared McNeill 	GPIO_PIN_SET(gpio, pin, !pin_value);
808c069412eSEmmanuel Vadot 	DELAY(delay_prop[1] * 5);
809d7acb49aSJared McNeill 	GPIO_PIN_SET(gpio, pin, pin_value);
810c069412eSEmmanuel Vadot 	DELAY(delay_prop[2] * 5);
811d7acb49aSJared McNeill 
812d7acb49aSJared McNeill 	return (0);
813d7acb49aSJared McNeill }
814d7acb49aSJared McNeill 
8156a05f063SJared McNeill static int
81650059a60SEmmanuel Vadot dwc_clock_init(struct dwc_softc *sc)
8176a05f063SJared McNeill {
81850059a60SEmmanuel Vadot 	int rv;
819824cfb47SOleksandr Tymoshenko 	int64_t freq;
8206a05f063SJared McNeill 
82150059a60SEmmanuel Vadot 	/* Required clock */
82250059a60SEmmanuel Vadot 	rv = clk_get_by_ofw_name(sc->dev, 0, "stmmaceth", &sc->clk_stmmaceth);
82350059a60SEmmanuel Vadot 	if (rv != 0) {
82450059a60SEmmanuel Vadot 		device_printf(sc->dev, "Cannot get GMAC main clock\n");
82550059a60SEmmanuel Vadot 		return (ENXIO);
8266a05f063SJared McNeill 	}
82750059a60SEmmanuel Vadot 	if ((rv = clk_enable(sc->clk_stmmaceth)) != 0) {
82850059a60SEmmanuel Vadot 		device_printf(sc->dev, "could not enable main clock\n");
82950059a60SEmmanuel Vadot 		return (rv);
8306a05f063SJared McNeill 	}
8316a05f063SJared McNeill 
83250059a60SEmmanuel Vadot 	/* Optional clock */
83350059a60SEmmanuel Vadot 	rv = clk_get_by_ofw_name(sc->dev, 0, "pclk", &sc->clk_pclk);
83450059a60SEmmanuel Vadot 	if (rv != 0)
83550059a60SEmmanuel Vadot 		return (0);
83650059a60SEmmanuel Vadot 	if ((rv = clk_enable(sc->clk_pclk)) != 0) {
83750059a60SEmmanuel Vadot 		device_printf(sc->dev, "could not enable peripheral clock\n");
83850059a60SEmmanuel Vadot 		return (rv);
8396a05f063SJared McNeill 	}
84050059a60SEmmanuel Vadot 
84150059a60SEmmanuel Vadot 	if (bootverbose) {
84250059a60SEmmanuel Vadot 		clk_get_freq(sc->clk_stmmaceth, &freq);
84350059a60SEmmanuel Vadot 		device_printf(sc->dev, "MAC clock(%s) freq: %jd\n",
84450059a60SEmmanuel Vadot 		    clk_get_name(sc->clk_stmmaceth), (intmax_t)freq);
84550059a60SEmmanuel Vadot 	}
84650059a60SEmmanuel Vadot 
84750059a60SEmmanuel Vadot 	return (0);
84850059a60SEmmanuel Vadot }
84950059a60SEmmanuel Vadot 
85050059a60SEmmanuel Vadot static int
85150059a60SEmmanuel Vadot dwc_reset_deassert(struct dwc_softc *sc)
85250059a60SEmmanuel Vadot {
85350059a60SEmmanuel Vadot 	int rv;
85450059a60SEmmanuel Vadot 
85550059a60SEmmanuel Vadot 	/* Required reset */
85650059a60SEmmanuel Vadot 	rv = hwreset_get_by_ofw_name(sc->dev, 0, "stmmaceth", &sc->rst_stmmaceth);
85750059a60SEmmanuel Vadot 	if (rv != 0) {
85850059a60SEmmanuel Vadot 		device_printf(sc->dev, "Cannot get GMAC reset\n");
85950059a60SEmmanuel Vadot 		return (ENXIO);
86050059a60SEmmanuel Vadot 	}
86150059a60SEmmanuel Vadot 	rv = hwreset_deassert(sc->rst_stmmaceth);
86250059a60SEmmanuel Vadot 	if (rv != 0) {
86350059a60SEmmanuel Vadot 		device_printf(sc->dev, "could not de-assert GMAC reset\n");
86450059a60SEmmanuel Vadot 		return (rv);
86550059a60SEmmanuel Vadot 	}
86650059a60SEmmanuel Vadot 
86750059a60SEmmanuel Vadot 	/* Optional reset */
86850059a60SEmmanuel Vadot 	rv = hwreset_get_by_ofw_name(sc->dev, 0, "ahb", &sc->rst_ahb);
86950059a60SEmmanuel Vadot 	if (rv != 0)
87050059a60SEmmanuel Vadot 		return (0);
87150059a60SEmmanuel Vadot 	rv = hwreset_deassert(sc->rst_ahb);
87250059a60SEmmanuel Vadot 	if (rv != 0) {
87350059a60SEmmanuel Vadot 		device_printf(sc->dev, "could not de-assert AHB reset\n");
87450059a60SEmmanuel Vadot 		return (rv);
8756a05f063SJared McNeill 	}
8766a05f063SJared McNeill 
8776a05f063SJared McNeill 	return (0);
8786a05f063SJared McNeill }
8796a05f063SJared McNeill 
88027f03f16SEmmanuel Vadot /*
88127f03f16SEmmanuel Vadot  * Probe/Attach functions
88227f03f16SEmmanuel Vadot  */
88327f03f16SEmmanuel Vadot 
8845d43fd68SRuslan Bukin static int
8855d43fd68SRuslan Bukin dwc_probe(device_t dev)
8865d43fd68SRuslan Bukin {
8875d43fd68SRuslan Bukin 
8885d43fd68SRuslan Bukin 	if (!ofw_bus_status_okay(dev))
8895d43fd68SRuslan Bukin 		return (ENXIO);
8905d43fd68SRuslan Bukin 
8915d43fd68SRuslan Bukin 	if (!ofw_bus_is_compatible(dev, "snps,dwmac"))
8925d43fd68SRuslan Bukin 		return (ENXIO);
8935d43fd68SRuslan Bukin 
8945d43fd68SRuslan Bukin 	device_set_desc(dev, "Gigabit Ethernet Controller");
8955d43fd68SRuslan Bukin 	return (BUS_PROBE_DEFAULT);
8965d43fd68SRuslan Bukin }
8975d43fd68SRuslan Bukin 
8985d43fd68SRuslan Bukin static int
8995d43fd68SRuslan Bukin dwc_attach(device_t dev)
9005d43fd68SRuslan Bukin {
9015d43fd68SRuslan Bukin 	uint8_t macaddr[ETHER_ADDR_LEN];
9025d43fd68SRuslan Bukin 	struct dwc_softc *sc;
903ca018790SMitchell Horne 	if_t ifp;
904ff0752c8SLuiz Otavio O Souza 	int error, i;
905ff0752c8SLuiz Otavio O Souza 	uint32_t reg;
9063e5cd548SEmmanuel Vadot 	uint32_t txpbl, rxpbl, pbl;
9075e38d9e4SEmmanuel Vadot 	bool nopblx8 = false;
9085e38d9e4SEmmanuel Vadot 	bool fixed_burst = false;
909f4bb6ea5SEmmanuel Vadot 	bool mixed_burst = false;
910b69c49d1SEmmanuel Vadot 	bool aal = false;
9115d43fd68SRuslan Bukin 
9125d43fd68SRuslan Bukin 	sc = device_get_softc(dev);
9135d43fd68SRuslan Bukin 	sc->dev = dev;
9145d43fd68SRuslan Bukin 	sc->rx_idx = 0;
915e5232621SOleksandr Tymoshenko 	sc->tx_desccount = TX_DESC_COUNT;
916e5232621SOleksandr Tymoshenko 	sc->tx_mapcount = 0;
9175df53927SLuiz Otavio O Souza 	sc->mii_clk = IF_DWC_MII_CLK(dev);
9185df53927SLuiz Otavio O Souza 	sc->mactype = IF_DWC_MAC_TYPE(dev);
9195df53927SLuiz Otavio O Souza 
920f114aaddSEmmanuel Vadot 	sc->node = ofw_bus_get_node(dev);
921f114aaddSEmmanuel Vadot 	switch (mii_fdt_get_contype(sc->node)) {
922f77d8d10SEmmanuel Vadot 	case MII_CONTYPE_RGMII:
923f77d8d10SEmmanuel Vadot 	case MII_CONTYPE_RGMII_ID:
924f77d8d10SEmmanuel Vadot 	case MII_CONTYPE_RGMII_RXID:
925f77d8d10SEmmanuel Vadot 	case MII_CONTYPE_RGMII_TXID:
926824cfb47SOleksandr Tymoshenko 		sc->phy_mode = PHY_MODE_RGMII;
927f77d8d10SEmmanuel Vadot 		break;
928f77d8d10SEmmanuel Vadot 	case MII_CONTYPE_RMII:
929824cfb47SOleksandr Tymoshenko 		sc->phy_mode = PHY_MODE_RMII;
930f77d8d10SEmmanuel Vadot 		break;
931da6252a6SEmmanuel Vadot 	case MII_CONTYPE_MII:
932da6252a6SEmmanuel Vadot 		sc->phy_mode = PHY_MODE_MII;
933da6252a6SEmmanuel Vadot 		break;
934f77d8d10SEmmanuel Vadot 	default:
935f77d8d10SEmmanuel Vadot 		device_printf(dev, "Unsupported MII type\n");
936f77d8d10SEmmanuel Vadot 		return (ENXIO);
937824cfb47SOleksandr Tymoshenko 	}
938824cfb47SOleksandr Tymoshenko 
939f114aaddSEmmanuel Vadot 	if (OF_getencprop(sc->node, "snps,pbl", &pbl, sizeof(uint32_t)) <= 0)
9403e5cd548SEmmanuel Vadot 		pbl = BUS_MODE_DEFAULT_PBL;
941f114aaddSEmmanuel Vadot 	if (OF_getencprop(sc->node, "snps,txpbl", &txpbl, sizeof(uint32_t)) <= 0)
9423e5cd548SEmmanuel Vadot 		txpbl = pbl;
943f114aaddSEmmanuel Vadot 	if (OF_getencprop(sc->node, "snps,rxpbl", &rxpbl, sizeof(uint32_t)) <= 0)
9443e5cd548SEmmanuel Vadot 		rxpbl = pbl;
945f114aaddSEmmanuel Vadot 	if (OF_hasprop(sc->node, "snps,no-pbl-x8") == 1)
9465e38d9e4SEmmanuel Vadot 		nopblx8 = true;
947f114aaddSEmmanuel Vadot 	if (OF_hasprop(sc->node, "snps,fixed-burst") == 1)
9485e38d9e4SEmmanuel Vadot 		fixed_burst = true;
949f4bb6ea5SEmmanuel Vadot 	if (OF_hasprop(sc->node, "snps,mixed-burst") == 1)
950f4bb6ea5SEmmanuel Vadot 		mixed_burst = true;
951b69c49d1SEmmanuel Vadot 	if (OF_hasprop(sc->node, "snps,aal") == 1)
952b69c49d1SEmmanuel Vadot 		aal = true;
9535e38d9e4SEmmanuel Vadot 
95450059a60SEmmanuel Vadot 	error = clk_set_assigned(dev, ofw_bus_get_node(dev));
95550059a60SEmmanuel Vadot 	if (error != 0) {
95650059a60SEmmanuel Vadot 		device_printf(dev, "clk_set_assigned failed\n");
95750059a60SEmmanuel Vadot 		return (error);
95850059a60SEmmanuel Vadot 	}
9595d43fd68SRuslan Bukin 
96050059a60SEmmanuel Vadot 	/* Enable main clock */
96150059a60SEmmanuel Vadot 	if ((error = dwc_clock_init(sc)) != 0)
96250059a60SEmmanuel Vadot 		return (error);
96350059a60SEmmanuel Vadot 	/* De-assert main reset */
96450059a60SEmmanuel Vadot 	if ((error = dwc_reset_deassert(sc)) != 0)
96550059a60SEmmanuel Vadot 		return (error);
96650059a60SEmmanuel Vadot 
96750059a60SEmmanuel Vadot 	if (IF_DWC_INIT(dev) != 0)
9686a05f063SJared McNeill 		return (ENXIO);
9696a05f063SJared McNeill 
9705d43fd68SRuslan Bukin 	if (bus_alloc_resources(dev, dwc_spec, sc->res)) {
9715d43fd68SRuslan Bukin 		device_printf(dev, "could not allocate resources\n");
9725d43fd68SRuslan Bukin 		return (ENXIO);
9735d43fd68SRuslan Bukin 	}
9745d43fd68SRuslan Bukin 
9755d43fd68SRuslan Bukin 	/* Read MAC before reset */
976f88e0af6SEmmanuel Vadot 	dwc_get_hwaddr(sc, macaddr);
9775d43fd68SRuslan Bukin 
978d7acb49aSJared McNeill 	/* Reset the PHY if needed */
979f114aaddSEmmanuel Vadot 	if (dwc_reset_phy(sc) != 0) {
980d7acb49aSJared McNeill 		device_printf(dev, "Can't reset the PHY\n");
98130f16ad4SEmmanuel Vadot 		bus_release_resources(dev, dwc_spec, sc->res);
982d7acb49aSJared McNeill 		return (ENXIO);
983d7acb49aSJared McNeill 	}
984d7acb49aSJared McNeill 
9855d43fd68SRuslan Bukin 	/* Reset */
9865d43fd68SRuslan Bukin 	reg = READ4(sc, BUS_MODE);
9875d43fd68SRuslan Bukin 	reg |= (BUS_MODE_SWR);
9885d43fd68SRuslan Bukin 	WRITE4(sc, BUS_MODE, reg);
9895d43fd68SRuslan Bukin 
990d8e5258dSRuslan Bukin 	for (i = 0; i < MAC_RESET_TIMEOUT; i++) {
9915d43fd68SRuslan Bukin 		if ((READ4(sc, BUS_MODE) & BUS_MODE_SWR) == 0)
9925d43fd68SRuslan Bukin 			break;
9935d43fd68SRuslan Bukin 		DELAY(10);
9945d43fd68SRuslan Bukin 	}
995d8e5258dSRuslan Bukin 	if (i >= MAC_RESET_TIMEOUT) {
9965d43fd68SRuslan Bukin 		device_printf(sc->dev, "Can't reset DWC.\n");
99730f16ad4SEmmanuel Vadot 		bus_release_resources(dev, dwc_spec, sc->res);
9985d43fd68SRuslan Bukin 		return (ENXIO);
9995d43fd68SRuslan Bukin 	}
10005d43fd68SRuslan Bukin 
10015e38d9e4SEmmanuel Vadot 	reg = BUS_MODE_USP;
10025e38d9e4SEmmanuel Vadot 	if (!nopblx8)
10035e38d9e4SEmmanuel Vadot 		reg |= BUS_MODE_EIGHTXPBL;
10045e38d9e4SEmmanuel Vadot 	reg |= (txpbl << BUS_MODE_PBL_SHIFT);
10055e38d9e4SEmmanuel Vadot 	reg |= (rxpbl << BUS_MODE_RPBL_SHIFT);
10065e38d9e4SEmmanuel Vadot 	if (fixed_burst)
10075e38d9e4SEmmanuel Vadot 		reg |= BUS_MODE_FIXEDBURST;
1008f4bb6ea5SEmmanuel Vadot 	if (mixed_burst)
1009f4bb6ea5SEmmanuel Vadot 		reg |= BUS_MODE_MIXEDBURST;
1010b69c49d1SEmmanuel Vadot 	if (aal)
1011b69c49d1SEmmanuel Vadot 		reg |= BUS_MODE_AAL;
10123e5cd548SEmmanuel Vadot 
10135d43fd68SRuslan Bukin 	WRITE4(sc, BUS_MODE, reg);
10145d43fd68SRuslan Bukin 
10155d43fd68SRuslan Bukin 	/*
10165d43fd68SRuslan Bukin 	 * DMA must be stop while changing descriptor list addresses.
10175d43fd68SRuslan Bukin 	 */
10185d43fd68SRuslan Bukin 	reg = READ4(sc, OPERATION_MODE);
10195d43fd68SRuslan Bukin 	reg &= ~(MODE_ST | MODE_SR);
10205d43fd68SRuslan Bukin 	WRITE4(sc, OPERATION_MODE, reg);
10215d43fd68SRuslan Bukin 
1022*972adf0fSEmmanuel Vadot 	if (dma1000_init(sc)) {
102330f16ad4SEmmanuel Vadot 		bus_release_resources(dev, dwc_spec, sc->res);
10245d43fd68SRuslan Bukin 		return (ENXIO);
102530f16ad4SEmmanuel Vadot 	}
10265d43fd68SRuslan Bukin 
10275d43fd68SRuslan Bukin 	/* Setup addresses */
10285d43fd68SRuslan Bukin 	WRITE4(sc, RX_DESCR_LIST_ADDR, sc->rxdesc_ring_paddr);
10295d43fd68SRuslan Bukin 	WRITE4(sc, TX_DESCR_LIST_ADDR, sc->txdesc_ring_paddr);
10305d43fd68SRuslan Bukin 
1031d8e5258dSRuslan Bukin 	mtx_init(&sc->mtx, device_get_nameunit(sc->dev),
1032d8e5258dSRuslan Bukin 	    MTX_NETWORK_LOCK, MTX_DEF);
1033d8e5258dSRuslan Bukin 
1034d8e5258dSRuslan Bukin 	callout_init_mtx(&sc->dwc_callout, &sc->mtx, 0);
1035d8e5258dSRuslan Bukin 
1036d8e5258dSRuslan Bukin 	/* Setup interrupt handler. */
1037d8e5258dSRuslan Bukin 	error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_NET | INTR_MPSAFE,
1038d8e5258dSRuslan Bukin 	    NULL, dwc_intr, sc, &sc->intr_cookie);
1039d8e5258dSRuslan Bukin 	if (error != 0) {
1040d8e5258dSRuslan Bukin 		device_printf(dev, "could not setup interrupt handler.\n");
104130f16ad4SEmmanuel Vadot 		bus_release_resources(dev, dwc_spec, sc->res);
1042d8e5258dSRuslan Bukin 		return (ENXIO);
1043d8e5258dSRuslan Bukin 	}
1044d8e5258dSRuslan Bukin 
10455d43fd68SRuslan Bukin 	/* Set up the ethernet interface. */
10465d43fd68SRuslan Bukin 	sc->ifp = ifp = if_alloc(IFT_ETHER);
10475d43fd68SRuslan Bukin 
1048ca018790SMitchell Horne 	if_setsoftc(ifp, sc);
10495d43fd68SRuslan Bukin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
10503bbd11eeSEmmanuel Vadot 	if_setflags(sc->ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
10513bbd11eeSEmmanuel Vadot 	if_setstartfn(ifp, dwc_txstart);
10523bbd11eeSEmmanuel Vadot 	if_setioctlfn(ifp, dwc_ioctl);
10533bbd11eeSEmmanuel Vadot 	if_setinitfn(ifp, dwc_init);
1054e5232621SOleksandr Tymoshenko 	if_setsendqlen(ifp, TX_MAP_COUNT - 1);
10553bbd11eeSEmmanuel Vadot 	if_setsendqready(sc->ifp);
105698ea5a7bSEmmanuel Vadot 	if_sethwassist(sc->ifp, CSUM_IP | CSUM_UDP | CSUM_TCP);
105798ea5a7bSEmmanuel Vadot 	if_setcapabilities(sc->ifp, IFCAP_VLAN_MTU | IFCAP_HWCSUM);
10583bbd11eeSEmmanuel Vadot 	if_setcapenable(sc->ifp, if_getcapabilities(sc->ifp));
10595d43fd68SRuslan Bukin 
10605d43fd68SRuslan Bukin 	/* Attach the mii driver. */
10615d43fd68SRuslan Bukin 	error = mii_attach(dev, &sc->miibus, ifp, dwc_media_change,
10625d43fd68SRuslan Bukin 	    dwc_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY,
10635d43fd68SRuslan Bukin 	    MII_OFFSET_ANY, 0);
10645d43fd68SRuslan Bukin 
10655d43fd68SRuslan Bukin 	if (error != 0) {
10665d43fd68SRuslan Bukin 		device_printf(dev, "PHY attach failed\n");
106730f16ad4SEmmanuel Vadot 		bus_teardown_intr(dev, sc->res[1], sc->intr_cookie);
106830f16ad4SEmmanuel Vadot 		bus_release_resources(dev, dwc_spec, sc->res);
10695d43fd68SRuslan Bukin 		return (ENXIO);
10705d43fd68SRuslan Bukin 	}
10715d43fd68SRuslan Bukin 	sc->mii_softc = device_get_softc(sc->miibus);
10725d43fd68SRuslan Bukin 
10735d43fd68SRuslan Bukin 	/* All ready to run, attach the ethernet interface. */
10745d43fd68SRuslan Bukin 	ether_ifattach(ifp, macaddr);
10755d43fd68SRuslan Bukin 	sc->is_attached = true;
10765d43fd68SRuslan Bukin 
10775d43fd68SRuslan Bukin 	return (0);
10785d43fd68SRuslan Bukin }
10795d43fd68SRuslan Bukin 
108027b39e58SMitchell Horne static int
108127b39e58SMitchell Horne dwc_detach(device_t dev)
108227b39e58SMitchell Horne {
108327b39e58SMitchell Horne 	struct dwc_softc *sc;
108427b39e58SMitchell Horne 
108527b39e58SMitchell Horne 	sc = device_get_softc(dev);
108627b39e58SMitchell Horne 
108727b39e58SMitchell Horne 	/*
108827b39e58SMitchell Horne 	 * Disable and tear down interrupts before anything else, so we don't
108927b39e58SMitchell Horne 	 * race with the handler.
109027b39e58SMitchell Horne 	 */
109127b39e58SMitchell Horne 	WRITE4(sc, INTERRUPT_ENABLE, 0);
109227b39e58SMitchell Horne 	if (sc->intr_cookie != NULL) {
109327b39e58SMitchell Horne 		bus_teardown_intr(dev, sc->res[1], sc->intr_cookie);
109427b39e58SMitchell Horne 	}
109527b39e58SMitchell Horne 
109627b39e58SMitchell Horne 	if (sc->is_attached) {
109727b39e58SMitchell Horne 		DWC_LOCK(sc);
109827b39e58SMitchell Horne 		sc->is_detaching = true;
109927b39e58SMitchell Horne 		dwc_stop_locked(sc);
110027b39e58SMitchell Horne 		DWC_UNLOCK(sc);
110127b39e58SMitchell Horne 		callout_drain(&sc->dwc_callout);
110227b39e58SMitchell Horne 		ether_ifdetach(sc->ifp);
110327b39e58SMitchell Horne 	}
110427b39e58SMitchell Horne 
110527b39e58SMitchell Horne 	if (sc->miibus != NULL) {
110627b39e58SMitchell Horne 		device_delete_child(dev, sc->miibus);
110727b39e58SMitchell Horne 		sc->miibus = NULL;
110827b39e58SMitchell Horne 	}
110927b39e58SMitchell Horne 	bus_generic_detach(dev);
111027b39e58SMitchell Horne 
111127b39e58SMitchell Horne 	/* Free DMA descriptors */
1112*972adf0fSEmmanuel Vadot 	dma1000_free(sc);
111327b39e58SMitchell Horne 
111427b39e58SMitchell Horne 	if (sc->ifp != NULL) {
111527b39e58SMitchell Horne 		if_free(sc->ifp);
111627b39e58SMitchell Horne 		sc->ifp = NULL;
111727b39e58SMitchell Horne 	}
111827b39e58SMitchell Horne 
111927b39e58SMitchell Horne 	bus_release_resources(dev, dwc_spec, sc->res);
112027b39e58SMitchell Horne 
112127b39e58SMitchell Horne 	mtx_destroy(&sc->mtx);
112227b39e58SMitchell Horne 	return (0);
112327b39e58SMitchell Horne }
112427b39e58SMitchell Horne 
11255d43fd68SRuslan Bukin static device_method_t dwc_methods[] = {
11265d43fd68SRuslan Bukin 	DEVMETHOD(device_probe,		dwc_probe),
11275d43fd68SRuslan Bukin 	DEVMETHOD(device_attach,	dwc_attach),
112827b39e58SMitchell Horne 	DEVMETHOD(device_detach,	dwc_detach),
11295d43fd68SRuslan Bukin 
11305d43fd68SRuslan Bukin 	/* MII Interface */
11315d43fd68SRuslan Bukin 	DEVMETHOD(miibus_readreg,	dwc_miibus_read_reg),
11325d43fd68SRuslan Bukin 	DEVMETHOD(miibus_writereg,	dwc_miibus_write_reg),
11335d43fd68SRuslan Bukin 	DEVMETHOD(miibus_statchg,	dwc_miibus_statchg),
11345d43fd68SRuslan Bukin 
11355d43fd68SRuslan Bukin 	{ 0, 0 }
11365d43fd68SRuslan Bukin };
11375d43fd68SRuslan Bukin 
11385df53927SLuiz Otavio O Souza driver_t dwc_driver = {
11395d43fd68SRuslan Bukin 	"dwc",
11405d43fd68SRuslan Bukin 	dwc_methods,
11415d43fd68SRuslan Bukin 	sizeof(struct dwc_softc),
11425d43fd68SRuslan Bukin };
11435d43fd68SRuslan Bukin 
1144e55d0536SJohn Baldwin DRIVER_MODULE(dwc, simplebus, dwc_driver, 0, 0);
11453e38757dSJohn Baldwin DRIVER_MODULE(miibus, dwc, miibus_driver, 0, 0);
11465d43fd68SRuslan Bukin 
11475d43fd68SRuslan Bukin MODULE_DEPEND(dwc, ether, 1, 1, 1);
11485d43fd68SRuslan Bukin MODULE_DEPEND(dwc, miibus, 1, 1, 1);
1149