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