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