xref: /freebsd-src/sys/dev/eqos/if_eqos.c (revision 4e62c3cafa4c4e41efd6f87b7fe559cf819cf3e4)
1702b53ddSSøren Schmidt /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3702b53ddSSøren Schmidt  *
4702b53ddSSøren Schmidt  * Copyright (c) 2022 Soren Schmidt <sos@deepcore.dk>
5702b53ddSSøren Schmidt  * Copyright (c) 2022 Jared McNeill <jmcneill@invisible.ca>
6702b53ddSSøren Schmidt  * All rights reserved.
7702b53ddSSøren Schmidt  *
8702b53ddSSøren Schmidt  * Redistribution and use in source and binary forms, with or without
9702b53ddSSøren Schmidt  * modification, are permitted provided that the following conditions
10702b53ddSSøren Schmidt  * are met:
11702b53ddSSøren Schmidt  * 1. Redistributions of source code must retain the above copyright
12702b53ddSSøren Schmidt  *    notice, this list of conditions and the following disclaimer.
13702b53ddSSøren Schmidt  * 2. Redistributions in binary form must reproduce the above copyright
14702b53ddSSøren Schmidt  *    notice, this list of conditions and the following disclaimer in the
15702b53ddSSøren Schmidt  *    documentation and/or other materials provided with the distribution.
16702b53ddSSøren Schmidt  *
17702b53ddSSøren Schmidt  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18702b53ddSSøren Schmidt  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19702b53ddSSøren Schmidt  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20702b53ddSSøren Schmidt  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21702b53ddSSøren Schmidt  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22702b53ddSSøren Schmidt  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23702b53ddSSøren Schmidt  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24702b53ddSSøren Schmidt  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25702b53ddSSøren Schmidt  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26702b53ddSSøren Schmidt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27702b53ddSSøren Schmidt  * SUCH DAMAGE.
28702b53ddSSøren Schmidt  *
29702b53ddSSøren Schmidt  * $Id: eqos.c 1059 2022-12-08 19:32:32Z sos $
30702b53ddSSøren Schmidt  */
31702b53ddSSøren Schmidt 
32702b53ddSSøren Schmidt /*
33702b53ddSSøren Schmidt  * DesignWare Ethernet Quality-of-Service controller
34702b53ddSSøren Schmidt  */
35702b53ddSSøren Schmidt 
36702b53ddSSøren Schmidt #include "opt_platform.h"
37fdafd315SWarner Losh 
38702b53ddSSøren Schmidt #include <sys/param.h>
39702b53ddSSøren Schmidt #include <sys/systm.h>
40702b53ddSSøren Schmidt #include <sys/kernel.h>
41702b53ddSSøren Schmidt #include <sys/mutex.h>
42702b53ddSSøren Schmidt #include <sys/rman.h>
43702b53ddSSøren Schmidt #include <sys/endian.h>
44702b53ddSSøren Schmidt #include <sys/module.h>
45702b53ddSSøren Schmidt #include <sys/bus.h>
46702b53ddSSøren Schmidt #include <sys/callout.h>
47702b53ddSSøren Schmidt #include <sys/socket.h>
48702b53ddSSøren Schmidt #include <sys/sockio.h>
49702b53ddSSøren Schmidt #include <sys/mbuf.h>
50702b53ddSSøren Schmidt #include <sys/systm.h>
51702b53ddSSøren Schmidt #include <machine/bus.h>
52702b53ddSSøren Schmidt 
53702b53ddSSøren Schmidt #include <net/ethernet.h>
54702b53ddSSøren Schmidt #include <net/if.h>
55702b53ddSSøren Schmidt #include <net/if_dl.h>
56702b53ddSSøren Schmidt #include <net/if_media.h>
57702b53ddSSøren Schmidt #include <net/if_types.h>
58702b53ddSSøren Schmidt #include <net/bpf.h>
59702b53ddSSøren Schmidt 
60702b53ddSSøren Schmidt #include <dev/mii/mii.h>
61702b53ddSSøren Schmidt #include <dev/mii/miivar.h>
62702b53ddSSøren Schmidt 
63702b53ddSSøren Schmidt #include "miibus_if.h"
64702b53ddSSøren Schmidt #include "if_eqos_if.h"
65702b53ddSSøren Schmidt 
66702b53ddSSøren Schmidt #ifdef FDT
67702b53ddSSøren Schmidt #include <dev/ofw/openfirm.h>
68702b53ddSSøren Schmidt #include <dev/ofw/ofw_bus.h>
69702b53ddSSøren Schmidt #include <dev/ofw/ofw_bus_subr.h>
70be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
71702b53ddSSøren Schmidt #endif
72702b53ddSSøren Schmidt 
73702b53ddSSøren Schmidt #include <dev/eqos/if_eqos_reg.h>
74702b53ddSSøren Schmidt #include <dev/eqos/if_eqos_var.h>
75702b53ddSSøren Schmidt 
76702b53ddSSøren Schmidt #define	DESC_BOUNDARY		(1ULL << 32)
77702b53ddSSøren Schmidt #define	DESC_ALIGN		sizeof(struct eqos_dma_desc)
78702b53ddSSøren Schmidt #define	DESC_OFFSET(n)		((n) * sizeof(struct eqos_dma_desc))
79702b53ddSSøren Schmidt 
80702b53ddSSøren Schmidt #define	TX_DESC_COUNT		EQOS_DMA_DESC_COUNT
81702b53ddSSøren Schmidt #define	TX_DESC_SIZE		(TX_DESC_COUNT * DESC_ALIGN)
82702b53ddSSøren Schmidt #define	TX_MAX_SEGS		(TX_DESC_COUNT / 2)
83702b53ddSSøren Schmidt #define	TX_NEXT(n)		(((n) + 1 ) % TX_DESC_COUNT)
84702b53ddSSøren Schmidt #define	TX_QUEUED(h, t)		((((h) - (t)) + TX_DESC_COUNT) % TX_DESC_COUNT)
85702b53ddSSøren Schmidt 
86702b53ddSSøren Schmidt #define	RX_DESC_COUNT		EQOS_DMA_DESC_COUNT
87702b53ddSSøren Schmidt #define	RX_DESC_SIZE		(RX_DESC_COUNT * DESC_ALIGN)
88702b53ddSSøren Schmidt #define	RX_NEXT(n)		(((n) + 1) % RX_DESC_COUNT)
89702b53ddSSøren Schmidt 
90702b53ddSSøren Schmidt #define	MII_BUSY_RETRY		1000
91702b53ddSSøren Schmidt #define	WATCHDOG_TIMEOUT_SECS	3
92702b53ddSSøren Schmidt 
93702b53ddSSøren Schmidt #define	EQOS_LOCK(sc)		mtx_lock(&(sc)->lock)
94702b53ddSSøren Schmidt #define	EQOS_UNLOCK(sc)		mtx_unlock(&(sc)->lock)
95702b53ddSSøren Schmidt #define	EQOS_ASSERT_LOCKED(sc)	mtx_assert(&(sc)->lock, MA_OWNED)
96702b53ddSSøren Schmidt 
97702b53ddSSøren Schmidt #define	RD4(sc, o)		bus_read_4(sc->res[EQOS_RES_MEM], (o))
98702b53ddSSøren Schmidt #define	WR4(sc, o, v)		bus_write_4(sc->res[EQOS_RES_MEM], (o), (v))
99702b53ddSSøren Schmidt 
100702b53ddSSøren Schmidt 
101702b53ddSSøren Schmidt static struct resource_spec eqos_spec[] = {
102702b53ddSSøren Schmidt 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
103702b53ddSSøren Schmidt 	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
104702b53ddSSøren Schmidt 	{ -1, 0 }
105702b53ddSSøren Schmidt };
106702b53ddSSøren Schmidt 
107702b53ddSSøren Schmidt static void eqos_tick(void *softc);
108702b53ddSSøren Schmidt 
109702b53ddSSøren Schmidt 
110702b53ddSSøren Schmidt static int
111702b53ddSSøren Schmidt eqos_miibus_readreg(device_t dev, int phy, int reg)
112702b53ddSSøren Schmidt {
113702b53ddSSøren Schmidt 	struct eqos_softc *sc = device_get_softc(dev);
114702b53ddSSøren Schmidt 	uint32_t addr;
115702b53ddSSøren Schmidt 	int retry, val;
116702b53ddSSøren Schmidt 
117702b53ddSSøren Schmidt 	addr = sc->csr_clock_range |
118702b53ddSSøren Schmidt 	    (phy << GMAC_MAC_MDIO_ADDRESS_PA_SHIFT) |
119702b53ddSSøren Schmidt 	    (reg << GMAC_MAC_MDIO_ADDRESS_RDA_SHIFT) |
120702b53ddSSøren Schmidt 	    GMAC_MAC_MDIO_ADDRESS_GOC_READ | GMAC_MAC_MDIO_ADDRESS_GB;
121702b53ddSSøren Schmidt 	WR4(sc, GMAC_MAC_MDIO_ADDRESS, addr);
122702b53ddSSøren Schmidt 
123702b53ddSSøren Schmidt 	DELAY(100);
124702b53ddSSøren Schmidt 
125702b53ddSSøren Schmidt 	for (retry = MII_BUSY_RETRY; retry > 0; retry--) {
126702b53ddSSøren Schmidt 		addr = RD4(sc, GMAC_MAC_MDIO_ADDRESS);
127702b53ddSSøren Schmidt 		if (!(addr & GMAC_MAC_MDIO_ADDRESS_GB)) {
128702b53ddSSøren Schmidt 			val = RD4(sc, GMAC_MAC_MDIO_DATA) & 0xFFFF;
129702b53ddSSøren Schmidt 			break;
130702b53ddSSøren Schmidt 		}
131702b53ddSSøren Schmidt 		DELAY(10);
132702b53ddSSøren Schmidt 	}
133702b53ddSSøren Schmidt 	if (!retry) {
134702b53ddSSøren Schmidt 		device_printf(dev, "phy read timeout, phy=%d reg=%d\n",
135702b53ddSSøren Schmidt 		    phy, reg);
136702b53ddSSøren Schmidt 		return (ETIMEDOUT);
137702b53ddSSøren Schmidt 	}
138702b53ddSSøren Schmidt 	return (val);
139702b53ddSSøren Schmidt }
140702b53ddSSøren Schmidt 
141702b53ddSSøren Schmidt static int
142702b53ddSSøren Schmidt eqos_miibus_writereg(device_t dev, int phy, int reg, int val)
143702b53ddSSøren Schmidt {
144702b53ddSSøren Schmidt 	struct eqos_softc *sc = device_get_softc(dev);
145702b53ddSSøren Schmidt 	uint32_t addr;
146702b53ddSSøren Schmidt 	int retry;
147702b53ddSSøren Schmidt 
148702b53ddSSøren Schmidt 	WR4(sc, GMAC_MAC_MDIO_DATA, val);
149702b53ddSSøren Schmidt 
150702b53ddSSøren Schmidt 	addr = sc->csr_clock_range |
151702b53ddSSøren Schmidt 	    (phy << GMAC_MAC_MDIO_ADDRESS_PA_SHIFT) |
152702b53ddSSøren Schmidt 	    (reg << GMAC_MAC_MDIO_ADDRESS_RDA_SHIFT) |
153702b53ddSSøren Schmidt 	    GMAC_MAC_MDIO_ADDRESS_GOC_WRITE | GMAC_MAC_MDIO_ADDRESS_GB;
154702b53ddSSøren Schmidt 	WR4(sc, GMAC_MAC_MDIO_ADDRESS, addr);
155702b53ddSSøren Schmidt 
156702b53ddSSøren Schmidt 	DELAY(100);
157702b53ddSSøren Schmidt 
158702b53ddSSøren Schmidt 	for (retry = MII_BUSY_RETRY; retry > 0; retry--) {
159702b53ddSSøren Schmidt 		addr = RD4(sc, GMAC_MAC_MDIO_ADDRESS);
160702b53ddSSøren Schmidt 		if (!(addr & GMAC_MAC_MDIO_ADDRESS_GB))
161702b53ddSSøren Schmidt 			break;
162702b53ddSSøren Schmidt 		DELAY(10);
163702b53ddSSøren Schmidt 	}
164702b53ddSSøren Schmidt 	if (!retry) {
165702b53ddSSøren Schmidt 		device_printf(dev, "phy write timeout, phy=%d reg=%d\n",
166702b53ddSSøren Schmidt 		    phy, reg);
167702b53ddSSøren Schmidt 		return (ETIMEDOUT);
168702b53ddSSøren Schmidt 	}
169702b53ddSSøren Schmidt 	return (0);
170702b53ddSSøren Schmidt }
171702b53ddSSøren Schmidt 
172702b53ddSSøren Schmidt static void
173702b53ddSSøren Schmidt eqos_miibus_statchg(device_t dev)
174702b53ddSSøren Schmidt {
175702b53ddSSøren Schmidt 	struct eqos_softc *sc = device_get_softc(dev);
176702b53ddSSøren Schmidt 	struct mii_data *mii = device_get_softc(sc->miibus);
177702b53ddSSøren Schmidt 	uint32_t reg;
178702b53ddSSøren Schmidt 
179702b53ddSSøren Schmidt 	EQOS_ASSERT_LOCKED(sc);
180702b53ddSSøren Schmidt 
181702b53ddSSøren Schmidt 	if (mii->mii_media_status & IFM_ACTIVE)
182702b53ddSSøren Schmidt 		sc->link_up = true;
183702b53ddSSøren Schmidt 	else
184702b53ddSSøren Schmidt 		sc->link_up = false;
185702b53ddSSøren Schmidt 
186702b53ddSSøren Schmidt 	reg = RD4(sc, GMAC_MAC_CONFIGURATION);
187702b53ddSSøren Schmidt 
188702b53ddSSøren Schmidt 	switch (IFM_SUBTYPE(mii->mii_media_active)) {
189702b53ddSSøren Schmidt 	case IFM_10_T:
190702b53ddSSøren Schmidt 		reg |= GMAC_MAC_CONFIGURATION_PS;
191702b53ddSSøren Schmidt 		reg &= ~GMAC_MAC_CONFIGURATION_FES;
192702b53ddSSøren Schmidt 		break;
193702b53ddSSøren Schmidt 	case IFM_100_TX:
194702b53ddSSøren Schmidt 		reg |= GMAC_MAC_CONFIGURATION_PS;
195702b53ddSSøren Schmidt 		reg |= GMAC_MAC_CONFIGURATION_FES;
196702b53ddSSøren Schmidt 		break;
197702b53ddSSøren Schmidt 	case IFM_1000_T:
198702b53ddSSøren Schmidt         case IFM_1000_SX:
199702b53ddSSøren Schmidt 		reg &= ~GMAC_MAC_CONFIGURATION_PS;
200702b53ddSSøren Schmidt 		reg &= ~GMAC_MAC_CONFIGURATION_FES;
201702b53ddSSøren Schmidt 		break;
202702b53ddSSøren Schmidt 	case IFM_2500_T:
203702b53ddSSøren Schmidt 	case IFM_2500_SX:
204702b53ddSSøren Schmidt 		reg &= ~GMAC_MAC_CONFIGURATION_PS;
205702b53ddSSøren Schmidt 		reg |= GMAC_MAC_CONFIGURATION_FES;
206702b53ddSSøren Schmidt 		break;
207702b53ddSSøren Schmidt 	default:
208702b53ddSSøren Schmidt 		sc->link_up = false;
209702b53ddSSøren Schmidt 		return;
210702b53ddSSøren Schmidt 	}
211702b53ddSSøren Schmidt 
212702b53ddSSøren Schmidt 	if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX))
213702b53ddSSøren Schmidt 		reg |= GMAC_MAC_CONFIGURATION_DM;
214702b53ddSSøren Schmidt 	else
215702b53ddSSøren Schmidt 		reg &= ~GMAC_MAC_CONFIGURATION_DM;
216702b53ddSSøren Schmidt 
217702b53ddSSøren Schmidt 	WR4(sc, GMAC_MAC_CONFIGURATION, reg);
218702b53ddSSøren Schmidt 
2195ba0691dSSøren Schmidt 	IF_EQOS_SET_SPEED(dev, IFM_SUBTYPE(mii->mii_media_active));
220702b53ddSSøren Schmidt 
221702b53ddSSøren Schmidt 	WR4(sc, GMAC_MAC_1US_TIC_COUNTER, (sc->csr_clock / 1000000) - 1);
222702b53ddSSøren Schmidt }
223702b53ddSSøren Schmidt 
224702b53ddSSøren Schmidt static void
225a472fd7aSJustin Hibbits eqos_media_status(if_t ifp, struct ifmediareq *ifmr)
226702b53ddSSøren Schmidt {
227a472fd7aSJustin Hibbits 	struct eqos_softc *sc = if_getsoftc(ifp);
228702b53ddSSøren Schmidt 	struct mii_data *mii = device_get_softc(sc->miibus);
229702b53ddSSøren Schmidt 
230702b53ddSSøren Schmidt 	EQOS_LOCK(sc);
231702b53ddSSøren Schmidt 	mii_pollstat(mii);
232702b53ddSSøren Schmidt 	ifmr->ifm_active = mii->mii_media_active;
233702b53ddSSøren Schmidt 	ifmr->ifm_status = mii->mii_media_status;
234702b53ddSSøren Schmidt 	EQOS_UNLOCK(sc);
235702b53ddSSøren Schmidt }
236702b53ddSSøren Schmidt 
237702b53ddSSøren Schmidt static int
238a472fd7aSJustin Hibbits eqos_media_change(if_t ifp)
239702b53ddSSøren Schmidt {
240a472fd7aSJustin Hibbits 	struct eqos_softc *sc = if_getsoftc(ifp);
241702b53ddSSøren Schmidt 	int error;
242702b53ddSSøren Schmidt 
243702b53ddSSøren Schmidt 	EQOS_LOCK(sc);
244702b53ddSSøren Schmidt 	error = mii_mediachg(device_get_softc(sc->miibus));
245702b53ddSSøren Schmidt 	EQOS_UNLOCK(sc);
246702b53ddSSøren Schmidt 	return (error);
247702b53ddSSøren Schmidt }
248702b53ddSSøren Schmidt 
249702b53ddSSøren Schmidt static void
250702b53ddSSøren Schmidt eqos_setup_txdesc(struct eqos_softc *sc, int index, int flags,
251702b53ddSSøren Schmidt     bus_addr_t paddr, u_int len, u_int total_len)
252702b53ddSSøren Schmidt {
253702b53ddSSøren Schmidt 	uint32_t tdes2, tdes3;
254702b53ddSSøren Schmidt 
255702b53ddSSøren Schmidt 	if (!paddr || !len) {
256702b53ddSSøren Schmidt 		tdes2 = 0;
257702b53ddSSøren Schmidt 		tdes3 = flags;
258702b53ddSSøren Schmidt 	} else {
259702b53ddSSøren Schmidt 		tdes2 = (flags & EQOS_TDES3_LD) ? EQOS_TDES2_IOC : 0;
260702b53ddSSøren Schmidt 		tdes3 = flags;
261702b53ddSSøren Schmidt 	}
262702b53ddSSøren Schmidt 	bus_dmamap_sync(sc->tx.desc_tag, sc->tx.desc_map, BUS_DMASYNC_PREWRITE);
263702b53ddSSøren Schmidt 	sc->tx.desc_ring[index].des0 = htole32((uint32_t)paddr);
264702b53ddSSøren Schmidt 	sc->tx.desc_ring[index].des1 = htole32((uint32_t)(paddr >> 32));
265702b53ddSSøren Schmidt 	sc->tx.desc_ring[index].des2 = htole32(tdes2 | len);
266702b53ddSSøren Schmidt 	sc->tx.desc_ring[index].des3 = htole32(tdes3 | total_len);
267702b53ddSSøren Schmidt }
268702b53ddSSøren Schmidt 
269702b53ddSSøren Schmidt static int
270702b53ddSSøren Schmidt eqos_setup_txbuf(struct eqos_softc *sc, struct mbuf *m)
271702b53ddSSøren Schmidt {
272702b53ddSSøren Schmidt 	bus_dma_segment_t segs[TX_MAX_SEGS];
273702b53ddSSøren Schmidt 	int first = sc->tx.head;
274702b53ddSSøren Schmidt 	int error, nsegs, idx;
275702b53ddSSøren Schmidt 	uint32_t flags;
276702b53ddSSøren Schmidt 
277702b53ddSSøren Schmidt 	error = bus_dmamap_load_mbuf_sg(sc->tx.buf_tag,
278702b53ddSSøren Schmidt 	    sc->tx.buf_map[first].map, m, segs, &nsegs, 0);
279702b53ddSSøren Schmidt 	if (error == EFBIG) {
280702b53ddSSøren Schmidt 		struct mbuf *mb;
281702b53ddSSøren Schmidt 
282702b53ddSSøren Schmidt 		device_printf(sc->dev, "TX packet too big trying defrag\n");
283702b53ddSSøren Schmidt 		bus_dmamap_unload(sc->tx.buf_tag, sc->tx.buf_map[first].map);
284702b53ddSSøren Schmidt 		if (!(mb = m_defrag(m, M_NOWAIT)))
285702b53ddSSøren Schmidt 			return (ENOMEM);
286702b53ddSSøren Schmidt 		m = mb;
287702b53ddSSøren Schmidt 		error = bus_dmamap_load_mbuf_sg(sc->tx.buf_tag,
288702b53ddSSøren Schmidt 		    sc->tx.buf_map[first].map, m, segs, &nsegs, 0);
289702b53ddSSøren Schmidt 	}
290702b53ddSSøren Schmidt 	if (error)
291702b53ddSSøren Schmidt 		return (ENOMEM);
292702b53ddSSøren Schmidt 
293702b53ddSSøren Schmidt 	if (TX_QUEUED(sc->tx.head, sc->tx.tail) + nsegs > TX_DESC_COUNT) {
294702b53ddSSøren Schmidt 		bus_dmamap_unload(sc->tx.buf_tag, sc->tx.buf_map[first].map);
295702b53ddSSøren Schmidt 		device_printf(sc->dev, "TX packet no more queue space\n");
296702b53ddSSøren Schmidt 		return (ENOMEM);
297702b53ddSSøren Schmidt 	}
298702b53ddSSøren Schmidt 
299702b53ddSSøren Schmidt 	bus_dmamap_sync(sc->tx.buf_tag, sc->tx.buf_map[first].map,
300702b53ddSSøren Schmidt 	    BUS_DMASYNC_PREWRITE);
301702b53ddSSøren Schmidt 
302702b53ddSSøren Schmidt 	sc->tx.buf_map[first].mbuf = m;
303702b53ddSSøren Schmidt 
304702b53ddSSøren Schmidt 	for (flags = EQOS_TDES3_FD, idx = 0; idx < nsegs; idx++) {
305702b53ddSSøren Schmidt 		if (idx == (nsegs - 1))
306702b53ddSSøren Schmidt 			flags |= EQOS_TDES3_LD;
307702b53ddSSøren Schmidt 		eqos_setup_txdesc(sc, sc->tx.head, flags, segs[idx].ds_addr,
308702b53ddSSøren Schmidt 		    segs[idx].ds_len, m->m_pkthdr.len);
309702b53ddSSøren Schmidt 		flags &= ~EQOS_TDES3_FD;
310702b53ddSSøren Schmidt 		flags |= EQOS_TDES3_OWN;
311702b53ddSSøren Schmidt 		sc->tx.head = TX_NEXT(sc->tx.head);
312702b53ddSSøren Schmidt 	}
313702b53ddSSøren Schmidt 
314702b53ddSSøren Schmidt 	/*
315702b53ddSSøren Schmidt 	 * Defer setting OWN bit on the first descriptor
316702b53ddSSøren Schmidt 	 * until all descriptors have been updated
317702b53ddSSøren Schmidt 	 */
318702b53ddSSøren Schmidt 	bus_dmamap_sync(sc->tx.desc_tag, sc->tx.desc_map, BUS_DMASYNC_PREWRITE);
319702b53ddSSøren Schmidt 	sc->tx.desc_ring[first].des3 |= htole32(EQOS_TDES3_OWN);
320702b53ddSSøren Schmidt 
321702b53ddSSøren Schmidt 	return (0);
322702b53ddSSøren Schmidt }
323702b53ddSSøren Schmidt 
324702b53ddSSøren Schmidt static void
325702b53ddSSøren Schmidt eqos_setup_rxdesc(struct eqos_softc *sc, int index, bus_addr_t paddr)
326702b53ddSSøren Schmidt {
327702b53ddSSøren Schmidt 
328702b53ddSSøren Schmidt 	sc->rx.desc_ring[index].des0 = htole32((uint32_t)paddr);
329702b53ddSSøren Schmidt 	sc->rx.desc_ring[index].des1 = htole32((uint32_t)(paddr >> 32));
330702b53ddSSøren Schmidt 	sc->rx.desc_ring[index].des2 = htole32(0);
331702b53ddSSøren Schmidt 	bus_dmamap_sync(sc->rx.desc_tag, sc->rx.desc_map, BUS_DMASYNC_PREWRITE);
332702b53ddSSøren Schmidt 	sc->rx.desc_ring[index].des3 = htole32(EQOS_RDES3_OWN | EQOS_RDES3_IOC |
333702b53ddSSøren Schmidt 	    EQOS_RDES3_BUF1V);
334702b53ddSSøren Schmidt }
335702b53ddSSøren Schmidt 
336702b53ddSSøren Schmidt static int
337702b53ddSSøren Schmidt eqos_setup_rxbuf(struct eqos_softc *sc, int index, struct mbuf *m)
338702b53ddSSøren Schmidt {
339702b53ddSSøren Schmidt 	struct bus_dma_segment seg;
340702b53ddSSøren Schmidt 	int error, nsegs;
341702b53ddSSøren Schmidt 
342702b53ddSSøren Schmidt 	m_adj(m, ETHER_ALIGN);
343702b53ddSSøren Schmidt 
344702b53ddSSøren Schmidt 	error = bus_dmamap_load_mbuf_sg(sc->rx.buf_tag,
345702b53ddSSøren Schmidt 	    sc->rx.buf_map[index].map, m, &seg, &nsegs, 0);
346702b53ddSSøren Schmidt 	if (error)
347702b53ddSSøren Schmidt 		return (error);
348702b53ddSSøren Schmidt 
349702b53ddSSøren Schmidt 	bus_dmamap_sync(sc->rx.buf_tag, sc->rx.buf_map[index].map,
350702b53ddSSøren Schmidt 	    BUS_DMASYNC_PREREAD);
351702b53ddSSøren Schmidt 
352702b53ddSSøren Schmidt 	sc->rx.buf_map[index].mbuf = m;
353702b53ddSSøren Schmidt 	eqos_setup_rxdesc(sc, index, seg.ds_addr);
354702b53ddSSøren Schmidt 
355702b53ddSSøren Schmidt 	return (0);
356702b53ddSSøren Schmidt }
357702b53ddSSøren Schmidt 
358702b53ddSSøren Schmidt static struct mbuf *
359702b53ddSSøren Schmidt eqos_alloc_mbufcl(struct eqos_softc *sc)
360702b53ddSSøren Schmidt {
361702b53ddSSøren Schmidt 	struct mbuf *m;
362702b53ddSSøren Schmidt 
363702b53ddSSøren Schmidt 	if ((m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR)))
364702b53ddSSøren Schmidt 		m->m_pkthdr.len = m->m_len = m->m_ext.ext_size;
365702b53ddSSøren Schmidt 	return (m);
366702b53ddSSøren Schmidt }
367702b53ddSSøren Schmidt 
368702b53ddSSøren Schmidt static void
369702b53ddSSøren Schmidt eqos_enable_intr(struct eqos_softc *sc)
370702b53ddSSøren Schmidt {
371702b53ddSSøren Schmidt 
372702b53ddSSøren Schmidt 	WR4(sc, GMAC_DMA_CHAN0_INTR_ENABLE,
373702b53ddSSøren Schmidt 	    GMAC_DMA_CHAN0_INTR_ENABLE_NIE | GMAC_DMA_CHAN0_INTR_ENABLE_AIE |
374702b53ddSSøren Schmidt 	    GMAC_DMA_CHAN0_INTR_ENABLE_FBE | GMAC_DMA_CHAN0_INTR_ENABLE_RIE |
375702b53ddSSøren Schmidt 	    GMAC_DMA_CHAN0_INTR_ENABLE_TIE);
376702b53ddSSøren Schmidt }
377702b53ddSSøren Schmidt 
378702b53ddSSøren Schmidt static void
379702b53ddSSøren Schmidt eqos_disable_intr(struct eqos_softc *sc)
380702b53ddSSøren Schmidt {
381702b53ddSSøren Schmidt 
382702b53ddSSøren Schmidt 	WR4(sc, GMAC_DMA_CHAN0_INTR_ENABLE, 0);
383702b53ddSSøren Schmidt }
384702b53ddSSøren Schmidt 
385702b53ddSSøren Schmidt static uint32_t
386702b53ddSSøren Schmidt eqos_bitrev32(uint32_t x)
387702b53ddSSøren Schmidt {
388702b53ddSSøren Schmidt 
389702b53ddSSøren Schmidt 	x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
390702b53ddSSøren Schmidt 	x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
391702b53ddSSøren Schmidt 	x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
392702b53ddSSøren Schmidt 	x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
393702b53ddSSøren Schmidt 	return ((x >> 16) | (x << 16));
394702b53ddSSøren Schmidt }
395702b53ddSSøren Schmidt 
396702b53ddSSøren Schmidt static u_int
397702b53ddSSøren Schmidt eqos_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
398702b53ddSSøren Schmidt {
399702b53ddSSøren Schmidt 	uint32_t crc, *hash = arg;
400702b53ddSSøren Schmidt 
401702b53ddSSøren Schmidt 	crc = ether_crc32_le(LLADDR(sdl), ETHER_ADDR_LEN);
402702b53ddSSøren Schmidt 	crc &= 0x7f;
403702b53ddSSøren Schmidt 	crc = eqos_bitrev32(~crc) >> 26;
404702b53ddSSøren Schmidt 	hash[crc >> 5] |= 1 << (crc & 0x1f);
405702b53ddSSøren Schmidt 	return (1);
406702b53ddSSøren Schmidt }
407702b53ddSSøren Schmidt 
408702b53ddSSøren Schmidt static void
409702b53ddSSøren Schmidt eqos_setup_rxfilter(struct eqos_softc *sc)
410702b53ddSSøren Schmidt {
411a472fd7aSJustin Hibbits 	if_t ifp = sc->ifp;
412702b53ddSSøren Schmidt 	uint32_t pfil, hash[2];
413702b53ddSSøren Schmidt 	const uint8_t *eaddr;
414702b53ddSSøren Schmidt 	uint32_t val;
415702b53ddSSøren Schmidt 
416702b53ddSSøren Schmidt 	EQOS_ASSERT_LOCKED(sc);
417702b53ddSSøren Schmidt 
418702b53ddSSøren Schmidt 	pfil = RD4(sc, GMAC_MAC_PACKET_FILTER);
419702b53ddSSøren Schmidt 	pfil &= ~(GMAC_MAC_PACKET_FILTER_PR |
420702b53ddSSøren Schmidt 	    GMAC_MAC_PACKET_FILTER_PM |
421702b53ddSSøren Schmidt 	    GMAC_MAC_PACKET_FILTER_HMC |
422702b53ddSSøren Schmidt 	    GMAC_MAC_PACKET_FILTER_PCF_MASK);
423702b53ddSSøren Schmidt 	hash[0] = hash[1] = 0xffffffff;
424702b53ddSSøren Schmidt 
425a472fd7aSJustin Hibbits 	if ((if_getflags(ifp) & IFF_PROMISC)) {
426702b53ddSSøren Schmidt 		pfil |= GMAC_MAC_PACKET_FILTER_PR |
427702b53ddSSøren Schmidt 		    GMAC_MAC_PACKET_FILTER_PCF_ALL;
428a472fd7aSJustin Hibbits 	} else if ((if_getflags(ifp) & IFF_ALLMULTI)) {
429702b53ddSSøren Schmidt 		pfil |= GMAC_MAC_PACKET_FILTER_PM;
430702b53ddSSøren Schmidt 	} else {
431702b53ddSSøren Schmidt 		hash[0] = hash[1] = 0;
432702b53ddSSøren Schmidt 		pfil |= GMAC_MAC_PACKET_FILTER_HMC;
433702b53ddSSøren Schmidt 		if_foreach_llmaddr(ifp, eqos_hash_maddr, hash);
434702b53ddSSøren Schmidt 	}
435702b53ddSSøren Schmidt 
436702b53ddSSøren Schmidt 	/* Write our unicast address */
437a472fd7aSJustin Hibbits 	eaddr = if_getlladdr(ifp);
438702b53ddSSøren Schmidt 	val = eaddr[4] | (eaddr[5] << 8);
439702b53ddSSøren Schmidt 	WR4(sc, GMAC_MAC_ADDRESS0_HIGH, val);
440702b53ddSSøren Schmidt 	val = eaddr[0] | (eaddr[1] << 8) | (eaddr[2] << 16) |
441702b53ddSSøren Schmidt 	    (eaddr[3] << 24);
442702b53ddSSøren Schmidt 	WR4(sc, GMAC_MAC_ADDRESS0_LOW, val);
443702b53ddSSøren Schmidt 
444702b53ddSSøren Schmidt 	/* Multicast hash filters */
445*bf8d4cfeSPeter Jeremy 	WR4(sc, GMAC_MAC_HASH_TABLE_REG0, hash[0]);
446*bf8d4cfeSPeter Jeremy 	WR4(sc, GMAC_MAC_HASH_TABLE_REG1, hash[1]);
447702b53ddSSøren Schmidt 
448702b53ddSSøren Schmidt 	/* Packet filter config */
449702b53ddSSøren Schmidt 	WR4(sc, GMAC_MAC_PACKET_FILTER, pfil);
450702b53ddSSøren Schmidt }
451702b53ddSSøren Schmidt 
452702b53ddSSøren Schmidt static int
453702b53ddSSøren Schmidt eqos_reset(struct eqos_softc *sc)
454702b53ddSSøren Schmidt {
455702b53ddSSøren Schmidt 	uint32_t val;
456702b53ddSSøren Schmidt 	int retry;
457702b53ddSSøren Schmidt 
458702b53ddSSøren Schmidt 	WR4(sc, GMAC_DMA_MODE, GMAC_DMA_MODE_SWR);
459702b53ddSSøren Schmidt 	for (retry = 2000; retry > 0; retry--) {
460702b53ddSSøren Schmidt 		DELAY(1000);
461702b53ddSSøren Schmidt 		val = RD4(sc, GMAC_DMA_MODE);
462702b53ddSSøren Schmidt 		if (!(val & GMAC_DMA_MODE_SWR))
463702b53ddSSøren Schmidt 			return (0);
464702b53ddSSøren Schmidt 	}
465702b53ddSSøren Schmidt 	return (ETIMEDOUT);
466702b53ddSSøren Schmidt }
467702b53ddSSøren Schmidt 
468702b53ddSSøren Schmidt static void
469702b53ddSSøren Schmidt eqos_init_rings(struct eqos_softc *sc)
470702b53ddSSøren Schmidt {
471702b53ddSSøren Schmidt 
472702b53ddSSøren Schmidt 	WR4(sc, GMAC_DMA_CHAN0_TX_BASE_ADDR_HI,
473702b53ddSSøren Schmidt 	    (uint32_t)(sc->tx.desc_ring_paddr >> 32));
474702b53ddSSøren Schmidt 	WR4(sc, GMAC_DMA_CHAN0_TX_BASE_ADDR,
475702b53ddSSøren Schmidt 	    (uint32_t)sc->tx.desc_ring_paddr);
476702b53ddSSøren Schmidt 	WR4(sc, GMAC_DMA_CHAN0_TX_RING_LEN, TX_DESC_COUNT - 1);
477702b53ddSSøren Schmidt 
478702b53ddSSøren Schmidt 	WR4(sc, GMAC_DMA_CHAN0_RX_BASE_ADDR_HI,
479702b53ddSSøren Schmidt 	    (uint32_t)(sc->rx.desc_ring_paddr >> 32));
480702b53ddSSøren Schmidt 	WR4(sc, GMAC_DMA_CHAN0_RX_BASE_ADDR,
481702b53ddSSøren Schmidt 	    (uint32_t)sc->rx.desc_ring_paddr);
482702b53ddSSøren Schmidt 	WR4(sc, GMAC_DMA_CHAN0_RX_RING_LEN, RX_DESC_COUNT - 1);
483702b53ddSSøren Schmidt 
484702b53ddSSøren Schmidt 	WR4(sc, GMAC_DMA_CHAN0_RX_END_ADDR,
485702b53ddSSøren Schmidt 	    (uint32_t)sc->rx.desc_ring_paddr + DESC_OFFSET(RX_DESC_COUNT));
486702b53ddSSøren Schmidt }
487702b53ddSSøren Schmidt 
488702b53ddSSøren Schmidt static void
489702b53ddSSøren Schmidt eqos_init(void *if_softc)
490702b53ddSSøren Schmidt {
491702b53ddSSøren Schmidt 	struct eqos_softc *sc = if_softc;
492a472fd7aSJustin Hibbits 	if_t ifp = sc->ifp;
493702b53ddSSøren Schmidt 	struct mii_data *mii = device_get_softc(sc->miibus);
494702b53ddSSøren Schmidt 	uint32_t val;
495702b53ddSSøren Schmidt 
496702b53ddSSøren Schmidt 	if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
497702b53ddSSøren Schmidt 		return;
498702b53ddSSøren Schmidt 
499702b53ddSSøren Schmidt 	EQOS_LOCK(sc);
500702b53ddSSøren Schmidt 
501702b53ddSSøren Schmidt 	eqos_init_rings(sc);
502702b53ddSSøren Schmidt 
503702b53ddSSøren Schmidt 	eqos_setup_rxfilter(sc);
504702b53ddSSøren Schmidt 
505702b53ddSSøren Schmidt 	WR4(sc, GMAC_MAC_1US_TIC_COUNTER, (sc->csr_clock / 1000000) - 1);
506702b53ddSSøren Schmidt 
507702b53ddSSøren Schmidt 	/* Enable transmit and receive DMA */
508702b53ddSSøren Schmidt 	val = RD4(sc, GMAC_DMA_CHAN0_CONTROL);
509702b53ddSSøren Schmidt 	val &= ~GMAC_DMA_CHAN0_CONTROL_DSL_MASK;
510702b53ddSSøren Schmidt 	val |= ((DESC_ALIGN - 16) / 8) << GMAC_DMA_CHAN0_CONTROL_DSL_SHIFT;
511702b53ddSSøren Schmidt 	val |= GMAC_DMA_CHAN0_CONTROL_PBLX8;
512702b53ddSSøren Schmidt 	WR4(sc, GMAC_DMA_CHAN0_CONTROL, val);
513702b53ddSSøren Schmidt 	val = RD4(sc, GMAC_DMA_CHAN0_TX_CONTROL);
514702b53ddSSøren Schmidt 	val |= GMAC_DMA_CHAN0_TX_CONTROL_OSP;
515702b53ddSSøren Schmidt 	val |= GMAC_DMA_CHAN0_TX_CONTROL_START;
516702b53ddSSøren Schmidt 	WR4(sc, GMAC_DMA_CHAN0_TX_CONTROL, val);
517702b53ddSSøren Schmidt 	val = RD4(sc, GMAC_DMA_CHAN0_RX_CONTROL);
518702b53ddSSøren Schmidt 	val &= ~GMAC_DMA_CHAN0_RX_CONTROL_RBSZ_MASK;
519702b53ddSSøren Schmidt 	val |= (MCLBYTES << GMAC_DMA_CHAN0_RX_CONTROL_RBSZ_SHIFT);
520702b53ddSSøren Schmidt 	val |= GMAC_DMA_CHAN0_RX_CONTROL_START;
521702b53ddSSøren Schmidt 	WR4(sc, GMAC_DMA_CHAN0_RX_CONTROL, val);
522702b53ddSSøren Schmidt 
523702b53ddSSøren Schmidt 	/* Disable counters */
524702b53ddSSøren Schmidt 	WR4(sc, GMAC_MMC_CONTROL,
525702b53ddSSøren Schmidt 	    GMAC_MMC_CONTROL_CNTFREEZ |
526702b53ddSSøren Schmidt 	    GMAC_MMC_CONTROL_CNTPRST |
527702b53ddSSøren Schmidt 	    GMAC_MMC_CONTROL_CNTPRSTLVL);
528702b53ddSSøren Schmidt 
529702b53ddSSøren Schmidt 	/* Configure operation modes */
530702b53ddSSøren Schmidt 	WR4(sc, GMAC_MTL_TXQ0_OPERATION_MODE,
531702b53ddSSøren Schmidt 	    GMAC_MTL_TXQ0_OPERATION_MODE_TSF |
532702b53ddSSøren Schmidt 	    GMAC_MTL_TXQ0_OPERATION_MODE_TXQEN_EN);
533702b53ddSSøren Schmidt 	WR4(sc, GMAC_MTL_RXQ0_OPERATION_MODE,
534702b53ddSSøren Schmidt 	    GMAC_MTL_RXQ0_OPERATION_MODE_RSF |
535702b53ddSSøren Schmidt 	    GMAC_MTL_RXQ0_OPERATION_MODE_FEP |
536702b53ddSSøren Schmidt 	    GMAC_MTL_RXQ0_OPERATION_MODE_FUP);
537702b53ddSSøren Schmidt 
538702b53ddSSøren Schmidt 	/* Enable flow control */
539702b53ddSSøren Schmidt 	val = RD4(sc, GMAC_MAC_Q0_TX_FLOW_CTRL);
540702b53ddSSøren Schmidt 	val |= 0xFFFFU << GMAC_MAC_Q0_TX_FLOW_CTRL_PT_SHIFT;
541702b53ddSSøren Schmidt 	val |= GMAC_MAC_Q0_TX_FLOW_CTRL_TFE;
542702b53ddSSøren Schmidt 	WR4(sc, GMAC_MAC_Q0_TX_FLOW_CTRL, val);
543702b53ddSSøren Schmidt 	val = RD4(sc, GMAC_MAC_RX_FLOW_CTRL);
544702b53ddSSøren Schmidt 	val |= GMAC_MAC_RX_FLOW_CTRL_RFE;
545702b53ddSSøren Schmidt 	WR4(sc, GMAC_MAC_RX_FLOW_CTRL, val);
546702b53ddSSøren Schmidt 
547702b53ddSSøren Schmidt 	/* set RX queue mode. must be in DCB mode. */
548702b53ddSSøren Schmidt 	WR4(sc, GMAC_RXQ_CTRL0, (GMAC_RXQ_CTRL0_EN_MASK << 16) |
549702b53ddSSøren Schmidt 	    GMAC_RXQ_CTRL0_EN_DCB);
550702b53ddSSøren Schmidt 
551702b53ddSSøren Schmidt 	/* Enable transmitter and receiver */
552702b53ddSSøren Schmidt 	val = RD4(sc, GMAC_MAC_CONFIGURATION);
553702b53ddSSøren Schmidt 	val |= GMAC_MAC_CONFIGURATION_BE;
554702b53ddSSøren Schmidt 	val |= GMAC_MAC_CONFIGURATION_JD;
555702b53ddSSøren Schmidt 	val |= GMAC_MAC_CONFIGURATION_JE;
556702b53ddSSøren Schmidt 	val |= GMAC_MAC_CONFIGURATION_DCRS;
557702b53ddSSøren Schmidt 	val |= GMAC_MAC_CONFIGURATION_TE;
558702b53ddSSøren Schmidt 	val |= GMAC_MAC_CONFIGURATION_RE;
559702b53ddSSøren Schmidt 	WR4(sc, GMAC_MAC_CONFIGURATION, val);
560702b53ddSSøren Schmidt 
561702b53ddSSøren Schmidt 	eqos_enable_intr(sc);
562702b53ddSSøren Schmidt 
563702b53ddSSøren Schmidt 	if_setdrvflagbits(ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE);
564702b53ddSSøren Schmidt 
565702b53ddSSøren Schmidt 	mii_mediachg(mii);
566702b53ddSSøren Schmidt 	callout_reset(&sc->callout, hz, eqos_tick, sc);
567702b53ddSSøren Schmidt 
568702b53ddSSøren Schmidt 	EQOS_UNLOCK(sc);
569702b53ddSSøren Schmidt }
570702b53ddSSøren Schmidt 
571702b53ddSSøren Schmidt static void
572a472fd7aSJustin Hibbits eqos_start_locked(if_t ifp)
573702b53ddSSøren Schmidt {
574a472fd7aSJustin Hibbits 	struct eqos_softc *sc = if_getsoftc(ifp);
575702b53ddSSøren Schmidt 	struct mbuf *m;
576702b53ddSSøren Schmidt 	int pending = 0;
577702b53ddSSøren Schmidt 
578702b53ddSSøren Schmidt 	if (!sc->link_up)
579702b53ddSSøren Schmidt 		return;
580702b53ddSSøren Schmidt 
581702b53ddSSøren Schmidt 	if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) !=
582702b53ddSSøren Schmidt 	    IFF_DRV_RUNNING)
583702b53ddSSøren Schmidt 		return;
584702b53ddSSøren Schmidt 
585702b53ddSSøren Schmidt 	while (true) {
586702b53ddSSøren Schmidt 		if (TX_QUEUED(sc->tx.head, sc->tx.tail) >=
587702b53ddSSøren Schmidt 		    TX_DESC_COUNT - TX_MAX_SEGS) {
588702b53ddSSøren Schmidt 			if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
589702b53ddSSøren Schmidt 			break;
590702b53ddSSøren Schmidt 		}
591702b53ddSSøren Schmidt 
592702b53ddSSøren Schmidt 		if (!(m = if_dequeue(ifp)))
593702b53ddSSøren Schmidt 			break;
594702b53ddSSøren Schmidt 
595702b53ddSSøren Schmidt 		if (eqos_setup_txbuf(sc, m)) {
596702b53ddSSøren Schmidt 			if_sendq_prepend(ifp, m);
597702b53ddSSøren Schmidt 			if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
598702b53ddSSøren Schmidt 			break;
599702b53ddSSøren Schmidt 		}
6002a371643SJustin Hibbits 		bpf_mtap_if(ifp, m);
601702b53ddSSøren Schmidt 		pending++;
602702b53ddSSøren Schmidt 	}
603702b53ddSSøren Schmidt 
604702b53ddSSøren Schmidt 	if (pending) {
605702b53ddSSøren Schmidt 		bus_dmamap_sync(sc->tx.desc_tag, sc->tx.desc_map,
606702b53ddSSøren Schmidt 		    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
607702b53ddSSøren Schmidt 
608702b53ddSSøren Schmidt 		/* Start and run TX DMA */
609702b53ddSSøren Schmidt 		WR4(sc, GMAC_DMA_CHAN0_TX_END_ADDR,
610702b53ddSSøren Schmidt 		    (uint32_t)sc->tx.desc_ring_paddr + DESC_OFFSET(sc->tx.head));
611702b53ddSSøren Schmidt 		sc->tx_watchdog = WATCHDOG_TIMEOUT_SECS;
612702b53ddSSøren Schmidt 	}
613702b53ddSSøren Schmidt }
614702b53ddSSøren Schmidt 
615702b53ddSSøren Schmidt static void
616a472fd7aSJustin Hibbits eqos_start(if_t ifp)
617702b53ddSSøren Schmidt {
618a472fd7aSJustin Hibbits 	struct eqos_softc *sc = if_getsoftc(ifp);
619702b53ddSSøren Schmidt 
620702b53ddSSøren Schmidt 	EQOS_LOCK(sc);
621702b53ddSSøren Schmidt 	eqos_start_locked(ifp);
622702b53ddSSøren Schmidt 	EQOS_UNLOCK(sc);
623702b53ddSSøren Schmidt }
624702b53ddSSøren Schmidt 
625702b53ddSSøren Schmidt static void
626702b53ddSSøren Schmidt eqos_stop(struct eqos_softc *sc)
627702b53ddSSøren Schmidt {
628a472fd7aSJustin Hibbits 	if_t ifp = sc->ifp;
629702b53ddSSøren Schmidt 	uint32_t val;
630702b53ddSSøren Schmidt 	int retry;
631702b53ddSSøren Schmidt 
632702b53ddSSøren Schmidt 	EQOS_LOCK(sc);
633702b53ddSSøren Schmidt 
634702b53ddSSøren Schmidt 	if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
635702b53ddSSøren Schmidt 
636702b53ddSSøren Schmidt 	callout_stop(&sc->callout);
637702b53ddSSøren Schmidt 
638702b53ddSSøren Schmidt 	/* Disable receiver */
639702b53ddSSøren Schmidt 	val = RD4(sc, GMAC_MAC_CONFIGURATION);
640702b53ddSSøren Schmidt 	val &= ~GMAC_MAC_CONFIGURATION_RE;
641702b53ddSSøren Schmidt 	WR4(sc, GMAC_MAC_CONFIGURATION, val);
642702b53ddSSøren Schmidt 
643702b53ddSSøren Schmidt 	/* Stop receive DMA */
644702b53ddSSøren Schmidt 	val = RD4(sc, GMAC_DMA_CHAN0_RX_CONTROL);
645702b53ddSSøren Schmidt 	val &= ~GMAC_DMA_CHAN0_RX_CONTROL_START;
646702b53ddSSøren Schmidt 	WR4(sc, GMAC_DMA_CHAN0_RX_CONTROL, val);
647702b53ddSSøren Schmidt 
648702b53ddSSøren Schmidt 	/* Stop transmit DMA */
649702b53ddSSøren Schmidt 	val = RD4(sc, GMAC_DMA_CHAN0_TX_CONTROL);
650702b53ddSSøren Schmidt 	val &= ~GMAC_DMA_CHAN0_TX_CONTROL_START;
651702b53ddSSøren Schmidt 	WR4(sc, GMAC_DMA_CHAN0_TX_CONTROL, val);
652702b53ddSSøren Schmidt 
653702b53ddSSøren Schmidt 	/* Flush data in the TX FIFO */
654702b53ddSSøren Schmidt 	val = RD4(sc, GMAC_MTL_TXQ0_OPERATION_MODE);
655702b53ddSSøren Schmidt 	val |= GMAC_MTL_TXQ0_OPERATION_MODE_FTQ;
656702b53ddSSøren Schmidt 	WR4(sc, GMAC_MTL_TXQ0_OPERATION_MODE, val);
657702b53ddSSøren Schmidt 	for (retry = 10000; retry > 0; retry--) {
658702b53ddSSøren Schmidt 		val = RD4(sc, GMAC_MTL_TXQ0_OPERATION_MODE);
659702b53ddSSøren Schmidt 		if (!(val & GMAC_MTL_TXQ0_OPERATION_MODE_FTQ))
660702b53ddSSøren Schmidt 			break;
661702b53ddSSøren Schmidt 		DELAY(10);
662702b53ddSSøren Schmidt 	}
663702b53ddSSøren Schmidt 	if (!retry)
664702b53ddSSøren Schmidt 		device_printf(sc->dev, "timeout flushing TX queue\n");
665702b53ddSSøren Schmidt 
666702b53ddSSøren Schmidt 	/* Disable transmitter */
667702b53ddSSøren Schmidt 	val = RD4(sc, GMAC_MAC_CONFIGURATION);
668702b53ddSSøren Schmidt 	val &= ~GMAC_MAC_CONFIGURATION_TE;
669702b53ddSSøren Schmidt 	WR4(sc, GMAC_MAC_CONFIGURATION, val);
670702b53ddSSøren Schmidt 
671702b53ddSSøren Schmidt 	eqos_disable_intr(sc);
672702b53ddSSøren Schmidt 
673702b53ddSSøren Schmidt 	EQOS_UNLOCK(sc);
674702b53ddSSøren Schmidt }
675702b53ddSSøren Schmidt 
676702b53ddSSøren Schmidt static void
677702b53ddSSøren Schmidt eqos_rxintr(struct eqos_softc *sc)
678702b53ddSSøren Schmidt {
679a472fd7aSJustin Hibbits 	if_t ifp = sc->ifp;
680702b53ddSSøren Schmidt 	struct mbuf *m;
681702b53ddSSøren Schmidt 	uint32_t rdes3;
682702b53ddSSøren Schmidt 	int error, length;
683702b53ddSSøren Schmidt 
684702b53ddSSøren Schmidt 	while (true) {
685702b53ddSSøren Schmidt 		rdes3 = le32toh(sc->rx.desc_ring[sc->rx.head].des3);
686702b53ddSSøren Schmidt 		if ((rdes3 & EQOS_RDES3_OWN))
687702b53ddSSøren Schmidt 			break;
688702b53ddSSøren Schmidt 
689702b53ddSSøren Schmidt 		if (rdes3 & (EQOS_RDES3_OE | EQOS_RDES3_RE))
690551921a7SGordon Bergling 			printf("Receive error rdes3=%08x\n", rdes3);
691702b53ddSSøren Schmidt 
692702b53ddSSøren Schmidt 		bus_dmamap_sync(sc->rx.buf_tag,
693702b53ddSSøren Schmidt 		    sc->rx.buf_map[sc->rx.head].map, BUS_DMASYNC_POSTREAD);
694702b53ddSSøren Schmidt 		bus_dmamap_unload(sc->rx.buf_tag,
695702b53ddSSøren Schmidt 		    sc->rx.buf_map[sc->rx.head].map);
696702b53ddSSøren Schmidt 
697702b53ddSSøren Schmidt 		length = rdes3 & EQOS_RDES3_LENGTH_MASK;
698702b53ddSSøren Schmidt 		if (length) {
699702b53ddSSøren Schmidt 			m = sc->rx.buf_map[sc->rx.head].mbuf;
700702b53ddSSøren Schmidt 			m->m_pkthdr.rcvif = ifp;
701702b53ddSSøren Schmidt 			m->m_pkthdr.len = length;
702702b53ddSSøren Schmidt 			m->m_len = length;
703702b53ddSSøren Schmidt 			m->m_nextpkt = NULL;
704702b53ddSSøren Schmidt 
705702b53ddSSøren Schmidt 			/* Remove trailing FCS */
706702b53ddSSøren Schmidt 			m_adj(m, -ETHER_CRC_LEN);
707702b53ddSSøren Schmidt 
708702b53ddSSøren Schmidt 			EQOS_UNLOCK(sc);
709a472fd7aSJustin Hibbits 			if_input(ifp, m);
710702b53ddSSøren Schmidt 			EQOS_LOCK(sc);
711702b53ddSSøren Schmidt 		}
712702b53ddSSøren Schmidt 
713702b53ddSSøren Schmidt 		if ((m = eqos_alloc_mbufcl(sc))) {
714702b53ddSSøren Schmidt 			if ((error = eqos_setup_rxbuf(sc, sc->rx.head, m)))
715702b53ddSSøren Schmidt 				printf("ERROR: Hole in RX ring!!\n");
716702b53ddSSøren Schmidt 		}
717702b53ddSSøren Schmidt 		else
718702b53ddSSøren Schmidt 			if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
719702b53ddSSøren Schmidt 
720702b53ddSSøren Schmidt 		if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
721702b53ddSSøren Schmidt 
722702b53ddSSøren Schmidt 		WR4(sc, GMAC_DMA_CHAN0_RX_END_ADDR,
723702b53ddSSøren Schmidt 		    (uint32_t)sc->rx.desc_ring_paddr + DESC_OFFSET(sc->rx.head));
724702b53ddSSøren Schmidt 
725702b53ddSSøren Schmidt 		sc->rx.head = RX_NEXT(sc->rx.head);
726702b53ddSSøren Schmidt 	}
727702b53ddSSøren Schmidt }
728702b53ddSSøren Schmidt 
729702b53ddSSøren Schmidt static void
730702b53ddSSøren Schmidt eqos_txintr(struct eqos_softc *sc)
731702b53ddSSøren Schmidt {
732a472fd7aSJustin Hibbits 	if_t ifp = sc->ifp;
733702b53ddSSøren Schmidt 	struct eqos_bufmap *bmap;
734702b53ddSSøren Schmidt 	uint32_t tdes3;
735702b53ddSSøren Schmidt 
736702b53ddSSøren Schmidt 	EQOS_ASSERT_LOCKED(sc);
737702b53ddSSøren Schmidt 
738702b53ddSSøren Schmidt 	while (sc->tx.tail != sc->tx.head) {
739702b53ddSSøren Schmidt 		tdes3 = le32toh(sc->tx.desc_ring[sc->tx.tail].des3);
740702b53ddSSøren Schmidt 		if ((tdes3 & EQOS_TDES3_OWN))
741702b53ddSSøren Schmidt 			break;
742702b53ddSSøren Schmidt 
743702b53ddSSøren Schmidt 		bmap = &sc->tx.buf_map[sc->tx.tail];
744702b53ddSSøren Schmidt 		if (bmap->mbuf) {
745702b53ddSSøren Schmidt 			bus_dmamap_sync(sc->tx.buf_tag, bmap->map,
746702b53ddSSøren Schmidt 			    BUS_DMASYNC_POSTWRITE);
747702b53ddSSøren Schmidt 			bus_dmamap_unload(sc->tx.buf_tag, bmap->map);
748702b53ddSSøren Schmidt 			m_freem(bmap->mbuf);
749702b53ddSSøren Schmidt 			bmap->mbuf = NULL;
750702b53ddSSøren Schmidt 		}
751702b53ddSSøren Schmidt 
752702b53ddSSøren Schmidt 		eqos_setup_txdesc(sc, sc->tx.tail, 0, 0, 0, 0);
753702b53ddSSøren Schmidt 
754702b53ddSSøren Schmidt 		if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
755702b53ddSSøren Schmidt 
756702b53ddSSøren Schmidt 		/* Last descriptor in a packet contains DMA status */
757702b53ddSSøren Schmidt 		if ((tdes3 & EQOS_TDES3_LD)) {
758702b53ddSSøren Schmidt 			if ((tdes3 & EQOS_TDES3_DE)) {
759702b53ddSSøren Schmidt 				if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
760702b53ddSSøren Schmidt 			} else if ((tdes3 & EQOS_TDES3_ES)) {
761702b53ddSSøren Schmidt 				if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
762702b53ddSSøren Schmidt 			} else {
763702b53ddSSøren Schmidt 				if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
764702b53ddSSøren Schmidt 			}
765702b53ddSSøren Schmidt 		}
766702b53ddSSøren Schmidt 		sc->tx.tail = TX_NEXT(sc->tx.tail);
767702b53ddSSøren Schmidt 	}
768702b53ddSSøren Schmidt 	if (sc->tx.tail == sc->tx.head)
769702b53ddSSøren Schmidt 		sc->tx_watchdog = 0;
770702b53ddSSøren Schmidt 	eqos_start_locked(sc->ifp);
771702b53ddSSøren Schmidt }
772702b53ddSSøren Schmidt 
773702b53ddSSøren Schmidt static void
774702b53ddSSøren Schmidt eqos_intr_mtl(struct eqos_softc *sc, uint32_t mtl_status)
775702b53ddSSøren Schmidt {
776702b53ddSSøren Schmidt 	uint32_t mtl_istat = 0;
777702b53ddSSøren Schmidt 
778702b53ddSSøren Schmidt 	if ((mtl_status & GMAC_MTL_INTERRUPT_STATUS_Q0IS)) {
779702b53ddSSøren Schmidt 		uint32_t mtl_clear = 0;
780702b53ddSSøren Schmidt 
781702b53ddSSøren Schmidt 		mtl_istat = RD4(sc, GMAC_MTL_Q0_INTERRUPT_CTRL_STATUS);
782702b53ddSSøren Schmidt 		if ((mtl_istat & GMAC_MTL_Q0_INTERRUPT_CTRL_STATUS_RXOVFIS)) {
783702b53ddSSøren Schmidt 			mtl_clear |= GMAC_MTL_Q0_INTERRUPT_CTRL_STATUS_RXOVFIS;
784702b53ddSSøren Schmidt 		}
785702b53ddSSøren Schmidt 		if ((mtl_istat & GMAC_MTL_Q0_INTERRUPT_CTRL_STATUS_TXUNFIS)) {
786702b53ddSSøren Schmidt 			mtl_clear |= GMAC_MTL_Q0_INTERRUPT_CTRL_STATUS_TXUNFIS;
787702b53ddSSøren Schmidt 		}
788702b53ddSSøren Schmidt 		if (mtl_clear) {
789702b53ddSSøren Schmidt 			mtl_clear |= (mtl_istat &
790702b53ddSSøren Schmidt 			    (GMAC_MTL_Q0_INTERRUPT_CTRL_STATUS_RXOIE |
791702b53ddSSøren Schmidt 			    GMAC_MTL_Q0_INTERRUPT_CTRL_STATUS_TXUIE));
792702b53ddSSøren Schmidt 			WR4(sc, GMAC_MTL_Q0_INTERRUPT_CTRL_STATUS, mtl_clear);
793702b53ddSSøren Schmidt 		}
794702b53ddSSøren Schmidt 	}
795702b53ddSSøren Schmidt 	if (bootverbose)
796702b53ddSSøren Schmidt 		device_printf(sc->dev,
797702b53ddSSøren Schmidt 		    "GMAC_MTL_INTERRUPT_STATUS = 0x%08X, "
798702b53ddSSøren Schmidt 		    "GMAC_MTL_INTERRUPT_STATUS_Q0IS = 0x%08X\n",
799702b53ddSSøren Schmidt 		    mtl_status, mtl_istat);
800702b53ddSSøren Schmidt }
801702b53ddSSøren Schmidt 
802702b53ddSSøren Schmidt static void
803702b53ddSSøren Schmidt eqos_tick(void *softc)
804702b53ddSSøren Schmidt {
805702b53ddSSøren Schmidt 	struct eqos_softc *sc = softc;
806702b53ddSSøren Schmidt 	struct mii_data *mii = device_get_softc(sc->miibus);
807702b53ddSSøren Schmidt 	bool link_status;
808702b53ddSSøren Schmidt 
809702b53ddSSøren Schmidt 	EQOS_ASSERT_LOCKED(sc);
810702b53ddSSøren Schmidt 
811702b53ddSSøren Schmidt 	if (sc->tx_watchdog > 0)
812702b53ddSSøren Schmidt 		if (!--sc->tx_watchdog) {
813702b53ddSSøren Schmidt 			device_printf(sc->dev, "watchdog timeout\n");
814702b53ddSSøren Schmidt 			eqos_txintr(sc);
815702b53ddSSøren Schmidt 		}
816702b53ddSSøren Schmidt 
817702b53ddSSøren Schmidt 	link_status = sc->link_up;
818702b53ddSSøren Schmidt 	mii_tick(mii);
819702b53ddSSøren Schmidt 	if (sc->link_up && !link_status)
820702b53ddSSøren Schmidt 		eqos_start_locked(sc->ifp);
821702b53ddSSøren Schmidt 
822702b53ddSSøren Schmidt 	callout_reset(&sc->callout, hz, eqos_tick, sc);
823702b53ddSSøren Schmidt }
824702b53ddSSøren Schmidt 
825702b53ddSSøren Schmidt static void
826702b53ddSSøren Schmidt eqos_intr(void *arg)
827702b53ddSSøren Schmidt {
828702b53ddSSøren Schmidt 	struct eqos_softc *sc = arg;
829702b53ddSSøren Schmidt 	uint32_t mac_status, mtl_status, dma_status, rx_tx_status;
830702b53ddSSøren Schmidt 
831702b53ddSSøren Schmidt 	mac_status = RD4(sc, GMAC_MAC_INTERRUPT_STATUS);
832702b53ddSSøren Schmidt 	mac_status &= RD4(sc, GMAC_MAC_INTERRUPT_ENABLE);
833702b53ddSSøren Schmidt 
834702b53ddSSøren Schmidt 	if (mac_status)
835702b53ddSSøren Schmidt 		device_printf(sc->dev, "MAC interrupt\n");
836702b53ddSSøren Schmidt 
837702b53ddSSøren Schmidt 	if ((mtl_status = RD4(sc, GMAC_MTL_INTERRUPT_STATUS)))
838702b53ddSSøren Schmidt 		eqos_intr_mtl(sc, mtl_status);
839702b53ddSSøren Schmidt 
840702b53ddSSøren Schmidt 	dma_status = RD4(sc, GMAC_DMA_CHAN0_STATUS);
841702b53ddSSøren Schmidt 	dma_status &= RD4(sc, GMAC_DMA_CHAN0_INTR_ENABLE);
842702b53ddSSøren Schmidt 
843702b53ddSSøren Schmidt 	if (dma_status)
844702b53ddSSøren Schmidt 		WR4(sc, GMAC_DMA_CHAN0_STATUS, dma_status);
845702b53ddSSøren Schmidt 
846702b53ddSSøren Schmidt 	EQOS_LOCK(sc);
847702b53ddSSøren Schmidt 
848702b53ddSSøren Schmidt 	if (dma_status & GMAC_DMA_CHAN0_STATUS_RI)
849702b53ddSSøren Schmidt 		eqos_rxintr(sc);
850702b53ddSSøren Schmidt 
851702b53ddSSøren Schmidt 	if (dma_status & GMAC_DMA_CHAN0_STATUS_TI)
852702b53ddSSøren Schmidt 		eqos_txintr(sc);
853702b53ddSSøren Schmidt 
854702b53ddSSøren Schmidt 	EQOS_UNLOCK(sc);
855702b53ddSSøren Schmidt 
856702b53ddSSøren Schmidt 	if (!(mac_status | mtl_status | dma_status)) {
857702b53ddSSøren Schmidt 		device_printf(sc->dev,
858702b53ddSSøren Schmidt 		    "spurious interrupt mac=%08x mtl=%08x dma=%08x\n",
859702b53ddSSøren Schmidt 		    RD4(sc, GMAC_MAC_INTERRUPT_STATUS),
860702b53ddSSøren Schmidt 		    RD4(sc, GMAC_MTL_INTERRUPT_STATUS),
861702b53ddSSøren Schmidt 		    RD4(sc, GMAC_DMA_CHAN0_STATUS));
862702b53ddSSøren Schmidt 	}
863702b53ddSSøren Schmidt 	if ((rx_tx_status = RD4(sc, GMAC_MAC_RX_TX_STATUS)))
864702b53ddSSøren Schmidt 		device_printf(sc->dev, "RX/TX status interrupt\n");
865702b53ddSSøren Schmidt }
866702b53ddSSøren Schmidt 
867702b53ddSSøren Schmidt static int
868a472fd7aSJustin Hibbits eqos_ioctl(if_t ifp, u_long cmd, caddr_t data)
869702b53ddSSøren Schmidt {
870a472fd7aSJustin Hibbits 	struct eqos_softc *sc = if_getsoftc(ifp);
871702b53ddSSøren Schmidt 	struct ifreq *ifr = (struct ifreq *)data;
872702b53ddSSøren Schmidt 	struct mii_data *mii;
873702b53ddSSøren Schmidt 	int flags, mask;
874702b53ddSSøren Schmidt 	int error = 0;
875702b53ddSSøren Schmidt 
876702b53ddSSøren Schmidt 	switch (cmd) {
877702b53ddSSøren Schmidt 	case SIOCSIFFLAGS:
878702b53ddSSøren Schmidt 		if (if_getflags(ifp) & IFF_UP) {
879702b53ddSSøren Schmidt 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
880702b53ddSSøren Schmidt 				flags = if_getflags(ifp);
881702b53ddSSøren Schmidt 				if ((flags & (IFF_PROMISC|IFF_ALLMULTI))) {
882702b53ddSSøren Schmidt 					EQOS_LOCK(sc);
883702b53ddSSøren Schmidt 					eqos_setup_rxfilter(sc);
884702b53ddSSøren Schmidt 					EQOS_UNLOCK(sc);
885702b53ddSSøren Schmidt 				}
886702b53ddSSøren Schmidt 			}
887702b53ddSSøren Schmidt 			else {
888702b53ddSSøren Schmidt 				eqos_init(sc);
889702b53ddSSøren Schmidt 			}
890702b53ddSSøren Schmidt 		}
891702b53ddSSøren Schmidt 		else {
892702b53ddSSøren Schmidt 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
893702b53ddSSøren Schmidt 				eqos_stop(sc);
894702b53ddSSøren Schmidt 		}
895702b53ddSSøren Schmidt 		break;
896702b53ddSSøren Schmidt 
897702b53ddSSøren Schmidt 	case SIOCADDMULTI:
898702b53ddSSøren Schmidt 	case SIOCDELMULTI:
899702b53ddSSøren Schmidt 		if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
900702b53ddSSøren Schmidt 			EQOS_LOCK(sc);
901702b53ddSSøren Schmidt 			eqos_setup_rxfilter(sc);
902702b53ddSSøren Schmidt 			EQOS_UNLOCK(sc);
903702b53ddSSøren Schmidt 		}
904702b53ddSSøren Schmidt 		break;
905702b53ddSSøren Schmidt 
906702b53ddSSøren Schmidt 	case SIOCSIFMEDIA:
907702b53ddSSøren Schmidt 	case SIOCGIFMEDIA:
908702b53ddSSøren Schmidt 		mii = device_get_softc(sc->miibus);
909702b53ddSSøren Schmidt 		error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
910702b53ddSSøren Schmidt 		break;
911702b53ddSSøren Schmidt 
912702b53ddSSøren Schmidt 	case SIOCSIFCAP:
913702b53ddSSøren Schmidt 		mask = ifr->ifr_reqcap ^ if_getcapenable(ifp);
914702b53ddSSøren Schmidt 		if (mask & IFCAP_VLAN_MTU)
915702b53ddSSøren Schmidt 			if_togglecapenable(ifp, IFCAP_VLAN_MTU);
916702b53ddSSøren Schmidt 		if (mask & IFCAP_RXCSUM)
917702b53ddSSøren Schmidt 			if_togglecapenable(ifp, IFCAP_RXCSUM);
918702b53ddSSøren Schmidt 		if (mask & IFCAP_TXCSUM)
919702b53ddSSøren Schmidt 			if_togglecapenable(ifp, IFCAP_TXCSUM);
920702b53ddSSøren Schmidt 		if ((if_getcapenable(ifp) & IFCAP_TXCSUM))
921702b53ddSSøren Schmidt 			if_sethwassistbits(ifp,
922702b53ddSSøren Schmidt 			    CSUM_IP | CSUM_UDP | CSUM_TCP, 0);
923702b53ddSSøren Schmidt 		else
924702b53ddSSøren Schmidt 			if_sethwassistbits(ifp,
925702b53ddSSøren Schmidt 			    0, CSUM_IP | CSUM_UDP | CSUM_TCP);
926702b53ddSSøren Schmidt 		break;
927702b53ddSSøren Schmidt 
928702b53ddSSøren Schmidt 	default:
929702b53ddSSøren Schmidt 		error = ether_ioctl(ifp, cmd, data);
930702b53ddSSøren Schmidt 		break;
931702b53ddSSøren Schmidt 	}
932702b53ddSSøren Schmidt 
933702b53ddSSøren Schmidt 	return (error);
934702b53ddSSøren Schmidt }
935702b53ddSSøren Schmidt 
936702b53ddSSøren Schmidt static void
937702b53ddSSøren Schmidt eqos_get_eaddr(struct eqos_softc *sc, uint8_t *eaddr)
938702b53ddSSøren Schmidt {
939702b53ddSSøren Schmidt 	uint32_t maclo, machi;
940702b53ddSSøren Schmidt 
941702b53ddSSøren Schmidt 	maclo = htobe32(RD4(sc, GMAC_MAC_ADDRESS0_LOW));
942702b53ddSSøren Schmidt 	machi = htobe16(RD4(sc, GMAC_MAC_ADDRESS0_HIGH) & 0xFFFF);
943702b53ddSSøren Schmidt 
944702b53ddSSøren Schmidt 	/* if no valid MAC address generate random */
945702b53ddSSøren Schmidt 	if (maclo == 0xffffffff && machi == 0xffff) {
946702b53ddSSøren Schmidt 		maclo = 0xf2 | (arc4random() & 0xffff0000);
947702b53ddSSøren Schmidt 		machi = arc4random() & 0x0000ffff;
948702b53ddSSøren Schmidt 	}
949702b53ddSSøren Schmidt 	eaddr[0] = maclo & 0xff;
950702b53ddSSøren Schmidt 	eaddr[1] = (maclo >> 8) & 0xff;
951702b53ddSSøren Schmidt 	eaddr[2] = (maclo >> 16) & 0xff;
952702b53ddSSøren Schmidt 	eaddr[3] = (maclo >> 24) & 0xff;
953702b53ddSSøren Schmidt 	eaddr[4] = machi & 0xff;
954702b53ddSSøren Schmidt 	eaddr[5] = (machi >> 8) & 0xff;
955702b53ddSSøren Schmidt }
956702b53ddSSøren Schmidt 
957702b53ddSSøren Schmidt static void
958702b53ddSSøren Schmidt eqos_axi_configure(struct eqos_softc *sc)
959702b53ddSSøren Schmidt {
960702b53ddSSøren Schmidt 	uint32_t val;
961702b53ddSSøren Schmidt 
962702b53ddSSøren Schmidt 	val = RD4(sc, GMAC_DMA_SYSBUS_MODE);
963702b53ddSSøren Schmidt 
964702b53ddSSøren Schmidt 	/* Max Write Outstanding Req Limit */
965702b53ddSSøren Schmidt 	val &= ~GMAC_DMA_SYSBUS_MODE_WR_OSR_LMT_MASK;
966702b53ddSSøren Schmidt 	val |= 0x03 << GMAC_DMA_SYSBUS_MODE_WR_OSR_LMT_SHIFT;
967702b53ddSSøren Schmidt 
968702b53ddSSøren Schmidt 	/* Max Read Outstanding Req Limit */
969702b53ddSSøren Schmidt 	val &= ~GMAC_DMA_SYSBUS_MODE_RD_OSR_LMT_MASK;
970702b53ddSSøren Schmidt 	val |= 0x07 << GMAC_DMA_SYSBUS_MODE_RD_OSR_LMT_SHIFT;
971702b53ddSSøren Schmidt 
972702b53ddSSøren Schmidt 	/* Allowed Burst Length's */
973702b53ddSSøren Schmidt 	val |= GMAC_DMA_SYSBUS_MODE_BLEN16;
974702b53ddSSøren Schmidt 	val |= GMAC_DMA_SYSBUS_MODE_BLEN8;
975702b53ddSSøren Schmidt 	val |= GMAC_DMA_SYSBUS_MODE_BLEN4;
976702b53ddSSøren Schmidt 
977702b53ddSSøren Schmidt 	/* Fixed Burst Length */
978702b53ddSSøren Schmidt 	val |= GMAC_DMA_SYSBUS_MODE_MB;
979702b53ddSSøren Schmidt 
980702b53ddSSøren Schmidt 	WR4(sc, GMAC_DMA_SYSBUS_MODE, val);
981702b53ddSSøren Schmidt }
982702b53ddSSøren Schmidt 
983702b53ddSSøren Schmidt static void
984702b53ddSSøren Schmidt eqos_get1paddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
985702b53ddSSøren Schmidt {
986702b53ddSSøren Schmidt 
987702b53ddSSøren Schmidt 	if (!error)
988702b53ddSSøren Schmidt 		*(bus_addr_t *)arg = segs[0].ds_addr;
989702b53ddSSøren Schmidt }
990702b53ddSSøren Schmidt 
991702b53ddSSøren Schmidt static int
992702b53ddSSøren Schmidt eqos_setup_dma(struct eqos_softc *sc)
993702b53ddSSøren Schmidt {
994702b53ddSSøren Schmidt 	struct mbuf *m;
995702b53ddSSøren Schmidt 	int error, i;
996702b53ddSSøren Schmidt 
997702b53ddSSøren Schmidt 	/* Set up TX descriptor ring, descriptors, and dma maps */
998702b53ddSSøren Schmidt 	if ((error = bus_dma_tag_create(bus_get_dma_tag(sc->dev),
999702b53ddSSøren Schmidt 					DESC_ALIGN, DESC_BOUNDARY,
1000702b53ddSSøren Schmidt 					BUS_SPACE_MAXADDR_32BIT,
1001702b53ddSSøren Schmidt 					BUS_SPACE_MAXADDR, NULL, NULL,
1002702b53ddSSøren Schmidt 					TX_DESC_SIZE, 1, TX_DESC_SIZE, 0,
1003702b53ddSSøren Schmidt 					NULL, NULL, &sc->tx.desc_tag))) {
1004702b53ddSSøren Schmidt 		device_printf(sc->dev, "could not create TX ring DMA tag\n");
1005702b53ddSSøren Schmidt 		return (error);
1006702b53ddSSøren Schmidt 	}
1007702b53ddSSøren Schmidt 
1008702b53ddSSøren Schmidt 	if ((error = bus_dmamem_alloc(sc->tx.desc_tag,
1009702b53ddSSøren Schmidt 	    (void**)&sc->tx.desc_ring,
1010702b53ddSSøren Schmidt 	    BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO,
1011702b53ddSSøren Schmidt 	    &sc->tx.desc_map))) {
1012702b53ddSSøren Schmidt 		device_printf(sc->dev,
1013702b53ddSSøren Schmidt 		    "could not allocate TX descriptor ring.\n");
1014702b53ddSSøren Schmidt 		return (error);
1015702b53ddSSøren Schmidt 	}
1016702b53ddSSøren Schmidt 
1017702b53ddSSøren Schmidt 	if ((error = bus_dmamap_load(sc->tx.desc_tag, sc->tx.desc_map,
1018702b53ddSSøren Schmidt 	    sc->tx.desc_ring,
1019702b53ddSSøren Schmidt 	    TX_DESC_SIZE, eqos_get1paddr, &sc->tx.desc_ring_paddr, 0))) {
1020702b53ddSSøren Schmidt 		device_printf(sc->dev,
1021702b53ddSSøren Schmidt 		    "could not load TX descriptor ring map.\n");
1022702b53ddSSøren Schmidt 		return (error);
1023702b53ddSSøren Schmidt 	}
1024702b53ddSSøren Schmidt 
1025702b53ddSSøren Schmidt 	if ((error = bus_dma_tag_create(bus_get_dma_tag(sc->dev), 1, 0,
1026702b53ddSSøren Schmidt 					BUS_SPACE_MAXADDR_32BIT,
1027702b53ddSSøren Schmidt 					BUS_SPACE_MAXADDR, NULL, NULL,
1028702b53ddSSøren Schmidt 					MCLBYTES*TX_MAX_SEGS, TX_MAX_SEGS,
1029702b53ddSSøren Schmidt 					MCLBYTES, 0, NULL, NULL,
1030702b53ddSSøren Schmidt 					&sc->tx.buf_tag))) {
1031702b53ddSSøren Schmidt 		device_printf(sc->dev, "could not create TX buffer DMA tag.\n");
1032702b53ddSSøren Schmidt 		return (error);
1033702b53ddSSøren Schmidt 	}
1034702b53ddSSøren Schmidt 
1035702b53ddSSøren Schmidt 	for (i = 0; i < TX_DESC_COUNT; i++) {
1036702b53ddSSøren Schmidt 		if ((error = bus_dmamap_create(sc->tx.buf_tag, BUS_DMA_COHERENT,
1037702b53ddSSøren Schmidt 		    &sc->tx.buf_map[i].map))) {
1038702b53ddSSøren Schmidt 			device_printf(sc->dev, "cannot create TX buffer map\n");
1039702b53ddSSøren Schmidt 			return (error);
1040702b53ddSSøren Schmidt 		}
1041702b53ddSSøren Schmidt 		eqos_setup_txdesc(sc, i, EQOS_TDES3_OWN, 0, 0, 0);
1042702b53ddSSøren Schmidt 	}
1043702b53ddSSøren Schmidt 
1044702b53ddSSøren Schmidt 	/* Set up RX descriptor ring, descriptors, dma maps, and mbufs */
1045702b53ddSSøren Schmidt 	if ((error = bus_dma_tag_create(bus_get_dma_tag(sc->dev),
1046702b53ddSSøren Schmidt 					DESC_ALIGN, DESC_BOUNDARY,
1047702b53ddSSøren Schmidt 					BUS_SPACE_MAXADDR_32BIT,
1048702b53ddSSøren Schmidt 					BUS_SPACE_MAXADDR, NULL, NULL,
1049702b53ddSSøren Schmidt 					RX_DESC_SIZE, 1, RX_DESC_SIZE, 0,
1050702b53ddSSøren Schmidt 					NULL, NULL, &sc->rx.desc_tag))) {
1051702b53ddSSøren Schmidt 		device_printf(sc->dev, "could not create RX ring DMA tag.\n");
1052702b53ddSSøren Schmidt 		return (error);
1053702b53ddSSøren Schmidt 	}
1054702b53ddSSøren Schmidt 
1055702b53ddSSøren Schmidt 	if ((error = bus_dmamem_alloc(sc->rx.desc_tag,
1056702b53ddSSøren Schmidt 	    (void **)&sc->rx.desc_ring,
1057702b53ddSSøren Schmidt 	    BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO,
1058702b53ddSSøren Schmidt 	    &sc->rx.desc_map))) {
1059702b53ddSSøren Schmidt 		device_printf(sc->dev,
1060702b53ddSSøren Schmidt 		    "could not allocate RX descriptor ring.\n");
1061702b53ddSSøren Schmidt 		return (error);
1062702b53ddSSøren Schmidt 	}
1063702b53ddSSøren Schmidt 
1064702b53ddSSøren Schmidt 	if ((error = bus_dmamap_load(sc->rx.desc_tag, sc->rx.desc_map,
1065702b53ddSSøren Schmidt 	    sc->rx.desc_ring, RX_DESC_SIZE, eqos_get1paddr,
1066702b53ddSSøren Schmidt 	    &sc->rx.desc_ring_paddr, 0))) {
1067702b53ddSSøren Schmidt 		device_printf(sc->dev,
1068702b53ddSSøren Schmidt 		    "could not load RX descriptor ring map.\n");
1069702b53ddSSøren Schmidt 		return (error);
1070702b53ddSSøren Schmidt 	}
1071702b53ddSSøren Schmidt 
1072702b53ddSSøren Schmidt 	if ((error = bus_dma_tag_create(bus_get_dma_tag(sc->dev), 1, 0,
1073702b53ddSSøren Schmidt 					BUS_SPACE_MAXADDR_32BIT,
1074702b53ddSSøren Schmidt 					BUS_SPACE_MAXADDR, NULL, NULL,
1075702b53ddSSøren Schmidt 					MCLBYTES, 1,
1076702b53ddSSøren Schmidt 					MCLBYTES, 0, NULL, NULL,
1077702b53ddSSøren Schmidt 					&sc->rx.buf_tag))) {
1078702b53ddSSøren Schmidt 		device_printf(sc->dev, "could not create RX buf DMA tag.\n");
1079702b53ddSSøren Schmidt 		return (error);
1080702b53ddSSøren Schmidt 	}
1081702b53ddSSøren Schmidt 
1082702b53ddSSøren Schmidt 	for (i = 0; i < RX_DESC_COUNT; i++) {
1083702b53ddSSøren Schmidt 		if ((error = bus_dmamap_create(sc->rx.buf_tag, BUS_DMA_COHERENT,
1084702b53ddSSøren Schmidt 		    &sc->rx.buf_map[i].map))) {
1085702b53ddSSøren Schmidt 			device_printf(sc->dev, "cannot create RX buffer map\n");
1086702b53ddSSøren Schmidt 			return (error);
1087702b53ddSSøren Schmidt 		}
1088702b53ddSSøren Schmidt 		if (!(m = eqos_alloc_mbufcl(sc))) {
1089702b53ddSSøren Schmidt 			device_printf(sc->dev, "cannot allocate RX mbuf\n");
1090702b53ddSSøren Schmidt 			return (ENOMEM);
1091702b53ddSSøren Schmidt 		}
1092702b53ddSSøren Schmidt 		if ((error = eqos_setup_rxbuf(sc, i, m))) {
1093702b53ddSSøren Schmidt 			device_printf(sc->dev, "cannot create RX buffer\n");
1094702b53ddSSøren Schmidt 			return (error);
1095702b53ddSSøren Schmidt 		}
1096702b53ddSSøren Schmidt 	}
1097702b53ddSSøren Schmidt 
1098702b53ddSSøren Schmidt 	if (bootverbose)
1099702b53ddSSøren Schmidt 		device_printf(sc->dev, "TX ring @ 0x%lx, RX ring @ 0x%lx\n",
1100702b53ddSSøren Schmidt 		    sc->tx.desc_ring_paddr, sc->rx.desc_ring_paddr);
1101702b53ddSSøren Schmidt 	return (0);
1102702b53ddSSøren Schmidt }
1103702b53ddSSøren Schmidt 
1104702b53ddSSøren Schmidt static int
1105702b53ddSSøren Schmidt eqos_attach(device_t dev)
1106702b53ddSSøren Schmidt {
1107702b53ddSSøren Schmidt 	struct eqos_softc *sc = device_get_softc(dev);
1108a472fd7aSJustin Hibbits 	if_t ifp;
1109702b53ddSSøren Schmidt 	uint32_t ver;
1110702b53ddSSøren Schmidt 	uint8_t eaddr[ETHER_ADDR_LEN];
1111702b53ddSSøren Schmidt 	u_int userver, snpsver;
1112702b53ddSSøren Schmidt 	int error;
1113702b53ddSSøren Schmidt 	int n;
1114702b53ddSSøren Schmidt 
1115702b53ddSSøren Schmidt 	/* setup resources */
1116702b53ddSSøren Schmidt 	if (bus_alloc_resources(dev, eqos_spec, sc->res)) {
1117702b53ddSSøren Schmidt 		device_printf(dev, "Could not allocate resources\n");
1118702b53ddSSøren Schmidt 		bus_release_resources(dev, eqos_spec, sc->res);
1119702b53ddSSøren Schmidt 		return (ENXIO);
1120702b53ddSSøren Schmidt 	}
1121702b53ddSSøren Schmidt 
1122bd077139SEmmanuel Vadot 	if ((error = IF_EQOS_INIT(dev)))
1123bd077139SEmmanuel Vadot 		return (error);
1124bd077139SEmmanuel Vadot 
1125702b53ddSSøren Schmidt 	sc->dev = dev;
1126702b53ddSSøren Schmidt 	ver  = RD4(sc, GMAC_MAC_VERSION);
1127702b53ddSSøren Schmidt 	userver = (ver & GMAC_MAC_VERSION_USERVER_MASK) >>
1128702b53ddSSøren Schmidt 	    GMAC_MAC_VERSION_USERVER_SHIFT;
1129702b53ddSSøren Schmidt 	snpsver = ver & GMAC_MAC_VERSION_SNPSVER_MASK;
1130702b53ddSSøren Schmidt 
1131702b53ddSSøren Schmidt 	if (snpsver != 0x51) {
113296812bd1SEmmanuel Vadot 		device_printf(dev, "EQOS version 0x%02x not supported\n",
1133702b53ddSSøren Schmidt 		    snpsver);
1134702b53ddSSøren Schmidt 		return (ENXIO);
1135702b53ddSSøren Schmidt 	}
1136702b53ddSSøren Schmidt 
1137702b53ddSSøren Schmidt 	for (n = 0; n < 4; n++)
1138702b53ddSSøren Schmidt 		sc->hw_feature[n] = RD4(sc, GMAC_MAC_HW_FEATURE(n));
1139702b53ddSSøren Schmidt 
1140702b53ddSSøren Schmidt 	if (bootverbose) {
1141702b53ddSSøren Schmidt 		device_printf(dev, "DesignWare EQOS ver 0x%02x (0x%02x)\n",
1142702b53ddSSøren Schmidt 		    snpsver, userver);
1143702b53ddSSøren Schmidt 		device_printf(dev, "hw features %08x %08x %08x %08x\n",
1144702b53ddSSøren Schmidt 		    sc->hw_feature[0], sc->hw_feature[1],
1145702b53ddSSøren Schmidt 		    sc->hw_feature[2], sc->hw_feature[3]);
1146702b53ddSSøren Schmidt 	}
1147702b53ddSSøren Schmidt 
1148702b53ddSSøren Schmidt 	mtx_init(&sc->lock, "eqos lock", MTX_NETWORK_LOCK, MTX_DEF);
1149702b53ddSSøren Schmidt 	callout_init_mtx(&sc->callout, &sc->lock, 0);
1150702b53ddSSøren Schmidt 
1151702b53ddSSøren Schmidt 	eqos_get_eaddr(sc, eaddr);
1152702b53ddSSøren Schmidt 	if (bootverbose)
1153702b53ddSSøren Schmidt 		device_printf(sc->dev, "Ethernet address %6D\n", eaddr, ":");
1154702b53ddSSøren Schmidt 
1155702b53ddSSøren Schmidt 	/* Soft reset EMAC core */
1156702b53ddSSøren Schmidt 	if ((error = eqos_reset(sc))) {
1157702b53ddSSøren Schmidt 		device_printf(sc->dev, "reset timeout!\n");
1158702b53ddSSøren Schmidt 		return (error);
1159702b53ddSSøren Schmidt 	}
1160702b53ddSSøren Schmidt 
1161702b53ddSSøren Schmidt 	/* Configure AXI Bus mode parameters */
1162702b53ddSSøren Schmidt 	eqos_axi_configure(sc);
1163702b53ddSSøren Schmidt 
1164702b53ddSSøren Schmidt 	/* Setup DMA descriptors */
1165702b53ddSSøren Schmidt 	if (eqos_setup_dma(sc)) {
1166702b53ddSSøren Schmidt 		device_printf(sc->dev, "failed to setup DMA descriptors\n");
1167702b53ddSSøren Schmidt 		return (EINVAL);
1168702b53ddSSøren Schmidt 	}
1169702b53ddSSøren Schmidt 
1170702b53ddSSøren Schmidt 	/* setup interrupt delivery */
1171702b53ddSSøren Schmidt 	if ((bus_setup_intr(dev, sc->res[EQOS_RES_IRQ0], EQOS_INTR_FLAGS,
1172702b53ddSSøren Schmidt 	    NULL, eqos_intr, sc, &sc->irq_handle))) {
1173702b53ddSSøren Schmidt 		device_printf(dev, "unable to setup 1st interrupt\n");
1174702b53ddSSøren Schmidt 		bus_release_resources(dev, eqos_spec, sc->res);
1175702b53ddSSøren Schmidt 		return (ENXIO);
1176702b53ddSSøren Schmidt 	}
1177702b53ddSSøren Schmidt 
1178702b53ddSSøren Schmidt 	/* Setup ethernet interface */
1179702b53ddSSøren Schmidt 	ifp = sc->ifp = if_alloc(IFT_ETHER);
1180a472fd7aSJustin Hibbits 	if_setsoftc(ifp, sc);
1181702b53ddSSøren Schmidt 	if_initname(ifp, device_get_name(sc->dev), device_get_unit(sc->dev));
1182702b53ddSSøren Schmidt 	if_setflags(sc->ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
1183702b53ddSSøren Schmidt 	if_setstartfn(ifp, eqos_start);
1184702b53ddSSøren Schmidt 	if_setioctlfn(ifp, eqos_ioctl);
1185702b53ddSSøren Schmidt 	if_setinitfn(ifp, eqos_init);
1186702b53ddSSøren Schmidt 	if_setsendqlen(ifp, TX_DESC_COUNT - 1);
1187702b53ddSSøren Schmidt 	if_setsendqready(ifp);
1188702b53ddSSøren Schmidt 	if_setcapabilities(ifp, IFCAP_VLAN_MTU /*| IFCAP_HWCSUM*/);
1189702b53ddSSøren Schmidt 	if_setcapenable(ifp, if_getcapabilities(ifp));
1190702b53ddSSøren Schmidt 
1191702b53ddSSøren Schmidt 	/* Attach MII driver */
1192702b53ddSSøren Schmidt 	if ((error = mii_attach(sc->dev, &sc->miibus, ifp, eqos_media_change,
1193702b53ddSSøren Schmidt 	    eqos_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY,
1194702b53ddSSøren Schmidt 	    MII_OFFSET_ANY, 0))) {
1195702b53ddSSøren Schmidt 		device_printf(sc->dev, "PHY attach failed\n");
1196702b53ddSSøren Schmidt 		return (ENXIO);
1197702b53ddSSøren Schmidt 	}
1198702b53ddSSøren Schmidt 
1199702b53ddSSøren Schmidt 	/* Attach ethernet interface */
1200702b53ddSSøren Schmidt 	ether_ifattach(ifp, eaddr);
1201702b53ddSSøren Schmidt 
1202702b53ddSSøren Schmidt 	return (0);
1203702b53ddSSøren Schmidt }
1204702b53ddSSøren Schmidt 
1205702b53ddSSøren Schmidt static int
1206702b53ddSSøren Schmidt eqos_detach(device_t dev)
1207702b53ddSSøren Schmidt {
1208702b53ddSSøren Schmidt 	struct eqos_softc *sc = device_get_softc(dev);
1209702b53ddSSøren Schmidt 	int i;
1210702b53ddSSøren Schmidt 
1211702b53ddSSøren Schmidt 	if (device_is_attached(dev)) {
1212702b53ddSSøren Schmidt 		EQOS_LOCK(sc);
1213702b53ddSSøren Schmidt 		eqos_stop(sc);
1214702b53ddSSøren Schmidt 		EQOS_UNLOCK(sc);
1215a472fd7aSJustin Hibbits 		if_setflagbits(sc->ifp, 0, IFF_UP);
1216702b53ddSSøren Schmidt 		ether_ifdetach(sc->ifp);
1217702b53ddSSøren Schmidt 	}
1218702b53ddSSøren Schmidt 
1219702b53ddSSøren Schmidt 	bus_generic_detach(dev);
1220702b53ddSSøren Schmidt 
1221702b53ddSSøren Schmidt 	if (sc->irq_handle)
1222702b53ddSSøren Schmidt 		bus_teardown_intr(dev, sc->res[EQOS_RES_IRQ0],
1223702b53ddSSøren Schmidt 		    sc->irq_handle);
1224702b53ddSSøren Schmidt 
1225702b53ddSSøren Schmidt 	if (sc->ifp)
1226702b53ddSSøren Schmidt 		if_free(sc->ifp);
1227702b53ddSSøren Schmidt 
1228702b53ddSSøren Schmidt 	bus_release_resources(dev, eqos_spec, sc->res);
1229702b53ddSSøren Schmidt 
1230702b53ddSSøren Schmidt 	if (sc->tx.desc_tag) {
1231702b53ddSSøren Schmidt 		if (sc->tx.desc_map) {
1232702b53ddSSøren Schmidt 			bus_dmamap_unload(sc->tx.desc_tag, sc->tx.desc_map);
1233702b53ddSSøren Schmidt 			bus_dmamem_free(sc->tx.desc_tag, sc->tx.desc_ring,
1234702b53ddSSøren Schmidt 			    sc->tx.desc_map);
1235702b53ddSSøren Schmidt 		}
1236702b53ddSSøren Schmidt 		bus_dma_tag_destroy(sc->tx.desc_tag);
1237702b53ddSSøren Schmidt 	}
1238702b53ddSSøren Schmidt 	if (sc->tx.buf_tag) {
1239702b53ddSSøren Schmidt 		for (i = 0; i < TX_DESC_COUNT; i++) {
1240702b53ddSSøren Schmidt 			m_free(sc->tx.buf_map[i].mbuf);
1241702b53ddSSøren Schmidt 			bus_dmamap_destroy(sc->tx.buf_tag,
1242702b53ddSSøren Schmidt 			    sc->tx.buf_map[i].map);
1243702b53ddSSøren Schmidt 		}
1244702b53ddSSøren Schmidt 		bus_dma_tag_destroy(sc->tx.buf_tag);
1245702b53ddSSøren Schmidt 	}
1246702b53ddSSøren Schmidt 
1247702b53ddSSøren Schmidt 	if (sc->rx.desc_tag) {
1248702b53ddSSøren Schmidt 		if (sc->rx.desc_map) {
1249702b53ddSSøren Schmidt 			bus_dmamap_unload(sc->rx.desc_tag, sc->rx.desc_map);
1250702b53ddSSøren Schmidt 			bus_dmamem_free(sc->rx.desc_tag, sc->rx.desc_ring,
1251702b53ddSSøren Schmidt 			    sc->rx.desc_map);
1252702b53ddSSøren Schmidt 		}
1253702b53ddSSøren Schmidt 		bus_dma_tag_destroy(sc->rx.desc_tag);
1254702b53ddSSøren Schmidt 	}
1255702b53ddSSøren Schmidt 	if (sc->rx.buf_tag) {
1256702b53ddSSøren Schmidt 		for (i = 0; i < RX_DESC_COUNT; i++) {
1257702b53ddSSøren Schmidt 			m_free(sc->rx.buf_map[i].mbuf);
1258702b53ddSSøren Schmidt 			bus_dmamap_destroy(sc->rx.buf_tag,
1259702b53ddSSøren Schmidt 			    sc->rx.buf_map[i].map);
1260702b53ddSSøren Schmidt 		}
1261702b53ddSSøren Schmidt 		bus_dma_tag_destroy(sc->rx.buf_tag);
1262702b53ddSSøren Schmidt 	}
1263702b53ddSSøren Schmidt 
1264702b53ddSSøren Schmidt 	mtx_destroy(&sc->lock);
1265702b53ddSSøren Schmidt 
1266702b53ddSSøren Schmidt 	return (0);
1267702b53ddSSøren Schmidt }
1268702b53ddSSøren Schmidt 
1269702b53ddSSøren Schmidt 
1270702b53ddSSøren Schmidt static device_method_t eqos_methods[] = {
1271702b53ddSSøren Schmidt 	/* Device Interface */
1272702b53ddSSøren Schmidt 	DEVMETHOD(device_attach,	eqos_attach),
1273702b53ddSSøren Schmidt 	DEVMETHOD(device_detach,	eqos_detach),
1274702b53ddSSøren Schmidt 
1275702b53ddSSøren Schmidt 	/* MII Interface */
1276702b53ddSSøren Schmidt 	DEVMETHOD(miibus_readreg,	eqos_miibus_readreg),
1277702b53ddSSøren Schmidt 	DEVMETHOD(miibus_writereg,	eqos_miibus_writereg),
1278702b53ddSSøren Schmidt 	DEVMETHOD(miibus_statchg,	eqos_miibus_statchg),
1279702b53ddSSøren Schmidt 
1280702b53ddSSøren Schmidt 	DEVMETHOD_END
1281702b53ddSSøren Schmidt };
1282702b53ddSSøren Schmidt 
1283702b53ddSSøren Schmidt driver_t eqos_driver = {
1284702b53ddSSøren Schmidt 	"eqos",
1285702b53ddSSøren Schmidt 	eqos_methods,
1286702b53ddSSøren Schmidt 	sizeof(struct eqos_softc),
1287702b53ddSSøren Schmidt };
1288702b53ddSSøren Schmidt 
1289702b53ddSSøren Schmidt DRIVER_MODULE(miibus, eqos, miibus_driver, 0, 0);
1290