xref: /openbsd-src/sys/dev/ic/dwqe.c (revision 84a278ff9748658ca1a944cc42f86f046094a7ec)
1*84a278ffSstsp /*	$OpenBSD: dwqe.c,v 1.22 2024/06/05 10:19:55 stsp Exp $	*/
2305ac5f9Spatrick /*
3305ac5f9Spatrick  * Copyright (c) 2008, 2019 Mark Kettenis <kettenis@openbsd.org>
4305ac5f9Spatrick  * Copyright (c) 2017, 2022 Patrick Wildt <patrick@blueri.se>
5305ac5f9Spatrick  *
6305ac5f9Spatrick  * Permission to use, copy, modify, and distribute this software for any
7305ac5f9Spatrick  * purpose with or without fee is hereby granted, provided that the above
8305ac5f9Spatrick  * copyright notice and this permission notice appear in all copies.
9305ac5f9Spatrick  *
10305ac5f9Spatrick  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11305ac5f9Spatrick  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12305ac5f9Spatrick  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13305ac5f9Spatrick  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14305ac5f9Spatrick  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15305ac5f9Spatrick  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16305ac5f9Spatrick  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17305ac5f9Spatrick  */
18305ac5f9Spatrick 
19305ac5f9Spatrick /*
20305ac5f9Spatrick  * Driver for the Synopsys Designware ethernet controller.
21305ac5f9Spatrick  */
22305ac5f9Spatrick 
23305ac5f9Spatrick #include "bpfilter.h"
24*84a278ffSstsp #include "vlan.h"
25305ac5f9Spatrick 
26305ac5f9Spatrick #include <sys/param.h>
27305ac5f9Spatrick #include <sys/systm.h>
28305ac5f9Spatrick #include <sys/device.h>
29305ac5f9Spatrick #include <sys/kernel.h>
30305ac5f9Spatrick #include <sys/malloc.h>
31305ac5f9Spatrick #include <sys/mbuf.h>
32305ac5f9Spatrick #include <sys/queue.h>
33305ac5f9Spatrick #include <sys/socket.h>
34305ac5f9Spatrick #include <sys/sockio.h>
35305ac5f9Spatrick #include <sys/timeout.h>
36305ac5f9Spatrick 
37305ac5f9Spatrick #include <machine/bus.h>
38305ac5f9Spatrick 
39305ac5f9Spatrick #include <net/if.h>
40305ac5f9Spatrick #include <net/if_media.h>
41305ac5f9Spatrick 
42305ac5f9Spatrick #include <dev/mii/mii.h>
43305ac5f9Spatrick #include <dev/mii/miivar.h>
44305ac5f9Spatrick 
45305ac5f9Spatrick #if NBPFILTER > 0
46305ac5f9Spatrick #include <net/bpf.h>
47305ac5f9Spatrick #endif
48305ac5f9Spatrick 
49305ac5f9Spatrick #include <netinet/in.h>
50305ac5f9Spatrick #include <netinet/if_ether.h>
51305ac5f9Spatrick 
52305ac5f9Spatrick #include <dev/ic/dwqevar.h>
53305ac5f9Spatrick #include <dev/ic/dwqereg.h>
54305ac5f9Spatrick 
55305ac5f9Spatrick struct cfdriver dwqe_cd = {
56305ac5f9Spatrick 	NULL, "dwqe", DV_IFNET
57305ac5f9Spatrick };
58305ac5f9Spatrick 
59305ac5f9Spatrick uint32_t dwqe_read(struct dwqe_softc *, bus_addr_t);
60305ac5f9Spatrick void	dwqe_write(struct dwqe_softc *, bus_addr_t, uint32_t);
61305ac5f9Spatrick 
62305ac5f9Spatrick int	dwqe_ioctl(struct ifnet *, u_long, caddr_t);
63305ac5f9Spatrick void	dwqe_start(struct ifqueue *);
64305ac5f9Spatrick void	dwqe_watchdog(struct ifnet *);
65305ac5f9Spatrick 
66305ac5f9Spatrick int	dwqe_media_change(struct ifnet *);
67305ac5f9Spatrick void	dwqe_media_status(struct ifnet *, struct ifmediareq *);
68305ac5f9Spatrick 
6945f5f3c8Sdlg void	dwqe_mii_attach(struct dwqe_softc *);
70305ac5f9Spatrick int	dwqe_mii_readreg(struct device *, int, int);
71305ac5f9Spatrick void	dwqe_mii_writereg(struct device *, int, int, int);
72305ac5f9Spatrick void	dwqe_mii_statchg(struct device *);
73305ac5f9Spatrick 
74305ac5f9Spatrick void	dwqe_lladdr_read(struct dwqe_softc *, uint8_t *);
75305ac5f9Spatrick void	dwqe_lladdr_write(struct dwqe_softc *);
76305ac5f9Spatrick 
77305ac5f9Spatrick void	dwqe_tick(void *);
78305ac5f9Spatrick void	dwqe_rxtick(void *);
79305ac5f9Spatrick 
80305ac5f9Spatrick int	dwqe_intr(void *);
81305ac5f9Spatrick void	dwqe_tx_proc(struct dwqe_softc *);
82305ac5f9Spatrick void	dwqe_rx_proc(struct dwqe_softc *);
83305ac5f9Spatrick 
84305ac5f9Spatrick void	dwqe_up(struct dwqe_softc *);
85305ac5f9Spatrick void	dwqe_down(struct dwqe_softc *);
86305ac5f9Spatrick void	dwqe_iff(struct dwqe_softc *);
87305ac5f9Spatrick int	dwqe_encap(struct dwqe_softc *, struct mbuf *, int *, int *);
88305ac5f9Spatrick 
89305ac5f9Spatrick void	dwqe_reset(struct dwqe_softc *);
90305ac5f9Spatrick 
91305ac5f9Spatrick struct dwqe_dmamem *
92305ac5f9Spatrick 	dwqe_dmamem_alloc(struct dwqe_softc *, bus_size_t, bus_size_t);
93305ac5f9Spatrick void	dwqe_dmamem_free(struct dwqe_softc *, struct dwqe_dmamem *);
94305ac5f9Spatrick struct mbuf *dwqe_alloc_mbuf(struct dwqe_softc *, bus_dmamap_t);
95305ac5f9Spatrick void	dwqe_fill_rx_ring(struct dwqe_softc *);
96305ac5f9Spatrick 
97305ac5f9Spatrick int
dwqe_have_tx_csum_offload(struct dwqe_softc * sc)9871083937Sstsp dwqe_have_tx_csum_offload(struct dwqe_softc *sc)
9971083937Sstsp {
10071083937Sstsp 	return (sc->sc_hw_feature[0] & GMAC_MAC_HW_FEATURE0_TXCOESEL);
10171083937Sstsp }
10271083937Sstsp 
10371083937Sstsp int
dwqe_have_tx_vlan_offload(struct dwqe_softc * sc)104*84a278ffSstsp dwqe_have_tx_vlan_offload(struct dwqe_softc *sc)
105*84a278ffSstsp {
106*84a278ffSstsp #if NVLAN > 0
107*84a278ffSstsp 	return (sc->sc_hw_feature[0] & GMAC_MAC_HW_FEATURE0_SAVLANINS);
108*84a278ffSstsp #else
109*84a278ffSstsp 	return 0;
110*84a278ffSstsp #endif
111*84a278ffSstsp }
112*84a278ffSstsp 
113*84a278ffSstsp void
dwqe_set_vlan_rx_mode(struct dwqe_softc * sc)114*84a278ffSstsp dwqe_set_vlan_rx_mode(struct dwqe_softc *sc)
115*84a278ffSstsp {
116*84a278ffSstsp #if NVLAN > 0
117*84a278ffSstsp 	uint32_t reg;
118*84a278ffSstsp 
119*84a278ffSstsp 	/* Enable outer VLAN tag stripping on Rx. */
120*84a278ffSstsp 	reg = dwqe_read(sc, GMAC_VLAN_TAG_CTRL);
121*84a278ffSstsp 	reg |= GMAC_VLAN_TAG_CTRL_EVLRXS | GMAC_VLAN_TAG_CTRL_STRIP_ALWAYS;
122*84a278ffSstsp 	dwqe_write(sc, GMAC_VLAN_TAG_CTRL, reg);
123*84a278ffSstsp #endif
124*84a278ffSstsp }
125*84a278ffSstsp 
126*84a278ffSstsp void
dwqe_set_vlan_tx_mode(struct dwqe_softc * sc)127*84a278ffSstsp dwqe_set_vlan_tx_mode(struct dwqe_softc *sc)
128*84a278ffSstsp {
129*84a278ffSstsp #if NVLAN > 0
130*84a278ffSstsp 	uint32_t reg;
131*84a278ffSstsp 
132*84a278ffSstsp 	reg = dwqe_read(sc, GMAC_VLAN_TAG_INCL);
133*84a278ffSstsp 
134*84a278ffSstsp 	/* Enable insertion of outer VLAN tag. */
135*84a278ffSstsp 	reg |= GMAC_VLAN_TAG_INCL_INSERT;
136*84a278ffSstsp 
137*84a278ffSstsp 	/*
138*84a278ffSstsp 	 * Generate C-VLAN tags (type 0x8100, 802.1Q). Setting this
139*84a278ffSstsp 	 * bit would result in S-VLAN tags (type 0x88A8, 802.1ad).
140*84a278ffSstsp 	 */
141*84a278ffSstsp 	reg &= ~GMAC_VLAN_TAG_INCL_CSVL;
142*84a278ffSstsp 
143*84a278ffSstsp 	/* Use VLAN tags provided in Tx context descriptors. */
144*84a278ffSstsp 	reg |= GMAC_VLAN_TAG_INCL_VLTI;
145*84a278ffSstsp 
146*84a278ffSstsp 	dwqe_write(sc, GMAC_VLAN_TAG_INCL, reg);
147*84a278ffSstsp #endif
148*84a278ffSstsp }
149*84a278ffSstsp 
150*84a278ffSstsp int
dwqe_attach(struct dwqe_softc * sc)151305ac5f9Spatrick dwqe_attach(struct dwqe_softc *sc)
152305ac5f9Spatrick {
153305ac5f9Spatrick 	struct ifnet *ifp;
154305ac5f9Spatrick 	uint32_t version, mode;
155305ac5f9Spatrick 	int i;
156305ac5f9Spatrick 
157305ac5f9Spatrick 	version = dwqe_read(sc, GMAC_VERSION);
1584fa84378Skettenis 	printf(": rev 0x%02x, address %s\n", version & GMAC_VERSION_SNPS_MASK,
159305ac5f9Spatrick 	    ether_sprintf(sc->sc_lladdr));
160305ac5f9Spatrick 
161305ac5f9Spatrick 	for (i = 0; i < 4; i++)
162305ac5f9Spatrick 		sc->sc_hw_feature[i] = dwqe_read(sc, GMAC_MAC_HW_FEATURE(i));
163305ac5f9Spatrick 
16447707f8eSdlg 	timeout_set(&sc->sc_phy_tick, dwqe_tick, sc);
165305ac5f9Spatrick 	timeout_set(&sc->sc_rxto, dwqe_rxtick, sc);
166305ac5f9Spatrick 
167305ac5f9Spatrick 	ifp = &sc->sc_ac.ac_if;
168305ac5f9Spatrick 	ifp->if_softc = sc;
169305ac5f9Spatrick 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
170305ac5f9Spatrick 	ifp->if_xflags = IFXF_MPSAFE;
171305ac5f9Spatrick 	ifp->if_ioctl = dwqe_ioctl;
172305ac5f9Spatrick 	ifp->if_qstart = dwqe_start;
173305ac5f9Spatrick 	ifp->if_watchdog = dwqe_watchdog;
174cf96265bSbluhm 	ifq_init_maxlen(&ifp->if_snd, DWQE_NTXDESC - 1);
175305ac5f9Spatrick 	bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
176305ac5f9Spatrick 
177305ac5f9Spatrick 	ifp->if_capabilities = IFCAP_VLAN_MTU;
178*84a278ffSstsp 	if (dwqe_have_tx_vlan_offload(sc))
179*84a278ffSstsp 		ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING;
18071083937Sstsp 	if (dwqe_have_tx_csum_offload(sc)) {
18171083937Sstsp 		ifp->if_capabilities |= (IFCAP_CSUM_IPv4 |
18271083937Sstsp 		    IFCAP_CSUM_TCPv4 | IFCAP_CSUM_UDPv4 |
18371083937Sstsp 		    IFCAP_CSUM_TCPv6 | IFCAP_CSUM_UDPv6);
18471083937Sstsp 	}
185305ac5f9Spatrick 
186305ac5f9Spatrick 	sc->sc_mii.mii_ifp = ifp;
187305ac5f9Spatrick 	sc->sc_mii.mii_readreg = dwqe_mii_readreg;
188305ac5f9Spatrick 	sc->sc_mii.mii_writereg = dwqe_mii_writereg;
189305ac5f9Spatrick 	sc->sc_mii.mii_statchg = dwqe_mii_statchg;
190305ac5f9Spatrick 
191305ac5f9Spatrick 	ifmedia_init(&sc->sc_media, 0, dwqe_media_change, dwqe_media_status);
192305ac5f9Spatrick 
193305ac5f9Spatrick 	dwqe_reset(sc);
194305ac5f9Spatrick 
195305ac5f9Spatrick 	/* Configure DMA engine. */
196305ac5f9Spatrick 	mode = dwqe_read(sc, GMAC_SYS_BUS_MODE);
197305ac5f9Spatrick 	if (sc->sc_fixed_burst)
198305ac5f9Spatrick 		mode |= GMAC_SYS_BUS_MODE_FB;
199305ac5f9Spatrick 	if (sc->sc_mixed_burst)
200305ac5f9Spatrick 		mode |= GMAC_SYS_BUS_MODE_MB;
201305ac5f9Spatrick 	if (sc->sc_aal)
202305ac5f9Spatrick 		mode |= GMAC_SYS_BUS_MODE_AAL;
203305ac5f9Spatrick 	dwqe_write(sc, GMAC_SYS_BUS_MODE, mode);
204305ac5f9Spatrick 
205305ac5f9Spatrick 	/* Configure channel 0. */
206305ac5f9Spatrick 	mode = dwqe_read(sc, GMAC_CHAN_CONTROL(0));
207305ac5f9Spatrick 	if (sc->sc_8xpbl)
208305ac5f9Spatrick 		mode |= GMAC_CHAN_CONTROL_8XPBL;
209305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_CONTROL(0), mode);
210305ac5f9Spatrick 
211305ac5f9Spatrick 	mode = dwqe_read(sc, GMAC_CHAN_TX_CONTROL(0));
212305ac5f9Spatrick 	mode &= ~GMAC_CHAN_TX_CONTROL_PBL_MASK;
213305ac5f9Spatrick 	mode |= sc->sc_txpbl << GMAC_CHAN_TX_CONTROL_PBL_SHIFT;
214305ac5f9Spatrick 	mode |= GMAC_CHAN_TX_CONTROL_OSP;
215305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_TX_CONTROL(0), mode);
216f6ce44eeSjsg 	mode = dwqe_read(sc, GMAC_CHAN_RX_CONTROL(0));
217305ac5f9Spatrick 	mode &= ~GMAC_CHAN_RX_CONTROL_RPBL_MASK;
218305ac5f9Spatrick 	mode |= sc->sc_rxpbl << GMAC_CHAN_RX_CONTROL_RPBL_SHIFT;
219305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_RX_CONTROL(0), mode);
220305ac5f9Spatrick 
221305ac5f9Spatrick 	/* Configure AXI master. */
222305ac5f9Spatrick 	if (sc->sc_axi_config) {
223305ac5f9Spatrick 		int i;
224305ac5f9Spatrick 
225305ac5f9Spatrick 		mode = dwqe_read(sc, GMAC_SYS_BUS_MODE);
226305ac5f9Spatrick 
227305ac5f9Spatrick 		mode &= ~GMAC_SYS_BUS_MODE_EN_LPI;
228305ac5f9Spatrick 		if (sc->sc_lpi_en)
229305ac5f9Spatrick 			mode |= GMAC_SYS_BUS_MODE_EN_LPI;
230305ac5f9Spatrick 		mode &= ~GMAC_SYS_BUS_MODE_LPI_XIT_FRM;
231305ac5f9Spatrick 		if (sc->sc_xit_frm)
232305ac5f9Spatrick 			mode |= GMAC_SYS_BUS_MODE_LPI_XIT_FRM;
233305ac5f9Spatrick 
234305ac5f9Spatrick 		mode &= ~GMAC_SYS_BUS_MODE_WR_OSR_LMT_MASK;
235305ac5f9Spatrick 		mode |= (sc->sc_wr_osr_lmt << GMAC_SYS_BUS_MODE_WR_OSR_LMT_SHIFT);
236305ac5f9Spatrick 		mode &= ~GMAC_SYS_BUS_MODE_RD_OSR_LMT_MASK;
237305ac5f9Spatrick 		mode |= (sc->sc_rd_osr_lmt << GMAC_SYS_BUS_MODE_RD_OSR_LMT_SHIFT);
238305ac5f9Spatrick 
239305ac5f9Spatrick 		for (i = 0; i < nitems(sc->sc_blen); i++) {
240305ac5f9Spatrick 			switch (sc->sc_blen[i]) {
241305ac5f9Spatrick 			case 256:
242305ac5f9Spatrick 				mode |= GMAC_SYS_BUS_MODE_BLEN_256;
243305ac5f9Spatrick 				break;
244305ac5f9Spatrick 			case 128:
245305ac5f9Spatrick 				mode |= GMAC_SYS_BUS_MODE_BLEN_128;
246305ac5f9Spatrick 				break;
247305ac5f9Spatrick 			case 64:
248305ac5f9Spatrick 				mode |= GMAC_SYS_BUS_MODE_BLEN_64;
249305ac5f9Spatrick 				break;
250305ac5f9Spatrick 			case 32:
251305ac5f9Spatrick 				mode |= GMAC_SYS_BUS_MODE_BLEN_32;
252305ac5f9Spatrick 				break;
253305ac5f9Spatrick 			case 16:
254305ac5f9Spatrick 				mode |= GMAC_SYS_BUS_MODE_BLEN_16;
255305ac5f9Spatrick 				break;
256305ac5f9Spatrick 			case 8:
257305ac5f9Spatrick 				mode |= GMAC_SYS_BUS_MODE_BLEN_8;
258305ac5f9Spatrick 				break;
259305ac5f9Spatrick 			case 4:
260305ac5f9Spatrick 				mode |= GMAC_SYS_BUS_MODE_BLEN_4;
261305ac5f9Spatrick 				break;
262305ac5f9Spatrick 			}
263305ac5f9Spatrick 		}
264305ac5f9Spatrick 
265305ac5f9Spatrick 		dwqe_write(sc, GMAC_SYS_BUS_MODE, mode);
266305ac5f9Spatrick 	}
267305ac5f9Spatrick 
26845f5f3c8Sdlg 	if (!sc->sc_fixed_link)
26945f5f3c8Sdlg 		dwqe_mii_attach(sc);
27045f5f3c8Sdlg 
271*84a278ffSstsp 	/*
272*84a278ffSstsp 	 * All devices support VLAN tag stripping on Rx but inserting
273*84a278ffSstsp 	 * VLAN tags during Tx is an optional feature.
274*84a278ffSstsp 	 */
275*84a278ffSstsp 	dwqe_set_vlan_rx_mode(sc);
276*84a278ffSstsp 	if (ifp->if_capabilities & IFCAP_VLAN_HWTAGGING)
277*84a278ffSstsp 		dwqe_set_vlan_tx_mode(sc);
278*84a278ffSstsp 
27945f5f3c8Sdlg 	if_attach(ifp);
28045f5f3c8Sdlg 	ether_ifattach(ifp);
28145f5f3c8Sdlg 
28245f5f3c8Sdlg 	/* Disable interrupts. */
28345f5f3c8Sdlg 	dwqe_write(sc, GMAC_INT_EN, 0);
28445f5f3c8Sdlg 	dwqe_write(sc, GMAC_CHAN_INTR_ENA(0), 0);
28564bdd17fSjmatthew 	dwqe_write(sc, GMAC_MMC_RX_INT_MASK, 0xffffffff);
28664bdd17fSjmatthew 	dwqe_write(sc, GMAC_MMC_TX_INT_MASK, 0xffffffff);
28745f5f3c8Sdlg 
28845f5f3c8Sdlg 	return 0;
28945f5f3c8Sdlg }
29045f5f3c8Sdlg 
29145f5f3c8Sdlg void
dwqe_mii_attach(struct dwqe_softc * sc)29245f5f3c8Sdlg dwqe_mii_attach(struct dwqe_softc *sc)
29345f5f3c8Sdlg {
29445f5f3c8Sdlg 	int mii_flags = 0;
29545f5f3c8Sdlg 
296e9f49f11Skettenis 	switch (sc->sc_phy_mode) {
297e9f49f11Skettenis 	case DWQE_PHY_MODE_RGMII:
298e9f49f11Skettenis 		mii_flags |= MIIF_SETDELAY;
299e9f49f11Skettenis 		break;
300e9f49f11Skettenis 	case DWQE_PHY_MODE_RGMII_ID:
301e9f49f11Skettenis 		mii_flags |= MIIF_SETDELAY | MIIF_RXID | MIIF_TXID;
302e9f49f11Skettenis 		break;
303e9f49f11Skettenis 	case DWQE_PHY_MODE_RGMII_RXID:
304e9f49f11Skettenis 		mii_flags |= MIIF_SETDELAY | MIIF_RXID;
305e9f49f11Skettenis 		break;
306e9f49f11Skettenis 	case DWQE_PHY_MODE_RGMII_TXID:
307e9f49f11Skettenis 		mii_flags |= MIIF_SETDELAY | MIIF_TXID;
308e9f49f11Skettenis 		break;
309e9f49f11Skettenis 	default:
310e9f49f11Skettenis 		break;
311e9f49f11Skettenis 	}
312e9f49f11Skettenis 
313305ac5f9Spatrick 	mii_attach(&sc->sc_dev, &sc->sc_mii, 0xffffffff, sc->sc_phyloc,
314e9f49f11Skettenis 	    (sc->sc_phyloc == MII_PHY_ANY) ? 0 : MII_OFFSET_ANY, mii_flags);
315305ac5f9Spatrick 	if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) {
316305ac5f9Spatrick 		printf("%s: no PHY found!\n", sc->sc_dev.dv_xname);
317305ac5f9Spatrick 		ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_MANUAL, 0, NULL);
318305ac5f9Spatrick 		ifmedia_set(&sc->sc_media, IFM_ETHER|IFM_MANUAL);
319305ac5f9Spatrick 	} else
320305ac5f9Spatrick 		ifmedia_set(&sc->sc_media, IFM_ETHER|IFM_AUTO);
321305ac5f9Spatrick }
322305ac5f9Spatrick 
323305ac5f9Spatrick uint32_t
dwqe_read(struct dwqe_softc * sc,bus_addr_t addr)324305ac5f9Spatrick dwqe_read(struct dwqe_softc *sc, bus_addr_t addr)
325305ac5f9Spatrick {
326305ac5f9Spatrick 	return bus_space_read_4(sc->sc_iot, sc->sc_ioh, addr);
327305ac5f9Spatrick }
328305ac5f9Spatrick 
329305ac5f9Spatrick void
dwqe_write(struct dwqe_softc * sc,bus_addr_t addr,uint32_t data)330305ac5f9Spatrick dwqe_write(struct dwqe_softc *sc, bus_addr_t addr, uint32_t data)
331305ac5f9Spatrick {
332305ac5f9Spatrick 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, addr, data);
333305ac5f9Spatrick }
334305ac5f9Spatrick 
335305ac5f9Spatrick void
dwqe_lladdr_read(struct dwqe_softc * sc,uint8_t * lladdr)336305ac5f9Spatrick dwqe_lladdr_read(struct dwqe_softc *sc, uint8_t *lladdr)
337305ac5f9Spatrick {
338305ac5f9Spatrick 	uint32_t machi, maclo;
339305ac5f9Spatrick 
340305ac5f9Spatrick 	machi = dwqe_read(sc, GMAC_MAC_ADDR0_HI);
341305ac5f9Spatrick 	maclo = dwqe_read(sc, GMAC_MAC_ADDR0_LO);
342305ac5f9Spatrick 
343305ac5f9Spatrick 	if (machi || maclo) {
344305ac5f9Spatrick 		lladdr[0] = (maclo >> 0) & 0xff;
345305ac5f9Spatrick 		lladdr[1] = (maclo >> 8) & 0xff;
346305ac5f9Spatrick 		lladdr[2] = (maclo >> 16) & 0xff;
347305ac5f9Spatrick 		lladdr[3] = (maclo >> 24) & 0xff;
348305ac5f9Spatrick 		lladdr[4] = (machi >> 0) & 0xff;
349305ac5f9Spatrick 		lladdr[5] = (machi >> 8) & 0xff;
350305ac5f9Spatrick 	} else {
351305ac5f9Spatrick 		ether_fakeaddr(&sc->sc_ac.ac_if);
352305ac5f9Spatrick 	}
353305ac5f9Spatrick }
354305ac5f9Spatrick 
355305ac5f9Spatrick void
dwqe_lladdr_write(struct dwqe_softc * sc)356305ac5f9Spatrick dwqe_lladdr_write(struct dwqe_softc *sc)
357305ac5f9Spatrick {
358305ac5f9Spatrick 	dwqe_write(sc, GMAC_MAC_ADDR0_HI,
359305ac5f9Spatrick 	    sc->sc_lladdr[5] << 8 | sc->sc_lladdr[4] << 0);
360305ac5f9Spatrick 	dwqe_write(sc, GMAC_MAC_ADDR0_LO,
361305ac5f9Spatrick 	    sc->sc_lladdr[3] << 24 | sc->sc_lladdr[2] << 16 |
362305ac5f9Spatrick 	    sc->sc_lladdr[1] << 8 | sc->sc_lladdr[0] << 0);
363305ac5f9Spatrick }
364305ac5f9Spatrick 
365305ac5f9Spatrick void
dwqe_start(struct ifqueue * ifq)366305ac5f9Spatrick dwqe_start(struct ifqueue *ifq)
367305ac5f9Spatrick {
368305ac5f9Spatrick 	struct ifnet *ifp = ifq->ifq_if;
369305ac5f9Spatrick 	struct dwqe_softc *sc = ifp->if_softc;
370305ac5f9Spatrick 	struct mbuf *m;
371305ac5f9Spatrick 	int error, idx, left, used;
372305ac5f9Spatrick 
373305ac5f9Spatrick 	if (!(ifp->if_flags & IFF_RUNNING))
374305ac5f9Spatrick 		return;
375305ac5f9Spatrick 	if (ifq_is_oactive(&ifp->if_snd))
376305ac5f9Spatrick 		return;
377305ac5f9Spatrick 	if (ifq_empty(&ifp->if_snd))
378305ac5f9Spatrick 		return;
379305ac5f9Spatrick 	if (!sc->sc_link)
380305ac5f9Spatrick 		return;
381305ac5f9Spatrick 
382305ac5f9Spatrick 	idx = sc->sc_tx_prod;
383305ac5f9Spatrick 	left = sc->sc_tx_cons;
384305ac5f9Spatrick 	if (left <= idx)
385305ac5f9Spatrick 		left += DWQE_NTXDESC;
386305ac5f9Spatrick 	left -= idx;
387305ac5f9Spatrick 	used = 0;
388305ac5f9Spatrick 
389305ac5f9Spatrick 	for (;;) {
390*84a278ffSstsp 		/* VLAN tags require an extra Tx context descriptor. */
391*84a278ffSstsp 		if (used + DWQE_NTXSEGS + 2 > left) {
392305ac5f9Spatrick 			ifq_set_oactive(ifq);
393305ac5f9Spatrick 			break;
394305ac5f9Spatrick 		}
395305ac5f9Spatrick 
396305ac5f9Spatrick 		m = ifq_dequeue(ifq);
397305ac5f9Spatrick 		if (m == NULL)
398305ac5f9Spatrick 			break;
399305ac5f9Spatrick 
400305ac5f9Spatrick 		error = dwqe_encap(sc, m, &idx, &used);
401305ac5f9Spatrick 		if (error == EFBIG) {
402305ac5f9Spatrick 			m_freem(m); /* give up: drop it */
403305ac5f9Spatrick 			ifp->if_oerrors++;
404305ac5f9Spatrick 			continue;
405305ac5f9Spatrick 		}
406305ac5f9Spatrick 
407305ac5f9Spatrick #if NBPFILTER > 0
408305ac5f9Spatrick 		if (ifp->if_bpf)
409305ac5f9Spatrick 			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
410305ac5f9Spatrick #endif
411305ac5f9Spatrick 	}
412305ac5f9Spatrick 
413555d2e79Suwe 	if (used > 0) {
414305ac5f9Spatrick 		sc->sc_tx_prod = idx;
415305ac5f9Spatrick 
416305ac5f9Spatrick 		/* Set a timeout in case the chip goes out to lunch. */
417305ac5f9Spatrick 		ifp->if_timer = 5;
418555d2e79Suwe 
419555d2e79Suwe 		/*
420555d2e79Suwe 		 * Start the transmit process after the last in-use Tx
421555d2e79Suwe 		 * descriptor's OWN bit has been updated.
422555d2e79Suwe 		 */
423555d2e79Suwe 		dwqe_write(sc, GMAC_CHAN_TX_END_ADDR(0), DWQE_DMA_DVA(sc->sc_txring) +
424555d2e79Suwe 		    idx * sizeof(struct dwqe_desc));
425305ac5f9Spatrick 	}
426305ac5f9Spatrick }
427305ac5f9Spatrick 
428305ac5f9Spatrick int
dwqe_ioctl(struct ifnet * ifp,u_long cmd,caddr_t addr)429305ac5f9Spatrick dwqe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr)
430305ac5f9Spatrick {
431305ac5f9Spatrick 	struct dwqe_softc *sc = ifp->if_softc;
432305ac5f9Spatrick 	struct ifreq *ifr = (struct ifreq *)addr;
433305ac5f9Spatrick 	int error = 0, s;
434305ac5f9Spatrick 
435305ac5f9Spatrick 	s = splnet();
436305ac5f9Spatrick 
437305ac5f9Spatrick 	switch (cmd) {
438305ac5f9Spatrick 	case SIOCSIFADDR:
439305ac5f9Spatrick 		ifp->if_flags |= IFF_UP;
440305ac5f9Spatrick 		/* FALLTHROUGH */
441305ac5f9Spatrick 	case SIOCSIFFLAGS:
442305ac5f9Spatrick 		if (ifp->if_flags & IFF_UP) {
443305ac5f9Spatrick 			if (ifp->if_flags & IFF_RUNNING)
444305ac5f9Spatrick 				error = ENETRESET;
445305ac5f9Spatrick 			else
446305ac5f9Spatrick 				dwqe_up(sc);
447305ac5f9Spatrick 		} else {
448305ac5f9Spatrick 			if (ifp->if_flags & IFF_RUNNING)
449305ac5f9Spatrick 				dwqe_down(sc);
450305ac5f9Spatrick 		}
451305ac5f9Spatrick 		break;
452305ac5f9Spatrick 
453305ac5f9Spatrick 	case SIOCGIFMEDIA:
454305ac5f9Spatrick 	case SIOCSIFMEDIA:
45545f5f3c8Sdlg 		if (sc->sc_fixed_link)
45645f5f3c8Sdlg 			error = ENOTTY;
45745f5f3c8Sdlg 		else
458305ac5f9Spatrick 			error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
459305ac5f9Spatrick 		break;
460305ac5f9Spatrick 
461305ac5f9Spatrick 	case SIOCGIFRXR:
462305ac5f9Spatrick 		error = if_rxr_ioctl((struct if_rxrinfo *)ifr->ifr_data,
463305ac5f9Spatrick 		    NULL, MCLBYTES, &sc->sc_rx_ring);
464305ac5f9Spatrick 		break;
465305ac5f9Spatrick 
466305ac5f9Spatrick 	default:
467305ac5f9Spatrick 		error = ether_ioctl(ifp, &sc->sc_ac, cmd, addr);
468305ac5f9Spatrick 		break;
469305ac5f9Spatrick 	}
470305ac5f9Spatrick 
471305ac5f9Spatrick 	if (error == ENETRESET) {
472305ac5f9Spatrick 		if (ifp->if_flags & IFF_RUNNING)
473305ac5f9Spatrick 			dwqe_iff(sc);
474305ac5f9Spatrick 		error = 0;
475305ac5f9Spatrick 	}
476305ac5f9Spatrick 
477305ac5f9Spatrick 	splx(s);
478305ac5f9Spatrick 	return (error);
479305ac5f9Spatrick }
480305ac5f9Spatrick 
481305ac5f9Spatrick void
dwqe_watchdog(struct ifnet * ifp)482305ac5f9Spatrick dwqe_watchdog(struct ifnet *ifp)
483305ac5f9Spatrick {
484305ac5f9Spatrick 	printf("%s\n", __func__);
485305ac5f9Spatrick }
486305ac5f9Spatrick 
487305ac5f9Spatrick int
dwqe_media_change(struct ifnet * ifp)488305ac5f9Spatrick dwqe_media_change(struct ifnet *ifp)
489305ac5f9Spatrick {
490305ac5f9Spatrick 	struct dwqe_softc *sc = ifp->if_softc;
491305ac5f9Spatrick 
492305ac5f9Spatrick 	if (LIST_FIRST(&sc->sc_mii.mii_phys))
493305ac5f9Spatrick 		mii_mediachg(&sc->sc_mii);
494305ac5f9Spatrick 
495305ac5f9Spatrick 	return (0);
496305ac5f9Spatrick }
497305ac5f9Spatrick 
498305ac5f9Spatrick void
dwqe_media_status(struct ifnet * ifp,struct ifmediareq * ifmr)499305ac5f9Spatrick dwqe_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
500305ac5f9Spatrick {
501305ac5f9Spatrick 	struct dwqe_softc *sc = ifp->if_softc;
502305ac5f9Spatrick 
503305ac5f9Spatrick 	if (LIST_FIRST(&sc->sc_mii.mii_phys)) {
504305ac5f9Spatrick 		mii_pollstat(&sc->sc_mii);
505305ac5f9Spatrick 		ifmr->ifm_active = sc->sc_mii.mii_media_active;
506305ac5f9Spatrick 		ifmr->ifm_status = sc->sc_mii.mii_media_status;
507305ac5f9Spatrick 	}
508305ac5f9Spatrick }
509305ac5f9Spatrick 
510305ac5f9Spatrick int
dwqe_mii_readreg(struct device * self,int phy,int reg)511305ac5f9Spatrick dwqe_mii_readreg(struct device *self, int phy, int reg)
512305ac5f9Spatrick {
513305ac5f9Spatrick 	struct dwqe_softc *sc = (void *)self;
514305ac5f9Spatrick 	int n;
515305ac5f9Spatrick 
516305ac5f9Spatrick 	dwqe_write(sc, GMAC_MAC_MDIO_ADDR,
5170738886aSkettenis 	    (sc->sc_clk << GMAC_MAC_MDIO_ADDR_CR_SHIFT) |
518305ac5f9Spatrick 	    (phy << GMAC_MAC_MDIO_ADDR_PA_SHIFT) |
519305ac5f9Spatrick 	    (reg << GMAC_MAC_MDIO_ADDR_RDA_SHIFT) |
520305ac5f9Spatrick 	    GMAC_MAC_MDIO_ADDR_GOC_READ |
521305ac5f9Spatrick 	    GMAC_MAC_MDIO_ADDR_GB);
522305ac5f9Spatrick 
5239fb70083Sdlg 	for (n = 0; n < 2000; n++) {
5249fb70083Sdlg 		delay(10);
525305ac5f9Spatrick 		if ((dwqe_read(sc, GMAC_MAC_MDIO_ADDR) & GMAC_MAC_MDIO_ADDR_GB) == 0)
526305ac5f9Spatrick 			return dwqe_read(sc, GMAC_MAC_MDIO_DATA);
527305ac5f9Spatrick 	}
528305ac5f9Spatrick 
529305ac5f9Spatrick 	printf("%s: mii_read timeout\n", sc->sc_dev.dv_xname);
530305ac5f9Spatrick 	return (0);
531305ac5f9Spatrick }
532305ac5f9Spatrick 
533305ac5f9Spatrick void
dwqe_mii_writereg(struct device * self,int phy,int reg,int val)534305ac5f9Spatrick dwqe_mii_writereg(struct device *self, int phy, int reg, int val)
535305ac5f9Spatrick {
536305ac5f9Spatrick 	struct dwqe_softc *sc = (void *)self;
537305ac5f9Spatrick 	int n;
538305ac5f9Spatrick 
539305ac5f9Spatrick 	dwqe_write(sc, GMAC_MAC_MDIO_DATA, val);
540305ac5f9Spatrick 	dwqe_write(sc, GMAC_MAC_MDIO_ADDR,
5410738886aSkettenis 	    (sc->sc_clk << GMAC_MAC_MDIO_ADDR_CR_SHIFT) |
542305ac5f9Spatrick 	    (phy << GMAC_MAC_MDIO_ADDR_PA_SHIFT) |
543305ac5f9Spatrick 	    (reg << GMAC_MAC_MDIO_ADDR_RDA_SHIFT) |
544305ac5f9Spatrick 	    GMAC_MAC_MDIO_ADDR_GOC_WRITE |
545305ac5f9Spatrick 	    GMAC_MAC_MDIO_ADDR_GB);
5469fb70083Sdlg 
5479fb70083Sdlg 	for (n = 0; n < 2000; n++) {
5489fb70083Sdlg 		delay(10);
549305ac5f9Spatrick 		if ((dwqe_read(sc, GMAC_MAC_MDIO_ADDR) & GMAC_MAC_MDIO_ADDR_GB) == 0)
550305ac5f9Spatrick 			return;
551305ac5f9Spatrick 	}
552305ac5f9Spatrick 
553305ac5f9Spatrick 	printf("%s: mii_write timeout\n", sc->sc_dev.dv_xname);
554305ac5f9Spatrick }
555305ac5f9Spatrick 
556305ac5f9Spatrick void
dwqe_mii_statchg(struct device * self)557305ac5f9Spatrick dwqe_mii_statchg(struct device *self)
558305ac5f9Spatrick {
559305ac5f9Spatrick 	struct dwqe_softc *sc = (void *)self;
560566e78fcSdlg 	struct ifnet *ifp = &sc->sc_ac.ac_if;
561305ac5f9Spatrick 	uint32_t conf;
562305ac5f9Spatrick 
563305ac5f9Spatrick 	conf = dwqe_read(sc, GMAC_MAC_CONF);
564305ac5f9Spatrick 	conf &= ~(GMAC_MAC_CONF_PS | GMAC_MAC_CONF_FES);
565305ac5f9Spatrick 
566566e78fcSdlg 	switch (ifp->if_baudrate) {
567566e78fcSdlg 	case IF_Mbps(1000):
568305ac5f9Spatrick 		sc->sc_link = 1;
569305ac5f9Spatrick 		break;
570566e78fcSdlg 	case IF_Mbps(100):
571305ac5f9Spatrick 		conf |= GMAC_MAC_CONF_PS | GMAC_MAC_CONF_FES;
572305ac5f9Spatrick 		sc->sc_link = 1;
573305ac5f9Spatrick 		break;
574566e78fcSdlg 	case IF_Mbps(10):
575305ac5f9Spatrick 		conf |= GMAC_MAC_CONF_PS;
576305ac5f9Spatrick 		sc->sc_link = 1;
577305ac5f9Spatrick 		break;
578305ac5f9Spatrick 	default:
579305ac5f9Spatrick 		sc->sc_link = 0;
580305ac5f9Spatrick 		return;
581305ac5f9Spatrick 	}
582305ac5f9Spatrick 
583305ac5f9Spatrick 	if (sc->sc_link == 0)
584305ac5f9Spatrick 		return;
585305ac5f9Spatrick 
586305ac5f9Spatrick 	conf &= ~GMAC_MAC_CONF_DM;
587566e78fcSdlg 	if (ifp->if_link_state == LINK_STATE_FULL_DUPLEX)
588305ac5f9Spatrick 		conf |= GMAC_MAC_CONF_DM;
589305ac5f9Spatrick 
590305ac5f9Spatrick 	dwqe_write(sc, GMAC_MAC_CONF, conf);
591305ac5f9Spatrick }
592305ac5f9Spatrick 
593305ac5f9Spatrick void
dwqe_tick(void * arg)594305ac5f9Spatrick dwqe_tick(void *arg)
595305ac5f9Spatrick {
596305ac5f9Spatrick 	struct dwqe_softc *sc = arg;
597305ac5f9Spatrick 	int s;
598305ac5f9Spatrick 
599305ac5f9Spatrick 	s = splnet();
600305ac5f9Spatrick 	mii_tick(&sc->sc_mii);
601305ac5f9Spatrick 	splx(s);
602305ac5f9Spatrick 
60347707f8eSdlg 	timeout_add_sec(&sc->sc_phy_tick, 1);
604305ac5f9Spatrick }
605305ac5f9Spatrick 
606305ac5f9Spatrick void
dwqe_rxtick(void * arg)607305ac5f9Spatrick dwqe_rxtick(void *arg)
608305ac5f9Spatrick {
609305ac5f9Spatrick 	struct dwqe_softc *sc = arg;
610305ac5f9Spatrick 	int s;
611305ac5f9Spatrick 
612305ac5f9Spatrick 	s = splnet();
613305ac5f9Spatrick 
614305ac5f9Spatrick 	/* TODO: disable RXQ? */
615305ac5f9Spatrick 	printf("%s:%d\n", __func__, __LINE__);
616305ac5f9Spatrick 
617305ac5f9Spatrick 	bus_dmamap_sync(sc->sc_dmat, DWQE_DMA_MAP(sc->sc_rxring),
618305ac5f9Spatrick 	    0, DWQE_DMA_LEN(sc->sc_rxring),
619305ac5f9Spatrick 	    BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
620305ac5f9Spatrick 
621305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_RX_BASE_ADDR_HI(0), 0);
622305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_RX_BASE_ADDR(0), 0);
623305ac5f9Spatrick 
624305ac5f9Spatrick 	sc->sc_rx_prod = sc->sc_rx_cons = 0;
625305ac5f9Spatrick 	dwqe_fill_rx_ring(sc);
626305ac5f9Spatrick 
627305ac5f9Spatrick 	bus_dmamap_sync(sc->sc_dmat, DWQE_DMA_MAP(sc->sc_rxring),
628305ac5f9Spatrick 	    0, DWQE_DMA_LEN(sc->sc_rxring),
629305ac5f9Spatrick 	    BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
630305ac5f9Spatrick 
631305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_RX_BASE_ADDR_HI(0), DWQE_DMA_DVA(sc->sc_rxring) >> 32);
632305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_RX_BASE_ADDR(0), DWQE_DMA_DVA(sc->sc_rxring));
633305ac5f9Spatrick 
634305ac5f9Spatrick 	/* TODO: re-enable RXQ? */
635305ac5f9Spatrick 
636305ac5f9Spatrick 	splx(s);
637305ac5f9Spatrick }
638305ac5f9Spatrick 
639305ac5f9Spatrick int
dwqe_intr(void * arg)640305ac5f9Spatrick dwqe_intr(void *arg)
641305ac5f9Spatrick {
642305ac5f9Spatrick 	struct dwqe_softc *sc = arg;
643305ac5f9Spatrick 	uint32_t reg;
644305ac5f9Spatrick 
645305ac5f9Spatrick 	reg = dwqe_read(sc, GMAC_INT_STATUS);
646305ac5f9Spatrick 	dwqe_write(sc, GMAC_INT_STATUS, reg);
647305ac5f9Spatrick 
648305ac5f9Spatrick 	reg = dwqe_read(sc, GMAC_CHAN_STATUS(0));
649305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_STATUS(0), reg);
650305ac5f9Spatrick 
651305ac5f9Spatrick 	if (reg & GMAC_CHAN_STATUS_RI)
652305ac5f9Spatrick 		dwqe_rx_proc(sc);
653305ac5f9Spatrick 
654305ac5f9Spatrick 	if (reg & GMAC_CHAN_STATUS_TI)
655305ac5f9Spatrick 		dwqe_tx_proc(sc);
656305ac5f9Spatrick 
657305ac5f9Spatrick 	return (1);
658305ac5f9Spatrick }
659305ac5f9Spatrick 
660305ac5f9Spatrick void
dwqe_tx_proc(struct dwqe_softc * sc)661305ac5f9Spatrick dwqe_tx_proc(struct dwqe_softc *sc)
662305ac5f9Spatrick {
663305ac5f9Spatrick 	struct ifnet *ifp = &sc->sc_ac.ac_if;
664305ac5f9Spatrick 	struct dwqe_desc *txd;
665305ac5f9Spatrick 	struct dwqe_buf *txb;
666305ac5f9Spatrick 	int idx, txfree;
667305ac5f9Spatrick 
6685b86e6aeSstsp 	if ((ifp->if_flags & IFF_RUNNING) == 0)
6695b86e6aeSstsp 		return;
6705b86e6aeSstsp 
671305ac5f9Spatrick 	bus_dmamap_sync(sc->sc_dmat, DWQE_DMA_MAP(sc->sc_txring), 0,
672305ac5f9Spatrick 	    DWQE_DMA_LEN(sc->sc_txring),
673305ac5f9Spatrick 	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
674305ac5f9Spatrick 
675305ac5f9Spatrick 	txfree = 0;
676305ac5f9Spatrick 	while (sc->sc_tx_cons != sc->sc_tx_prod) {
677305ac5f9Spatrick 		idx = sc->sc_tx_cons;
678305ac5f9Spatrick 		KASSERT(idx < DWQE_NTXDESC);
679305ac5f9Spatrick 
680305ac5f9Spatrick 		txd = &sc->sc_txdesc[idx];
681305ac5f9Spatrick 		if (txd->sd_tdes3 & TDES3_OWN)
682305ac5f9Spatrick 			break;
683305ac5f9Spatrick 
684e8974f33Skettenis 		if (txd->sd_tdes3 & TDES3_ES)
685e8974f33Skettenis 			ifp->if_oerrors++;
686e8974f33Skettenis 
687305ac5f9Spatrick 		txb = &sc->sc_txbuf[idx];
688305ac5f9Spatrick 		if (txb->tb_m) {
689305ac5f9Spatrick 			bus_dmamap_sync(sc->sc_dmat, txb->tb_map, 0,
690305ac5f9Spatrick 			    txb->tb_map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
691305ac5f9Spatrick 			bus_dmamap_unload(sc->sc_dmat, txb->tb_map);
692305ac5f9Spatrick 
693305ac5f9Spatrick 			m_freem(txb->tb_m);
694305ac5f9Spatrick 			txb->tb_m = NULL;
695305ac5f9Spatrick 		}
696305ac5f9Spatrick 
697305ac5f9Spatrick 		txfree++;
698305ac5f9Spatrick 
699305ac5f9Spatrick 		if (sc->sc_tx_cons == (DWQE_NTXDESC - 1))
700305ac5f9Spatrick 			sc->sc_tx_cons = 0;
701305ac5f9Spatrick 		else
702305ac5f9Spatrick 			sc->sc_tx_cons++;
703305ac5f9Spatrick 
704305ac5f9Spatrick 		txd->sd_tdes3 = 0;
705305ac5f9Spatrick 	}
706305ac5f9Spatrick 
707305ac5f9Spatrick 	if (sc->sc_tx_cons == sc->sc_tx_prod)
708305ac5f9Spatrick 		ifp->if_timer = 0;
709305ac5f9Spatrick 
710305ac5f9Spatrick 	if (txfree) {
711305ac5f9Spatrick 		if (ifq_is_oactive(&ifp->if_snd))
712305ac5f9Spatrick 			ifq_restart(&ifp->if_snd);
713305ac5f9Spatrick 	}
714305ac5f9Spatrick }
715305ac5f9Spatrick 
716a72369d8Sstsp int
dwqe_have_rx_csum_offload(struct dwqe_softc * sc)717a72369d8Sstsp dwqe_have_rx_csum_offload(struct dwqe_softc *sc)
718a72369d8Sstsp {
719a72369d8Sstsp 	return (sc->sc_hw_feature[0] & GMAC_MAC_HW_FEATURE0_RXCOESEL);
720a72369d8Sstsp }
721a72369d8Sstsp 
722a72369d8Sstsp void
dwqe_rx_csum(struct dwqe_softc * sc,struct mbuf * m,struct dwqe_desc * rxd)723a72369d8Sstsp dwqe_rx_csum(struct dwqe_softc *sc, struct mbuf *m, struct dwqe_desc *rxd)
724a72369d8Sstsp {
725a72369d8Sstsp 	uint16_t csum_flags = 0;
726a72369d8Sstsp 
727a72369d8Sstsp 	/*
728a72369d8Sstsp 	 * Checksum offload must be supported, the Last-Descriptor bit
729a72369d8Sstsp 	 * must be set, RDES1 must be valid, and checksumming must not
730a72369d8Sstsp 	 * have been bypassed (happens for unknown packet types), and
731a72369d8Sstsp 	 * an IP header must have been detected.
732a72369d8Sstsp 	 */
733a72369d8Sstsp 	if (!dwqe_have_rx_csum_offload(sc) ||
734a72369d8Sstsp 	    (rxd->sd_tdes3 & RDES3_LD) == 0 ||
735a72369d8Sstsp 	    (rxd->sd_tdes3 & RDES3_RDES1_VALID) == 0 ||
736a72369d8Sstsp 	    (rxd->sd_tdes1 & RDES1_IP_CSUM_BYPASS) ||
737a72369d8Sstsp 	    (rxd->sd_tdes1 & (RDES1_IPV4_HDR | RDES1_IPV6_HDR)) == 0)
738a72369d8Sstsp 		return;
739a72369d8Sstsp 
740a72369d8Sstsp 	/* If the IP header checksum is invalid then the payload is ignored. */
741a72369d8Sstsp 	if (rxd->sd_tdes1 & RDES1_IP_HDR_ERROR) {
742a72369d8Sstsp 		if (rxd->sd_tdes1 & RDES1_IPV4_HDR)
743a72369d8Sstsp 			csum_flags |= M_IPV4_CSUM_IN_BAD;
744a72369d8Sstsp 	} else {
745a72369d8Sstsp 		if (rxd->sd_tdes1 & RDES1_IPV4_HDR)
746a72369d8Sstsp 			csum_flags |= M_IPV4_CSUM_IN_OK;
747a72369d8Sstsp 
748a72369d8Sstsp 		/* Detect payload type and corresponding checksum errors. */
749a72369d8Sstsp 		switch (rxd->sd_tdes1 & RDES1_IP_PAYLOAD_TYPE) {
750a72369d8Sstsp 		case RDES1_IP_PAYLOAD_UDP:
751a72369d8Sstsp 			if (rxd->sd_tdes1 & RDES1_IP_PAYLOAD_ERROR)
752a72369d8Sstsp 				csum_flags |= M_UDP_CSUM_IN_BAD;
753a72369d8Sstsp 			else
754a72369d8Sstsp 				csum_flags |= M_UDP_CSUM_IN_OK;
755a72369d8Sstsp 			break;
756a72369d8Sstsp 		case RDES1_IP_PAYLOAD_TCP:
757a72369d8Sstsp 			if (rxd->sd_tdes1 & RDES1_IP_PAYLOAD_ERROR)
758a72369d8Sstsp 				csum_flags |= M_TCP_CSUM_IN_BAD;
759a72369d8Sstsp 			else
760a72369d8Sstsp 				csum_flags |= M_TCP_CSUM_IN_OK;
761a72369d8Sstsp 			break;
762a72369d8Sstsp 		case RDES1_IP_PAYLOAD_ICMP:
763a72369d8Sstsp 			if (rxd->sd_tdes1 & RDES1_IP_PAYLOAD_ERROR)
764a72369d8Sstsp 				csum_flags |= M_ICMP_CSUM_IN_BAD;
765a72369d8Sstsp 			else
766a72369d8Sstsp 				csum_flags |= M_ICMP_CSUM_IN_OK;
767a72369d8Sstsp 			break;
768a72369d8Sstsp 		default:
769a72369d8Sstsp 			break;
770a72369d8Sstsp 		}
771a72369d8Sstsp 	}
772a72369d8Sstsp 
773a72369d8Sstsp 	m->m_pkthdr.csum_flags |= csum_flags;
774a72369d8Sstsp }
775a72369d8Sstsp 
776305ac5f9Spatrick void
dwqe_vlan_strip(struct dwqe_softc * sc,struct mbuf * m,struct dwqe_desc * rxd)777*84a278ffSstsp dwqe_vlan_strip(struct dwqe_softc *sc, struct mbuf *m, struct dwqe_desc *rxd)
778*84a278ffSstsp {
779*84a278ffSstsp #if NVLAN > 0
780*84a278ffSstsp 	uint16_t tag;
781*84a278ffSstsp 
782*84a278ffSstsp 	if ((rxd->sd_tdes3 & RDES3_RDES0_VALID) &&
783*84a278ffSstsp 	    (rxd->sd_tdes3 & RDES3_LD)) {
784*84a278ffSstsp 		tag = rxd->sd_tdes0 & RDES0_OVT;
785*84a278ffSstsp 		m->m_pkthdr.ether_vtag = le16toh(tag);
786*84a278ffSstsp 		m->m_flags |= M_VLANTAG;
787*84a278ffSstsp 	}
788*84a278ffSstsp #endif
789*84a278ffSstsp }
790*84a278ffSstsp 
791*84a278ffSstsp void
dwqe_rx_proc(struct dwqe_softc * sc)792305ac5f9Spatrick dwqe_rx_proc(struct dwqe_softc *sc)
793305ac5f9Spatrick {
794305ac5f9Spatrick 	struct ifnet *ifp = &sc->sc_ac.ac_if;
795305ac5f9Spatrick 	struct dwqe_desc *rxd;
796305ac5f9Spatrick 	struct dwqe_buf *rxb;
797305ac5f9Spatrick 	struct mbuf_list ml = MBUF_LIST_INITIALIZER();
798305ac5f9Spatrick 	struct mbuf *m;
799305ac5f9Spatrick 	int idx, len, cnt, put;
800305ac5f9Spatrick 
801305ac5f9Spatrick 	if ((ifp->if_flags & IFF_RUNNING) == 0)
802305ac5f9Spatrick 		return;
803305ac5f9Spatrick 
804305ac5f9Spatrick 	bus_dmamap_sync(sc->sc_dmat, DWQE_DMA_MAP(sc->sc_rxring), 0,
805305ac5f9Spatrick 	    DWQE_DMA_LEN(sc->sc_rxring),
806305ac5f9Spatrick 	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
807305ac5f9Spatrick 
808305ac5f9Spatrick 	cnt = if_rxr_inuse(&sc->sc_rx_ring);
809305ac5f9Spatrick 	put = 0;
810305ac5f9Spatrick 	while (put < cnt) {
811305ac5f9Spatrick 		idx = sc->sc_rx_cons;
812305ac5f9Spatrick 		KASSERT(idx < DWQE_NRXDESC);
813305ac5f9Spatrick 
814305ac5f9Spatrick 		rxd = &sc->sc_rxdesc[idx];
815305ac5f9Spatrick 		if (rxd->sd_tdes3 & RDES3_OWN)
816305ac5f9Spatrick 			break;
817305ac5f9Spatrick 
818305ac5f9Spatrick 		len = rxd->sd_tdes3 & RDES3_LENGTH;
819305ac5f9Spatrick 		rxb = &sc->sc_rxbuf[idx];
820305ac5f9Spatrick 		KASSERT(rxb->tb_m);
821305ac5f9Spatrick 
822305ac5f9Spatrick 		bus_dmamap_sync(sc->sc_dmat, rxb->tb_map, 0,
823305ac5f9Spatrick 		    len, BUS_DMASYNC_POSTREAD);
824305ac5f9Spatrick 		bus_dmamap_unload(sc->sc_dmat, rxb->tb_map);
825305ac5f9Spatrick 
826b58ef082Skettenis 		m = rxb->tb_m;
827b58ef082Skettenis 		rxb->tb_m = NULL;
828b58ef082Skettenis 
829b58ef082Skettenis 		if (rxd->sd_tdes3 & RDES3_ES) {
830b58ef082Skettenis 			ifp->if_ierrors++;
831b58ef082Skettenis 			m_freem(m);
832b58ef082Skettenis 		} else {
833305ac5f9Spatrick 			/* Strip off CRC. */
834305ac5f9Spatrick 			len -= ETHER_CRC_LEN;
835305ac5f9Spatrick 			KASSERT(len > 0);
836305ac5f9Spatrick 
837305ac5f9Spatrick 			m->m_pkthdr.len = m->m_len = len;
838305ac5f9Spatrick 
839a72369d8Sstsp 			dwqe_rx_csum(sc, m, rxd);
840*84a278ffSstsp 			dwqe_vlan_strip(sc, m, rxd);
841305ac5f9Spatrick 			ml_enqueue(&ml, m);
842b58ef082Skettenis 		}
843305ac5f9Spatrick 
844305ac5f9Spatrick 		put++;
845305ac5f9Spatrick 		if (sc->sc_rx_cons == (DWQE_NRXDESC - 1))
846305ac5f9Spatrick 			sc->sc_rx_cons = 0;
847305ac5f9Spatrick 		else
848305ac5f9Spatrick 			sc->sc_rx_cons++;
849305ac5f9Spatrick 	}
850305ac5f9Spatrick 
851305ac5f9Spatrick 	if_rxr_put(&sc->sc_rx_ring, put);
852305ac5f9Spatrick 	if (ifiq_input(&ifp->if_rcv, &ml))
853305ac5f9Spatrick 		if_rxr_livelocked(&sc->sc_rx_ring);
854305ac5f9Spatrick 
855305ac5f9Spatrick 	dwqe_fill_rx_ring(sc);
856305ac5f9Spatrick 
857305ac5f9Spatrick 	bus_dmamap_sync(sc->sc_dmat, DWQE_DMA_MAP(sc->sc_rxring), 0,
858305ac5f9Spatrick 	    DWQE_DMA_LEN(sc->sc_rxring),
859305ac5f9Spatrick 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
860305ac5f9Spatrick }
861305ac5f9Spatrick 
862305ac5f9Spatrick void
dwqe_up(struct dwqe_softc * sc)863305ac5f9Spatrick dwqe_up(struct dwqe_softc *sc)
864305ac5f9Spatrick {
865305ac5f9Spatrick 	struct ifnet *ifp = &sc->sc_ac.ac_if;
866305ac5f9Spatrick 	struct dwqe_buf *txb, *rxb;
8676e9149a4Sstsp 	uint32_t mode, reg, fifosz, tqs, rqs;
868305ac5f9Spatrick 	int i;
869305ac5f9Spatrick 
870305ac5f9Spatrick 	/* Allocate Tx descriptor ring. */
871305ac5f9Spatrick 	sc->sc_txring = dwqe_dmamem_alloc(sc,
872305ac5f9Spatrick 	    DWQE_NTXDESC * sizeof(struct dwqe_desc), 8);
873305ac5f9Spatrick 	sc->sc_txdesc = DWQE_DMA_KVA(sc->sc_txring);
874305ac5f9Spatrick 
875305ac5f9Spatrick 	sc->sc_txbuf = malloc(sizeof(struct dwqe_buf) * DWQE_NTXDESC,
876305ac5f9Spatrick 	    M_DEVBUF, M_WAITOK);
877305ac5f9Spatrick 	for (i = 0; i < DWQE_NTXDESC; i++) {
878305ac5f9Spatrick 		txb = &sc->sc_txbuf[i];
879305ac5f9Spatrick 		bus_dmamap_create(sc->sc_dmat, MCLBYTES, DWQE_NTXSEGS,
880305ac5f9Spatrick 		    MCLBYTES, 0, BUS_DMA_WAITOK, &txb->tb_map);
881305ac5f9Spatrick 		txb->tb_m = NULL;
882305ac5f9Spatrick 	}
883305ac5f9Spatrick 
884305ac5f9Spatrick 	bus_dmamap_sync(sc->sc_dmat, DWQE_DMA_MAP(sc->sc_txring),
885305ac5f9Spatrick 	    0, DWQE_DMA_LEN(sc->sc_txring), BUS_DMASYNC_PREWRITE);
886305ac5f9Spatrick 
887305ac5f9Spatrick 	sc->sc_tx_prod = sc->sc_tx_cons = 0;
888305ac5f9Spatrick 
889305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_TX_BASE_ADDR_HI(0), DWQE_DMA_DVA(sc->sc_txring) >> 32);
890305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_TX_BASE_ADDR(0), DWQE_DMA_DVA(sc->sc_txring));
891305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_TX_RING_LEN(0), DWQE_NTXDESC - 1);
892305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_TX_END_ADDR(0), DWQE_DMA_DVA(sc->sc_txring));
893305ac5f9Spatrick 
894305ac5f9Spatrick 	/* Allocate  descriptor ring. */
895305ac5f9Spatrick 	sc->sc_rxring = dwqe_dmamem_alloc(sc,
896305ac5f9Spatrick 	    DWQE_NRXDESC * sizeof(struct dwqe_desc), 8);
897305ac5f9Spatrick 	sc->sc_rxdesc = DWQE_DMA_KVA(sc->sc_rxring);
898305ac5f9Spatrick 
899305ac5f9Spatrick 	sc->sc_rxbuf = malloc(sizeof(struct dwqe_buf) * DWQE_NRXDESC,
900305ac5f9Spatrick 	    M_DEVBUF, M_WAITOK);
901305ac5f9Spatrick 
902305ac5f9Spatrick 	for (i = 0; i < DWQE_NRXDESC; i++) {
903305ac5f9Spatrick 		rxb = &sc->sc_rxbuf[i];
904305ac5f9Spatrick 		bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1,
905305ac5f9Spatrick 		    MCLBYTES, 0, BUS_DMA_WAITOK, &rxb->tb_map);
906305ac5f9Spatrick 		rxb->tb_m = NULL;
907305ac5f9Spatrick 	}
908305ac5f9Spatrick 
909f51cb083Sbluhm 	if_rxr_init(&sc->sc_rx_ring, 2, DWQE_NRXDESC - 1);
910305ac5f9Spatrick 
911305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_RX_BASE_ADDR_HI(0), DWQE_DMA_DVA(sc->sc_rxring) >> 32);
912305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_RX_BASE_ADDR(0), DWQE_DMA_DVA(sc->sc_rxring));
913305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_RX_RING_LEN(0), DWQE_NRXDESC - 1);
914305ac5f9Spatrick 
915305ac5f9Spatrick 	sc->sc_rx_prod = sc->sc_rx_cons = 0;
916305ac5f9Spatrick 	dwqe_fill_rx_ring(sc);
917305ac5f9Spatrick 
918305ac5f9Spatrick 	bus_dmamap_sync(sc->sc_dmat, DWQE_DMA_MAP(sc->sc_rxring),
919305ac5f9Spatrick 	    0, DWQE_DMA_LEN(sc->sc_rxring),
920305ac5f9Spatrick 	    BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
921305ac5f9Spatrick 
922305ac5f9Spatrick 	dwqe_lladdr_write(sc);
923305ac5f9Spatrick 
924305ac5f9Spatrick 	/* Configure media. */
925305ac5f9Spatrick 	if (LIST_FIRST(&sc->sc_mii.mii_phys))
926305ac5f9Spatrick 		mii_mediachg(&sc->sc_mii);
927305ac5f9Spatrick 
928305ac5f9Spatrick 	/* Program promiscuous mode and multicast filters. */
929305ac5f9Spatrick 	dwqe_iff(sc);
930305ac5f9Spatrick 
931305ac5f9Spatrick 	ifp->if_flags |= IFF_RUNNING;
932305ac5f9Spatrick 	ifq_clr_oactive(&ifp->if_snd);
933305ac5f9Spatrick 
934b97405d6Sstsp 	dwqe_write(sc, GMAC_MAC_1US_TIC_CTR, (sc->sc_clkrate / 1000000) - 1);
935305ac5f9Spatrick 
936305ac5f9Spatrick 	/* Start receive DMA */
937305ac5f9Spatrick 	reg = dwqe_read(sc, GMAC_CHAN_RX_CONTROL(0));
938305ac5f9Spatrick 	reg |= GMAC_CHAN_RX_CONTROL_SR;
939305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_RX_CONTROL(0), reg);
940305ac5f9Spatrick 
941305ac5f9Spatrick 	/* Start transmit DMA */
942305ac5f9Spatrick 	reg = dwqe_read(sc, GMAC_CHAN_TX_CONTROL(0));
943305ac5f9Spatrick 	reg |= GMAC_CHAN_TX_CONTROL_ST;
944305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_TX_CONTROL(0), reg);
945305ac5f9Spatrick 
946305ac5f9Spatrick 	mode = dwqe_read(sc, GMAC_MTL_CHAN_RX_OP_MODE(0));
947305ac5f9Spatrick 	if (sc->sc_force_thresh_dma_mode) {
948305ac5f9Spatrick 		mode &= ~GMAC_MTL_CHAN_RX_OP_MODE_RSF;
949305ac5f9Spatrick 		mode &= ~GMAC_MTL_CHAN_RX_OP_MODE_RTC_MASK;
950305ac5f9Spatrick 		mode |= GMAC_MTL_CHAN_RX_OP_MODE_RTC_128;
951305ac5f9Spatrick 	} else {
952305ac5f9Spatrick 		mode |= GMAC_MTL_CHAN_RX_OP_MODE_RSF;
953305ac5f9Spatrick 	}
954305ac5f9Spatrick 	mode &= ~GMAC_MTL_CHAN_RX_OP_MODE_RQS_MASK;
9556e9149a4Sstsp 	if (sc->sc_rxfifo_size)
9566e9149a4Sstsp 		fifosz = sc->sc_rxfifo_size;
9576e9149a4Sstsp 	else
9586e9149a4Sstsp 		fifosz = (128 <<
9596e9149a4Sstsp 		    GMAC_MAC_HW_FEATURE1_RXFIFOSIZE(sc->sc_hw_feature[1]));
9606e9149a4Sstsp 	rqs = fifosz / 256 - 1;
9616e9149a4Sstsp 	mode |= (rqs << GMAC_MTL_CHAN_RX_OP_MODE_RQS_SHIFT) &
9626e9149a4Sstsp 	   GMAC_MTL_CHAN_RX_OP_MODE_RQS_MASK;
9636e9149a4Sstsp 	if (fifosz >= 4096) {
9646e9149a4Sstsp 		mode |= GMAC_MTL_CHAN_RX_OP_MODE_EHFC;
9656e9149a4Sstsp 		mode &= ~GMAC_MTL_CHAN_RX_OP_MODE_RFD_MASK;
9666e9149a4Sstsp 		mode |= 0x3 << GMAC_MTL_CHAN_RX_OP_MODE_RFD_SHIFT;
9676e9149a4Sstsp 		mode &= ~GMAC_MTL_CHAN_RX_OP_MODE_RFA_MASK;
9686e9149a4Sstsp 		mode |= 0x1 << GMAC_MTL_CHAN_RX_OP_MODE_RFA_SHIFT;
9696e9149a4Sstsp 	}
970305ac5f9Spatrick 	dwqe_write(sc, GMAC_MTL_CHAN_RX_OP_MODE(0), mode);
971305ac5f9Spatrick 
972305ac5f9Spatrick 	mode = dwqe_read(sc, GMAC_MTL_CHAN_TX_OP_MODE(0));
973305ac5f9Spatrick 	if (sc->sc_force_thresh_dma_mode) {
974305ac5f9Spatrick 		mode &= ~GMAC_MTL_CHAN_TX_OP_MODE_TSF;
975305ac5f9Spatrick 		mode &= ~GMAC_MTL_CHAN_TX_OP_MODE_TTC_MASK;
976e8974f33Skettenis 		mode |= GMAC_MTL_CHAN_TX_OP_MODE_TTC_512;
977305ac5f9Spatrick 	} else {
978305ac5f9Spatrick 		mode |= GMAC_MTL_CHAN_TX_OP_MODE_TSF;
979305ac5f9Spatrick 	}
980305ac5f9Spatrick 	mode &= ~GMAC_MTL_CHAN_TX_OP_MODE_TXQEN_MASK;
981305ac5f9Spatrick 	mode |= GMAC_MTL_CHAN_TX_OP_MODE_TXQEN;
982305ac5f9Spatrick 	mode &= ~GMAC_MTL_CHAN_TX_OP_MODE_TQS_MASK;
9836e9149a4Sstsp 	if (sc->sc_txfifo_size)
9846e9149a4Sstsp 		fifosz = sc->sc_txfifo_size;
9856e9149a4Sstsp 	else
9866e9149a4Sstsp 		fifosz = (128 <<
9876e9149a4Sstsp 		    GMAC_MAC_HW_FEATURE1_TXFIFOSIZE(sc->sc_hw_feature[1]));
9886e9149a4Sstsp 	tqs = (fifosz / 256) - 1;
9896e9149a4Sstsp 	mode |= (tqs << GMAC_MTL_CHAN_TX_OP_MODE_TQS_SHIFT) &
9906e9149a4Sstsp 	    GMAC_MTL_CHAN_TX_OP_MODE_TQS_MASK;
991305ac5f9Spatrick 	dwqe_write(sc, GMAC_MTL_CHAN_TX_OP_MODE(0), mode);
992305ac5f9Spatrick 
993305ac5f9Spatrick 	reg = dwqe_read(sc, GMAC_QX_TX_FLOW_CTRL(0));
994305ac5f9Spatrick 	reg |= 0xffffU << GMAC_QX_TX_FLOW_CTRL_PT_SHIFT;
995305ac5f9Spatrick 	reg |= GMAC_QX_TX_FLOW_CTRL_TFE;
996305ac5f9Spatrick 	dwqe_write(sc, GMAC_QX_TX_FLOW_CTRL(0), reg);
997305ac5f9Spatrick 	reg = dwqe_read(sc, GMAC_RX_FLOW_CTRL);
998305ac5f9Spatrick 	reg |= GMAC_RX_FLOW_CTRL_RFE;
999305ac5f9Spatrick 	dwqe_write(sc, GMAC_RX_FLOW_CTRL, reg);
1000305ac5f9Spatrick 
1001305ac5f9Spatrick 	dwqe_write(sc, GMAC_RXQ_CTRL0, GMAC_RXQ_CTRL0_DCB_QUEUE_EN(0));
1002305ac5f9Spatrick 
1003305ac5f9Spatrick 	dwqe_write(sc, GMAC_MAC_CONF, dwqe_read(sc, GMAC_MAC_CONF) |
1004305ac5f9Spatrick 	    GMAC_MAC_CONF_BE | GMAC_MAC_CONF_JD | GMAC_MAC_CONF_JE |
1005305ac5f9Spatrick 	    GMAC_MAC_CONF_DCRS | GMAC_MAC_CONF_TE | GMAC_MAC_CONF_RE);
1006305ac5f9Spatrick 
1007305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_INTR_ENA(0),
1008305ac5f9Spatrick 	    GMAC_CHAN_INTR_ENA_NIE |
1009305ac5f9Spatrick 	    GMAC_CHAN_INTR_ENA_AIE |
1010305ac5f9Spatrick 	    GMAC_CHAN_INTR_ENA_FBE |
1011305ac5f9Spatrick 	    GMAC_CHAN_INTR_ENA_RIE |
1012305ac5f9Spatrick 	    GMAC_CHAN_INTR_ENA_TIE);
1013305ac5f9Spatrick 
101445f5f3c8Sdlg 	if (!sc->sc_fixed_link)
101547707f8eSdlg 		timeout_add_sec(&sc->sc_phy_tick, 1);
1016a72369d8Sstsp 
1017a72369d8Sstsp 	if (dwqe_have_rx_csum_offload(sc)) {
1018a72369d8Sstsp 		reg = dwqe_read(sc, GMAC_MAC_CONF);
1019a72369d8Sstsp 		reg |= GMAC_MAC_CONF_IPC;
1020a72369d8Sstsp 		dwqe_write(sc, GMAC_MAC_CONF, reg);
1021a72369d8Sstsp 	}
1022305ac5f9Spatrick }
1023305ac5f9Spatrick 
1024305ac5f9Spatrick void
dwqe_down(struct dwqe_softc * sc)1025305ac5f9Spatrick dwqe_down(struct dwqe_softc *sc)
1026305ac5f9Spatrick {
1027305ac5f9Spatrick 	struct ifnet *ifp = &sc->sc_ac.ac_if;
1028305ac5f9Spatrick 	struct dwqe_buf *txb, *rxb;
1029305ac5f9Spatrick 	uint32_t reg;
1030305ac5f9Spatrick 	int i;
1031305ac5f9Spatrick 
1032305ac5f9Spatrick 	timeout_del(&sc->sc_rxto);
103345f5f3c8Sdlg 	if (!sc->sc_fixed_link)
103447707f8eSdlg 		timeout_del(&sc->sc_phy_tick);
1035305ac5f9Spatrick 
1036305ac5f9Spatrick 	ifp->if_flags &= ~IFF_RUNNING;
1037305ac5f9Spatrick 	ifq_clr_oactive(&ifp->if_snd);
1038305ac5f9Spatrick 	ifp->if_timer = 0;
1039305ac5f9Spatrick 
1040305ac5f9Spatrick 	/* Disable receiver */
1041305ac5f9Spatrick 	reg = dwqe_read(sc, GMAC_MAC_CONF);
1042305ac5f9Spatrick 	reg &= ~GMAC_MAC_CONF_RE;
1043305ac5f9Spatrick 	dwqe_write(sc, GMAC_MAC_CONF, reg);
1044305ac5f9Spatrick 
1045305ac5f9Spatrick 	/* Stop receive DMA */
1046305ac5f9Spatrick 	reg = dwqe_read(sc, GMAC_CHAN_RX_CONTROL(0));
1047305ac5f9Spatrick 	reg &= ~GMAC_CHAN_RX_CONTROL_SR;
1048305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_RX_CONTROL(0), reg);
1049305ac5f9Spatrick 
1050305ac5f9Spatrick 	/* Stop transmit DMA */
1051305ac5f9Spatrick 	reg = dwqe_read(sc, GMAC_CHAN_TX_CONTROL(0));
1052305ac5f9Spatrick 	reg &= ~GMAC_CHAN_TX_CONTROL_ST;
1053305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_TX_CONTROL(0), reg);
1054305ac5f9Spatrick 
1055305ac5f9Spatrick 	/* Flush data in the TX FIFO */
1056305ac5f9Spatrick 	reg = dwqe_read(sc, GMAC_MTL_CHAN_TX_OP_MODE(0));
1057305ac5f9Spatrick 	reg |= GMAC_MTL_CHAN_TX_OP_MODE_FTQ;
1058305ac5f9Spatrick 	dwqe_write(sc, GMAC_MTL_CHAN_TX_OP_MODE(0), reg);
1059305ac5f9Spatrick 	/* Wait for flush to complete */
1060305ac5f9Spatrick 	for (i = 10000; i > 0; i--) {
1061305ac5f9Spatrick 		reg = dwqe_read(sc, GMAC_MTL_CHAN_TX_OP_MODE(0));
1062305ac5f9Spatrick 		if ((reg & GMAC_MTL_CHAN_TX_OP_MODE_FTQ) == 0)
1063305ac5f9Spatrick 			break;
1064305ac5f9Spatrick 		delay(1);
1065305ac5f9Spatrick 	}
1066305ac5f9Spatrick 	if (i == 0) {
1067305ac5f9Spatrick 		printf("%s: timeout flushing TX queue\n",
1068305ac5f9Spatrick 		    sc->sc_dev.dv_xname);
1069305ac5f9Spatrick 	}
1070305ac5f9Spatrick 
1071305ac5f9Spatrick 	/* Disable transmitter */
1072305ac5f9Spatrick 	reg = dwqe_read(sc, GMAC_MAC_CONF);
1073305ac5f9Spatrick 	reg &= ~GMAC_MAC_CONF_TE;
1074305ac5f9Spatrick 	dwqe_write(sc, GMAC_MAC_CONF, reg);
1075305ac5f9Spatrick 
1076305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_INTR_ENA(0), 0);
1077305ac5f9Spatrick 
1078305ac5f9Spatrick 	intr_barrier(sc->sc_ih);
1079305ac5f9Spatrick 	ifq_barrier(&ifp->if_snd);
1080305ac5f9Spatrick 
1081305ac5f9Spatrick 	for (i = 0; i < DWQE_NTXDESC; i++) {
1082305ac5f9Spatrick 		txb = &sc->sc_txbuf[i];
1083305ac5f9Spatrick 		if (txb->tb_m) {
1084305ac5f9Spatrick 			bus_dmamap_sync(sc->sc_dmat, txb->tb_map, 0,
1085305ac5f9Spatrick 			    txb->tb_map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
1086305ac5f9Spatrick 			bus_dmamap_unload(sc->sc_dmat, txb->tb_map);
1087305ac5f9Spatrick 			m_freem(txb->tb_m);
1088305ac5f9Spatrick 		}
1089305ac5f9Spatrick 		bus_dmamap_destroy(sc->sc_dmat, txb->tb_map);
1090305ac5f9Spatrick 	}
1091305ac5f9Spatrick 
1092305ac5f9Spatrick 	dwqe_dmamem_free(sc, sc->sc_txring);
1093305ac5f9Spatrick 	free(sc->sc_txbuf, M_DEVBUF, 0);
1094305ac5f9Spatrick 
1095305ac5f9Spatrick 	for (i = 0; i < DWQE_NRXDESC; i++) {
1096305ac5f9Spatrick 		rxb = &sc->sc_rxbuf[i];
1097305ac5f9Spatrick 		if (rxb->tb_m) {
1098305ac5f9Spatrick 			bus_dmamap_sync(sc->sc_dmat, rxb->tb_map, 0,
1099305ac5f9Spatrick 			    rxb->tb_map->dm_mapsize, BUS_DMASYNC_POSTREAD);
1100305ac5f9Spatrick 			bus_dmamap_unload(sc->sc_dmat, rxb->tb_map);
1101305ac5f9Spatrick 			m_freem(rxb->tb_m);
1102305ac5f9Spatrick 		}
1103305ac5f9Spatrick 		bus_dmamap_destroy(sc->sc_dmat, rxb->tb_map);
1104305ac5f9Spatrick 	}
1105305ac5f9Spatrick 
1106305ac5f9Spatrick 	dwqe_dmamem_free(sc, sc->sc_rxring);
1107305ac5f9Spatrick 	free(sc->sc_rxbuf, M_DEVBUF, 0);
1108305ac5f9Spatrick }
1109305ac5f9Spatrick 
1110305ac5f9Spatrick /* Bit Reversal - http://aggregate.org/MAGIC/#Bit%20Reversal */
1111305ac5f9Spatrick static uint32_t
bitrev32(uint32_t x)1112305ac5f9Spatrick bitrev32(uint32_t x)
1113305ac5f9Spatrick {
1114305ac5f9Spatrick 	x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
1115305ac5f9Spatrick 	x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
1116305ac5f9Spatrick 	x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
1117305ac5f9Spatrick 	x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
1118305ac5f9Spatrick 
1119305ac5f9Spatrick 	return (x >> 16) | (x << 16);
1120305ac5f9Spatrick }
1121305ac5f9Spatrick 
1122305ac5f9Spatrick void
dwqe_iff(struct dwqe_softc * sc)1123305ac5f9Spatrick dwqe_iff(struct dwqe_softc *sc)
1124305ac5f9Spatrick {
1125305ac5f9Spatrick 	struct arpcom *ac = &sc->sc_ac;
1126305ac5f9Spatrick 	struct ifnet *ifp = &sc->sc_ac.ac_if;
1127305ac5f9Spatrick 	struct ether_multi *enm;
1128305ac5f9Spatrick 	struct ether_multistep step;
1129305ac5f9Spatrick 	uint32_t crc, hash[2], hashbit, hashreg;
1130305ac5f9Spatrick 	uint32_t reg;
1131305ac5f9Spatrick 
1132305ac5f9Spatrick 	reg = 0;
1133305ac5f9Spatrick 
1134305ac5f9Spatrick 	ifp->if_flags &= ~IFF_ALLMULTI;
1135305ac5f9Spatrick 	bzero(hash, sizeof(hash));
1136305ac5f9Spatrick 	if (ifp->if_flags & IFF_PROMISC || ac->ac_multirangecnt > 0) {
1137305ac5f9Spatrick 		ifp->if_flags |= IFF_ALLMULTI;
1138305ac5f9Spatrick 		reg |= GMAC_MAC_PACKET_FILTER_PM;
1139305ac5f9Spatrick 		if (ifp->if_flags & IFF_PROMISC)
1140305ac5f9Spatrick 			reg |= GMAC_MAC_PACKET_FILTER_PR |
1141305ac5f9Spatrick 			    GMAC_MAC_PACKET_FILTER_PCF_ALL;
1142305ac5f9Spatrick 	} else {
1143305ac5f9Spatrick 		reg |= GMAC_MAC_PACKET_FILTER_HMC;
1144305ac5f9Spatrick 		ETHER_FIRST_MULTI(step, ac, enm);
1145305ac5f9Spatrick 		while (enm != NULL) {
1146305ac5f9Spatrick 			crc = ether_crc32_le(enm->enm_addrlo,
1147305ac5f9Spatrick 			    ETHER_ADDR_LEN) & 0x7f;
1148305ac5f9Spatrick 
1149305ac5f9Spatrick 			crc = bitrev32(~crc) >> 26;
1150305ac5f9Spatrick 			hashreg = (crc >> 5);
1151305ac5f9Spatrick 			hashbit = (crc & 0x1f);
1152305ac5f9Spatrick 			hash[hashreg] |= (1 << hashbit);
1153305ac5f9Spatrick 
1154305ac5f9Spatrick 			ETHER_NEXT_MULTI(step, enm);
1155305ac5f9Spatrick 		}
1156305ac5f9Spatrick 	}
1157305ac5f9Spatrick 
1158305ac5f9Spatrick 	dwqe_lladdr_write(sc);
1159305ac5f9Spatrick 
1160305ac5f9Spatrick 	dwqe_write(sc, GMAC_MAC_HASH_TAB_REG0, hash[0]);
1161305ac5f9Spatrick 	dwqe_write(sc, GMAC_MAC_HASH_TAB_REG1, hash[1]);
1162305ac5f9Spatrick 
1163305ac5f9Spatrick 	dwqe_write(sc, GMAC_MAC_PACKET_FILTER, reg);
1164305ac5f9Spatrick }
1165305ac5f9Spatrick 
116671083937Sstsp void
dwqe_tx_csum(struct dwqe_softc * sc,struct mbuf * m,struct dwqe_desc * txd)116771083937Sstsp dwqe_tx_csum(struct dwqe_softc *sc, struct mbuf *m, struct dwqe_desc *txd)
116871083937Sstsp {
116971083937Sstsp 	if (!dwqe_have_tx_csum_offload(sc))
117071083937Sstsp 		return;
117171083937Sstsp 
117271083937Sstsp 	/* Checksum flags are valid only on first descriptor. */
117371083937Sstsp 	if ((txd->sd_tdes3 & TDES3_FS) == 0)
117471083937Sstsp 		return;
117571083937Sstsp 
117671083937Sstsp 	/* TSO and Tx checksum offloading are incompatible. */
117771083937Sstsp 	if (txd->sd_tdes3 & TDES3_TSO_EN)
117871083937Sstsp 		return;
117971083937Sstsp 
118071083937Sstsp 	if (m->m_pkthdr.csum_flags & (M_IPV4_CSUM_OUT |
118171083937Sstsp 	    M_TCP_CSUM_OUT | M_UDP_CSUM_OUT))
118271083937Sstsp 		txd->sd_tdes3 |= TDES3_CSUM_IPHDR_PAYLOAD_PSEUDOHDR;
118371083937Sstsp }
118471083937Sstsp 
1185*84a278ffSstsp uint16_t
dwqe_set_tx_context_desc(struct dwqe_softc * sc,struct mbuf * m,int idx)1186*84a278ffSstsp dwqe_set_tx_context_desc(struct dwqe_softc *sc, struct mbuf *m, int idx)
1187*84a278ffSstsp {
1188*84a278ffSstsp 	uint16_t tag = 0;
1189*84a278ffSstsp #if NVLAN > 0
1190*84a278ffSstsp 	struct dwqe_desc *ctxt_txd;
1191*84a278ffSstsp 
1192*84a278ffSstsp 	if ((m->m_flags & M_VLANTAG) == 0)
1193*84a278ffSstsp 		return 0;
1194*84a278ffSstsp 
1195*84a278ffSstsp 	tag = m->m_pkthdr.ether_vtag;
1196*84a278ffSstsp 	if (tag) {
1197*84a278ffSstsp 		ctxt_txd = &sc->sc_txdesc[idx];
1198*84a278ffSstsp 		ctxt_txd->sd_tdes3 |= (htole16(tag) & TDES3_VLAN_TAG);
1199*84a278ffSstsp 		ctxt_txd->sd_tdes3 |= TDES3_VLAN_TAG_VALID;
1200*84a278ffSstsp 		ctxt_txd->sd_tdes3 |= (TDES3_CTXT | TDES3_OWN);
1201*84a278ffSstsp 	}
1202*84a278ffSstsp #endif
1203*84a278ffSstsp 	return tag;
1204*84a278ffSstsp }
1205*84a278ffSstsp 
1206305ac5f9Spatrick int
dwqe_encap(struct dwqe_softc * sc,struct mbuf * m,int * idx,int * used)1207305ac5f9Spatrick dwqe_encap(struct dwqe_softc *sc, struct mbuf *m, int *idx, int *used)
1208305ac5f9Spatrick {
1209305ac5f9Spatrick 	struct dwqe_desc *txd, *txd_start;
1210305ac5f9Spatrick 	bus_dmamap_t map;
1211305ac5f9Spatrick 	int cur, frag, i;
1212*84a278ffSstsp 	uint16_t vlan_tag = 0;
1213305ac5f9Spatrick 
1214305ac5f9Spatrick 	cur = frag = *idx;
1215305ac5f9Spatrick 	map = sc->sc_txbuf[cur].tb_map;
1216305ac5f9Spatrick 
1217305ac5f9Spatrick 	if (bus_dmamap_load_mbuf(sc->sc_dmat, map, m, BUS_DMA_NOWAIT)) {
1218305ac5f9Spatrick 		if (m_defrag(m, M_DONTWAIT))
1219305ac5f9Spatrick 			return (EFBIG);
1220305ac5f9Spatrick 		if (bus_dmamap_load_mbuf(sc->sc_dmat, map, m, BUS_DMA_NOWAIT))
1221305ac5f9Spatrick 			return (EFBIG);
1222305ac5f9Spatrick 	}
1223305ac5f9Spatrick 
1224305ac5f9Spatrick 	/* Sync the DMA map. */
1225305ac5f9Spatrick 	bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
1226305ac5f9Spatrick 	    BUS_DMASYNC_PREWRITE);
1227305ac5f9Spatrick 
1228*84a278ffSstsp 	if (dwqe_have_tx_vlan_offload(sc)) {
1229*84a278ffSstsp 		vlan_tag = dwqe_set_tx_context_desc(sc, m, frag);
1230*84a278ffSstsp 		if (vlan_tag) {
1231*84a278ffSstsp 			(*used)++;
1232*84a278ffSstsp 			if (frag == (DWQE_NTXDESC - 1))
1233*84a278ffSstsp 				frag = 0;
1234*84a278ffSstsp 			else
1235*84a278ffSstsp 				frag++;
1236*84a278ffSstsp 		}
1237*84a278ffSstsp 	}
1238*84a278ffSstsp 
1239305ac5f9Spatrick 	txd = txd_start = &sc->sc_txdesc[frag];
1240305ac5f9Spatrick 	for (i = 0; i < map->dm_nsegs; i++) {
1241305ac5f9Spatrick 		/* TODO: check for 32-bit vs 64-bit support */
1242305ac5f9Spatrick 		KASSERT((map->dm_segs[i].ds_addr >> 32) == 0);
1243305ac5f9Spatrick 
1244305ac5f9Spatrick 		txd->sd_tdes0 = (uint32_t)map->dm_segs[i].ds_addr;
1245305ac5f9Spatrick 		txd->sd_tdes1 = (uint32_t)(map->dm_segs[i].ds_addr >> 32);
1246305ac5f9Spatrick 		txd->sd_tdes2 = map->dm_segs[i].ds_len;
1247305ac5f9Spatrick 		txd->sd_tdes3 = m->m_pkthdr.len;
124871083937Sstsp 		if (i == 0) {
1249305ac5f9Spatrick 			txd->sd_tdes3 |= TDES3_FS;
125071083937Sstsp 			dwqe_tx_csum(sc, m, txd);
1251*84a278ffSstsp 			if (vlan_tag)
1252*84a278ffSstsp 				txd->sd_tdes2 |= TDES2_VLAN_TAG_INSERT;
125371083937Sstsp 		}
1254305ac5f9Spatrick 		if (i == (map->dm_nsegs - 1)) {
1255305ac5f9Spatrick 			txd->sd_tdes2 |= TDES2_IC;
1256305ac5f9Spatrick 			txd->sd_tdes3 |= TDES3_LS;
1257305ac5f9Spatrick 		}
1258305ac5f9Spatrick 		if (i != 0)
1259305ac5f9Spatrick 			txd->sd_tdes3 |= TDES3_OWN;
1260305ac5f9Spatrick 
1261305ac5f9Spatrick 		bus_dmamap_sync(sc->sc_dmat, DWQE_DMA_MAP(sc->sc_txring),
1262305ac5f9Spatrick 		    frag * sizeof(*txd), sizeof(*txd), BUS_DMASYNC_PREWRITE);
1263305ac5f9Spatrick 
1264305ac5f9Spatrick 		cur = frag;
1265305ac5f9Spatrick 		if (frag == (DWQE_NTXDESC - 1)) {
1266305ac5f9Spatrick 			txd = &sc->sc_txdesc[0];
1267305ac5f9Spatrick 			frag = 0;
1268305ac5f9Spatrick 		} else {
1269305ac5f9Spatrick 			txd++;
1270305ac5f9Spatrick 			frag++;
1271305ac5f9Spatrick 		}
1272305ac5f9Spatrick 		KASSERT(frag != sc->sc_tx_cons);
1273305ac5f9Spatrick 	}
1274305ac5f9Spatrick 
1275305ac5f9Spatrick 	txd_start->sd_tdes3 |= TDES3_OWN;
1276305ac5f9Spatrick 	bus_dmamap_sync(sc->sc_dmat, DWQE_DMA_MAP(sc->sc_txring),
1277305ac5f9Spatrick 	    *idx * sizeof(*txd), sizeof(*txd), BUS_DMASYNC_PREWRITE);
1278305ac5f9Spatrick 
1279305ac5f9Spatrick 	KASSERT(sc->sc_txbuf[cur].tb_m == NULL);
1280305ac5f9Spatrick 	sc->sc_txbuf[*idx].tb_map = sc->sc_txbuf[cur].tb_map;
1281305ac5f9Spatrick 	sc->sc_txbuf[cur].tb_map = map;
1282305ac5f9Spatrick 	sc->sc_txbuf[cur].tb_m = m;
1283305ac5f9Spatrick 
1284305ac5f9Spatrick 	*idx = frag;
1285305ac5f9Spatrick 	*used += map->dm_nsegs;
1286305ac5f9Spatrick 
1287305ac5f9Spatrick 	return (0);
1288305ac5f9Spatrick }
1289305ac5f9Spatrick 
1290305ac5f9Spatrick void
dwqe_reset(struct dwqe_softc * sc)1291305ac5f9Spatrick dwqe_reset(struct dwqe_softc *sc)
1292305ac5f9Spatrick {
1293305ac5f9Spatrick 	int n;
1294305ac5f9Spatrick 
1295305ac5f9Spatrick 	dwqe_write(sc, GMAC_BUS_MODE, dwqe_read(sc, GMAC_BUS_MODE) |
1296305ac5f9Spatrick 	    GMAC_BUS_MODE_SWR);
1297305ac5f9Spatrick 
1298305ac5f9Spatrick 	for (n = 0; n < 30000; n++) {
1299305ac5f9Spatrick 		if ((dwqe_read(sc, GMAC_BUS_MODE) &
1300305ac5f9Spatrick 		    GMAC_BUS_MODE_SWR) == 0)
1301305ac5f9Spatrick 			return;
1302305ac5f9Spatrick 		delay(10);
1303305ac5f9Spatrick 	}
1304305ac5f9Spatrick 
1305305ac5f9Spatrick 	printf("%s: reset timeout\n", sc->sc_dev.dv_xname);
1306305ac5f9Spatrick }
1307305ac5f9Spatrick 
1308305ac5f9Spatrick struct dwqe_dmamem *
dwqe_dmamem_alloc(struct dwqe_softc * sc,bus_size_t size,bus_size_t align)1309305ac5f9Spatrick dwqe_dmamem_alloc(struct dwqe_softc *sc, bus_size_t size, bus_size_t align)
1310305ac5f9Spatrick {
1311305ac5f9Spatrick 	struct dwqe_dmamem *tdm;
1312305ac5f9Spatrick 	int nsegs;
1313305ac5f9Spatrick 
1314305ac5f9Spatrick 	tdm = malloc(sizeof(*tdm), M_DEVBUF, M_WAITOK | M_ZERO);
1315305ac5f9Spatrick 	tdm->tdm_size = size;
1316305ac5f9Spatrick 
1317305ac5f9Spatrick 	if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
1318305ac5f9Spatrick 	    BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &tdm->tdm_map) != 0)
1319305ac5f9Spatrick 		goto tdmfree;
1320305ac5f9Spatrick 
1321305ac5f9Spatrick 	if (bus_dmamem_alloc(sc->sc_dmat, size, align, 0, &tdm->tdm_seg, 1,
1322305ac5f9Spatrick 	    &nsegs, BUS_DMA_WAITOK) != 0)
1323305ac5f9Spatrick 		goto destroy;
1324305ac5f9Spatrick 
1325305ac5f9Spatrick 	if (bus_dmamem_map(sc->sc_dmat, &tdm->tdm_seg, nsegs, size,
1326305ac5f9Spatrick 	    &tdm->tdm_kva, BUS_DMA_WAITOK | BUS_DMA_COHERENT) != 0)
1327305ac5f9Spatrick 		goto free;
1328305ac5f9Spatrick 
1329305ac5f9Spatrick 	if (bus_dmamap_load(sc->sc_dmat, tdm->tdm_map, tdm->tdm_kva, size,
1330305ac5f9Spatrick 	    NULL, BUS_DMA_WAITOK) != 0)
1331305ac5f9Spatrick 		goto unmap;
1332305ac5f9Spatrick 
1333305ac5f9Spatrick 	bzero(tdm->tdm_kva, size);
1334305ac5f9Spatrick 
1335305ac5f9Spatrick 	return (tdm);
1336305ac5f9Spatrick 
1337305ac5f9Spatrick unmap:
1338305ac5f9Spatrick 	bus_dmamem_unmap(sc->sc_dmat, tdm->tdm_kva, size);
1339305ac5f9Spatrick free:
1340305ac5f9Spatrick 	bus_dmamem_free(sc->sc_dmat, &tdm->tdm_seg, 1);
1341305ac5f9Spatrick destroy:
1342305ac5f9Spatrick 	bus_dmamap_destroy(sc->sc_dmat, tdm->tdm_map);
1343305ac5f9Spatrick tdmfree:
1344305ac5f9Spatrick 	free(tdm, M_DEVBUF, 0);
1345305ac5f9Spatrick 
1346305ac5f9Spatrick 	return (NULL);
1347305ac5f9Spatrick }
1348305ac5f9Spatrick 
1349305ac5f9Spatrick void
dwqe_dmamem_free(struct dwqe_softc * sc,struct dwqe_dmamem * tdm)1350305ac5f9Spatrick dwqe_dmamem_free(struct dwqe_softc *sc, struct dwqe_dmamem *tdm)
1351305ac5f9Spatrick {
1352305ac5f9Spatrick 	bus_dmamem_unmap(sc->sc_dmat, tdm->tdm_kva, tdm->tdm_size);
1353305ac5f9Spatrick 	bus_dmamem_free(sc->sc_dmat, &tdm->tdm_seg, 1);
1354305ac5f9Spatrick 	bus_dmamap_destroy(sc->sc_dmat, tdm->tdm_map);
1355305ac5f9Spatrick 	free(tdm, M_DEVBUF, 0);
1356305ac5f9Spatrick }
1357305ac5f9Spatrick 
1358305ac5f9Spatrick struct mbuf *
dwqe_alloc_mbuf(struct dwqe_softc * sc,bus_dmamap_t map)1359305ac5f9Spatrick dwqe_alloc_mbuf(struct dwqe_softc *sc, bus_dmamap_t map)
1360305ac5f9Spatrick {
1361305ac5f9Spatrick 	struct mbuf *m = NULL;
1362305ac5f9Spatrick 
1363305ac5f9Spatrick 	m = MCLGETL(NULL, M_DONTWAIT, MCLBYTES);
1364305ac5f9Spatrick 	if (!m)
1365305ac5f9Spatrick 		return (NULL);
1366305ac5f9Spatrick 	m->m_len = m->m_pkthdr.len = MCLBYTES;
1367305ac5f9Spatrick 	m_adj(m, ETHER_ALIGN);
1368305ac5f9Spatrick 
1369305ac5f9Spatrick 	if (bus_dmamap_load_mbuf(sc->sc_dmat, map, m, BUS_DMA_NOWAIT) != 0) {
1370305ac5f9Spatrick 		printf("%s: could not load mbuf DMA map", DEVNAME(sc));
1371305ac5f9Spatrick 		m_freem(m);
1372305ac5f9Spatrick 		return (NULL);
1373305ac5f9Spatrick 	}
1374305ac5f9Spatrick 
1375305ac5f9Spatrick 	bus_dmamap_sync(sc->sc_dmat, map, 0,
1376305ac5f9Spatrick 	    m->m_pkthdr.len, BUS_DMASYNC_PREREAD);
1377305ac5f9Spatrick 
1378305ac5f9Spatrick 	return (m);
1379305ac5f9Spatrick }
1380305ac5f9Spatrick 
1381305ac5f9Spatrick void
dwqe_fill_rx_ring(struct dwqe_softc * sc)1382305ac5f9Spatrick dwqe_fill_rx_ring(struct dwqe_softc *sc)
1383305ac5f9Spatrick {
1384305ac5f9Spatrick 	struct dwqe_desc *rxd;
1385305ac5f9Spatrick 	struct dwqe_buf *rxb;
1386305ac5f9Spatrick 	u_int slots;
1387305ac5f9Spatrick 
1388305ac5f9Spatrick 	for (slots = if_rxr_get(&sc->sc_rx_ring, DWQE_NRXDESC);
1389305ac5f9Spatrick 	    slots > 0; slots--) {
1390305ac5f9Spatrick 		rxb = &sc->sc_rxbuf[sc->sc_rx_prod];
1391305ac5f9Spatrick 		rxb->tb_m = dwqe_alloc_mbuf(sc, rxb->tb_map);
1392305ac5f9Spatrick 		if (rxb->tb_m == NULL)
1393305ac5f9Spatrick 			break;
1394305ac5f9Spatrick 
1395305ac5f9Spatrick 		/* TODO: check for 32-bit vs 64-bit support */
13964fa84378Skettenis 		KASSERT((rxb->tb_map->dm_segs[0].ds_addr >> 32) == 0);
1397305ac5f9Spatrick 
1398305ac5f9Spatrick 		rxd = &sc->sc_rxdesc[sc->sc_rx_prod];
13994fa84378Skettenis 		rxd->sd_tdes0 = (uint32_t)rxb->tb_map->dm_segs[0].ds_addr;
14004fa84378Skettenis 		rxd->sd_tdes1 = (uint32_t)(rxb->tb_map->dm_segs[0].ds_addr >> 32);
1401305ac5f9Spatrick 		rxd->sd_tdes2 = 0;
1402305ac5f9Spatrick 		rxd->sd_tdes3 = RDES3_OWN | RDES3_IC | RDES3_BUF1V;
1403305ac5f9Spatrick 
1404305ac5f9Spatrick 		if (sc->sc_rx_prod == (DWQE_NRXDESC - 1))
1405305ac5f9Spatrick 			sc->sc_rx_prod = 0;
1406305ac5f9Spatrick 		else
1407305ac5f9Spatrick 			sc->sc_rx_prod++;
1408305ac5f9Spatrick 	}
1409305ac5f9Spatrick 	if_rxr_put(&sc->sc_rx_ring, slots);
1410305ac5f9Spatrick 
1411305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_RX_END_ADDR(0), DWQE_DMA_DVA(sc->sc_rxring) +
1412305ac5f9Spatrick 	    sc->sc_rx_prod * sizeof(*rxd));
1413305ac5f9Spatrick 
1414305ac5f9Spatrick 	if (if_rxr_inuse(&sc->sc_rx_ring) == 0)
1415305ac5f9Spatrick 		timeout_add(&sc->sc_rxto, 1);
1416305ac5f9Spatrick }
1417