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