xref: /freebsd-src/sys/dev/xilinx/if_xae.c (revision 11a9117871e6037ae7b8011b243939322efce569)
185ae89f4SRuslan Bukin /*-
285ae89f4SRuslan Bukin  * SPDX-License-Identifier: BSD-2-Clause
385ae89f4SRuslan Bukin  *
485ae89f4SRuslan Bukin  * Copyright (c) 2019 Ruslan Bukin <br@bsdpad.com>
585ae89f4SRuslan Bukin  *
685ae89f4SRuslan Bukin  * This software was developed by SRI International and the University of
785ae89f4SRuslan Bukin  * Cambridge Computer Laboratory (Department of Computer Science and
885ae89f4SRuslan Bukin  * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
985ae89f4SRuslan Bukin  * DARPA SSITH research programme.
1085ae89f4SRuslan Bukin  *
1185ae89f4SRuslan Bukin  * Redistribution and use in source and binary forms, with or without
1285ae89f4SRuslan Bukin  * modification, are permitted provided that the following conditions
1385ae89f4SRuslan Bukin  * are met:
1485ae89f4SRuslan Bukin  * 1. Redistributions of source code must retain the above copyright
1585ae89f4SRuslan Bukin  *    notice, this list of conditions and the following disclaimer.
1685ae89f4SRuslan Bukin  * 2. Redistributions in binary form must reproduce the above copyright
1785ae89f4SRuslan Bukin  *    notice, this list of conditions and the following disclaimer in the
1885ae89f4SRuslan Bukin  *    documentation and/or other materials provided with the distribution.
1985ae89f4SRuslan Bukin  *
2085ae89f4SRuslan Bukin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2185ae89f4SRuslan Bukin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2285ae89f4SRuslan Bukin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2385ae89f4SRuslan Bukin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2485ae89f4SRuslan Bukin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2585ae89f4SRuslan Bukin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2685ae89f4SRuslan Bukin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2785ae89f4SRuslan Bukin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2885ae89f4SRuslan Bukin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2985ae89f4SRuslan Bukin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3085ae89f4SRuslan Bukin  * SUCH DAMAGE.
3185ae89f4SRuslan Bukin  */
3285ae89f4SRuslan Bukin 
3385ae89f4SRuslan Bukin #include <sys/param.h>
3485ae89f4SRuslan Bukin #include <sys/systm.h>
3585ae89f4SRuslan Bukin #include <sys/bus.h>
3685ae89f4SRuslan Bukin #include <sys/kernel.h>
3785ae89f4SRuslan Bukin #include <sys/lock.h>
3885ae89f4SRuslan Bukin #include <sys/malloc.h>
3985ae89f4SRuslan Bukin #include <sys/mbuf.h>
4085ae89f4SRuslan Bukin #include <sys/module.h>
4185ae89f4SRuslan Bukin #include <sys/mutex.h>
4285ae89f4SRuslan Bukin #include <sys/rman.h>
4385ae89f4SRuslan Bukin #include <sys/socket.h>
4485ae89f4SRuslan Bukin #include <sys/sockio.h>
4585ae89f4SRuslan Bukin 
4685ae89f4SRuslan Bukin #include <net/bpf.h>
4785ae89f4SRuslan Bukin #include <net/if.h>
4885ae89f4SRuslan Bukin #include <net/ethernet.h>
4985ae89f4SRuslan Bukin #include <net/if_dl.h>
5085ae89f4SRuslan Bukin #include <net/if_media.h>
5185ae89f4SRuslan Bukin #include <net/if_types.h>
5285ae89f4SRuslan Bukin #include <net/if_var.h>
5385ae89f4SRuslan Bukin 
5485ae89f4SRuslan Bukin #include <machine/bus.h>
5585ae89f4SRuslan Bukin 
5685ae89f4SRuslan Bukin #include <dev/mii/mii.h>
5785ae89f4SRuslan Bukin #include <dev/mii/miivar.h>
5885ae89f4SRuslan Bukin #include <dev/mii/tiphy.h>
5985ae89f4SRuslan Bukin #include <dev/ofw/ofw_bus.h>
6085ae89f4SRuslan Bukin #include <dev/ofw/ofw_bus_subr.h>
6185ae89f4SRuslan Bukin #include <dev/xilinx/if_xaereg.h>
6285ae89f4SRuslan Bukin #include <dev/xilinx/if_xaevar.h>
6385ae89f4SRuslan Bukin 
64a8692c16SRuslan Bukin #include <dev/xilinx/axidma.h>
65a8692c16SRuslan Bukin 
6685ae89f4SRuslan Bukin #include "miibus_if.h"
6785ae89f4SRuslan Bukin 
6885ae89f4SRuslan Bukin #define	READ4(_sc, _reg) \
6985ae89f4SRuslan Bukin 	bus_read_4((_sc)->res[0], _reg)
7085ae89f4SRuslan Bukin #define	WRITE4(_sc, _reg, _val) \
7185ae89f4SRuslan Bukin 	bus_write_4((_sc)->res[0], _reg, _val)
7285ae89f4SRuslan Bukin 
7385ae89f4SRuslan Bukin #define	READ8(_sc, _reg) \
7485ae89f4SRuslan Bukin 	bus_read_8((_sc)->res[0], _reg)
7585ae89f4SRuslan Bukin #define	WRITE8(_sc, _reg, _val) \
7685ae89f4SRuslan Bukin 	bus_write_8((_sc)->res[0], _reg, _val)
7785ae89f4SRuslan Bukin 
7885ae89f4SRuslan Bukin #define	XAE_LOCK(sc)			mtx_lock(&(sc)->mtx)
7985ae89f4SRuslan Bukin #define	XAE_UNLOCK(sc)			mtx_unlock(&(sc)->mtx)
8085ae89f4SRuslan Bukin #define	XAE_ASSERT_LOCKED(sc)		mtx_assert(&(sc)->mtx, MA_OWNED)
8185ae89f4SRuslan Bukin #define	XAE_ASSERT_UNLOCKED(sc)		mtx_assert(&(sc)->mtx, MA_NOTOWNED)
8285ae89f4SRuslan Bukin 
8385ae89f4SRuslan Bukin #define XAE_DEBUG
8485ae89f4SRuslan Bukin #undef XAE_DEBUG
8585ae89f4SRuslan Bukin 
8685ae89f4SRuslan Bukin #ifdef XAE_DEBUG
8785ae89f4SRuslan Bukin #define dprintf(fmt, ...)  printf(fmt, ##__VA_ARGS__)
8885ae89f4SRuslan Bukin #else
8985ae89f4SRuslan Bukin #define dprintf(fmt, ...)
9085ae89f4SRuslan Bukin #endif
9185ae89f4SRuslan Bukin 
9285ae89f4SRuslan Bukin #define	RX_QUEUE_SIZE		64
9385ae89f4SRuslan Bukin #define	TX_QUEUE_SIZE		64
9485ae89f4SRuslan Bukin #define	NUM_RX_MBUF		16
9585ae89f4SRuslan Bukin #define	BUFRING_SIZE		8192
9685ae89f4SRuslan Bukin #define	MDIO_CLK_DIV_DEFAULT	29
9785ae89f4SRuslan Bukin 
9885ae89f4SRuslan Bukin #define	PHY1_RD(sc, _r)		\
9985ae89f4SRuslan Bukin 	xae_miibus_read_reg(sc->dev, 1, _r)
10085ae89f4SRuslan Bukin #define	PHY1_WR(sc, _r, _v)	\
10185ae89f4SRuslan Bukin 	xae_miibus_write_reg(sc->dev, 1, _r, _v)
10285ae89f4SRuslan Bukin 
10385ae89f4SRuslan Bukin #define	PHY_RD(sc, _r)		\
10485ae89f4SRuslan Bukin 	xae_miibus_read_reg(sc->dev, sc->phy_addr, _r)
10585ae89f4SRuslan Bukin #define	PHY_WR(sc, _r, _v)	\
10685ae89f4SRuslan Bukin 	xae_miibus_write_reg(sc->dev, sc->phy_addr, _r, _v)
10785ae89f4SRuslan Bukin 
10885ae89f4SRuslan Bukin /* Use this macro to access regs > 0x1f */
10985ae89f4SRuslan Bukin #define WRITE_TI_EREG(sc, reg, data) {					\
11085ae89f4SRuslan Bukin 	PHY_WR(sc, MII_MMDACR, MMDACR_DADDRMASK);			\
11185ae89f4SRuslan Bukin 	PHY_WR(sc, MII_MMDAADR, reg);					\
11285ae89f4SRuslan Bukin 	PHY_WR(sc, MII_MMDACR, MMDACR_DADDRMASK | MMDACR_FN_DATANPI);	\
11385ae89f4SRuslan Bukin 	PHY_WR(sc, MII_MMDAADR, data);					\
11485ae89f4SRuslan Bukin }
11585ae89f4SRuslan Bukin 
11685ae89f4SRuslan Bukin /* Not documented, Xilinx VCU118 workaround */
11785ae89f4SRuslan Bukin #define	 CFG4_SGMII_TMR			0x160 /* bits 8:7 MUST be '10' */
11885ae89f4SRuslan Bukin #define	DP83867_SGMIICTL1		0xD3 /* not documented register */
11985ae89f4SRuslan Bukin #define	 SGMIICTL1_SGMII_6W		(1 << 14) /* no idea what it is */
12085ae89f4SRuslan Bukin 
12185ae89f4SRuslan Bukin static struct resource_spec xae_spec[] = {
12285ae89f4SRuslan Bukin 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
12385ae89f4SRuslan Bukin 	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
12485ae89f4SRuslan Bukin 	{ -1, 0 }
12585ae89f4SRuslan Bukin };
12685ae89f4SRuslan Bukin 
12785ae89f4SRuslan Bukin static void xae_stop_locked(struct xae_softc *sc);
12885ae89f4SRuslan Bukin static void xae_setup_rxfilter(struct xae_softc *sc);
12985ae89f4SRuslan Bukin 
13085ae89f4SRuslan Bukin static int
13185ae89f4SRuslan Bukin xae_rx_enqueue(struct xae_softc *sc, uint32_t n)
13285ae89f4SRuslan Bukin {
13385ae89f4SRuslan Bukin 	struct mbuf *m;
13485ae89f4SRuslan Bukin 	int i;
13585ae89f4SRuslan Bukin 
13685ae89f4SRuslan Bukin 	for (i = 0; i < n; i++) {
13785ae89f4SRuslan Bukin 		m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
13885ae89f4SRuslan Bukin 		if (m == NULL) {
13985ae89f4SRuslan Bukin 			device_printf(sc->dev,
14085ae89f4SRuslan Bukin 			    "%s: Can't alloc rx mbuf\n", __func__);
14185ae89f4SRuslan Bukin 			return (-1);
14285ae89f4SRuslan Bukin 		}
14385ae89f4SRuslan Bukin 
14485ae89f4SRuslan Bukin 		m->m_pkthdr.len = m->m_len = m->m_ext.ext_size;
14585ae89f4SRuslan Bukin 		xdma_enqueue_mbuf(sc->xchan_rx, &m, 0, 4, 4, XDMA_DEV_TO_MEM);
14685ae89f4SRuslan Bukin 	}
14785ae89f4SRuslan Bukin 
14885ae89f4SRuslan Bukin 	return (0);
14985ae89f4SRuslan Bukin }
15085ae89f4SRuslan Bukin 
15185ae89f4SRuslan Bukin static int
15285ae89f4SRuslan Bukin xae_get_phyaddr(phandle_t node, int *phy_addr)
15385ae89f4SRuslan Bukin {
15485ae89f4SRuslan Bukin 	phandle_t phy_node;
15585ae89f4SRuslan Bukin 	pcell_t phy_handle, phy_reg;
15685ae89f4SRuslan Bukin 
15785ae89f4SRuslan Bukin 	if (OF_getencprop(node, "phy-handle", (void *)&phy_handle,
15885ae89f4SRuslan Bukin 	    sizeof(phy_handle)) <= 0)
15985ae89f4SRuslan Bukin 		return (ENXIO);
16085ae89f4SRuslan Bukin 
16185ae89f4SRuslan Bukin 	phy_node = OF_node_from_xref(phy_handle);
16285ae89f4SRuslan Bukin 
16385ae89f4SRuslan Bukin 	if (OF_getencprop(phy_node, "reg", (void *)&phy_reg,
16485ae89f4SRuslan Bukin 	    sizeof(phy_reg)) <= 0)
16585ae89f4SRuslan Bukin 		return (ENXIO);
16685ae89f4SRuslan Bukin 
16785ae89f4SRuslan Bukin 	*phy_addr = phy_reg;
16885ae89f4SRuslan Bukin 
16985ae89f4SRuslan Bukin 	return (0);
17085ae89f4SRuslan Bukin }
17185ae89f4SRuslan Bukin 
17285ae89f4SRuslan Bukin static int
17385ae89f4SRuslan Bukin xae_xdma_tx_intr(void *arg, xdma_transfer_status_t *status)
17485ae89f4SRuslan Bukin {
17585ae89f4SRuslan Bukin 	xdma_transfer_status_t st;
17685ae89f4SRuslan Bukin 	struct xae_softc *sc;
177dba12f75SJustin Hibbits 	if_t ifp;
17885ae89f4SRuslan Bukin 	struct mbuf *m;
17985ae89f4SRuslan Bukin 	int err;
18085ae89f4SRuslan Bukin 
18185ae89f4SRuslan Bukin 	sc = arg;
18285ae89f4SRuslan Bukin 
18385ae89f4SRuslan Bukin 	XAE_LOCK(sc);
18485ae89f4SRuslan Bukin 
18585ae89f4SRuslan Bukin 	ifp = sc->ifp;
18685ae89f4SRuslan Bukin 
18785ae89f4SRuslan Bukin 	for (;;) {
18885ae89f4SRuslan Bukin 		err = xdma_dequeue_mbuf(sc->xchan_tx, &m, &st);
18985ae89f4SRuslan Bukin 		if (err != 0) {
19085ae89f4SRuslan Bukin 			break;
19185ae89f4SRuslan Bukin 		}
19285ae89f4SRuslan Bukin 
19385ae89f4SRuslan Bukin 		if (st.error != 0) {
19485ae89f4SRuslan Bukin 			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
19585ae89f4SRuslan Bukin 		}
19685ae89f4SRuslan Bukin 
19785ae89f4SRuslan Bukin 		m_freem(m);
19885ae89f4SRuslan Bukin 	}
19985ae89f4SRuslan Bukin 
200dba12f75SJustin Hibbits 	if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
20185ae89f4SRuslan Bukin 
20285ae89f4SRuslan Bukin 	XAE_UNLOCK(sc);
20385ae89f4SRuslan Bukin 
20485ae89f4SRuslan Bukin 	return (0);
20585ae89f4SRuslan Bukin }
20685ae89f4SRuslan Bukin 
20785ae89f4SRuslan Bukin static int
20885ae89f4SRuslan Bukin xae_xdma_rx_intr(void *arg, xdma_transfer_status_t *status)
20985ae89f4SRuslan Bukin {
21085ae89f4SRuslan Bukin 	xdma_transfer_status_t st;
21185ae89f4SRuslan Bukin 	struct xae_softc *sc;
212dba12f75SJustin Hibbits 	if_t ifp;
21385ae89f4SRuslan Bukin 	struct mbuf *m;
21485ae89f4SRuslan Bukin 	int err;
21585ae89f4SRuslan Bukin 	uint32_t cnt_processed;
21685ae89f4SRuslan Bukin 
21785ae89f4SRuslan Bukin 	sc = arg;
21885ae89f4SRuslan Bukin 
21985ae89f4SRuslan Bukin 	dprintf("%s\n", __func__);
22085ae89f4SRuslan Bukin 
22185ae89f4SRuslan Bukin 	XAE_LOCK(sc);
22285ae89f4SRuslan Bukin 
22385ae89f4SRuslan Bukin 	ifp = sc->ifp;
22485ae89f4SRuslan Bukin 
22585ae89f4SRuslan Bukin 	cnt_processed = 0;
22685ae89f4SRuslan Bukin 	for (;;) {
22785ae89f4SRuslan Bukin 		err = xdma_dequeue_mbuf(sc->xchan_rx, &m, &st);
22885ae89f4SRuslan Bukin 		if (err != 0) {
22985ae89f4SRuslan Bukin 			break;
23085ae89f4SRuslan Bukin 		}
23185ae89f4SRuslan Bukin 		cnt_processed++;
23285ae89f4SRuslan Bukin 
23385ae89f4SRuslan Bukin 		if (st.error != 0) {
23485ae89f4SRuslan Bukin 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
23585ae89f4SRuslan Bukin 			m_freem(m);
23685ae89f4SRuslan Bukin 			continue;
23785ae89f4SRuslan Bukin 		}
23885ae89f4SRuslan Bukin 
23985ae89f4SRuslan Bukin 		m->m_pkthdr.len = m->m_len = st.transferred;
24085ae89f4SRuslan Bukin 		m->m_pkthdr.rcvif = ifp;
24185ae89f4SRuslan Bukin 		XAE_UNLOCK(sc);
242dba12f75SJustin Hibbits 		if_input(ifp, m);
24385ae89f4SRuslan Bukin 		XAE_LOCK(sc);
24485ae89f4SRuslan Bukin 	}
24585ae89f4SRuslan Bukin 
24685ae89f4SRuslan Bukin 	xae_rx_enqueue(sc, cnt_processed);
24785ae89f4SRuslan Bukin 
24885ae89f4SRuslan Bukin 	XAE_UNLOCK(sc);
24985ae89f4SRuslan Bukin 
25085ae89f4SRuslan Bukin 	return (0);
25185ae89f4SRuslan Bukin }
25285ae89f4SRuslan Bukin 
25385ae89f4SRuslan Bukin static void
254dba12f75SJustin Hibbits xae_qflush(if_t ifp)
25585ae89f4SRuslan Bukin {
25685ae89f4SRuslan Bukin }
25785ae89f4SRuslan Bukin 
25885ae89f4SRuslan Bukin static int
259dba12f75SJustin Hibbits xae_transmit_locked(if_t ifp)
26085ae89f4SRuslan Bukin {
26185ae89f4SRuslan Bukin 	struct xae_softc *sc;
26285ae89f4SRuslan Bukin 	struct mbuf *m;
26385ae89f4SRuslan Bukin 	struct buf_ring *br;
26485ae89f4SRuslan Bukin 	int error;
26585ae89f4SRuslan Bukin 	int enq;
26685ae89f4SRuslan Bukin 
26785ae89f4SRuslan Bukin 	dprintf("%s\n", __func__);
26885ae89f4SRuslan Bukin 
269dba12f75SJustin Hibbits 	sc = if_getsoftc(ifp);
27085ae89f4SRuslan Bukin 	br = sc->br;
27185ae89f4SRuslan Bukin 
27285ae89f4SRuslan Bukin 	enq = 0;
27385ae89f4SRuslan Bukin 
27485ae89f4SRuslan Bukin 	while ((m = drbr_peek(ifp, br)) != NULL) {
27585ae89f4SRuslan Bukin 		error = xdma_enqueue_mbuf(sc->xchan_tx,
27685ae89f4SRuslan Bukin 		    &m, 0, 4, 4, XDMA_MEM_TO_DEV);
27785ae89f4SRuslan Bukin 		if (error != 0) {
27885ae89f4SRuslan Bukin 			/* No space in request queue available yet. */
27985ae89f4SRuslan Bukin 			drbr_putback(ifp, br, m);
28085ae89f4SRuslan Bukin 			break;
28185ae89f4SRuslan Bukin 		}
28285ae89f4SRuslan Bukin 
28385ae89f4SRuslan Bukin 		drbr_advance(ifp, br);
28485ae89f4SRuslan Bukin 
28585ae89f4SRuslan Bukin 		enq++;
28685ae89f4SRuslan Bukin 
28785ae89f4SRuslan Bukin 		/* If anyone is interested give them a copy. */
28885ae89f4SRuslan Bukin 		ETHER_BPF_MTAP(ifp, m);
28985ae89f4SRuslan Bukin         }
29085ae89f4SRuslan Bukin 
29185ae89f4SRuslan Bukin 	if (enq > 0)
29285ae89f4SRuslan Bukin 		xdma_queue_submit(sc->xchan_tx);
29385ae89f4SRuslan Bukin 
29485ae89f4SRuslan Bukin 	return (0);
29585ae89f4SRuslan Bukin }
29685ae89f4SRuslan Bukin 
29785ae89f4SRuslan Bukin static int
298dba12f75SJustin Hibbits xae_transmit(if_t ifp, struct mbuf *m)
29985ae89f4SRuslan Bukin {
30085ae89f4SRuslan Bukin 	struct xae_softc *sc;
30185ae89f4SRuslan Bukin 	int error;
30285ae89f4SRuslan Bukin 
30385ae89f4SRuslan Bukin 	dprintf("%s\n", __func__);
30485ae89f4SRuslan Bukin 
305dba12f75SJustin Hibbits 	sc = if_getsoftc(ifp);
30685ae89f4SRuslan Bukin 
30785ae89f4SRuslan Bukin 	XAE_LOCK(sc);
30885ae89f4SRuslan Bukin 
30985ae89f4SRuslan Bukin 	error = drbr_enqueue(ifp, sc->br, m);
31085ae89f4SRuslan Bukin 	if (error) {
31185ae89f4SRuslan Bukin 		XAE_UNLOCK(sc);
31285ae89f4SRuslan Bukin 		return (error);
31385ae89f4SRuslan Bukin 	}
31485ae89f4SRuslan Bukin 
315dba12f75SJustin Hibbits 	if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
31685ae89f4SRuslan Bukin 	    IFF_DRV_RUNNING) {
31785ae89f4SRuslan Bukin 		XAE_UNLOCK(sc);
31885ae89f4SRuslan Bukin 		return (0);
31985ae89f4SRuslan Bukin 	}
32085ae89f4SRuslan Bukin 
32185ae89f4SRuslan Bukin 	if (!sc->link_is_up) {
32285ae89f4SRuslan Bukin 		XAE_UNLOCK(sc);
32385ae89f4SRuslan Bukin 		return (0);
32485ae89f4SRuslan Bukin 	}
32585ae89f4SRuslan Bukin 
32685ae89f4SRuslan Bukin 	error = xae_transmit_locked(ifp);
32785ae89f4SRuslan Bukin 
32885ae89f4SRuslan Bukin 	XAE_UNLOCK(sc);
32985ae89f4SRuslan Bukin 
33085ae89f4SRuslan Bukin 	return (error);
33185ae89f4SRuslan Bukin }
33285ae89f4SRuslan Bukin 
33385ae89f4SRuslan Bukin static void
33485ae89f4SRuslan Bukin xae_stop_locked(struct xae_softc *sc)
33585ae89f4SRuslan Bukin {
336dba12f75SJustin Hibbits 	if_t ifp;
33785ae89f4SRuslan Bukin 	uint32_t reg;
33885ae89f4SRuslan Bukin 
33985ae89f4SRuslan Bukin 	XAE_ASSERT_LOCKED(sc);
34085ae89f4SRuslan Bukin 
34185ae89f4SRuslan Bukin 	ifp = sc->ifp;
342dba12f75SJustin Hibbits 	if_setdrvflagbits(ifp, 0, (IFF_DRV_RUNNING | IFF_DRV_OACTIVE));
34385ae89f4SRuslan Bukin 
34485ae89f4SRuslan Bukin 	callout_stop(&sc->xae_callout);
34585ae89f4SRuslan Bukin 
34685ae89f4SRuslan Bukin 	/* Stop the transmitter */
34785ae89f4SRuslan Bukin 	reg = READ4(sc, XAE_TC);
34885ae89f4SRuslan Bukin 	reg &= ~TC_TX;
34985ae89f4SRuslan Bukin 	WRITE4(sc, XAE_TC, reg);
35085ae89f4SRuslan Bukin 
35185ae89f4SRuslan Bukin 	/* Stop the receiver. */
35285ae89f4SRuslan Bukin 	reg = READ4(sc, XAE_RCW1);
35385ae89f4SRuslan Bukin 	reg &= ~RCW1_RX;
35485ae89f4SRuslan Bukin 	WRITE4(sc, XAE_RCW1, reg);
35585ae89f4SRuslan Bukin }
35685ae89f4SRuslan Bukin 
35785ae89f4SRuslan Bukin static uint64_t
35885ae89f4SRuslan Bukin xae_stat(struct xae_softc *sc, int counter_id)
35985ae89f4SRuslan Bukin {
36085ae89f4SRuslan Bukin 	uint64_t new, old;
36185ae89f4SRuslan Bukin 	uint64_t delta;
36285ae89f4SRuslan Bukin 
36385ae89f4SRuslan Bukin 	KASSERT(counter_id < XAE_MAX_COUNTERS,
36485ae89f4SRuslan Bukin 		("counter %d is out of range", counter_id));
36585ae89f4SRuslan Bukin 
36685ae89f4SRuslan Bukin 	new = READ8(sc, XAE_STATCNT(counter_id));
36785ae89f4SRuslan Bukin 	old = sc->counters[counter_id];
36885ae89f4SRuslan Bukin 
36985ae89f4SRuslan Bukin 	if (new >= old)
37085ae89f4SRuslan Bukin 		delta = new - old;
37185ae89f4SRuslan Bukin 	else
37285ae89f4SRuslan Bukin 		delta = UINT64_MAX - old + new;
37385ae89f4SRuslan Bukin 	sc->counters[counter_id] = new;
37485ae89f4SRuslan Bukin 
37585ae89f4SRuslan Bukin 	return (delta);
37685ae89f4SRuslan Bukin }
37785ae89f4SRuslan Bukin 
37885ae89f4SRuslan Bukin static void
37985ae89f4SRuslan Bukin xae_harvest_stats(struct xae_softc *sc)
38085ae89f4SRuslan Bukin {
381dba12f75SJustin Hibbits 	if_t ifp;
38285ae89f4SRuslan Bukin 
38385ae89f4SRuslan Bukin 	ifp = sc->ifp;
38485ae89f4SRuslan Bukin 
38585ae89f4SRuslan Bukin 	if_inc_counter(ifp, IFCOUNTER_IPACKETS, xae_stat(sc, RX_GOOD_FRAMES));
38685ae89f4SRuslan Bukin 	if_inc_counter(ifp, IFCOUNTER_IMCASTS, xae_stat(sc, RX_GOOD_MCASTS));
38785ae89f4SRuslan Bukin 	if_inc_counter(ifp, IFCOUNTER_IERRORS,
38885ae89f4SRuslan Bukin 	    xae_stat(sc, RX_FRAME_CHECK_SEQ_ERROR) +
38985ae89f4SRuslan Bukin 	    xae_stat(sc, RX_LEN_OUT_OF_RANGE) +
39085ae89f4SRuslan Bukin 	    xae_stat(sc, RX_ALIGNMENT_ERRORS));
39185ae89f4SRuslan Bukin 
39285ae89f4SRuslan Bukin 	if_inc_counter(ifp, IFCOUNTER_OBYTES, xae_stat(sc, TX_BYTES));
39385ae89f4SRuslan Bukin 	if_inc_counter(ifp, IFCOUNTER_OPACKETS, xae_stat(sc, TX_GOOD_FRAMES));
39485ae89f4SRuslan Bukin 	if_inc_counter(ifp, IFCOUNTER_OMCASTS, xae_stat(sc, TX_GOOD_MCASTS));
39585ae89f4SRuslan Bukin 	if_inc_counter(ifp, IFCOUNTER_OERRORS,
39685ae89f4SRuslan Bukin 	    xae_stat(sc, TX_GOOD_UNDERRUN_ERRORS));
39785ae89f4SRuslan Bukin 
39885ae89f4SRuslan Bukin 	if_inc_counter(ifp, IFCOUNTER_COLLISIONS,
39985ae89f4SRuslan Bukin 	    xae_stat(sc, TX_SINGLE_COLLISION_FRAMES) +
40085ae89f4SRuslan Bukin 	    xae_stat(sc, TX_MULTI_COLLISION_FRAMES) +
40185ae89f4SRuslan Bukin 	    xae_stat(sc, TX_LATE_COLLISIONS) +
40285ae89f4SRuslan Bukin 	    xae_stat(sc, TX_EXCESS_COLLISIONS));
40385ae89f4SRuslan Bukin }
40485ae89f4SRuslan Bukin 
40585ae89f4SRuslan Bukin static void
40685ae89f4SRuslan Bukin xae_tick(void *arg)
40785ae89f4SRuslan Bukin {
40885ae89f4SRuslan Bukin 	struct xae_softc *sc;
409dba12f75SJustin Hibbits 	if_t ifp;
41085ae89f4SRuslan Bukin 	int link_was_up;
41185ae89f4SRuslan Bukin 
41285ae89f4SRuslan Bukin 	sc = arg;
41385ae89f4SRuslan Bukin 
41485ae89f4SRuslan Bukin 	XAE_ASSERT_LOCKED(sc);
41585ae89f4SRuslan Bukin 
41685ae89f4SRuslan Bukin 	ifp = sc->ifp;
41785ae89f4SRuslan Bukin 
418dba12f75SJustin Hibbits 	if (!(if_getdrvflags(ifp) & IFF_DRV_RUNNING))
41985ae89f4SRuslan Bukin 		return;
42085ae89f4SRuslan Bukin 
42185ae89f4SRuslan Bukin 	/* Gather stats from hardware counters. */
42285ae89f4SRuslan Bukin 	xae_harvest_stats(sc);
42385ae89f4SRuslan Bukin 
42485ae89f4SRuslan Bukin 	/* Check the media status. */
42585ae89f4SRuslan Bukin 	link_was_up = sc->link_is_up;
42685ae89f4SRuslan Bukin 	mii_tick(sc->mii_softc);
42785ae89f4SRuslan Bukin 	if (sc->link_is_up && !link_was_up)
42885ae89f4SRuslan Bukin 		xae_transmit_locked(sc->ifp);
42985ae89f4SRuslan Bukin 
43085ae89f4SRuslan Bukin 	/* Schedule another check one second from now. */
43185ae89f4SRuslan Bukin 	callout_reset(&sc->xae_callout, hz, xae_tick, sc);
43285ae89f4SRuslan Bukin }
43385ae89f4SRuslan Bukin 
43485ae89f4SRuslan Bukin static void
43585ae89f4SRuslan Bukin xae_init_locked(struct xae_softc *sc)
43685ae89f4SRuslan Bukin {
437dba12f75SJustin Hibbits 	if_t ifp;
43885ae89f4SRuslan Bukin 
43985ae89f4SRuslan Bukin 	XAE_ASSERT_LOCKED(sc);
44085ae89f4SRuslan Bukin 
44185ae89f4SRuslan Bukin 	ifp = sc->ifp;
442dba12f75SJustin Hibbits 	if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
44385ae89f4SRuslan Bukin 		return;
44485ae89f4SRuslan Bukin 
445dba12f75SJustin Hibbits 	if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
44685ae89f4SRuslan Bukin 
44785ae89f4SRuslan Bukin 	xae_setup_rxfilter(sc);
44885ae89f4SRuslan Bukin 
44985ae89f4SRuslan Bukin 	/* Enable the transmitter */
45085ae89f4SRuslan Bukin 	WRITE4(sc, XAE_TC, TC_TX);
45185ae89f4SRuslan Bukin 
45285ae89f4SRuslan Bukin 	/* Enable the receiver. */
45385ae89f4SRuslan Bukin 	WRITE4(sc, XAE_RCW1, RCW1_RX);
45485ae89f4SRuslan Bukin 
45585ae89f4SRuslan Bukin 	/*
45685ae89f4SRuslan Bukin 	 * Call mii_mediachg() which will call back into xae_miibus_statchg()
45785ae89f4SRuslan Bukin 	 * to set up the remaining config registers based on current media.
45885ae89f4SRuslan Bukin 	 */
45985ae89f4SRuslan Bukin 	mii_mediachg(sc->mii_softc);
46085ae89f4SRuslan Bukin 	callout_reset(&sc->xae_callout, hz, xae_tick, sc);
46185ae89f4SRuslan Bukin }
46285ae89f4SRuslan Bukin 
46385ae89f4SRuslan Bukin static void
46485ae89f4SRuslan Bukin xae_init(void *arg)
46585ae89f4SRuslan Bukin {
46685ae89f4SRuslan Bukin 	struct xae_softc *sc;
46785ae89f4SRuslan Bukin 
46885ae89f4SRuslan Bukin 	sc = arg;
46985ae89f4SRuslan Bukin 
47085ae89f4SRuslan Bukin 	XAE_LOCK(sc);
47185ae89f4SRuslan Bukin 	xae_init_locked(sc);
47285ae89f4SRuslan Bukin 	XAE_UNLOCK(sc);
47385ae89f4SRuslan Bukin }
47485ae89f4SRuslan Bukin 
47585ae89f4SRuslan Bukin static void
476dba12f75SJustin Hibbits xae_media_status(if_t  ifp, struct ifmediareq *ifmr)
47785ae89f4SRuslan Bukin {
47885ae89f4SRuslan Bukin 	struct xae_softc *sc;
47985ae89f4SRuslan Bukin 	struct mii_data *mii;
48085ae89f4SRuslan Bukin 
481dba12f75SJustin Hibbits 	sc = if_getsoftc(ifp);
48285ae89f4SRuslan Bukin 	mii = sc->mii_softc;
48385ae89f4SRuslan Bukin 
48485ae89f4SRuslan Bukin 	XAE_LOCK(sc);
48585ae89f4SRuslan Bukin 	mii_pollstat(mii);
48685ae89f4SRuslan Bukin 	ifmr->ifm_active = mii->mii_media_active;
48785ae89f4SRuslan Bukin 	ifmr->ifm_status = mii->mii_media_status;
48885ae89f4SRuslan Bukin 	XAE_UNLOCK(sc);
48985ae89f4SRuslan Bukin }
49085ae89f4SRuslan Bukin 
49185ae89f4SRuslan Bukin static int
49285ae89f4SRuslan Bukin xae_media_change_locked(struct xae_softc *sc)
49385ae89f4SRuslan Bukin {
49485ae89f4SRuslan Bukin 
49585ae89f4SRuslan Bukin 	return (mii_mediachg(sc->mii_softc));
49685ae89f4SRuslan Bukin }
49785ae89f4SRuslan Bukin 
49885ae89f4SRuslan Bukin static int
499dba12f75SJustin Hibbits xae_media_change(if_t  ifp)
50085ae89f4SRuslan Bukin {
50185ae89f4SRuslan Bukin 	struct xae_softc *sc;
50285ae89f4SRuslan Bukin 	int error;
50385ae89f4SRuslan Bukin 
504dba12f75SJustin Hibbits 	sc = if_getsoftc(ifp);
50585ae89f4SRuslan Bukin 
50685ae89f4SRuslan Bukin 	XAE_LOCK(sc);
50785ae89f4SRuslan Bukin 	error = xae_media_change_locked(sc);
50885ae89f4SRuslan Bukin 	XAE_UNLOCK(sc);
50985ae89f4SRuslan Bukin 
51085ae89f4SRuslan Bukin 	return (error);
51185ae89f4SRuslan Bukin }
51285ae89f4SRuslan Bukin 
5134d50c26aSGleb Smirnoff static u_int
5144d50c26aSGleb Smirnoff xae_write_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
5154d50c26aSGleb Smirnoff {
5164d50c26aSGleb Smirnoff 	struct xae_softc *sc = arg;
5174d50c26aSGleb Smirnoff 	uint32_t reg;
5184d50c26aSGleb Smirnoff 	uint8_t *ma;
5194d50c26aSGleb Smirnoff 
5204d50c26aSGleb Smirnoff 	if (cnt >= XAE_MULTICAST_TABLE_SIZE)
5214d50c26aSGleb Smirnoff 		return (1);
5224d50c26aSGleb Smirnoff 
5234d50c26aSGleb Smirnoff 	ma = LLADDR(sdl);
5244d50c26aSGleb Smirnoff 
5254d50c26aSGleb Smirnoff 	reg = READ4(sc, XAE_FFC) & 0xffffff00;
5264d50c26aSGleb Smirnoff 	reg |= cnt;
5274d50c26aSGleb Smirnoff 	WRITE4(sc, XAE_FFC, reg);
5284d50c26aSGleb Smirnoff 
5294d50c26aSGleb Smirnoff 	reg = (ma[0]);
5304d50c26aSGleb Smirnoff 	reg |= (ma[1] << 8);
5314d50c26aSGleb Smirnoff 	reg |= (ma[2] << 16);
5324d50c26aSGleb Smirnoff 	reg |= (ma[3] << 24);
5334d50c26aSGleb Smirnoff 	WRITE4(sc, XAE_FFV(0), reg);
5344d50c26aSGleb Smirnoff 
5354d50c26aSGleb Smirnoff 	reg = ma[4];
5364d50c26aSGleb Smirnoff 	reg |= ma[5] << 8;
5374d50c26aSGleb Smirnoff 	WRITE4(sc, XAE_FFV(1), reg);
5384d50c26aSGleb Smirnoff 
5394d50c26aSGleb Smirnoff 	return (1);
5404d50c26aSGleb Smirnoff }
5414d50c26aSGleb Smirnoff 
54285ae89f4SRuslan Bukin static void
54385ae89f4SRuslan Bukin xae_setup_rxfilter(struct xae_softc *sc)
54485ae89f4SRuslan Bukin {
545dba12f75SJustin Hibbits 	if_t ifp;
54685ae89f4SRuslan Bukin 	uint32_t reg;
54785ae89f4SRuslan Bukin 
54885ae89f4SRuslan Bukin 	XAE_ASSERT_LOCKED(sc);
54985ae89f4SRuslan Bukin 
55085ae89f4SRuslan Bukin 	ifp = sc->ifp;
55185ae89f4SRuslan Bukin 
55285ae89f4SRuslan Bukin 	/*
55385ae89f4SRuslan Bukin 	 * Set the multicast (group) filter hash.
55485ae89f4SRuslan Bukin 	 */
555dba12f75SJustin Hibbits 	if ((if_getflags(ifp) & (IFF_ALLMULTI | IFF_PROMISC)) != 0) {
55685ae89f4SRuslan Bukin 		reg = READ4(sc, XAE_FFC);
55785ae89f4SRuslan Bukin 		reg |= FFC_PM;
55885ae89f4SRuslan Bukin 		WRITE4(sc, XAE_FFC, reg);
55985ae89f4SRuslan Bukin 	} else {
56085ae89f4SRuslan Bukin 		reg = READ4(sc, XAE_FFC);
56185ae89f4SRuslan Bukin 		reg &= ~FFC_PM;
56285ae89f4SRuslan Bukin 		WRITE4(sc, XAE_FFC, reg);
56385ae89f4SRuslan Bukin 
5644d50c26aSGleb Smirnoff 		if_foreach_llmaddr(ifp, xae_write_maddr, sc);
56585ae89f4SRuslan Bukin 	}
56685ae89f4SRuslan Bukin 
56785ae89f4SRuslan Bukin 	/*
56885ae89f4SRuslan Bukin 	 * Set the primary address.
56985ae89f4SRuslan Bukin 	 */
57085ae89f4SRuslan Bukin 	reg = sc->macaddr[0];
57185ae89f4SRuslan Bukin 	reg |= (sc->macaddr[1] << 8);
57285ae89f4SRuslan Bukin 	reg |= (sc->macaddr[2] << 16);
57385ae89f4SRuslan Bukin 	reg |= (sc->macaddr[3] << 24);
57485ae89f4SRuslan Bukin 	WRITE4(sc, XAE_UAW0, reg);
57585ae89f4SRuslan Bukin 
57685ae89f4SRuslan Bukin 	reg = sc->macaddr[4];
57785ae89f4SRuslan Bukin 	reg |= (sc->macaddr[5] << 8);
57885ae89f4SRuslan Bukin 	WRITE4(sc, XAE_UAW1, reg);
57985ae89f4SRuslan Bukin }
58085ae89f4SRuslan Bukin 
58185ae89f4SRuslan Bukin static int
582dba12f75SJustin Hibbits xae_ioctl(if_t ifp, u_long cmd, caddr_t data)
58385ae89f4SRuslan Bukin {
58485ae89f4SRuslan Bukin 	struct xae_softc *sc;
58585ae89f4SRuslan Bukin 	struct mii_data *mii;
58685ae89f4SRuslan Bukin 	struct ifreq *ifr;
58785ae89f4SRuslan Bukin 	int mask, error;
58885ae89f4SRuslan Bukin 
589dba12f75SJustin Hibbits 	sc = if_getsoftc(ifp);
59085ae89f4SRuslan Bukin 	ifr = (struct ifreq *)data;
59185ae89f4SRuslan Bukin 
59285ae89f4SRuslan Bukin 	error = 0;
59385ae89f4SRuslan Bukin 	switch (cmd) {
59485ae89f4SRuslan Bukin 	case SIOCSIFFLAGS:
59585ae89f4SRuslan Bukin 		XAE_LOCK(sc);
596dba12f75SJustin Hibbits 		if (if_getflags(ifp) & IFF_UP) {
597dba12f75SJustin Hibbits 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
598dba12f75SJustin Hibbits 				if ((if_getflags(ifp) ^ sc->if_flags) &
59985ae89f4SRuslan Bukin 				    (IFF_PROMISC | IFF_ALLMULTI))
60085ae89f4SRuslan Bukin 					xae_setup_rxfilter(sc);
60185ae89f4SRuslan Bukin 			} else {
60285ae89f4SRuslan Bukin 				if (!sc->is_detaching)
60385ae89f4SRuslan Bukin 					xae_init_locked(sc);
60485ae89f4SRuslan Bukin 			}
60585ae89f4SRuslan Bukin 		} else {
606dba12f75SJustin Hibbits 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
60785ae89f4SRuslan Bukin 				xae_stop_locked(sc);
60885ae89f4SRuslan Bukin 		}
609dba12f75SJustin Hibbits 		sc->if_flags = if_getflags(ifp);
61085ae89f4SRuslan Bukin 		XAE_UNLOCK(sc);
61185ae89f4SRuslan Bukin 		break;
61285ae89f4SRuslan Bukin 	case SIOCADDMULTI:
61385ae89f4SRuslan Bukin 	case SIOCDELMULTI:
614dba12f75SJustin Hibbits 		if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
61585ae89f4SRuslan Bukin 			XAE_LOCK(sc);
61685ae89f4SRuslan Bukin 			xae_setup_rxfilter(sc);
61785ae89f4SRuslan Bukin 			XAE_UNLOCK(sc);
61885ae89f4SRuslan Bukin 		}
61985ae89f4SRuslan Bukin 		break;
62085ae89f4SRuslan Bukin 	case SIOCSIFMEDIA:
62185ae89f4SRuslan Bukin 	case SIOCGIFMEDIA:
62285ae89f4SRuslan Bukin 		mii = sc->mii_softc;
62385ae89f4SRuslan Bukin 		error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
62485ae89f4SRuslan Bukin 		break;
62585ae89f4SRuslan Bukin 	case SIOCSIFCAP:
626dba12f75SJustin Hibbits 		mask = if_getcapenable(ifp) ^ ifr->ifr_reqcap;
62785ae89f4SRuslan Bukin 		if (mask & IFCAP_VLAN_MTU) {
62885ae89f4SRuslan Bukin 			/* No work to do except acknowledge the change took */
629dba12f75SJustin Hibbits 			if_togglecapenable(ifp, IFCAP_VLAN_MTU);
63085ae89f4SRuslan Bukin 		}
63185ae89f4SRuslan Bukin 		break;
63285ae89f4SRuslan Bukin 
63385ae89f4SRuslan Bukin 	default:
63485ae89f4SRuslan Bukin 		error = ether_ioctl(ifp, cmd, data);
63585ae89f4SRuslan Bukin 		break;
63685ae89f4SRuslan Bukin 	}
63785ae89f4SRuslan Bukin 
63885ae89f4SRuslan Bukin 	return (error);
63985ae89f4SRuslan Bukin }
64085ae89f4SRuslan Bukin 
64185ae89f4SRuslan Bukin static void
64285ae89f4SRuslan Bukin xae_intr(void *arg)
64385ae89f4SRuslan Bukin {
64485ae89f4SRuslan Bukin 
64585ae89f4SRuslan Bukin }
64685ae89f4SRuslan Bukin 
64785ae89f4SRuslan Bukin static int
64885ae89f4SRuslan Bukin xae_get_hwaddr(struct xae_softc *sc, uint8_t *hwaddr)
64985ae89f4SRuslan Bukin {
65085ae89f4SRuslan Bukin 	phandle_t node;
65185ae89f4SRuslan Bukin 	int len;
65285ae89f4SRuslan Bukin 
65385ae89f4SRuslan Bukin 	node = ofw_bus_get_node(sc->dev);
65485ae89f4SRuslan Bukin 
65585ae89f4SRuslan Bukin 	/* Check if there is property */
65685ae89f4SRuslan Bukin 	if ((len = OF_getproplen(node, "local-mac-address")) <= 0)
65785ae89f4SRuslan Bukin 		return (EINVAL);
65885ae89f4SRuslan Bukin 
65985ae89f4SRuslan Bukin 	if (len != ETHER_ADDR_LEN)
66085ae89f4SRuslan Bukin 		return (EINVAL);
66185ae89f4SRuslan Bukin 
66285ae89f4SRuslan Bukin 	OF_getprop(node, "local-mac-address", hwaddr,
66385ae89f4SRuslan Bukin 	    ETHER_ADDR_LEN);
66485ae89f4SRuslan Bukin 
66585ae89f4SRuslan Bukin 	return (0);
66685ae89f4SRuslan Bukin }
66785ae89f4SRuslan Bukin 
66885ae89f4SRuslan Bukin static int
66985ae89f4SRuslan Bukin mdio_wait(struct xae_softc *sc)
67085ae89f4SRuslan Bukin {
67185ae89f4SRuslan Bukin 	uint32_t reg;
67285ae89f4SRuslan Bukin 	int timeout;
67385ae89f4SRuslan Bukin 
67485ae89f4SRuslan Bukin 	timeout = 200;
67585ae89f4SRuslan Bukin 
67685ae89f4SRuslan Bukin 	do {
67785ae89f4SRuslan Bukin 		reg = READ4(sc, XAE_MDIO_CTRL);
67885ae89f4SRuslan Bukin 		if (reg & MDIO_CTRL_READY)
67985ae89f4SRuslan Bukin 			break;
68085ae89f4SRuslan Bukin 		DELAY(1);
68185ae89f4SRuslan Bukin 	} while (timeout--);
68285ae89f4SRuslan Bukin 
68385ae89f4SRuslan Bukin 	if (timeout <= 0) {
68485ae89f4SRuslan Bukin 		printf("Failed to get MDIO ready\n");
68585ae89f4SRuslan Bukin 		return (1);
68685ae89f4SRuslan Bukin 	}
68785ae89f4SRuslan Bukin 
68885ae89f4SRuslan Bukin 	return (0);
68985ae89f4SRuslan Bukin }
69085ae89f4SRuslan Bukin 
69185ae89f4SRuslan Bukin static int
69285ae89f4SRuslan Bukin xae_miibus_read_reg(device_t dev, int phy, int reg)
69385ae89f4SRuslan Bukin {
69485ae89f4SRuslan Bukin 	struct xae_softc *sc;
69585ae89f4SRuslan Bukin 	uint32_t mii;
69685ae89f4SRuslan Bukin 	int rv;
69785ae89f4SRuslan Bukin 
69885ae89f4SRuslan Bukin 	sc = device_get_softc(dev);
69985ae89f4SRuslan Bukin 
70085ae89f4SRuslan Bukin 	if (mdio_wait(sc))
70185ae89f4SRuslan Bukin 		return (0);
70285ae89f4SRuslan Bukin 
70385ae89f4SRuslan Bukin 	mii = MDIO_CTRL_TX_OP_READ | MDIO_CTRL_INITIATE;
70485ae89f4SRuslan Bukin 	mii |= (reg << MDIO_TX_REGAD_S);
70585ae89f4SRuslan Bukin 	mii |= (phy << MDIO_TX_PHYAD_S);
70685ae89f4SRuslan Bukin 
70785ae89f4SRuslan Bukin 	WRITE4(sc, XAE_MDIO_CTRL, mii);
70885ae89f4SRuslan Bukin 
70985ae89f4SRuslan Bukin 	if (mdio_wait(sc))
71085ae89f4SRuslan Bukin 		return (0);
71185ae89f4SRuslan Bukin 
71285ae89f4SRuslan Bukin 	rv = READ4(sc, XAE_MDIO_READ);
71385ae89f4SRuslan Bukin 
71485ae89f4SRuslan Bukin 	return (rv);
71585ae89f4SRuslan Bukin }
71685ae89f4SRuslan Bukin 
71785ae89f4SRuslan Bukin static int
71885ae89f4SRuslan Bukin xae_miibus_write_reg(device_t dev, int phy, int reg, int val)
71985ae89f4SRuslan Bukin {
72085ae89f4SRuslan Bukin 	struct xae_softc *sc;
72185ae89f4SRuslan Bukin 	uint32_t mii;
72285ae89f4SRuslan Bukin 
72385ae89f4SRuslan Bukin 	sc = device_get_softc(dev);
72485ae89f4SRuslan Bukin 
72585ae89f4SRuslan Bukin 	if (mdio_wait(sc))
72685ae89f4SRuslan Bukin 		return (1);
72785ae89f4SRuslan Bukin 
72885ae89f4SRuslan Bukin 	mii = MDIO_CTRL_TX_OP_WRITE | MDIO_CTRL_INITIATE;
72985ae89f4SRuslan Bukin 	mii |= (reg << MDIO_TX_REGAD_S);
73085ae89f4SRuslan Bukin 	mii |= (phy << MDIO_TX_PHYAD_S);
73185ae89f4SRuslan Bukin 
73285ae89f4SRuslan Bukin 	WRITE4(sc, XAE_MDIO_WRITE, val);
73385ae89f4SRuslan Bukin 	WRITE4(sc, XAE_MDIO_CTRL, mii);
73485ae89f4SRuslan Bukin 
73585ae89f4SRuslan Bukin 	if (mdio_wait(sc))
73685ae89f4SRuslan Bukin 		return (1);
73785ae89f4SRuslan Bukin 
73885ae89f4SRuslan Bukin 	return (0);
73985ae89f4SRuslan Bukin }
74085ae89f4SRuslan Bukin 
74185ae89f4SRuslan Bukin static void
74285ae89f4SRuslan Bukin xae_phy_fixup(struct xae_softc *sc)
74385ae89f4SRuslan Bukin {
74485ae89f4SRuslan Bukin 	uint32_t reg;
74585ae89f4SRuslan Bukin 
74685ae89f4SRuslan Bukin 	do {
74785ae89f4SRuslan Bukin 		WRITE_TI_EREG(sc, DP83867_SGMIICTL1, SGMIICTL1_SGMII_6W);
74885ae89f4SRuslan Bukin 		PHY_WR(sc, DP83867_PHYCR, PHYCR_SGMII_EN);
74985ae89f4SRuslan Bukin 
75085ae89f4SRuslan Bukin 		reg = PHY_RD(sc, DP83867_CFG2);
75185ae89f4SRuslan Bukin 		reg &= ~CFG2_SPEED_OPT_ATTEMPT_CNT_M;
75285ae89f4SRuslan Bukin 		reg |= (CFG2_SPEED_OPT_ATTEMPT_CNT_4);
75385ae89f4SRuslan Bukin 		reg |= CFG2_INTERRUPT_POLARITY;
75485ae89f4SRuslan Bukin 		reg |= CFG2_SPEED_OPT_ENHANCED_EN;
75585ae89f4SRuslan Bukin 		reg |= CFG2_SPEED_OPT_10M_EN;
75685ae89f4SRuslan Bukin 		PHY_WR(sc, DP83867_CFG2, reg);
75785ae89f4SRuslan Bukin 
75885ae89f4SRuslan Bukin 		WRITE_TI_EREG(sc, DP83867_CFG4, CFG4_SGMII_TMR);
75985ae89f4SRuslan Bukin 		PHY_WR(sc, MII_BMCR,
76085ae89f4SRuslan Bukin 		    BMCR_AUTOEN | BMCR_FDX | BMCR_SPEED1 | BMCR_RESET);
76185ae89f4SRuslan Bukin 	} while (PHY1_RD(sc, MII_BMCR) == 0x0ffff);
76285ae89f4SRuslan Bukin 
76385ae89f4SRuslan Bukin 	do {
76485ae89f4SRuslan Bukin 		PHY1_WR(sc, MII_BMCR,
76585ae89f4SRuslan Bukin 		    BMCR_AUTOEN | BMCR_FDX | BMCR_SPEED1 | BMCR_STARTNEG);
76685ae89f4SRuslan Bukin 		DELAY(40000);
76785ae89f4SRuslan Bukin 	} while ((PHY1_RD(sc, MII_BMSR) & BMSR_ACOMP) == 0);
76885ae89f4SRuslan Bukin }
76985ae89f4SRuslan Bukin 
77085ae89f4SRuslan Bukin static int
771a8692c16SRuslan Bukin get_xdma_std(struct xae_softc *sc)
772a8692c16SRuslan Bukin {
773a8692c16SRuslan Bukin 
774a8692c16SRuslan Bukin 	sc->xdma_tx = xdma_ofw_get(sc->dev, "tx");
775a8692c16SRuslan Bukin 	if (sc->xdma_tx == NULL)
776a8692c16SRuslan Bukin 		return (ENXIO);
777a8692c16SRuslan Bukin 
778a8692c16SRuslan Bukin 	sc->xdma_rx = xdma_ofw_get(sc->dev, "rx");
779a8692c16SRuslan Bukin 	if (sc->xdma_rx == NULL) {
780a8692c16SRuslan Bukin 		xdma_put(sc->xdma_tx);
781a8692c16SRuslan Bukin 		return (ENXIO);
782a8692c16SRuslan Bukin 	}
783a8692c16SRuslan Bukin 
784a8692c16SRuslan Bukin 	return (0);
785a8692c16SRuslan Bukin }
786a8692c16SRuslan Bukin 
787a8692c16SRuslan Bukin static int
788a8692c16SRuslan Bukin get_xdma_axistream(struct xae_softc *sc)
789a8692c16SRuslan Bukin {
790a8692c16SRuslan Bukin 	struct axidma_fdt_data *data;
791a8692c16SRuslan Bukin 	device_t dma_dev;
792a8692c16SRuslan Bukin 	phandle_t node;
793a8692c16SRuslan Bukin 	pcell_t prop;
794a8692c16SRuslan Bukin 	size_t len;
795a8692c16SRuslan Bukin 
796a8692c16SRuslan Bukin 	node = ofw_bus_get_node(sc->dev);
797a8692c16SRuslan Bukin 	len = OF_getencprop(node, "axistream-connected", &prop, sizeof(prop));
798a8692c16SRuslan Bukin 	if (len != sizeof(prop)) {
799a8692c16SRuslan Bukin 		device_printf(sc->dev,
800a8692c16SRuslan Bukin 		    "%s: Couldn't get axistream-connected prop.\n", __func__);
801a8692c16SRuslan Bukin 		return (ENXIO);
802a8692c16SRuslan Bukin 	}
803a8692c16SRuslan Bukin 	dma_dev = OF_device_from_xref(prop);
804a8692c16SRuslan Bukin 	if (dma_dev == NULL) {
805a8692c16SRuslan Bukin 		device_printf(sc->dev, "Could not get DMA device by xref.\n");
806a8692c16SRuslan Bukin 		return (ENXIO);
807a8692c16SRuslan Bukin 	}
808a8692c16SRuslan Bukin 
809a8692c16SRuslan Bukin 	sc->xdma_tx = xdma_get(sc->dev, dma_dev);
810a8692c16SRuslan Bukin 	if (sc->xdma_tx == NULL) {
811a8692c16SRuslan Bukin 		device_printf(sc->dev, "Could not find DMA controller.\n");
812a8692c16SRuslan Bukin 		return (ENXIO);
813a8692c16SRuslan Bukin 	}
814a8692c16SRuslan Bukin 	data = malloc(sizeof(struct axidma_fdt_data),
815a8692c16SRuslan Bukin 	    M_DEVBUF, (M_WAITOK | M_ZERO));
816a8692c16SRuslan Bukin 	data->id = AXIDMA_TX_CHAN;
817a8692c16SRuslan Bukin 	sc->xdma_tx->data = data;
818a8692c16SRuslan Bukin 
819a8692c16SRuslan Bukin 	sc->xdma_rx = xdma_get(sc->dev, dma_dev);
820a8692c16SRuslan Bukin 	if (sc->xdma_rx == NULL) {
821a8692c16SRuslan Bukin 		device_printf(sc->dev, "Could not find DMA controller.\n");
822a8692c16SRuslan Bukin 		return (ENXIO);
823a8692c16SRuslan Bukin 	}
824a8692c16SRuslan Bukin 	data = malloc(sizeof(struct axidma_fdt_data),
825a8692c16SRuslan Bukin 	    M_DEVBUF, (M_WAITOK | M_ZERO));
826a8692c16SRuslan Bukin 	data->id = AXIDMA_RX_CHAN;
827a8692c16SRuslan Bukin 	sc->xdma_rx->data = data;
828a8692c16SRuslan Bukin 
829a8692c16SRuslan Bukin 	return (0);
830a8692c16SRuslan Bukin }
831a8692c16SRuslan Bukin 
832a8692c16SRuslan Bukin static int
83385ae89f4SRuslan Bukin setup_xdma(struct xae_softc *sc)
83485ae89f4SRuslan Bukin {
83585ae89f4SRuslan Bukin 	device_t dev;
83685ae89f4SRuslan Bukin 	vmem_t *vmem;
83785ae89f4SRuslan Bukin 	int error;
83885ae89f4SRuslan Bukin 
83985ae89f4SRuslan Bukin 	dev = sc->dev;
84085ae89f4SRuslan Bukin 
84185ae89f4SRuslan Bukin 	/* Get xDMA controller */
842a8692c16SRuslan Bukin 	error = get_xdma_std(sc);
843a8692c16SRuslan Bukin 
844a8692c16SRuslan Bukin 	if (error) {
845a8692c16SRuslan Bukin 		device_printf(sc->dev,
846a8692c16SRuslan Bukin 		    "Fallback to axistream-connected property\n");
847a8692c16SRuslan Bukin 		error = get_xdma_axistream(sc);
84885ae89f4SRuslan Bukin 	}
84985ae89f4SRuslan Bukin 
850a8692c16SRuslan Bukin 	if (error) {
851a8692c16SRuslan Bukin 		device_printf(dev, "Could not find xDMA controllers.\n");
85285ae89f4SRuslan Bukin 		return (ENXIO);
85385ae89f4SRuslan Bukin 	}
85485ae89f4SRuslan Bukin 
85585ae89f4SRuslan Bukin 	/* Alloc xDMA TX virtual channel. */
85685ae89f4SRuslan Bukin 	sc->xchan_tx = xdma_channel_alloc(sc->xdma_tx, 0);
85785ae89f4SRuslan Bukin 	if (sc->xchan_tx == NULL) {
85885ae89f4SRuslan Bukin 		device_printf(dev, "Can't alloc virtual DMA TX channel.\n");
85985ae89f4SRuslan Bukin 		return (ENXIO);
86085ae89f4SRuslan Bukin 	}
86185ae89f4SRuslan Bukin 
86285ae89f4SRuslan Bukin 	/* Setup interrupt handler. */
863d987842dSRuslan Bukin 	error = xdma_setup_intr(sc->xchan_tx, 0,
86485ae89f4SRuslan Bukin 	    xae_xdma_tx_intr, sc, &sc->ih_tx);
86585ae89f4SRuslan Bukin 	if (error) {
86685ae89f4SRuslan Bukin 		device_printf(sc->dev,
86785ae89f4SRuslan Bukin 		    "Can't setup xDMA TX interrupt handler.\n");
86885ae89f4SRuslan Bukin 		return (ENXIO);
86985ae89f4SRuslan Bukin 	}
87085ae89f4SRuslan Bukin 
87185ae89f4SRuslan Bukin 	/* Alloc xDMA RX virtual channel. */
87285ae89f4SRuslan Bukin 	sc->xchan_rx = xdma_channel_alloc(sc->xdma_rx, 0);
87385ae89f4SRuslan Bukin 	if (sc->xchan_rx == NULL) {
87485ae89f4SRuslan Bukin 		device_printf(dev, "Can't alloc virtual DMA RX channel.\n");
87585ae89f4SRuslan Bukin 		return (ENXIO);
87685ae89f4SRuslan Bukin 	}
87785ae89f4SRuslan Bukin 
87885ae89f4SRuslan Bukin 	/* Setup interrupt handler. */
879d987842dSRuslan Bukin 	error = xdma_setup_intr(sc->xchan_rx, XDMA_INTR_NET,
88085ae89f4SRuslan Bukin 	    xae_xdma_rx_intr, sc, &sc->ih_rx);
88185ae89f4SRuslan Bukin 	if (error) {
88285ae89f4SRuslan Bukin 		device_printf(sc->dev,
88385ae89f4SRuslan Bukin 		    "Can't setup xDMA RX interrupt handler.\n");
88485ae89f4SRuslan Bukin 		return (ENXIO);
88585ae89f4SRuslan Bukin 	}
88685ae89f4SRuslan Bukin 
88785ae89f4SRuslan Bukin 	/* Setup bounce buffer */
88885ae89f4SRuslan Bukin 	vmem = xdma_get_memory(dev);
88985ae89f4SRuslan Bukin 	if (vmem) {
89085ae89f4SRuslan Bukin 		xchan_set_memory(sc->xchan_tx, vmem);
89185ae89f4SRuslan Bukin 		xchan_set_memory(sc->xchan_rx, vmem);
89285ae89f4SRuslan Bukin 	}
89385ae89f4SRuslan Bukin 
89485ae89f4SRuslan Bukin 	xdma_prep_sg(sc->xchan_tx,
89585ae89f4SRuslan Bukin 	    TX_QUEUE_SIZE,	/* xchan requests queue size */
89685ae89f4SRuslan Bukin 	    MCLBYTES,	/* maxsegsize */
89785ae89f4SRuslan Bukin 	    8,		/* maxnsegs */
89885ae89f4SRuslan Bukin 	    16,		/* alignment */
89985ae89f4SRuslan Bukin 	    0,		/* boundary */
90085ae89f4SRuslan Bukin 	    BUS_SPACE_MAXADDR_32BIT,
90185ae89f4SRuslan Bukin 	    BUS_SPACE_MAXADDR);
90285ae89f4SRuslan Bukin 
90385ae89f4SRuslan Bukin 	xdma_prep_sg(sc->xchan_rx,
90485ae89f4SRuslan Bukin 	    RX_QUEUE_SIZE,	/* xchan requests queue size */
90585ae89f4SRuslan Bukin 	    MCLBYTES,	/* maxsegsize */
90685ae89f4SRuslan Bukin 	    1,		/* maxnsegs */
90785ae89f4SRuslan Bukin 	    16,		/* alignment */
90885ae89f4SRuslan Bukin 	    0,		/* boundary */
90985ae89f4SRuslan Bukin 	    BUS_SPACE_MAXADDR_32BIT,
91085ae89f4SRuslan Bukin 	    BUS_SPACE_MAXADDR);
91185ae89f4SRuslan Bukin 
91285ae89f4SRuslan Bukin 	return (0);
91385ae89f4SRuslan Bukin }
91485ae89f4SRuslan Bukin 
91585ae89f4SRuslan Bukin static int
91685ae89f4SRuslan Bukin xae_probe(device_t dev)
91785ae89f4SRuslan Bukin {
91885ae89f4SRuslan Bukin 
91985ae89f4SRuslan Bukin 	if (!ofw_bus_status_okay(dev))
92085ae89f4SRuslan Bukin 		return (ENXIO);
92185ae89f4SRuslan Bukin 
92285ae89f4SRuslan Bukin 	if (!ofw_bus_is_compatible(dev, "xlnx,axi-ethernet-1.00.a"))
92385ae89f4SRuslan Bukin 		return (ENXIO);
92485ae89f4SRuslan Bukin 
92585ae89f4SRuslan Bukin 	device_set_desc(dev, "Xilinx AXI Ethernet");
92685ae89f4SRuslan Bukin 
92785ae89f4SRuslan Bukin 	return (BUS_PROBE_DEFAULT);
92885ae89f4SRuslan Bukin }
92985ae89f4SRuslan Bukin 
93085ae89f4SRuslan Bukin static int
93185ae89f4SRuslan Bukin xae_attach(device_t dev)
93285ae89f4SRuslan Bukin {
93385ae89f4SRuslan Bukin 	struct xae_softc *sc;
934dba12f75SJustin Hibbits 	if_t ifp;
93585ae89f4SRuslan Bukin 	phandle_t node;
93685ae89f4SRuslan Bukin 	uint32_t reg;
93785ae89f4SRuslan Bukin 	int error;
93885ae89f4SRuslan Bukin 
93985ae89f4SRuslan Bukin 	sc = device_get_softc(dev);
94085ae89f4SRuslan Bukin 	sc->dev = dev;
94185ae89f4SRuslan Bukin 	node = ofw_bus_get_node(dev);
94285ae89f4SRuslan Bukin 
94385ae89f4SRuslan Bukin 	if (setup_xdma(sc) != 0) {
94485ae89f4SRuslan Bukin 		device_printf(dev, "Could not setup xDMA.\n");
94585ae89f4SRuslan Bukin 		return (ENXIO);
94685ae89f4SRuslan Bukin 	}
94785ae89f4SRuslan Bukin 
94885ae89f4SRuslan Bukin 	mtx_init(&sc->mtx, device_get_nameunit(sc->dev),
94985ae89f4SRuslan Bukin 	    MTX_NETWORK_LOCK, MTX_DEF);
95085ae89f4SRuslan Bukin 
95185ae89f4SRuslan Bukin 	sc->br = buf_ring_alloc(BUFRING_SIZE, M_DEVBUF,
95285ae89f4SRuslan Bukin 	    M_NOWAIT, &sc->mtx);
95385ae89f4SRuslan Bukin 	if (sc->br == NULL)
95485ae89f4SRuslan Bukin 		return (ENOMEM);
95585ae89f4SRuslan Bukin 
95685ae89f4SRuslan Bukin 	if (bus_alloc_resources(dev, xae_spec, sc->res)) {
95785ae89f4SRuslan Bukin 		device_printf(dev, "could not allocate resources\n");
95885ae89f4SRuslan Bukin 		return (ENXIO);
95985ae89f4SRuslan Bukin 	}
96085ae89f4SRuslan Bukin 
96185ae89f4SRuslan Bukin 	/* Memory interface */
96285ae89f4SRuslan Bukin 	sc->bst = rman_get_bustag(sc->res[0]);
96385ae89f4SRuslan Bukin 	sc->bsh = rman_get_bushandle(sc->res[0]);
96485ae89f4SRuslan Bukin 
96585ae89f4SRuslan Bukin 	device_printf(sc->dev, "Identification: %x\n",
96685ae89f4SRuslan Bukin 	    READ4(sc, XAE_IDENT));
96785ae89f4SRuslan Bukin 
96885ae89f4SRuslan Bukin 	/* Get MAC addr */
96985ae89f4SRuslan Bukin 	if (xae_get_hwaddr(sc, sc->macaddr)) {
97085ae89f4SRuslan Bukin 		device_printf(sc->dev, "can't get mac\n");
97185ae89f4SRuslan Bukin 		return (ENXIO);
97285ae89f4SRuslan Bukin 	}
97385ae89f4SRuslan Bukin 
97485ae89f4SRuslan Bukin 	/* Enable MII clock */
97585ae89f4SRuslan Bukin 	reg = (MDIO_CLK_DIV_DEFAULT << MDIO_SETUP_CLK_DIV_S);
97685ae89f4SRuslan Bukin 	reg |= MDIO_SETUP_ENABLE;
97785ae89f4SRuslan Bukin 	WRITE4(sc, XAE_MDIO_SETUP, reg);
97885ae89f4SRuslan Bukin 	if (mdio_wait(sc))
97985ae89f4SRuslan Bukin 		return (ENXIO);
98085ae89f4SRuslan Bukin 
98185ae89f4SRuslan Bukin 	callout_init_mtx(&sc->xae_callout, &sc->mtx, 0);
98285ae89f4SRuslan Bukin 
98385ae89f4SRuslan Bukin 	/* Setup interrupt handler. */
98485ae89f4SRuslan Bukin 	error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_NET | INTR_MPSAFE,
98585ae89f4SRuslan Bukin 	    NULL, xae_intr, sc, &sc->intr_cookie);
98685ae89f4SRuslan Bukin 	if (error != 0) {
98785ae89f4SRuslan Bukin 		device_printf(dev, "could not setup interrupt handler.\n");
98885ae89f4SRuslan Bukin 		return (ENXIO);
98985ae89f4SRuslan Bukin 	}
99085ae89f4SRuslan Bukin 
99185ae89f4SRuslan Bukin 	/* Set up the ethernet interface. */
99285ae89f4SRuslan Bukin 	sc->ifp = ifp = if_alloc(IFT_ETHER);
993dba12f75SJustin Hibbits 	if_setsoftc(ifp, sc);
99485ae89f4SRuslan Bukin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
995dba12f75SJustin Hibbits 	if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
996dba12f75SJustin Hibbits 	if_setcapabilities(ifp, IFCAP_VLAN_MTU);
997dba12f75SJustin Hibbits 	if_setcapenable(ifp, if_getcapabilities(ifp));
998dba12f75SJustin Hibbits 	if_settransmitfn(ifp, xae_transmit);
999dba12f75SJustin Hibbits 	if_setqflushfn(ifp, xae_qflush);
1000dba12f75SJustin Hibbits 	if_setioctlfn(ifp, xae_ioctl);
1001dba12f75SJustin Hibbits 	if_setinitfn(ifp, xae_init);
1002dba12f75SJustin Hibbits 	if_setsendqlen(ifp, TX_DESC_COUNT - 1);
1003dba12f75SJustin Hibbits 	if_setsendqready(ifp);
100485ae89f4SRuslan Bukin 
100585ae89f4SRuslan Bukin 	if (xae_get_phyaddr(node, &sc->phy_addr) != 0)
100685ae89f4SRuslan Bukin 		return (ENXIO);
100785ae89f4SRuslan Bukin 
100885ae89f4SRuslan Bukin 	/* Attach the mii driver. */
100985ae89f4SRuslan Bukin 	error = mii_attach(dev, &sc->miibus, ifp, xae_media_change,
101085ae89f4SRuslan Bukin 	    xae_media_status, BMSR_DEFCAPMASK, sc->phy_addr,
101185ae89f4SRuslan Bukin 	    MII_OFFSET_ANY, 0);
101285ae89f4SRuslan Bukin 
101385ae89f4SRuslan Bukin 	if (error != 0) {
101485ae89f4SRuslan Bukin 		device_printf(dev, "PHY attach failed\n");
101585ae89f4SRuslan Bukin 		return (ENXIO);
101685ae89f4SRuslan Bukin 	}
101785ae89f4SRuslan Bukin 	sc->mii_softc = device_get_softc(sc->miibus);
101885ae89f4SRuslan Bukin 
101985ae89f4SRuslan Bukin 	/* Apply vcu118 workaround. */
102085ae89f4SRuslan Bukin 	if (OF_getproplen(node, "xlnx,vcu118") >= 0)
102185ae89f4SRuslan Bukin 		xae_phy_fixup(sc);
102285ae89f4SRuslan Bukin 
102385ae89f4SRuslan Bukin 	/* All ready to run, attach the ethernet interface. */
102485ae89f4SRuslan Bukin 	ether_ifattach(ifp, sc->macaddr);
102585ae89f4SRuslan Bukin 	sc->is_attached = true;
102685ae89f4SRuslan Bukin 
102785ae89f4SRuslan Bukin 	xae_rx_enqueue(sc, NUM_RX_MBUF);
102885ae89f4SRuslan Bukin 	xdma_queue_submit(sc->xchan_rx);
102985ae89f4SRuslan Bukin 
103085ae89f4SRuslan Bukin 	return (0);
103185ae89f4SRuslan Bukin }
103285ae89f4SRuslan Bukin 
103385ae89f4SRuslan Bukin static int
103485ae89f4SRuslan Bukin xae_detach(device_t dev)
103585ae89f4SRuslan Bukin {
103685ae89f4SRuslan Bukin 	struct xae_softc *sc;
1037dba12f75SJustin Hibbits 	if_t ifp;
103885ae89f4SRuslan Bukin 
103985ae89f4SRuslan Bukin 	sc = device_get_softc(dev);
104085ae89f4SRuslan Bukin 
104185ae89f4SRuslan Bukin 	KASSERT(mtx_initialized(&sc->mtx), ("%s: mutex not initialized",
104285ae89f4SRuslan Bukin 	    device_get_nameunit(dev)));
104385ae89f4SRuslan Bukin 
104485ae89f4SRuslan Bukin 	ifp = sc->ifp;
104585ae89f4SRuslan Bukin 
104685ae89f4SRuslan Bukin 	/* Only cleanup if attach succeeded. */
104785ae89f4SRuslan Bukin 	if (device_is_attached(dev)) {
104885ae89f4SRuslan Bukin 		XAE_LOCK(sc);
104985ae89f4SRuslan Bukin 		xae_stop_locked(sc);
105085ae89f4SRuslan Bukin 		XAE_UNLOCK(sc);
105185ae89f4SRuslan Bukin 		callout_drain(&sc->xae_callout);
105285ae89f4SRuslan Bukin 		ether_ifdetach(ifp);
105385ae89f4SRuslan Bukin 	}
105485ae89f4SRuslan Bukin 
1055*11a91178SJohn Baldwin 	bus_generic_detach(dev);
105685ae89f4SRuslan Bukin 
105785ae89f4SRuslan Bukin 	if (ifp != NULL)
105885ae89f4SRuslan Bukin 		if_free(ifp);
105985ae89f4SRuslan Bukin 
106085ae89f4SRuslan Bukin 	mtx_destroy(&sc->mtx);
106185ae89f4SRuslan Bukin 
106285ae89f4SRuslan Bukin 	bus_teardown_intr(dev, sc->res[1], sc->intr_cookie);
106385ae89f4SRuslan Bukin 
106485ae89f4SRuslan Bukin 	bus_release_resources(dev, xae_spec, sc->res);
106585ae89f4SRuslan Bukin 
106685ae89f4SRuslan Bukin 	xdma_channel_free(sc->xchan_tx);
106785ae89f4SRuslan Bukin 	xdma_channel_free(sc->xchan_rx);
106885ae89f4SRuslan Bukin 	xdma_put(sc->xdma_tx);
106985ae89f4SRuslan Bukin 	xdma_put(sc->xdma_rx);
107085ae89f4SRuslan Bukin 
107185ae89f4SRuslan Bukin 	return (0);
107285ae89f4SRuslan Bukin }
107385ae89f4SRuslan Bukin 
107485ae89f4SRuslan Bukin static void
107585ae89f4SRuslan Bukin xae_miibus_statchg(device_t dev)
107685ae89f4SRuslan Bukin {
107785ae89f4SRuslan Bukin 	struct xae_softc *sc;
107885ae89f4SRuslan Bukin 	struct mii_data *mii;
107985ae89f4SRuslan Bukin 	uint32_t reg;
108085ae89f4SRuslan Bukin 
108185ae89f4SRuslan Bukin 	/*
108285ae89f4SRuslan Bukin 	 * Called by the MII bus driver when the PHY establishes
108385ae89f4SRuslan Bukin 	 * link to set the MAC interface registers.
108485ae89f4SRuslan Bukin 	 */
108585ae89f4SRuslan Bukin 
108685ae89f4SRuslan Bukin 	sc = device_get_softc(dev);
108785ae89f4SRuslan Bukin 
108885ae89f4SRuslan Bukin 	XAE_ASSERT_LOCKED(sc);
108985ae89f4SRuslan Bukin 
109085ae89f4SRuslan Bukin 	mii = sc->mii_softc;
109185ae89f4SRuslan Bukin 
109285ae89f4SRuslan Bukin 	if (mii->mii_media_status & IFM_ACTIVE)
109385ae89f4SRuslan Bukin 		sc->link_is_up = true;
109485ae89f4SRuslan Bukin 	else
109585ae89f4SRuslan Bukin 		sc->link_is_up = false;
109685ae89f4SRuslan Bukin 
109785ae89f4SRuslan Bukin 	switch (IFM_SUBTYPE(mii->mii_media_active)) {
109885ae89f4SRuslan Bukin 	case IFM_1000_T:
109985ae89f4SRuslan Bukin 	case IFM_1000_SX:
110085ae89f4SRuslan Bukin 		reg = SPEED_1000;
110185ae89f4SRuslan Bukin 		break;
110285ae89f4SRuslan Bukin 	case IFM_100_TX:
110385ae89f4SRuslan Bukin 		reg = SPEED_100;
110485ae89f4SRuslan Bukin 		break;
110585ae89f4SRuslan Bukin 	case IFM_10_T:
110685ae89f4SRuslan Bukin 		reg = SPEED_10;
110785ae89f4SRuslan Bukin 		break;
110885ae89f4SRuslan Bukin 	case IFM_NONE:
110985ae89f4SRuslan Bukin 		sc->link_is_up = false;
111085ae89f4SRuslan Bukin 		return;
111185ae89f4SRuslan Bukin 	default:
111285ae89f4SRuslan Bukin 		sc->link_is_up = false;
111385ae89f4SRuslan Bukin 		device_printf(dev, "Unsupported media %u\n",
111485ae89f4SRuslan Bukin 		    IFM_SUBTYPE(mii->mii_media_active));
111585ae89f4SRuslan Bukin 		return;
111685ae89f4SRuslan Bukin 	}
111785ae89f4SRuslan Bukin 
111885ae89f4SRuslan Bukin 	WRITE4(sc, XAE_SPEED, reg);
111985ae89f4SRuslan Bukin }
112085ae89f4SRuslan Bukin 
112185ae89f4SRuslan Bukin static device_method_t xae_methods[] = {
112285ae89f4SRuslan Bukin 	DEVMETHOD(device_probe,		xae_probe),
112385ae89f4SRuslan Bukin 	DEVMETHOD(device_attach,	xae_attach),
112485ae89f4SRuslan Bukin 	DEVMETHOD(device_detach,	xae_detach),
112585ae89f4SRuslan Bukin 
112685ae89f4SRuslan Bukin 	/* MII Interface */
112785ae89f4SRuslan Bukin 	DEVMETHOD(miibus_readreg,	xae_miibus_read_reg),
112885ae89f4SRuslan Bukin 	DEVMETHOD(miibus_writereg,	xae_miibus_write_reg),
112985ae89f4SRuslan Bukin 	DEVMETHOD(miibus_statchg,	xae_miibus_statchg),
113085ae89f4SRuslan Bukin 	{ 0, 0 }
113185ae89f4SRuslan Bukin };
113285ae89f4SRuslan Bukin 
113385ae89f4SRuslan Bukin driver_t xae_driver = {
113485ae89f4SRuslan Bukin 	"xae",
113585ae89f4SRuslan Bukin 	xae_methods,
113685ae89f4SRuslan Bukin 	sizeof(struct xae_softc),
113785ae89f4SRuslan Bukin };
113885ae89f4SRuslan Bukin 
113990b8b224SJohn Baldwin DRIVER_MODULE(xae, simplebus, xae_driver, 0, 0);
11403e38757dSJohn Baldwin DRIVER_MODULE(miibus, xae, miibus_driver, 0, 0);
114185ae89f4SRuslan Bukin 
114285ae89f4SRuslan Bukin MODULE_DEPEND(xae, ether, 1, 1, 1);
114385ae89f4SRuslan Bukin MODULE_DEPEND(xae, miibus, 1, 1, 1);
1144