xref: /freebsd/sys/dev/xilinx/if_xae.c (revision 85ae89f4bb5a023f54ab93e661d7de337c743dbe)
1*85ae89f4SRuslan Bukin /*-
2*85ae89f4SRuslan Bukin  * SPDX-License-Identifier: BSD-2-Clause
3*85ae89f4SRuslan Bukin  *
4*85ae89f4SRuslan Bukin  * Copyright (c) 2019 Ruslan Bukin <br@bsdpad.com>
5*85ae89f4SRuslan Bukin  *
6*85ae89f4SRuslan Bukin  * This software was developed by SRI International and the University of
7*85ae89f4SRuslan Bukin  * Cambridge Computer Laboratory (Department of Computer Science and
8*85ae89f4SRuslan Bukin  * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
9*85ae89f4SRuslan Bukin  * DARPA SSITH research programme.
10*85ae89f4SRuslan Bukin  *
11*85ae89f4SRuslan Bukin  * Redistribution and use in source and binary forms, with or without
12*85ae89f4SRuslan Bukin  * modification, are permitted provided that the following conditions
13*85ae89f4SRuslan Bukin  * are met:
14*85ae89f4SRuslan Bukin  * 1. Redistributions of source code must retain the above copyright
15*85ae89f4SRuslan Bukin  *    notice, this list of conditions and the following disclaimer.
16*85ae89f4SRuslan Bukin  * 2. Redistributions in binary form must reproduce the above copyright
17*85ae89f4SRuslan Bukin  *    notice, this list of conditions and the following disclaimer in the
18*85ae89f4SRuslan Bukin  *    documentation and/or other materials provided with the distribution.
19*85ae89f4SRuslan Bukin  *
20*85ae89f4SRuslan Bukin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21*85ae89f4SRuslan Bukin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22*85ae89f4SRuslan Bukin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23*85ae89f4SRuslan Bukin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24*85ae89f4SRuslan Bukin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25*85ae89f4SRuslan Bukin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26*85ae89f4SRuslan Bukin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27*85ae89f4SRuslan Bukin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28*85ae89f4SRuslan Bukin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29*85ae89f4SRuslan Bukin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30*85ae89f4SRuslan Bukin  * SUCH DAMAGE.
31*85ae89f4SRuslan Bukin  */
32*85ae89f4SRuslan Bukin 
33*85ae89f4SRuslan Bukin #include <sys/cdefs.h>
34*85ae89f4SRuslan Bukin __FBSDID("$FreeBSD$");
35*85ae89f4SRuslan Bukin 
36*85ae89f4SRuslan Bukin #include <sys/param.h>
37*85ae89f4SRuslan Bukin #include <sys/systm.h>
38*85ae89f4SRuslan Bukin #include <sys/bus.h>
39*85ae89f4SRuslan Bukin #include <sys/kernel.h>
40*85ae89f4SRuslan Bukin #include <sys/lock.h>
41*85ae89f4SRuslan Bukin #include <sys/malloc.h>
42*85ae89f4SRuslan Bukin #include <sys/mbuf.h>
43*85ae89f4SRuslan Bukin #include <sys/module.h>
44*85ae89f4SRuslan Bukin #include <sys/mutex.h>
45*85ae89f4SRuslan Bukin #include <sys/rman.h>
46*85ae89f4SRuslan Bukin #include <sys/socket.h>
47*85ae89f4SRuslan Bukin #include <sys/sockio.h>
48*85ae89f4SRuslan Bukin 
49*85ae89f4SRuslan Bukin #include <net/bpf.h>
50*85ae89f4SRuslan Bukin #include <net/if.h>
51*85ae89f4SRuslan Bukin #include <net/ethernet.h>
52*85ae89f4SRuslan Bukin #include <net/if_dl.h>
53*85ae89f4SRuslan Bukin #include <net/if_media.h>
54*85ae89f4SRuslan Bukin #include <net/if_types.h>
55*85ae89f4SRuslan Bukin #include <net/if_var.h>
56*85ae89f4SRuslan Bukin 
57*85ae89f4SRuslan Bukin #include <machine/bus.h>
58*85ae89f4SRuslan Bukin 
59*85ae89f4SRuslan Bukin #include <dev/mii/mii.h>
60*85ae89f4SRuslan Bukin #include <dev/mii/miivar.h>
61*85ae89f4SRuslan Bukin #include <dev/mii/tiphy.h>
62*85ae89f4SRuslan Bukin #include <dev/ofw/ofw_bus.h>
63*85ae89f4SRuslan Bukin #include <dev/ofw/ofw_bus_subr.h>
64*85ae89f4SRuslan Bukin #include <dev/xilinx/if_xaereg.h>
65*85ae89f4SRuslan Bukin #include <dev/xilinx/if_xaevar.h>
66*85ae89f4SRuslan Bukin 
67*85ae89f4SRuslan Bukin #include "miibus_if.h"
68*85ae89f4SRuslan Bukin 
69*85ae89f4SRuslan Bukin #define	READ4(_sc, _reg) \
70*85ae89f4SRuslan Bukin 	bus_read_4((_sc)->res[0], _reg)
71*85ae89f4SRuslan Bukin #define	WRITE4(_sc, _reg, _val) \
72*85ae89f4SRuslan Bukin 	bus_write_4((_sc)->res[0], _reg, _val)
73*85ae89f4SRuslan Bukin 
74*85ae89f4SRuslan Bukin #define	READ8(_sc, _reg) \
75*85ae89f4SRuslan Bukin 	bus_read_8((_sc)->res[0], _reg)
76*85ae89f4SRuslan Bukin #define	WRITE8(_sc, _reg, _val) \
77*85ae89f4SRuslan Bukin 	bus_write_8((_sc)->res[0], _reg, _val)
78*85ae89f4SRuslan Bukin 
79*85ae89f4SRuslan Bukin #define	XAE_LOCK(sc)			mtx_lock(&(sc)->mtx)
80*85ae89f4SRuslan Bukin #define	XAE_UNLOCK(sc)			mtx_unlock(&(sc)->mtx)
81*85ae89f4SRuslan Bukin #define	XAE_ASSERT_LOCKED(sc)		mtx_assert(&(sc)->mtx, MA_OWNED)
82*85ae89f4SRuslan Bukin #define	XAE_ASSERT_UNLOCKED(sc)		mtx_assert(&(sc)->mtx, MA_NOTOWNED)
83*85ae89f4SRuslan Bukin 
84*85ae89f4SRuslan Bukin #define XAE_DEBUG
85*85ae89f4SRuslan Bukin #undef XAE_DEBUG
86*85ae89f4SRuslan Bukin 
87*85ae89f4SRuslan Bukin #ifdef XAE_DEBUG
88*85ae89f4SRuslan Bukin #define dprintf(fmt, ...)  printf(fmt, ##__VA_ARGS__)
89*85ae89f4SRuslan Bukin #else
90*85ae89f4SRuslan Bukin #define dprintf(fmt, ...)
91*85ae89f4SRuslan Bukin #endif
92*85ae89f4SRuslan Bukin 
93*85ae89f4SRuslan Bukin #define	RX_QUEUE_SIZE		64
94*85ae89f4SRuslan Bukin #define	TX_QUEUE_SIZE		64
95*85ae89f4SRuslan Bukin #define	NUM_RX_MBUF		16
96*85ae89f4SRuslan Bukin #define	BUFRING_SIZE		8192
97*85ae89f4SRuslan Bukin #define	MDIO_CLK_DIV_DEFAULT	29
98*85ae89f4SRuslan Bukin 
99*85ae89f4SRuslan Bukin #define	PHY1_RD(sc, _r)		\
100*85ae89f4SRuslan Bukin 	xae_miibus_read_reg(sc->dev, 1, _r)
101*85ae89f4SRuslan Bukin #define	PHY1_WR(sc, _r, _v)	\
102*85ae89f4SRuslan Bukin 	xae_miibus_write_reg(sc->dev, 1, _r, _v)
103*85ae89f4SRuslan Bukin 
104*85ae89f4SRuslan Bukin #define	PHY_RD(sc, _r)		\
105*85ae89f4SRuslan Bukin 	xae_miibus_read_reg(sc->dev, sc->phy_addr, _r)
106*85ae89f4SRuslan Bukin #define	PHY_WR(sc, _r, _v)	\
107*85ae89f4SRuslan Bukin 	xae_miibus_write_reg(sc->dev, sc->phy_addr, _r, _v)
108*85ae89f4SRuslan Bukin 
109*85ae89f4SRuslan Bukin /* Use this macro to access regs > 0x1f */
110*85ae89f4SRuslan Bukin #define WRITE_TI_EREG(sc, reg, data) {					\
111*85ae89f4SRuslan Bukin 	PHY_WR(sc, MII_MMDACR, MMDACR_DADDRMASK);			\
112*85ae89f4SRuslan Bukin 	PHY_WR(sc, MII_MMDAADR, reg);					\
113*85ae89f4SRuslan Bukin 	PHY_WR(sc, MII_MMDACR, MMDACR_DADDRMASK | MMDACR_FN_DATANPI);	\
114*85ae89f4SRuslan Bukin 	PHY_WR(sc, MII_MMDAADR, data);					\
115*85ae89f4SRuslan Bukin }
116*85ae89f4SRuslan Bukin 
117*85ae89f4SRuslan Bukin /* Not documented, Xilinx VCU118 workaround */
118*85ae89f4SRuslan Bukin #define	 CFG4_SGMII_TMR			0x160 /* bits 8:7 MUST be '10' */
119*85ae89f4SRuslan Bukin #define	DP83867_SGMIICTL1		0xD3 /* not documented register */
120*85ae89f4SRuslan Bukin #define	 SGMIICTL1_SGMII_6W		(1 << 14) /* no idea what it is */
121*85ae89f4SRuslan Bukin 
122*85ae89f4SRuslan Bukin static struct resource_spec xae_spec[] = {
123*85ae89f4SRuslan Bukin 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
124*85ae89f4SRuslan Bukin 	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
125*85ae89f4SRuslan Bukin 	{ -1, 0 }
126*85ae89f4SRuslan Bukin };
127*85ae89f4SRuslan Bukin 
128*85ae89f4SRuslan Bukin static void xae_stop_locked(struct xae_softc *sc);
129*85ae89f4SRuslan Bukin static void xae_setup_rxfilter(struct xae_softc *sc);
130*85ae89f4SRuslan Bukin 
131*85ae89f4SRuslan Bukin static int
132*85ae89f4SRuslan Bukin xae_rx_enqueue(struct xae_softc *sc, uint32_t n)
133*85ae89f4SRuslan Bukin {
134*85ae89f4SRuslan Bukin 	struct mbuf *m;
135*85ae89f4SRuslan Bukin 	int i;
136*85ae89f4SRuslan Bukin 
137*85ae89f4SRuslan Bukin 	for (i = 0; i < n; i++) {
138*85ae89f4SRuslan Bukin 		m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
139*85ae89f4SRuslan Bukin 		if (m == NULL) {
140*85ae89f4SRuslan Bukin 			device_printf(sc->dev,
141*85ae89f4SRuslan Bukin 			    "%s: Can't alloc rx mbuf\n", __func__);
142*85ae89f4SRuslan Bukin 			return (-1);
143*85ae89f4SRuslan Bukin 		}
144*85ae89f4SRuslan Bukin 
145*85ae89f4SRuslan Bukin 		m->m_pkthdr.len = m->m_len = m->m_ext.ext_size;
146*85ae89f4SRuslan Bukin 		xdma_enqueue_mbuf(sc->xchan_rx, &m, 0, 4, 4, XDMA_DEV_TO_MEM);
147*85ae89f4SRuslan Bukin 	}
148*85ae89f4SRuslan Bukin 
149*85ae89f4SRuslan Bukin 	return (0);
150*85ae89f4SRuslan Bukin }
151*85ae89f4SRuslan Bukin 
152*85ae89f4SRuslan Bukin static int
153*85ae89f4SRuslan Bukin xae_get_phyaddr(phandle_t node, int *phy_addr)
154*85ae89f4SRuslan Bukin {
155*85ae89f4SRuslan Bukin 	phandle_t phy_node;
156*85ae89f4SRuslan Bukin 	pcell_t phy_handle, phy_reg;
157*85ae89f4SRuslan Bukin 
158*85ae89f4SRuslan Bukin 	if (OF_getencprop(node, "phy-handle", (void *)&phy_handle,
159*85ae89f4SRuslan Bukin 	    sizeof(phy_handle)) <= 0)
160*85ae89f4SRuslan Bukin 		return (ENXIO);
161*85ae89f4SRuslan Bukin 
162*85ae89f4SRuslan Bukin 	phy_node = OF_node_from_xref(phy_handle);
163*85ae89f4SRuslan Bukin 
164*85ae89f4SRuslan Bukin 	if (OF_getencprop(phy_node, "reg", (void *)&phy_reg,
165*85ae89f4SRuslan Bukin 	    sizeof(phy_reg)) <= 0)
166*85ae89f4SRuslan Bukin 		return (ENXIO);
167*85ae89f4SRuslan Bukin 
168*85ae89f4SRuslan Bukin 	*phy_addr = phy_reg;
169*85ae89f4SRuslan Bukin 
170*85ae89f4SRuslan Bukin 	return (0);
171*85ae89f4SRuslan Bukin }
172*85ae89f4SRuslan Bukin 
173*85ae89f4SRuslan Bukin static int
174*85ae89f4SRuslan Bukin xae_xdma_tx_intr(void *arg, xdma_transfer_status_t *status)
175*85ae89f4SRuslan Bukin {
176*85ae89f4SRuslan Bukin 	xdma_transfer_status_t st;
177*85ae89f4SRuslan Bukin 	struct xae_softc *sc;
178*85ae89f4SRuslan Bukin 	struct ifnet *ifp;
179*85ae89f4SRuslan Bukin 	struct mbuf *m;
180*85ae89f4SRuslan Bukin 	int err;
181*85ae89f4SRuslan Bukin 
182*85ae89f4SRuslan Bukin 	sc = arg;
183*85ae89f4SRuslan Bukin 
184*85ae89f4SRuslan Bukin 	XAE_LOCK(sc);
185*85ae89f4SRuslan Bukin 
186*85ae89f4SRuslan Bukin 	ifp = sc->ifp;
187*85ae89f4SRuslan Bukin 
188*85ae89f4SRuslan Bukin 	for (;;) {
189*85ae89f4SRuslan Bukin 		err = xdma_dequeue_mbuf(sc->xchan_tx, &m, &st);
190*85ae89f4SRuslan Bukin 		if (err != 0) {
191*85ae89f4SRuslan Bukin 			break;
192*85ae89f4SRuslan Bukin 		}
193*85ae89f4SRuslan Bukin 
194*85ae89f4SRuslan Bukin 		if (st.error != 0) {
195*85ae89f4SRuslan Bukin 			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
196*85ae89f4SRuslan Bukin 		}
197*85ae89f4SRuslan Bukin 
198*85ae89f4SRuslan Bukin 		m_freem(m);
199*85ae89f4SRuslan Bukin 	}
200*85ae89f4SRuslan Bukin 
201*85ae89f4SRuslan Bukin 	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
202*85ae89f4SRuslan Bukin 
203*85ae89f4SRuslan Bukin 	XAE_UNLOCK(sc);
204*85ae89f4SRuslan Bukin 
205*85ae89f4SRuslan Bukin 	return (0);
206*85ae89f4SRuslan Bukin }
207*85ae89f4SRuslan Bukin 
208*85ae89f4SRuslan Bukin static int
209*85ae89f4SRuslan Bukin xae_xdma_rx_intr(void *arg, xdma_transfer_status_t *status)
210*85ae89f4SRuslan Bukin {
211*85ae89f4SRuslan Bukin 	xdma_transfer_status_t st;
212*85ae89f4SRuslan Bukin 	struct xae_softc *sc;
213*85ae89f4SRuslan Bukin 	struct ifnet *ifp;
214*85ae89f4SRuslan Bukin 	struct mbuf *m;
215*85ae89f4SRuslan Bukin 	int err;
216*85ae89f4SRuslan Bukin 	uint32_t cnt_processed;
217*85ae89f4SRuslan Bukin 
218*85ae89f4SRuslan Bukin 	sc = arg;
219*85ae89f4SRuslan Bukin 
220*85ae89f4SRuslan Bukin 	dprintf("%s\n", __func__);
221*85ae89f4SRuslan Bukin 
222*85ae89f4SRuslan Bukin 	XAE_LOCK(sc);
223*85ae89f4SRuslan Bukin 
224*85ae89f4SRuslan Bukin 	ifp = sc->ifp;
225*85ae89f4SRuslan Bukin 
226*85ae89f4SRuslan Bukin 	cnt_processed = 0;
227*85ae89f4SRuslan Bukin 	for (;;) {
228*85ae89f4SRuslan Bukin 		err = xdma_dequeue_mbuf(sc->xchan_rx, &m, &st);
229*85ae89f4SRuslan Bukin 		if (err != 0) {
230*85ae89f4SRuslan Bukin 			break;
231*85ae89f4SRuslan Bukin 		}
232*85ae89f4SRuslan Bukin 		cnt_processed++;
233*85ae89f4SRuslan Bukin 
234*85ae89f4SRuslan Bukin 		if (st.error != 0) {
235*85ae89f4SRuslan Bukin 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
236*85ae89f4SRuslan Bukin 			m_freem(m);
237*85ae89f4SRuslan Bukin 			continue;
238*85ae89f4SRuslan Bukin 		}
239*85ae89f4SRuslan Bukin 
240*85ae89f4SRuslan Bukin 		m->m_pkthdr.len = m->m_len = st.transferred;
241*85ae89f4SRuslan Bukin 		m->m_pkthdr.rcvif = ifp;
242*85ae89f4SRuslan Bukin 		XAE_UNLOCK(sc);
243*85ae89f4SRuslan Bukin 		(*ifp->if_input)(ifp, m);
244*85ae89f4SRuslan Bukin 		XAE_LOCK(sc);
245*85ae89f4SRuslan Bukin 	}
246*85ae89f4SRuslan Bukin 
247*85ae89f4SRuslan Bukin 	xae_rx_enqueue(sc, cnt_processed);
248*85ae89f4SRuslan Bukin 
249*85ae89f4SRuslan Bukin 	XAE_UNLOCK(sc);
250*85ae89f4SRuslan Bukin 
251*85ae89f4SRuslan Bukin 	return (0);
252*85ae89f4SRuslan Bukin }
253*85ae89f4SRuslan Bukin 
254*85ae89f4SRuslan Bukin static void
255*85ae89f4SRuslan Bukin xae_qflush(struct ifnet *ifp)
256*85ae89f4SRuslan Bukin {
257*85ae89f4SRuslan Bukin 	struct xae_softc *sc;
258*85ae89f4SRuslan Bukin 
259*85ae89f4SRuslan Bukin 	sc = ifp->if_softc;
260*85ae89f4SRuslan Bukin }
261*85ae89f4SRuslan Bukin 
262*85ae89f4SRuslan Bukin static int
263*85ae89f4SRuslan Bukin xae_transmit_locked(struct ifnet *ifp)
264*85ae89f4SRuslan Bukin {
265*85ae89f4SRuslan Bukin 	struct xae_softc *sc;
266*85ae89f4SRuslan Bukin 	struct mbuf *m;
267*85ae89f4SRuslan Bukin 	struct buf_ring *br;
268*85ae89f4SRuslan Bukin 	int error;
269*85ae89f4SRuslan Bukin 	int enq;
270*85ae89f4SRuslan Bukin 
271*85ae89f4SRuslan Bukin 	dprintf("%s\n", __func__);
272*85ae89f4SRuslan Bukin 
273*85ae89f4SRuslan Bukin 	sc = ifp->if_softc;
274*85ae89f4SRuslan Bukin 	br = sc->br;
275*85ae89f4SRuslan Bukin 
276*85ae89f4SRuslan Bukin 	enq = 0;
277*85ae89f4SRuslan Bukin 
278*85ae89f4SRuslan Bukin 	while ((m = drbr_peek(ifp, br)) != NULL) {
279*85ae89f4SRuslan Bukin 		error = xdma_enqueue_mbuf(sc->xchan_tx,
280*85ae89f4SRuslan Bukin 		    &m, 0, 4, 4, XDMA_MEM_TO_DEV);
281*85ae89f4SRuslan Bukin 		if (error != 0) {
282*85ae89f4SRuslan Bukin 			/* No space in request queue available yet. */
283*85ae89f4SRuslan Bukin 			drbr_putback(ifp, br, m);
284*85ae89f4SRuslan Bukin 			break;
285*85ae89f4SRuslan Bukin 		}
286*85ae89f4SRuslan Bukin 
287*85ae89f4SRuslan Bukin 		drbr_advance(ifp, br);
288*85ae89f4SRuslan Bukin 
289*85ae89f4SRuslan Bukin 		enq++;
290*85ae89f4SRuslan Bukin 
291*85ae89f4SRuslan Bukin 		/* If anyone is interested give them a copy. */
292*85ae89f4SRuslan Bukin 		ETHER_BPF_MTAP(ifp, m);
293*85ae89f4SRuslan Bukin         }
294*85ae89f4SRuslan Bukin 
295*85ae89f4SRuslan Bukin 	if (enq > 0)
296*85ae89f4SRuslan Bukin 		xdma_queue_submit(sc->xchan_tx);
297*85ae89f4SRuslan Bukin 
298*85ae89f4SRuslan Bukin 	return (0);
299*85ae89f4SRuslan Bukin }
300*85ae89f4SRuslan Bukin 
301*85ae89f4SRuslan Bukin static int
302*85ae89f4SRuslan Bukin xae_transmit(struct ifnet *ifp, struct mbuf *m)
303*85ae89f4SRuslan Bukin {
304*85ae89f4SRuslan Bukin 	struct xae_softc *sc;
305*85ae89f4SRuslan Bukin 	int error;
306*85ae89f4SRuslan Bukin 
307*85ae89f4SRuslan Bukin 	dprintf("%s\n", __func__);
308*85ae89f4SRuslan Bukin 
309*85ae89f4SRuslan Bukin 	sc = ifp->if_softc;
310*85ae89f4SRuslan Bukin 
311*85ae89f4SRuslan Bukin 	XAE_LOCK(sc);
312*85ae89f4SRuslan Bukin 
313*85ae89f4SRuslan Bukin 	error = drbr_enqueue(ifp, sc->br, m);
314*85ae89f4SRuslan Bukin 	if (error) {
315*85ae89f4SRuslan Bukin 		XAE_UNLOCK(sc);
316*85ae89f4SRuslan Bukin 		return (error);
317*85ae89f4SRuslan Bukin 	}
318*85ae89f4SRuslan Bukin 
319*85ae89f4SRuslan Bukin 	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
320*85ae89f4SRuslan Bukin 	    IFF_DRV_RUNNING) {
321*85ae89f4SRuslan Bukin 		XAE_UNLOCK(sc);
322*85ae89f4SRuslan Bukin 		return (0);
323*85ae89f4SRuslan Bukin 	}
324*85ae89f4SRuslan Bukin 
325*85ae89f4SRuslan Bukin 	if (!sc->link_is_up) {
326*85ae89f4SRuslan Bukin 		XAE_UNLOCK(sc);
327*85ae89f4SRuslan Bukin 		return (0);
328*85ae89f4SRuslan Bukin 	}
329*85ae89f4SRuslan Bukin 
330*85ae89f4SRuslan Bukin 	error = xae_transmit_locked(ifp);
331*85ae89f4SRuslan Bukin 
332*85ae89f4SRuslan Bukin 	XAE_UNLOCK(sc);
333*85ae89f4SRuslan Bukin 
334*85ae89f4SRuslan Bukin 	return (error);
335*85ae89f4SRuslan Bukin }
336*85ae89f4SRuslan Bukin 
337*85ae89f4SRuslan Bukin static void
338*85ae89f4SRuslan Bukin xae_stop_locked(struct xae_softc *sc)
339*85ae89f4SRuslan Bukin {
340*85ae89f4SRuslan Bukin 	struct ifnet *ifp;
341*85ae89f4SRuslan Bukin 	uint32_t reg;
342*85ae89f4SRuslan Bukin 
343*85ae89f4SRuslan Bukin 	XAE_ASSERT_LOCKED(sc);
344*85ae89f4SRuslan Bukin 
345*85ae89f4SRuslan Bukin 	ifp = sc->ifp;
346*85ae89f4SRuslan Bukin 	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
347*85ae89f4SRuslan Bukin 
348*85ae89f4SRuslan Bukin 	callout_stop(&sc->xae_callout);
349*85ae89f4SRuslan Bukin 
350*85ae89f4SRuslan Bukin 	/* Stop the transmitter */
351*85ae89f4SRuslan Bukin 	reg = READ4(sc, XAE_TC);
352*85ae89f4SRuslan Bukin 	reg &= ~TC_TX;
353*85ae89f4SRuslan Bukin 	WRITE4(sc, XAE_TC, reg);
354*85ae89f4SRuslan Bukin 
355*85ae89f4SRuslan Bukin 	/* Stop the receiver. */
356*85ae89f4SRuslan Bukin 	reg = READ4(sc, XAE_RCW1);
357*85ae89f4SRuslan Bukin 	reg &= ~RCW1_RX;
358*85ae89f4SRuslan Bukin 	WRITE4(sc, XAE_RCW1, reg);
359*85ae89f4SRuslan Bukin }
360*85ae89f4SRuslan Bukin 
361*85ae89f4SRuslan Bukin static uint64_t
362*85ae89f4SRuslan Bukin xae_stat(struct xae_softc *sc, int counter_id)
363*85ae89f4SRuslan Bukin {
364*85ae89f4SRuslan Bukin 	uint64_t new, old;
365*85ae89f4SRuslan Bukin 	uint64_t delta;
366*85ae89f4SRuslan Bukin 
367*85ae89f4SRuslan Bukin 	KASSERT(counter_id < XAE_MAX_COUNTERS,
368*85ae89f4SRuslan Bukin 		("counter %d is out of range", counter_id));
369*85ae89f4SRuslan Bukin 
370*85ae89f4SRuslan Bukin 	new = READ8(sc, XAE_STATCNT(counter_id));
371*85ae89f4SRuslan Bukin 	old = sc->counters[counter_id];
372*85ae89f4SRuslan Bukin 
373*85ae89f4SRuslan Bukin 	if (new >= old)
374*85ae89f4SRuslan Bukin 		delta = new - old;
375*85ae89f4SRuslan Bukin 	else
376*85ae89f4SRuslan Bukin 		delta = UINT64_MAX - old + new;
377*85ae89f4SRuslan Bukin 	sc->counters[counter_id] = new;
378*85ae89f4SRuslan Bukin 
379*85ae89f4SRuslan Bukin 	return (delta);
380*85ae89f4SRuslan Bukin }
381*85ae89f4SRuslan Bukin 
382*85ae89f4SRuslan Bukin static void
383*85ae89f4SRuslan Bukin xae_harvest_stats(struct xae_softc *sc)
384*85ae89f4SRuslan Bukin {
385*85ae89f4SRuslan Bukin 	struct ifnet *ifp;
386*85ae89f4SRuslan Bukin 
387*85ae89f4SRuslan Bukin 	ifp = sc->ifp;
388*85ae89f4SRuslan Bukin 
389*85ae89f4SRuslan Bukin 	if_inc_counter(ifp, IFCOUNTER_IPACKETS, xae_stat(sc, RX_GOOD_FRAMES));
390*85ae89f4SRuslan Bukin 	if_inc_counter(ifp, IFCOUNTER_IMCASTS, xae_stat(sc, RX_GOOD_MCASTS));
391*85ae89f4SRuslan Bukin 	if_inc_counter(ifp, IFCOUNTER_IERRORS,
392*85ae89f4SRuslan Bukin 	    xae_stat(sc, RX_FRAME_CHECK_SEQ_ERROR) +
393*85ae89f4SRuslan Bukin 	    xae_stat(sc, RX_LEN_OUT_OF_RANGE) +
394*85ae89f4SRuslan Bukin 	    xae_stat(sc, RX_ALIGNMENT_ERRORS));
395*85ae89f4SRuslan Bukin 
396*85ae89f4SRuslan Bukin 	if_inc_counter(ifp, IFCOUNTER_OBYTES, xae_stat(sc, TX_BYTES));
397*85ae89f4SRuslan Bukin 	if_inc_counter(ifp, IFCOUNTER_OPACKETS, xae_stat(sc, TX_GOOD_FRAMES));
398*85ae89f4SRuslan Bukin 	if_inc_counter(ifp, IFCOUNTER_OMCASTS, xae_stat(sc, TX_GOOD_MCASTS));
399*85ae89f4SRuslan Bukin 	if_inc_counter(ifp, IFCOUNTER_OERRORS,
400*85ae89f4SRuslan Bukin 	    xae_stat(sc, TX_GOOD_UNDERRUN_ERRORS));
401*85ae89f4SRuslan Bukin 
402*85ae89f4SRuslan Bukin 	if_inc_counter(ifp, IFCOUNTER_COLLISIONS,
403*85ae89f4SRuslan Bukin 	    xae_stat(sc, TX_SINGLE_COLLISION_FRAMES) +
404*85ae89f4SRuslan Bukin 	    xae_stat(sc, TX_MULTI_COLLISION_FRAMES) +
405*85ae89f4SRuslan Bukin 	    xae_stat(sc, TX_LATE_COLLISIONS) +
406*85ae89f4SRuslan Bukin 	    xae_stat(sc, TX_EXCESS_COLLISIONS));
407*85ae89f4SRuslan Bukin }
408*85ae89f4SRuslan Bukin 
409*85ae89f4SRuslan Bukin static void
410*85ae89f4SRuslan Bukin xae_tick(void *arg)
411*85ae89f4SRuslan Bukin {
412*85ae89f4SRuslan Bukin 	struct xae_softc *sc;
413*85ae89f4SRuslan Bukin 	struct ifnet *ifp;
414*85ae89f4SRuslan Bukin 	int link_was_up;
415*85ae89f4SRuslan Bukin 
416*85ae89f4SRuslan Bukin 	sc = arg;
417*85ae89f4SRuslan Bukin 
418*85ae89f4SRuslan Bukin 	XAE_ASSERT_LOCKED(sc);
419*85ae89f4SRuslan Bukin 
420*85ae89f4SRuslan Bukin 	ifp = sc->ifp;
421*85ae89f4SRuslan Bukin 
422*85ae89f4SRuslan Bukin 	if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
423*85ae89f4SRuslan Bukin 		return;
424*85ae89f4SRuslan Bukin 
425*85ae89f4SRuslan Bukin 	/* Gather stats from hardware counters. */
426*85ae89f4SRuslan Bukin 	xae_harvest_stats(sc);
427*85ae89f4SRuslan Bukin 
428*85ae89f4SRuslan Bukin 	/* Check the media status. */
429*85ae89f4SRuslan Bukin 	link_was_up = sc->link_is_up;
430*85ae89f4SRuslan Bukin 	mii_tick(sc->mii_softc);
431*85ae89f4SRuslan Bukin 	if (sc->link_is_up && !link_was_up)
432*85ae89f4SRuslan Bukin 		xae_transmit_locked(sc->ifp);
433*85ae89f4SRuslan Bukin 
434*85ae89f4SRuslan Bukin 	/* Schedule another check one second from now. */
435*85ae89f4SRuslan Bukin 	callout_reset(&sc->xae_callout, hz, xae_tick, sc);
436*85ae89f4SRuslan Bukin }
437*85ae89f4SRuslan Bukin 
438*85ae89f4SRuslan Bukin static void
439*85ae89f4SRuslan Bukin xae_init_locked(struct xae_softc *sc)
440*85ae89f4SRuslan Bukin {
441*85ae89f4SRuslan Bukin 	struct ifnet *ifp;
442*85ae89f4SRuslan Bukin 
443*85ae89f4SRuslan Bukin 	XAE_ASSERT_LOCKED(sc);
444*85ae89f4SRuslan Bukin 
445*85ae89f4SRuslan Bukin 	ifp = sc->ifp;
446*85ae89f4SRuslan Bukin 	if (ifp->if_drv_flags & IFF_DRV_RUNNING)
447*85ae89f4SRuslan Bukin 		return;
448*85ae89f4SRuslan Bukin 
449*85ae89f4SRuslan Bukin 	ifp->if_drv_flags |= IFF_DRV_RUNNING;
450*85ae89f4SRuslan Bukin 
451*85ae89f4SRuslan Bukin 	xae_setup_rxfilter(sc);
452*85ae89f4SRuslan Bukin 
453*85ae89f4SRuslan Bukin 	/* Enable the transmitter */
454*85ae89f4SRuslan Bukin 	WRITE4(sc, XAE_TC, TC_TX);
455*85ae89f4SRuslan Bukin 
456*85ae89f4SRuslan Bukin 	/* Enable the receiver. */
457*85ae89f4SRuslan Bukin 	WRITE4(sc, XAE_RCW1, RCW1_RX);
458*85ae89f4SRuslan Bukin 
459*85ae89f4SRuslan Bukin 	/*
460*85ae89f4SRuslan Bukin 	 * Call mii_mediachg() which will call back into xae_miibus_statchg()
461*85ae89f4SRuslan Bukin 	 * to set up the remaining config registers based on current media.
462*85ae89f4SRuslan Bukin 	 */
463*85ae89f4SRuslan Bukin 	mii_mediachg(sc->mii_softc);
464*85ae89f4SRuslan Bukin 	callout_reset(&sc->xae_callout, hz, xae_tick, sc);
465*85ae89f4SRuslan Bukin }
466*85ae89f4SRuslan Bukin 
467*85ae89f4SRuslan Bukin static void
468*85ae89f4SRuslan Bukin xae_init(void *arg)
469*85ae89f4SRuslan Bukin {
470*85ae89f4SRuslan Bukin 	struct xae_softc *sc;
471*85ae89f4SRuslan Bukin 
472*85ae89f4SRuslan Bukin 	sc = arg;
473*85ae89f4SRuslan Bukin 
474*85ae89f4SRuslan Bukin 	XAE_LOCK(sc);
475*85ae89f4SRuslan Bukin 	xae_init_locked(sc);
476*85ae89f4SRuslan Bukin 	XAE_UNLOCK(sc);
477*85ae89f4SRuslan Bukin }
478*85ae89f4SRuslan Bukin 
479*85ae89f4SRuslan Bukin static void
480*85ae89f4SRuslan Bukin xae_media_status(struct ifnet * ifp, struct ifmediareq *ifmr)
481*85ae89f4SRuslan Bukin {
482*85ae89f4SRuslan Bukin 	struct xae_softc *sc;
483*85ae89f4SRuslan Bukin 	struct mii_data *mii;
484*85ae89f4SRuslan Bukin 
485*85ae89f4SRuslan Bukin 	sc = ifp->if_softc;
486*85ae89f4SRuslan Bukin 	mii = sc->mii_softc;
487*85ae89f4SRuslan Bukin 
488*85ae89f4SRuslan Bukin 	XAE_LOCK(sc);
489*85ae89f4SRuslan Bukin 	mii_pollstat(mii);
490*85ae89f4SRuslan Bukin 	ifmr->ifm_active = mii->mii_media_active;
491*85ae89f4SRuslan Bukin 	ifmr->ifm_status = mii->mii_media_status;
492*85ae89f4SRuslan Bukin 	XAE_UNLOCK(sc);
493*85ae89f4SRuslan Bukin }
494*85ae89f4SRuslan Bukin 
495*85ae89f4SRuslan Bukin static int
496*85ae89f4SRuslan Bukin xae_media_change_locked(struct xae_softc *sc)
497*85ae89f4SRuslan Bukin {
498*85ae89f4SRuslan Bukin 
499*85ae89f4SRuslan Bukin 	return (mii_mediachg(sc->mii_softc));
500*85ae89f4SRuslan Bukin }
501*85ae89f4SRuslan Bukin 
502*85ae89f4SRuslan Bukin static int
503*85ae89f4SRuslan Bukin xae_media_change(struct ifnet * ifp)
504*85ae89f4SRuslan Bukin {
505*85ae89f4SRuslan Bukin 	struct xae_softc *sc;
506*85ae89f4SRuslan Bukin 	int error;
507*85ae89f4SRuslan Bukin 
508*85ae89f4SRuslan Bukin 	sc = ifp->if_softc;
509*85ae89f4SRuslan Bukin 
510*85ae89f4SRuslan Bukin 	XAE_LOCK(sc);
511*85ae89f4SRuslan Bukin 	error = xae_media_change_locked(sc);
512*85ae89f4SRuslan Bukin 	XAE_UNLOCK(sc);
513*85ae89f4SRuslan Bukin 
514*85ae89f4SRuslan Bukin 	return (error);
515*85ae89f4SRuslan Bukin }
516*85ae89f4SRuslan Bukin 
517*85ae89f4SRuslan Bukin static void
518*85ae89f4SRuslan Bukin xae_setup_rxfilter(struct xae_softc *sc)
519*85ae89f4SRuslan Bukin {
520*85ae89f4SRuslan Bukin 	struct ifmultiaddr *ifma;
521*85ae89f4SRuslan Bukin 	struct ifnet *ifp;
522*85ae89f4SRuslan Bukin 	uint32_t reg;
523*85ae89f4SRuslan Bukin 	uint8_t *ma;
524*85ae89f4SRuslan Bukin 	int i;
525*85ae89f4SRuslan Bukin 
526*85ae89f4SRuslan Bukin 	XAE_ASSERT_LOCKED(sc);
527*85ae89f4SRuslan Bukin 
528*85ae89f4SRuslan Bukin 	ifp = sc->ifp;
529*85ae89f4SRuslan Bukin 
530*85ae89f4SRuslan Bukin 	/*
531*85ae89f4SRuslan Bukin 	 * Set the multicast (group) filter hash.
532*85ae89f4SRuslan Bukin 	 */
533*85ae89f4SRuslan Bukin 	if ((ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) != 0) {
534*85ae89f4SRuslan Bukin 		reg = READ4(sc, XAE_FFC);
535*85ae89f4SRuslan Bukin 		reg |= FFC_PM;
536*85ae89f4SRuslan Bukin 		WRITE4(sc, XAE_FFC, reg);
537*85ae89f4SRuslan Bukin 	} else {
538*85ae89f4SRuslan Bukin 		reg = READ4(sc, XAE_FFC);
539*85ae89f4SRuslan Bukin 		reg &= ~FFC_PM;
540*85ae89f4SRuslan Bukin 		WRITE4(sc, XAE_FFC, reg);
541*85ae89f4SRuslan Bukin 
542*85ae89f4SRuslan Bukin 		if_maddr_rlock(ifp);
543*85ae89f4SRuslan Bukin 
544*85ae89f4SRuslan Bukin 		i = 0;
545*85ae89f4SRuslan Bukin 		CK_STAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) {
546*85ae89f4SRuslan Bukin 			if (ifma->ifma_addr->sa_family != AF_LINK)
547*85ae89f4SRuslan Bukin 				continue;
548*85ae89f4SRuslan Bukin 
549*85ae89f4SRuslan Bukin 			if (i >= XAE_MULTICAST_TABLE_SIZE)
550*85ae89f4SRuslan Bukin 				break;
551*85ae89f4SRuslan Bukin 
552*85ae89f4SRuslan Bukin 			ma = LLADDR((struct sockaddr_dl *)ifma->ifma_addr);
553*85ae89f4SRuslan Bukin 
554*85ae89f4SRuslan Bukin 			reg = READ4(sc, XAE_FFC) & 0xffffff00;
555*85ae89f4SRuslan Bukin 			reg |= i++;
556*85ae89f4SRuslan Bukin 			WRITE4(sc, XAE_FFC, reg);
557*85ae89f4SRuslan Bukin 
558*85ae89f4SRuslan Bukin 			reg = (ma[0]);
559*85ae89f4SRuslan Bukin 			reg |= (ma[1] << 8);
560*85ae89f4SRuslan Bukin 			reg |= (ma[2] << 16);
561*85ae89f4SRuslan Bukin 			reg |= (ma[3] << 24);
562*85ae89f4SRuslan Bukin 			WRITE4(sc, XAE_FFV(0), reg);
563*85ae89f4SRuslan Bukin 
564*85ae89f4SRuslan Bukin 			reg = ma[4];
565*85ae89f4SRuslan Bukin 			reg |= ma[5] << 8;
566*85ae89f4SRuslan Bukin 			WRITE4(sc, XAE_FFV(1), reg);
567*85ae89f4SRuslan Bukin 		}
568*85ae89f4SRuslan Bukin 		if_maddr_runlock(ifp);
569*85ae89f4SRuslan Bukin 	}
570*85ae89f4SRuslan Bukin 
571*85ae89f4SRuslan Bukin 	/*
572*85ae89f4SRuslan Bukin 	 * Set the primary address.
573*85ae89f4SRuslan Bukin 	 */
574*85ae89f4SRuslan Bukin 	reg = sc->macaddr[0];
575*85ae89f4SRuslan Bukin 	reg |= (sc->macaddr[1] << 8);
576*85ae89f4SRuslan Bukin 	reg |= (sc->macaddr[2] << 16);
577*85ae89f4SRuslan Bukin 	reg |= (sc->macaddr[3] << 24);
578*85ae89f4SRuslan Bukin 	WRITE4(sc, XAE_UAW0, reg);
579*85ae89f4SRuslan Bukin 
580*85ae89f4SRuslan Bukin 	reg = sc->macaddr[4];
581*85ae89f4SRuslan Bukin 	reg |= (sc->macaddr[5] << 8);
582*85ae89f4SRuslan Bukin 	WRITE4(sc, XAE_UAW1, reg);
583*85ae89f4SRuslan Bukin }
584*85ae89f4SRuslan Bukin 
585*85ae89f4SRuslan Bukin static int
586*85ae89f4SRuslan Bukin xae_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
587*85ae89f4SRuslan Bukin {
588*85ae89f4SRuslan Bukin 	struct xae_softc *sc;
589*85ae89f4SRuslan Bukin 	struct mii_data *mii;
590*85ae89f4SRuslan Bukin 	struct ifreq *ifr;
591*85ae89f4SRuslan Bukin 	int mask, error;
592*85ae89f4SRuslan Bukin 
593*85ae89f4SRuslan Bukin 	sc = ifp->if_softc;
594*85ae89f4SRuslan Bukin 	ifr = (struct ifreq *)data;
595*85ae89f4SRuslan Bukin 
596*85ae89f4SRuslan Bukin 	error = 0;
597*85ae89f4SRuslan Bukin 	switch (cmd) {
598*85ae89f4SRuslan Bukin 	case SIOCSIFFLAGS:
599*85ae89f4SRuslan Bukin 		XAE_LOCK(sc);
600*85ae89f4SRuslan Bukin 		if (ifp->if_flags & IFF_UP) {
601*85ae89f4SRuslan Bukin 			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
602*85ae89f4SRuslan Bukin 				if ((ifp->if_flags ^ sc->if_flags) &
603*85ae89f4SRuslan Bukin 				    (IFF_PROMISC | IFF_ALLMULTI))
604*85ae89f4SRuslan Bukin 					xae_setup_rxfilter(sc);
605*85ae89f4SRuslan Bukin 			} else {
606*85ae89f4SRuslan Bukin 				if (!sc->is_detaching)
607*85ae89f4SRuslan Bukin 					xae_init_locked(sc);
608*85ae89f4SRuslan Bukin 			}
609*85ae89f4SRuslan Bukin 		} else {
610*85ae89f4SRuslan Bukin 			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
611*85ae89f4SRuslan Bukin 				xae_stop_locked(sc);
612*85ae89f4SRuslan Bukin 		}
613*85ae89f4SRuslan Bukin 		sc->if_flags = ifp->if_flags;
614*85ae89f4SRuslan Bukin 		XAE_UNLOCK(sc);
615*85ae89f4SRuslan Bukin 		break;
616*85ae89f4SRuslan Bukin 	case SIOCADDMULTI:
617*85ae89f4SRuslan Bukin 	case SIOCDELMULTI:
618*85ae89f4SRuslan Bukin 		if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
619*85ae89f4SRuslan Bukin 			XAE_LOCK(sc);
620*85ae89f4SRuslan Bukin 			xae_setup_rxfilter(sc);
621*85ae89f4SRuslan Bukin 			XAE_UNLOCK(sc);
622*85ae89f4SRuslan Bukin 		}
623*85ae89f4SRuslan Bukin 		break;
624*85ae89f4SRuslan Bukin 	case SIOCSIFMEDIA:
625*85ae89f4SRuslan Bukin 	case SIOCGIFMEDIA:
626*85ae89f4SRuslan Bukin 		mii = sc->mii_softc;
627*85ae89f4SRuslan Bukin 		error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
628*85ae89f4SRuslan Bukin 		break;
629*85ae89f4SRuslan Bukin 	case SIOCSIFCAP:
630*85ae89f4SRuslan Bukin 		mask = ifp->if_capenable ^ ifr->ifr_reqcap;
631*85ae89f4SRuslan Bukin 		if (mask & IFCAP_VLAN_MTU) {
632*85ae89f4SRuslan Bukin 			/* No work to do except acknowledge the change took */
633*85ae89f4SRuslan Bukin 			ifp->if_capenable ^= IFCAP_VLAN_MTU;
634*85ae89f4SRuslan Bukin 		}
635*85ae89f4SRuslan Bukin 		break;
636*85ae89f4SRuslan Bukin 
637*85ae89f4SRuslan Bukin 	default:
638*85ae89f4SRuslan Bukin 		error = ether_ioctl(ifp, cmd, data);
639*85ae89f4SRuslan Bukin 		break;
640*85ae89f4SRuslan Bukin 	}
641*85ae89f4SRuslan Bukin 
642*85ae89f4SRuslan Bukin 	return (error);
643*85ae89f4SRuslan Bukin }
644*85ae89f4SRuslan Bukin 
645*85ae89f4SRuslan Bukin static void
646*85ae89f4SRuslan Bukin xae_intr(void *arg)
647*85ae89f4SRuslan Bukin {
648*85ae89f4SRuslan Bukin 
649*85ae89f4SRuslan Bukin }
650*85ae89f4SRuslan Bukin 
651*85ae89f4SRuslan Bukin static int
652*85ae89f4SRuslan Bukin xae_get_hwaddr(struct xae_softc *sc, uint8_t *hwaddr)
653*85ae89f4SRuslan Bukin {
654*85ae89f4SRuslan Bukin 	phandle_t node;
655*85ae89f4SRuslan Bukin 	int len;
656*85ae89f4SRuslan Bukin 
657*85ae89f4SRuslan Bukin 	node = ofw_bus_get_node(sc->dev);
658*85ae89f4SRuslan Bukin 
659*85ae89f4SRuslan Bukin 	/* Check if there is property */
660*85ae89f4SRuslan Bukin 	if ((len = OF_getproplen(node, "local-mac-address")) <= 0)
661*85ae89f4SRuslan Bukin 		return (EINVAL);
662*85ae89f4SRuslan Bukin 
663*85ae89f4SRuslan Bukin 	if (len != ETHER_ADDR_LEN)
664*85ae89f4SRuslan Bukin 		return (EINVAL);
665*85ae89f4SRuslan Bukin 
666*85ae89f4SRuslan Bukin 	OF_getprop(node, "local-mac-address", hwaddr,
667*85ae89f4SRuslan Bukin 	    ETHER_ADDR_LEN);
668*85ae89f4SRuslan Bukin 
669*85ae89f4SRuslan Bukin 	return (0);
670*85ae89f4SRuslan Bukin }
671*85ae89f4SRuslan Bukin 
672*85ae89f4SRuslan Bukin static int
673*85ae89f4SRuslan Bukin mdio_wait(struct xae_softc *sc)
674*85ae89f4SRuslan Bukin {
675*85ae89f4SRuslan Bukin 	uint32_t reg;
676*85ae89f4SRuslan Bukin 	int timeout;
677*85ae89f4SRuslan Bukin 
678*85ae89f4SRuslan Bukin 	timeout = 200;
679*85ae89f4SRuslan Bukin 
680*85ae89f4SRuslan Bukin 	do {
681*85ae89f4SRuslan Bukin 		reg = READ4(sc, XAE_MDIO_CTRL);
682*85ae89f4SRuslan Bukin 		if (reg & MDIO_CTRL_READY)
683*85ae89f4SRuslan Bukin 			break;
684*85ae89f4SRuslan Bukin 		DELAY(1);
685*85ae89f4SRuslan Bukin 	} while (timeout--);
686*85ae89f4SRuslan Bukin 
687*85ae89f4SRuslan Bukin 	if (timeout <= 0) {
688*85ae89f4SRuslan Bukin 		printf("Failed to get MDIO ready\n");
689*85ae89f4SRuslan Bukin 		return (1);
690*85ae89f4SRuslan Bukin 	}
691*85ae89f4SRuslan Bukin 
692*85ae89f4SRuslan Bukin 	return (0);
693*85ae89f4SRuslan Bukin }
694*85ae89f4SRuslan Bukin 
695*85ae89f4SRuslan Bukin static int
696*85ae89f4SRuslan Bukin xae_miibus_read_reg(device_t dev, int phy, int reg)
697*85ae89f4SRuslan Bukin {
698*85ae89f4SRuslan Bukin 	struct xae_softc *sc;
699*85ae89f4SRuslan Bukin 	uint32_t mii;
700*85ae89f4SRuslan Bukin 	int rv;
701*85ae89f4SRuslan Bukin 
702*85ae89f4SRuslan Bukin 	sc = device_get_softc(dev);
703*85ae89f4SRuslan Bukin 
704*85ae89f4SRuslan Bukin 	if (mdio_wait(sc))
705*85ae89f4SRuslan Bukin 		return (0);
706*85ae89f4SRuslan Bukin 
707*85ae89f4SRuslan Bukin 	mii = MDIO_CTRL_TX_OP_READ | MDIO_CTRL_INITIATE;
708*85ae89f4SRuslan Bukin 	mii |= (reg << MDIO_TX_REGAD_S);
709*85ae89f4SRuslan Bukin 	mii |= (phy << MDIO_TX_PHYAD_S);
710*85ae89f4SRuslan Bukin 
711*85ae89f4SRuslan Bukin 	WRITE4(sc, XAE_MDIO_CTRL, mii);
712*85ae89f4SRuslan Bukin 
713*85ae89f4SRuslan Bukin 	if (mdio_wait(sc))
714*85ae89f4SRuslan Bukin 		return (0);
715*85ae89f4SRuslan Bukin 
716*85ae89f4SRuslan Bukin 	rv = READ4(sc, XAE_MDIO_READ);
717*85ae89f4SRuslan Bukin 
718*85ae89f4SRuslan Bukin 	return (rv);
719*85ae89f4SRuslan Bukin }
720*85ae89f4SRuslan Bukin 
721*85ae89f4SRuslan Bukin static int
722*85ae89f4SRuslan Bukin xae_miibus_write_reg(device_t dev, int phy, int reg, int val)
723*85ae89f4SRuslan Bukin {
724*85ae89f4SRuslan Bukin 	struct xae_softc *sc;
725*85ae89f4SRuslan Bukin 	uint32_t mii;
726*85ae89f4SRuslan Bukin 
727*85ae89f4SRuslan Bukin 	sc = device_get_softc(dev);
728*85ae89f4SRuslan Bukin 
729*85ae89f4SRuslan Bukin 	if (mdio_wait(sc))
730*85ae89f4SRuslan Bukin 		return (1);
731*85ae89f4SRuslan Bukin 
732*85ae89f4SRuslan Bukin 	mii = MDIO_CTRL_TX_OP_WRITE | MDIO_CTRL_INITIATE;
733*85ae89f4SRuslan Bukin 	mii |= (reg << MDIO_TX_REGAD_S);
734*85ae89f4SRuslan Bukin 	mii |= (phy << MDIO_TX_PHYAD_S);
735*85ae89f4SRuslan Bukin 
736*85ae89f4SRuslan Bukin 	WRITE4(sc, XAE_MDIO_WRITE, val);
737*85ae89f4SRuslan Bukin 	WRITE4(sc, XAE_MDIO_CTRL, mii);
738*85ae89f4SRuslan Bukin 
739*85ae89f4SRuslan Bukin 	if (mdio_wait(sc))
740*85ae89f4SRuslan Bukin 		return (1);
741*85ae89f4SRuslan Bukin 
742*85ae89f4SRuslan Bukin 	return (0);
743*85ae89f4SRuslan Bukin }
744*85ae89f4SRuslan Bukin 
745*85ae89f4SRuslan Bukin static void
746*85ae89f4SRuslan Bukin xae_phy_fixup(struct xae_softc *sc)
747*85ae89f4SRuslan Bukin {
748*85ae89f4SRuslan Bukin 	uint32_t reg;
749*85ae89f4SRuslan Bukin 	device_t dev;
750*85ae89f4SRuslan Bukin 
751*85ae89f4SRuslan Bukin 	dev = sc->dev;
752*85ae89f4SRuslan Bukin 
753*85ae89f4SRuslan Bukin 	do {
754*85ae89f4SRuslan Bukin 		WRITE_TI_EREG(sc, DP83867_SGMIICTL1, SGMIICTL1_SGMII_6W);
755*85ae89f4SRuslan Bukin 		PHY_WR(sc, DP83867_PHYCR, PHYCR_SGMII_EN);
756*85ae89f4SRuslan Bukin 
757*85ae89f4SRuslan Bukin 		reg = PHY_RD(sc, DP83867_CFG2);
758*85ae89f4SRuslan Bukin 		reg &= ~CFG2_SPEED_OPT_ATTEMPT_CNT_M;
759*85ae89f4SRuslan Bukin 		reg |= (CFG2_SPEED_OPT_ATTEMPT_CNT_4);
760*85ae89f4SRuslan Bukin 		reg |= CFG2_INTERRUPT_POLARITY;
761*85ae89f4SRuslan Bukin 		reg |= CFG2_SPEED_OPT_ENHANCED_EN;
762*85ae89f4SRuslan Bukin 		reg |= CFG2_SPEED_OPT_10M_EN;
763*85ae89f4SRuslan Bukin 		PHY_WR(sc, DP83867_CFG2, reg);
764*85ae89f4SRuslan Bukin 
765*85ae89f4SRuslan Bukin 		WRITE_TI_EREG(sc, DP83867_CFG4, CFG4_SGMII_TMR);
766*85ae89f4SRuslan Bukin 		PHY_WR(sc, MII_BMCR,
767*85ae89f4SRuslan Bukin 		    BMCR_AUTOEN | BMCR_FDX | BMCR_SPEED1 | BMCR_RESET);
768*85ae89f4SRuslan Bukin 	} while (PHY1_RD(sc, MII_BMCR) == 0x0ffff);
769*85ae89f4SRuslan Bukin 
770*85ae89f4SRuslan Bukin 	do {
771*85ae89f4SRuslan Bukin 		PHY1_WR(sc, MII_BMCR,
772*85ae89f4SRuslan Bukin 		    BMCR_AUTOEN | BMCR_FDX | BMCR_SPEED1 | BMCR_STARTNEG);
773*85ae89f4SRuslan Bukin 		DELAY(40000);
774*85ae89f4SRuslan Bukin 	} while ((PHY1_RD(sc, MII_BMSR) & BMSR_ACOMP) == 0);
775*85ae89f4SRuslan Bukin }
776*85ae89f4SRuslan Bukin 
777*85ae89f4SRuslan Bukin static int
778*85ae89f4SRuslan Bukin setup_xdma(struct xae_softc *sc)
779*85ae89f4SRuslan Bukin {
780*85ae89f4SRuslan Bukin 	device_t dev;
781*85ae89f4SRuslan Bukin 	vmem_t *vmem;
782*85ae89f4SRuslan Bukin 	int error;
783*85ae89f4SRuslan Bukin 
784*85ae89f4SRuslan Bukin 	dev = sc->dev;
785*85ae89f4SRuslan Bukin 
786*85ae89f4SRuslan Bukin 	/* Get xDMA controller */
787*85ae89f4SRuslan Bukin 	sc->xdma_tx = xdma_ofw_get(sc->dev, "tx");
788*85ae89f4SRuslan Bukin 	if (sc->xdma_tx == NULL) {
789*85ae89f4SRuslan Bukin 		device_printf(dev, "Could not find DMA controller.\n");
790*85ae89f4SRuslan Bukin 		return (ENXIO);
791*85ae89f4SRuslan Bukin 	}
792*85ae89f4SRuslan Bukin 
793*85ae89f4SRuslan Bukin 	sc->xdma_rx = xdma_ofw_get(sc->dev, "rx");
794*85ae89f4SRuslan Bukin 	if (sc->xdma_rx == NULL) {
795*85ae89f4SRuslan Bukin 		device_printf(dev, "Could not find DMA controller.\n");
796*85ae89f4SRuslan Bukin 		return (ENXIO);
797*85ae89f4SRuslan Bukin 	}
798*85ae89f4SRuslan Bukin 
799*85ae89f4SRuslan Bukin 	/* Alloc xDMA TX virtual channel. */
800*85ae89f4SRuslan Bukin 	sc->xchan_tx = xdma_channel_alloc(sc->xdma_tx, 0);
801*85ae89f4SRuslan Bukin 	if (sc->xchan_tx == NULL) {
802*85ae89f4SRuslan Bukin 		device_printf(dev, "Can't alloc virtual DMA TX channel.\n");
803*85ae89f4SRuslan Bukin 		return (ENXIO);
804*85ae89f4SRuslan Bukin 	}
805*85ae89f4SRuslan Bukin 
806*85ae89f4SRuslan Bukin 	/* Setup interrupt handler. */
807*85ae89f4SRuslan Bukin 	error = xdma_setup_intr(sc->xchan_tx,
808*85ae89f4SRuslan Bukin 	    xae_xdma_tx_intr, sc, &sc->ih_tx);
809*85ae89f4SRuslan Bukin 	if (error) {
810*85ae89f4SRuslan Bukin 		device_printf(sc->dev,
811*85ae89f4SRuslan Bukin 		    "Can't setup xDMA TX interrupt handler.\n");
812*85ae89f4SRuslan Bukin 		return (ENXIO);
813*85ae89f4SRuslan Bukin 	}
814*85ae89f4SRuslan Bukin 
815*85ae89f4SRuslan Bukin 	/* Alloc xDMA RX virtual channel. */
816*85ae89f4SRuslan Bukin 	sc->xchan_rx = xdma_channel_alloc(sc->xdma_rx, 0);
817*85ae89f4SRuslan Bukin 	if (sc->xchan_rx == NULL) {
818*85ae89f4SRuslan Bukin 		device_printf(dev, "Can't alloc virtual DMA RX channel.\n");
819*85ae89f4SRuslan Bukin 		return (ENXIO);
820*85ae89f4SRuslan Bukin 	}
821*85ae89f4SRuslan Bukin 
822*85ae89f4SRuslan Bukin 	/* Setup interrupt handler. */
823*85ae89f4SRuslan Bukin 	error = xdma_setup_intr(sc->xchan_rx,
824*85ae89f4SRuslan Bukin 	    xae_xdma_rx_intr, sc, &sc->ih_rx);
825*85ae89f4SRuslan Bukin 	if (error) {
826*85ae89f4SRuslan Bukin 		device_printf(sc->dev,
827*85ae89f4SRuslan Bukin 		    "Can't setup xDMA RX interrupt handler.\n");
828*85ae89f4SRuslan Bukin 		return (ENXIO);
829*85ae89f4SRuslan Bukin 	}
830*85ae89f4SRuslan Bukin 
831*85ae89f4SRuslan Bukin 	/* Setup bounce buffer */
832*85ae89f4SRuslan Bukin 	vmem = xdma_get_memory(dev);
833*85ae89f4SRuslan Bukin 	if (vmem) {
834*85ae89f4SRuslan Bukin 		xchan_set_memory(sc->xchan_tx, vmem);
835*85ae89f4SRuslan Bukin 		xchan_set_memory(sc->xchan_rx, vmem);
836*85ae89f4SRuslan Bukin 	}
837*85ae89f4SRuslan Bukin 
838*85ae89f4SRuslan Bukin 	xdma_prep_sg(sc->xchan_tx,
839*85ae89f4SRuslan Bukin 	    TX_QUEUE_SIZE,	/* xchan requests queue size */
840*85ae89f4SRuslan Bukin 	    MCLBYTES,	/* maxsegsize */
841*85ae89f4SRuslan Bukin 	    8,		/* maxnsegs */
842*85ae89f4SRuslan Bukin 	    16,		/* alignment */
843*85ae89f4SRuslan Bukin 	    0,		/* boundary */
844*85ae89f4SRuslan Bukin 	    BUS_SPACE_MAXADDR_32BIT,
845*85ae89f4SRuslan Bukin 	    BUS_SPACE_MAXADDR);
846*85ae89f4SRuslan Bukin 
847*85ae89f4SRuslan Bukin 	xdma_prep_sg(sc->xchan_rx,
848*85ae89f4SRuslan Bukin 	    RX_QUEUE_SIZE,	/* xchan requests queue size */
849*85ae89f4SRuslan Bukin 	    MCLBYTES,	/* maxsegsize */
850*85ae89f4SRuslan Bukin 	    1,		/* maxnsegs */
851*85ae89f4SRuslan Bukin 	    16,		/* alignment */
852*85ae89f4SRuslan Bukin 	    0,		/* boundary */
853*85ae89f4SRuslan Bukin 	    BUS_SPACE_MAXADDR_32BIT,
854*85ae89f4SRuslan Bukin 	    BUS_SPACE_MAXADDR);
855*85ae89f4SRuslan Bukin 
856*85ae89f4SRuslan Bukin 	return (0);
857*85ae89f4SRuslan Bukin }
858*85ae89f4SRuslan Bukin 
859*85ae89f4SRuslan Bukin static int
860*85ae89f4SRuslan Bukin xae_probe(device_t dev)
861*85ae89f4SRuslan Bukin {
862*85ae89f4SRuslan Bukin 
863*85ae89f4SRuslan Bukin 	if (!ofw_bus_status_okay(dev))
864*85ae89f4SRuslan Bukin 		return (ENXIO);
865*85ae89f4SRuslan Bukin 
866*85ae89f4SRuslan Bukin 	if (!ofw_bus_is_compatible(dev, "xlnx,axi-ethernet-1.00.a"))
867*85ae89f4SRuslan Bukin 		return (ENXIO);
868*85ae89f4SRuslan Bukin 
869*85ae89f4SRuslan Bukin 	device_set_desc(dev, "Xilinx AXI Ethernet");
870*85ae89f4SRuslan Bukin 
871*85ae89f4SRuslan Bukin 	return (BUS_PROBE_DEFAULT);
872*85ae89f4SRuslan Bukin }
873*85ae89f4SRuslan Bukin 
874*85ae89f4SRuslan Bukin static int
875*85ae89f4SRuslan Bukin xae_attach(device_t dev)
876*85ae89f4SRuslan Bukin {
877*85ae89f4SRuslan Bukin 	struct xae_softc *sc;
878*85ae89f4SRuslan Bukin 	struct ifnet *ifp;
879*85ae89f4SRuslan Bukin 	phandle_t node;
880*85ae89f4SRuslan Bukin 	uint32_t reg;
881*85ae89f4SRuslan Bukin 	int error;
882*85ae89f4SRuslan Bukin 
883*85ae89f4SRuslan Bukin 	sc = device_get_softc(dev);
884*85ae89f4SRuslan Bukin 	sc->dev = dev;
885*85ae89f4SRuslan Bukin 	node = ofw_bus_get_node(dev);
886*85ae89f4SRuslan Bukin 
887*85ae89f4SRuslan Bukin 	if (setup_xdma(sc) != 0) {
888*85ae89f4SRuslan Bukin 		device_printf(dev, "Could not setup xDMA.\n");
889*85ae89f4SRuslan Bukin 		return (ENXIO);
890*85ae89f4SRuslan Bukin 	}
891*85ae89f4SRuslan Bukin 
892*85ae89f4SRuslan Bukin 	mtx_init(&sc->mtx, device_get_nameunit(sc->dev),
893*85ae89f4SRuslan Bukin 	    MTX_NETWORK_LOCK, MTX_DEF);
894*85ae89f4SRuslan Bukin 
895*85ae89f4SRuslan Bukin 	sc->br = buf_ring_alloc(BUFRING_SIZE, M_DEVBUF,
896*85ae89f4SRuslan Bukin 	    M_NOWAIT, &sc->mtx);
897*85ae89f4SRuslan Bukin 	if (sc->br == NULL)
898*85ae89f4SRuslan Bukin 		return (ENOMEM);
899*85ae89f4SRuslan Bukin 
900*85ae89f4SRuslan Bukin 	if (bus_alloc_resources(dev, xae_spec, sc->res)) {
901*85ae89f4SRuslan Bukin 		device_printf(dev, "could not allocate resources\n");
902*85ae89f4SRuslan Bukin 		return (ENXIO);
903*85ae89f4SRuslan Bukin 	}
904*85ae89f4SRuslan Bukin 
905*85ae89f4SRuslan Bukin 	/* Memory interface */
906*85ae89f4SRuslan Bukin 	sc->bst = rman_get_bustag(sc->res[0]);
907*85ae89f4SRuslan Bukin 	sc->bsh = rman_get_bushandle(sc->res[0]);
908*85ae89f4SRuslan Bukin 
909*85ae89f4SRuslan Bukin 	device_printf(sc->dev, "Identification: %x\n",
910*85ae89f4SRuslan Bukin 	    READ4(sc, XAE_IDENT));
911*85ae89f4SRuslan Bukin 
912*85ae89f4SRuslan Bukin 	/* Get MAC addr */
913*85ae89f4SRuslan Bukin 	if (xae_get_hwaddr(sc, sc->macaddr)) {
914*85ae89f4SRuslan Bukin 		device_printf(sc->dev, "can't get mac\n");
915*85ae89f4SRuslan Bukin 		return (ENXIO);
916*85ae89f4SRuslan Bukin 	}
917*85ae89f4SRuslan Bukin 
918*85ae89f4SRuslan Bukin 	/* Enable MII clock */
919*85ae89f4SRuslan Bukin 	reg = (MDIO_CLK_DIV_DEFAULT << MDIO_SETUP_CLK_DIV_S);
920*85ae89f4SRuslan Bukin 	reg |= MDIO_SETUP_ENABLE;
921*85ae89f4SRuslan Bukin 	WRITE4(sc, XAE_MDIO_SETUP, reg);
922*85ae89f4SRuslan Bukin 	if (mdio_wait(sc))
923*85ae89f4SRuslan Bukin 		return (ENXIO);
924*85ae89f4SRuslan Bukin 
925*85ae89f4SRuslan Bukin 	callout_init_mtx(&sc->xae_callout, &sc->mtx, 0);
926*85ae89f4SRuslan Bukin 
927*85ae89f4SRuslan Bukin 	/* Setup interrupt handler. */
928*85ae89f4SRuslan Bukin 	error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_NET | INTR_MPSAFE,
929*85ae89f4SRuslan Bukin 	    NULL, xae_intr, sc, &sc->intr_cookie);
930*85ae89f4SRuslan Bukin 	if (error != 0) {
931*85ae89f4SRuslan Bukin 		device_printf(dev, "could not setup interrupt handler.\n");
932*85ae89f4SRuslan Bukin 		return (ENXIO);
933*85ae89f4SRuslan Bukin 	}
934*85ae89f4SRuslan Bukin 
935*85ae89f4SRuslan Bukin 	/* Set up the ethernet interface. */
936*85ae89f4SRuslan Bukin 	sc->ifp = ifp = if_alloc(IFT_ETHER);
937*85ae89f4SRuslan Bukin 	if (ifp == NULL) {
938*85ae89f4SRuslan Bukin 		device_printf(dev, "could not allocate ifp.\n");
939*85ae89f4SRuslan Bukin 		return (ENXIO);
940*85ae89f4SRuslan Bukin 	}
941*85ae89f4SRuslan Bukin 
942*85ae89f4SRuslan Bukin 	ifp->if_softc = sc;
943*85ae89f4SRuslan Bukin 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
944*85ae89f4SRuslan Bukin 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
945*85ae89f4SRuslan Bukin 	ifp->if_capabilities = IFCAP_VLAN_MTU;
946*85ae89f4SRuslan Bukin 	ifp->if_capenable = ifp->if_capabilities;
947*85ae89f4SRuslan Bukin 	ifp->if_transmit = xae_transmit;
948*85ae89f4SRuslan Bukin 	ifp->if_qflush = xae_qflush;
949*85ae89f4SRuslan Bukin 	ifp->if_ioctl = xae_ioctl;
950*85ae89f4SRuslan Bukin 	ifp->if_init = xae_init;
951*85ae89f4SRuslan Bukin 	IFQ_SET_MAXLEN(&ifp->if_snd, TX_DESC_COUNT - 1);
952*85ae89f4SRuslan Bukin 	ifp->if_snd.ifq_drv_maxlen = TX_DESC_COUNT - 1;
953*85ae89f4SRuslan Bukin 	IFQ_SET_READY(&ifp->if_snd);
954*85ae89f4SRuslan Bukin 
955*85ae89f4SRuslan Bukin 	if (xae_get_phyaddr(node, &sc->phy_addr) != 0)
956*85ae89f4SRuslan Bukin 		return (ENXIO);
957*85ae89f4SRuslan Bukin 
958*85ae89f4SRuslan Bukin 	/* Attach the mii driver. */
959*85ae89f4SRuslan Bukin 	error = mii_attach(dev, &sc->miibus, ifp, xae_media_change,
960*85ae89f4SRuslan Bukin 	    xae_media_status, BMSR_DEFCAPMASK, sc->phy_addr,
961*85ae89f4SRuslan Bukin 	    MII_OFFSET_ANY, 0);
962*85ae89f4SRuslan Bukin 
963*85ae89f4SRuslan Bukin 	if (error != 0) {
964*85ae89f4SRuslan Bukin 		device_printf(dev, "PHY attach failed\n");
965*85ae89f4SRuslan Bukin 		return (ENXIO);
966*85ae89f4SRuslan Bukin 	}
967*85ae89f4SRuslan Bukin 	sc->mii_softc = device_get_softc(sc->miibus);
968*85ae89f4SRuslan Bukin 
969*85ae89f4SRuslan Bukin 	/* Apply vcu118 workaround. */
970*85ae89f4SRuslan Bukin 	if (OF_getproplen(node, "xlnx,vcu118") >= 0)
971*85ae89f4SRuslan Bukin 		xae_phy_fixup(sc);
972*85ae89f4SRuslan Bukin 
973*85ae89f4SRuslan Bukin 	/* All ready to run, attach the ethernet interface. */
974*85ae89f4SRuslan Bukin 	ether_ifattach(ifp, sc->macaddr);
975*85ae89f4SRuslan Bukin 	sc->is_attached = true;
976*85ae89f4SRuslan Bukin 
977*85ae89f4SRuslan Bukin 	xae_rx_enqueue(sc, NUM_RX_MBUF);
978*85ae89f4SRuslan Bukin 	xdma_queue_submit(sc->xchan_rx);
979*85ae89f4SRuslan Bukin 
980*85ae89f4SRuslan Bukin 	return (0);
981*85ae89f4SRuslan Bukin }
982*85ae89f4SRuslan Bukin 
983*85ae89f4SRuslan Bukin static int
984*85ae89f4SRuslan Bukin xae_detach(device_t dev)
985*85ae89f4SRuslan Bukin {
986*85ae89f4SRuslan Bukin 	struct xae_softc *sc;
987*85ae89f4SRuslan Bukin 	struct ifnet *ifp;
988*85ae89f4SRuslan Bukin 
989*85ae89f4SRuslan Bukin 	sc = device_get_softc(dev);
990*85ae89f4SRuslan Bukin 
991*85ae89f4SRuslan Bukin 	KASSERT(mtx_initialized(&sc->mtx), ("%s: mutex not initialized",
992*85ae89f4SRuslan Bukin 	    device_get_nameunit(dev)));
993*85ae89f4SRuslan Bukin 
994*85ae89f4SRuslan Bukin 	ifp = sc->ifp;
995*85ae89f4SRuslan Bukin 
996*85ae89f4SRuslan Bukin 	/* Only cleanup if attach succeeded. */
997*85ae89f4SRuslan Bukin 	if (device_is_attached(dev)) {
998*85ae89f4SRuslan Bukin 		XAE_LOCK(sc);
999*85ae89f4SRuslan Bukin 		xae_stop_locked(sc);
1000*85ae89f4SRuslan Bukin 		XAE_UNLOCK(sc);
1001*85ae89f4SRuslan Bukin 		callout_drain(&sc->xae_callout);
1002*85ae89f4SRuslan Bukin 		ether_ifdetach(ifp);
1003*85ae89f4SRuslan Bukin 	}
1004*85ae89f4SRuslan Bukin 
1005*85ae89f4SRuslan Bukin 	if (sc->miibus != NULL)
1006*85ae89f4SRuslan Bukin 		device_delete_child(dev, sc->miibus);
1007*85ae89f4SRuslan Bukin 
1008*85ae89f4SRuslan Bukin 	if (ifp != NULL)
1009*85ae89f4SRuslan Bukin 		if_free(ifp);
1010*85ae89f4SRuslan Bukin 
1011*85ae89f4SRuslan Bukin 	mtx_destroy(&sc->mtx);
1012*85ae89f4SRuslan Bukin 
1013*85ae89f4SRuslan Bukin 	bus_teardown_intr(dev, sc->res[1], sc->intr_cookie);
1014*85ae89f4SRuslan Bukin 
1015*85ae89f4SRuslan Bukin 	bus_release_resources(dev, xae_spec, sc->res);
1016*85ae89f4SRuslan Bukin 
1017*85ae89f4SRuslan Bukin 	xdma_channel_free(sc->xchan_tx);
1018*85ae89f4SRuslan Bukin 	xdma_channel_free(sc->xchan_rx);
1019*85ae89f4SRuslan Bukin 	xdma_put(sc->xdma_tx);
1020*85ae89f4SRuslan Bukin 	xdma_put(sc->xdma_rx);
1021*85ae89f4SRuslan Bukin 
1022*85ae89f4SRuslan Bukin 	return (0);
1023*85ae89f4SRuslan Bukin }
1024*85ae89f4SRuslan Bukin 
1025*85ae89f4SRuslan Bukin static void
1026*85ae89f4SRuslan Bukin xae_miibus_statchg(device_t dev)
1027*85ae89f4SRuslan Bukin {
1028*85ae89f4SRuslan Bukin 	struct xae_softc *sc;
1029*85ae89f4SRuslan Bukin 	struct mii_data *mii;
1030*85ae89f4SRuslan Bukin 	uint32_t reg;
1031*85ae89f4SRuslan Bukin 
1032*85ae89f4SRuslan Bukin 	/*
1033*85ae89f4SRuslan Bukin 	 * Called by the MII bus driver when the PHY establishes
1034*85ae89f4SRuslan Bukin 	 * link to set the MAC interface registers.
1035*85ae89f4SRuslan Bukin 	 */
1036*85ae89f4SRuslan Bukin 
1037*85ae89f4SRuslan Bukin 	sc = device_get_softc(dev);
1038*85ae89f4SRuslan Bukin 
1039*85ae89f4SRuslan Bukin 	XAE_ASSERT_LOCKED(sc);
1040*85ae89f4SRuslan Bukin 
1041*85ae89f4SRuslan Bukin 	mii = sc->mii_softc;
1042*85ae89f4SRuslan Bukin 
1043*85ae89f4SRuslan Bukin 	if (mii->mii_media_status & IFM_ACTIVE)
1044*85ae89f4SRuslan Bukin 		sc->link_is_up = true;
1045*85ae89f4SRuslan Bukin 	else
1046*85ae89f4SRuslan Bukin 		sc->link_is_up = false;
1047*85ae89f4SRuslan Bukin 
1048*85ae89f4SRuslan Bukin 	switch (IFM_SUBTYPE(mii->mii_media_active)) {
1049*85ae89f4SRuslan Bukin 	case IFM_1000_T:
1050*85ae89f4SRuslan Bukin 	case IFM_1000_SX:
1051*85ae89f4SRuslan Bukin 		reg = SPEED_1000;
1052*85ae89f4SRuslan Bukin 		break;
1053*85ae89f4SRuslan Bukin 	case IFM_100_TX:
1054*85ae89f4SRuslan Bukin 		reg = SPEED_100;
1055*85ae89f4SRuslan Bukin 		break;
1056*85ae89f4SRuslan Bukin 	case IFM_10_T:
1057*85ae89f4SRuslan Bukin 		reg = SPEED_10;
1058*85ae89f4SRuslan Bukin 		break;
1059*85ae89f4SRuslan Bukin 	case IFM_NONE:
1060*85ae89f4SRuslan Bukin 		sc->link_is_up = false;
1061*85ae89f4SRuslan Bukin 		return;
1062*85ae89f4SRuslan Bukin 	default:
1063*85ae89f4SRuslan Bukin 		sc->link_is_up = false;
1064*85ae89f4SRuslan Bukin 		device_printf(dev, "Unsupported media %u\n",
1065*85ae89f4SRuslan Bukin 		    IFM_SUBTYPE(mii->mii_media_active));
1066*85ae89f4SRuslan Bukin 		return;
1067*85ae89f4SRuslan Bukin 	}
1068*85ae89f4SRuslan Bukin 
1069*85ae89f4SRuslan Bukin 	WRITE4(sc, XAE_SPEED, reg);
1070*85ae89f4SRuslan Bukin }
1071*85ae89f4SRuslan Bukin 
1072*85ae89f4SRuslan Bukin static device_method_t xae_methods[] = {
1073*85ae89f4SRuslan Bukin 	DEVMETHOD(device_probe,		xae_probe),
1074*85ae89f4SRuslan Bukin 	DEVMETHOD(device_attach,	xae_attach),
1075*85ae89f4SRuslan Bukin 	DEVMETHOD(device_detach,	xae_detach),
1076*85ae89f4SRuslan Bukin 
1077*85ae89f4SRuslan Bukin 	/* MII Interface */
1078*85ae89f4SRuslan Bukin 	DEVMETHOD(miibus_readreg,	xae_miibus_read_reg),
1079*85ae89f4SRuslan Bukin 	DEVMETHOD(miibus_writereg,	xae_miibus_write_reg),
1080*85ae89f4SRuslan Bukin 	DEVMETHOD(miibus_statchg,	xae_miibus_statchg),
1081*85ae89f4SRuslan Bukin 
1082*85ae89f4SRuslan Bukin 	{ 0, 0 }
1083*85ae89f4SRuslan Bukin };
1084*85ae89f4SRuslan Bukin 
1085*85ae89f4SRuslan Bukin driver_t xae_driver = {
1086*85ae89f4SRuslan Bukin 	"xae",
1087*85ae89f4SRuslan Bukin 	xae_methods,
1088*85ae89f4SRuslan Bukin 	sizeof(struct xae_softc),
1089*85ae89f4SRuslan Bukin };
1090*85ae89f4SRuslan Bukin 
1091*85ae89f4SRuslan Bukin static devclass_t xae_devclass;
1092*85ae89f4SRuslan Bukin 
1093*85ae89f4SRuslan Bukin DRIVER_MODULE(xae, simplebus, xae_driver, xae_devclass, 0, 0);
1094*85ae89f4SRuslan Bukin DRIVER_MODULE(miibus, xae, miibus_driver, miibus_devclass, 0, 0);
1095*85ae89f4SRuslan Bukin 
1096*85ae89f4SRuslan Bukin MODULE_DEPEND(xae, ether, 1, 1, 1);
1097*85ae89f4SRuslan Bukin MODULE_DEPEND(xae, miibus, 1, 1, 1);
1098