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> 455d43fd68SRuslan Bukin #include <sys/kernel.h> 465d43fd68SRuslan Bukin #include <sys/lock.h> 47da9a326bSLuiz Otavio O Souza #include <sys/malloc.h> 485d43fd68SRuslan Bukin #include <sys/mbuf.h> 49da9a326bSLuiz Otavio O Souza #include <sys/module.h> 505d43fd68SRuslan Bukin #include <sys/mutex.h> 51da9a326bSLuiz Otavio O Souza #include <sys/rman.h> 525d43fd68SRuslan Bukin #include <sys/socket.h> 535d43fd68SRuslan Bukin #include <sys/sockio.h> 545d43fd68SRuslan Bukin 555d43fd68SRuslan Bukin #include <net/bpf.h> 565d43fd68SRuslan Bukin #include <net/if.h> 575d43fd68SRuslan Bukin #include <net/ethernet.h> 585d43fd68SRuslan Bukin #include <net/if_dl.h> 595d43fd68SRuslan Bukin #include <net/if_media.h> 605d43fd68SRuslan Bukin #include <net/if_types.h> 615d43fd68SRuslan Bukin #include <net/if_var.h> 625d43fd68SRuslan Bukin 635d43fd68SRuslan Bukin #include <machine/bus.h> 645d43fd68SRuslan Bukin 65da9a326bSLuiz Otavio O Souza #include <dev/dwc/if_dwc.h> 66*5df53927SLuiz Otavio O Souza #include <dev/dwc/if_dwcvar.h> 675d43fd68SRuslan Bukin #include <dev/mii/mii.h> 685d43fd68SRuslan Bukin #include <dev/mii/miivar.h> 69da9a326bSLuiz Otavio O Souza #include <dev/ofw/ofw_bus.h> 70da9a326bSLuiz Otavio O Souza #include <dev/ofw/ofw_bus_subr.h> 71da9a326bSLuiz Otavio O Souza 72*5df53927SLuiz Otavio O Souza #include "if_dwc_if.h" 735d43fd68SRuslan Bukin #include "miibus_if.h" 745d43fd68SRuslan Bukin 755d43fd68SRuslan Bukin #define READ4(_sc, _reg) \ 765d43fd68SRuslan Bukin bus_read_4((_sc)->res[0], _reg) 775d43fd68SRuslan Bukin #define WRITE4(_sc, _reg, _val) \ 785d43fd68SRuslan Bukin bus_write_4((_sc)->res[0], _reg, _val) 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 #define DWC_LOCK(sc) mtx_lock(&(sc)->mtx) 855d43fd68SRuslan Bukin #define DWC_UNLOCK(sc) mtx_unlock(&(sc)->mtx) 86ff0752c8SLuiz Otavio O Souza #define DWC_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED) 87ff0752c8SLuiz Otavio O Souza #define DWC_ASSERT_UNLOCKED(sc) mtx_assert(&(sc)->mtx, MA_NOTOWNED) 885d43fd68SRuslan Bukin 89ff0752c8SLuiz Otavio O Souza #define DDESC_TDES0_OWN (1U << 31) 90ff0752c8SLuiz Otavio O Souza #define DDESC_TDES0_TXINT (1U << 30) 91ff0752c8SLuiz Otavio O Souza #define DDESC_TDES0_TXLAST (1U << 29) 92ff0752c8SLuiz Otavio O Souza #define DDESC_TDES0_TXFIRST (1U << 28) 93ff0752c8SLuiz Otavio O Souza #define DDESC_TDES0_TXCRCDIS (1U << 27) 94ff0752c8SLuiz Otavio O Souza #define DDESC_TDES0_TXRINGEND (1U << 21) 95ff0752c8SLuiz Otavio O Souza #define DDESC_TDES0_TXCHAIN (1U << 20) 965d43fd68SRuslan Bukin 97ff0752c8SLuiz Otavio O Souza #define DDESC_RDES0_OWN (1U << 31) 985d43fd68SRuslan Bukin #define DDESC_RDES0_FL_MASK 0x3fff 995d43fd68SRuslan Bukin #define DDESC_RDES0_FL_SHIFT 16 /* Frame Length */ 100ff0752c8SLuiz Otavio O Souza #define DDESC_RDES1_CHAINED (1U << 14) 1015d43fd68SRuslan Bukin 102*5df53927SLuiz Otavio O Souza /* Alt descriptor bits. */ 103*5df53927SLuiz Otavio O Souza #define DDESC_CNTL_TXINT (1U << 31) 104*5df53927SLuiz Otavio O Souza #define DDESC_CNTL_TXLAST (1U << 30) 105*5df53927SLuiz Otavio O Souza #define DDESC_CNTL_TXFIRST (1U << 29) 106*5df53927SLuiz Otavio O Souza #define DDESC_CNTL_TXCRCDIS (1U << 26) 107*5df53927SLuiz Otavio O Souza #define DDESC_CNTL_TXRINGEND (1U << 25) 108*5df53927SLuiz Otavio O Souza #define DDESC_CNTL_TXCHAIN (1U << 24) 109*5df53927SLuiz Otavio O Souza 110*5df53927SLuiz Otavio O Souza #define DDESC_CNTL_CHAINED (1U << 24) 1115d43fd68SRuslan Bukin 1125d43fd68SRuslan Bukin /* 1135d43fd68SRuslan Bukin * A hardware buffer descriptor. Rx and Tx buffers have the same descriptor 114*5df53927SLuiz Otavio O Souza * layout, but the bits in the fields have different meanings. 1155d43fd68SRuslan Bukin */ 1165d43fd68SRuslan Bukin struct dwc_hwdesc 1175d43fd68SRuslan Bukin { 118*5df53927SLuiz Otavio O Souza uint32_t tdes0; /* status for alt layout */ 119*5df53927SLuiz Otavio O Souza uint32_t tdes1; /* cntl for alt layout */ 1205d43fd68SRuslan Bukin uint32_t addr; /* pointer to buffer data */ 1215d43fd68SRuslan Bukin uint32_t addr_next; /* link to next descriptor */ 1225d43fd68SRuslan Bukin }; 1235d43fd68SRuslan Bukin 1245d43fd68SRuslan Bukin /* 1255d43fd68SRuslan Bukin * The hardware imposes alignment restrictions on various objects involved in 1265d43fd68SRuslan Bukin * DMA transfers. These values are expressed in bytes (not bits). 1275d43fd68SRuslan Bukin */ 1285d43fd68SRuslan Bukin #define DWC_DESC_RING_ALIGN 2048 1295d43fd68SRuslan Bukin 1305d43fd68SRuslan Bukin static struct resource_spec dwc_spec[] = { 1315d43fd68SRuslan Bukin { SYS_RES_MEMORY, 0, RF_ACTIVE }, 1325d43fd68SRuslan Bukin { SYS_RES_IRQ, 0, RF_ACTIVE }, 1335d43fd68SRuslan Bukin { -1, 0 } 1345d43fd68SRuslan Bukin }; 1355d43fd68SRuslan Bukin 1365d43fd68SRuslan Bukin static void dwc_txfinish_locked(struct dwc_softc *sc); 1375d43fd68SRuslan Bukin static void dwc_rxfinish_locked(struct dwc_softc *sc); 1385d43fd68SRuslan Bukin static void dwc_stop_locked(struct dwc_softc *sc); 1395d43fd68SRuslan Bukin static void dwc_setup_rxfilter(struct dwc_softc *sc); 1405d43fd68SRuslan Bukin 1415d43fd68SRuslan Bukin static inline uint32_t 1425d43fd68SRuslan Bukin next_rxidx(struct dwc_softc *sc, uint32_t curidx) 1435d43fd68SRuslan Bukin { 1445d43fd68SRuslan Bukin 1455d43fd68SRuslan Bukin return ((curidx + 1) % RX_DESC_COUNT); 1465d43fd68SRuslan Bukin } 1475d43fd68SRuslan Bukin 1485d43fd68SRuslan Bukin static inline uint32_t 1495d43fd68SRuslan Bukin next_txidx(struct dwc_softc *sc, uint32_t curidx) 1505d43fd68SRuslan Bukin { 1515d43fd68SRuslan Bukin 1525d43fd68SRuslan Bukin return ((curidx + 1) % TX_DESC_COUNT); 1535d43fd68SRuslan Bukin } 1545d43fd68SRuslan Bukin 1555d43fd68SRuslan Bukin static void 1565d43fd68SRuslan Bukin dwc_get1paddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 1575d43fd68SRuslan Bukin { 1585d43fd68SRuslan Bukin 1595d43fd68SRuslan Bukin if (error != 0) 1605d43fd68SRuslan Bukin return; 1615d43fd68SRuslan Bukin *(bus_addr_t *)arg = segs[0].ds_addr; 1625d43fd68SRuslan Bukin } 1635d43fd68SRuslan Bukin 1645d43fd68SRuslan Bukin inline static uint32_t 1655d43fd68SRuslan Bukin dwc_setup_txdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr, 1665d43fd68SRuslan Bukin uint32_t len) 1675d43fd68SRuslan Bukin { 1685d43fd68SRuslan Bukin uint32_t flags; 1695d43fd68SRuslan Bukin uint32_t nidx; 1705d43fd68SRuslan Bukin 1715d43fd68SRuslan Bukin nidx = next_txidx(sc, idx); 1725d43fd68SRuslan Bukin 1735d43fd68SRuslan Bukin /* Addr/len 0 means we're clearing the descriptor after xmit done. */ 1745d43fd68SRuslan Bukin if (paddr == 0 || len == 0) { 1755d43fd68SRuslan Bukin flags = 0; 1765d43fd68SRuslan Bukin --sc->txcount; 1775d43fd68SRuslan Bukin } else { 178*5df53927SLuiz Otavio O Souza if (sc->mactype == DWC_GMAC_ALT_DESC) 179*5df53927SLuiz Otavio O Souza flags = DDESC_CNTL_TXCHAIN | DDESC_CNTL_TXFIRST 180*5df53927SLuiz Otavio O Souza | DDESC_CNTL_TXLAST | DDESC_CNTL_TXINT; 181*5df53927SLuiz Otavio O Souza else 1825d43fd68SRuslan Bukin flags = DDESC_TDES0_TXCHAIN | DDESC_TDES0_TXFIRST 1835d43fd68SRuslan Bukin | DDESC_TDES0_TXLAST | DDESC_TDES0_TXINT; 1845d43fd68SRuslan Bukin ++sc->txcount; 1855d43fd68SRuslan Bukin } 1865d43fd68SRuslan Bukin 1875d43fd68SRuslan Bukin sc->txdesc_ring[idx].addr = (uint32_t)(paddr); 188*5df53927SLuiz Otavio O Souza if (sc->mactype == DWC_GMAC_ALT_DESC) { 189*5df53927SLuiz Otavio O Souza sc->txdesc_ring[idx].tdes0 = 0; 190*5df53927SLuiz Otavio O Souza sc->txdesc_ring[idx].tdes1 = flags | len; 191*5df53927SLuiz Otavio O Souza } else { 1925d43fd68SRuslan Bukin sc->txdesc_ring[idx].tdes0 = flags; 1935d43fd68SRuslan Bukin sc->txdesc_ring[idx].tdes1 = len; 194*5df53927SLuiz Otavio O Souza } 1955d43fd68SRuslan Bukin 1965d43fd68SRuslan Bukin if (paddr && len) { 1975d43fd68SRuslan Bukin wmb(); 1985d43fd68SRuslan Bukin sc->txdesc_ring[idx].tdes0 |= DDESC_TDES0_OWN; 1995d43fd68SRuslan Bukin wmb(); 2005d43fd68SRuslan Bukin } 2015d43fd68SRuslan Bukin 2025d43fd68SRuslan Bukin return (nidx); 2035d43fd68SRuslan Bukin } 2045d43fd68SRuslan Bukin 2055d43fd68SRuslan Bukin static int 2065d43fd68SRuslan Bukin dwc_setup_txbuf(struct dwc_softc *sc, int idx, struct mbuf **mp) 2075d43fd68SRuslan Bukin { 2085d43fd68SRuslan Bukin struct bus_dma_segment seg; 2095d43fd68SRuslan Bukin int error, nsegs; 2105d43fd68SRuslan Bukin struct mbuf * m; 2115d43fd68SRuslan Bukin 2125d43fd68SRuslan Bukin if ((m = m_defrag(*mp, M_NOWAIT)) == NULL) 2135d43fd68SRuslan Bukin return (ENOMEM); 2145d43fd68SRuslan Bukin *mp = m; 2155d43fd68SRuslan Bukin 2165d43fd68SRuslan Bukin error = bus_dmamap_load_mbuf_sg(sc->txbuf_tag, sc->txbuf_map[idx].map, 2175d43fd68SRuslan Bukin m, &seg, &nsegs, 0); 2185d43fd68SRuslan Bukin if (error != 0) { 2195d43fd68SRuslan Bukin return (ENOMEM); 2205d43fd68SRuslan Bukin } 2215d43fd68SRuslan Bukin 2225d43fd68SRuslan Bukin KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 2235d43fd68SRuslan Bukin 2245d43fd68SRuslan Bukin bus_dmamap_sync(sc->txbuf_tag, sc->txbuf_map[idx].map, 2255d43fd68SRuslan Bukin BUS_DMASYNC_PREWRITE); 2265d43fd68SRuslan Bukin 2275d43fd68SRuslan Bukin sc->txbuf_map[idx].mbuf = m; 2285d43fd68SRuslan Bukin 2295d43fd68SRuslan Bukin dwc_setup_txdesc(sc, idx, seg.ds_addr, seg.ds_len); 2305d43fd68SRuslan Bukin 2315d43fd68SRuslan Bukin return (0); 2325d43fd68SRuslan Bukin } 2335d43fd68SRuslan Bukin 2345d43fd68SRuslan Bukin static void 2355d43fd68SRuslan Bukin dwc_txstart_locked(struct dwc_softc *sc) 2365d43fd68SRuslan Bukin { 2375d43fd68SRuslan Bukin struct ifnet *ifp; 2385d43fd68SRuslan Bukin struct mbuf *m; 2395d43fd68SRuslan Bukin int enqueued; 2405d43fd68SRuslan Bukin 2415d43fd68SRuslan Bukin DWC_ASSERT_LOCKED(sc); 2425d43fd68SRuslan Bukin 2435d43fd68SRuslan Bukin if (!sc->link_is_up) 2445d43fd68SRuslan Bukin return; 2455d43fd68SRuslan Bukin 2465d43fd68SRuslan Bukin ifp = sc->ifp; 2475d43fd68SRuslan Bukin 2485d43fd68SRuslan Bukin if (ifp->if_drv_flags & IFF_DRV_OACTIVE) { 2495d43fd68SRuslan Bukin return; 2505d43fd68SRuslan Bukin } 2515d43fd68SRuslan Bukin 2525d43fd68SRuslan Bukin enqueued = 0; 2535d43fd68SRuslan Bukin 2545d43fd68SRuslan Bukin for (;;) { 2555d43fd68SRuslan Bukin if (sc->txcount == (TX_DESC_COUNT-1)) { 2565d43fd68SRuslan Bukin ifp->if_drv_flags |= IFF_DRV_OACTIVE; 2575d43fd68SRuslan Bukin break; 2585d43fd68SRuslan Bukin } 2595d43fd68SRuslan Bukin 2605d43fd68SRuslan Bukin IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 2615d43fd68SRuslan Bukin if (m == NULL) 2625d43fd68SRuslan Bukin break; 2635d43fd68SRuslan Bukin if (dwc_setup_txbuf(sc, sc->tx_idx_head, &m) != 0) { 2645d43fd68SRuslan Bukin IFQ_DRV_PREPEND(&ifp->if_snd, m); 2655d43fd68SRuslan Bukin break; 2665d43fd68SRuslan Bukin } 2675d43fd68SRuslan Bukin BPF_MTAP(ifp, m); 2685d43fd68SRuslan Bukin sc->tx_idx_head = next_txidx(sc, sc->tx_idx_head); 2695d43fd68SRuslan Bukin ++enqueued; 2705d43fd68SRuslan Bukin } 2715d43fd68SRuslan Bukin 2725d43fd68SRuslan Bukin if (enqueued != 0) { 2735d43fd68SRuslan Bukin WRITE4(sc, TRANSMIT_POLL_DEMAND, 0x1); 2745d43fd68SRuslan Bukin sc->tx_watchdog_count = WATCHDOG_TIMEOUT_SECS; 2755d43fd68SRuslan Bukin } 2765d43fd68SRuslan Bukin } 2775d43fd68SRuslan Bukin 2785d43fd68SRuslan Bukin static void 2795d43fd68SRuslan Bukin dwc_txstart(struct ifnet *ifp) 2805d43fd68SRuslan Bukin { 2815d43fd68SRuslan Bukin struct dwc_softc *sc = ifp->if_softc; 2825d43fd68SRuslan Bukin 2835d43fd68SRuslan Bukin DWC_LOCK(sc); 2845d43fd68SRuslan Bukin dwc_txstart_locked(sc); 2855d43fd68SRuslan Bukin DWC_UNLOCK(sc); 2865d43fd68SRuslan Bukin } 2875d43fd68SRuslan Bukin 2885d43fd68SRuslan Bukin static void 2895d43fd68SRuslan Bukin dwc_stop_locked(struct dwc_softc *sc) 2905d43fd68SRuslan Bukin { 2915d43fd68SRuslan Bukin struct ifnet *ifp; 292ff0752c8SLuiz Otavio O Souza uint32_t reg; 2935d43fd68SRuslan Bukin 2945d43fd68SRuslan Bukin DWC_ASSERT_LOCKED(sc); 2955d43fd68SRuslan Bukin 2965d43fd68SRuslan Bukin ifp = sc->ifp; 2975d43fd68SRuslan Bukin ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 2985d43fd68SRuslan Bukin sc->tx_watchdog_count = 0; 2995d43fd68SRuslan Bukin sc->stats_harvest_count = 0; 3005d43fd68SRuslan Bukin 3015d43fd68SRuslan Bukin callout_stop(&sc->dwc_callout); 3025d43fd68SRuslan Bukin 3035d43fd68SRuslan Bukin /* Stop DMA TX */ 3045d43fd68SRuslan Bukin reg = READ4(sc, OPERATION_MODE); 3055d43fd68SRuslan Bukin reg &= ~(MODE_ST); 3065d43fd68SRuslan Bukin WRITE4(sc, OPERATION_MODE, reg); 3075d43fd68SRuslan Bukin 3085d43fd68SRuslan Bukin /* Flush TX */ 3095d43fd68SRuslan Bukin reg = READ4(sc, OPERATION_MODE); 3105d43fd68SRuslan Bukin reg |= (MODE_FTF); 3115d43fd68SRuslan Bukin WRITE4(sc, OPERATION_MODE, reg); 3125d43fd68SRuslan Bukin 3135d43fd68SRuslan Bukin /* Stop transmitters */ 3145d43fd68SRuslan Bukin reg = READ4(sc, MAC_CONFIGURATION); 3155d43fd68SRuslan Bukin reg &= ~(CONF_TE | CONF_RE); 3165d43fd68SRuslan Bukin WRITE4(sc, MAC_CONFIGURATION, reg); 3175d43fd68SRuslan Bukin 3185d43fd68SRuslan Bukin /* Stop DMA RX */ 3195d43fd68SRuslan Bukin reg = READ4(sc, OPERATION_MODE); 3205d43fd68SRuslan Bukin reg &= ~(MODE_SR); 3215d43fd68SRuslan Bukin WRITE4(sc, OPERATION_MODE, reg); 3225d43fd68SRuslan Bukin } 3235d43fd68SRuslan Bukin 3245d43fd68SRuslan Bukin static void dwc_clear_stats(struct dwc_softc *sc) 3255d43fd68SRuslan Bukin { 326ff0752c8SLuiz Otavio O Souza uint32_t reg; 3275d43fd68SRuslan Bukin 3285d43fd68SRuslan Bukin reg = READ4(sc, MMC_CONTROL); 3295d43fd68SRuslan Bukin reg |= (MMC_CONTROL_CNTRST); 3305d43fd68SRuslan Bukin WRITE4(sc, MMC_CONTROL, reg); 3315d43fd68SRuslan Bukin } 3325d43fd68SRuslan Bukin 3335d43fd68SRuslan Bukin static void 3345d43fd68SRuslan Bukin dwc_harvest_stats(struct dwc_softc *sc) 3355d43fd68SRuslan Bukin { 3365d43fd68SRuslan Bukin struct ifnet *ifp; 3375d43fd68SRuslan Bukin 3385d43fd68SRuslan Bukin /* We don't need to harvest too often. */ 3395d43fd68SRuslan Bukin if (++sc->stats_harvest_count < STATS_HARVEST_INTERVAL) 3405d43fd68SRuslan Bukin return; 3415d43fd68SRuslan Bukin 3425d43fd68SRuslan Bukin sc->stats_harvest_count = 0; 3435d43fd68SRuslan Bukin ifp = sc->ifp; 3445d43fd68SRuslan Bukin 3455d43fd68SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_IPACKETS, READ4(sc, RXFRAMECOUNT_GB)); 3465d43fd68SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_IMCASTS, READ4(sc, RXMULTICASTFRAMES_G)); 3475d43fd68SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_IERRORS, 3485d43fd68SRuslan Bukin READ4(sc, RXOVERSIZE_G) + READ4(sc, RXUNDERSIZE_G) + 3495d43fd68SRuslan Bukin READ4(sc, RXCRCERROR) + READ4(sc, RXALIGNMENTERROR) + 3505d43fd68SRuslan Bukin READ4(sc, RXRUNTERROR) + READ4(sc, RXJABBERERROR) + 3515d43fd68SRuslan Bukin READ4(sc, RXLENGTHERROR)); 3525d43fd68SRuslan Bukin 3535d43fd68SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_OPACKETS, READ4(sc, TXFRAMECOUNT_G)); 3545d43fd68SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_OMCASTS, READ4(sc, TXMULTICASTFRAMES_G)); 3555d43fd68SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_OERRORS, 3565d43fd68SRuslan Bukin READ4(sc, TXOVERSIZE_G) + READ4(sc, TXEXCESSDEF) + 3575d43fd68SRuslan Bukin READ4(sc, TXCARRIERERR) + READ4(sc, TXUNDERFLOWERROR)); 3585d43fd68SRuslan Bukin 3595d43fd68SRuslan Bukin if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 3605d43fd68SRuslan Bukin READ4(sc, TXEXESSCOL) + READ4(sc, TXLATECOL)); 3615d43fd68SRuslan Bukin 3625d43fd68SRuslan Bukin dwc_clear_stats(sc); 3635d43fd68SRuslan Bukin } 3645d43fd68SRuslan Bukin 3655d43fd68SRuslan Bukin static void 3665d43fd68SRuslan Bukin dwc_tick(void *arg) 3675d43fd68SRuslan Bukin { 3685d43fd68SRuslan Bukin struct dwc_softc *sc; 3695d43fd68SRuslan Bukin struct ifnet *ifp; 3705d43fd68SRuslan Bukin int link_was_up; 3715d43fd68SRuslan Bukin 3725d43fd68SRuslan Bukin sc = arg; 3735d43fd68SRuslan Bukin 3745d43fd68SRuslan Bukin DWC_ASSERT_LOCKED(sc); 3755d43fd68SRuslan Bukin 3765d43fd68SRuslan Bukin ifp = sc->ifp; 3775d43fd68SRuslan Bukin 3785d43fd68SRuslan Bukin if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) 3795d43fd68SRuslan Bukin return; 3805d43fd68SRuslan Bukin 3815d43fd68SRuslan Bukin /* 3825d43fd68SRuslan Bukin * Typical tx watchdog. If this fires it indicates that we enqueued 3835d43fd68SRuslan Bukin * packets for output and never got a txdone interrupt for them. Maybe 3845d43fd68SRuslan Bukin * it's a missed interrupt somehow, just pretend we got one. 3855d43fd68SRuslan Bukin */ 3865d43fd68SRuslan Bukin if (sc->tx_watchdog_count > 0) { 3875d43fd68SRuslan Bukin if (--sc->tx_watchdog_count == 0) { 3885d43fd68SRuslan Bukin dwc_txfinish_locked(sc); 3895d43fd68SRuslan Bukin } 3905d43fd68SRuslan Bukin } 3915d43fd68SRuslan Bukin 3925d43fd68SRuslan Bukin /* Gather stats from hardware counters. */ 3935d43fd68SRuslan Bukin dwc_harvest_stats(sc); 3945d43fd68SRuslan Bukin 3955d43fd68SRuslan Bukin /* Check the media status. */ 3965d43fd68SRuslan Bukin link_was_up = sc->link_is_up; 3975d43fd68SRuslan Bukin mii_tick(sc->mii_softc); 3985d43fd68SRuslan Bukin if (sc->link_is_up && !link_was_up) 3995d43fd68SRuslan Bukin dwc_txstart_locked(sc); 4005d43fd68SRuslan Bukin 4015d43fd68SRuslan Bukin /* Schedule another check one second from now. */ 4025d43fd68SRuslan Bukin callout_reset(&sc->dwc_callout, hz, dwc_tick, sc); 4035d43fd68SRuslan Bukin } 4045d43fd68SRuslan Bukin 4055d43fd68SRuslan Bukin static void 4065d43fd68SRuslan Bukin dwc_init_locked(struct dwc_softc *sc) 4075d43fd68SRuslan Bukin { 4085d43fd68SRuslan Bukin struct ifnet *ifp = sc->ifp; 409ff0752c8SLuiz Otavio O Souza uint32_t reg; 4105d43fd68SRuslan Bukin 4115d43fd68SRuslan Bukin DWC_ASSERT_LOCKED(sc); 4125d43fd68SRuslan Bukin 4135d43fd68SRuslan Bukin if (ifp->if_drv_flags & IFF_DRV_RUNNING) 4145d43fd68SRuslan Bukin return; 4155d43fd68SRuslan Bukin 4165d43fd68SRuslan Bukin ifp->if_drv_flags |= IFF_DRV_RUNNING; 4175d43fd68SRuslan Bukin 4185d43fd68SRuslan Bukin dwc_setup_rxfilter(sc); 4195d43fd68SRuslan Bukin 4205d43fd68SRuslan Bukin /* Initializa DMA and enable transmitters */ 4215d43fd68SRuslan Bukin reg = READ4(sc, OPERATION_MODE); 4225d43fd68SRuslan Bukin reg |= (MODE_TSF | MODE_OSF | MODE_FUF); 4235d43fd68SRuslan Bukin reg &= ~(MODE_RSF); 4245d43fd68SRuslan Bukin reg |= (MODE_RTC_LEV32 << MODE_RTC_SHIFT); 4255d43fd68SRuslan Bukin WRITE4(sc, OPERATION_MODE, reg); 4265d43fd68SRuslan Bukin 4275d43fd68SRuslan Bukin WRITE4(sc, INTERRUPT_ENABLE, INT_EN_DEFAULT); 4285d43fd68SRuslan Bukin 4295d43fd68SRuslan Bukin /* Start DMA */ 4305d43fd68SRuslan Bukin reg = READ4(sc, OPERATION_MODE); 4315d43fd68SRuslan Bukin reg |= (MODE_ST | MODE_SR); 4325d43fd68SRuslan Bukin WRITE4(sc, OPERATION_MODE, reg); 4335d43fd68SRuslan Bukin 4345d43fd68SRuslan Bukin /* Enable transmitters */ 4355d43fd68SRuslan Bukin reg = READ4(sc, MAC_CONFIGURATION); 4365d43fd68SRuslan Bukin reg |= (CONF_JD | CONF_ACS | CONF_BE); 4375d43fd68SRuslan Bukin reg |= (CONF_TE | CONF_RE); 4385d43fd68SRuslan Bukin WRITE4(sc, MAC_CONFIGURATION, reg); 4395d43fd68SRuslan Bukin 4405d43fd68SRuslan Bukin /* 4415d43fd68SRuslan Bukin * Call mii_mediachg() which will call back into dwc_miibus_statchg() 4425d43fd68SRuslan Bukin * to set up the remaining config registers based on current media. 4435d43fd68SRuslan Bukin */ 4445d43fd68SRuslan Bukin mii_mediachg(sc->mii_softc); 4455d43fd68SRuslan Bukin callout_reset(&sc->dwc_callout, hz, dwc_tick, sc); 4465d43fd68SRuslan Bukin } 4475d43fd68SRuslan Bukin 4485d43fd68SRuslan Bukin static void 4495d43fd68SRuslan Bukin dwc_init(void *if_softc) 4505d43fd68SRuslan Bukin { 4515d43fd68SRuslan Bukin struct dwc_softc *sc = if_softc; 4525d43fd68SRuslan Bukin 4535d43fd68SRuslan Bukin DWC_LOCK(sc); 4545d43fd68SRuslan Bukin dwc_init_locked(sc); 4555d43fd68SRuslan Bukin DWC_UNLOCK(sc); 4565d43fd68SRuslan Bukin } 4575d43fd68SRuslan Bukin 4585d43fd68SRuslan Bukin inline static uint32_t 4595d43fd68SRuslan Bukin dwc_setup_rxdesc(struct dwc_softc *sc, int idx, bus_addr_t paddr) 4605d43fd68SRuslan Bukin { 4615d43fd68SRuslan Bukin uint32_t nidx; 4625d43fd68SRuslan Bukin 4635d43fd68SRuslan Bukin sc->rxdesc_ring[idx].addr = (uint32_t)paddr; 4645d43fd68SRuslan Bukin nidx = next_rxidx(sc, idx); 4655d43fd68SRuslan Bukin sc->rxdesc_ring[idx].addr_next = sc->rxdesc_ring_paddr + \ 4665d43fd68SRuslan Bukin (nidx * sizeof(struct dwc_hwdesc)); 467*5df53927SLuiz Otavio O Souza if (sc->mactype == DWC_GMAC_ALT_DESC) 468*5df53927SLuiz Otavio O Souza sc->rxdesc_ring[idx].tdes1 = DDESC_CNTL_CHAINED | RX_MAX_PACKET; 469*5df53927SLuiz Otavio O Souza else 4705d43fd68SRuslan Bukin sc->rxdesc_ring[idx].tdes1 = DDESC_RDES1_CHAINED | MCLBYTES; 4715d43fd68SRuslan Bukin 4725d43fd68SRuslan Bukin wmb(); 4735d43fd68SRuslan Bukin sc->rxdesc_ring[idx].tdes0 = DDESC_RDES0_OWN; 4745d43fd68SRuslan Bukin wmb(); 4755d43fd68SRuslan Bukin 4765d43fd68SRuslan Bukin return (nidx); 4775d43fd68SRuslan Bukin } 4785d43fd68SRuslan Bukin 4795d43fd68SRuslan Bukin static int 4805d43fd68SRuslan Bukin dwc_setup_rxbuf(struct dwc_softc *sc, int idx, struct mbuf *m) 4815d43fd68SRuslan Bukin { 4825d43fd68SRuslan Bukin struct bus_dma_segment seg; 4835d43fd68SRuslan Bukin int error, nsegs; 4845d43fd68SRuslan Bukin 4855d43fd68SRuslan Bukin m_adj(m, ETHER_ALIGN); 4865d43fd68SRuslan Bukin 4875d43fd68SRuslan Bukin error = bus_dmamap_load_mbuf_sg(sc->rxbuf_tag, sc->rxbuf_map[idx].map, 4885d43fd68SRuslan Bukin m, &seg, &nsegs, 0); 4895d43fd68SRuslan Bukin if (error != 0) { 4905d43fd68SRuslan Bukin return (error); 4915d43fd68SRuslan Bukin } 4925d43fd68SRuslan Bukin 4935d43fd68SRuslan Bukin KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 4945d43fd68SRuslan Bukin 4955d43fd68SRuslan Bukin bus_dmamap_sync(sc->rxbuf_tag, sc->rxbuf_map[idx].map, 4965d43fd68SRuslan Bukin BUS_DMASYNC_PREREAD); 4975d43fd68SRuslan Bukin 4985d43fd68SRuslan Bukin sc->rxbuf_map[idx].mbuf = m; 4995d43fd68SRuslan Bukin dwc_setup_rxdesc(sc, idx, seg.ds_addr); 5005d43fd68SRuslan Bukin 5015d43fd68SRuslan Bukin return (0); 5025d43fd68SRuslan Bukin } 5035d43fd68SRuslan Bukin 5045d43fd68SRuslan Bukin static struct mbuf * 5055d43fd68SRuslan Bukin dwc_alloc_mbufcl(struct dwc_softc *sc) 5065d43fd68SRuslan Bukin { 5075d43fd68SRuslan Bukin struct mbuf *m; 5085d43fd68SRuslan Bukin 5095d43fd68SRuslan Bukin m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 510db8a143aSRuslan Bukin if (m != NULL) 5115d43fd68SRuslan Bukin m->m_pkthdr.len = m->m_len = m->m_ext.ext_size; 5125d43fd68SRuslan Bukin 5135d43fd68SRuslan Bukin return (m); 5145d43fd68SRuslan Bukin } 5155d43fd68SRuslan Bukin 5165d43fd68SRuslan Bukin static void 5175d43fd68SRuslan Bukin dwc_media_status(struct ifnet * ifp, struct ifmediareq *ifmr) 5185d43fd68SRuslan Bukin { 5195d43fd68SRuslan Bukin struct dwc_softc *sc; 5205d43fd68SRuslan Bukin struct mii_data *mii; 5215d43fd68SRuslan Bukin 5225d43fd68SRuslan Bukin sc = ifp->if_softc; 5235d43fd68SRuslan Bukin mii = sc->mii_softc; 5245d43fd68SRuslan Bukin DWC_LOCK(sc); 5255d43fd68SRuslan Bukin mii_pollstat(mii); 5265d43fd68SRuslan Bukin ifmr->ifm_active = mii->mii_media_active; 5275d43fd68SRuslan Bukin ifmr->ifm_status = mii->mii_media_status; 5285d43fd68SRuslan Bukin DWC_UNLOCK(sc); 5295d43fd68SRuslan Bukin } 5305d43fd68SRuslan Bukin 5315d43fd68SRuslan Bukin static int 5325d43fd68SRuslan Bukin dwc_media_change_locked(struct dwc_softc *sc) 5335d43fd68SRuslan Bukin { 5345d43fd68SRuslan Bukin 5355d43fd68SRuslan Bukin return (mii_mediachg(sc->mii_softc)); 5365d43fd68SRuslan Bukin } 5375d43fd68SRuslan Bukin 5385d43fd68SRuslan Bukin static int 5395d43fd68SRuslan Bukin dwc_media_change(struct ifnet * ifp) 5405d43fd68SRuslan Bukin { 5415d43fd68SRuslan Bukin struct dwc_softc *sc; 5425d43fd68SRuslan Bukin int error; 5435d43fd68SRuslan Bukin 5445d43fd68SRuslan Bukin sc = ifp->if_softc; 5455d43fd68SRuslan Bukin 5465d43fd68SRuslan Bukin DWC_LOCK(sc); 5475d43fd68SRuslan Bukin error = dwc_media_change_locked(sc); 5485d43fd68SRuslan Bukin DWC_UNLOCK(sc); 5495d43fd68SRuslan Bukin return (error); 5505d43fd68SRuslan Bukin } 5515d43fd68SRuslan Bukin 5525d43fd68SRuslan Bukin static const uint8_t nibbletab[] = { 5535d43fd68SRuslan Bukin /* 0x0 0000 -> 0000 */ 0x0, 5545d43fd68SRuslan Bukin /* 0x1 0001 -> 1000 */ 0x8, 5555d43fd68SRuslan Bukin /* 0x2 0010 -> 0100 */ 0x4, 5565d43fd68SRuslan Bukin /* 0x3 0011 -> 1100 */ 0xc, 5575d43fd68SRuslan Bukin /* 0x4 0100 -> 0010 */ 0x2, 5585d43fd68SRuslan Bukin /* 0x5 0101 -> 1010 */ 0xa, 5595d43fd68SRuslan Bukin /* 0x6 0110 -> 0110 */ 0x6, 5605d43fd68SRuslan Bukin /* 0x7 0111 -> 1110 */ 0xe, 5615d43fd68SRuslan Bukin /* 0x8 1000 -> 0001 */ 0x1, 5625d43fd68SRuslan Bukin /* 0x9 1001 -> 1001 */ 0x9, 5635d43fd68SRuslan Bukin /* 0xa 1010 -> 0101 */ 0x5, 5645d43fd68SRuslan Bukin /* 0xb 1011 -> 1101 */ 0xd, 5655d43fd68SRuslan Bukin /* 0xc 1100 -> 0011 */ 0x3, 5665d43fd68SRuslan Bukin /* 0xd 1101 -> 1011 */ 0xb, 5675d43fd68SRuslan Bukin /* 0xe 1110 -> 0111 */ 0x7, 5685d43fd68SRuslan Bukin /* 0xf 1111 -> 1111 */ 0xf, }; 5695d43fd68SRuslan Bukin 5705d43fd68SRuslan Bukin static uint8_t 5715d43fd68SRuslan Bukin bitreverse(uint8_t x) 5725d43fd68SRuslan Bukin { 5735d43fd68SRuslan Bukin 5745d43fd68SRuslan Bukin return (nibbletab[x & 0xf] << 4) | nibbletab[x >> 4]; 5755d43fd68SRuslan Bukin } 5765d43fd68SRuslan Bukin 5775d43fd68SRuslan Bukin static void 5785d43fd68SRuslan Bukin dwc_setup_rxfilter(struct dwc_softc *sc) 5795d43fd68SRuslan Bukin { 5805d43fd68SRuslan Bukin struct ifmultiaddr *ifma; 5815d43fd68SRuslan Bukin struct ifnet *ifp; 582ff0752c8SLuiz Otavio O Souza uint8_t *eaddr, val; 583ff0752c8SLuiz Otavio O Souza uint32_t crc, ffval, hashbit, hashreg, hi, lo, reg; 5845d43fd68SRuslan Bukin 5855d43fd68SRuslan Bukin DWC_ASSERT_LOCKED(sc); 5865d43fd68SRuslan Bukin 5875d43fd68SRuslan Bukin ifp = sc->ifp; 5885d43fd68SRuslan Bukin 5895d43fd68SRuslan Bukin /* 5905d43fd68SRuslan Bukin * Set the multicast (group) filter hash. 5915d43fd68SRuslan Bukin */ 5925d43fd68SRuslan Bukin if ((ifp->if_flags & IFF_ALLMULTI)) 5935d43fd68SRuslan Bukin ffval = (FRAME_FILTER_PM); 5945d43fd68SRuslan Bukin else { 5955d43fd68SRuslan Bukin ffval = (FRAME_FILTER_HMC); 5965d43fd68SRuslan Bukin if_maddr_rlock(ifp); 5975d43fd68SRuslan Bukin TAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) { 5985d43fd68SRuslan Bukin if (ifma->ifma_addr->sa_family != AF_LINK) 5995d43fd68SRuslan Bukin continue; 6005d43fd68SRuslan Bukin crc = ether_crc32_le(LLADDR((struct sockaddr_dl *) 6015d43fd68SRuslan Bukin ifma->ifma_addr), ETHER_ADDR_LEN); 6025d43fd68SRuslan Bukin 6035d43fd68SRuslan Bukin /* Take lower 8 bits and reverse it */ 6045d43fd68SRuslan Bukin val = bitreverse(~crc & 0xff); 6055d43fd68SRuslan Bukin hashreg = (val >> 5); 6065d43fd68SRuslan Bukin hashbit = (val & 31); 6075d43fd68SRuslan Bukin 6085d43fd68SRuslan Bukin reg = READ4(sc, HASH_TABLE_REG(hashreg)); 6095d43fd68SRuslan Bukin reg |= (1 << hashbit); 6105d43fd68SRuslan Bukin WRITE4(sc, HASH_TABLE_REG(hashreg), reg); 6115d43fd68SRuslan Bukin } 6125d43fd68SRuslan Bukin if_maddr_runlock(ifp); 6135d43fd68SRuslan Bukin } 6145d43fd68SRuslan Bukin 6155d43fd68SRuslan Bukin /* 6165d43fd68SRuslan Bukin * Set the individual address filter hash. 6175d43fd68SRuslan Bukin */ 6185d43fd68SRuslan Bukin if (ifp->if_flags & IFF_PROMISC) 6195d43fd68SRuslan Bukin ffval |= (FRAME_FILTER_PR); 6205d43fd68SRuslan Bukin 6215d43fd68SRuslan Bukin /* 6225d43fd68SRuslan Bukin * Set the primary address. 6235d43fd68SRuslan Bukin */ 6245d43fd68SRuslan Bukin eaddr = IF_LLADDR(ifp); 6255d43fd68SRuslan Bukin lo = eaddr[0] | (eaddr[1] << 8) | (eaddr[2] << 16) | 6265d43fd68SRuslan Bukin (eaddr[3] << 24); 6275d43fd68SRuslan Bukin hi = eaddr[4] | (eaddr[5] << 8); 6285d43fd68SRuslan Bukin WRITE4(sc, MAC_ADDRESS_LOW(0), lo); 6295d43fd68SRuslan Bukin WRITE4(sc, MAC_ADDRESS_HIGH(0), hi); 6305d43fd68SRuslan Bukin WRITE4(sc, MAC_FRAME_FILTER, ffval); 6315d43fd68SRuslan Bukin } 6325d43fd68SRuslan Bukin 6335d43fd68SRuslan Bukin static int 6345d43fd68SRuslan Bukin dwc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 6355d43fd68SRuslan Bukin { 6365d43fd68SRuslan Bukin struct dwc_softc *sc; 6375d43fd68SRuslan Bukin struct mii_data *mii; 6385d43fd68SRuslan Bukin struct ifreq *ifr; 6395d43fd68SRuslan Bukin int mask, error; 6405d43fd68SRuslan Bukin 6415d43fd68SRuslan Bukin sc = ifp->if_softc; 6425d43fd68SRuslan Bukin ifr = (struct ifreq *)data; 6435d43fd68SRuslan Bukin 6445d43fd68SRuslan Bukin error = 0; 6455d43fd68SRuslan Bukin switch (cmd) { 6465d43fd68SRuslan Bukin case SIOCSIFFLAGS: 6475d43fd68SRuslan Bukin DWC_LOCK(sc); 6485d43fd68SRuslan Bukin if (ifp->if_flags & IFF_UP) { 6495d43fd68SRuslan Bukin if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 6505d43fd68SRuslan Bukin if ((ifp->if_flags ^ sc->if_flags) & 6515d43fd68SRuslan Bukin (IFF_PROMISC | IFF_ALLMULTI)) 6525d43fd68SRuslan Bukin dwc_setup_rxfilter(sc); 6535d43fd68SRuslan Bukin } else { 6545d43fd68SRuslan Bukin if (!sc->is_detaching) 6555d43fd68SRuslan Bukin dwc_init_locked(sc); 6565d43fd68SRuslan Bukin } 6575d43fd68SRuslan Bukin } else { 6585d43fd68SRuslan Bukin if (ifp->if_drv_flags & IFF_DRV_RUNNING) 6595d43fd68SRuslan Bukin dwc_stop_locked(sc); 6605d43fd68SRuslan Bukin } 6615d43fd68SRuslan Bukin sc->if_flags = ifp->if_flags; 6625d43fd68SRuslan Bukin DWC_UNLOCK(sc); 6635d43fd68SRuslan Bukin break; 6645d43fd68SRuslan Bukin case SIOCADDMULTI: 6655d43fd68SRuslan Bukin case SIOCDELMULTI: 6665d43fd68SRuslan Bukin if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 6675d43fd68SRuslan Bukin DWC_LOCK(sc); 6685d43fd68SRuslan Bukin dwc_setup_rxfilter(sc); 6695d43fd68SRuslan Bukin DWC_UNLOCK(sc); 6705d43fd68SRuslan Bukin } 6715d43fd68SRuslan Bukin break; 6725d43fd68SRuslan Bukin case SIOCSIFMEDIA: 6735d43fd68SRuslan Bukin case SIOCGIFMEDIA: 6745d43fd68SRuslan Bukin mii = sc->mii_softc; 6755d43fd68SRuslan Bukin error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); 6765d43fd68SRuslan Bukin break; 6775d43fd68SRuslan Bukin case SIOCSIFCAP: 6785d43fd68SRuslan Bukin mask = ifp->if_capenable ^ ifr->ifr_reqcap; 6795d43fd68SRuslan Bukin if (mask & IFCAP_VLAN_MTU) { 6805d43fd68SRuslan Bukin /* No work to do except acknowledge the change took */ 6815d43fd68SRuslan Bukin ifp->if_capenable ^= IFCAP_VLAN_MTU; 6825d43fd68SRuslan Bukin } 6835d43fd68SRuslan Bukin break; 6845d43fd68SRuslan Bukin 6855d43fd68SRuslan Bukin default: 6865d43fd68SRuslan Bukin error = ether_ioctl(ifp, cmd, data); 6875d43fd68SRuslan Bukin break; 6885d43fd68SRuslan Bukin } 6895d43fd68SRuslan Bukin 6905d43fd68SRuslan Bukin return (error); 6915d43fd68SRuslan Bukin } 6925d43fd68SRuslan Bukin 6935d43fd68SRuslan Bukin static void 6945d43fd68SRuslan Bukin dwc_txfinish_locked(struct dwc_softc *sc) 6955d43fd68SRuslan Bukin { 6965d43fd68SRuslan Bukin struct dwc_bufmap *bmap; 6975d43fd68SRuslan Bukin struct dwc_hwdesc *desc; 6989500101cSLuiz Otavio O Souza struct ifnet *ifp; 6995d43fd68SRuslan Bukin 7005d43fd68SRuslan Bukin DWC_ASSERT_LOCKED(sc); 7015d43fd68SRuslan Bukin 702ecb3497fSLuiz Otavio O Souza ifp = sc->ifp; 7035d43fd68SRuslan Bukin while (sc->tx_idx_tail != sc->tx_idx_head) { 7045d43fd68SRuslan Bukin desc = &sc->txdesc_ring[sc->tx_idx_tail]; 7055d43fd68SRuslan Bukin if ((desc->tdes0 & DDESC_TDES0_OWN) != 0) 7065d43fd68SRuslan Bukin break; 7075d43fd68SRuslan Bukin bmap = &sc->txbuf_map[sc->tx_idx_tail]; 7085d43fd68SRuslan Bukin bus_dmamap_sync(sc->txbuf_tag, bmap->map, 7095d43fd68SRuslan Bukin BUS_DMASYNC_POSTWRITE); 7105d43fd68SRuslan Bukin bus_dmamap_unload(sc->txbuf_tag, bmap->map); 7115d43fd68SRuslan Bukin m_freem(bmap->mbuf); 7125d43fd68SRuslan Bukin bmap->mbuf = NULL; 7135d43fd68SRuslan Bukin dwc_setup_txdesc(sc, sc->tx_idx_tail, 0, 0); 7145d43fd68SRuslan Bukin sc->tx_idx_tail = next_txidx(sc, sc->tx_idx_tail); 7159500101cSLuiz Otavio O Souza ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 716a5221d68SLuiz Otavio O Souza if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 7175d43fd68SRuslan Bukin } 7185d43fd68SRuslan Bukin 7195d43fd68SRuslan Bukin /* If there are no buffers outstanding, muzzle the watchdog. */ 7205d43fd68SRuslan Bukin if (sc->tx_idx_tail == sc->tx_idx_head) { 7215d43fd68SRuslan Bukin sc->tx_watchdog_count = 0; 7225d43fd68SRuslan Bukin } 7235d43fd68SRuslan Bukin } 7245d43fd68SRuslan Bukin 7255d43fd68SRuslan Bukin static void 7265d43fd68SRuslan Bukin dwc_rxfinish_locked(struct dwc_softc *sc) 7275d43fd68SRuslan Bukin { 7285d43fd68SRuslan Bukin struct ifnet *ifp; 7295d43fd68SRuslan Bukin struct mbuf *m0; 7305d43fd68SRuslan Bukin struct mbuf *m; 731ff0752c8SLuiz Otavio O Souza int error, idx, len; 732ff0752c8SLuiz Otavio O Souza uint32_t rdes0; 7335d43fd68SRuslan Bukin 7345d43fd68SRuslan Bukin ifp = sc->ifp; 7355d43fd68SRuslan Bukin 7365d43fd68SRuslan Bukin for (;;) { 7375d43fd68SRuslan Bukin idx = sc->rx_idx; 7385d43fd68SRuslan Bukin 7395d43fd68SRuslan Bukin rdes0 = sc->rxdesc_ring[idx].tdes0; 7405d43fd68SRuslan Bukin if ((rdes0 & DDESC_RDES0_OWN) != 0) 7415d43fd68SRuslan Bukin break; 7425d43fd68SRuslan Bukin 7435d43fd68SRuslan Bukin bus_dmamap_sync(sc->rxbuf_tag, sc->rxbuf_map[idx].map, 7445d43fd68SRuslan Bukin BUS_DMASYNC_POSTREAD); 7455d43fd68SRuslan Bukin bus_dmamap_unload(sc->rxbuf_tag, sc->rxbuf_map[idx].map); 7465d43fd68SRuslan Bukin 7475d43fd68SRuslan Bukin len = (rdes0 >> DDESC_RDES0_FL_SHIFT) & DDESC_RDES0_FL_MASK; 7485d43fd68SRuslan Bukin if (len != 0) { 7495d43fd68SRuslan Bukin m = sc->rxbuf_map[idx].mbuf; 7505d43fd68SRuslan Bukin m->m_pkthdr.rcvif = ifp; 7515d43fd68SRuslan Bukin m->m_pkthdr.len = len; 7525d43fd68SRuslan Bukin m->m_len = len; 75308c95c53SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 7545d43fd68SRuslan Bukin 7555d43fd68SRuslan Bukin DWC_UNLOCK(sc); 7565d43fd68SRuslan Bukin (*ifp->if_input)(ifp, m); 7575d43fd68SRuslan Bukin DWC_LOCK(sc); 7585d43fd68SRuslan Bukin } else { 7595d43fd68SRuslan Bukin /* XXX Zero-length packet ? */ 7605d43fd68SRuslan Bukin } 7615d43fd68SRuslan Bukin 7625d43fd68SRuslan Bukin if ((m0 = dwc_alloc_mbufcl(sc)) != NULL) { 7635d43fd68SRuslan Bukin if ((error = dwc_setup_rxbuf(sc, idx, m0)) != 0) { 7645d43fd68SRuslan Bukin /* 7655d43fd68SRuslan Bukin * XXX Now what? 7665d43fd68SRuslan Bukin * We've got a hole in the rx ring. 7675d43fd68SRuslan Bukin */ 7685d43fd68SRuslan Bukin } 7695d43fd68SRuslan Bukin } else 7705d43fd68SRuslan Bukin if_inc_counter(sc->ifp, IFCOUNTER_IQDROPS, 1); 7715d43fd68SRuslan Bukin 7725d43fd68SRuslan Bukin sc->rx_idx = next_rxidx(sc, sc->rx_idx); 7735d43fd68SRuslan Bukin } 7745d43fd68SRuslan Bukin } 7755d43fd68SRuslan Bukin 7765d43fd68SRuslan Bukin static void 7775d43fd68SRuslan Bukin dwc_intr(void *arg) 7785d43fd68SRuslan Bukin { 7795d43fd68SRuslan Bukin struct dwc_softc *sc; 7805d43fd68SRuslan Bukin uint32_t reg; 7815d43fd68SRuslan Bukin 7825d43fd68SRuslan Bukin sc = arg; 7835d43fd68SRuslan Bukin 7845d43fd68SRuslan Bukin DWC_LOCK(sc); 7855d43fd68SRuslan Bukin 7865d43fd68SRuslan Bukin reg = READ4(sc, INTERRUPT_STATUS); 7878fbc5d18SLuiz Otavio O Souza if (reg) 7885d43fd68SRuslan Bukin READ4(sc, SGMII_RGMII_SMII_CTRL_STATUS); 7895d43fd68SRuslan Bukin 7905d43fd68SRuslan Bukin reg = READ4(sc, DMA_STATUS); 7915d43fd68SRuslan Bukin if (reg & DMA_STATUS_NIS) { 7925d43fd68SRuslan Bukin if (reg & DMA_STATUS_RI) 7935d43fd68SRuslan Bukin dwc_rxfinish_locked(sc); 7945d43fd68SRuslan Bukin 7959500101cSLuiz Otavio O Souza if (reg & DMA_STATUS_TI) { 7965d43fd68SRuslan Bukin dwc_txfinish_locked(sc); 7979500101cSLuiz Otavio O Souza dwc_txstart_locked(sc); 7989500101cSLuiz Otavio O Souza } 7995d43fd68SRuslan Bukin } 8005d43fd68SRuslan Bukin 8015d43fd68SRuslan Bukin if (reg & DMA_STATUS_AIS) { 8025d43fd68SRuslan Bukin if (reg & DMA_STATUS_FBI) { 8035d43fd68SRuslan Bukin /* Fatal bus error */ 8045d43fd68SRuslan Bukin device_printf(sc->dev, 8055d43fd68SRuslan Bukin "Ethernet DMA error, restarting controller.\n"); 8065d43fd68SRuslan Bukin dwc_stop_locked(sc); 8075d43fd68SRuslan Bukin dwc_init_locked(sc); 8085d43fd68SRuslan Bukin } 8095d43fd68SRuslan Bukin } 8105d43fd68SRuslan Bukin 8115d43fd68SRuslan Bukin WRITE4(sc, DMA_STATUS, reg & DMA_STATUS_INTR_MASK); 8125d43fd68SRuslan Bukin DWC_UNLOCK(sc); 8135d43fd68SRuslan Bukin } 8145d43fd68SRuslan Bukin 8155d43fd68SRuslan Bukin static int 8165d43fd68SRuslan Bukin setup_dma(struct dwc_softc *sc) 8175d43fd68SRuslan Bukin { 8185d43fd68SRuslan Bukin struct mbuf *m; 8195d43fd68SRuslan Bukin int error; 8205d43fd68SRuslan Bukin int nidx; 8215d43fd68SRuslan Bukin int idx; 8225d43fd68SRuslan Bukin 8235d43fd68SRuslan Bukin /* 8245d43fd68SRuslan Bukin * Set up TX descriptor ring, descriptors, and dma maps. 8255d43fd68SRuslan Bukin */ 8265d43fd68SRuslan Bukin error = bus_dma_tag_create( 8275d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 8285d43fd68SRuslan Bukin DWC_DESC_RING_ALIGN, 0, /* alignment, boundary */ 8295d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 8305d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 8315d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 8325d43fd68SRuslan Bukin TX_DESC_SIZE, 1, /* maxsize, nsegments */ 8335d43fd68SRuslan Bukin TX_DESC_SIZE, /* maxsegsize */ 8345d43fd68SRuslan Bukin 0, /* flags */ 8355d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 8365d43fd68SRuslan Bukin &sc->txdesc_tag); 8375d43fd68SRuslan Bukin if (error != 0) { 8385d43fd68SRuslan Bukin device_printf(sc->dev, 8395d43fd68SRuslan Bukin "could not create TX ring DMA tag.\n"); 8405d43fd68SRuslan Bukin goto out; 8415d43fd68SRuslan Bukin } 8425d43fd68SRuslan Bukin 8435d43fd68SRuslan Bukin error = bus_dmamem_alloc(sc->txdesc_tag, (void**)&sc->txdesc_ring, 8445d43fd68SRuslan Bukin BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, 8455d43fd68SRuslan Bukin &sc->txdesc_map); 8465d43fd68SRuslan Bukin if (error != 0) { 8475d43fd68SRuslan Bukin device_printf(sc->dev, 8485d43fd68SRuslan Bukin "could not allocate TX descriptor ring.\n"); 8495d43fd68SRuslan Bukin goto out; 8505d43fd68SRuslan Bukin } 8515d43fd68SRuslan Bukin 8525d43fd68SRuslan Bukin error = bus_dmamap_load(sc->txdesc_tag, sc->txdesc_map, 8535d43fd68SRuslan Bukin sc->txdesc_ring, TX_DESC_SIZE, dwc_get1paddr, 8545d43fd68SRuslan Bukin &sc->txdesc_ring_paddr, 0); 8555d43fd68SRuslan Bukin if (error != 0) { 8565d43fd68SRuslan Bukin device_printf(sc->dev, 8575d43fd68SRuslan Bukin "could not load TX descriptor ring map.\n"); 8585d43fd68SRuslan Bukin goto out; 8595d43fd68SRuslan Bukin } 8605d43fd68SRuslan Bukin 8615d43fd68SRuslan Bukin for (idx = 0; idx < TX_DESC_COUNT; idx++) { 8625d43fd68SRuslan Bukin nidx = next_txidx(sc, idx); 8631d7a7309SLuiz Otavio O Souza sc->txdesc_ring[idx].addr_next = sc->txdesc_ring_paddr + 8645d43fd68SRuslan Bukin (nidx * sizeof(struct dwc_hwdesc)); 8655d43fd68SRuslan Bukin } 8665d43fd68SRuslan Bukin 8675d43fd68SRuslan Bukin error = bus_dma_tag_create( 8685d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 8695d43fd68SRuslan Bukin 1, 0, /* alignment, boundary */ 8705d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 8715d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 8725d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 8735d43fd68SRuslan Bukin MCLBYTES, 1, /* maxsize, nsegments */ 8745d43fd68SRuslan Bukin MCLBYTES, /* maxsegsize */ 8755d43fd68SRuslan Bukin 0, /* flags */ 8765d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 8775d43fd68SRuslan Bukin &sc->txbuf_tag); 8785d43fd68SRuslan Bukin if (error != 0) { 8795d43fd68SRuslan Bukin device_printf(sc->dev, 8805d43fd68SRuslan Bukin "could not create TX ring DMA tag.\n"); 8815d43fd68SRuslan Bukin goto out; 8825d43fd68SRuslan Bukin } 8835d43fd68SRuslan Bukin 8845d43fd68SRuslan Bukin for (idx = 0; idx < TX_DESC_COUNT; idx++) { 8855d43fd68SRuslan Bukin error = bus_dmamap_create(sc->txbuf_tag, BUS_DMA_COHERENT, 8865d43fd68SRuslan Bukin &sc->txbuf_map[idx].map); 8875d43fd68SRuslan Bukin if (error != 0) { 8885d43fd68SRuslan Bukin device_printf(sc->dev, 8895d43fd68SRuslan Bukin "could not create TX buffer DMA map.\n"); 8905d43fd68SRuslan Bukin goto out; 8915d43fd68SRuslan Bukin } 8925d43fd68SRuslan Bukin dwc_setup_txdesc(sc, idx, 0, 0); 8935d43fd68SRuslan Bukin } 8945d43fd68SRuslan Bukin 8955d43fd68SRuslan Bukin /* 8965d43fd68SRuslan Bukin * Set up RX descriptor ring, descriptors, dma maps, and mbufs. 8975d43fd68SRuslan Bukin */ 8985d43fd68SRuslan Bukin error = bus_dma_tag_create( 8995d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 9005d43fd68SRuslan Bukin DWC_DESC_RING_ALIGN, 0, /* alignment, boundary */ 9015d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 9025d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 9035d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 9045d43fd68SRuslan Bukin RX_DESC_SIZE, 1, /* maxsize, nsegments */ 9055d43fd68SRuslan Bukin RX_DESC_SIZE, /* maxsegsize */ 9065d43fd68SRuslan Bukin 0, /* flags */ 9075d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 9085d43fd68SRuslan Bukin &sc->rxdesc_tag); 9095d43fd68SRuslan Bukin if (error != 0) { 9105d43fd68SRuslan Bukin device_printf(sc->dev, 9115d43fd68SRuslan Bukin "could not create RX ring DMA tag.\n"); 9125d43fd68SRuslan Bukin goto out; 9135d43fd68SRuslan Bukin } 9145d43fd68SRuslan Bukin 9155d43fd68SRuslan Bukin error = bus_dmamem_alloc(sc->rxdesc_tag, (void **)&sc->rxdesc_ring, 9165d43fd68SRuslan Bukin BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO, 9175d43fd68SRuslan Bukin &sc->rxdesc_map); 9185d43fd68SRuslan Bukin if (error != 0) { 9195d43fd68SRuslan Bukin device_printf(sc->dev, 9205d43fd68SRuslan Bukin "could not allocate RX descriptor ring.\n"); 9215d43fd68SRuslan Bukin goto out; 9225d43fd68SRuslan Bukin } 9235d43fd68SRuslan Bukin 9245d43fd68SRuslan Bukin error = bus_dmamap_load(sc->rxdesc_tag, sc->rxdesc_map, 9255d43fd68SRuslan Bukin sc->rxdesc_ring, RX_DESC_SIZE, dwc_get1paddr, 9265d43fd68SRuslan Bukin &sc->rxdesc_ring_paddr, 0); 9275d43fd68SRuslan Bukin if (error != 0) { 9285d43fd68SRuslan Bukin device_printf(sc->dev, 9295d43fd68SRuslan Bukin "could not load RX descriptor ring map.\n"); 9305d43fd68SRuslan Bukin goto out; 9315d43fd68SRuslan Bukin } 9325d43fd68SRuslan Bukin 9335d43fd68SRuslan Bukin error = bus_dma_tag_create( 9345d43fd68SRuslan Bukin bus_get_dma_tag(sc->dev), /* Parent tag. */ 9355d43fd68SRuslan Bukin 1, 0, /* alignment, boundary */ 9365d43fd68SRuslan Bukin BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 9375d43fd68SRuslan Bukin BUS_SPACE_MAXADDR, /* highaddr */ 9385d43fd68SRuslan Bukin NULL, NULL, /* filter, filterarg */ 9395d43fd68SRuslan Bukin MCLBYTES, 1, /* maxsize, nsegments */ 9405d43fd68SRuslan Bukin MCLBYTES, /* maxsegsize */ 9415d43fd68SRuslan Bukin 0, /* flags */ 9425d43fd68SRuslan Bukin NULL, NULL, /* lockfunc, lockarg */ 9435d43fd68SRuslan Bukin &sc->rxbuf_tag); 9445d43fd68SRuslan Bukin if (error != 0) { 9455d43fd68SRuslan Bukin device_printf(sc->dev, 9465d43fd68SRuslan Bukin "could not create RX buf DMA tag.\n"); 9475d43fd68SRuslan Bukin goto out; 9485d43fd68SRuslan Bukin } 9495d43fd68SRuslan Bukin 9505d43fd68SRuslan Bukin for (idx = 0; idx < RX_DESC_COUNT; idx++) { 9515d43fd68SRuslan Bukin error = bus_dmamap_create(sc->rxbuf_tag, BUS_DMA_COHERENT, 9525d43fd68SRuslan Bukin &sc->rxbuf_map[idx].map); 9535d43fd68SRuslan Bukin if (error != 0) { 9545d43fd68SRuslan Bukin device_printf(sc->dev, 9555d43fd68SRuslan Bukin "could not create RX buffer DMA map.\n"); 9565d43fd68SRuslan Bukin goto out; 9575d43fd68SRuslan Bukin } 9585d43fd68SRuslan Bukin if ((m = dwc_alloc_mbufcl(sc)) == NULL) { 9595d43fd68SRuslan Bukin device_printf(sc->dev, "Could not alloc mbuf\n"); 9605d43fd68SRuslan Bukin error = ENOMEM; 9615d43fd68SRuslan Bukin goto out; 9625d43fd68SRuslan Bukin } 9635d43fd68SRuslan Bukin if ((error = dwc_setup_rxbuf(sc, idx, m)) != 0) { 9645d43fd68SRuslan Bukin device_printf(sc->dev, 9655d43fd68SRuslan Bukin "could not create new RX buffer.\n"); 9665d43fd68SRuslan Bukin goto out; 9675d43fd68SRuslan Bukin } 9685d43fd68SRuslan Bukin } 9695d43fd68SRuslan Bukin 9705d43fd68SRuslan Bukin out: 9715d43fd68SRuslan Bukin if (error != 0) 9725d43fd68SRuslan Bukin return (ENXIO); 9735d43fd68SRuslan Bukin 9745d43fd68SRuslan Bukin return (0); 9755d43fd68SRuslan Bukin } 9765d43fd68SRuslan Bukin 9775d43fd68SRuslan Bukin static int 9785d43fd68SRuslan Bukin dwc_get_hwaddr(struct dwc_softc *sc, uint8_t *hwaddr) 9795d43fd68SRuslan Bukin { 980ff0752c8SLuiz Otavio O Souza uint32_t hi, lo, rnd; 9815d43fd68SRuslan Bukin 9825d43fd68SRuslan Bukin /* 9835d43fd68SRuslan Bukin * Try to recover a MAC address from the running hardware. If there's 9845d43fd68SRuslan Bukin * something non-zero there, assume the bootloader did the right thing 9855d43fd68SRuslan Bukin * and just use it. 9865d43fd68SRuslan Bukin * 9875d43fd68SRuslan Bukin * Otherwise, set the address to a convenient locally assigned address, 9885d43fd68SRuslan Bukin * 'bsd' + random 24 low-order bits. 'b' is 0x62, which has the locally 9895d43fd68SRuslan Bukin * assigned bit set, and the broadcast/multicast bit clear. 9905d43fd68SRuslan Bukin */ 9915d43fd68SRuslan Bukin lo = READ4(sc, MAC_ADDRESS_LOW(0)); 9925d43fd68SRuslan Bukin hi = READ4(sc, MAC_ADDRESS_HIGH(0)) & 0xffff; 9935d43fd68SRuslan Bukin if ((lo != 0xffffffff) || (hi != 0xffff)) { 9945d43fd68SRuslan Bukin hwaddr[0] = (lo >> 0) & 0xff; 9955d43fd68SRuslan Bukin hwaddr[1] = (lo >> 8) & 0xff; 9965d43fd68SRuslan Bukin hwaddr[2] = (lo >> 16) & 0xff; 9975d43fd68SRuslan Bukin hwaddr[3] = (lo >> 24) & 0xff; 9985d43fd68SRuslan Bukin hwaddr[4] = (hi >> 0) & 0xff; 9995d43fd68SRuslan Bukin hwaddr[5] = (hi >> 8) & 0xff; 10005d43fd68SRuslan Bukin } else { 10015d43fd68SRuslan Bukin rnd = arc4random() & 0x00ffffff; 10025d43fd68SRuslan Bukin hwaddr[0] = 'b'; 10035d43fd68SRuslan Bukin hwaddr[1] = 's'; 10045d43fd68SRuslan Bukin hwaddr[2] = 'd'; 10055d43fd68SRuslan Bukin hwaddr[3] = rnd >> 16; 10065d43fd68SRuslan Bukin hwaddr[4] = rnd >> 8; 10075d43fd68SRuslan Bukin hwaddr[5] = rnd >> 0; 10085d43fd68SRuslan Bukin } 10095d43fd68SRuslan Bukin 10105d43fd68SRuslan Bukin return (0); 10115d43fd68SRuslan Bukin } 10125d43fd68SRuslan Bukin 10135d43fd68SRuslan Bukin static int 10145d43fd68SRuslan Bukin dwc_probe(device_t dev) 10155d43fd68SRuslan Bukin { 10165d43fd68SRuslan Bukin 10175d43fd68SRuslan Bukin if (!ofw_bus_status_okay(dev)) 10185d43fd68SRuslan Bukin return (ENXIO); 10195d43fd68SRuslan Bukin 10205d43fd68SRuslan Bukin if (!ofw_bus_is_compatible(dev, "snps,dwmac")) 10215d43fd68SRuslan Bukin return (ENXIO); 10225d43fd68SRuslan Bukin 10235d43fd68SRuslan Bukin device_set_desc(dev, "Gigabit Ethernet Controller"); 10245d43fd68SRuslan Bukin return (BUS_PROBE_DEFAULT); 10255d43fd68SRuslan Bukin } 10265d43fd68SRuslan Bukin 10275d43fd68SRuslan Bukin static int 10285d43fd68SRuslan Bukin dwc_attach(device_t dev) 10295d43fd68SRuslan Bukin { 10305d43fd68SRuslan Bukin uint8_t macaddr[ETHER_ADDR_LEN]; 10315d43fd68SRuslan Bukin struct dwc_softc *sc; 10325d43fd68SRuslan Bukin struct ifnet *ifp; 1033ff0752c8SLuiz Otavio O Souza int error, i; 1034ff0752c8SLuiz Otavio O Souza uint32_t reg; 10355d43fd68SRuslan Bukin 10365d43fd68SRuslan Bukin sc = device_get_softc(dev); 10375d43fd68SRuslan Bukin sc->dev = dev; 10385d43fd68SRuslan Bukin sc->rx_idx = 0; 10395d43fd68SRuslan Bukin sc->txcount = TX_DESC_COUNT; 1040*5df53927SLuiz Otavio O Souza sc->mii_clk = IF_DWC_MII_CLK(dev); 1041*5df53927SLuiz Otavio O Souza sc->mactype = IF_DWC_MAC_TYPE(dev); 1042*5df53927SLuiz Otavio O Souza 1043*5df53927SLuiz Otavio O Souza if (IF_DWC_INIT(dev) != 0) 1044*5df53927SLuiz Otavio O Souza return (ENXIO); 10455d43fd68SRuslan Bukin 10465d43fd68SRuslan Bukin if (bus_alloc_resources(dev, dwc_spec, sc->res)) { 10475d43fd68SRuslan Bukin device_printf(dev, "could not allocate resources\n"); 10485d43fd68SRuslan Bukin return (ENXIO); 10495d43fd68SRuslan Bukin } 10505d43fd68SRuslan Bukin 10515d43fd68SRuslan Bukin /* Memory interface */ 10525d43fd68SRuslan Bukin sc->bst = rman_get_bustag(sc->res[0]); 10535d43fd68SRuslan Bukin sc->bsh = rman_get_bushandle(sc->res[0]); 10545d43fd68SRuslan Bukin 10555d43fd68SRuslan Bukin /* Read MAC before reset */ 10565d43fd68SRuslan Bukin if (dwc_get_hwaddr(sc, macaddr)) { 10575d43fd68SRuslan Bukin device_printf(sc->dev, "can't get mac\n"); 10585d43fd68SRuslan Bukin return (ENXIO); 10595d43fd68SRuslan Bukin } 10605d43fd68SRuslan Bukin 10615d43fd68SRuslan Bukin /* Reset */ 10625d43fd68SRuslan Bukin reg = READ4(sc, BUS_MODE); 10635d43fd68SRuslan Bukin reg |= (BUS_MODE_SWR); 10645d43fd68SRuslan Bukin WRITE4(sc, BUS_MODE, reg); 10655d43fd68SRuslan Bukin 1066d8e5258dSRuslan Bukin for (i = 0; i < MAC_RESET_TIMEOUT; i++) { 10675d43fd68SRuslan Bukin if ((READ4(sc, BUS_MODE) & BUS_MODE_SWR) == 0) 10685d43fd68SRuslan Bukin break; 10695d43fd68SRuslan Bukin DELAY(10); 10705d43fd68SRuslan Bukin } 1071d8e5258dSRuslan Bukin if (i >= MAC_RESET_TIMEOUT) { 10725d43fd68SRuslan Bukin device_printf(sc->dev, "Can't reset DWC.\n"); 10735d43fd68SRuslan Bukin return (ENXIO); 10745d43fd68SRuslan Bukin } 10755d43fd68SRuslan Bukin 1076*5df53927SLuiz Otavio O Souza if (sc->mactype == DWC_GMAC_ALT_DESC) { 1077*5df53927SLuiz Otavio O Souza reg = BUS_MODE_FIXEDBURST; 1078*5df53927SLuiz Otavio O Souza reg |= (BUS_MODE_PRIORXTX_41 << BUS_MODE_PRIORXTX_SHIFT); 1079*5df53927SLuiz Otavio O Souza } else 1080*5df53927SLuiz Otavio O Souza reg = (BUS_MODE_EIGHTXPBL); 10815d43fd68SRuslan Bukin reg |= (BUS_MODE_PBL_BEATS_8 << BUS_MODE_PBL_SHIFT); 10825d43fd68SRuslan Bukin WRITE4(sc, BUS_MODE, reg); 10835d43fd68SRuslan Bukin 10845d43fd68SRuslan Bukin /* 10855d43fd68SRuslan Bukin * DMA must be stop while changing descriptor list addresses. 10865d43fd68SRuslan Bukin */ 10875d43fd68SRuslan Bukin reg = READ4(sc, OPERATION_MODE); 10885d43fd68SRuslan Bukin reg &= ~(MODE_ST | MODE_SR); 10895d43fd68SRuslan Bukin WRITE4(sc, OPERATION_MODE, reg); 10905d43fd68SRuslan Bukin 10915d43fd68SRuslan Bukin if (setup_dma(sc)) 10925d43fd68SRuslan Bukin return (ENXIO); 10935d43fd68SRuslan Bukin 10945d43fd68SRuslan Bukin /* Setup addresses */ 10955d43fd68SRuslan Bukin WRITE4(sc, RX_DESCR_LIST_ADDR, sc->rxdesc_ring_paddr); 10965d43fd68SRuslan Bukin WRITE4(sc, TX_DESCR_LIST_ADDR, sc->txdesc_ring_paddr); 10975d43fd68SRuslan Bukin 1098d8e5258dSRuslan Bukin mtx_init(&sc->mtx, device_get_nameunit(sc->dev), 1099d8e5258dSRuslan Bukin MTX_NETWORK_LOCK, MTX_DEF); 1100d8e5258dSRuslan Bukin 1101d8e5258dSRuslan Bukin callout_init_mtx(&sc->dwc_callout, &sc->mtx, 0); 1102d8e5258dSRuslan Bukin 1103d8e5258dSRuslan Bukin /* Setup interrupt handler. */ 1104d8e5258dSRuslan Bukin error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_NET | INTR_MPSAFE, 1105d8e5258dSRuslan Bukin NULL, dwc_intr, sc, &sc->intr_cookie); 1106d8e5258dSRuslan Bukin if (error != 0) { 1107d8e5258dSRuslan Bukin device_printf(dev, "could not setup interrupt handler.\n"); 1108d8e5258dSRuslan Bukin return (ENXIO); 1109d8e5258dSRuslan Bukin } 1110d8e5258dSRuslan Bukin 11115d43fd68SRuslan Bukin /* Set up the ethernet interface. */ 11125d43fd68SRuslan Bukin sc->ifp = ifp = if_alloc(IFT_ETHER); 11135d43fd68SRuslan Bukin 11145d43fd68SRuslan Bukin ifp->if_softc = sc; 11155d43fd68SRuslan Bukin if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 11165d43fd68SRuslan Bukin ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 11175d43fd68SRuslan Bukin ifp->if_capabilities = IFCAP_VLAN_MTU; 11185d43fd68SRuslan Bukin ifp->if_capenable = ifp->if_capabilities; 11195d43fd68SRuslan Bukin ifp->if_start = dwc_txstart; 11205d43fd68SRuslan Bukin ifp->if_ioctl = dwc_ioctl; 11215d43fd68SRuslan Bukin ifp->if_init = dwc_init; 11225d43fd68SRuslan Bukin IFQ_SET_MAXLEN(&ifp->if_snd, TX_DESC_COUNT - 1); 11235d43fd68SRuslan Bukin ifp->if_snd.ifq_drv_maxlen = TX_DESC_COUNT - 1; 11245d43fd68SRuslan Bukin IFQ_SET_READY(&ifp->if_snd); 11255d43fd68SRuslan Bukin 11265d43fd68SRuslan Bukin /* Attach the mii driver. */ 11275d43fd68SRuslan Bukin error = mii_attach(dev, &sc->miibus, ifp, dwc_media_change, 11285d43fd68SRuslan Bukin dwc_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY, 11295d43fd68SRuslan Bukin MII_OFFSET_ANY, 0); 11305d43fd68SRuslan Bukin 11315d43fd68SRuslan Bukin if (error != 0) { 11325d43fd68SRuslan Bukin device_printf(dev, "PHY attach failed\n"); 11335d43fd68SRuslan Bukin return (ENXIO); 11345d43fd68SRuslan Bukin } 11355d43fd68SRuslan Bukin sc->mii_softc = device_get_softc(sc->miibus); 11365d43fd68SRuslan Bukin 11375d43fd68SRuslan Bukin /* All ready to run, attach the ethernet interface. */ 11385d43fd68SRuslan Bukin ether_ifattach(ifp, macaddr); 11395d43fd68SRuslan Bukin sc->is_attached = true; 11405d43fd68SRuslan Bukin 11415d43fd68SRuslan Bukin return (0); 11425d43fd68SRuslan Bukin } 11435d43fd68SRuslan Bukin 11445d43fd68SRuslan Bukin static int 11455d43fd68SRuslan Bukin dwc_miibus_read_reg(device_t dev, int phy, int reg) 11465d43fd68SRuslan Bukin { 11475d43fd68SRuslan Bukin struct dwc_softc *sc; 11485d43fd68SRuslan Bukin uint16_t mii; 11495d43fd68SRuslan Bukin size_t cnt; 11505d43fd68SRuslan Bukin int rv = 0; 11515d43fd68SRuslan Bukin 11525d43fd68SRuslan Bukin sc = device_get_softc(dev); 11535d43fd68SRuslan Bukin 11545d43fd68SRuslan Bukin mii = ((phy & GMII_ADDRESS_PA_MASK) << GMII_ADDRESS_PA_SHIFT) 11555d43fd68SRuslan Bukin | ((reg & GMII_ADDRESS_GR_MASK) << GMII_ADDRESS_GR_SHIFT) 11565d43fd68SRuslan Bukin | (sc->mii_clk << GMII_ADDRESS_CR_SHIFT) 11575d43fd68SRuslan Bukin | GMII_ADDRESS_GB; /* Busy flag */ 11585d43fd68SRuslan Bukin 11595d43fd68SRuslan Bukin WRITE4(sc, GMII_ADDRESS, mii); 11605d43fd68SRuslan Bukin 11615d43fd68SRuslan Bukin for (cnt = 0; cnt < 1000; cnt++) { 11625d43fd68SRuslan Bukin if (!(READ4(sc, GMII_ADDRESS) & GMII_ADDRESS_GB)) { 11635d43fd68SRuslan Bukin rv = READ4(sc, GMII_DATA); 11645d43fd68SRuslan Bukin break; 11655d43fd68SRuslan Bukin } 11665d43fd68SRuslan Bukin DELAY(10); 11675d43fd68SRuslan Bukin } 11685d43fd68SRuslan Bukin 11695d43fd68SRuslan Bukin return rv; 11705d43fd68SRuslan Bukin } 11715d43fd68SRuslan Bukin 11725d43fd68SRuslan Bukin static int 11735d43fd68SRuslan Bukin dwc_miibus_write_reg(device_t dev, int phy, int reg, int val) 11745d43fd68SRuslan Bukin { 11755d43fd68SRuslan Bukin struct dwc_softc *sc; 11765d43fd68SRuslan Bukin uint16_t mii; 11775d43fd68SRuslan Bukin size_t cnt; 11785d43fd68SRuslan Bukin 11795d43fd68SRuslan Bukin sc = device_get_softc(dev); 11805d43fd68SRuslan Bukin 11815d43fd68SRuslan Bukin mii = ((phy & GMII_ADDRESS_PA_MASK) << GMII_ADDRESS_PA_SHIFT) 11825d43fd68SRuslan Bukin | ((reg & GMII_ADDRESS_GR_MASK) << GMII_ADDRESS_GR_SHIFT) 11835d43fd68SRuslan Bukin | (sc->mii_clk << GMII_ADDRESS_CR_SHIFT) 11845d43fd68SRuslan Bukin | GMII_ADDRESS_GB | GMII_ADDRESS_GW; 11855d43fd68SRuslan Bukin 11865d43fd68SRuslan Bukin WRITE4(sc, GMII_DATA, val); 11875d43fd68SRuslan Bukin WRITE4(sc, GMII_ADDRESS, mii); 11885d43fd68SRuslan Bukin 11895d43fd68SRuslan Bukin for (cnt = 0; cnt < 1000; cnt++) { 11905d43fd68SRuslan Bukin if (!(READ4(sc, GMII_ADDRESS) & GMII_ADDRESS_GB)) { 11915d43fd68SRuslan Bukin break; 11925d43fd68SRuslan Bukin } 11935d43fd68SRuslan Bukin DELAY(10); 11945d43fd68SRuslan Bukin } 11955d43fd68SRuslan Bukin 11965d43fd68SRuslan Bukin return (0); 11975d43fd68SRuslan Bukin } 11985d43fd68SRuslan Bukin 11995d43fd68SRuslan Bukin static void 12005d43fd68SRuslan Bukin dwc_miibus_statchg(device_t dev) 12015d43fd68SRuslan Bukin { 12025d43fd68SRuslan Bukin struct dwc_softc *sc; 12035d43fd68SRuslan Bukin struct mii_data *mii; 1204ff0752c8SLuiz Otavio O Souza uint32_t reg; 12055d43fd68SRuslan Bukin 12065d43fd68SRuslan Bukin /* 12075d43fd68SRuslan Bukin * Called by the MII bus driver when the PHY establishes 12085d43fd68SRuslan Bukin * link to set the MAC interface registers. 12095d43fd68SRuslan Bukin */ 12105d43fd68SRuslan Bukin 12115d43fd68SRuslan Bukin sc = device_get_softc(dev); 12125d43fd68SRuslan Bukin 12135d43fd68SRuslan Bukin DWC_ASSERT_LOCKED(sc); 12145d43fd68SRuslan Bukin 12155d43fd68SRuslan Bukin mii = sc->mii_softc; 12165d43fd68SRuslan Bukin 12175d43fd68SRuslan Bukin if (mii->mii_media_status & IFM_ACTIVE) 12185d43fd68SRuslan Bukin sc->link_is_up = true; 12195d43fd68SRuslan Bukin else 12205d43fd68SRuslan Bukin sc->link_is_up = false; 12215d43fd68SRuslan Bukin 12225d43fd68SRuslan Bukin reg = READ4(sc, MAC_CONFIGURATION); 12235d43fd68SRuslan Bukin switch (IFM_SUBTYPE(mii->mii_media_active)) { 12245d43fd68SRuslan Bukin case IFM_1000_T: 12255d43fd68SRuslan Bukin case IFM_1000_SX: 12265d43fd68SRuslan Bukin reg &= ~(CONF_FES | CONF_PS); 12275d43fd68SRuslan Bukin break; 12285d43fd68SRuslan Bukin case IFM_100_TX: 12295d43fd68SRuslan Bukin reg |= (CONF_FES | CONF_PS); 12305d43fd68SRuslan Bukin break; 12315d43fd68SRuslan Bukin case IFM_10_T: 12325d43fd68SRuslan Bukin reg &= ~(CONF_FES); 12335d43fd68SRuslan Bukin reg |= (CONF_PS); 12345d43fd68SRuslan Bukin break; 12355d43fd68SRuslan Bukin case IFM_NONE: 12365d43fd68SRuslan Bukin sc->link_is_up = false; 12375d43fd68SRuslan Bukin return; 12385d43fd68SRuslan Bukin default: 12395d43fd68SRuslan Bukin sc->link_is_up = false; 12405d43fd68SRuslan Bukin device_printf(dev, "Unsupported media %u\n", 12415d43fd68SRuslan Bukin IFM_SUBTYPE(mii->mii_media_active)); 12425d43fd68SRuslan Bukin return; 12435d43fd68SRuslan Bukin } 12445d43fd68SRuslan Bukin if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) 12455d43fd68SRuslan Bukin reg |= (CONF_DM); 12465d43fd68SRuslan Bukin else 12475d43fd68SRuslan Bukin reg &= ~(CONF_DM); 12485d43fd68SRuslan Bukin WRITE4(sc, MAC_CONFIGURATION, reg); 12495d43fd68SRuslan Bukin } 12505d43fd68SRuslan Bukin 12515d43fd68SRuslan Bukin static device_method_t dwc_methods[] = { 12525d43fd68SRuslan Bukin DEVMETHOD(device_probe, dwc_probe), 12535d43fd68SRuslan Bukin DEVMETHOD(device_attach, dwc_attach), 12545d43fd68SRuslan Bukin 12555d43fd68SRuslan Bukin /* MII Interface */ 12565d43fd68SRuslan Bukin DEVMETHOD(miibus_readreg, dwc_miibus_read_reg), 12575d43fd68SRuslan Bukin DEVMETHOD(miibus_writereg, dwc_miibus_write_reg), 12585d43fd68SRuslan Bukin DEVMETHOD(miibus_statchg, dwc_miibus_statchg), 12595d43fd68SRuslan Bukin 12605d43fd68SRuslan Bukin { 0, 0 } 12615d43fd68SRuslan Bukin }; 12625d43fd68SRuslan Bukin 1263*5df53927SLuiz Otavio O Souza driver_t dwc_driver = { 12645d43fd68SRuslan Bukin "dwc", 12655d43fd68SRuslan Bukin dwc_methods, 12665d43fd68SRuslan Bukin sizeof(struct dwc_softc), 12675d43fd68SRuslan Bukin }; 12685d43fd68SRuslan Bukin 12695d43fd68SRuslan Bukin static devclass_t dwc_devclass; 12705d43fd68SRuslan Bukin 12715d43fd68SRuslan Bukin DRIVER_MODULE(dwc, simplebus, dwc_driver, dwc_devclass, 0, 0); 12725d43fd68SRuslan Bukin DRIVER_MODULE(miibus, dwc, miibus_driver, miibus_devclass, 0, 0); 12735d43fd68SRuslan Bukin 12745d43fd68SRuslan Bukin MODULE_DEPEND(dwc, ether, 1, 1, 1); 12755d43fd68SRuslan Bukin MODULE_DEPEND(dwc, miibus, 1, 1, 1); 1276