15d43fd68SRuslan Bukin /*- 25d43fd68SRuslan Bukin * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com> 35d43fd68SRuslan Bukin * All rights reserved. 45d43fd68SRuslan Bukin * 55d43fd68SRuslan Bukin * This software was developed by SRI International and the University of 65d43fd68SRuslan Bukin * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) 75d43fd68SRuslan Bukin * ("CTSRD"), as part of the DARPA CRASH research programme. 85d43fd68SRuslan Bukin * 95d43fd68SRuslan Bukin * Redistribution and use in source and binary forms, with or without 105d43fd68SRuslan Bukin * modification, are permitted provided that the following conditions 115d43fd68SRuslan Bukin * are met: 125d43fd68SRuslan Bukin * 1. Redistributions of source code must retain the above copyright 135d43fd68SRuslan Bukin * notice, this list of conditions and the following disclaimer. 145d43fd68SRuslan Bukin * 2. Redistributions in binary form must reproduce the above copyright 155d43fd68SRuslan Bukin * notice, this list of conditions and the following disclaimer in the 165d43fd68SRuslan Bukin * documentation and/or other materials provided with the distribution. 175d43fd68SRuslan Bukin * 185d43fd68SRuslan Bukin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 195d43fd68SRuslan Bukin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 205d43fd68SRuslan Bukin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 215d43fd68SRuslan Bukin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 225d43fd68SRuslan Bukin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 235d43fd68SRuslan Bukin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 245d43fd68SRuslan Bukin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 255d43fd68SRuslan Bukin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 265d43fd68SRuslan Bukin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 275d43fd68SRuslan Bukin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 285d43fd68SRuslan Bukin * SUCH DAMAGE. 295d43fd68SRuslan Bukin */ 305d43fd68SRuslan Bukin 315d43fd68SRuslan Bukin /* 325d43fd68SRuslan Bukin * Ethernet media access controller (EMAC) 335d43fd68SRuslan Bukin * Chapter 17, Altera Cyclone V Device Handbook (CV-5V2 2014.07.22) 345d43fd68SRuslan Bukin * 355d43fd68SRuslan Bukin * EMAC is an instance of the Synopsys DesignWare 3504-0 365d43fd68SRuslan Bukin * Universal 10/100/1000 Ethernet MAC (DWC_gmac). 375d43fd68SRuslan Bukin */ 385d43fd68SRuslan Bukin 395d43fd68SRuslan Bukin #include <sys/cdefs.h> 405d43fd68SRuslan Bukin __FBSDID("$FreeBSD$"); 415d43fd68SRuslan Bukin 425d43fd68SRuslan Bukin #include <sys/param.h> 435d43fd68SRuslan Bukin #include <sys/systm.h> 445d43fd68SRuslan Bukin #include <sys/bus.h> 45d7acb49aSJared McNeill #include <sys/gpio.h> 465d43fd68SRuslan Bukin #include <sys/kernel.h> 475d43fd68SRuslan Bukin #include <sys/lock.h> 48da9a326bSLuiz Otavio O Souza #include <sys/malloc.h> 495d43fd68SRuslan Bukin #include <sys/mbuf.h> 50da9a326bSLuiz Otavio O Souza #include <sys/module.h> 515d43fd68SRuslan Bukin #include <sys/mutex.h> 52da9a326bSLuiz Otavio O Souza #include <sys/rman.h> 535d43fd68SRuslan Bukin #include <sys/socket.h> 545d43fd68SRuslan Bukin #include <sys/sockio.h> 555d43fd68SRuslan Bukin 565d43fd68SRuslan Bukin #include <net/bpf.h> 575d43fd68SRuslan Bukin #include <net/if.h> 585d43fd68SRuslan Bukin #include <net/ethernet.h> 595d43fd68SRuslan Bukin #include <net/if_dl.h> 605d43fd68SRuslan Bukin #include <net/if_media.h> 615d43fd68SRuslan Bukin #include <net/if_types.h> 625d43fd68SRuslan Bukin #include <net/if_var.h> 635d43fd68SRuslan Bukin 645d43fd68SRuslan Bukin #include <machine/bus.h> 655d43fd68SRuslan Bukin 66da9a326bSLuiz Otavio O Souza #include <dev/dwc/if_dwc.h> 675df53927SLuiz Otavio O Souza #include <dev/dwc/if_dwcvar.h> 685d43fd68SRuslan Bukin #include <dev/mii/mii.h> 695d43fd68SRuslan Bukin #include <dev/mii/miivar.h> 70da9a326bSLuiz Otavio O Souza #include <dev/ofw/ofw_bus.h> 71da9a326bSLuiz Otavio O Souza #include <dev/ofw/ofw_bus_subr.h> 72da9a326bSLuiz Otavio O Souza 736a05f063SJared McNeill #ifdef EXT_RESOURCES 746a05f063SJared McNeill #include <dev/extres/clk/clk.h> 756a05f063SJared McNeill #include <dev/extres/hwreset/hwreset.h> 766a05f063SJared McNeill #endif 776a05f063SJared McNeill 785df53927SLuiz Otavio O Souza #include "if_dwc_if.h" 79d7acb49aSJared McNeill #include "gpio_if.h" 805d43fd68SRuslan Bukin #include "miibus_if.h" 815d43fd68SRuslan Bukin 825d43fd68SRuslan Bukin #define READ4(_sc, _reg) \ 835d43fd68SRuslan Bukin bus_read_4((_sc)->res[0], _reg) 845d43fd68SRuslan Bukin #define WRITE4(_sc, _reg, _val) \ 855d43fd68SRuslan Bukin bus_write_4((_sc)->res[0], _reg, _val) 865d43fd68SRuslan Bukin 87d8e5258dSRuslan Bukin #define MAC_RESET_TIMEOUT 100 885d43fd68SRuslan Bukin #define WATCHDOG_TIMEOUT_SECS 5 895d43fd68SRuslan Bukin #define STATS_HARVEST_INTERVAL 2 905d43fd68SRuslan Bukin 915d43fd68SRuslan Bukin #define DWC_LOCK(sc) mtx_lock(&(sc)->mtx) 925d43fd68SRuslan Bukin #define DWC_UNLOCK(sc) mtx_unlock(&(sc)->mtx) 93ff0752c8SLuiz Otavio O Souza #define DWC_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED) 94ff0752c8SLuiz Otavio O Souza #define DWC_ASSERT_UNLOCKED(sc) mtx_assert(&(sc)->mtx, MA_NOTOWNED) 955d43fd68SRuslan Bukin 96ff0752c8SLuiz Otavio O Souza #define DDESC_TDES0_OWN (1U << 31) 97ff0752c8SLuiz Otavio O Souza #define DDESC_TDES0_TXINT (1U << 30) 98ff0752c8SLuiz Otavio O Souza #define DDESC_TDES0_TXLAST (1U << 29) 99ff0752c8SLuiz Otavio O Souza #define DDESC_TDES0_TXFIRST (1U << 28) 100ff0752c8SLuiz Otavio O Souza #define DDESC_TDES0_TXCRCDIS (1U << 27) 101ff0752c8SLuiz Otavio O Souza #define DDESC_TDES0_TXRINGEND (1U << 21) 102ff0752c8SLuiz Otavio O Souza #define DDESC_TDES0_TXCHAIN (1U << 20) 1035d43fd68SRuslan Bukin 104ff0752c8SLuiz Otavio O Souza #define DDESC_RDES0_OWN (1U << 31) 1055d43fd68SRuslan Bukin #define DDESC_RDES0_FL_MASK 0x3fff 1065d43fd68SRuslan Bukin #define DDESC_RDES0_FL_SHIFT 16 /* Frame Length */ 107ff0752c8SLuiz Otavio O Souza #define DDESC_RDES1_CHAINED (1U << 14) 1085d43fd68SRuslan Bukin 1095df53927SLuiz Otavio O Souza /* Alt descriptor bits. */ 1105df53927SLuiz Otavio O Souza #define DDESC_CNTL_TXINT (1U << 31) 1115df53927SLuiz Otavio O Souza #define DDESC_CNTL_TXLAST (1U << 30) 1125df53927SLuiz Otavio O Souza #define DDESC_CNTL_TXFIRST (1U << 29) 1135df53927SLuiz Otavio O Souza #define DDESC_CNTL_TXCRCDIS (1U << 26) 1145df53927SLuiz Otavio O Souza #define DDESC_CNTL_TXRINGEND (1U << 25) 1155df53927SLuiz Otavio O Souza #define DDESC_CNTL_TXCHAIN (1U << 24) 1165df53927SLuiz Otavio O Souza 1175df53927SLuiz Otavio O Souza #define DDESC_CNTL_CHAINED (1U << 24) 1185d43fd68SRuslan Bukin 1195d43fd68SRuslan Bukin /* 1205d43fd68SRuslan Bukin * A hardware buffer descriptor. Rx and Tx buffers have the same descriptor 1215df53927SLuiz Otavio O Souza * layout, but the bits in the fields have different meanings. 1225d43fd68SRuslan Bukin */ 1235d43fd68SRuslan Bukin struct dwc_hwdesc 1245d43fd68SRuslan Bukin { 1255df53927SLuiz Otavio O Souza uint32_t tdes0; /* status for alt layout */ 1265df53927SLuiz Otavio O Souza uint32_t tdes1; /* cntl for alt layout */ 1275d43fd68SRuslan Bukin uint32_t addr; /* pointer to buffer data */ 1285d43fd68SRuslan Bukin uint32_t addr_next; /* link to next descriptor */ 1295d43fd68SRuslan Bukin }; 1305d43fd68SRuslan Bukin 1315d43fd68SRuslan Bukin /* 1325d43fd68SRuslan Bukin * The hardware imposes alignment restrictions on various objects involved in 1335d43fd68SRuslan Bukin * DMA transfers. These values are expressed in bytes (not bits). 1345d43fd68SRuslan Bukin */ 1355d43fd68SRuslan Bukin #define DWC_DESC_RING_ALIGN 2048 1365d43fd68SRuslan Bukin 1375d43fd68SRuslan Bukin static struct resource_spec dwc_spec[] = { 1385d43fd68SRuslan Bukin { SYS_RES_MEMORY, 0, RF_ACTIVE }, 1395d43fd68SRuslan Bukin { SYS_RES_IRQ, 0, RF_ACTIVE }, 1405d43fd68SRuslan Bukin { -1, 0 } 1415d43fd68SRuslan Bukin }; 1425d43fd68SRuslan Bukin 1435d43fd68SRuslan Bukin static void dwc_txfinish_locked(struct dwc_softc *sc); 1445d43fd68SRuslan Bukin static void dwc_rxfinish_locked(struct dwc_softc *sc); 1455d43fd68SRuslan Bukin static void dwc_stop_locked(struct dwc_softc *sc); 1465d43fd68SRuslan Bukin static void dwc_setup_rxfilter(struct dwc_softc *sc); 1475d43fd68SRuslan Bukin 1485d43fd68SRuslan Bukin static inline uint32_t 1495d43fd68SRuslan Bukin next_rxidx(struct dwc_softc *sc, uint32_t curidx) 1505d43fd68SRuslan Bukin { 1515d43fd68SRuslan Bukin 1525d43fd68SRuslan Bukin return ((curidx + 1) % RX_DESC_COUNT); 1535d43fd68SRuslan Bukin } 1545d43fd68SRuslan Bukin 1555d43fd68SRuslan Bukin static inline uint32_t 1565d43fd68SRuslan Bukin next_txidx(struct dwc_softc *sc, uint32_t curidx) 1575d43fd68SRuslan Bukin { 1585d43fd68SRuslan Bukin 1595d43fd68SRuslan Bukin return ((curidx + 1) % TX_DESC_COUNT); 1605d43fd68SRuslan Bukin } 1615d43fd68SRuslan Bukin 1625d43fd68SRuslan Bukin static void 1635d43fd68SRuslan Bukin dwc_get1paddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 1645d43fd68SRuslan Bukin { 1655d43fd68SRuslan Bukin 1665d43fd68SRuslan Bukin if (error != 0) 1675d43fd68SRuslan Bukin return; 1685d43fd68SRuslan Bukin *(bus_addr_t *)arg = segs[0].ds_addr; 1695d43fd68SRuslan Bukin } 1705d43fd68SRuslan Bukin 1715d43fd68SRuslan Bukin inline static uint32_t 1725d43fd68SRuslan Bukin dwc_setup_txdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr, 1735d43fd68SRuslan Bukin uint32_t len) 1745d43fd68SRuslan Bukin { 1755d43fd68SRuslan Bukin uint32_t flags; 1765d43fd68SRuslan Bukin uint32_t nidx; 1775d43fd68SRuslan Bukin 1785d43fd68SRuslan Bukin nidx = next_txidx(sc, idx); 1795d43fd68SRuslan Bukin 1805d43fd68SRuslan Bukin /* Addr/len 0 means we're clearing the descriptor after xmit done. */ 1815d43fd68SRuslan Bukin if (paddr == 0 || len == 0) { 1825d43fd68SRuslan Bukin flags = 0; 1835d43fd68SRuslan Bukin --sc->txcount; 1845d43fd68SRuslan Bukin } else { 1855df53927SLuiz Otavio O Souza if (sc->mactype == DWC_GMAC_ALT_DESC) 1865df53927SLuiz Otavio O Souza flags = DDESC_CNTL_TXCHAIN | DDESC_CNTL_TXFIRST 1875df53927SLuiz Otavio O Souza | DDESC_CNTL_TXLAST | DDESC_CNTL_TXINT; 1885df53927SLuiz Otavio O Souza else 1895d43fd68SRuslan Bukin flags = DDESC_TDES0_TXCHAIN | DDESC_TDES0_TXFIRST 1905d43fd68SRuslan Bukin | DDESC_TDES0_TXLAST | DDESC_TDES0_TXINT; 1915d43fd68SRuslan Bukin ++sc->txcount; 1925d43fd68SRuslan Bukin } 1935d43fd68SRuslan Bukin 1945d43fd68SRuslan Bukin sc->txdesc_ring[idx].addr = (uint32_t)(paddr); 1955df53927SLuiz Otavio O Souza if (sc->mactype == DWC_GMAC_ALT_DESC) { 1965df53927SLuiz Otavio O Souza sc->txdesc_ring[idx].tdes0 = 0; 1975df53927SLuiz Otavio O Souza sc->txdesc_ring[idx].tdes1 = flags | len; 1985df53927SLuiz Otavio O Souza } else { 1995d43fd68SRuslan Bukin sc->txdesc_ring[idx].tdes0 = flags; 2005d43fd68SRuslan Bukin sc->txdesc_ring[idx].tdes1 = len; 2015df53927SLuiz Otavio O Souza } 2025d43fd68SRuslan Bukin 2035d43fd68SRuslan Bukin if (paddr && len) { 2045d43fd68SRuslan Bukin wmb(); 2055d43fd68SRuslan Bukin sc->txdesc_ring[idx].tdes0 |= DDESC_TDES0_OWN; 2065d43fd68SRuslan Bukin wmb(); 2075d43fd68SRuslan Bukin } 2085d43fd68SRuslan Bukin 2095d43fd68SRuslan Bukin return (nidx); 2105d43fd68SRuslan Bukin } 2115d43fd68SRuslan Bukin 2125d43fd68SRuslan Bukin static int 2135d43fd68SRuslan Bukin dwc_setup_txbuf(struct dwc_softc *sc, int idx, struct mbuf **mp) 2145d43fd68SRuslan Bukin { 2155d43fd68SRuslan Bukin struct bus_dma_segment seg; 2165d43fd68SRuslan Bukin int error, nsegs; 2175d43fd68SRuslan Bukin struct mbuf * m; 2185d43fd68SRuslan Bukin 2195d43fd68SRuslan Bukin if ((m = m_defrag(*mp, M_NOWAIT)) == NULL) 2205d43fd68SRuslan Bukin return (ENOMEM); 2215d43fd68SRuslan Bukin *mp = m; 2225d43fd68SRuslan Bukin 2235d43fd68SRuslan Bukin error = bus_dmamap_load_mbuf_sg(sc->txbuf_tag, sc->txbuf_map[idx].map, 2245d43fd68SRuslan Bukin m, &seg, &nsegs, 0); 2255d43fd68SRuslan Bukin if (error != 0) { 2265d43fd68SRuslan Bukin return (ENOMEM); 2275d43fd68SRuslan Bukin } 2285d43fd68SRuslan Bukin 2295d43fd68SRuslan Bukin KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 2305d43fd68SRuslan Bukin 2315d43fd68SRuslan Bukin bus_dmamap_sync(sc->txbuf_tag, sc->txbuf_map[idx].map, 2325d43fd68SRuslan Bukin BUS_DMASYNC_PREWRITE); 2335d43fd68SRuslan Bukin 2345d43fd68SRuslan Bukin sc->txbuf_map[idx].mbuf = m; 2355d43fd68SRuslan Bukin 2365d43fd68SRuslan Bukin dwc_setup_txdesc(sc, idx, seg.ds_addr, seg.ds_len); 2375d43fd68SRuslan Bukin 2385d43fd68SRuslan Bukin return (0); 2395d43fd68SRuslan Bukin } 2405d43fd68SRuslan Bukin 2415d43fd68SRuslan Bukin static void 2425d43fd68SRuslan Bukin dwc_txstart_locked(struct dwc_softc *sc) 2435d43fd68SRuslan Bukin { 2445d43fd68SRuslan Bukin struct ifnet *ifp; 2455d43fd68SRuslan Bukin struct mbuf *m; 2465d43fd68SRuslan Bukin int enqueued; 2475d43fd68SRuslan Bukin 2485d43fd68SRuslan Bukin DWC_ASSERT_LOCKED(sc); 2495d43fd68SRuslan Bukin 2505d43fd68SRuslan Bukin if (!sc->link_is_up) 2515d43fd68SRuslan Bukin return; 2525d43fd68SRuslan Bukin 2535d43fd68SRuslan Bukin ifp = sc->ifp; 2545d43fd68SRuslan Bukin 2555d43fd68SRuslan Bukin if (ifp->if_drv_flags & IFF_DRV_OACTIVE) { 2565d43fd68SRuslan Bukin return; 2575d43fd68SRuslan Bukin } 2585d43fd68SRuslan Bukin 2595d43fd68SRuslan Bukin enqueued = 0; 2605d43fd68SRuslan Bukin 2615d43fd68SRuslan Bukin for (;;) { 2625d43fd68SRuslan Bukin if (sc->txcount == (TX_DESC_COUNT-1)) { 2635d43fd68SRuslan Bukin ifp->if_drv_flags |= IFF_DRV_OACTIVE; 2645d43fd68SRuslan Bukin break; 2655d43fd68SRuslan Bukin } 2665d43fd68SRuslan Bukin 2675d43fd68SRuslan Bukin IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 2685d43fd68SRuslan Bukin if (m == NULL) 2695d43fd68SRuslan Bukin break; 2705d43fd68SRuslan Bukin if (dwc_setup_txbuf(sc, sc->tx_idx_head, &m) != 0) { 2715d43fd68SRuslan Bukin IFQ_DRV_PREPEND(&ifp->if_snd, m); 2725d43fd68SRuslan Bukin break; 2735d43fd68SRuslan Bukin } 2745d43fd68SRuslan Bukin BPF_MTAP(ifp, m); 2755d43fd68SRuslan Bukin sc->tx_idx_head = next_txidx(sc, sc->tx_idx_head); 2765d43fd68SRuslan Bukin ++enqueued; 2775d43fd68SRuslan Bukin } 2785d43fd68SRuslan Bukin 2795d43fd68SRuslan Bukin if (enqueued != 0) { 2805d43fd68SRuslan Bukin WRITE4(sc, TRANSMIT_POLL_DEMAND, 0x1); 2815d43fd68SRuslan Bukin sc->tx_watchdog_count = WATCHDOG_TIMEOUT_SECS; 2825d43fd68SRuslan Bukin } 2835d43fd68SRuslan Bukin } 2845d43fd68SRuslan Bukin 2855d43fd68SRuslan Bukin static void 2865d43fd68SRuslan Bukin dwc_txstart(struct ifnet *ifp) 2875d43fd68SRuslan Bukin { 2885d43fd68SRuslan Bukin struct dwc_softc *sc = ifp->if_softc; 2895d43fd68SRuslan Bukin 2905d43fd68SRuslan Bukin DWC_LOCK(sc); 2915d43fd68SRuslan Bukin dwc_txstart_locked(sc); 2925d43fd68SRuslan Bukin DWC_UNLOCK(sc); 2935d43fd68SRuslan Bukin } 2945d43fd68SRuslan Bukin 2955d43fd68SRuslan Bukin static void 2965d43fd68SRuslan Bukin dwc_stop_locked(struct dwc_softc *sc) 2975d43fd68SRuslan Bukin { 2985d43fd68SRuslan Bukin struct ifnet *ifp; 299ff0752c8SLuiz Otavio O Souza uint32_t reg; 3005d43fd68SRuslan Bukin 3015d43fd68SRuslan Bukin DWC_ASSERT_LOCKED(sc); 3025d43fd68SRuslan Bukin 3035d43fd68SRuslan Bukin ifp = sc->ifp; 3045d43fd68SRuslan Bukin ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 3055d43fd68SRuslan Bukin sc->tx_watchdog_count = 0; 3065d43fd68SRuslan Bukin sc->stats_harvest_count = 0; 3075d43fd68SRuslan Bukin 3085d43fd68SRuslan Bukin callout_stop(&sc->dwc_callout); 3095d43fd68SRuslan Bukin 3105d43fd68SRuslan Bukin /* Stop DMA TX */ 3115d43fd68SRuslan Bukin reg = READ4(sc, OPERATION_MODE); 3125d43fd68SRuslan Bukin reg &= ~(MODE_ST); 3135d43fd68SRuslan Bukin WRITE4(sc, OPERATION_MODE, reg); 3145d43fd68SRuslan Bukin 3155d43fd68SRuslan Bukin /* Flush TX */ 3165d43fd68SRuslan Bukin reg = READ4(sc, OPERATION_MODE); 3175d43fd68SRuslan Bukin reg |= (MODE_FTF); 3185d43fd68SRuslan Bukin WRITE4(sc, OPERATION_MODE, reg); 3195d43fd68SRuslan Bukin 3205d43fd68SRuslan Bukin /* Stop transmitters */ 3215d43fd68SRuslan Bukin reg = READ4(sc, MAC_CONFIGURATION); 3225d43fd68SRuslan Bukin reg &= ~(CONF_TE | CONF_RE); 3235d43fd68SRuslan Bukin WRITE4(sc, MAC_CONFIGURATION, reg); 3245d43fd68SRuslan Bukin 3255d43fd68SRuslan Bukin /* Stop DMA RX */ 3265d43fd68SRuslan Bukin reg = READ4(sc, OPERATION_MODE); 3275d43fd68SRuslan Bukin reg &= ~(MODE_SR); 3285d43fd68SRuslan Bukin WRITE4(sc, OPERATION_MODE, reg); 3295d43fd68SRuslan Bukin } 3305d43fd68SRuslan Bukin 3315d43fd68SRuslan Bukin static void dwc_clear_stats(struct dwc_softc *sc) 3325d43fd68SRuslan Bukin { 333ff0752c8SLuiz Otavio O Souza uint32_t reg; 3345d43fd68SRuslan Bukin 3355d43fd68SRuslan Bukin reg = READ4(sc, MMC_CONTROL); 3365d43fd68SRuslan Bukin reg |= (MMC_CONTROL_CNTRST); 3375d43fd68SRuslan Bukin WRITE4(sc, MMC_CONTROL, reg); 3385d43fd68SRuslan Bukin } 3395d43fd68SRuslan Bukin 3405d43fd68SRuslan Bukin static void 3415d43fd68SRuslan Bukin dwc_harvest_stats(struct dwc_softc *sc) 3425d43fd68SRuslan Bukin { 3435d43fd68SRuslan Bukin struct ifnet *ifp; 3445d43fd68SRuslan Bukin 3455d43fd68SRuslan Bukin /* We don't need to harvest too often. */ 3465d43fd68SRuslan Bukin if (++sc->stats_harvest_count < STATS_HARVEST_INTERVAL) 3475d43fd68SRuslan Bukin return; 3485d43fd68SRuslan Bukin 3495d43fd68SRuslan Bukin sc->stats_harvest_count = 0; 3505d43fd68SRuslan Bukin ifp = sc->ifp; 3515d43fd68SRuslan Bukin 3525d43fd68SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_IPACKETS, READ4(sc, RXFRAMECOUNT_GB)); 3535d43fd68SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_IMCASTS, READ4(sc, RXMULTICASTFRAMES_G)); 3545d43fd68SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_IERRORS, 3555d43fd68SRuslan Bukin READ4(sc, RXOVERSIZE_G) + READ4(sc, RXUNDERSIZE_G) + 3565d43fd68SRuslan Bukin READ4(sc, RXCRCERROR) + READ4(sc, RXALIGNMENTERROR) + 3575d43fd68SRuslan Bukin READ4(sc, RXRUNTERROR) + READ4(sc, RXJABBERERROR) + 3585d43fd68SRuslan Bukin READ4(sc, RXLENGTHERROR)); 3595d43fd68SRuslan Bukin 3605d43fd68SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_OPACKETS, READ4(sc, TXFRAMECOUNT_G)); 3615d43fd68SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_OMCASTS, READ4(sc, TXMULTICASTFRAMES_G)); 3625d43fd68SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_OERRORS, 3635d43fd68SRuslan Bukin READ4(sc, TXOVERSIZE_G) + READ4(sc, TXEXCESSDEF) + 3645d43fd68SRuslan Bukin READ4(sc, TXCARRIERERR) + READ4(sc, TXUNDERFLOWERROR)); 3655d43fd68SRuslan Bukin 3665d43fd68SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 3675d43fd68SRuslan Bukin READ4(sc, TXEXESSCOL) + READ4(sc, TXLATECOL)); 3685d43fd68SRuslan Bukin 3695d43fd68SRuslan Bukin dwc_clear_stats(sc); 3705d43fd68SRuslan Bukin } 3715d43fd68SRuslan Bukin 3725d43fd68SRuslan Bukin static void 3735d43fd68SRuslan Bukin dwc_tick(void *arg) 3745d43fd68SRuslan Bukin { 3755d43fd68SRuslan Bukin struct dwc_softc *sc; 3765d43fd68SRuslan Bukin struct ifnet *ifp; 3775d43fd68SRuslan Bukin int link_was_up; 3785d43fd68SRuslan Bukin 3795d43fd68SRuslan Bukin sc = arg; 3805d43fd68SRuslan Bukin 3815d43fd68SRuslan Bukin DWC_ASSERT_LOCKED(sc); 3825d43fd68SRuslan Bukin 3835d43fd68SRuslan Bukin ifp = sc->ifp; 3845d43fd68SRuslan Bukin 3855d43fd68SRuslan Bukin if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) 3865d43fd68SRuslan Bukin return; 3875d43fd68SRuslan Bukin 3885d43fd68SRuslan Bukin /* 3895d43fd68SRuslan Bukin * Typical tx watchdog. If this fires it indicates that we enqueued 3905d43fd68SRuslan Bukin * packets for output and never got a txdone interrupt for them. Maybe 3915d43fd68SRuslan Bukin * it's a missed interrupt somehow, just pretend we got one. 3925d43fd68SRuslan Bukin */ 3935d43fd68SRuslan Bukin if (sc->tx_watchdog_count > 0) { 3945d43fd68SRuslan Bukin if (--sc->tx_watchdog_count == 0) { 3955d43fd68SRuslan Bukin dwc_txfinish_locked(sc); 3965d43fd68SRuslan Bukin } 3975d43fd68SRuslan Bukin } 3985d43fd68SRuslan Bukin 3995d43fd68SRuslan Bukin /* Gather stats from hardware counters. */ 4005d43fd68SRuslan Bukin dwc_harvest_stats(sc); 4015d43fd68SRuslan Bukin 4025d43fd68SRuslan Bukin /* Check the media status. */ 4035d43fd68SRuslan Bukin link_was_up = sc->link_is_up; 4045d43fd68SRuslan Bukin mii_tick(sc->mii_softc); 4055d43fd68SRuslan Bukin if (sc->link_is_up && !link_was_up) 4065d43fd68SRuslan Bukin dwc_txstart_locked(sc); 4075d43fd68SRuslan Bukin 4085d43fd68SRuslan Bukin /* Schedule another check one second from now. */ 4095d43fd68SRuslan Bukin callout_reset(&sc->dwc_callout, hz, dwc_tick, sc); 4105d43fd68SRuslan Bukin } 4115d43fd68SRuslan Bukin 4125d43fd68SRuslan Bukin static void 4135d43fd68SRuslan Bukin dwc_init_locked(struct dwc_softc *sc) 4145d43fd68SRuslan Bukin { 4155d43fd68SRuslan Bukin struct ifnet *ifp = sc->ifp; 416ff0752c8SLuiz Otavio O Souza uint32_t reg; 4175d43fd68SRuslan Bukin 4185d43fd68SRuslan Bukin DWC_ASSERT_LOCKED(sc); 4195d43fd68SRuslan Bukin 4205d43fd68SRuslan Bukin if (ifp->if_drv_flags & IFF_DRV_RUNNING) 4215d43fd68SRuslan Bukin return; 4225d43fd68SRuslan Bukin 4235d43fd68SRuslan Bukin ifp->if_drv_flags |= IFF_DRV_RUNNING; 4245d43fd68SRuslan Bukin 4255d43fd68SRuslan Bukin dwc_setup_rxfilter(sc); 4265d43fd68SRuslan Bukin 4275d43fd68SRuslan Bukin /* Initializa DMA and enable transmitters */ 4285d43fd68SRuslan Bukin reg = READ4(sc, OPERATION_MODE); 4295d43fd68SRuslan Bukin reg |= (MODE_TSF | MODE_OSF | MODE_FUF); 4305d43fd68SRuslan Bukin reg &= ~(MODE_RSF); 4315d43fd68SRuslan Bukin reg |= (MODE_RTC_LEV32 << MODE_RTC_SHIFT); 4325d43fd68SRuslan Bukin WRITE4(sc, OPERATION_MODE, reg); 4335d43fd68SRuslan Bukin 4345d43fd68SRuslan Bukin WRITE4(sc, INTERRUPT_ENABLE, INT_EN_DEFAULT); 4355d43fd68SRuslan Bukin 4365d43fd68SRuslan Bukin /* Start DMA */ 4375d43fd68SRuslan Bukin reg = READ4(sc, OPERATION_MODE); 4385d43fd68SRuslan Bukin reg |= (MODE_ST | MODE_SR); 4395d43fd68SRuslan Bukin WRITE4(sc, OPERATION_MODE, reg); 4405d43fd68SRuslan Bukin 4415d43fd68SRuslan Bukin /* Enable transmitters */ 4425d43fd68SRuslan Bukin reg = READ4(sc, MAC_CONFIGURATION); 4435d43fd68SRuslan Bukin reg |= (CONF_JD | CONF_ACS | CONF_BE); 4445d43fd68SRuslan Bukin reg |= (CONF_TE | CONF_RE); 4455d43fd68SRuslan Bukin WRITE4(sc, MAC_CONFIGURATION, reg); 4465d43fd68SRuslan Bukin 4475d43fd68SRuslan Bukin /* 4485d43fd68SRuslan Bukin * Call mii_mediachg() which will call back into dwc_miibus_statchg() 4495d43fd68SRuslan Bukin * to set up the remaining config registers based on current media. 4505d43fd68SRuslan Bukin */ 4515d43fd68SRuslan Bukin mii_mediachg(sc->mii_softc); 4525d43fd68SRuslan Bukin callout_reset(&sc->dwc_callout, hz, dwc_tick, sc); 4535d43fd68SRuslan Bukin } 4545d43fd68SRuslan Bukin 4555d43fd68SRuslan Bukin static void 4565d43fd68SRuslan Bukin dwc_init(void *if_softc) 4575d43fd68SRuslan Bukin { 4585d43fd68SRuslan Bukin struct dwc_softc *sc = if_softc; 4595d43fd68SRuslan Bukin 4605d43fd68SRuslan Bukin DWC_LOCK(sc); 4615d43fd68SRuslan Bukin dwc_init_locked(sc); 4625d43fd68SRuslan Bukin DWC_UNLOCK(sc); 4635d43fd68SRuslan Bukin } 4645d43fd68SRuslan Bukin 4655d43fd68SRuslan Bukin inline static uint32_t 4665d43fd68SRuslan Bukin dwc_setup_rxdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr) 4675d43fd68SRuslan Bukin { 4685d43fd68SRuslan Bukin uint32_t nidx; 4695d43fd68SRuslan Bukin 4705d43fd68SRuslan Bukin sc->rxdesc_ring[idx].addr = (uint32_t)paddr; 4715d43fd68SRuslan Bukin nidx = next_rxidx(sc, idx); 4725d43fd68SRuslan Bukin sc->rxdesc_ring[idx].addr_next = sc->rxdesc_ring_paddr + \ 4735d43fd68SRuslan Bukin (nidx * sizeof(struct dwc_hwdesc)); 4745df53927SLuiz Otavio O Souza if (sc->mactype == DWC_GMAC_ALT_DESC) 4755df53927SLuiz Otavio O Souza sc->rxdesc_ring[idx].tdes1 = DDESC_CNTL_CHAINED | RX_MAX_PACKET; 4765df53927SLuiz Otavio O Souza else 4775d43fd68SRuslan Bukin sc->rxdesc_ring[idx].tdes1 = DDESC_RDES1_CHAINED | MCLBYTES; 4785d43fd68SRuslan Bukin 4795d43fd68SRuslan Bukin wmb(); 4805d43fd68SRuslan Bukin sc->rxdesc_ring[idx].tdes0 = DDESC_RDES0_OWN; 4815d43fd68SRuslan Bukin wmb(); 4825d43fd68SRuslan Bukin 4835d43fd68SRuslan Bukin return (nidx); 4845d43fd68SRuslan Bukin } 4855d43fd68SRuslan Bukin 4865d43fd68SRuslan Bukin static int 4875d43fd68SRuslan Bukin dwc_setup_rxbuf(struct dwc_softc *sc, int idx, struct mbuf *m) 4885d43fd68SRuslan Bukin { 4895d43fd68SRuslan Bukin struct bus_dma_segment seg; 4905d43fd68SRuslan Bukin int error, nsegs; 4915d43fd68SRuslan Bukin 4925d43fd68SRuslan Bukin m_adj(m, ETHER_ALIGN); 4935d43fd68SRuslan Bukin 4945d43fd68SRuslan Bukin error = bus_dmamap_load_mbuf_sg(sc->rxbuf_tag, sc->rxbuf_map[idx].map, 4955d43fd68SRuslan Bukin m, &seg, &nsegs, 0); 4965d43fd68SRuslan Bukin if (error != 0) { 4975d43fd68SRuslan Bukin return (error); 4985d43fd68SRuslan Bukin } 4995d43fd68SRuslan Bukin 5005d43fd68SRuslan Bukin KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 5015d43fd68SRuslan Bukin 5025d43fd68SRuslan Bukin bus_dmamap_sync(sc->rxbuf_tag, sc->rxbuf_map[idx].map, 5035d43fd68SRuslan Bukin BUS_DMASYNC_PREREAD); 5045d43fd68SRuslan Bukin 5055d43fd68SRuslan Bukin sc->rxbuf_map[idx].mbuf = m; 5065d43fd68SRuslan Bukin dwc_setup_rxdesc(sc, idx, seg.ds_addr); 5075d43fd68SRuslan Bukin 5085d43fd68SRuslan Bukin return (0); 5095d43fd68SRuslan Bukin } 5105d43fd68SRuslan Bukin 5115d43fd68SRuslan Bukin static struct mbuf * 5125d43fd68SRuslan Bukin dwc_alloc_mbufcl(struct dwc_softc *sc) 5135d43fd68SRuslan Bukin { 5145d43fd68SRuslan Bukin struct mbuf *m; 5155d43fd68SRuslan Bukin 5165d43fd68SRuslan Bukin m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 517db8a143aSRuslan Bukin if (m != NULL) 5185d43fd68SRuslan Bukin m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; 5195d43fd68SRuslan Bukin 5205d43fd68SRuslan Bukin return (m); 5215d43fd68SRuslan Bukin } 5225d43fd68SRuslan Bukin 5235d43fd68SRuslan Bukin static void 5245d43fd68SRuslan Bukin dwc_media_status(struct ifnet * ifp, struct ifmediareq *ifmr) 5255d43fd68SRuslan Bukin { 5265d43fd68SRuslan Bukin struct dwc_softc *sc; 5275d43fd68SRuslan Bukin struct mii_data *mii; 5285d43fd68SRuslan Bukin 5295d43fd68SRuslan Bukin sc = ifp->if_softc; 5305d43fd68SRuslan Bukin mii = sc->mii_softc; 5315d43fd68SRuslan Bukin DWC_LOCK(sc); 5325d43fd68SRuslan Bukin mii_pollstat(mii); 5335d43fd68SRuslan Bukin ifmr->ifm_active = mii->mii_media_active; 5345d43fd68SRuslan Bukin ifmr->ifm_status = mii->mii_media_status; 5355d43fd68SRuslan Bukin DWC_UNLOCK(sc); 5365d43fd68SRuslan Bukin } 5375d43fd68SRuslan Bukin 5385d43fd68SRuslan Bukin static int 5395d43fd68SRuslan Bukin dwc_media_change_locked(struct dwc_softc *sc) 5405d43fd68SRuslan Bukin { 5415d43fd68SRuslan Bukin 5425d43fd68SRuslan Bukin return (mii_mediachg(sc->mii_softc)); 5435d43fd68SRuslan Bukin } 5445d43fd68SRuslan Bukin 5455d43fd68SRuslan Bukin static int 5465d43fd68SRuslan Bukin dwc_media_change(struct ifnet * ifp) 5475d43fd68SRuslan Bukin { 5485d43fd68SRuslan Bukin struct dwc_softc *sc; 5495d43fd68SRuslan Bukin int error; 5505d43fd68SRuslan Bukin 5515d43fd68SRuslan Bukin sc = ifp->if_softc; 5525d43fd68SRuslan Bukin 5535d43fd68SRuslan Bukin DWC_LOCK(sc); 5545d43fd68SRuslan Bukin error = dwc_media_change_locked(sc); 5555d43fd68SRuslan Bukin DWC_UNLOCK(sc); 5565d43fd68SRuslan Bukin return (error); 5575d43fd68SRuslan Bukin } 5585d43fd68SRuslan Bukin 5595d43fd68SRuslan Bukin static const uint8_t nibbletab[] = { 5605d43fd68SRuslan Bukin /* 0x0 0000 -> 0000 */ 0x0, 5615d43fd68SRuslan Bukin /* 0x1 0001 -> 1000 */ 0x8, 5625d43fd68SRuslan Bukin /* 0x2 0010 -> 0100 */ 0x4, 5635d43fd68SRuslan Bukin /* 0x3 0011 -> 1100 */ 0xc, 5645d43fd68SRuslan Bukin /* 0x4 0100 -> 0010 */ 0x2, 5655d43fd68SRuslan Bukin /* 0x5 0101 -> 1010 */ 0xa, 5665d43fd68SRuslan Bukin /* 0x6 0110 -> 0110 */ 0x6, 5675d43fd68SRuslan Bukin /* 0x7 0111 -> 1110 */ 0xe, 5685d43fd68SRuslan Bukin /* 0x8 1000 -> 0001 */ 0x1, 5695d43fd68SRuslan Bukin /* 0x9 1001 -> 1001 */ 0x9, 5705d43fd68SRuslan Bukin /* 0xa 1010 -> 0101 */ 0x5, 5715d43fd68SRuslan Bukin /* 0xb 1011 -> 1101 */ 0xd, 5725d43fd68SRuslan Bukin /* 0xc 1100 -> 0011 */ 0x3, 5735d43fd68SRuslan Bukin /* 0xd 1101 -> 1011 */ 0xb, 5745d43fd68SRuslan Bukin /* 0xe 1110 -> 0111 */ 0x7, 5755d43fd68SRuslan Bukin /* 0xf 1111 -> 1111 */ 0xf, }; 5765d43fd68SRuslan Bukin 5775d43fd68SRuslan Bukin static uint8_t 5785d43fd68SRuslan Bukin bitreverse(uint8_t x) 5795d43fd68SRuslan Bukin { 5805d43fd68SRuslan Bukin 5815d43fd68SRuslan Bukin return (nibbletab[x & 0xf] << 4) | nibbletab[x >> 4]; 5825d43fd68SRuslan Bukin } 5835d43fd68SRuslan Bukin 5845d43fd68SRuslan Bukin static void 5855d43fd68SRuslan Bukin dwc_setup_rxfilter(struct dwc_softc *sc) 5865d43fd68SRuslan Bukin { 5875d43fd68SRuslan Bukin struct ifmultiaddr *ifma; 5885d43fd68SRuslan Bukin struct ifnet *ifp; 589ff0752c8SLuiz Otavio O Souza uint8_t *eaddr, val; 590ff0752c8SLuiz Otavio O Souza uint32_t crc, ffval, hashbit, hashreg, hi, lo, reg; 5915d43fd68SRuslan Bukin 5925d43fd68SRuslan Bukin DWC_ASSERT_LOCKED(sc); 5935d43fd68SRuslan Bukin 5945d43fd68SRuslan Bukin ifp = sc->ifp; 5955d43fd68SRuslan Bukin 5965d43fd68SRuslan Bukin /* 5975d43fd68SRuslan Bukin * Set the multicast (group) filter hash. 5985d43fd68SRuslan Bukin */ 5995d43fd68SRuslan Bukin if ((ifp->if_flags & IFF_ALLMULTI)) 6005d43fd68SRuslan Bukin ffval = (FRAME_FILTER_PM); 6015d43fd68SRuslan Bukin else { 6025d43fd68SRuslan Bukin ffval = (FRAME_FILTER_HMC); 6035d43fd68SRuslan Bukin if_maddr_rlock(ifp); 6045d43fd68SRuslan Bukin TAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) { 6055d43fd68SRuslan Bukin if (ifma->ifma_addr->sa_family != AF_LINK) 6065d43fd68SRuslan Bukin continue; 6075d43fd68SRuslan Bukin crc = ether_crc32_le(LLADDR((struct sockaddr_dl *) 6085d43fd68SRuslan Bukin ifma->ifma_addr), ETHER_ADDR_LEN); 6095d43fd68SRuslan Bukin 6105d43fd68SRuslan Bukin /* Take lower 8 bits and reverse it */ 6115d43fd68SRuslan Bukin val = bitreverse(~crc & 0xff); 6125d43fd68SRuslan Bukin hashreg = (val >> 5); 6135d43fd68SRuslan Bukin hashbit = (val & 31); 6145d43fd68SRuslan Bukin 6155d43fd68SRuslan Bukin reg = READ4(sc, HASH_TABLE_REG(hashreg)); 6165d43fd68SRuslan Bukin reg |= (1 << hashbit); 6175d43fd68SRuslan Bukin WRITE4(sc, HASH_TABLE_REG(hashreg), reg); 6185d43fd68SRuslan Bukin } 6195d43fd68SRuslan Bukin if_maddr_runlock(ifp); 6205d43fd68SRuslan Bukin } 6215d43fd68SRuslan Bukin 6225d43fd68SRuslan Bukin /* 6235d43fd68SRuslan Bukin * Set the individual address filter hash. 6245d43fd68SRuslan Bukin */ 6255d43fd68SRuslan Bukin if (ifp->if_flags & IFF_PROMISC) 6265d43fd68SRuslan Bukin ffval |= (FRAME_FILTER_PR); 6275d43fd68SRuslan Bukin 6285d43fd68SRuslan Bukin /* 6295d43fd68SRuslan Bukin * Set the primary address. 6305d43fd68SRuslan Bukin */ 6315d43fd68SRuslan Bukin eaddr = IF_LLADDR(ifp); 6325d43fd68SRuslan Bukin lo = eaddr[0] | (eaddr[1] << 8) | (eaddr[2] << 16) | 6335d43fd68SRuslan Bukin (eaddr[3] << 24); 6345d43fd68SRuslan Bukin hi = eaddr[4] | (eaddr[5] << 8); 6355d43fd68SRuslan Bukin WRITE4(sc, MAC_ADDRESS_LOW(0), lo); 6365d43fd68SRuslan Bukin WRITE4(sc, MAC_ADDRESS_HIGH(0), hi); 6375d43fd68SRuslan Bukin WRITE4(sc, MAC_FRAME_FILTER, ffval); 6385d43fd68SRuslan Bukin } 6395d43fd68SRuslan Bukin 6405d43fd68SRuslan Bukin static int 6415d43fd68SRuslan Bukin dwc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 6425d43fd68SRuslan Bukin { 6435d43fd68SRuslan Bukin struct dwc_softc *sc; 6445d43fd68SRuslan Bukin struct mii_data *mii; 6455d43fd68SRuslan Bukin struct ifreq *ifr; 6465d43fd68SRuslan Bukin int mask, error; 6475d43fd68SRuslan Bukin 6485d43fd68SRuslan Bukin sc = ifp->if_softc; 6495d43fd68SRuslan Bukin ifr = (struct ifreq *)data; 6505d43fd68SRuslan Bukin 6515d43fd68SRuslan Bukin error = 0; 6525d43fd68SRuslan Bukin switch (cmd) { 6535d43fd68SRuslan Bukin case SIOCSIFFLAGS: 6545d43fd68SRuslan Bukin DWC_LOCK(sc); 6555d43fd68SRuslan Bukin if (ifp->if_flags & IFF_UP) { 6565d43fd68SRuslan Bukin if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 6575d43fd68SRuslan Bukin if ((ifp->if_flags ^ sc->if_flags) & 6585d43fd68SRuslan Bukin (IFF_PROMISC | IFF_ALLMULTI)) 6595d43fd68SRuslan Bukin dwc_setup_rxfilter(sc); 6605d43fd68SRuslan Bukin } else { 6615d43fd68SRuslan Bukin if (!sc->is_detaching) 6625d43fd68SRuslan Bukin dwc_init_locked(sc); 6635d43fd68SRuslan Bukin } 6645d43fd68SRuslan Bukin } else { 6655d43fd68SRuslan Bukin if (ifp->if_drv_flags & IFF_DRV_RUNNING) 6665d43fd68SRuslan Bukin dwc_stop_locked(sc); 6675d43fd68SRuslan Bukin } 6685d43fd68SRuslan Bukin sc->if_flags = ifp->if_flags; 6695d43fd68SRuslan Bukin DWC_UNLOCK(sc); 6705d43fd68SRuslan Bukin break; 6715d43fd68SRuslan Bukin case SIOCADDMULTI: 6725d43fd68SRuslan Bukin case SIOCDELMULTI: 6735d43fd68SRuslan Bukin if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 6745d43fd68SRuslan Bukin DWC_LOCK(sc); 6755d43fd68SRuslan Bukin dwc_setup_rxfilter(sc); 6765d43fd68SRuslan Bukin DWC_UNLOCK(sc); 6775d43fd68SRuslan Bukin } 6785d43fd68SRuslan Bukin break; 6795d43fd68SRuslan Bukin case SIOCSIFMEDIA: 6805d43fd68SRuslan Bukin case SIOCGIFMEDIA: 6815d43fd68SRuslan Bukin mii = sc->mii_softc; 6825d43fd68SRuslan Bukin error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); 6835d43fd68SRuslan Bukin break; 6845d43fd68SRuslan Bukin case SIOCSIFCAP: 6855d43fd68SRuslan Bukin mask = ifp->if_capenable ^ ifr->ifr_reqcap; 6865d43fd68SRuslan Bukin if (mask & IFCAP_VLAN_MTU) { 6875d43fd68SRuslan Bukin /* No work to do except acknowledge the change took */ 6885d43fd68SRuslan Bukin ifp->if_capenable ^= IFCAP_VLAN_MTU; 6895d43fd68SRuslan Bukin } 6905d43fd68SRuslan Bukin break; 6915d43fd68SRuslan Bukin 6925d43fd68SRuslan Bukin default: 6935d43fd68SRuslan Bukin error = ether_ioctl(ifp, cmd, data); 6945d43fd68SRuslan Bukin break; 6955d43fd68SRuslan Bukin } 6965d43fd68SRuslan Bukin 6975d43fd68SRuslan Bukin return (error); 6985d43fd68SRuslan Bukin } 6995d43fd68SRuslan Bukin 7005d43fd68SRuslan Bukin static void 7015d43fd68SRuslan Bukin dwc_txfinish_locked(struct dwc_softc *sc) 7025d43fd68SRuslan Bukin { 7035d43fd68SRuslan Bukin struct dwc_bufmap *bmap; 7045d43fd68SRuslan Bukin struct dwc_hwdesc *desc; 7059500101cSLuiz Otavio O Souza struct ifnet *ifp; 7065d43fd68SRuslan Bukin 7075d43fd68SRuslan Bukin DWC_ASSERT_LOCKED(sc); 7085d43fd68SRuslan Bukin 709ecb3497fSLuiz Otavio O Souza ifp = sc->ifp; 7105d43fd68SRuslan Bukin while (sc->tx_idx_tail != sc->tx_idx_head) { 7115d43fd68SRuslan Bukin desc = &sc->txdesc_ring[sc->tx_idx_tail]; 7125d43fd68SRuslan Bukin if ((desc->tdes0 & DDESC_TDES0_OWN) != 0) 7135d43fd68SRuslan Bukin break; 7145d43fd68SRuslan Bukin bmap = &sc->txbuf_map[sc->tx_idx_tail]; 7155d43fd68SRuslan Bukin bus_dmamap_sync(sc->txbuf_tag, bmap->map, 7165d43fd68SRuslan Bukin BUS_DMASYNC_POSTWRITE); 7175d43fd68SRuslan Bukin bus_dmamap_unload(sc->txbuf_tag, bmap->map); 7185d43fd68SRuslan Bukin m_freem(bmap->mbuf); 7195d43fd68SRuslan Bukin bmap->mbuf = NULL; 7205d43fd68SRuslan Bukin dwc_setup_txdesc(sc, sc->tx_idx_tail, 0, 0); 7215d43fd68SRuslan Bukin sc->tx_idx_tail = next_txidx(sc, sc->tx_idx_tail); 7229500101cSLuiz Otavio O Souza ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 723a5221d68SLuiz Otavio O Souza if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 7245d43fd68SRuslan Bukin } 7255d43fd68SRuslan Bukin 7265d43fd68SRuslan Bukin /* If there are no buffers outstanding, muzzle the watchdog. */ 7275d43fd68SRuslan Bukin if (sc->tx_idx_tail == sc->tx_idx_head) { 7285d43fd68SRuslan Bukin sc->tx_watchdog_count = 0; 7295d43fd68SRuslan Bukin } 7305d43fd68SRuslan Bukin } 7315d43fd68SRuslan Bukin 7325d43fd68SRuslan Bukin static void 7335d43fd68SRuslan Bukin dwc_rxfinish_locked(struct dwc_softc *sc) 7345d43fd68SRuslan Bukin { 7355d43fd68SRuslan Bukin struct ifnet *ifp; 7365d43fd68SRuslan Bukin struct mbuf *m0; 7375d43fd68SRuslan Bukin struct mbuf *m; 738ff0752c8SLuiz Otavio O Souza int error, idx, len; 739ff0752c8SLuiz Otavio O Souza uint32_t rdes0; 7405d43fd68SRuslan Bukin 7415d43fd68SRuslan Bukin ifp = sc->ifp; 7425d43fd68SRuslan Bukin 7435d43fd68SRuslan Bukin for (;;) { 7445d43fd68SRuslan Bukin idx = sc->rx_idx; 7455d43fd68SRuslan Bukin 7465d43fd68SRuslan Bukin rdes0 = sc->rxdesc_ring[idx].tdes0; 7475d43fd68SRuslan Bukin if ((rdes0 & DDESC_RDES0_OWN) != 0) 7485d43fd68SRuslan Bukin break; 7495d43fd68SRuslan Bukin 7505d43fd68SRuslan Bukin bus_dmamap_sync(sc->rxbuf_tag, sc->rxbuf_map[idx].map, 7515d43fd68SRuslan Bukin BUS_DMASYNC_POSTREAD); 7525d43fd68SRuslan Bukin bus_dmamap_unload(sc->rxbuf_tag, sc->rxbuf_map[idx].map); 7535d43fd68SRuslan Bukin 7545d43fd68SRuslan Bukin len = (rdes0 >> DDESC_RDES0_FL_SHIFT) & DDESC_RDES0_FL_MASK; 7555d43fd68SRuslan Bukin if (len != 0) { 7565d43fd68SRuslan Bukin m = sc->rxbuf_map[idx].mbuf; 7575d43fd68SRuslan Bukin m->m_pkthdr.rcvif = ifp; 7585d43fd68SRuslan Bukin m->m_pkthdr.len = len; 7595d43fd68SRuslan Bukin m->m_len = len; 76008c95c53SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 7615d43fd68SRuslan Bukin 7625d43fd68SRuslan Bukin DWC_UNLOCK(sc); 7635d43fd68SRuslan Bukin (*ifp->if_input)(ifp, m); 7645d43fd68SRuslan Bukin DWC_LOCK(sc); 7655d43fd68SRuslan Bukin } else { 7665d43fd68SRuslan Bukin /* XXX Zero-length packet ? */ 7675d43fd68SRuslan Bukin } 7685d43fd68SRuslan Bukin 7695d43fd68SRuslan Bukin if ((m0 = dwc_alloc_mbufcl(sc)) != NULL) { 7705d43fd68SRuslan Bukin if ((error = dwc_setup_rxbuf(sc, idx, m0)) != 0) { 7715d43fd68SRuslan Bukin /* 7725d43fd68SRuslan Bukin * XXX Now what? 7735d43fd68SRuslan Bukin * We've got a hole in the rx ring. 7745d43fd68SRuslan Bukin */ 7755d43fd68SRuslan Bukin } 7765d43fd68SRuslan Bukin } else 7775d43fd68SRuslan Bukin if_inc_counter(sc->ifp, IFCOUNTER_IQDROPS, 1); 7785d43fd68SRuslan Bukin 7795d43fd68SRuslan Bukin sc->rx_idx = next_rxidx(sc, sc->rx_idx); 7805d43fd68SRuslan Bukin } 7815d43fd68SRuslan Bukin } 7825d43fd68SRuslan Bukin 7835d43fd68SRuslan Bukin static void 7845d43fd68SRuslan Bukin dwc_intr(void *arg) 7855d43fd68SRuslan Bukin { 7865d43fd68SRuslan Bukin struct dwc_softc *sc; 7875d43fd68SRuslan Bukin uint32_t reg; 7885d43fd68SRuslan Bukin 7895d43fd68SRuslan Bukin sc = arg; 7905d43fd68SRuslan Bukin 7915d43fd68SRuslan Bukin DWC_LOCK(sc); 7925d43fd68SRuslan Bukin 7935d43fd68SRuslan Bukin reg = READ4(sc, INTERRUPT_STATUS); 7948fbc5d18SLuiz Otavio O Souza if (reg) 7955d43fd68SRuslan Bukin READ4(sc, SGMII_RGMII_SMII_CTRL_STATUS); 7965d43fd68SRuslan Bukin 7975d43fd68SRuslan Bukin reg = READ4(sc, DMA_STATUS); 7985d43fd68SRuslan Bukin if (reg & DMA_STATUS_NIS) { 7995d43fd68SRuslan Bukin if (reg & DMA_STATUS_RI) 8005d43fd68SRuslan Bukin dwc_rxfinish_locked(sc); 8015d43fd68SRuslan Bukin 8029500101cSLuiz Otavio O Souza if (reg & DMA_STATUS_TI) { 8035d43fd68SRuslan Bukin dwc_txfinish_locked(sc); 8049500101cSLuiz Otavio O Souza dwc_txstart_locked(sc); 8059500101cSLuiz Otavio O Souza } 8065d43fd68SRuslan Bukin } 8075d43fd68SRuslan Bukin 8085d43fd68SRuslan Bukin if (reg & DMA_STATUS_AIS) { 8095d43fd68SRuslan Bukin if (reg & DMA_STATUS_FBI) { 8105d43fd68SRuslan Bukin /* Fatal bus error */ 8115d43fd68SRuslan Bukin device_printf(sc->dev, 8125d43fd68SRuslan Bukin "Ethernet DMA error, restarting controller.\n"); 8135d43fd68SRuslan Bukin dwc_stop_locked(sc); 8145d43fd68SRuslan Bukin dwc_init_locked(sc); 8155d43fd68SRuslan Bukin } 8165d43fd68SRuslan Bukin } 8175d43fd68SRuslan Bukin 8185d43fd68SRuslan Bukin WRITE4(sc, DMA_STATUS, reg & DMA_STATUS_INTR_MASK); 8195d43fd68SRuslan Bukin DWC_UNLOCK(sc); 8205d43fd68SRuslan Bukin } 8215d43fd68SRuslan Bukin 8225d43fd68SRuslan Bukin static int 8235d43fd68SRuslan Bukin setup_dma(struct dwc_softc *sc) 8245d43fd68SRuslan Bukin { 8255d43fd68SRuslan Bukin struct mbuf *m; 8265d43fd68SRuslan Bukin int error; 8275d43fd68SRuslan Bukin int nidx; 8285d43fd68SRuslan Bukin int idx; 8295d43fd68SRuslan Bukin 8305d43fd68SRuslan Bukin /* 8315d43fd68SRuslan Bukin * Set up TX descriptor ring, descriptors, and dma maps. 8325d43fd68SRuslan Bukin */ 8335d43fd68SRuslan Bukin error = bus_dma_tag_create( 8345d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 8355d43fd68SRuslan Bukin DWC_DESC_RING_ALIGN, 0, /* alignment, boundary */ 8365d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 8375d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 8385d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 8395d43fd68SRuslan Bukin TX_DESC_SIZE, 1, /* maxsize, nsegments */ 8405d43fd68SRuslan Bukin TX_DESC_SIZE, /* maxsegsize */ 8415d43fd68SRuslan Bukin 0, /* flags */ 8425d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 8435d43fd68SRuslan Bukin &sc->txdesc_tag); 8445d43fd68SRuslan Bukin if (error != 0) { 8455d43fd68SRuslan Bukin device_printf(sc->dev, 8465d43fd68SRuslan Bukin "could not create TX ring DMA tag.\n"); 8475d43fd68SRuslan Bukin goto out; 8485d43fd68SRuslan Bukin } 8495d43fd68SRuslan Bukin 8505d43fd68SRuslan Bukin error = bus_dmamem_alloc(sc->txdesc_tag, (void**)&sc->txdesc_ring, 8515d43fd68SRuslan Bukin BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, 8525d43fd68SRuslan Bukin &sc->txdesc_map); 8535d43fd68SRuslan Bukin if (error != 0) { 8545d43fd68SRuslan Bukin device_printf(sc->dev, 8555d43fd68SRuslan Bukin "could not allocate TX descriptor ring.\n"); 8565d43fd68SRuslan Bukin goto out; 8575d43fd68SRuslan Bukin } 8585d43fd68SRuslan Bukin 8595d43fd68SRuslan Bukin error = bus_dmamap_load(sc->txdesc_tag, sc->txdesc_map, 8605d43fd68SRuslan Bukin sc->txdesc_ring, TX_DESC_SIZE, dwc_get1paddr, 8615d43fd68SRuslan Bukin &sc->txdesc_ring_paddr, 0); 8625d43fd68SRuslan Bukin if (error != 0) { 8635d43fd68SRuslan Bukin device_printf(sc->dev, 8645d43fd68SRuslan Bukin "could not load TX descriptor ring map.\n"); 8655d43fd68SRuslan Bukin goto out; 8665d43fd68SRuslan Bukin } 8675d43fd68SRuslan Bukin 8685d43fd68SRuslan Bukin for (idx = 0; idx < TX_DESC_COUNT; idx++) { 8695d43fd68SRuslan Bukin nidx = next_txidx(sc, idx); 8701d7a7309SLuiz Otavio O Souza sc->txdesc_ring[idx].addr_next = sc->txdesc_ring_paddr + 8715d43fd68SRuslan Bukin (nidx * sizeof(struct dwc_hwdesc)); 8725d43fd68SRuslan Bukin } 8735d43fd68SRuslan Bukin 8745d43fd68SRuslan Bukin error = bus_dma_tag_create( 8755d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 8765d43fd68SRuslan Bukin 1, 0, /* alignment, boundary */ 8775d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 8785d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 8795d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 8805d43fd68SRuslan Bukin MCLBYTES, 1, /* maxsize, nsegments */ 8815d43fd68SRuslan Bukin MCLBYTES, /* maxsegsize */ 8825d43fd68SRuslan Bukin 0, /* flags */ 8835d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 8845d43fd68SRuslan Bukin &sc->txbuf_tag); 8855d43fd68SRuslan Bukin if (error != 0) { 8865d43fd68SRuslan Bukin device_printf(sc->dev, 8875d43fd68SRuslan Bukin "could not create TX ring DMA tag.\n"); 8885d43fd68SRuslan Bukin goto out; 8895d43fd68SRuslan Bukin } 8905d43fd68SRuslan Bukin 8915d43fd68SRuslan Bukin for (idx = 0; idx < TX_DESC_COUNT; idx++) { 8925d43fd68SRuslan Bukin error = bus_dmamap_create(sc->txbuf_tag, BUS_DMA_COHERENT, 8935d43fd68SRuslan Bukin &sc->txbuf_map[idx].map); 8945d43fd68SRuslan Bukin if (error != 0) { 8955d43fd68SRuslan Bukin device_printf(sc->dev, 8965d43fd68SRuslan Bukin "could not create TX buffer DMA map.\n"); 8975d43fd68SRuslan Bukin goto out; 8985d43fd68SRuslan Bukin } 8995d43fd68SRuslan Bukin dwc_setup_txdesc(sc, idx, 0, 0); 9005d43fd68SRuslan Bukin } 9015d43fd68SRuslan Bukin 9025d43fd68SRuslan Bukin /* 9035d43fd68SRuslan Bukin * Set up RX descriptor ring, descriptors, dma maps, and mbufs. 9045d43fd68SRuslan Bukin */ 9055d43fd68SRuslan Bukin error = bus_dma_tag_create( 9065d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 9075d43fd68SRuslan Bukin DWC_DESC_RING_ALIGN, 0, /* alignment, boundary */ 9085d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 9095d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 9105d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 9115d43fd68SRuslan Bukin RX_DESC_SIZE, 1, /* maxsize, nsegments */ 9125d43fd68SRuslan Bukin RX_DESC_SIZE, /* maxsegsize */ 9135d43fd68SRuslan Bukin 0, /* flags */ 9145d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 9155d43fd68SRuslan Bukin &sc->rxdesc_tag); 9165d43fd68SRuslan Bukin if (error != 0) { 9175d43fd68SRuslan Bukin device_printf(sc->dev, 9185d43fd68SRuslan Bukin "could not create RX ring DMA tag.\n"); 9195d43fd68SRuslan Bukin goto out; 9205d43fd68SRuslan Bukin } 9215d43fd68SRuslan Bukin 9225d43fd68SRuslan Bukin error = bus_dmamem_alloc(sc->rxdesc_tag, (void **)&sc->rxdesc_ring, 9235d43fd68SRuslan Bukin BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, 9245d43fd68SRuslan Bukin &sc->rxdesc_map); 9255d43fd68SRuslan Bukin if (error != 0) { 9265d43fd68SRuslan Bukin device_printf(sc->dev, 9275d43fd68SRuslan Bukin "could not allocate RX descriptor ring.\n"); 9285d43fd68SRuslan Bukin goto out; 9295d43fd68SRuslan Bukin } 9305d43fd68SRuslan Bukin 9315d43fd68SRuslan Bukin error = bus_dmamap_load(sc->rxdesc_tag, sc->rxdesc_map, 9325d43fd68SRuslan Bukin sc->rxdesc_ring, RX_DESC_SIZE, dwc_get1paddr, 9335d43fd68SRuslan Bukin &sc->rxdesc_ring_paddr, 0); 9345d43fd68SRuslan Bukin if (error != 0) { 9355d43fd68SRuslan Bukin device_printf(sc->dev, 9365d43fd68SRuslan Bukin "could not load RX descriptor ring map.\n"); 9375d43fd68SRuslan Bukin goto out; 9385d43fd68SRuslan Bukin } 9395d43fd68SRuslan Bukin 9405d43fd68SRuslan Bukin error = bus_dma_tag_create( 9415d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 9425d43fd68SRuslan Bukin 1, 0, /* alignment, boundary */ 9435d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 9445d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 9455d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 9465d43fd68SRuslan Bukin MCLBYTES, 1, /* maxsize, nsegments */ 9475d43fd68SRuslan Bukin MCLBYTES, /* maxsegsize */ 9485d43fd68SRuslan Bukin 0, /* flags */ 9495d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 9505d43fd68SRuslan Bukin &sc->rxbuf_tag); 9515d43fd68SRuslan Bukin if (error != 0) { 9525d43fd68SRuslan Bukin device_printf(sc->dev, 9535d43fd68SRuslan Bukin "could not create RX buf DMA tag.\n"); 9545d43fd68SRuslan Bukin goto out; 9555d43fd68SRuslan Bukin } 9565d43fd68SRuslan Bukin 9575d43fd68SRuslan Bukin for (idx = 0; idx < RX_DESC_COUNT; idx++) { 9585d43fd68SRuslan Bukin error = bus_dmamap_create(sc->rxbuf_tag, BUS_DMA_COHERENT, 9595d43fd68SRuslan Bukin &sc->rxbuf_map[idx].map); 9605d43fd68SRuslan Bukin if (error != 0) { 9615d43fd68SRuslan Bukin device_printf(sc->dev, 9625d43fd68SRuslan Bukin "could not create RX buffer DMA map.\n"); 9635d43fd68SRuslan Bukin goto out; 9645d43fd68SRuslan Bukin } 9655d43fd68SRuslan Bukin if ((m = dwc_alloc_mbufcl(sc)) == NULL) { 9665d43fd68SRuslan Bukin device_printf(sc->dev, "Could not alloc mbuf\n"); 9675d43fd68SRuslan Bukin error = ENOMEM; 9685d43fd68SRuslan Bukin goto out; 9695d43fd68SRuslan Bukin } 9705d43fd68SRuslan Bukin if ((error = dwc_setup_rxbuf(sc, idx, m)) != 0) { 9715d43fd68SRuslan Bukin device_printf(sc->dev, 9725d43fd68SRuslan Bukin "could not create new RX buffer.\n"); 9735d43fd68SRuslan Bukin goto out; 9745d43fd68SRuslan Bukin } 9755d43fd68SRuslan Bukin } 9765d43fd68SRuslan Bukin 9775d43fd68SRuslan Bukin out: 9785d43fd68SRuslan Bukin if (error != 0) 9795d43fd68SRuslan Bukin return (ENXIO); 9805d43fd68SRuslan Bukin 9815d43fd68SRuslan Bukin return (0); 9825d43fd68SRuslan Bukin } 9835d43fd68SRuslan Bukin 9845d43fd68SRuslan Bukin static int 9855d43fd68SRuslan Bukin dwc_get_hwaddr(struct dwc_softc *sc, uint8_t *hwaddr) 9865d43fd68SRuslan Bukin { 987ff0752c8SLuiz Otavio O Souza uint32_t hi, lo, rnd; 9885d43fd68SRuslan Bukin 9895d43fd68SRuslan Bukin /* 9905d43fd68SRuslan Bukin * Try to recover a MAC address from the running hardware. If there's 9915d43fd68SRuslan Bukin * something non-zero there, assume the bootloader did the right thing 9925d43fd68SRuslan Bukin * and just use it. 9935d43fd68SRuslan Bukin * 9945d43fd68SRuslan Bukin * Otherwise, set the address to a convenient locally assigned address, 9955d43fd68SRuslan Bukin * 'bsd' + random 24 low-order bits. 'b' is 0x62, which has the locally 9965d43fd68SRuslan Bukin * assigned bit set, and the broadcast/multicast bit clear. 9975d43fd68SRuslan Bukin */ 9985d43fd68SRuslan Bukin lo = READ4(sc, MAC_ADDRESS_LOW(0)); 9995d43fd68SRuslan Bukin hi = READ4(sc, MAC_ADDRESS_HIGH(0)) & 0xffff; 10005d43fd68SRuslan Bukin if ((lo != 0xffffffff) || (hi != 0xffff)) { 10015d43fd68SRuslan Bukin hwaddr[0] = (lo >> 0) & 0xff; 10025d43fd68SRuslan Bukin hwaddr[1] = (lo >> 8) & 0xff; 10035d43fd68SRuslan Bukin hwaddr[2] = (lo >> 16) & 0xff; 10045d43fd68SRuslan Bukin hwaddr[3] = (lo >> 24) & 0xff; 10055d43fd68SRuslan Bukin hwaddr[4] = (hi >> 0) & 0xff; 10065d43fd68SRuslan Bukin hwaddr[5] = (hi >> 8) & 0xff; 10075d43fd68SRuslan Bukin } else { 10085d43fd68SRuslan Bukin rnd = arc4random() & 0x00ffffff; 10095d43fd68SRuslan Bukin hwaddr[0] = 'b'; 10105d43fd68SRuslan Bukin hwaddr[1] = 's'; 10115d43fd68SRuslan Bukin hwaddr[2] = 'd'; 10125d43fd68SRuslan Bukin hwaddr[3] = rnd >> 16; 10135d43fd68SRuslan Bukin hwaddr[4] = rnd >> 8; 10145d43fd68SRuslan Bukin hwaddr[5] = rnd >> 0; 10155d43fd68SRuslan Bukin } 10165d43fd68SRuslan Bukin 10175d43fd68SRuslan Bukin return (0); 10185d43fd68SRuslan Bukin } 10195d43fd68SRuslan Bukin 1020d7acb49aSJared McNeill #define GPIO_ACTIVE_LOW 1 1021d7acb49aSJared McNeill 1022d7acb49aSJared McNeill static int 1023d7acb49aSJared McNeill dwc_reset(device_t dev) 1024d7acb49aSJared McNeill { 1025d7acb49aSJared McNeill pcell_t gpio_prop[4]; 1026d7acb49aSJared McNeill pcell_t delay_prop[3]; 1027d7acb49aSJared McNeill phandle_t node, gpio_node; 1028d7acb49aSJared McNeill device_t gpio; 1029d7acb49aSJared McNeill uint32_t pin, flags; 1030d7acb49aSJared McNeill uint32_t pin_value; 1031d7acb49aSJared McNeill 1032d7acb49aSJared McNeill node = ofw_bus_get_node(dev); 1033d7acb49aSJared McNeill if (OF_getencprop(node, "snps,reset-gpio", 1034d7acb49aSJared McNeill gpio_prop, sizeof(gpio_prop)) <= 0) 1035d7acb49aSJared McNeill return (0); 1036d7acb49aSJared McNeill 1037d7acb49aSJared McNeill if (OF_getencprop(node, "snps,reset-delays-us", 1038d7acb49aSJared McNeill delay_prop, sizeof(delay_prop)) <= 0) { 1039d7acb49aSJared McNeill device_printf(dev, 1040d7acb49aSJared McNeill "Wrong property for snps,reset-delays-us"); 1041d7acb49aSJared McNeill return (ENXIO); 1042d7acb49aSJared McNeill } 1043d7acb49aSJared McNeill 1044d7acb49aSJared McNeill gpio_node = OF_node_from_xref(gpio_prop[0]); 1045d7acb49aSJared McNeill if ((gpio = OF_device_from_xref(gpio_prop[0])) == NULL) { 1046d7acb49aSJared McNeill device_printf(dev, 1047d7acb49aSJared McNeill "Can't find gpio controller for phy reset\n"); 1048d7acb49aSJared McNeill return (ENXIO); 1049d7acb49aSJared McNeill } 1050d7acb49aSJared McNeill 1051d7acb49aSJared McNeill if (GPIO_MAP_GPIOS(gpio, node, gpio_node, 1052*73a1170aSPedro F. Giffuni nitems(gpio_prop) - 1, 1053d7acb49aSJared McNeill gpio_prop + 1, &pin, &flags) != 0) { 1054d7acb49aSJared McNeill device_printf(dev, "Can't map gpio for phy reset\n"); 1055d7acb49aSJared McNeill return (ENXIO); 1056d7acb49aSJared McNeill } 1057d7acb49aSJared McNeill 1058d7acb49aSJared McNeill pin_value = GPIO_PIN_LOW; 1059d7acb49aSJared McNeill if (OF_hasprop(node, "snps,reset-active-low")) 1060d7acb49aSJared McNeill pin_value = GPIO_PIN_HIGH; 1061d7acb49aSJared McNeill 1062d7acb49aSJared McNeill if (flags & GPIO_ACTIVE_LOW) 1063d7acb49aSJared McNeill pin_value = !pin_value; 1064d7acb49aSJared McNeill 1065d7acb49aSJared McNeill GPIO_PIN_SETFLAGS(gpio, pin, GPIO_PIN_OUTPUT); 1066d7acb49aSJared McNeill GPIO_PIN_SET(gpio, pin, pin_value); 1067d7acb49aSJared McNeill DELAY(delay_prop[0]); 1068d7acb49aSJared McNeill GPIO_PIN_SET(gpio, pin, !pin_value); 1069d7acb49aSJared McNeill DELAY(delay_prop[1]); 1070d7acb49aSJared McNeill GPIO_PIN_SET(gpio, pin, pin_value); 1071d7acb49aSJared McNeill DELAY(delay_prop[2]); 1072d7acb49aSJared McNeill 1073d7acb49aSJared McNeill return (0); 1074d7acb49aSJared McNeill } 1075d7acb49aSJared McNeill 10766a05f063SJared McNeill #ifdef EXT_RESOURCES 10776a05f063SJared McNeill static int 10786a05f063SJared McNeill dwc_clock_init(device_t dev) 10796a05f063SJared McNeill { 10806a05f063SJared McNeill hwreset_t rst; 10816a05f063SJared McNeill clk_t clk; 10826a05f063SJared McNeill int error; 10836a05f063SJared McNeill 10846a05f063SJared McNeill /* Enable clock */ 10856a05f063SJared McNeill if (clk_get_by_ofw_name(dev, "stmmaceth", &clk) == 0) { 10866a05f063SJared McNeill error = clk_enable(clk); 10876a05f063SJared McNeill if (error != 0) { 10886a05f063SJared McNeill device_printf(dev, "could not enable main clock\n"); 10896a05f063SJared McNeill return (error); 10906a05f063SJared McNeill } 10916a05f063SJared McNeill } 10926a05f063SJared McNeill 10936a05f063SJared McNeill /* De-assert reset */ 10946a05f063SJared McNeill if (hwreset_get_by_ofw_name(dev, "stmmaceth", &rst) == 0) { 10956a05f063SJared McNeill error = hwreset_deassert(rst); 10966a05f063SJared McNeill if (error != 0) { 10976a05f063SJared McNeill device_printf(dev, "could not de-assert reset\n"); 10986a05f063SJared McNeill return (error); 10996a05f063SJared McNeill } 11006a05f063SJared McNeill } 11016a05f063SJared McNeill 11026a05f063SJared McNeill return (0); 11036a05f063SJared McNeill } 11046a05f063SJared McNeill #endif 11056a05f063SJared McNeill 11065d43fd68SRuslan Bukin static int 11075d43fd68SRuslan Bukin dwc_probe(device_t dev) 11085d43fd68SRuslan Bukin { 11095d43fd68SRuslan Bukin 11105d43fd68SRuslan Bukin if (!ofw_bus_status_okay(dev)) 11115d43fd68SRuslan Bukin return (ENXIO); 11125d43fd68SRuslan Bukin 11135d43fd68SRuslan Bukin if (!ofw_bus_is_compatible(dev, "snps,dwmac")) 11145d43fd68SRuslan Bukin return (ENXIO); 11155d43fd68SRuslan Bukin 11165d43fd68SRuslan Bukin device_set_desc(dev, "Gigabit Ethernet Controller"); 11175d43fd68SRuslan Bukin return (BUS_PROBE_DEFAULT); 11185d43fd68SRuslan Bukin } 11195d43fd68SRuslan Bukin 11205d43fd68SRuslan Bukin static int 11215d43fd68SRuslan Bukin dwc_attach(device_t dev) 11225d43fd68SRuslan Bukin { 11235d43fd68SRuslan Bukin uint8_t macaddr[ETHER_ADDR_LEN]; 11245d43fd68SRuslan Bukin struct dwc_softc *sc; 11255d43fd68SRuslan Bukin struct ifnet *ifp; 1126ff0752c8SLuiz Otavio O Souza int error, i; 1127ff0752c8SLuiz Otavio O Souza uint32_t reg; 11285d43fd68SRuslan Bukin 11295d43fd68SRuslan Bukin sc = device_get_softc(dev); 11305d43fd68SRuslan Bukin sc->dev = dev; 11315d43fd68SRuslan Bukin sc->rx_idx = 0; 11325d43fd68SRuslan Bukin sc->txcount = TX_DESC_COUNT; 11335df53927SLuiz Otavio O Souza sc->mii_clk = IF_DWC_MII_CLK(dev); 11345df53927SLuiz Otavio O Souza sc->mactype = IF_DWC_MAC_TYPE(dev); 11355df53927SLuiz Otavio O Souza 11365df53927SLuiz Otavio O Souza if (IF_DWC_INIT(dev) != 0) 11375df53927SLuiz Otavio O Souza return (ENXIO); 11385d43fd68SRuslan Bukin 11396a05f063SJared McNeill #ifdef EXT_RESOURCES 11406a05f063SJared McNeill if (dwc_clock_init(dev) != 0) 11416a05f063SJared McNeill return (ENXIO); 11426a05f063SJared McNeill #endif 11436a05f063SJared McNeill 11445d43fd68SRuslan Bukin if (bus_alloc_resources(dev, dwc_spec, sc->res)) { 11455d43fd68SRuslan Bukin device_printf(dev, "could not allocate resources\n"); 11465d43fd68SRuslan Bukin return (ENXIO); 11475d43fd68SRuslan Bukin } 11485d43fd68SRuslan Bukin 11495d43fd68SRuslan Bukin /* Memory interface */ 11505d43fd68SRuslan Bukin sc->bst = rman_get_bustag(sc->res[0]); 11515d43fd68SRuslan Bukin sc->bsh = rman_get_bushandle(sc->res[0]); 11525d43fd68SRuslan Bukin 11535d43fd68SRuslan Bukin /* Read MAC before reset */ 11545d43fd68SRuslan Bukin if (dwc_get_hwaddr(sc, macaddr)) { 11555d43fd68SRuslan Bukin device_printf(sc->dev, "can't get mac\n"); 11565d43fd68SRuslan Bukin return (ENXIO); 11575d43fd68SRuslan Bukin } 11585d43fd68SRuslan Bukin 1159d7acb49aSJared McNeill /* Reset the PHY if needed */ 1160d7acb49aSJared McNeill if (dwc_reset(dev) != 0) { 1161d7acb49aSJared McNeill device_printf(dev, "Can't reset the PHY\n"); 1162d7acb49aSJared McNeill return (ENXIO); 1163d7acb49aSJared McNeill } 1164d7acb49aSJared McNeill 11655d43fd68SRuslan Bukin /* Reset */ 11665d43fd68SRuslan Bukin reg = READ4(sc, BUS_MODE); 11675d43fd68SRuslan Bukin reg |= (BUS_MODE_SWR); 11685d43fd68SRuslan Bukin WRITE4(sc, BUS_MODE, reg); 11695d43fd68SRuslan Bukin 1170d8e5258dSRuslan Bukin for (i = 0; i < MAC_RESET_TIMEOUT; i++) { 11715d43fd68SRuslan Bukin if ((READ4(sc, BUS_MODE) & BUS_MODE_SWR) == 0) 11725d43fd68SRuslan Bukin break; 11735d43fd68SRuslan Bukin DELAY(10); 11745d43fd68SRuslan Bukin } 1175d8e5258dSRuslan Bukin if (i >= MAC_RESET_TIMEOUT) { 11765d43fd68SRuslan Bukin device_printf(sc->dev, "Can't reset DWC.\n"); 11775d43fd68SRuslan Bukin return (ENXIO); 11785d43fd68SRuslan Bukin } 11795d43fd68SRuslan Bukin 11805df53927SLuiz Otavio O Souza if (sc->mactype == DWC_GMAC_ALT_DESC) { 11815df53927SLuiz Otavio O Souza reg = BUS_MODE_FIXEDBURST; 11825df53927SLuiz Otavio O Souza reg |= (BUS_MODE_PRIORXTX_41 << BUS_MODE_PRIORXTX_SHIFT); 11835df53927SLuiz Otavio O Souza } else 11845df53927SLuiz Otavio O Souza reg = (BUS_MODE_EIGHTXPBL); 11855d43fd68SRuslan Bukin reg |= (BUS_MODE_PBL_BEATS_8 << BUS_MODE_PBL_SHIFT); 11865d43fd68SRuslan Bukin WRITE4(sc, BUS_MODE, reg); 11875d43fd68SRuslan Bukin 11885d43fd68SRuslan Bukin /* 11895d43fd68SRuslan Bukin * DMA must be stop while changing descriptor list addresses. 11905d43fd68SRuslan Bukin */ 11915d43fd68SRuslan Bukin reg = READ4(sc, OPERATION_MODE); 11925d43fd68SRuslan Bukin reg &= ~(MODE_ST | MODE_SR); 11935d43fd68SRuslan Bukin WRITE4(sc, OPERATION_MODE, reg); 11945d43fd68SRuslan Bukin 11955d43fd68SRuslan Bukin if (setup_dma(sc)) 11965d43fd68SRuslan Bukin return (ENXIO); 11975d43fd68SRuslan Bukin 11985d43fd68SRuslan Bukin /* Setup addresses */ 11995d43fd68SRuslan Bukin WRITE4(sc, RX_DESCR_LIST_ADDR, sc->rxdesc_ring_paddr); 12005d43fd68SRuslan Bukin WRITE4(sc, TX_DESCR_LIST_ADDR, sc->txdesc_ring_paddr); 12015d43fd68SRuslan Bukin 1202d8e5258dSRuslan Bukin mtx_init(&sc->mtx, device_get_nameunit(sc->dev), 1203d8e5258dSRuslan Bukin MTX_NETWORK_LOCK, MTX_DEF); 1204d8e5258dSRuslan Bukin 1205d8e5258dSRuslan Bukin callout_init_mtx(&sc->dwc_callout, &sc->mtx, 0); 1206d8e5258dSRuslan Bukin 1207d8e5258dSRuslan Bukin /* Setup interrupt handler. */ 1208d8e5258dSRuslan Bukin error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_NET | INTR_MPSAFE, 1209d8e5258dSRuslan Bukin NULL, dwc_intr, sc, &sc->intr_cookie); 1210d8e5258dSRuslan Bukin if (error != 0) { 1211d8e5258dSRuslan Bukin device_printf(dev, "could not setup interrupt handler.\n"); 1212d8e5258dSRuslan Bukin return (ENXIO); 1213d8e5258dSRuslan Bukin } 1214d8e5258dSRuslan Bukin 12155d43fd68SRuslan Bukin /* Set up the ethernet interface. */ 12165d43fd68SRuslan Bukin sc->ifp = ifp = if_alloc(IFT_ETHER); 12175d43fd68SRuslan Bukin 12185d43fd68SRuslan Bukin ifp->if_softc = sc; 12195d43fd68SRuslan Bukin if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 12205d43fd68SRuslan Bukin ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 12215d43fd68SRuslan Bukin ifp->if_capabilities = IFCAP_VLAN_MTU; 12225d43fd68SRuslan Bukin ifp->if_capenable = ifp->if_capabilities; 12235d43fd68SRuslan Bukin ifp->if_start = dwc_txstart; 12245d43fd68SRuslan Bukin ifp->if_ioctl = dwc_ioctl; 12255d43fd68SRuslan Bukin ifp->if_init = dwc_init; 12265d43fd68SRuslan Bukin IFQ_SET_MAXLEN(&ifp->if_snd, TX_DESC_COUNT - 1); 12275d43fd68SRuslan Bukin ifp->if_snd.ifq_drv_maxlen = TX_DESC_COUNT - 1; 12285d43fd68SRuslan Bukin IFQ_SET_READY(&ifp->if_snd); 12295d43fd68SRuslan Bukin 12305d43fd68SRuslan Bukin /* Attach the mii driver. */ 12315d43fd68SRuslan Bukin error = mii_attach(dev, &sc->miibus, ifp, dwc_media_change, 12325d43fd68SRuslan Bukin dwc_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY, 12335d43fd68SRuslan Bukin MII_OFFSET_ANY, 0); 12345d43fd68SRuslan Bukin 12355d43fd68SRuslan Bukin if (error != 0) { 12365d43fd68SRuslan Bukin device_printf(dev, "PHY attach failed\n"); 12375d43fd68SRuslan Bukin return (ENXIO); 12385d43fd68SRuslan Bukin } 12395d43fd68SRuslan Bukin sc->mii_softc = device_get_softc(sc->miibus); 12405d43fd68SRuslan Bukin 12415d43fd68SRuslan Bukin /* All ready to run, attach the ethernet interface. */ 12425d43fd68SRuslan Bukin ether_ifattach(ifp, macaddr); 12435d43fd68SRuslan Bukin sc->is_attached = true; 12445d43fd68SRuslan Bukin 12455d43fd68SRuslan Bukin return (0); 12465d43fd68SRuslan Bukin } 12475d43fd68SRuslan Bukin 12485d43fd68SRuslan Bukin static int 12495d43fd68SRuslan Bukin dwc_miibus_read_reg(device_t dev, int phy, int reg) 12505d43fd68SRuslan Bukin { 12515d43fd68SRuslan Bukin struct dwc_softc *sc; 12525d43fd68SRuslan Bukin uint16_t mii; 12535d43fd68SRuslan Bukin size_t cnt; 12545d43fd68SRuslan Bukin int rv = 0; 12555d43fd68SRuslan Bukin 12565d43fd68SRuslan Bukin sc = device_get_softc(dev); 12575d43fd68SRuslan Bukin 12585d43fd68SRuslan Bukin mii = ((phy & GMII_ADDRESS_PA_MASK) << GMII_ADDRESS_PA_SHIFT) 12595d43fd68SRuslan Bukin | ((reg & GMII_ADDRESS_GR_MASK) << GMII_ADDRESS_GR_SHIFT) 12605d43fd68SRuslan Bukin | (sc->mii_clk << GMII_ADDRESS_CR_SHIFT) 12615d43fd68SRuslan Bukin | GMII_ADDRESS_GB; /* Busy flag */ 12625d43fd68SRuslan Bukin 12635d43fd68SRuslan Bukin WRITE4(sc, GMII_ADDRESS, mii); 12645d43fd68SRuslan Bukin 12655d43fd68SRuslan Bukin for (cnt = 0; cnt < 1000; cnt++) { 12665d43fd68SRuslan Bukin if (!(READ4(sc, GMII_ADDRESS) & GMII_ADDRESS_GB)) { 12675d43fd68SRuslan Bukin rv = READ4(sc, GMII_DATA); 12685d43fd68SRuslan Bukin break; 12695d43fd68SRuslan Bukin } 12705d43fd68SRuslan Bukin DELAY(10); 12715d43fd68SRuslan Bukin } 12725d43fd68SRuslan Bukin 12735d43fd68SRuslan Bukin return rv; 12745d43fd68SRuslan Bukin } 12755d43fd68SRuslan Bukin 12765d43fd68SRuslan Bukin static int 12775d43fd68SRuslan Bukin dwc_miibus_write_reg(device_t dev, int phy, int reg, int val) 12785d43fd68SRuslan Bukin { 12795d43fd68SRuslan Bukin struct dwc_softc *sc; 12805d43fd68SRuslan Bukin uint16_t mii; 12815d43fd68SRuslan Bukin size_t cnt; 12825d43fd68SRuslan Bukin 12835d43fd68SRuslan Bukin sc = device_get_softc(dev); 12845d43fd68SRuslan Bukin 12855d43fd68SRuslan Bukin mii = ((phy & GMII_ADDRESS_PA_MASK) << GMII_ADDRESS_PA_SHIFT) 12865d43fd68SRuslan Bukin | ((reg & GMII_ADDRESS_GR_MASK) << GMII_ADDRESS_GR_SHIFT) 12875d43fd68SRuslan Bukin | (sc->mii_clk << GMII_ADDRESS_CR_SHIFT) 12885d43fd68SRuslan Bukin | GMII_ADDRESS_GB | GMII_ADDRESS_GW; 12895d43fd68SRuslan Bukin 12905d43fd68SRuslan Bukin WRITE4(sc, GMII_DATA, val); 12915d43fd68SRuslan Bukin WRITE4(sc, GMII_ADDRESS, mii); 12925d43fd68SRuslan Bukin 12935d43fd68SRuslan Bukin for (cnt = 0; cnt < 1000; cnt++) { 12945d43fd68SRuslan Bukin if (!(READ4(sc, GMII_ADDRESS) & GMII_ADDRESS_GB)) { 12955d43fd68SRuslan Bukin break; 12965d43fd68SRuslan Bukin } 12975d43fd68SRuslan Bukin DELAY(10); 12985d43fd68SRuslan Bukin } 12995d43fd68SRuslan Bukin 13005d43fd68SRuslan Bukin return (0); 13015d43fd68SRuslan Bukin } 13025d43fd68SRuslan Bukin 13035d43fd68SRuslan Bukin static void 13045d43fd68SRuslan Bukin dwc_miibus_statchg(device_t dev) 13055d43fd68SRuslan Bukin { 13065d43fd68SRuslan Bukin struct dwc_softc *sc; 13075d43fd68SRuslan Bukin struct mii_data *mii; 1308ff0752c8SLuiz Otavio O Souza uint32_t reg; 13095d43fd68SRuslan Bukin 13105d43fd68SRuslan Bukin /* 13115d43fd68SRuslan Bukin * Called by the MII bus driver when the PHY establishes 13125d43fd68SRuslan Bukin * link to set the MAC interface registers. 13135d43fd68SRuslan Bukin */ 13145d43fd68SRuslan Bukin 13155d43fd68SRuslan Bukin sc = device_get_softc(dev); 13165d43fd68SRuslan Bukin 13175d43fd68SRuslan Bukin DWC_ASSERT_LOCKED(sc); 13185d43fd68SRuslan Bukin 13195d43fd68SRuslan Bukin mii = sc->mii_softc; 13205d43fd68SRuslan Bukin 13215d43fd68SRuslan Bukin if (mii->mii_media_status & IFM_ACTIVE) 13225d43fd68SRuslan Bukin sc->link_is_up = true; 13235d43fd68SRuslan Bukin else 13245d43fd68SRuslan Bukin sc->link_is_up = false; 13255d43fd68SRuslan Bukin 13265d43fd68SRuslan Bukin reg = READ4(sc, MAC_CONFIGURATION); 13275d43fd68SRuslan Bukin switch (IFM_SUBTYPE(mii->mii_media_active)) { 13285d43fd68SRuslan Bukin case IFM_1000_T: 13295d43fd68SRuslan Bukin case IFM_1000_SX: 13305d43fd68SRuslan Bukin reg &= ~(CONF_FES | CONF_PS); 13315d43fd68SRuslan Bukin break; 13325d43fd68SRuslan Bukin case IFM_100_TX: 13335d43fd68SRuslan Bukin reg |= (CONF_FES | CONF_PS); 13345d43fd68SRuslan Bukin break; 13355d43fd68SRuslan Bukin case IFM_10_T: 13365d43fd68SRuslan Bukin reg &= ~(CONF_FES); 13375d43fd68SRuslan Bukin reg |= (CONF_PS); 13385d43fd68SRuslan Bukin break; 13395d43fd68SRuslan Bukin case IFM_NONE: 13405d43fd68SRuslan Bukin sc->link_is_up = false; 13415d43fd68SRuslan Bukin return; 13425d43fd68SRuslan Bukin default: 13435d43fd68SRuslan Bukin sc->link_is_up = false; 13445d43fd68SRuslan Bukin device_printf(dev, "Unsupported media %u\n", 13455d43fd68SRuslan Bukin IFM_SUBTYPE(mii->mii_media_active)); 13465d43fd68SRuslan Bukin return; 13475d43fd68SRuslan Bukin } 13485d43fd68SRuslan Bukin if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) 13495d43fd68SRuslan Bukin reg |= (CONF_DM); 13505d43fd68SRuslan Bukin else 13515d43fd68SRuslan Bukin reg &= ~(CONF_DM); 13525d43fd68SRuslan Bukin WRITE4(sc, MAC_CONFIGURATION, reg); 13535d43fd68SRuslan Bukin } 13545d43fd68SRuslan Bukin 13555d43fd68SRuslan Bukin static device_method_t dwc_methods[] = { 13565d43fd68SRuslan Bukin DEVMETHOD(device_probe, dwc_probe), 13575d43fd68SRuslan Bukin DEVMETHOD(device_attach, dwc_attach), 13585d43fd68SRuslan Bukin 13595d43fd68SRuslan Bukin /* MII Interface */ 13605d43fd68SRuslan Bukin DEVMETHOD(miibus_readreg, dwc_miibus_read_reg), 13615d43fd68SRuslan Bukin DEVMETHOD(miibus_writereg, dwc_miibus_write_reg), 13625d43fd68SRuslan Bukin DEVMETHOD(miibus_statchg, dwc_miibus_statchg), 13635d43fd68SRuslan Bukin 13645d43fd68SRuslan Bukin { 0, 0 } 13655d43fd68SRuslan Bukin }; 13665d43fd68SRuslan Bukin 13675df53927SLuiz Otavio O Souza driver_t dwc_driver = { 13685d43fd68SRuslan Bukin "dwc", 13695d43fd68SRuslan Bukin dwc_methods, 13705d43fd68SRuslan Bukin sizeof(struct dwc_softc), 13715d43fd68SRuslan Bukin }; 13725d43fd68SRuslan Bukin 13735d43fd68SRuslan Bukin static devclass_t dwc_devclass; 13745d43fd68SRuslan Bukin 13755d43fd68SRuslan Bukin DRIVER_MODULE(dwc, simplebus, dwc_driver, dwc_devclass, 0, 0); 13765d43fd68SRuslan Bukin DRIVER_MODULE(miibus, dwc, miibus_driver, miibus_devclass, 0, 0); 13775d43fd68SRuslan Bukin 13785d43fd68SRuslan Bukin MODULE_DEPEND(dwc, ether, 1, 1, 1); 13795d43fd68SRuslan Bukin MODULE_DEPEND(dwc, miibus, 1, 1, 1); 1380