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