xref: /openbsd/sys/dev/ic/dwqe.c (revision 71083937)
1*71083937Sstsp /*	$OpenBSD: dwqe.c,v 1.21 2024/05/06 09:54:38 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"
24305ac5f9Spatrick 
25305ac5f9Spatrick #include <sys/param.h>
26305ac5f9Spatrick #include <sys/systm.h>
27305ac5f9Spatrick #include <sys/device.h>
28305ac5f9Spatrick #include <sys/kernel.h>
29305ac5f9Spatrick #include <sys/malloc.h>
30305ac5f9Spatrick #include <sys/mbuf.h>
31305ac5f9Spatrick #include <sys/queue.h>
32305ac5f9Spatrick #include <sys/socket.h>
33305ac5f9Spatrick #include <sys/sockio.h>
34305ac5f9Spatrick #include <sys/timeout.h>
35305ac5f9Spatrick 
36305ac5f9Spatrick #include <machine/bus.h>
37305ac5f9Spatrick 
38305ac5f9Spatrick #include <net/if.h>
39305ac5f9Spatrick #include <net/if_media.h>
40305ac5f9Spatrick 
41305ac5f9Spatrick #include <dev/mii/mii.h>
42305ac5f9Spatrick #include <dev/mii/miivar.h>
43305ac5f9Spatrick 
44305ac5f9Spatrick #if NBPFILTER > 0
45305ac5f9Spatrick #include <net/bpf.h>
46305ac5f9Spatrick #endif
47305ac5f9Spatrick 
48305ac5f9Spatrick #include <netinet/in.h>
49305ac5f9Spatrick #include <netinet/if_ether.h>
50305ac5f9Spatrick 
51305ac5f9Spatrick #include <dev/ic/dwqevar.h>
52305ac5f9Spatrick #include <dev/ic/dwqereg.h>
53305ac5f9Spatrick 
54305ac5f9Spatrick struct cfdriver dwqe_cd = {
55305ac5f9Spatrick 	NULL, "dwqe", DV_IFNET
56305ac5f9Spatrick };
57305ac5f9Spatrick 
58305ac5f9Spatrick uint32_t dwqe_read(struct dwqe_softc *, bus_addr_t);
59305ac5f9Spatrick void	dwqe_write(struct dwqe_softc *, bus_addr_t, uint32_t);
60305ac5f9Spatrick 
61305ac5f9Spatrick int	dwqe_ioctl(struct ifnet *, u_long, caddr_t);
62305ac5f9Spatrick void	dwqe_start(struct ifqueue *);
63305ac5f9Spatrick void	dwqe_watchdog(struct ifnet *);
64305ac5f9Spatrick 
65305ac5f9Spatrick int	dwqe_media_change(struct ifnet *);
66305ac5f9Spatrick void	dwqe_media_status(struct ifnet *, struct ifmediareq *);
67305ac5f9Spatrick 
6845f5f3c8Sdlg void	dwqe_mii_attach(struct dwqe_softc *);
69305ac5f9Spatrick int	dwqe_mii_readreg(struct device *, int, int);
70305ac5f9Spatrick void	dwqe_mii_writereg(struct device *, int, int, int);
71305ac5f9Spatrick void	dwqe_mii_statchg(struct device *);
72305ac5f9Spatrick 
73305ac5f9Spatrick void	dwqe_lladdr_read(struct dwqe_softc *, uint8_t *);
74305ac5f9Spatrick void	dwqe_lladdr_write(struct dwqe_softc *);
75305ac5f9Spatrick 
76305ac5f9Spatrick void	dwqe_tick(void *);
77305ac5f9Spatrick void	dwqe_rxtick(void *);
78305ac5f9Spatrick 
79305ac5f9Spatrick int	dwqe_intr(void *);
80305ac5f9Spatrick void	dwqe_tx_proc(struct dwqe_softc *);
81305ac5f9Spatrick void	dwqe_rx_proc(struct dwqe_softc *);
82305ac5f9Spatrick 
83305ac5f9Spatrick void	dwqe_up(struct dwqe_softc *);
84305ac5f9Spatrick void	dwqe_down(struct dwqe_softc *);
85305ac5f9Spatrick void	dwqe_iff(struct dwqe_softc *);
86305ac5f9Spatrick int	dwqe_encap(struct dwqe_softc *, struct mbuf *, int *, int *);
87305ac5f9Spatrick 
88305ac5f9Spatrick void	dwqe_reset(struct dwqe_softc *);
89305ac5f9Spatrick 
90305ac5f9Spatrick struct dwqe_dmamem *
91305ac5f9Spatrick 	dwqe_dmamem_alloc(struct dwqe_softc *, bus_size_t, bus_size_t);
92305ac5f9Spatrick void	dwqe_dmamem_free(struct dwqe_softc *, struct dwqe_dmamem *);
93305ac5f9Spatrick struct mbuf *dwqe_alloc_mbuf(struct dwqe_softc *, bus_dmamap_t);
94305ac5f9Spatrick void	dwqe_fill_rx_ring(struct dwqe_softc *);
95305ac5f9Spatrick 
96305ac5f9Spatrick int
dwqe_have_tx_csum_offload(struct dwqe_softc * sc)97*71083937Sstsp dwqe_have_tx_csum_offload(struct dwqe_softc *sc)
98*71083937Sstsp {
99*71083937Sstsp 	return (sc->sc_hw_feature[0] & GMAC_MAC_HW_FEATURE0_TXCOESEL);
100*71083937Sstsp }
101*71083937Sstsp 
102*71083937Sstsp int
dwqe_attach(struct dwqe_softc * sc)103305ac5f9Spatrick dwqe_attach(struct dwqe_softc *sc)
104305ac5f9Spatrick {
105305ac5f9Spatrick 	struct ifnet *ifp;
106305ac5f9Spatrick 	uint32_t version, mode;
107305ac5f9Spatrick 	int i;
108305ac5f9Spatrick 
109305ac5f9Spatrick 	version = dwqe_read(sc, GMAC_VERSION);
1104fa84378Skettenis 	printf(": rev 0x%02x, address %s\n", version & GMAC_VERSION_SNPS_MASK,
111305ac5f9Spatrick 	    ether_sprintf(sc->sc_lladdr));
112305ac5f9Spatrick 
113305ac5f9Spatrick 	for (i = 0; i < 4; i++)
114305ac5f9Spatrick 		sc->sc_hw_feature[i] = dwqe_read(sc, GMAC_MAC_HW_FEATURE(i));
115305ac5f9Spatrick 
11647707f8eSdlg 	timeout_set(&sc->sc_phy_tick, dwqe_tick, sc);
117305ac5f9Spatrick 	timeout_set(&sc->sc_rxto, dwqe_rxtick, sc);
118305ac5f9Spatrick 
119305ac5f9Spatrick 	ifp = &sc->sc_ac.ac_if;
120305ac5f9Spatrick 	ifp->if_softc = sc;
121305ac5f9Spatrick 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
122305ac5f9Spatrick 	ifp->if_xflags = IFXF_MPSAFE;
123305ac5f9Spatrick 	ifp->if_ioctl = dwqe_ioctl;
124305ac5f9Spatrick 	ifp->if_qstart = dwqe_start;
125305ac5f9Spatrick 	ifp->if_watchdog = dwqe_watchdog;
126cf96265bSbluhm 	ifq_init_maxlen(&ifp->if_snd, DWQE_NTXDESC - 1);
127305ac5f9Spatrick 	bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
128305ac5f9Spatrick 
129305ac5f9Spatrick 	ifp->if_capabilities = IFCAP_VLAN_MTU;
130*71083937Sstsp 	if (dwqe_have_tx_csum_offload(sc)) {
131*71083937Sstsp 		ifp->if_capabilities |= (IFCAP_CSUM_IPv4 |
132*71083937Sstsp 		    IFCAP_CSUM_TCPv4 | IFCAP_CSUM_UDPv4 |
133*71083937Sstsp 		    IFCAP_CSUM_TCPv6 | IFCAP_CSUM_UDPv6);
134*71083937Sstsp 	}
135305ac5f9Spatrick 
136305ac5f9Spatrick 	sc->sc_mii.mii_ifp = ifp;
137305ac5f9Spatrick 	sc->sc_mii.mii_readreg = dwqe_mii_readreg;
138305ac5f9Spatrick 	sc->sc_mii.mii_writereg = dwqe_mii_writereg;
139305ac5f9Spatrick 	sc->sc_mii.mii_statchg = dwqe_mii_statchg;
140305ac5f9Spatrick 
141305ac5f9Spatrick 	ifmedia_init(&sc->sc_media, 0, dwqe_media_change, dwqe_media_status);
142305ac5f9Spatrick 
143305ac5f9Spatrick 	dwqe_reset(sc);
144305ac5f9Spatrick 
145305ac5f9Spatrick 	/* Configure DMA engine. */
146305ac5f9Spatrick 	mode = dwqe_read(sc, GMAC_SYS_BUS_MODE);
147305ac5f9Spatrick 	if (sc->sc_fixed_burst)
148305ac5f9Spatrick 		mode |= GMAC_SYS_BUS_MODE_FB;
149305ac5f9Spatrick 	if (sc->sc_mixed_burst)
150305ac5f9Spatrick 		mode |= GMAC_SYS_BUS_MODE_MB;
151305ac5f9Spatrick 	if (sc->sc_aal)
152305ac5f9Spatrick 		mode |= GMAC_SYS_BUS_MODE_AAL;
153305ac5f9Spatrick 	dwqe_write(sc, GMAC_SYS_BUS_MODE, mode);
154305ac5f9Spatrick 
155305ac5f9Spatrick 	/* Configure channel 0. */
156305ac5f9Spatrick 	mode = dwqe_read(sc, GMAC_CHAN_CONTROL(0));
157305ac5f9Spatrick 	if (sc->sc_8xpbl)
158305ac5f9Spatrick 		mode |= GMAC_CHAN_CONTROL_8XPBL;
159305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_CONTROL(0), mode);
160305ac5f9Spatrick 
161305ac5f9Spatrick 	mode = dwqe_read(sc, GMAC_CHAN_TX_CONTROL(0));
162305ac5f9Spatrick 	mode &= ~GMAC_CHAN_TX_CONTROL_PBL_MASK;
163305ac5f9Spatrick 	mode |= sc->sc_txpbl << GMAC_CHAN_TX_CONTROL_PBL_SHIFT;
164305ac5f9Spatrick 	mode |= GMAC_CHAN_TX_CONTROL_OSP;
165305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_TX_CONTROL(0), mode);
166f6ce44eeSjsg 	mode = dwqe_read(sc, GMAC_CHAN_RX_CONTROL(0));
167305ac5f9Spatrick 	mode &= ~GMAC_CHAN_RX_CONTROL_RPBL_MASK;
168305ac5f9Spatrick 	mode |= sc->sc_rxpbl << GMAC_CHAN_RX_CONTROL_RPBL_SHIFT;
169305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_RX_CONTROL(0), mode);
170305ac5f9Spatrick 
171305ac5f9Spatrick 	/* Configure AXI master. */
172305ac5f9Spatrick 	if (sc->sc_axi_config) {
173305ac5f9Spatrick 		int i;
174305ac5f9Spatrick 
175305ac5f9Spatrick 		mode = dwqe_read(sc, GMAC_SYS_BUS_MODE);
176305ac5f9Spatrick 
177305ac5f9Spatrick 		mode &= ~GMAC_SYS_BUS_MODE_EN_LPI;
178305ac5f9Spatrick 		if (sc->sc_lpi_en)
179305ac5f9Spatrick 			mode |= GMAC_SYS_BUS_MODE_EN_LPI;
180305ac5f9Spatrick 		mode &= ~GMAC_SYS_BUS_MODE_LPI_XIT_FRM;
181305ac5f9Spatrick 		if (sc->sc_xit_frm)
182305ac5f9Spatrick 			mode |= GMAC_SYS_BUS_MODE_LPI_XIT_FRM;
183305ac5f9Spatrick 
184305ac5f9Spatrick 		mode &= ~GMAC_SYS_BUS_MODE_WR_OSR_LMT_MASK;
185305ac5f9Spatrick 		mode |= (sc->sc_wr_osr_lmt << GMAC_SYS_BUS_MODE_WR_OSR_LMT_SHIFT);
186305ac5f9Spatrick 		mode &= ~GMAC_SYS_BUS_MODE_RD_OSR_LMT_MASK;
187305ac5f9Spatrick 		mode |= (sc->sc_rd_osr_lmt << GMAC_SYS_BUS_MODE_RD_OSR_LMT_SHIFT);
188305ac5f9Spatrick 
189305ac5f9Spatrick 		for (i = 0; i < nitems(sc->sc_blen); i++) {
190305ac5f9Spatrick 			switch (sc->sc_blen[i]) {
191305ac5f9Spatrick 			case 256:
192305ac5f9Spatrick 				mode |= GMAC_SYS_BUS_MODE_BLEN_256;
193305ac5f9Spatrick 				break;
194305ac5f9Spatrick 			case 128:
195305ac5f9Spatrick 				mode |= GMAC_SYS_BUS_MODE_BLEN_128;
196305ac5f9Spatrick 				break;
197305ac5f9Spatrick 			case 64:
198305ac5f9Spatrick 				mode |= GMAC_SYS_BUS_MODE_BLEN_64;
199305ac5f9Spatrick 				break;
200305ac5f9Spatrick 			case 32:
201305ac5f9Spatrick 				mode |= GMAC_SYS_BUS_MODE_BLEN_32;
202305ac5f9Spatrick 				break;
203305ac5f9Spatrick 			case 16:
204305ac5f9Spatrick 				mode |= GMAC_SYS_BUS_MODE_BLEN_16;
205305ac5f9Spatrick 				break;
206305ac5f9Spatrick 			case 8:
207305ac5f9Spatrick 				mode |= GMAC_SYS_BUS_MODE_BLEN_8;
208305ac5f9Spatrick 				break;
209305ac5f9Spatrick 			case 4:
210305ac5f9Spatrick 				mode |= GMAC_SYS_BUS_MODE_BLEN_4;
211305ac5f9Spatrick 				break;
212305ac5f9Spatrick 			}
213305ac5f9Spatrick 		}
214305ac5f9Spatrick 
215305ac5f9Spatrick 		dwqe_write(sc, GMAC_SYS_BUS_MODE, mode);
216305ac5f9Spatrick 	}
217305ac5f9Spatrick 
21845f5f3c8Sdlg 	if (!sc->sc_fixed_link)
21945f5f3c8Sdlg 		dwqe_mii_attach(sc);
22045f5f3c8Sdlg 
22145f5f3c8Sdlg 	if_attach(ifp);
22245f5f3c8Sdlg 	ether_ifattach(ifp);
22345f5f3c8Sdlg 
22445f5f3c8Sdlg 	/* Disable interrupts. */
22545f5f3c8Sdlg 	dwqe_write(sc, GMAC_INT_EN, 0);
22645f5f3c8Sdlg 	dwqe_write(sc, GMAC_CHAN_INTR_ENA(0), 0);
22764bdd17fSjmatthew 	dwqe_write(sc, GMAC_MMC_RX_INT_MASK, 0xffffffff);
22864bdd17fSjmatthew 	dwqe_write(sc, GMAC_MMC_TX_INT_MASK, 0xffffffff);
22945f5f3c8Sdlg 
23045f5f3c8Sdlg 	return 0;
23145f5f3c8Sdlg }
23245f5f3c8Sdlg 
23345f5f3c8Sdlg void
dwqe_mii_attach(struct dwqe_softc * sc)23445f5f3c8Sdlg dwqe_mii_attach(struct dwqe_softc *sc)
23545f5f3c8Sdlg {
23645f5f3c8Sdlg 	int mii_flags = 0;
23745f5f3c8Sdlg 
238e9f49f11Skettenis 	switch (sc->sc_phy_mode) {
239e9f49f11Skettenis 	case DWQE_PHY_MODE_RGMII:
240e9f49f11Skettenis 		mii_flags |= MIIF_SETDELAY;
241e9f49f11Skettenis 		break;
242e9f49f11Skettenis 	case DWQE_PHY_MODE_RGMII_ID:
243e9f49f11Skettenis 		mii_flags |= MIIF_SETDELAY | MIIF_RXID | MIIF_TXID;
244e9f49f11Skettenis 		break;
245e9f49f11Skettenis 	case DWQE_PHY_MODE_RGMII_RXID:
246e9f49f11Skettenis 		mii_flags |= MIIF_SETDELAY | MIIF_RXID;
247e9f49f11Skettenis 		break;
248e9f49f11Skettenis 	case DWQE_PHY_MODE_RGMII_TXID:
249e9f49f11Skettenis 		mii_flags |= MIIF_SETDELAY | MIIF_TXID;
250e9f49f11Skettenis 		break;
251e9f49f11Skettenis 	default:
252e9f49f11Skettenis 		break;
253e9f49f11Skettenis 	}
254e9f49f11Skettenis 
255305ac5f9Spatrick 	mii_attach(&sc->sc_dev, &sc->sc_mii, 0xffffffff, sc->sc_phyloc,
256e9f49f11Skettenis 	    (sc->sc_phyloc == MII_PHY_ANY) ? 0 : MII_OFFSET_ANY, mii_flags);
257305ac5f9Spatrick 	if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) {
258305ac5f9Spatrick 		printf("%s: no PHY found!\n", sc->sc_dev.dv_xname);
259305ac5f9Spatrick 		ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_MANUAL, 0, NULL);
260305ac5f9Spatrick 		ifmedia_set(&sc->sc_media, IFM_ETHER|IFM_MANUAL);
261305ac5f9Spatrick 	} else
262305ac5f9Spatrick 		ifmedia_set(&sc->sc_media, IFM_ETHER|IFM_AUTO);
263305ac5f9Spatrick }
264305ac5f9Spatrick 
265305ac5f9Spatrick uint32_t
dwqe_read(struct dwqe_softc * sc,bus_addr_t addr)266305ac5f9Spatrick dwqe_read(struct dwqe_softc *sc, bus_addr_t addr)
267305ac5f9Spatrick {
268305ac5f9Spatrick 	return bus_space_read_4(sc->sc_iot, sc->sc_ioh, addr);
269305ac5f9Spatrick }
270305ac5f9Spatrick 
271305ac5f9Spatrick void
dwqe_write(struct dwqe_softc * sc,bus_addr_t addr,uint32_t data)272305ac5f9Spatrick dwqe_write(struct dwqe_softc *sc, bus_addr_t addr, uint32_t data)
273305ac5f9Spatrick {
274305ac5f9Spatrick 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, addr, data);
275305ac5f9Spatrick }
276305ac5f9Spatrick 
277305ac5f9Spatrick void
dwqe_lladdr_read(struct dwqe_softc * sc,uint8_t * lladdr)278305ac5f9Spatrick dwqe_lladdr_read(struct dwqe_softc *sc, uint8_t *lladdr)
279305ac5f9Spatrick {
280305ac5f9Spatrick 	uint32_t machi, maclo;
281305ac5f9Spatrick 
282305ac5f9Spatrick 	machi = dwqe_read(sc, GMAC_MAC_ADDR0_HI);
283305ac5f9Spatrick 	maclo = dwqe_read(sc, GMAC_MAC_ADDR0_LO);
284305ac5f9Spatrick 
285305ac5f9Spatrick 	if (machi || maclo) {
286305ac5f9Spatrick 		lladdr[0] = (maclo >> 0) & 0xff;
287305ac5f9Spatrick 		lladdr[1] = (maclo >> 8) & 0xff;
288305ac5f9Spatrick 		lladdr[2] = (maclo >> 16) & 0xff;
289305ac5f9Spatrick 		lladdr[3] = (maclo >> 24) & 0xff;
290305ac5f9Spatrick 		lladdr[4] = (machi >> 0) & 0xff;
291305ac5f9Spatrick 		lladdr[5] = (machi >> 8) & 0xff;
292305ac5f9Spatrick 	} else {
293305ac5f9Spatrick 		ether_fakeaddr(&sc->sc_ac.ac_if);
294305ac5f9Spatrick 	}
295305ac5f9Spatrick }
296305ac5f9Spatrick 
297305ac5f9Spatrick void
dwqe_lladdr_write(struct dwqe_softc * sc)298305ac5f9Spatrick dwqe_lladdr_write(struct dwqe_softc *sc)
299305ac5f9Spatrick {
300305ac5f9Spatrick 	dwqe_write(sc, GMAC_MAC_ADDR0_HI,
301305ac5f9Spatrick 	    sc->sc_lladdr[5] << 8 | sc->sc_lladdr[4] << 0);
302305ac5f9Spatrick 	dwqe_write(sc, GMAC_MAC_ADDR0_LO,
303305ac5f9Spatrick 	    sc->sc_lladdr[3] << 24 | sc->sc_lladdr[2] << 16 |
304305ac5f9Spatrick 	    sc->sc_lladdr[1] << 8 | sc->sc_lladdr[0] << 0);
305305ac5f9Spatrick }
306305ac5f9Spatrick 
307305ac5f9Spatrick void
dwqe_start(struct ifqueue * ifq)308305ac5f9Spatrick dwqe_start(struct ifqueue *ifq)
309305ac5f9Spatrick {
310305ac5f9Spatrick 	struct ifnet *ifp = ifq->ifq_if;
311305ac5f9Spatrick 	struct dwqe_softc *sc = ifp->if_softc;
312305ac5f9Spatrick 	struct mbuf *m;
313305ac5f9Spatrick 	int error, idx, left, used;
314305ac5f9Spatrick 
315305ac5f9Spatrick 	if (!(ifp->if_flags & IFF_RUNNING))
316305ac5f9Spatrick 		return;
317305ac5f9Spatrick 	if (ifq_is_oactive(&ifp->if_snd))
318305ac5f9Spatrick 		return;
319305ac5f9Spatrick 	if (ifq_empty(&ifp->if_snd))
320305ac5f9Spatrick 		return;
321305ac5f9Spatrick 	if (!sc->sc_link)
322305ac5f9Spatrick 		return;
323305ac5f9Spatrick 
324305ac5f9Spatrick 	idx = sc->sc_tx_prod;
325305ac5f9Spatrick 	left = sc->sc_tx_cons;
326305ac5f9Spatrick 	if (left <= idx)
327305ac5f9Spatrick 		left += DWQE_NTXDESC;
328305ac5f9Spatrick 	left -= idx;
329305ac5f9Spatrick 	used = 0;
330305ac5f9Spatrick 
331305ac5f9Spatrick 	for (;;) {
332305ac5f9Spatrick 		if (used + DWQE_NTXSEGS + 1 > left) {
333305ac5f9Spatrick 			ifq_set_oactive(ifq);
334305ac5f9Spatrick 			break;
335305ac5f9Spatrick 		}
336305ac5f9Spatrick 
337305ac5f9Spatrick 		m = ifq_dequeue(ifq);
338305ac5f9Spatrick 		if (m == NULL)
339305ac5f9Spatrick 			break;
340305ac5f9Spatrick 
341305ac5f9Spatrick 		error = dwqe_encap(sc, m, &idx, &used);
342305ac5f9Spatrick 		if (error == EFBIG) {
343305ac5f9Spatrick 			m_freem(m); /* give up: drop it */
344305ac5f9Spatrick 			ifp->if_oerrors++;
345305ac5f9Spatrick 			continue;
346305ac5f9Spatrick 		}
347305ac5f9Spatrick 
348305ac5f9Spatrick #if NBPFILTER > 0
349305ac5f9Spatrick 		if (ifp->if_bpf)
350305ac5f9Spatrick 			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
351305ac5f9Spatrick #endif
352305ac5f9Spatrick 	}
353305ac5f9Spatrick 
354555d2e79Suwe 	if (used > 0) {
355305ac5f9Spatrick 		sc->sc_tx_prod = idx;
356305ac5f9Spatrick 
357305ac5f9Spatrick 		/* Set a timeout in case the chip goes out to lunch. */
358305ac5f9Spatrick 		ifp->if_timer = 5;
359555d2e79Suwe 
360555d2e79Suwe 		/*
361555d2e79Suwe 		 * Start the transmit process after the last in-use Tx
362555d2e79Suwe 		 * descriptor's OWN bit has been updated.
363555d2e79Suwe 		 */
364555d2e79Suwe 		dwqe_write(sc, GMAC_CHAN_TX_END_ADDR(0), DWQE_DMA_DVA(sc->sc_txring) +
365555d2e79Suwe 		    idx * sizeof(struct dwqe_desc));
366305ac5f9Spatrick 	}
367305ac5f9Spatrick }
368305ac5f9Spatrick 
369305ac5f9Spatrick int
dwqe_ioctl(struct ifnet * ifp,u_long cmd,caddr_t addr)370305ac5f9Spatrick dwqe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr)
371305ac5f9Spatrick {
372305ac5f9Spatrick 	struct dwqe_softc *sc = ifp->if_softc;
373305ac5f9Spatrick 	struct ifreq *ifr = (struct ifreq *)addr;
374305ac5f9Spatrick 	int error = 0, s;
375305ac5f9Spatrick 
376305ac5f9Spatrick 	s = splnet();
377305ac5f9Spatrick 
378305ac5f9Spatrick 	switch (cmd) {
379305ac5f9Spatrick 	case SIOCSIFADDR:
380305ac5f9Spatrick 		ifp->if_flags |= IFF_UP;
381305ac5f9Spatrick 		/* FALLTHROUGH */
382305ac5f9Spatrick 	case SIOCSIFFLAGS:
383305ac5f9Spatrick 		if (ifp->if_flags & IFF_UP) {
384305ac5f9Spatrick 			if (ifp->if_flags & IFF_RUNNING)
385305ac5f9Spatrick 				error = ENETRESET;
386305ac5f9Spatrick 			else
387305ac5f9Spatrick 				dwqe_up(sc);
388305ac5f9Spatrick 		} else {
389305ac5f9Spatrick 			if (ifp->if_flags & IFF_RUNNING)
390305ac5f9Spatrick 				dwqe_down(sc);
391305ac5f9Spatrick 		}
392305ac5f9Spatrick 		break;
393305ac5f9Spatrick 
394305ac5f9Spatrick 	case SIOCGIFMEDIA:
395305ac5f9Spatrick 	case SIOCSIFMEDIA:
39645f5f3c8Sdlg 		if (sc->sc_fixed_link)
39745f5f3c8Sdlg 			error = ENOTTY;
39845f5f3c8Sdlg 		else
399305ac5f9Spatrick 			error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
400305ac5f9Spatrick 		break;
401305ac5f9Spatrick 
402305ac5f9Spatrick 	case SIOCGIFRXR:
403305ac5f9Spatrick 		error = if_rxr_ioctl((struct if_rxrinfo *)ifr->ifr_data,
404305ac5f9Spatrick 		    NULL, MCLBYTES, &sc->sc_rx_ring);
405305ac5f9Spatrick 		break;
406305ac5f9Spatrick 
407305ac5f9Spatrick 	default:
408305ac5f9Spatrick 		error = ether_ioctl(ifp, &sc->sc_ac, cmd, addr);
409305ac5f9Spatrick 		break;
410305ac5f9Spatrick 	}
411305ac5f9Spatrick 
412305ac5f9Spatrick 	if (error == ENETRESET) {
413305ac5f9Spatrick 		if (ifp->if_flags & IFF_RUNNING)
414305ac5f9Spatrick 			dwqe_iff(sc);
415305ac5f9Spatrick 		error = 0;
416305ac5f9Spatrick 	}
417305ac5f9Spatrick 
418305ac5f9Spatrick 	splx(s);
419305ac5f9Spatrick 	return (error);
420305ac5f9Spatrick }
421305ac5f9Spatrick 
422305ac5f9Spatrick void
dwqe_watchdog(struct ifnet * ifp)423305ac5f9Spatrick dwqe_watchdog(struct ifnet *ifp)
424305ac5f9Spatrick {
425305ac5f9Spatrick 	printf("%s\n", __func__);
426305ac5f9Spatrick }
427305ac5f9Spatrick 
428305ac5f9Spatrick int
dwqe_media_change(struct ifnet * ifp)429305ac5f9Spatrick dwqe_media_change(struct ifnet *ifp)
430305ac5f9Spatrick {
431305ac5f9Spatrick 	struct dwqe_softc *sc = ifp->if_softc;
432305ac5f9Spatrick 
433305ac5f9Spatrick 	if (LIST_FIRST(&sc->sc_mii.mii_phys))
434305ac5f9Spatrick 		mii_mediachg(&sc->sc_mii);
435305ac5f9Spatrick 
436305ac5f9Spatrick 	return (0);
437305ac5f9Spatrick }
438305ac5f9Spatrick 
439305ac5f9Spatrick void
dwqe_media_status(struct ifnet * ifp,struct ifmediareq * ifmr)440305ac5f9Spatrick dwqe_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
441305ac5f9Spatrick {
442305ac5f9Spatrick 	struct dwqe_softc *sc = ifp->if_softc;
443305ac5f9Spatrick 
444305ac5f9Spatrick 	if (LIST_FIRST(&sc->sc_mii.mii_phys)) {
445305ac5f9Spatrick 		mii_pollstat(&sc->sc_mii);
446305ac5f9Spatrick 		ifmr->ifm_active = sc->sc_mii.mii_media_active;
447305ac5f9Spatrick 		ifmr->ifm_status = sc->sc_mii.mii_media_status;
448305ac5f9Spatrick 	}
449305ac5f9Spatrick }
450305ac5f9Spatrick 
451305ac5f9Spatrick int
dwqe_mii_readreg(struct device * self,int phy,int reg)452305ac5f9Spatrick dwqe_mii_readreg(struct device *self, int phy, int reg)
453305ac5f9Spatrick {
454305ac5f9Spatrick 	struct dwqe_softc *sc = (void *)self;
455305ac5f9Spatrick 	int n;
456305ac5f9Spatrick 
457305ac5f9Spatrick 	dwqe_write(sc, GMAC_MAC_MDIO_ADDR,
4580738886aSkettenis 	    (sc->sc_clk << GMAC_MAC_MDIO_ADDR_CR_SHIFT) |
459305ac5f9Spatrick 	    (phy << GMAC_MAC_MDIO_ADDR_PA_SHIFT) |
460305ac5f9Spatrick 	    (reg << GMAC_MAC_MDIO_ADDR_RDA_SHIFT) |
461305ac5f9Spatrick 	    GMAC_MAC_MDIO_ADDR_GOC_READ |
462305ac5f9Spatrick 	    GMAC_MAC_MDIO_ADDR_GB);
463305ac5f9Spatrick 
4649fb70083Sdlg 	for (n = 0; n < 2000; n++) {
4659fb70083Sdlg 		delay(10);
466305ac5f9Spatrick 		if ((dwqe_read(sc, GMAC_MAC_MDIO_ADDR) & GMAC_MAC_MDIO_ADDR_GB) == 0)
467305ac5f9Spatrick 			return dwqe_read(sc, GMAC_MAC_MDIO_DATA);
468305ac5f9Spatrick 	}
469305ac5f9Spatrick 
470305ac5f9Spatrick 	printf("%s: mii_read timeout\n", sc->sc_dev.dv_xname);
471305ac5f9Spatrick 	return (0);
472305ac5f9Spatrick }
473305ac5f9Spatrick 
474305ac5f9Spatrick void
dwqe_mii_writereg(struct device * self,int phy,int reg,int val)475305ac5f9Spatrick dwqe_mii_writereg(struct device *self, int phy, int reg, int val)
476305ac5f9Spatrick {
477305ac5f9Spatrick 	struct dwqe_softc *sc = (void *)self;
478305ac5f9Spatrick 	int n;
479305ac5f9Spatrick 
480305ac5f9Spatrick 	dwqe_write(sc, GMAC_MAC_MDIO_DATA, val);
481305ac5f9Spatrick 	dwqe_write(sc, GMAC_MAC_MDIO_ADDR,
4820738886aSkettenis 	    (sc->sc_clk << GMAC_MAC_MDIO_ADDR_CR_SHIFT) |
483305ac5f9Spatrick 	    (phy << GMAC_MAC_MDIO_ADDR_PA_SHIFT) |
484305ac5f9Spatrick 	    (reg << GMAC_MAC_MDIO_ADDR_RDA_SHIFT) |
485305ac5f9Spatrick 	    GMAC_MAC_MDIO_ADDR_GOC_WRITE |
486305ac5f9Spatrick 	    GMAC_MAC_MDIO_ADDR_GB);
4879fb70083Sdlg 
4889fb70083Sdlg 	for (n = 0; n < 2000; n++) {
4899fb70083Sdlg 		delay(10);
490305ac5f9Spatrick 		if ((dwqe_read(sc, GMAC_MAC_MDIO_ADDR) & GMAC_MAC_MDIO_ADDR_GB) == 0)
491305ac5f9Spatrick 			return;
492305ac5f9Spatrick 	}
493305ac5f9Spatrick 
494305ac5f9Spatrick 	printf("%s: mii_write timeout\n", sc->sc_dev.dv_xname);
495305ac5f9Spatrick }
496305ac5f9Spatrick 
497305ac5f9Spatrick void
dwqe_mii_statchg(struct device * self)498305ac5f9Spatrick dwqe_mii_statchg(struct device *self)
499305ac5f9Spatrick {
500305ac5f9Spatrick 	struct dwqe_softc *sc = (void *)self;
501566e78fcSdlg 	struct ifnet *ifp = &sc->sc_ac.ac_if;
502305ac5f9Spatrick 	uint32_t conf;
503305ac5f9Spatrick 
504305ac5f9Spatrick 	conf = dwqe_read(sc, GMAC_MAC_CONF);
505305ac5f9Spatrick 	conf &= ~(GMAC_MAC_CONF_PS | GMAC_MAC_CONF_FES);
506305ac5f9Spatrick 
507566e78fcSdlg 	switch (ifp->if_baudrate) {
508566e78fcSdlg 	case IF_Mbps(1000):
509305ac5f9Spatrick 		sc->sc_link = 1;
510305ac5f9Spatrick 		break;
511566e78fcSdlg 	case IF_Mbps(100):
512305ac5f9Spatrick 		conf |= GMAC_MAC_CONF_PS | GMAC_MAC_CONF_FES;
513305ac5f9Spatrick 		sc->sc_link = 1;
514305ac5f9Spatrick 		break;
515566e78fcSdlg 	case IF_Mbps(10):
516305ac5f9Spatrick 		conf |= GMAC_MAC_CONF_PS;
517305ac5f9Spatrick 		sc->sc_link = 1;
518305ac5f9Spatrick 		break;
519305ac5f9Spatrick 	default:
520305ac5f9Spatrick 		sc->sc_link = 0;
521305ac5f9Spatrick 		return;
522305ac5f9Spatrick 	}
523305ac5f9Spatrick 
524305ac5f9Spatrick 	if (sc->sc_link == 0)
525305ac5f9Spatrick 		return;
526305ac5f9Spatrick 
527305ac5f9Spatrick 	conf &= ~GMAC_MAC_CONF_DM;
528566e78fcSdlg 	if (ifp->if_link_state == LINK_STATE_FULL_DUPLEX)
529305ac5f9Spatrick 		conf |= GMAC_MAC_CONF_DM;
530305ac5f9Spatrick 
531305ac5f9Spatrick 	dwqe_write(sc, GMAC_MAC_CONF, conf);
532305ac5f9Spatrick }
533305ac5f9Spatrick 
534305ac5f9Spatrick void
dwqe_tick(void * arg)535305ac5f9Spatrick dwqe_tick(void *arg)
536305ac5f9Spatrick {
537305ac5f9Spatrick 	struct dwqe_softc *sc = arg;
538305ac5f9Spatrick 	int s;
539305ac5f9Spatrick 
540305ac5f9Spatrick 	s = splnet();
541305ac5f9Spatrick 	mii_tick(&sc->sc_mii);
542305ac5f9Spatrick 	splx(s);
543305ac5f9Spatrick 
54447707f8eSdlg 	timeout_add_sec(&sc->sc_phy_tick, 1);
545305ac5f9Spatrick }
546305ac5f9Spatrick 
547305ac5f9Spatrick void
dwqe_rxtick(void * arg)548305ac5f9Spatrick dwqe_rxtick(void *arg)
549305ac5f9Spatrick {
550305ac5f9Spatrick 	struct dwqe_softc *sc = arg;
551305ac5f9Spatrick 	int s;
552305ac5f9Spatrick 
553305ac5f9Spatrick 	s = splnet();
554305ac5f9Spatrick 
555305ac5f9Spatrick 	/* TODO: disable RXQ? */
556305ac5f9Spatrick 	printf("%s:%d\n", __func__, __LINE__);
557305ac5f9Spatrick 
558305ac5f9Spatrick 	bus_dmamap_sync(sc->sc_dmat, DWQE_DMA_MAP(sc->sc_rxring),
559305ac5f9Spatrick 	    0, DWQE_DMA_LEN(sc->sc_rxring),
560305ac5f9Spatrick 	    BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
561305ac5f9Spatrick 
562305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_RX_BASE_ADDR_HI(0), 0);
563305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_RX_BASE_ADDR(0), 0);
564305ac5f9Spatrick 
565305ac5f9Spatrick 	sc->sc_rx_prod = sc->sc_rx_cons = 0;
566305ac5f9Spatrick 	dwqe_fill_rx_ring(sc);
567305ac5f9Spatrick 
568305ac5f9Spatrick 	bus_dmamap_sync(sc->sc_dmat, DWQE_DMA_MAP(sc->sc_rxring),
569305ac5f9Spatrick 	    0, DWQE_DMA_LEN(sc->sc_rxring),
570305ac5f9Spatrick 	    BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
571305ac5f9Spatrick 
572305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_RX_BASE_ADDR_HI(0), DWQE_DMA_DVA(sc->sc_rxring) >> 32);
573305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_RX_BASE_ADDR(0), DWQE_DMA_DVA(sc->sc_rxring));
574305ac5f9Spatrick 
575305ac5f9Spatrick 	/* TODO: re-enable RXQ? */
576305ac5f9Spatrick 
577305ac5f9Spatrick 	splx(s);
578305ac5f9Spatrick }
579305ac5f9Spatrick 
580305ac5f9Spatrick int
dwqe_intr(void * arg)581305ac5f9Spatrick dwqe_intr(void *arg)
582305ac5f9Spatrick {
583305ac5f9Spatrick 	struct dwqe_softc *sc = arg;
584305ac5f9Spatrick 	uint32_t reg;
585305ac5f9Spatrick 
586305ac5f9Spatrick 	reg = dwqe_read(sc, GMAC_INT_STATUS);
587305ac5f9Spatrick 	dwqe_write(sc, GMAC_INT_STATUS, reg);
588305ac5f9Spatrick 
589305ac5f9Spatrick 	reg = dwqe_read(sc, GMAC_CHAN_STATUS(0));
590305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_STATUS(0), reg);
591305ac5f9Spatrick 
592305ac5f9Spatrick 	if (reg & GMAC_CHAN_STATUS_RI)
593305ac5f9Spatrick 		dwqe_rx_proc(sc);
594305ac5f9Spatrick 
595305ac5f9Spatrick 	if (reg & GMAC_CHAN_STATUS_TI)
596305ac5f9Spatrick 		dwqe_tx_proc(sc);
597305ac5f9Spatrick 
598305ac5f9Spatrick 	return (1);
599305ac5f9Spatrick }
600305ac5f9Spatrick 
601305ac5f9Spatrick void
dwqe_tx_proc(struct dwqe_softc * sc)602305ac5f9Spatrick dwqe_tx_proc(struct dwqe_softc *sc)
603305ac5f9Spatrick {
604305ac5f9Spatrick 	struct ifnet *ifp = &sc->sc_ac.ac_if;
605305ac5f9Spatrick 	struct dwqe_desc *txd;
606305ac5f9Spatrick 	struct dwqe_buf *txb;
607305ac5f9Spatrick 	int idx, txfree;
608305ac5f9Spatrick 
6095b86e6aeSstsp 	if ((ifp->if_flags & IFF_RUNNING) == 0)
6105b86e6aeSstsp 		return;
6115b86e6aeSstsp 
612305ac5f9Spatrick 	bus_dmamap_sync(sc->sc_dmat, DWQE_DMA_MAP(sc->sc_txring), 0,
613305ac5f9Spatrick 	    DWQE_DMA_LEN(sc->sc_txring),
614305ac5f9Spatrick 	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
615305ac5f9Spatrick 
616305ac5f9Spatrick 	txfree = 0;
617305ac5f9Spatrick 	while (sc->sc_tx_cons != sc->sc_tx_prod) {
618305ac5f9Spatrick 		idx = sc->sc_tx_cons;
619305ac5f9Spatrick 		KASSERT(idx < DWQE_NTXDESC);
620305ac5f9Spatrick 
621305ac5f9Spatrick 		txd = &sc->sc_txdesc[idx];
622305ac5f9Spatrick 		if (txd->sd_tdes3 & TDES3_OWN)
623305ac5f9Spatrick 			break;
624305ac5f9Spatrick 
625e8974f33Skettenis 		if (txd->sd_tdes3 & TDES3_ES)
626e8974f33Skettenis 			ifp->if_oerrors++;
627e8974f33Skettenis 
628305ac5f9Spatrick 		txb = &sc->sc_txbuf[idx];
629305ac5f9Spatrick 		if (txb->tb_m) {
630305ac5f9Spatrick 			bus_dmamap_sync(sc->sc_dmat, txb->tb_map, 0,
631305ac5f9Spatrick 			    txb->tb_map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
632305ac5f9Spatrick 			bus_dmamap_unload(sc->sc_dmat, txb->tb_map);
633305ac5f9Spatrick 
634305ac5f9Spatrick 			m_freem(txb->tb_m);
635305ac5f9Spatrick 			txb->tb_m = NULL;
636305ac5f9Spatrick 		}
637305ac5f9Spatrick 
638305ac5f9Spatrick 		txfree++;
639305ac5f9Spatrick 
640305ac5f9Spatrick 		if (sc->sc_tx_cons == (DWQE_NTXDESC - 1))
641305ac5f9Spatrick 			sc->sc_tx_cons = 0;
642305ac5f9Spatrick 		else
643305ac5f9Spatrick 			sc->sc_tx_cons++;
644305ac5f9Spatrick 
645305ac5f9Spatrick 		txd->sd_tdes3 = 0;
646305ac5f9Spatrick 	}
647305ac5f9Spatrick 
648305ac5f9Spatrick 	if (sc->sc_tx_cons == sc->sc_tx_prod)
649305ac5f9Spatrick 		ifp->if_timer = 0;
650305ac5f9Spatrick 
651305ac5f9Spatrick 	if (txfree) {
652305ac5f9Spatrick 		if (ifq_is_oactive(&ifp->if_snd))
653305ac5f9Spatrick 			ifq_restart(&ifp->if_snd);
654305ac5f9Spatrick 	}
655305ac5f9Spatrick }
656305ac5f9Spatrick 
657a72369d8Sstsp int
dwqe_have_rx_csum_offload(struct dwqe_softc * sc)658a72369d8Sstsp dwqe_have_rx_csum_offload(struct dwqe_softc *sc)
659a72369d8Sstsp {
660a72369d8Sstsp 	return (sc->sc_hw_feature[0] & GMAC_MAC_HW_FEATURE0_RXCOESEL);
661a72369d8Sstsp }
662a72369d8Sstsp 
663a72369d8Sstsp void
dwqe_rx_csum(struct dwqe_softc * sc,struct mbuf * m,struct dwqe_desc * rxd)664a72369d8Sstsp dwqe_rx_csum(struct dwqe_softc *sc, struct mbuf *m, struct dwqe_desc *rxd)
665a72369d8Sstsp {
666a72369d8Sstsp 	uint16_t csum_flags = 0;
667a72369d8Sstsp 
668a72369d8Sstsp 	/*
669a72369d8Sstsp 	 * Checksum offload must be supported, the Last-Descriptor bit
670a72369d8Sstsp 	 * must be set, RDES1 must be valid, and checksumming must not
671a72369d8Sstsp 	 * have been bypassed (happens for unknown packet types), and
672a72369d8Sstsp 	 * an IP header must have been detected.
673a72369d8Sstsp 	 */
674a72369d8Sstsp 	if (!dwqe_have_rx_csum_offload(sc) ||
675a72369d8Sstsp 	    (rxd->sd_tdes3 & RDES3_LD) == 0 ||
676a72369d8Sstsp 	    (rxd->sd_tdes3 & RDES3_RDES1_VALID) == 0 ||
677a72369d8Sstsp 	    (rxd->sd_tdes1 & RDES1_IP_CSUM_BYPASS) ||
678a72369d8Sstsp 	    (rxd->sd_tdes1 & (RDES1_IPV4_HDR | RDES1_IPV6_HDR)) == 0)
679a72369d8Sstsp 		return;
680a72369d8Sstsp 
681a72369d8Sstsp 	/* If the IP header checksum is invalid then the payload is ignored. */
682a72369d8Sstsp 	if (rxd->sd_tdes1 & RDES1_IP_HDR_ERROR) {
683a72369d8Sstsp 		if (rxd->sd_tdes1 & RDES1_IPV4_HDR)
684a72369d8Sstsp 			csum_flags |= M_IPV4_CSUM_IN_BAD;
685a72369d8Sstsp 	} else {
686a72369d8Sstsp 		if (rxd->sd_tdes1 & RDES1_IPV4_HDR)
687a72369d8Sstsp 			csum_flags |= M_IPV4_CSUM_IN_OK;
688a72369d8Sstsp 
689a72369d8Sstsp 		/* Detect payload type and corresponding checksum errors. */
690a72369d8Sstsp 		switch (rxd->sd_tdes1 & RDES1_IP_PAYLOAD_TYPE) {
691a72369d8Sstsp 		case RDES1_IP_PAYLOAD_UDP:
692a72369d8Sstsp 			if (rxd->sd_tdes1 & RDES1_IP_PAYLOAD_ERROR)
693a72369d8Sstsp 				csum_flags |= M_UDP_CSUM_IN_BAD;
694a72369d8Sstsp 			else
695a72369d8Sstsp 				csum_flags |= M_UDP_CSUM_IN_OK;
696a72369d8Sstsp 			break;
697a72369d8Sstsp 		case RDES1_IP_PAYLOAD_TCP:
698a72369d8Sstsp 			if (rxd->sd_tdes1 & RDES1_IP_PAYLOAD_ERROR)
699a72369d8Sstsp 				csum_flags |= M_TCP_CSUM_IN_BAD;
700a72369d8Sstsp 			else
701a72369d8Sstsp 				csum_flags |= M_TCP_CSUM_IN_OK;
702a72369d8Sstsp 			break;
703a72369d8Sstsp 		case RDES1_IP_PAYLOAD_ICMP:
704a72369d8Sstsp 			if (rxd->sd_tdes1 & RDES1_IP_PAYLOAD_ERROR)
705a72369d8Sstsp 				csum_flags |= M_ICMP_CSUM_IN_BAD;
706a72369d8Sstsp 			else
707a72369d8Sstsp 				csum_flags |= M_ICMP_CSUM_IN_OK;
708a72369d8Sstsp 			break;
709a72369d8Sstsp 		default:
710a72369d8Sstsp 			break;
711a72369d8Sstsp 		}
712a72369d8Sstsp 	}
713a72369d8Sstsp 
714a72369d8Sstsp 	m->m_pkthdr.csum_flags |= csum_flags;
715a72369d8Sstsp }
716a72369d8Sstsp 
717305ac5f9Spatrick void
dwqe_rx_proc(struct dwqe_softc * sc)718305ac5f9Spatrick dwqe_rx_proc(struct dwqe_softc *sc)
719305ac5f9Spatrick {
720305ac5f9Spatrick 	struct ifnet *ifp = &sc->sc_ac.ac_if;
721305ac5f9Spatrick 	struct dwqe_desc *rxd;
722305ac5f9Spatrick 	struct dwqe_buf *rxb;
723305ac5f9Spatrick 	struct mbuf_list ml = MBUF_LIST_INITIALIZER();
724305ac5f9Spatrick 	struct mbuf *m;
725305ac5f9Spatrick 	int idx, len, cnt, put;
726305ac5f9Spatrick 
727305ac5f9Spatrick 	if ((ifp->if_flags & IFF_RUNNING) == 0)
728305ac5f9Spatrick 		return;
729305ac5f9Spatrick 
730305ac5f9Spatrick 	bus_dmamap_sync(sc->sc_dmat, DWQE_DMA_MAP(sc->sc_rxring), 0,
731305ac5f9Spatrick 	    DWQE_DMA_LEN(sc->sc_rxring),
732305ac5f9Spatrick 	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
733305ac5f9Spatrick 
734305ac5f9Spatrick 	cnt = if_rxr_inuse(&sc->sc_rx_ring);
735305ac5f9Spatrick 	put = 0;
736305ac5f9Spatrick 	while (put < cnt) {
737305ac5f9Spatrick 		idx = sc->sc_rx_cons;
738305ac5f9Spatrick 		KASSERT(idx < DWQE_NRXDESC);
739305ac5f9Spatrick 
740305ac5f9Spatrick 		rxd = &sc->sc_rxdesc[idx];
741305ac5f9Spatrick 		if (rxd->sd_tdes3 & RDES3_OWN)
742305ac5f9Spatrick 			break;
743305ac5f9Spatrick 
744305ac5f9Spatrick 		len = rxd->sd_tdes3 & RDES3_LENGTH;
745305ac5f9Spatrick 		rxb = &sc->sc_rxbuf[idx];
746305ac5f9Spatrick 		KASSERT(rxb->tb_m);
747305ac5f9Spatrick 
748305ac5f9Spatrick 		bus_dmamap_sync(sc->sc_dmat, rxb->tb_map, 0,
749305ac5f9Spatrick 		    len, BUS_DMASYNC_POSTREAD);
750305ac5f9Spatrick 		bus_dmamap_unload(sc->sc_dmat, rxb->tb_map);
751305ac5f9Spatrick 
752b58ef082Skettenis 		m = rxb->tb_m;
753b58ef082Skettenis 		rxb->tb_m = NULL;
754b58ef082Skettenis 
755b58ef082Skettenis 		if (rxd->sd_tdes3 & RDES3_ES) {
756b58ef082Skettenis 			ifp->if_ierrors++;
757b58ef082Skettenis 			m_freem(m);
758b58ef082Skettenis 		} else {
759305ac5f9Spatrick 			/* Strip off CRC. */
760305ac5f9Spatrick 			len -= ETHER_CRC_LEN;
761305ac5f9Spatrick 			KASSERT(len > 0);
762305ac5f9Spatrick 
763305ac5f9Spatrick 			m->m_pkthdr.len = m->m_len = len;
764305ac5f9Spatrick 
765a72369d8Sstsp 			dwqe_rx_csum(sc, m, rxd);
766305ac5f9Spatrick 			ml_enqueue(&ml, m);
767b58ef082Skettenis 		}
768305ac5f9Spatrick 
769305ac5f9Spatrick 		put++;
770305ac5f9Spatrick 		if (sc->sc_rx_cons == (DWQE_NRXDESC - 1))
771305ac5f9Spatrick 			sc->sc_rx_cons = 0;
772305ac5f9Spatrick 		else
773305ac5f9Spatrick 			sc->sc_rx_cons++;
774305ac5f9Spatrick 	}
775305ac5f9Spatrick 
776305ac5f9Spatrick 	if_rxr_put(&sc->sc_rx_ring, put);
777305ac5f9Spatrick 	if (ifiq_input(&ifp->if_rcv, &ml))
778305ac5f9Spatrick 		if_rxr_livelocked(&sc->sc_rx_ring);
779305ac5f9Spatrick 
780305ac5f9Spatrick 	dwqe_fill_rx_ring(sc);
781305ac5f9Spatrick 
782305ac5f9Spatrick 	bus_dmamap_sync(sc->sc_dmat, DWQE_DMA_MAP(sc->sc_rxring), 0,
783305ac5f9Spatrick 	    DWQE_DMA_LEN(sc->sc_rxring),
784305ac5f9Spatrick 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
785305ac5f9Spatrick }
786305ac5f9Spatrick 
787305ac5f9Spatrick void
dwqe_up(struct dwqe_softc * sc)788305ac5f9Spatrick dwqe_up(struct dwqe_softc *sc)
789305ac5f9Spatrick {
790305ac5f9Spatrick 	struct ifnet *ifp = &sc->sc_ac.ac_if;
791305ac5f9Spatrick 	struct dwqe_buf *txb, *rxb;
7926e9149a4Sstsp 	uint32_t mode, reg, fifosz, tqs, rqs;
793305ac5f9Spatrick 	int i;
794305ac5f9Spatrick 
795305ac5f9Spatrick 	/* Allocate Tx descriptor ring. */
796305ac5f9Spatrick 	sc->sc_txring = dwqe_dmamem_alloc(sc,
797305ac5f9Spatrick 	    DWQE_NTXDESC * sizeof(struct dwqe_desc), 8);
798305ac5f9Spatrick 	sc->sc_txdesc = DWQE_DMA_KVA(sc->sc_txring);
799305ac5f9Spatrick 
800305ac5f9Spatrick 	sc->sc_txbuf = malloc(sizeof(struct dwqe_buf) * DWQE_NTXDESC,
801305ac5f9Spatrick 	    M_DEVBUF, M_WAITOK);
802305ac5f9Spatrick 	for (i = 0; i < DWQE_NTXDESC; i++) {
803305ac5f9Spatrick 		txb = &sc->sc_txbuf[i];
804305ac5f9Spatrick 		bus_dmamap_create(sc->sc_dmat, MCLBYTES, DWQE_NTXSEGS,
805305ac5f9Spatrick 		    MCLBYTES, 0, BUS_DMA_WAITOK, &txb->tb_map);
806305ac5f9Spatrick 		txb->tb_m = NULL;
807305ac5f9Spatrick 	}
808305ac5f9Spatrick 
809305ac5f9Spatrick 	bus_dmamap_sync(sc->sc_dmat, DWQE_DMA_MAP(sc->sc_txring),
810305ac5f9Spatrick 	    0, DWQE_DMA_LEN(sc->sc_txring), BUS_DMASYNC_PREWRITE);
811305ac5f9Spatrick 
812305ac5f9Spatrick 	sc->sc_tx_prod = sc->sc_tx_cons = 0;
813305ac5f9Spatrick 
814305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_TX_BASE_ADDR_HI(0), DWQE_DMA_DVA(sc->sc_txring) >> 32);
815305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_TX_BASE_ADDR(0), DWQE_DMA_DVA(sc->sc_txring));
816305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_TX_RING_LEN(0), DWQE_NTXDESC - 1);
817305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_TX_END_ADDR(0), DWQE_DMA_DVA(sc->sc_txring));
818305ac5f9Spatrick 
819305ac5f9Spatrick 	/* Allocate  descriptor ring. */
820305ac5f9Spatrick 	sc->sc_rxring = dwqe_dmamem_alloc(sc,
821305ac5f9Spatrick 	    DWQE_NRXDESC * sizeof(struct dwqe_desc), 8);
822305ac5f9Spatrick 	sc->sc_rxdesc = DWQE_DMA_KVA(sc->sc_rxring);
823305ac5f9Spatrick 
824305ac5f9Spatrick 	sc->sc_rxbuf = malloc(sizeof(struct dwqe_buf) * DWQE_NRXDESC,
825305ac5f9Spatrick 	    M_DEVBUF, M_WAITOK);
826305ac5f9Spatrick 
827305ac5f9Spatrick 	for (i = 0; i < DWQE_NRXDESC; i++) {
828305ac5f9Spatrick 		rxb = &sc->sc_rxbuf[i];
829305ac5f9Spatrick 		bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1,
830305ac5f9Spatrick 		    MCLBYTES, 0, BUS_DMA_WAITOK, &rxb->tb_map);
831305ac5f9Spatrick 		rxb->tb_m = NULL;
832305ac5f9Spatrick 	}
833305ac5f9Spatrick 
834f51cb083Sbluhm 	if_rxr_init(&sc->sc_rx_ring, 2, DWQE_NRXDESC - 1);
835305ac5f9Spatrick 
836305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_RX_BASE_ADDR_HI(0), DWQE_DMA_DVA(sc->sc_rxring) >> 32);
837305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_RX_BASE_ADDR(0), DWQE_DMA_DVA(sc->sc_rxring));
838305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_RX_RING_LEN(0), DWQE_NRXDESC - 1);
839305ac5f9Spatrick 
840305ac5f9Spatrick 	sc->sc_rx_prod = sc->sc_rx_cons = 0;
841305ac5f9Spatrick 	dwqe_fill_rx_ring(sc);
842305ac5f9Spatrick 
843305ac5f9Spatrick 	bus_dmamap_sync(sc->sc_dmat, DWQE_DMA_MAP(sc->sc_rxring),
844305ac5f9Spatrick 	    0, DWQE_DMA_LEN(sc->sc_rxring),
845305ac5f9Spatrick 	    BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
846305ac5f9Spatrick 
847305ac5f9Spatrick 	dwqe_lladdr_write(sc);
848305ac5f9Spatrick 
849305ac5f9Spatrick 	/* Configure media. */
850305ac5f9Spatrick 	if (LIST_FIRST(&sc->sc_mii.mii_phys))
851305ac5f9Spatrick 		mii_mediachg(&sc->sc_mii);
852305ac5f9Spatrick 
853305ac5f9Spatrick 	/* Program promiscuous mode and multicast filters. */
854305ac5f9Spatrick 	dwqe_iff(sc);
855305ac5f9Spatrick 
856305ac5f9Spatrick 	ifp->if_flags |= IFF_RUNNING;
857305ac5f9Spatrick 	ifq_clr_oactive(&ifp->if_snd);
858305ac5f9Spatrick 
859b97405d6Sstsp 	dwqe_write(sc, GMAC_MAC_1US_TIC_CTR, (sc->sc_clkrate / 1000000) - 1);
860305ac5f9Spatrick 
861305ac5f9Spatrick 	/* Start receive DMA */
862305ac5f9Spatrick 	reg = dwqe_read(sc, GMAC_CHAN_RX_CONTROL(0));
863305ac5f9Spatrick 	reg |= GMAC_CHAN_RX_CONTROL_SR;
864305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_RX_CONTROL(0), reg);
865305ac5f9Spatrick 
866305ac5f9Spatrick 	/* Start transmit DMA */
867305ac5f9Spatrick 	reg = dwqe_read(sc, GMAC_CHAN_TX_CONTROL(0));
868305ac5f9Spatrick 	reg |= GMAC_CHAN_TX_CONTROL_ST;
869305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_TX_CONTROL(0), reg);
870305ac5f9Spatrick 
871305ac5f9Spatrick 	mode = dwqe_read(sc, GMAC_MTL_CHAN_RX_OP_MODE(0));
872305ac5f9Spatrick 	if (sc->sc_force_thresh_dma_mode) {
873305ac5f9Spatrick 		mode &= ~GMAC_MTL_CHAN_RX_OP_MODE_RSF;
874305ac5f9Spatrick 		mode &= ~GMAC_MTL_CHAN_RX_OP_MODE_RTC_MASK;
875305ac5f9Spatrick 		mode |= GMAC_MTL_CHAN_RX_OP_MODE_RTC_128;
876305ac5f9Spatrick 	} else {
877305ac5f9Spatrick 		mode |= GMAC_MTL_CHAN_RX_OP_MODE_RSF;
878305ac5f9Spatrick 	}
879305ac5f9Spatrick 	mode &= ~GMAC_MTL_CHAN_RX_OP_MODE_RQS_MASK;
8806e9149a4Sstsp 	if (sc->sc_rxfifo_size)
8816e9149a4Sstsp 		fifosz = sc->sc_rxfifo_size;
8826e9149a4Sstsp 	else
8836e9149a4Sstsp 		fifosz = (128 <<
8846e9149a4Sstsp 		    GMAC_MAC_HW_FEATURE1_RXFIFOSIZE(sc->sc_hw_feature[1]));
8856e9149a4Sstsp 	rqs = fifosz / 256 - 1;
8866e9149a4Sstsp 	mode |= (rqs << GMAC_MTL_CHAN_RX_OP_MODE_RQS_SHIFT) &
8876e9149a4Sstsp 	   GMAC_MTL_CHAN_RX_OP_MODE_RQS_MASK;
8886e9149a4Sstsp 	if (fifosz >= 4096) {
8896e9149a4Sstsp 		mode |= GMAC_MTL_CHAN_RX_OP_MODE_EHFC;
8906e9149a4Sstsp 		mode &= ~GMAC_MTL_CHAN_RX_OP_MODE_RFD_MASK;
8916e9149a4Sstsp 		mode |= 0x3 << GMAC_MTL_CHAN_RX_OP_MODE_RFD_SHIFT;
8926e9149a4Sstsp 		mode &= ~GMAC_MTL_CHAN_RX_OP_MODE_RFA_MASK;
8936e9149a4Sstsp 		mode |= 0x1 << GMAC_MTL_CHAN_RX_OP_MODE_RFA_SHIFT;
8946e9149a4Sstsp 	}
895305ac5f9Spatrick 	dwqe_write(sc, GMAC_MTL_CHAN_RX_OP_MODE(0), mode);
896305ac5f9Spatrick 
897305ac5f9Spatrick 	mode = dwqe_read(sc, GMAC_MTL_CHAN_TX_OP_MODE(0));
898305ac5f9Spatrick 	if (sc->sc_force_thresh_dma_mode) {
899305ac5f9Spatrick 		mode &= ~GMAC_MTL_CHAN_TX_OP_MODE_TSF;
900305ac5f9Spatrick 		mode &= ~GMAC_MTL_CHAN_TX_OP_MODE_TTC_MASK;
901e8974f33Skettenis 		mode |= GMAC_MTL_CHAN_TX_OP_MODE_TTC_512;
902305ac5f9Spatrick 	} else {
903305ac5f9Spatrick 		mode |= GMAC_MTL_CHAN_TX_OP_MODE_TSF;
904305ac5f9Spatrick 	}
905305ac5f9Spatrick 	mode &= ~GMAC_MTL_CHAN_TX_OP_MODE_TXQEN_MASK;
906305ac5f9Spatrick 	mode |= GMAC_MTL_CHAN_TX_OP_MODE_TXQEN;
907305ac5f9Spatrick 	mode &= ~GMAC_MTL_CHAN_TX_OP_MODE_TQS_MASK;
9086e9149a4Sstsp 	if (sc->sc_txfifo_size)
9096e9149a4Sstsp 		fifosz = sc->sc_txfifo_size;
9106e9149a4Sstsp 	else
9116e9149a4Sstsp 		fifosz = (128 <<
9126e9149a4Sstsp 		    GMAC_MAC_HW_FEATURE1_TXFIFOSIZE(sc->sc_hw_feature[1]));
9136e9149a4Sstsp 	tqs = (fifosz / 256) - 1;
9146e9149a4Sstsp 	mode |= (tqs << GMAC_MTL_CHAN_TX_OP_MODE_TQS_SHIFT) &
9156e9149a4Sstsp 	    GMAC_MTL_CHAN_TX_OP_MODE_TQS_MASK;
916305ac5f9Spatrick 	dwqe_write(sc, GMAC_MTL_CHAN_TX_OP_MODE(0), mode);
917305ac5f9Spatrick 
918305ac5f9Spatrick 	reg = dwqe_read(sc, GMAC_QX_TX_FLOW_CTRL(0));
919305ac5f9Spatrick 	reg |= 0xffffU << GMAC_QX_TX_FLOW_CTRL_PT_SHIFT;
920305ac5f9Spatrick 	reg |= GMAC_QX_TX_FLOW_CTRL_TFE;
921305ac5f9Spatrick 	dwqe_write(sc, GMAC_QX_TX_FLOW_CTRL(0), reg);
922305ac5f9Spatrick 	reg = dwqe_read(sc, GMAC_RX_FLOW_CTRL);
923305ac5f9Spatrick 	reg |= GMAC_RX_FLOW_CTRL_RFE;
924305ac5f9Spatrick 	dwqe_write(sc, GMAC_RX_FLOW_CTRL, reg);
925305ac5f9Spatrick 
926305ac5f9Spatrick 	dwqe_write(sc, GMAC_RXQ_CTRL0, GMAC_RXQ_CTRL0_DCB_QUEUE_EN(0));
927305ac5f9Spatrick 
928305ac5f9Spatrick 	dwqe_write(sc, GMAC_MAC_CONF, dwqe_read(sc, GMAC_MAC_CONF) |
929305ac5f9Spatrick 	    GMAC_MAC_CONF_BE | GMAC_MAC_CONF_JD | GMAC_MAC_CONF_JE |
930305ac5f9Spatrick 	    GMAC_MAC_CONF_DCRS | GMAC_MAC_CONF_TE | GMAC_MAC_CONF_RE);
931305ac5f9Spatrick 
932305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_INTR_ENA(0),
933305ac5f9Spatrick 	    GMAC_CHAN_INTR_ENA_NIE |
934305ac5f9Spatrick 	    GMAC_CHAN_INTR_ENA_AIE |
935305ac5f9Spatrick 	    GMAC_CHAN_INTR_ENA_FBE |
936305ac5f9Spatrick 	    GMAC_CHAN_INTR_ENA_RIE |
937305ac5f9Spatrick 	    GMAC_CHAN_INTR_ENA_TIE);
938305ac5f9Spatrick 
93945f5f3c8Sdlg 	if (!sc->sc_fixed_link)
94047707f8eSdlg 		timeout_add_sec(&sc->sc_phy_tick, 1);
941a72369d8Sstsp 
942a72369d8Sstsp 	if (dwqe_have_rx_csum_offload(sc)) {
943a72369d8Sstsp 		reg = dwqe_read(sc, GMAC_MAC_CONF);
944a72369d8Sstsp 		reg |= GMAC_MAC_CONF_IPC;
945a72369d8Sstsp 		dwqe_write(sc, GMAC_MAC_CONF, reg);
946a72369d8Sstsp 	}
947305ac5f9Spatrick }
948305ac5f9Spatrick 
949305ac5f9Spatrick void
dwqe_down(struct dwqe_softc * sc)950305ac5f9Spatrick dwqe_down(struct dwqe_softc *sc)
951305ac5f9Spatrick {
952305ac5f9Spatrick 	struct ifnet *ifp = &sc->sc_ac.ac_if;
953305ac5f9Spatrick 	struct dwqe_buf *txb, *rxb;
954305ac5f9Spatrick 	uint32_t reg;
955305ac5f9Spatrick 	int i;
956305ac5f9Spatrick 
957305ac5f9Spatrick 	timeout_del(&sc->sc_rxto);
95845f5f3c8Sdlg 	if (!sc->sc_fixed_link)
95947707f8eSdlg 		timeout_del(&sc->sc_phy_tick);
960305ac5f9Spatrick 
961305ac5f9Spatrick 	ifp->if_flags &= ~IFF_RUNNING;
962305ac5f9Spatrick 	ifq_clr_oactive(&ifp->if_snd);
963305ac5f9Spatrick 	ifp->if_timer = 0;
964305ac5f9Spatrick 
965305ac5f9Spatrick 	/* Disable receiver */
966305ac5f9Spatrick 	reg = dwqe_read(sc, GMAC_MAC_CONF);
967305ac5f9Spatrick 	reg &= ~GMAC_MAC_CONF_RE;
968305ac5f9Spatrick 	dwqe_write(sc, GMAC_MAC_CONF, reg);
969305ac5f9Spatrick 
970305ac5f9Spatrick 	/* Stop receive DMA */
971305ac5f9Spatrick 	reg = dwqe_read(sc, GMAC_CHAN_RX_CONTROL(0));
972305ac5f9Spatrick 	reg &= ~GMAC_CHAN_RX_CONTROL_SR;
973305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_RX_CONTROL(0), reg);
974305ac5f9Spatrick 
975305ac5f9Spatrick 	/* Stop transmit DMA */
976305ac5f9Spatrick 	reg = dwqe_read(sc, GMAC_CHAN_TX_CONTROL(0));
977305ac5f9Spatrick 	reg &= ~GMAC_CHAN_TX_CONTROL_ST;
978305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_TX_CONTROL(0), reg);
979305ac5f9Spatrick 
980305ac5f9Spatrick 	/* Flush data in the TX FIFO */
981305ac5f9Spatrick 	reg = dwqe_read(sc, GMAC_MTL_CHAN_TX_OP_MODE(0));
982305ac5f9Spatrick 	reg |= GMAC_MTL_CHAN_TX_OP_MODE_FTQ;
983305ac5f9Spatrick 	dwqe_write(sc, GMAC_MTL_CHAN_TX_OP_MODE(0), reg);
984305ac5f9Spatrick 	/* Wait for flush to complete */
985305ac5f9Spatrick 	for (i = 10000; i > 0; i--) {
986305ac5f9Spatrick 		reg = dwqe_read(sc, GMAC_MTL_CHAN_TX_OP_MODE(0));
987305ac5f9Spatrick 		if ((reg & GMAC_MTL_CHAN_TX_OP_MODE_FTQ) == 0)
988305ac5f9Spatrick 			break;
989305ac5f9Spatrick 		delay(1);
990305ac5f9Spatrick 	}
991305ac5f9Spatrick 	if (i == 0) {
992305ac5f9Spatrick 		printf("%s: timeout flushing TX queue\n",
993305ac5f9Spatrick 		    sc->sc_dev.dv_xname);
994305ac5f9Spatrick 	}
995305ac5f9Spatrick 
996305ac5f9Spatrick 	/* Disable transmitter */
997305ac5f9Spatrick 	reg = dwqe_read(sc, GMAC_MAC_CONF);
998305ac5f9Spatrick 	reg &= ~GMAC_MAC_CONF_TE;
999305ac5f9Spatrick 	dwqe_write(sc, GMAC_MAC_CONF, reg);
1000305ac5f9Spatrick 
1001305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_INTR_ENA(0), 0);
1002305ac5f9Spatrick 
1003305ac5f9Spatrick 	intr_barrier(sc->sc_ih);
1004305ac5f9Spatrick 	ifq_barrier(&ifp->if_snd);
1005305ac5f9Spatrick 
1006305ac5f9Spatrick 	for (i = 0; i < DWQE_NTXDESC; i++) {
1007305ac5f9Spatrick 		txb = &sc->sc_txbuf[i];
1008305ac5f9Spatrick 		if (txb->tb_m) {
1009305ac5f9Spatrick 			bus_dmamap_sync(sc->sc_dmat, txb->tb_map, 0,
1010305ac5f9Spatrick 			    txb->tb_map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
1011305ac5f9Spatrick 			bus_dmamap_unload(sc->sc_dmat, txb->tb_map);
1012305ac5f9Spatrick 			m_freem(txb->tb_m);
1013305ac5f9Spatrick 		}
1014305ac5f9Spatrick 		bus_dmamap_destroy(sc->sc_dmat, txb->tb_map);
1015305ac5f9Spatrick 	}
1016305ac5f9Spatrick 
1017305ac5f9Spatrick 	dwqe_dmamem_free(sc, sc->sc_txring);
1018305ac5f9Spatrick 	free(sc->sc_txbuf, M_DEVBUF, 0);
1019305ac5f9Spatrick 
1020305ac5f9Spatrick 	for (i = 0; i < DWQE_NRXDESC; i++) {
1021305ac5f9Spatrick 		rxb = &sc->sc_rxbuf[i];
1022305ac5f9Spatrick 		if (rxb->tb_m) {
1023305ac5f9Spatrick 			bus_dmamap_sync(sc->sc_dmat, rxb->tb_map, 0,
1024305ac5f9Spatrick 			    rxb->tb_map->dm_mapsize, BUS_DMASYNC_POSTREAD);
1025305ac5f9Spatrick 			bus_dmamap_unload(sc->sc_dmat, rxb->tb_map);
1026305ac5f9Spatrick 			m_freem(rxb->tb_m);
1027305ac5f9Spatrick 		}
1028305ac5f9Spatrick 		bus_dmamap_destroy(sc->sc_dmat, rxb->tb_map);
1029305ac5f9Spatrick 	}
1030305ac5f9Spatrick 
1031305ac5f9Spatrick 	dwqe_dmamem_free(sc, sc->sc_rxring);
1032305ac5f9Spatrick 	free(sc->sc_rxbuf, M_DEVBUF, 0);
1033305ac5f9Spatrick }
1034305ac5f9Spatrick 
1035305ac5f9Spatrick /* Bit Reversal - http://aggregate.org/MAGIC/#Bit%20Reversal */
1036305ac5f9Spatrick static uint32_t
bitrev32(uint32_t x)1037305ac5f9Spatrick bitrev32(uint32_t x)
1038305ac5f9Spatrick {
1039305ac5f9Spatrick 	x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
1040305ac5f9Spatrick 	x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
1041305ac5f9Spatrick 	x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
1042305ac5f9Spatrick 	x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
1043305ac5f9Spatrick 
1044305ac5f9Spatrick 	return (x >> 16) | (x << 16);
1045305ac5f9Spatrick }
1046305ac5f9Spatrick 
1047305ac5f9Spatrick void
dwqe_iff(struct dwqe_softc * sc)1048305ac5f9Spatrick dwqe_iff(struct dwqe_softc *sc)
1049305ac5f9Spatrick {
1050305ac5f9Spatrick 	struct arpcom *ac = &sc->sc_ac;
1051305ac5f9Spatrick 	struct ifnet *ifp = &sc->sc_ac.ac_if;
1052305ac5f9Spatrick 	struct ether_multi *enm;
1053305ac5f9Spatrick 	struct ether_multistep step;
1054305ac5f9Spatrick 	uint32_t crc, hash[2], hashbit, hashreg;
1055305ac5f9Spatrick 	uint32_t reg;
1056305ac5f9Spatrick 
1057305ac5f9Spatrick 	reg = 0;
1058305ac5f9Spatrick 
1059305ac5f9Spatrick 	ifp->if_flags &= ~IFF_ALLMULTI;
1060305ac5f9Spatrick 	bzero(hash, sizeof(hash));
1061305ac5f9Spatrick 	if (ifp->if_flags & IFF_PROMISC || ac->ac_multirangecnt > 0) {
1062305ac5f9Spatrick 		ifp->if_flags |= IFF_ALLMULTI;
1063305ac5f9Spatrick 		reg |= GMAC_MAC_PACKET_FILTER_PM;
1064305ac5f9Spatrick 		if (ifp->if_flags & IFF_PROMISC)
1065305ac5f9Spatrick 			reg |= GMAC_MAC_PACKET_FILTER_PR |
1066305ac5f9Spatrick 			    GMAC_MAC_PACKET_FILTER_PCF_ALL;
1067305ac5f9Spatrick 	} else {
1068305ac5f9Spatrick 		reg |= GMAC_MAC_PACKET_FILTER_HMC;
1069305ac5f9Spatrick 		ETHER_FIRST_MULTI(step, ac, enm);
1070305ac5f9Spatrick 		while (enm != NULL) {
1071305ac5f9Spatrick 			crc = ether_crc32_le(enm->enm_addrlo,
1072305ac5f9Spatrick 			    ETHER_ADDR_LEN) & 0x7f;
1073305ac5f9Spatrick 
1074305ac5f9Spatrick 			crc = bitrev32(~crc) >> 26;
1075305ac5f9Spatrick 			hashreg = (crc >> 5);
1076305ac5f9Spatrick 			hashbit = (crc & 0x1f);
1077305ac5f9Spatrick 			hash[hashreg] |= (1 << hashbit);
1078305ac5f9Spatrick 
1079305ac5f9Spatrick 			ETHER_NEXT_MULTI(step, enm);
1080305ac5f9Spatrick 		}
1081305ac5f9Spatrick 	}
1082305ac5f9Spatrick 
1083305ac5f9Spatrick 	dwqe_lladdr_write(sc);
1084305ac5f9Spatrick 
1085305ac5f9Spatrick 	dwqe_write(sc, GMAC_MAC_HASH_TAB_REG0, hash[0]);
1086305ac5f9Spatrick 	dwqe_write(sc, GMAC_MAC_HASH_TAB_REG1, hash[1]);
1087305ac5f9Spatrick 
1088305ac5f9Spatrick 	dwqe_write(sc, GMAC_MAC_PACKET_FILTER, reg);
1089305ac5f9Spatrick }
1090305ac5f9Spatrick 
1091*71083937Sstsp void
dwqe_tx_csum(struct dwqe_softc * sc,struct mbuf * m,struct dwqe_desc * txd)1092*71083937Sstsp dwqe_tx_csum(struct dwqe_softc *sc, struct mbuf *m, struct dwqe_desc *txd)
1093*71083937Sstsp {
1094*71083937Sstsp 	if (!dwqe_have_tx_csum_offload(sc))
1095*71083937Sstsp 		return;
1096*71083937Sstsp 
1097*71083937Sstsp 	/* Checksum flags are valid only on first descriptor. */
1098*71083937Sstsp 	if ((txd->sd_tdes3 & TDES3_FS) == 0)
1099*71083937Sstsp 		return;
1100*71083937Sstsp 
1101*71083937Sstsp 	/* TSO and Tx checksum offloading are incompatible. */
1102*71083937Sstsp 	if (txd->sd_tdes3 & TDES3_TSO_EN)
1103*71083937Sstsp 		return;
1104*71083937Sstsp 
1105*71083937Sstsp 	if (m->m_pkthdr.csum_flags & (M_IPV4_CSUM_OUT |
1106*71083937Sstsp 	    M_TCP_CSUM_OUT | M_UDP_CSUM_OUT))
1107*71083937Sstsp 		txd->sd_tdes3 |= TDES3_CSUM_IPHDR_PAYLOAD_PSEUDOHDR;
1108*71083937Sstsp }
1109*71083937Sstsp 
1110305ac5f9Spatrick int
dwqe_encap(struct dwqe_softc * sc,struct mbuf * m,int * idx,int * used)1111305ac5f9Spatrick dwqe_encap(struct dwqe_softc *sc, struct mbuf *m, int *idx, int *used)
1112305ac5f9Spatrick {
1113305ac5f9Spatrick 	struct dwqe_desc *txd, *txd_start;
1114305ac5f9Spatrick 	bus_dmamap_t map;
1115305ac5f9Spatrick 	int cur, frag, i;
1116305ac5f9Spatrick 
1117305ac5f9Spatrick 	cur = frag = *idx;
1118305ac5f9Spatrick 	map = sc->sc_txbuf[cur].tb_map;
1119305ac5f9Spatrick 
1120305ac5f9Spatrick 	if (bus_dmamap_load_mbuf(sc->sc_dmat, map, m, BUS_DMA_NOWAIT)) {
1121305ac5f9Spatrick 		if (m_defrag(m, M_DONTWAIT))
1122305ac5f9Spatrick 			return (EFBIG);
1123305ac5f9Spatrick 		if (bus_dmamap_load_mbuf(sc->sc_dmat, map, m, BUS_DMA_NOWAIT))
1124305ac5f9Spatrick 			return (EFBIG);
1125305ac5f9Spatrick 	}
1126305ac5f9Spatrick 
1127305ac5f9Spatrick 	/* Sync the DMA map. */
1128305ac5f9Spatrick 	bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
1129305ac5f9Spatrick 	    BUS_DMASYNC_PREWRITE);
1130305ac5f9Spatrick 
1131305ac5f9Spatrick 	txd = txd_start = &sc->sc_txdesc[frag];
1132305ac5f9Spatrick 	for (i = 0; i < map->dm_nsegs; i++) {
1133305ac5f9Spatrick 		/* TODO: check for 32-bit vs 64-bit support */
1134305ac5f9Spatrick 		KASSERT((map->dm_segs[i].ds_addr >> 32) == 0);
1135305ac5f9Spatrick 
1136305ac5f9Spatrick 		txd->sd_tdes0 = (uint32_t)map->dm_segs[i].ds_addr;
1137305ac5f9Spatrick 		txd->sd_tdes1 = (uint32_t)(map->dm_segs[i].ds_addr >> 32);
1138305ac5f9Spatrick 		txd->sd_tdes2 = map->dm_segs[i].ds_len;
1139305ac5f9Spatrick 		txd->sd_tdes3 = m->m_pkthdr.len;
1140*71083937Sstsp 		if (i == 0) {
1141305ac5f9Spatrick 			txd->sd_tdes3 |= TDES3_FS;
1142*71083937Sstsp 			dwqe_tx_csum(sc, m, txd);
1143*71083937Sstsp 		}
1144305ac5f9Spatrick 		if (i == (map->dm_nsegs - 1)) {
1145305ac5f9Spatrick 			txd->sd_tdes2 |= TDES2_IC;
1146305ac5f9Spatrick 			txd->sd_tdes3 |= TDES3_LS;
1147305ac5f9Spatrick 		}
1148305ac5f9Spatrick 		if (i != 0)
1149305ac5f9Spatrick 			txd->sd_tdes3 |= TDES3_OWN;
1150305ac5f9Spatrick 
1151305ac5f9Spatrick 		bus_dmamap_sync(sc->sc_dmat, DWQE_DMA_MAP(sc->sc_txring),
1152305ac5f9Spatrick 		    frag * sizeof(*txd), sizeof(*txd), BUS_DMASYNC_PREWRITE);
1153305ac5f9Spatrick 
1154305ac5f9Spatrick 		cur = frag;
1155305ac5f9Spatrick 		if (frag == (DWQE_NTXDESC - 1)) {
1156305ac5f9Spatrick 			txd = &sc->sc_txdesc[0];
1157305ac5f9Spatrick 			frag = 0;
1158305ac5f9Spatrick 		} else {
1159305ac5f9Spatrick 			txd++;
1160305ac5f9Spatrick 			frag++;
1161305ac5f9Spatrick 		}
1162305ac5f9Spatrick 		KASSERT(frag != sc->sc_tx_cons);
1163305ac5f9Spatrick 	}
1164305ac5f9Spatrick 
1165305ac5f9Spatrick 	txd_start->sd_tdes3 |= TDES3_OWN;
1166305ac5f9Spatrick 	bus_dmamap_sync(sc->sc_dmat, DWQE_DMA_MAP(sc->sc_txring),
1167305ac5f9Spatrick 	    *idx * sizeof(*txd), sizeof(*txd), BUS_DMASYNC_PREWRITE);
1168305ac5f9Spatrick 
1169305ac5f9Spatrick 	KASSERT(sc->sc_txbuf[cur].tb_m == NULL);
1170305ac5f9Spatrick 	sc->sc_txbuf[*idx].tb_map = sc->sc_txbuf[cur].tb_map;
1171305ac5f9Spatrick 	sc->sc_txbuf[cur].tb_map = map;
1172305ac5f9Spatrick 	sc->sc_txbuf[cur].tb_m = m;
1173305ac5f9Spatrick 
1174305ac5f9Spatrick 	*idx = frag;
1175305ac5f9Spatrick 	*used += map->dm_nsegs;
1176305ac5f9Spatrick 
1177305ac5f9Spatrick 	return (0);
1178305ac5f9Spatrick }
1179305ac5f9Spatrick 
1180305ac5f9Spatrick void
dwqe_reset(struct dwqe_softc * sc)1181305ac5f9Spatrick dwqe_reset(struct dwqe_softc *sc)
1182305ac5f9Spatrick {
1183305ac5f9Spatrick 	int n;
1184305ac5f9Spatrick 
1185305ac5f9Spatrick 	dwqe_write(sc, GMAC_BUS_MODE, dwqe_read(sc, GMAC_BUS_MODE) |
1186305ac5f9Spatrick 	    GMAC_BUS_MODE_SWR);
1187305ac5f9Spatrick 
1188305ac5f9Spatrick 	for (n = 0; n < 30000; n++) {
1189305ac5f9Spatrick 		if ((dwqe_read(sc, GMAC_BUS_MODE) &
1190305ac5f9Spatrick 		    GMAC_BUS_MODE_SWR) == 0)
1191305ac5f9Spatrick 			return;
1192305ac5f9Spatrick 		delay(10);
1193305ac5f9Spatrick 	}
1194305ac5f9Spatrick 
1195305ac5f9Spatrick 	printf("%s: reset timeout\n", sc->sc_dev.dv_xname);
1196305ac5f9Spatrick }
1197305ac5f9Spatrick 
1198305ac5f9Spatrick struct dwqe_dmamem *
dwqe_dmamem_alloc(struct dwqe_softc * sc,bus_size_t size,bus_size_t align)1199305ac5f9Spatrick dwqe_dmamem_alloc(struct dwqe_softc *sc, bus_size_t size, bus_size_t align)
1200305ac5f9Spatrick {
1201305ac5f9Spatrick 	struct dwqe_dmamem *tdm;
1202305ac5f9Spatrick 	int nsegs;
1203305ac5f9Spatrick 
1204305ac5f9Spatrick 	tdm = malloc(sizeof(*tdm), M_DEVBUF, M_WAITOK | M_ZERO);
1205305ac5f9Spatrick 	tdm->tdm_size = size;
1206305ac5f9Spatrick 
1207305ac5f9Spatrick 	if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
1208305ac5f9Spatrick 	    BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &tdm->tdm_map) != 0)
1209305ac5f9Spatrick 		goto tdmfree;
1210305ac5f9Spatrick 
1211305ac5f9Spatrick 	if (bus_dmamem_alloc(sc->sc_dmat, size, align, 0, &tdm->tdm_seg, 1,
1212305ac5f9Spatrick 	    &nsegs, BUS_DMA_WAITOK) != 0)
1213305ac5f9Spatrick 		goto destroy;
1214305ac5f9Spatrick 
1215305ac5f9Spatrick 	if (bus_dmamem_map(sc->sc_dmat, &tdm->tdm_seg, nsegs, size,
1216305ac5f9Spatrick 	    &tdm->tdm_kva, BUS_DMA_WAITOK | BUS_DMA_COHERENT) != 0)
1217305ac5f9Spatrick 		goto free;
1218305ac5f9Spatrick 
1219305ac5f9Spatrick 	if (bus_dmamap_load(sc->sc_dmat, tdm->tdm_map, tdm->tdm_kva, size,
1220305ac5f9Spatrick 	    NULL, BUS_DMA_WAITOK) != 0)
1221305ac5f9Spatrick 		goto unmap;
1222305ac5f9Spatrick 
1223305ac5f9Spatrick 	bzero(tdm->tdm_kva, size);
1224305ac5f9Spatrick 
1225305ac5f9Spatrick 	return (tdm);
1226305ac5f9Spatrick 
1227305ac5f9Spatrick unmap:
1228305ac5f9Spatrick 	bus_dmamem_unmap(sc->sc_dmat, tdm->tdm_kva, size);
1229305ac5f9Spatrick free:
1230305ac5f9Spatrick 	bus_dmamem_free(sc->sc_dmat, &tdm->tdm_seg, 1);
1231305ac5f9Spatrick destroy:
1232305ac5f9Spatrick 	bus_dmamap_destroy(sc->sc_dmat, tdm->tdm_map);
1233305ac5f9Spatrick tdmfree:
1234305ac5f9Spatrick 	free(tdm, M_DEVBUF, 0);
1235305ac5f9Spatrick 
1236305ac5f9Spatrick 	return (NULL);
1237305ac5f9Spatrick }
1238305ac5f9Spatrick 
1239305ac5f9Spatrick void
dwqe_dmamem_free(struct dwqe_softc * sc,struct dwqe_dmamem * tdm)1240305ac5f9Spatrick dwqe_dmamem_free(struct dwqe_softc *sc, struct dwqe_dmamem *tdm)
1241305ac5f9Spatrick {
1242305ac5f9Spatrick 	bus_dmamem_unmap(sc->sc_dmat, tdm->tdm_kva, tdm->tdm_size);
1243305ac5f9Spatrick 	bus_dmamem_free(sc->sc_dmat, &tdm->tdm_seg, 1);
1244305ac5f9Spatrick 	bus_dmamap_destroy(sc->sc_dmat, tdm->tdm_map);
1245305ac5f9Spatrick 	free(tdm, M_DEVBUF, 0);
1246305ac5f9Spatrick }
1247305ac5f9Spatrick 
1248305ac5f9Spatrick struct mbuf *
dwqe_alloc_mbuf(struct dwqe_softc * sc,bus_dmamap_t map)1249305ac5f9Spatrick dwqe_alloc_mbuf(struct dwqe_softc *sc, bus_dmamap_t map)
1250305ac5f9Spatrick {
1251305ac5f9Spatrick 	struct mbuf *m = NULL;
1252305ac5f9Spatrick 
1253305ac5f9Spatrick 	m = MCLGETL(NULL, M_DONTWAIT, MCLBYTES);
1254305ac5f9Spatrick 	if (!m)
1255305ac5f9Spatrick 		return (NULL);
1256305ac5f9Spatrick 	m->m_len = m->m_pkthdr.len = MCLBYTES;
1257305ac5f9Spatrick 	m_adj(m, ETHER_ALIGN);
1258305ac5f9Spatrick 
1259305ac5f9Spatrick 	if (bus_dmamap_load_mbuf(sc->sc_dmat, map, m, BUS_DMA_NOWAIT) != 0) {
1260305ac5f9Spatrick 		printf("%s: could not load mbuf DMA map", DEVNAME(sc));
1261305ac5f9Spatrick 		m_freem(m);
1262305ac5f9Spatrick 		return (NULL);
1263305ac5f9Spatrick 	}
1264305ac5f9Spatrick 
1265305ac5f9Spatrick 	bus_dmamap_sync(sc->sc_dmat, map, 0,
1266305ac5f9Spatrick 	    m->m_pkthdr.len, BUS_DMASYNC_PREREAD);
1267305ac5f9Spatrick 
1268305ac5f9Spatrick 	return (m);
1269305ac5f9Spatrick }
1270305ac5f9Spatrick 
1271305ac5f9Spatrick void
dwqe_fill_rx_ring(struct dwqe_softc * sc)1272305ac5f9Spatrick dwqe_fill_rx_ring(struct dwqe_softc *sc)
1273305ac5f9Spatrick {
1274305ac5f9Spatrick 	struct dwqe_desc *rxd;
1275305ac5f9Spatrick 	struct dwqe_buf *rxb;
1276305ac5f9Spatrick 	u_int slots;
1277305ac5f9Spatrick 
1278305ac5f9Spatrick 	for (slots = if_rxr_get(&sc->sc_rx_ring, DWQE_NRXDESC);
1279305ac5f9Spatrick 	    slots > 0; slots--) {
1280305ac5f9Spatrick 		rxb = &sc->sc_rxbuf[sc->sc_rx_prod];
1281305ac5f9Spatrick 		rxb->tb_m = dwqe_alloc_mbuf(sc, rxb->tb_map);
1282305ac5f9Spatrick 		if (rxb->tb_m == NULL)
1283305ac5f9Spatrick 			break;
1284305ac5f9Spatrick 
1285305ac5f9Spatrick 		/* TODO: check for 32-bit vs 64-bit support */
12864fa84378Skettenis 		KASSERT((rxb->tb_map->dm_segs[0].ds_addr >> 32) == 0);
1287305ac5f9Spatrick 
1288305ac5f9Spatrick 		rxd = &sc->sc_rxdesc[sc->sc_rx_prod];
12894fa84378Skettenis 		rxd->sd_tdes0 = (uint32_t)rxb->tb_map->dm_segs[0].ds_addr;
12904fa84378Skettenis 		rxd->sd_tdes1 = (uint32_t)(rxb->tb_map->dm_segs[0].ds_addr >> 32);
1291305ac5f9Spatrick 		rxd->sd_tdes2 = 0;
1292305ac5f9Spatrick 		rxd->sd_tdes3 = RDES3_OWN | RDES3_IC | RDES3_BUF1V;
1293305ac5f9Spatrick 
1294305ac5f9Spatrick 		if (sc->sc_rx_prod == (DWQE_NRXDESC - 1))
1295305ac5f9Spatrick 			sc->sc_rx_prod = 0;
1296305ac5f9Spatrick 		else
1297305ac5f9Spatrick 			sc->sc_rx_prod++;
1298305ac5f9Spatrick 	}
1299305ac5f9Spatrick 	if_rxr_put(&sc->sc_rx_ring, slots);
1300305ac5f9Spatrick 
1301305ac5f9Spatrick 	dwqe_write(sc, GMAC_CHAN_RX_END_ADDR(0), DWQE_DMA_DVA(sc->sc_rxring) +
1302305ac5f9Spatrick 	    sc->sc_rx_prod * sizeof(*rxd));
1303305ac5f9Spatrick 
1304305ac5f9Spatrick 	if (if_rxr_inuse(&sc->sc_rx_ring) == 0)
1305305ac5f9Spatrick 		timeout_add(&sc->sc_rxto, 1);
1306305ac5f9Spatrick }
1307