xref: /freebsd-src/sys/dev/neta/if_mvneta.c (revision 3ddaf8200bc90b1410755ebac7b5c979ea90a2f6)
1a8d7fc4aSZbigniew Bodek /*
2a8d7fc4aSZbigniew Bodek  * Copyright (c) 2017 Stormshield.
3a8d7fc4aSZbigniew Bodek  * Copyright (c) 2017 Semihalf.
4a8d7fc4aSZbigniew Bodek  * All rights reserved.
5a8d7fc4aSZbigniew Bodek  *
6a8d7fc4aSZbigniew Bodek  * Redistribution and use in source and binary forms, with or without
7a8d7fc4aSZbigniew Bodek  * modification, are permitted provided that the following conditions
8a8d7fc4aSZbigniew Bodek  * are met:
9a8d7fc4aSZbigniew Bodek  * 1. Redistributions of source code must retain the above copyright
10a8d7fc4aSZbigniew Bodek  *    notice, this list of conditions and the following disclaimer.
11a8d7fc4aSZbigniew Bodek  * 2. Redistributions in binary form must reproduce the above copyright
12a8d7fc4aSZbigniew Bodek  *    notice, this list of conditions and the following disclaimer in the
13a8d7fc4aSZbigniew Bodek  *    documentation and/or other materials provided with the distribution.
14a8d7fc4aSZbigniew Bodek  *
15a8d7fc4aSZbigniew Bodek  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16a8d7fc4aSZbigniew Bodek  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17a8d7fc4aSZbigniew Bodek  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18a8d7fc4aSZbigniew Bodek  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
19a8d7fc4aSZbigniew Bodek  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20a8d7fc4aSZbigniew Bodek  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21a8d7fc4aSZbigniew Bodek  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22a8d7fc4aSZbigniew Bodek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
23a8d7fc4aSZbigniew Bodek  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
24a8d7fc4aSZbigniew Bodek  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25a8d7fc4aSZbigniew Bodek  * POSSIBILITY OF SUCH DAMAGE.
26a8d7fc4aSZbigniew Bodek  */
27a8d7fc4aSZbigniew Bodek 
28a8d7fc4aSZbigniew Bodek #include "opt_platform.h"
29fdafd315SWarner Losh 
30a8d7fc4aSZbigniew Bodek #include <sys/param.h>
31a8d7fc4aSZbigniew Bodek #include <sys/systm.h>
32a8d7fc4aSZbigniew Bodek #include <sys/endian.h>
33a8d7fc4aSZbigniew Bodek #include <sys/mbuf.h>
34a8d7fc4aSZbigniew Bodek #include <sys/lock.h>
35a8d7fc4aSZbigniew Bodek #include <sys/mutex.h>
36a8d7fc4aSZbigniew Bodek #include <sys/kernel.h>
37a8d7fc4aSZbigniew Bodek #include <sys/module.h>
38a8d7fc4aSZbigniew Bodek #include <sys/socket.h>
39a8d7fc4aSZbigniew Bodek #include <sys/sysctl.h>
40a8d7fc4aSZbigniew Bodek #include <sys/smp.h>
41a8d7fc4aSZbigniew Bodek #include <sys/taskqueue.h>
42a8d7fc4aSZbigniew Bodek #ifdef MVNETA_KTR
43a8d7fc4aSZbigniew Bodek #include <sys/ktr.h>
44a8d7fc4aSZbigniew Bodek #endif
45a8d7fc4aSZbigniew Bodek 
46a8d7fc4aSZbigniew Bodek #include <net/ethernet.h>
47a8d7fc4aSZbigniew Bodek #include <net/bpf.h>
48a8d7fc4aSZbigniew Bodek #include <net/if.h>
49a8d7fc4aSZbigniew Bodek #include <net/if_arp.h>
50a8d7fc4aSZbigniew Bodek #include <net/if_dl.h>
51a8d7fc4aSZbigniew Bodek #include <net/if_media.h>
52a8d7fc4aSZbigniew Bodek #include <net/if_types.h>
53a8d7fc4aSZbigniew Bodek #include <net/if_vlan_var.h>
54a8d7fc4aSZbigniew Bodek 
55a8d7fc4aSZbigniew Bodek #include <netinet/in_systm.h>
56a8d7fc4aSZbigniew Bodek #include <netinet/in.h>
57a8d7fc4aSZbigniew Bodek #include <netinet/ip.h>
58a8d7fc4aSZbigniew Bodek #include <netinet/tcp_lro.h>
59a8d7fc4aSZbigniew Bodek 
60a8d7fc4aSZbigniew Bodek #include <sys/sockio.h>
61a8d7fc4aSZbigniew Bodek #include <sys/bus.h>
62a8d7fc4aSZbigniew Bodek #include <machine/bus.h>
63a8d7fc4aSZbigniew Bodek #include <sys/rman.h>
64a8d7fc4aSZbigniew Bodek #include <machine/resource.h>
65a8d7fc4aSZbigniew Bodek 
66be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
674885d6f3SHubert Mazur 
68a8d7fc4aSZbigniew Bodek #include <dev/mii/mii.h>
69a8d7fc4aSZbigniew Bodek #include <dev/mii/miivar.h>
70a8d7fc4aSZbigniew Bodek 
71a8d7fc4aSZbigniew Bodek #include <dev/mdio/mdio.h>
72a8d7fc4aSZbigniew Bodek 
73a8d7fc4aSZbigniew Bodek #include <arm/mv/mvvar.h>
74e314ac07SMarcin Wojtas 
75e314ac07SMarcin Wojtas #if !defined(__aarch64__)
76e314ac07SMarcin Wojtas #include <arm/mv/mvreg.h>
77a8d7fc4aSZbigniew Bodek #include <arm/mv/mvwin.h>
78e314ac07SMarcin Wojtas #endif
79a8d7fc4aSZbigniew Bodek 
80a8d7fc4aSZbigniew Bodek #include "if_mvnetareg.h"
81a8d7fc4aSZbigniew Bodek #include "if_mvnetavar.h"
82a8d7fc4aSZbigniew Bodek 
83a8d7fc4aSZbigniew Bodek #include "miibus_if.h"
84a8d7fc4aSZbigniew Bodek #include "mdio_if.h"
85a8d7fc4aSZbigniew Bodek 
86a8d7fc4aSZbigniew Bodek #ifdef MVNETA_DEBUG
87a8d7fc4aSZbigniew Bodek #define	STATIC /* nothing */
88a8d7fc4aSZbigniew Bodek #else
89a8d7fc4aSZbigniew Bodek #define	STATIC static
90a8d7fc4aSZbigniew Bodek #endif
91a8d7fc4aSZbigniew Bodek 
92a8d7fc4aSZbigniew Bodek #define	DASSERT(x) KASSERT((x), (#x))
93a8d7fc4aSZbigniew Bodek 
94e314ac07SMarcin Wojtas #define	A3700_TCLK_250MHZ		250000000
95e314ac07SMarcin Wojtas 
96a8d7fc4aSZbigniew Bodek /* Device Register Initialization */
97992fa62bSJustin Hibbits STATIC int mvneta_initreg(if_t);
98a8d7fc4aSZbigniew Bodek 
99a8d7fc4aSZbigniew Bodek /* Descriptor Ring Control for each of queues */
100a8d7fc4aSZbigniew Bodek STATIC int mvneta_ring_alloc_rx_queue(struct mvneta_softc *, int);
101a8d7fc4aSZbigniew Bodek STATIC int mvneta_ring_alloc_tx_queue(struct mvneta_softc *, int);
102a8d7fc4aSZbigniew Bodek STATIC void mvneta_ring_dealloc_rx_queue(struct mvneta_softc *, int);
103a8d7fc4aSZbigniew Bodek STATIC void mvneta_ring_dealloc_tx_queue(struct mvneta_softc *, int);
104a8d7fc4aSZbigniew Bodek STATIC int mvneta_ring_init_rx_queue(struct mvneta_softc *, int);
105a8d7fc4aSZbigniew Bodek STATIC int mvneta_ring_init_tx_queue(struct mvneta_softc *, int);
106a8d7fc4aSZbigniew Bodek STATIC void mvneta_ring_flush_rx_queue(struct mvneta_softc *, int);
107a8d7fc4aSZbigniew Bodek STATIC void mvneta_ring_flush_tx_queue(struct mvneta_softc *, int);
108a8d7fc4aSZbigniew Bodek STATIC void mvneta_dmamap_cb(void *, bus_dma_segment_t *, int, int);
109a8d7fc4aSZbigniew Bodek STATIC int mvneta_dma_create(struct mvneta_softc *);
110a8d7fc4aSZbigniew Bodek 
111a8d7fc4aSZbigniew Bodek /* Rx/Tx Queue Control */
112992fa62bSJustin Hibbits STATIC int mvneta_rx_queue_init(if_t, int);
113992fa62bSJustin Hibbits STATIC int mvneta_tx_queue_init(if_t, int);
114992fa62bSJustin Hibbits STATIC int mvneta_rx_queue_enable(if_t, int);
115992fa62bSJustin Hibbits STATIC int mvneta_tx_queue_enable(if_t, int);
116a8d7fc4aSZbigniew Bodek STATIC void mvneta_rx_lockq(struct mvneta_softc *, int);
117a8d7fc4aSZbigniew Bodek STATIC void mvneta_rx_unlockq(struct mvneta_softc *, int);
118a8d7fc4aSZbigniew Bodek STATIC void mvneta_tx_lockq(struct mvneta_softc *, int);
119a8d7fc4aSZbigniew Bodek STATIC void mvneta_tx_unlockq(struct mvneta_softc *, int);
120a8d7fc4aSZbigniew Bodek 
121a8d7fc4aSZbigniew Bodek /* Interrupt Handlers */
122a8d7fc4aSZbigniew Bodek STATIC void mvneta_disable_intr(struct mvneta_softc *);
123a8d7fc4aSZbigniew Bodek STATIC void mvneta_enable_intr(struct mvneta_softc *);
124a8d7fc4aSZbigniew Bodek STATIC void mvneta_rxtxth_intr(void *);
125a8d7fc4aSZbigniew Bodek STATIC int mvneta_misc_intr(struct mvneta_softc *);
126a8d7fc4aSZbigniew Bodek STATIC void mvneta_tick(void *);
127a8d7fc4aSZbigniew Bodek /* struct ifnet and mii callbacks*/
128a8d7fc4aSZbigniew Bodek STATIC int mvneta_xmitfast_locked(struct mvneta_softc *, int, struct mbuf **);
129a8d7fc4aSZbigniew Bodek STATIC int mvneta_xmit_locked(struct mvneta_softc *, int);
130a8d7fc4aSZbigniew Bodek #ifdef MVNETA_MULTIQUEUE
131992fa62bSJustin Hibbits STATIC int mvneta_transmit(if_t, struct mbuf *);
132a8d7fc4aSZbigniew Bodek #else /* !MVNETA_MULTIQUEUE */
133992fa62bSJustin Hibbits STATIC void mvneta_start(if_t);
134a8d7fc4aSZbigniew Bodek #endif
135992fa62bSJustin Hibbits STATIC void mvneta_qflush(if_t);
136a8d7fc4aSZbigniew Bodek STATIC void mvneta_tx_task(void *, int);
137992fa62bSJustin Hibbits STATIC int mvneta_ioctl(if_t, u_long, caddr_t);
138a8d7fc4aSZbigniew Bodek STATIC void mvneta_init(void *);
139a8d7fc4aSZbigniew Bodek STATIC void mvneta_init_locked(void *);
140a8d7fc4aSZbigniew Bodek STATIC void mvneta_stop(struct mvneta_softc *);
141a8d7fc4aSZbigniew Bodek STATIC void mvneta_stop_locked(struct mvneta_softc *);
142992fa62bSJustin Hibbits STATIC int mvneta_mediachange(if_t);
143992fa62bSJustin Hibbits STATIC void mvneta_mediastatus(if_t, struct ifmediareq *);
144a8d7fc4aSZbigniew Bodek STATIC void mvneta_portup(struct mvneta_softc *);
145a8d7fc4aSZbigniew Bodek STATIC void mvneta_portdown(struct mvneta_softc *);
146a8d7fc4aSZbigniew Bodek 
147a8d7fc4aSZbigniew Bodek /* Link State Notify */
148a8d7fc4aSZbigniew Bodek STATIC void mvneta_update_autoneg(struct mvneta_softc *, int);
149a8d7fc4aSZbigniew Bodek STATIC int mvneta_update_media(struct mvneta_softc *, int);
150a8d7fc4aSZbigniew Bodek STATIC void mvneta_adjust_link(struct mvneta_softc *);
151a8d7fc4aSZbigniew Bodek STATIC void mvneta_update_eee(struct mvneta_softc *);
152a8d7fc4aSZbigniew Bodek STATIC void mvneta_update_fc(struct mvneta_softc *);
153a8d7fc4aSZbigniew Bodek STATIC void mvneta_link_isr(struct mvneta_softc *);
154a8d7fc4aSZbigniew Bodek STATIC void mvneta_linkupdate(struct mvneta_softc *, boolean_t);
155a8d7fc4aSZbigniew Bodek STATIC void mvneta_linkup(struct mvneta_softc *);
156a8d7fc4aSZbigniew Bodek STATIC void mvneta_linkdown(struct mvneta_softc *);
157a8d7fc4aSZbigniew Bodek STATIC void mvneta_linkreset(struct mvneta_softc *);
158a8d7fc4aSZbigniew Bodek 
159a8d7fc4aSZbigniew Bodek /* Tx Subroutines */
160a8d7fc4aSZbigniew Bodek STATIC int mvneta_tx_queue(struct mvneta_softc *, struct mbuf **, int);
161992fa62bSJustin Hibbits STATIC void mvneta_tx_set_csumflag(if_t,
162a8d7fc4aSZbigniew Bodek     struct mvneta_tx_desc *, struct mbuf *);
163a8d7fc4aSZbigniew Bodek STATIC void mvneta_tx_queue_complete(struct mvneta_softc *, int);
164a8d7fc4aSZbigniew Bodek STATIC void mvneta_tx_drain(struct mvneta_softc *);
165a8d7fc4aSZbigniew Bodek 
166a8d7fc4aSZbigniew Bodek /* Rx Subroutines */
167a8d7fc4aSZbigniew Bodek STATIC int mvneta_rx(struct mvneta_softc *, int, int);
168a8d7fc4aSZbigniew Bodek STATIC void mvneta_rx_queue(struct mvneta_softc *, int, int);
169a8d7fc4aSZbigniew Bodek STATIC void mvneta_rx_queue_refill(struct mvneta_softc *, int);
170992fa62bSJustin Hibbits STATIC void mvneta_rx_set_csumflag(if_t,
171a8d7fc4aSZbigniew Bodek     struct mvneta_rx_desc *, struct mbuf *);
172a8d7fc4aSZbigniew Bodek STATIC void mvneta_rx_buf_free(struct mvneta_softc *, struct mvneta_buf *);
173a8d7fc4aSZbigniew Bodek 
174a8d7fc4aSZbigniew Bodek /* MAC address filter */
175a8d7fc4aSZbigniew Bodek STATIC void mvneta_filter_setup(struct mvneta_softc *);
176a8d7fc4aSZbigniew Bodek 
177a8d7fc4aSZbigniew Bodek /* sysctl(9) */
178a8d7fc4aSZbigniew Bodek STATIC int sysctl_read_mib(SYSCTL_HANDLER_ARGS);
179a8d7fc4aSZbigniew Bodek STATIC int sysctl_clear_mib(SYSCTL_HANDLER_ARGS);
180a8d7fc4aSZbigniew Bodek STATIC int sysctl_set_queue_rxthtime(SYSCTL_HANDLER_ARGS);
181a8d7fc4aSZbigniew Bodek STATIC void sysctl_mvneta_init(struct mvneta_softc *);
182a8d7fc4aSZbigniew Bodek 
183a8d7fc4aSZbigniew Bodek /* MIB */
184a8d7fc4aSZbigniew Bodek STATIC void mvneta_clear_mib(struct mvneta_softc *);
185caf552a6SMark Johnston STATIC uint64_t mvneta_read_mib(struct mvneta_softc *, int);
186a8d7fc4aSZbigniew Bodek STATIC void mvneta_update_mib(struct mvneta_softc *);
187a8d7fc4aSZbigniew Bodek 
188a8d7fc4aSZbigniew Bodek /* Switch */
189a8d7fc4aSZbigniew Bodek STATIC boolean_t mvneta_has_switch(device_t);
190a8d7fc4aSZbigniew Bodek 
191a8d7fc4aSZbigniew Bodek #define	mvneta_sc_lock(sc) mtx_lock(&sc->mtx)
192a8d7fc4aSZbigniew Bodek #define	mvneta_sc_unlock(sc) mtx_unlock(&sc->mtx)
193a8d7fc4aSZbigniew Bodek 
194a8d7fc4aSZbigniew Bodek STATIC struct mtx mii_mutex;
195a8d7fc4aSZbigniew Bodek STATIC int mii_init = 0;
196a8d7fc4aSZbigniew Bodek 
197a8d7fc4aSZbigniew Bodek /* Device */
198a8d7fc4aSZbigniew Bodek STATIC int mvneta_detach(device_t);
199a8d7fc4aSZbigniew Bodek /* MII */
200a8d7fc4aSZbigniew Bodek STATIC int mvneta_miibus_readreg(device_t, int, int);
201a8d7fc4aSZbigniew Bodek STATIC int mvneta_miibus_writereg(device_t, int, int, int);
202a8d7fc4aSZbigniew Bodek 
203a8d7fc4aSZbigniew Bodek static device_method_t mvneta_methods[] = {
204a8d7fc4aSZbigniew Bodek 	/* Device interface */
205a8d7fc4aSZbigniew Bodek 	DEVMETHOD(device_detach,	mvneta_detach),
206a8d7fc4aSZbigniew Bodek 	/* MII interface */
207a8d7fc4aSZbigniew Bodek 	DEVMETHOD(miibus_readreg,       mvneta_miibus_readreg),
208a8d7fc4aSZbigniew Bodek 	DEVMETHOD(miibus_writereg,      mvneta_miibus_writereg),
209a8d7fc4aSZbigniew Bodek 	/* MDIO interface */
210a8d7fc4aSZbigniew Bodek 	DEVMETHOD(mdio_readreg,		mvneta_miibus_readreg),
211a8d7fc4aSZbigniew Bodek 	DEVMETHOD(mdio_writereg,	mvneta_miibus_writereg),
212a8d7fc4aSZbigniew Bodek 
213a8d7fc4aSZbigniew Bodek 	/* End */
214a8d7fc4aSZbigniew Bodek 	DEVMETHOD_END
215a8d7fc4aSZbigniew Bodek };
216a8d7fc4aSZbigniew Bodek 
217a8d7fc4aSZbigniew Bodek DEFINE_CLASS_0(mvneta, mvneta_driver, mvneta_methods, sizeof(struct mvneta_softc));
218a8d7fc4aSZbigniew Bodek 
2193e38757dSJohn Baldwin DRIVER_MODULE(miibus, mvneta, miibus_driver, 0, 0);
2208933f7d6SJohn Baldwin DRIVER_MODULE(mdio, mvneta, mdio_driver, 0, 0);
221a8d7fc4aSZbigniew Bodek MODULE_DEPEND(mvneta, mdio, 1, 1, 1);
222a8d7fc4aSZbigniew Bodek MODULE_DEPEND(mvneta, ether, 1, 1, 1);
223a8d7fc4aSZbigniew Bodek MODULE_DEPEND(mvneta, miibus, 1, 1, 1);
2245572fda3SWojciech Macek MODULE_DEPEND(mvneta, mvxpbm, 1, 1, 1);
225a8d7fc4aSZbigniew Bodek 
226a8d7fc4aSZbigniew Bodek /*
227a8d7fc4aSZbigniew Bodek  * List of MIB register and names
228a8d7fc4aSZbigniew Bodek  */
229a8d7fc4aSZbigniew Bodek enum mvneta_mib_idx
230a8d7fc4aSZbigniew Bodek {
231a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_RX_GOOD_OCT_IDX,
232a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_RX_BAD_OCT_IDX,
233a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_TX_MAC_TRNS_ERR_IDX,
234a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_RX_GOOD_FRAME_IDX,
235a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_RX_BAD_FRAME_IDX,
236a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_RX_BCAST_FRAME_IDX,
237a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_RX_MCAST_FRAME_IDX,
238a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_RX_FRAME64_OCT_IDX,
239a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_RX_FRAME127_OCT_IDX,
240a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_RX_FRAME255_OCT_IDX,
241a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_RX_FRAME511_OCT_IDX,
242a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_RX_FRAME1023_OCT_IDX,
243a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_RX_FRAMEMAX_OCT_IDX,
244a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_TX_GOOD_OCT_IDX,
245a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_TX_GOOD_FRAME_IDX,
246a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_TX_EXCES_COL_IDX,
247a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_TX_MCAST_FRAME_IDX,
248a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_TX_BCAST_FRAME_IDX,
249a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_TX_MAC_CTL_ERR_IDX,
250a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_FC_SENT_IDX,
251a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_FC_GOOD_IDX,
252a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_FC_BAD_IDX,
253a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_PKT_UNDERSIZE_IDX,
254a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_PKT_FRAGMENT_IDX,
255a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_PKT_OVERSIZE_IDX,
256a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_PKT_JABBER_IDX,
257a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_MAC_RX_ERR_IDX,
258a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_MAC_CRC_ERR_IDX,
259a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_MAC_COL_IDX,
260a8d7fc4aSZbigniew Bodek 	MVNETA_MIB_MAC_LATE_COL_IDX,
261a8d7fc4aSZbigniew Bodek };
262a8d7fc4aSZbigniew Bodek 
263a8d7fc4aSZbigniew Bodek STATIC struct mvneta_mib_def {
264a8d7fc4aSZbigniew Bodek 	uint32_t regnum;
265a8d7fc4aSZbigniew Bodek 	int reg64;
266a8d7fc4aSZbigniew Bodek 	const char *sysctl_name;
267a8d7fc4aSZbigniew Bodek 	const char *desc;
268a8d7fc4aSZbigniew Bodek } mvneta_mib_list[] = {
269a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_RX_GOOD_OCT_IDX] = {MVNETA_MIB_RX_GOOD_OCT, 1,
270a8d7fc4aSZbigniew Bodek 	    "rx_good_oct", "Good Octets Rx"},
271a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_RX_BAD_OCT_IDX] = {MVNETA_MIB_RX_BAD_OCT, 0,
272a8d7fc4aSZbigniew Bodek 	    "rx_bad_oct", "Bad  Octets Rx"},
273a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_TX_MAC_TRNS_ERR_IDX] = {MVNETA_MIB_TX_MAC_TRNS_ERR, 0,
274a8d7fc4aSZbigniew Bodek 	    "tx_mac_err", "MAC Transmit Error"},
275a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_RX_GOOD_FRAME_IDX] = {MVNETA_MIB_RX_GOOD_FRAME, 0,
276a8d7fc4aSZbigniew Bodek 	    "rx_good_frame", "Good Frames Rx"},
277a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_RX_BAD_FRAME_IDX] = {MVNETA_MIB_RX_BAD_FRAME, 0,
278a8d7fc4aSZbigniew Bodek 	    "rx_bad_frame", "Bad Frames Rx"},
279a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_RX_BCAST_FRAME_IDX] = {MVNETA_MIB_RX_BCAST_FRAME, 0,
280a8d7fc4aSZbigniew Bodek 	    "rx_bcast_frame", "Broadcast Frames Rx"},
281a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_RX_MCAST_FRAME_IDX] = {MVNETA_MIB_RX_MCAST_FRAME, 0,
282a8d7fc4aSZbigniew Bodek 	    "rx_mcast_frame", "Multicast Frames Rx"},
283a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_RX_FRAME64_OCT_IDX] = {MVNETA_MIB_RX_FRAME64_OCT, 0,
284a8d7fc4aSZbigniew Bodek 	    "rx_frame_1_64", "Frame Size    1 -   64"},
285a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_RX_FRAME127_OCT_IDX] = {MVNETA_MIB_RX_FRAME127_OCT, 0,
286a8d7fc4aSZbigniew Bodek 	    "rx_frame_65_127", "Frame Size   65 -  127"},
287a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_RX_FRAME255_OCT_IDX] = {MVNETA_MIB_RX_FRAME255_OCT, 0,
288a8d7fc4aSZbigniew Bodek 	    "rx_frame_128_255", "Frame Size  128 -  255"},
289a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_RX_FRAME511_OCT_IDX] = {MVNETA_MIB_RX_FRAME511_OCT, 0,
290a8d7fc4aSZbigniew Bodek 	    "rx_frame_256_511", "Frame Size  256 -  511"},
291a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_RX_FRAME1023_OCT_IDX] = {MVNETA_MIB_RX_FRAME1023_OCT, 0,
292a8d7fc4aSZbigniew Bodek 	    "rx_frame_512_1023", "Frame Size  512 - 1023"},
293a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_RX_FRAMEMAX_OCT_IDX] = {MVNETA_MIB_RX_FRAMEMAX_OCT, 0,
294a8d7fc4aSZbigniew Bodek 	    "rx_fame_1024_max", "Frame Size 1024 -  Max"},
295a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_TX_GOOD_OCT_IDX] = {MVNETA_MIB_TX_GOOD_OCT, 1,
296a8d7fc4aSZbigniew Bodek 	    "tx_good_oct", "Good Octets Tx"},
297a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_TX_GOOD_FRAME_IDX] = {MVNETA_MIB_TX_GOOD_FRAME, 0,
298a8d7fc4aSZbigniew Bodek 	    "tx_good_frame", "Good Frames Tx"},
299a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_TX_EXCES_COL_IDX] = {MVNETA_MIB_TX_EXCES_COL, 0,
300a8d7fc4aSZbigniew Bodek 	    "tx_exces_collision", "Excessive Collision"},
301a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_TX_MCAST_FRAME_IDX] = {MVNETA_MIB_TX_MCAST_FRAME, 0,
302a8d7fc4aSZbigniew Bodek 	    "tx_mcast_frame", "Multicast Frames Tx"},
303a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_TX_BCAST_FRAME_IDX] = {MVNETA_MIB_TX_BCAST_FRAME, 0,
304a8d7fc4aSZbigniew Bodek 	    "tx_bcast_frame", "Broadcast Frames Tx"},
305a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_TX_MAC_CTL_ERR_IDX] = {MVNETA_MIB_TX_MAC_CTL_ERR, 0,
306a8d7fc4aSZbigniew Bodek 	    "tx_mac_ctl_err", "Unknown MAC Control"},
307a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_FC_SENT_IDX] = {MVNETA_MIB_FC_SENT, 0,
308a8d7fc4aSZbigniew Bodek 	    "fc_tx", "Flow Control Tx"},
309a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_FC_GOOD_IDX] = {MVNETA_MIB_FC_GOOD, 0,
310a8d7fc4aSZbigniew Bodek 	    "fc_rx_good", "Good Flow Control Rx"},
311a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_FC_BAD_IDX] = {MVNETA_MIB_FC_BAD, 0,
312a8d7fc4aSZbigniew Bodek 	    "fc_rx_bad", "Bad Flow Control Rx"},
313a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_PKT_UNDERSIZE_IDX] = {MVNETA_MIB_PKT_UNDERSIZE, 0,
314a8d7fc4aSZbigniew Bodek 	    "pkt_undersize", "Undersized Packets Rx"},
315a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_PKT_FRAGMENT_IDX] = {MVNETA_MIB_PKT_FRAGMENT, 0,
316a8d7fc4aSZbigniew Bodek 	    "pkt_fragment", "Fragmented Packets Rx"},
317a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_PKT_OVERSIZE_IDX] = {MVNETA_MIB_PKT_OVERSIZE, 0,
318a8d7fc4aSZbigniew Bodek 	    "pkt_oversize", "Oversized Packets Rx"},
319a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_PKT_JABBER_IDX] = {MVNETA_MIB_PKT_JABBER, 0,
320a8d7fc4aSZbigniew Bodek 	    "pkt_jabber", "Jabber Packets Rx"},
321a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_MAC_RX_ERR_IDX] = {MVNETA_MIB_MAC_RX_ERR, 0,
322a8d7fc4aSZbigniew Bodek 	    "mac_rx_err", "MAC Rx Errors"},
323a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_MAC_CRC_ERR_IDX] = {MVNETA_MIB_MAC_CRC_ERR, 0,
324a8d7fc4aSZbigniew Bodek 	    "mac_crc_err", "MAC CRC Errors"},
325a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_MAC_COL_IDX] = {MVNETA_MIB_MAC_COL, 0,
326a8d7fc4aSZbigniew Bodek 	    "mac_collision", "MAC Collision"},
327a8d7fc4aSZbigniew Bodek 	[MVNETA_MIB_MAC_LATE_COL_IDX] = {MVNETA_MIB_MAC_LATE_COL, 0,
328a8d7fc4aSZbigniew Bodek 	    "mac_late_collision", "MAC Late Collision"},
329a8d7fc4aSZbigniew Bodek };
330a8d7fc4aSZbigniew Bodek 
331a8d7fc4aSZbigniew Bodek static struct resource_spec res_spec[] = {
332a8d7fc4aSZbigniew Bodek 	{ SYS_RES_MEMORY, 0, RF_ACTIVE },
333a8d7fc4aSZbigniew Bodek 	{ SYS_RES_IRQ, 0, RF_ACTIVE },
334a8d7fc4aSZbigniew Bodek 	{ -1, 0}
335a8d7fc4aSZbigniew Bodek };
336a8d7fc4aSZbigniew Bodek 
337a8d7fc4aSZbigniew Bodek static struct {
338a8d7fc4aSZbigniew Bodek 	driver_intr_t *handler;
339a8d7fc4aSZbigniew Bodek 	char * description;
340a8d7fc4aSZbigniew Bodek } mvneta_intrs[] = {
341a8d7fc4aSZbigniew Bodek 	{ mvneta_rxtxth_intr, "MVNETA aggregated interrupt" },
342a8d7fc4aSZbigniew Bodek };
343a8d7fc4aSZbigniew Bodek 
344a8d7fc4aSZbigniew Bodek static int
345a8d7fc4aSZbigniew Bodek mvneta_set_mac_address(struct mvneta_softc *sc, uint8_t *addr)
346a8d7fc4aSZbigniew Bodek {
347a8d7fc4aSZbigniew Bodek 	unsigned int mac_h;
348a8d7fc4aSZbigniew Bodek 	unsigned int mac_l;
349a8d7fc4aSZbigniew Bodek 
350a8d7fc4aSZbigniew Bodek 	mac_l = (addr[4] << 8) | (addr[5]);
351a8d7fc4aSZbigniew Bodek 	mac_h = (addr[0] << 24) | (addr[1] << 16) |
352a8d7fc4aSZbigniew Bodek 	    (addr[2] << 8) | (addr[3] << 0);
353a8d7fc4aSZbigniew Bodek 
354a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_MACAL, mac_l);
355a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_MACAH, mac_h);
356a8d7fc4aSZbigniew Bodek 	return (0);
357a8d7fc4aSZbigniew Bodek }
358a8d7fc4aSZbigniew Bodek 
359a8d7fc4aSZbigniew Bodek static int
360a8d7fc4aSZbigniew Bodek mvneta_get_mac_address(struct mvneta_softc *sc, uint8_t *addr)
361a8d7fc4aSZbigniew Bodek {
362a8d7fc4aSZbigniew Bodek 	uint32_t mac_l, mac_h;
363a8d7fc4aSZbigniew Bodek 
364a8d7fc4aSZbigniew Bodek #ifdef FDT
365a8d7fc4aSZbigniew Bodek 	if (mvneta_fdt_mac_address(sc, addr) == 0)
366a8d7fc4aSZbigniew Bodek 		return (0);
367a8d7fc4aSZbigniew Bodek #endif
368a8d7fc4aSZbigniew Bodek 	/*
369a8d7fc4aSZbigniew Bodek 	 * Fall back -- use the currently programmed address.
370a8d7fc4aSZbigniew Bodek 	 */
371a8d7fc4aSZbigniew Bodek 	mac_l = MVNETA_READ(sc, MVNETA_MACAL);
372a8d7fc4aSZbigniew Bodek 	mac_h = MVNETA_READ(sc, MVNETA_MACAH);
373a8d7fc4aSZbigniew Bodek 	if (mac_l == 0 && mac_h == 0) {
374a8d7fc4aSZbigniew Bodek 		/*
375a8d7fc4aSZbigniew Bodek 		 * Generate pseudo-random MAC.
376a8d7fc4aSZbigniew Bodek 		 * Set lower part to random number | unit number.
377a8d7fc4aSZbigniew Bodek 		 */
378a8d7fc4aSZbigniew Bodek 		mac_l = arc4random() & ~0xff;
379a8d7fc4aSZbigniew Bodek 		mac_l |= device_get_unit(sc->dev) & 0xff;
380a8d7fc4aSZbigniew Bodek 		mac_h = arc4random();
381a8d7fc4aSZbigniew Bodek 		mac_h &= ~(3 << 24);	/* Clear multicast and LAA bits */
382a8d7fc4aSZbigniew Bodek 		if (bootverbose) {
383a8d7fc4aSZbigniew Bodek 			device_printf(sc->dev,
384a8d7fc4aSZbigniew Bodek 			    "Could not acquire MAC address. "
385a8d7fc4aSZbigniew Bodek 			    "Using randomized one.\n");
386a8d7fc4aSZbigniew Bodek 		}
387a8d7fc4aSZbigniew Bodek 	}
388a8d7fc4aSZbigniew Bodek 
389a8d7fc4aSZbigniew Bodek 	addr[0] = (mac_h & 0xff000000) >> 24;
390a8d7fc4aSZbigniew Bodek 	addr[1] = (mac_h & 0x00ff0000) >> 16;
391a8d7fc4aSZbigniew Bodek 	addr[2] = (mac_h & 0x0000ff00) >> 8;
392a8d7fc4aSZbigniew Bodek 	addr[3] = (mac_h & 0x000000ff);
393a8d7fc4aSZbigniew Bodek 	addr[4] = (mac_l & 0x0000ff00) >> 8;
394a8d7fc4aSZbigniew Bodek 	addr[5] = (mac_l & 0x000000ff);
395a8d7fc4aSZbigniew Bodek 	return (0);
396a8d7fc4aSZbigniew Bodek }
397a8d7fc4aSZbigniew Bodek 
398a8d7fc4aSZbigniew Bodek STATIC boolean_t
399a8d7fc4aSZbigniew Bodek mvneta_has_switch(device_t self)
400a8d7fc4aSZbigniew Bodek {
4015572fda3SWojciech Macek #ifdef FDT
4025572fda3SWojciech Macek 	return (mvneta_has_switch_fdt(self));
4035572fda3SWojciech Macek #endif
404a8d7fc4aSZbigniew Bodek 
4055572fda3SWojciech Macek 	return (false);
406a8d7fc4aSZbigniew Bodek }
407a8d7fc4aSZbigniew Bodek 
408a8d7fc4aSZbigniew Bodek STATIC int
409a8d7fc4aSZbigniew Bodek mvneta_dma_create(struct mvneta_softc *sc)
410a8d7fc4aSZbigniew Bodek {
411a8d7fc4aSZbigniew Bodek 	size_t maxsize, maxsegsz;
412a8d7fc4aSZbigniew Bodek 	size_t q;
413a8d7fc4aSZbigniew Bodek 	int error;
414a8d7fc4aSZbigniew Bodek 
415a8d7fc4aSZbigniew Bodek 	/*
416a8d7fc4aSZbigniew Bodek 	 * Create Tx DMA
417a8d7fc4aSZbigniew Bodek 	 */
418a8d7fc4aSZbigniew Bodek 	maxsize = maxsegsz = sizeof(struct mvneta_tx_desc) * MVNETA_TX_RING_CNT;
419a8d7fc4aSZbigniew Bodek 
420a8d7fc4aSZbigniew Bodek 	error = bus_dma_tag_create(
421a8d7fc4aSZbigniew Bodek 	    bus_get_dma_tag(sc->dev),		/* parent */
422a8d7fc4aSZbigniew Bodek 	    16, 0,                              /* alignment, boundary */
423a8d7fc4aSZbigniew Bodek 	    BUS_SPACE_MAXADDR_32BIT,            /* lowaddr */
424a8d7fc4aSZbigniew Bodek 	    BUS_SPACE_MAXADDR,                  /* highaddr */
425a8d7fc4aSZbigniew Bodek 	    NULL, NULL,                         /* filtfunc, filtfuncarg */
426a8d7fc4aSZbigniew Bodek 	    maxsize,				/* maxsize */
427a8d7fc4aSZbigniew Bodek 	    1,					/* nsegments */
428a8d7fc4aSZbigniew Bodek 	    maxsegsz,				/* maxsegsz */
429a8d7fc4aSZbigniew Bodek 	    0,					/* flags */
430a8d7fc4aSZbigniew Bodek 	    NULL, NULL,				/* lockfunc, lockfuncarg */
431a8d7fc4aSZbigniew Bodek 	    &sc->tx_dtag);			/* dmat */
432a8d7fc4aSZbigniew Bodek 	if (error != 0) {
433a8d7fc4aSZbigniew Bodek 		device_printf(sc->dev,
434a8d7fc4aSZbigniew Bodek 		    "Failed to create DMA tag for Tx descriptors.\n");
435a8d7fc4aSZbigniew Bodek 		goto fail;
436a8d7fc4aSZbigniew Bodek 	}
437a8d7fc4aSZbigniew Bodek 	error = bus_dma_tag_create(
438a8d7fc4aSZbigniew Bodek 	    bus_get_dma_tag(sc->dev),		/* parent */
439a8d7fc4aSZbigniew Bodek 	    1, 0,				/* alignment, boundary */
440a8d7fc4aSZbigniew Bodek 	    BUS_SPACE_MAXADDR_32BIT,		/* lowaddr */
441a8d7fc4aSZbigniew Bodek 	    BUS_SPACE_MAXADDR,			/* highaddr */
442a8d7fc4aSZbigniew Bodek 	    NULL, NULL,				/* filtfunc, filtfuncarg */
44373f20bb3SMarcin Wojtas 	    MVNETA_MAX_FRAME,			/* maxsize */
444a8d7fc4aSZbigniew Bodek 	    MVNETA_TX_SEGLIMIT,			/* nsegments */
44573f20bb3SMarcin Wojtas 	    MVNETA_MAX_FRAME,			/* maxsegsz */
446a8d7fc4aSZbigniew Bodek 	    BUS_DMA_ALLOCNOW,			/* flags */
447a8d7fc4aSZbigniew Bodek 	    NULL, NULL,				/* lockfunc, lockfuncarg */
448a8d7fc4aSZbigniew Bodek 	    &sc->txmbuf_dtag);
449a8d7fc4aSZbigniew Bodek 	if (error != 0) {
450a8d7fc4aSZbigniew Bodek 		device_printf(sc->dev,
451a8d7fc4aSZbigniew Bodek 		    "Failed to create DMA tag for Tx mbufs.\n");
452a8d7fc4aSZbigniew Bodek 		goto fail;
453a8d7fc4aSZbigniew Bodek 	}
454a8d7fc4aSZbigniew Bodek 
455a8d7fc4aSZbigniew Bodek 	for (q = 0; q < MVNETA_TX_QNUM_MAX; q++) {
456a8d7fc4aSZbigniew Bodek 		error = mvneta_ring_alloc_tx_queue(sc, q);
457a8d7fc4aSZbigniew Bodek 		if (error != 0) {
458a8d7fc4aSZbigniew Bodek 			device_printf(sc->dev,
459e314ac07SMarcin Wojtas 			    "Failed to allocate DMA safe memory for TxQ: %zu\n", q);
460a8d7fc4aSZbigniew Bodek 			goto fail;
461a8d7fc4aSZbigniew Bodek 		}
462a8d7fc4aSZbigniew Bodek 	}
463a8d7fc4aSZbigniew Bodek 
464a8d7fc4aSZbigniew Bodek 	/*
465a8d7fc4aSZbigniew Bodek 	 * Create Rx DMA.
466a8d7fc4aSZbigniew Bodek 	 */
467a8d7fc4aSZbigniew Bodek 	/* Create tag for Rx descripors */
468a8d7fc4aSZbigniew Bodek 	error = bus_dma_tag_create(
469a8d7fc4aSZbigniew Bodek 	    bus_get_dma_tag(sc->dev),		/* parent */
470a8d7fc4aSZbigniew Bodek 	    32, 0,                              /* alignment, boundary */
471a8d7fc4aSZbigniew Bodek 	    BUS_SPACE_MAXADDR_32BIT,            /* lowaddr */
472a8d7fc4aSZbigniew Bodek 	    BUS_SPACE_MAXADDR,                  /* highaddr */
473a8d7fc4aSZbigniew Bodek 	    NULL, NULL,                         /* filtfunc, filtfuncarg */
474a8d7fc4aSZbigniew Bodek 	    sizeof(struct mvneta_rx_desc) * MVNETA_RX_RING_CNT, /* maxsize */
475a8d7fc4aSZbigniew Bodek 	    1,					/* nsegments */
476a8d7fc4aSZbigniew Bodek 	    sizeof(struct mvneta_rx_desc) * MVNETA_RX_RING_CNT, /* maxsegsz */
477a8d7fc4aSZbigniew Bodek 	    0,					/* flags */
478a8d7fc4aSZbigniew Bodek 	    NULL, NULL,				/* lockfunc, lockfuncarg */
479a8d7fc4aSZbigniew Bodek 	    &sc->rx_dtag);			/* dmat */
480a8d7fc4aSZbigniew Bodek 	if (error != 0) {
481a8d7fc4aSZbigniew Bodek 		device_printf(sc->dev,
482a8d7fc4aSZbigniew Bodek 		    "Failed to create DMA tag for Rx descriptors.\n");
483a8d7fc4aSZbigniew Bodek 		goto fail;
484a8d7fc4aSZbigniew Bodek 	}
485a8d7fc4aSZbigniew Bodek 
486a8d7fc4aSZbigniew Bodek 	/* Create tag for Rx buffers */
487a8d7fc4aSZbigniew Bodek 	error = bus_dma_tag_create(
488a8d7fc4aSZbigniew Bodek 	    bus_get_dma_tag(sc->dev),		/* parent */
489a8d7fc4aSZbigniew Bodek 	    32, 0,				/* alignment, boundary */
490a8d7fc4aSZbigniew Bodek 	    BUS_SPACE_MAXADDR_32BIT,		/* lowaddr */
491a8d7fc4aSZbigniew Bodek 	    BUS_SPACE_MAXADDR,			/* highaddr */
492a8d7fc4aSZbigniew Bodek 	    NULL, NULL,				/* filtfunc, filtfuncarg */
49373f20bb3SMarcin Wojtas 	    MVNETA_MAX_FRAME, 1,		/* maxsize, nsegments */
49473f20bb3SMarcin Wojtas 	    MVNETA_MAX_FRAME,			/* maxsegsz */
495a8d7fc4aSZbigniew Bodek 	    0,					/* flags */
496a8d7fc4aSZbigniew Bodek 	    NULL, NULL,				/* lockfunc, lockfuncarg */
497a8d7fc4aSZbigniew Bodek 	    &sc->rxbuf_dtag);			/* dmat */
498a8d7fc4aSZbigniew Bodek 	if (error != 0) {
499a8d7fc4aSZbigniew Bodek 		device_printf(sc->dev,
500a8d7fc4aSZbigniew Bodek 		    "Failed to create DMA tag for Rx buffers.\n");
501a8d7fc4aSZbigniew Bodek 		goto fail;
502a8d7fc4aSZbigniew Bodek 	}
503a8d7fc4aSZbigniew Bodek 
504a8d7fc4aSZbigniew Bodek 	for (q = 0; q < MVNETA_RX_QNUM_MAX; q++) {
505a8d7fc4aSZbigniew Bodek 		if (mvneta_ring_alloc_rx_queue(sc, q) != 0) {
506a8d7fc4aSZbigniew Bodek 			device_printf(sc->dev,
507e314ac07SMarcin Wojtas 			    "Failed to allocate DMA safe memory for RxQ: %zu\n", q);
508a8d7fc4aSZbigniew Bodek 			goto fail;
509a8d7fc4aSZbigniew Bodek 		}
510a8d7fc4aSZbigniew Bodek 	}
511a8d7fc4aSZbigniew Bodek 
512a8d7fc4aSZbigniew Bodek 	return (0);
513a8d7fc4aSZbigniew Bodek fail:
514a8d7fc4aSZbigniew Bodek 	mvneta_detach(sc->dev);
515a8d7fc4aSZbigniew Bodek 
516a8d7fc4aSZbigniew Bodek 	return (error);
517a8d7fc4aSZbigniew Bodek }
518a8d7fc4aSZbigniew Bodek 
519a8d7fc4aSZbigniew Bodek /* ARGSUSED */
520a8d7fc4aSZbigniew Bodek int
521a8d7fc4aSZbigniew Bodek mvneta_attach(device_t self)
522a8d7fc4aSZbigniew Bodek {
523a8d7fc4aSZbigniew Bodek 	struct mvneta_softc *sc;
524992fa62bSJustin Hibbits 	if_t ifp;
525a8d7fc4aSZbigniew Bodek 	device_t child;
526a8d7fc4aSZbigniew Bodek 	int ifm_target;
527a8d7fc4aSZbigniew Bodek 	int q, error;
528e314ac07SMarcin Wojtas #if !defined(__aarch64__)
529a8d7fc4aSZbigniew Bodek 	uint32_t reg;
530e314ac07SMarcin Wojtas #endif
5314885d6f3SHubert Mazur 	clk_t clk;
53206c30b2cSAlbert Jakieła 
533a8d7fc4aSZbigniew Bodek 	sc = device_get_softc(self);
534a8d7fc4aSZbigniew Bodek 	sc->dev = self;
535a8d7fc4aSZbigniew Bodek 
536a8d7fc4aSZbigniew Bodek 	mtx_init(&sc->mtx, "mvneta_sc", NULL, MTX_DEF);
537a8d7fc4aSZbigniew Bodek 
538a8d7fc4aSZbigniew Bodek 	error = bus_alloc_resources(self, res_spec, sc->res);
539a8d7fc4aSZbigniew Bodek 	if (error) {
540a8d7fc4aSZbigniew Bodek 		device_printf(self, "could not allocate resources\n");
541a8d7fc4aSZbigniew Bodek 		return (ENXIO);
542a8d7fc4aSZbigniew Bodek 	}
543a8d7fc4aSZbigniew Bodek 
544a8d7fc4aSZbigniew Bodek 	sc->version = MVNETA_READ(sc, MVNETA_PV);
545a8d7fc4aSZbigniew Bodek 	device_printf(self, "version is %x\n", sc->version);
546a8d7fc4aSZbigniew Bodek 	callout_init(&sc->tick_ch, 0);
547a8d7fc4aSZbigniew Bodek 
548a8d7fc4aSZbigniew Bodek 	/*
549a8d7fc4aSZbigniew Bodek 	 * make sure DMA engines are in reset state
550a8d7fc4aSZbigniew Bodek 	 */
551a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PRXINIT, 0x00000001);
552a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PTXINIT, 0x00000001);
553a8d7fc4aSZbigniew Bodek 
5544885d6f3SHubert Mazur 	error = clk_get_by_ofw_index(sc->dev, ofw_bus_get_node(sc->dev), 0,
5554885d6f3SHubert Mazur 	    &clk);
5564885d6f3SHubert Mazur 	if (error != 0) {
55706c30b2cSAlbert Jakieła #if defined(__aarch64__)
5584885d6f3SHubert Mazur 		device_printf(sc->dev,
5594885d6f3SHubert Mazur 			"Cannot get clock, using default frequency: %d\n",
5604885d6f3SHubert Mazur 			A3700_TCLK_250MHZ);
5614885d6f3SHubert Mazur 		sc->clk_freq = A3700_TCLK_250MHZ;
56206c30b2cSAlbert Jakieła #else
56306c30b2cSAlbert Jakieła 		device_printf(sc->dev,
56406c30b2cSAlbert Jakieła 			"Cannot get clock, using get_tclk()\n");
56506c30b2cSAlbert Jakieła 		sc->clk_freq = get_tclk();
56606c30b2cSAlbert Jakieła #endif
5674885d6f3SHubert Mazur 	} else {
5684885d6f3SHubert Mazur 		error = clk_get_freq(clk, &sc->clk_freq);
5694885d6f3SHubert Mazur 		if (error != 0) {
5704885d6f3SHubert Mazur 			device_printf(sc->dev,
5714885d6f3SHubert Mazur 				"Cannot obtain frequency from parent clock\n");
5724885d6f3SHubert Mazur 			bus_release_resources(sc->dev, res_spec, sc->res);
5734885d6f3SHubert Mazur 			return (error);
5744885d6f3SHubert Mazur 		}
5754885d6f3SHubert Mazur 	}
5764885d6f3SHubert Mazur 
577e314ac07SMarcin Wojtas #if !defined(__aarch64__)
578a8d7fc4aSZbigniew Bodek 	/*
579a8d7fc4aSZbigniew Bodek 	 * Disable port snoop for buffers and descriptors
580a8d7fc4aSZbigniew Bodek 	 * to avoid L2 caching of both without DRAM copy.
581a8d7fc4aSZbigniew Bodek 	 * Obtain coherency settings from the first MBUS
582a8d7fc4aSZbigniew Bodek 	 * window attribute.
583a8d7fc4aSZbigniew Bodek 	 */
584a8d7fc4aSZbigniew Bodek 	if ((MVNETA_READ(sc, MV_WIN_NETA_BASE(0)) & IO_WIN_COH_ATTR_MASK) == 0) {
585a8d7fc4aSZbigniew Bodek 		reg = MVNETA_READ(sc, MVNETA_PSNPCFG);
586a8d7fc4aSZbigniew Bodek 		reg &= ~MVNETA_PSNPCFG_DESCSNP_MASK;
587a8d7fc4aSZbigniew Bodek 		reg &= ~MVNETA_PSNPCFG_BUFSNP_MASK;
588a8d7fc4aSZbigniew Bodek 		MVNETA_WRITE(sc, MVNETA_PSNPCFG, reg);
589a8d7fc4aSZbigniew Bodek 	}
590e314ac07SMarcin Wojtas #endif
591a8d7fc4aSZbigniew Bodek 
592b831f9ceSHubert Mazur 	error = bus_setup_intr(self, sc->res[1],
593b831f9ceSHubert Mazur 	    INTR_TYPE_NET | INTR_MPSAFE, NULL, mvneta_intrs[0].handler, sc,
594b831f9ceSHubert Mazur 	    &sc->ih_cookie[0]);
595b831f9ceSHubert Mazur 	if (error) {
596b831f9ceSHubert Mazur 		device_printf(self, "could not setup %s\n",
597b831f9ceSHubert Mazur 		    mvneta_intrs[0].description);
598b831f9ceSHubert Mazur 		mvneta_detach(self);
599b831f9ceSHubert Mazur 		return (error);
600b831f9ceSHubert Mazur 	}
601b831f9ceSHubert Mazur 
602a8d7fc4aSZbigniew Bodek 	/*
603a8d7fc4aSZbigniew Bodek 	 * MAC address
604a8d7fc4aSZbigniew Bodek 	 */
605a8d7fc4aSZbigniew Bodek 	if (mvneta_get_mac_address(sc, sc->enaddr)) {
606a8d7fc4aSZbigniew Bodek 		device_printf(self, "no mac address.\n");
607a8d7fc4aSZbigniew Bodek 		return (ENXIO);
608a8d7fc4aSZbigniew Bodek 	}
609a8d7fc4aSZbigniew Bodek 	mvneta_set_mac_address(sc, sc->enaddr);
610a8d7fc4aSZbigniew Bodek 
611a8d7fc4aSZbigniew Bodek 	mvneta_disable_intr(sc);
612a8d7fc4aSZbigniew Bodek 
613a8d7fc4aSZbigniew Bodek 	/* Allocate network interface */
614a8d7fc4aSZbigniew Bodek 	ifp = sc->ifp = if_alloc(IFT_ETHER);
615a8d7fc4aSZbigniew Bodek 	if_initname(ifp, device_get_name(self), device_get_unit(self));
616a8d7fc4aSZbigniew Bodek 
617a8d7fc4aSZbigniew Bodek 	/*
618a8d7fc4aSZbigniew Bodek 	 * We can support 802.1Q VLAN-sized frames and jumbo
619a8d7fc4aSZbigniew Bodek 	 * Ethernet frames.
620a8d7fc4aSZbigniew Bodek 	 */
621992fa62bSJustin Hibbits 	if_setcapabilitiesbit(ifp, IFCAP_VLAN_MTU | IFCAP_JUMBO_MTU, 0);
622a8d7fc4aSZbigniew Bodek 
623992fa62bSJustin Hibbits 	if_setsoftc(ifp, sc);
624992fa62bSJustin Hibbits 	if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
625a8d7fc4aSZbigniew Bodek #ifdef MVNETA_MULTIQUEUE
626992fa62bSJustin Hibbits 	if_settransmitfn(ifp, mvneta_transmit);
627992fa62bSJustin Hibbits 	if_setqflushfn(ifp, mvneta_qflush);
628a8d7fc4aSZbigniew Bodek #else /* !MVNETA_MULTIQUEUE */
629992fa62bSJustin Hibbits 	if_setstartfn(ifp, mvneta_start);
630992fa62bSJustin Hibbits 	if_setsendqlen(ifp, MVNETA_TX_RING_CNT - 1);
631992fa62bSJustin Hibbits 	if_setsendqready(ifp);
632a8d7fc4aSZbigniew Bodek #endif
633992fa62bSJustin Hibbits 	if_setinitfn(ifp, mvneta_init);
634992fa62bSJustin Hibbits 	if_setioctlfn(ifp, mvneta_ioctl);
635a8d7fc4aSZbigniew Bodek 
636a8d7fc4aSZbigniew Bodek 	/*
637a8d7fc4aSZbigniew Bodek 	 * We can do IPv4/TCPv4/UDPv4/TCPv6/UDPv6 checksums in hardware.
638a8d7fc4aSZbigniew Bodek 	 */
639992fa62bSJustin Hibbits 	if_setcapabilitiesbit(ifp, IFCAP_HWCSUM, 0);
640a8d7fc4aSZbigniew Bodek 
641a8d7fc4aSZbigniew Bodek 	/*
642a8d7fc4aSZbigniew Bodek 	 * As VLAN hardware tagging is not supported
643a8d7fc4aSZbigniew Bodek 	 * but is necessary to perform VLAN hardware checksums,
644a8d7fc4aSZbigniew Bodek 	 * it is done in the driver
645a8d7fc4aSZbigniew Bodek 	 */
646992fa62bSJustin Hibbits 	if_setcapabilitiesbit(ifp, IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM, 0);
647a8d7fc4aSZbigniew Bodek 
648a8d7fc4aSZbigniew Bodek 	/*
649a8d7fc4aSZbigniew Bodek 	 * Currently IPv6 HW checksum is broken, so make sure it is disabled.
650a8d7fc4aSZbigniew Bodek 	 */
651992fa62bSJustin Hibbits 	if_setcapabilitiesbit(ifp, 0, IFCAP_HWCSUM_IPV6);
652992fa62bSJustin Hibbits 	if_setcapenable(ifp, if_getcapabilities(ifp));
653a8d7fc4aSZbigniew Bodek 
654a8d7fc4aSZbigniew Bodek 	/*
655a8d7fc4aSZbigniew Bodek 	 * Disabled option(s):
656a8d7fc4aSZbigniew Bodek 	 * - Support for Large Receive Offload
657a8d7fc4aSZbigniew Bodek 	 */
658992fa62bSJustin Hibbits 	if_setcapabilitiesbit(ifp, IFCAP_LRO, 0);
659a8d7fc4aSZbigniew Bodek 
660992fa62bSJustin Hibbits 	if_sethwassist(ifp, CSUM_IP | CSUM_TCP | CSUM_UDP);
661a8d7fc4aSZbigniew Bodek 
66273f20bb3SMarcin Wojtas 	sc->rx_frame_size = MCLBYTES; /* ether_ifattach() always sets normal mtu */
66373f20bb3SMarcin Wojtas 
664a8d7fc4aSZbigniew Bodek 	/*
665a8d7fc4aSZbigniew Bodek 	 * Device DMA Buffer allocation.
666a8d7fc4aSZbigniew Bodek 	 * Handles resource deallocation in case of failure.
667a8d7fc4aSZbigniew Bodek 	 */
668a8d7fc4aSZbigniew Bodek 	error = mvneta_dma_create(sc);
669a8d7fc4aSZbigniew Bodek 	if (error != 0) {
670a8d7fc4aSZbigniew Bodek 		mvneta_detach(self);
671a8d7fc4aSZbigniew Bodek 		return (error);
672a8d7fc4aSZbigniew Bodek 	}
673a8d7fc4aSZbigniew Bodek 
674a8d7fc4aSZbigniew Bodek 	/* Initialize queues */
675a8d7fc4aSZbigniew Bodek 	for (q = 0; q < MVNETA_TX_QNUM_MAX; q++) {
676a8d7fc4aSZbigniew Bodek 		error = mvneta_ring_init_tx_queue(sc, q);
677a8d7fc4aSZbigniew Bodek 		if (error != 0) {
678a8d7fc4aSZbigniew Bodek 			mvneta_detach(self);
679a8d7fc4aSZbigniew Bodek 			return (error);
680a8d7fc4aSZbigniew Bodek 		}
681a8d7fc4aSZbigniew Bodek 	}
682a8d7fc4aSZbigniew Bodek 
683a8d7fc4aSZbigniew Bodek 	for (q = 0; q < MVNETA_RX_QNUM_MAX; q++) {
684a8d7fc4aSZbigniew Bodek 		error = mvneta_ring_init_rx_queue(sc, q);
685a8d7fc4aSZbigniew Bodek 		if (error != 0) {
686a8d7fc4aSZbigniew Bodek 			mvneta_detach(self);
687a8d7fc4aSZbigniew Bodek 			return (error);
688a8d7fc4aSZbigniew Bodek 		}
689a8d7fc4aSZbigniew Bodek 	}
690a8d7fc4aSZbigniew Bodek 
691a8d7fc4aSZbigniew Bodek 	/*
692a8d7fc4aSZbigniew Bodek 	 * Enable DMA engines and Initialize Device Registers.
693a8d7fc4aSZbigniew Bodek 	 */
694a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PRXINIT, 0x00000000);
695a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PTXINIT, 0x00000000);
696a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PACC, MVNETA_PACC_ACCELERATIONMODE_EDM);
697a8d7fc4aSZbigniew Bodek 	mvneta_sc_lock(sc);
698a8d7fc4aSZbigniew Bodek 	mvneta_filter_setup(sc);
699a8d7fc4aSZbigniew Bodek 	mvneta_sc_unlock(sc);
700a8d7fc4aSZbigniew Bodek 	mvneta_initreg(ifp);
701a8d7fc4aSZbigniew Bodek 
702a8d7fc4aSZbigniew Bodek 	/*
703a8d7fc4aSZbigniew Bodek 	 * Now MAC is working, setup MII.
704a8d7fc4aSZbigniew Bodek 	 */
705a8d7fc4aSZbigniew Bodek 	if (mii_init == 0) {
706a8d7fc4aSZbigniew Bodek 		/*
707a8d7fc4aSZbigniew Bodek 		 * MII bus is shared by all MACs and all PHYs in SoC.
708a8d7fc4aSZbigniew Bodek 		 * serializing the bus access should be safe.
709a8d7fc4aSZbigniew Bodek 		 */
710a8d7fc4aSZbigniew Bodek 		mtx_init(&mii_mutex, "mvneta_mii", NULL, MTX_DEF);
711a8d7fc4aSZbigniew Bodek 		mii_init = 1;
712a8d7fc4aSZbigniew Bodek 	}
713a8d7fc4aSZbigniew Bodek 
714a8d7fc4aSZbigniew Bodek 	/* Attach PHY(s) */
715a8d7fc4aSZbigniew Bodek 	if ((sc->phy_addr != MII_PHY_ANY) && (!sc->use_inband_status)) {
716a8d7fc4aSZbigniew Bodek 		error = mii_attach(self, &sc->miibus, ifp, mvneta_mediachange,
717a8d7fc4aSZbigniew Bodek 		    mvneta_mediastatus, BMSR_DEFCAPMASK, sc->phy_addr,
718a8d7fc4aSZbigniew Bodek 		    MII_OFFSET_ANY, 0);
719a8d7fc4aSZbigniew Bodek 		if (error != 0) {
720ed166a01SMark Johnston 			device_printf(self, "MII attach failed, error: %d\n",
721ed166a01SMark Johnston 			    error);
722a8d7fc4aSZbigniew Bodek 			ether_ifdetach(sc->ifp);
723a8d7fc4aSZbigniew Bodek 			mvneta_detach(self);
724a8d7fc4aSZbigniew Bodek 			return (error);
725a8d7fc4aSZbigniew Bodek 		}
726a8d7fc4aSZbigniew Bodek 		sc->mii = device_get_softc(sc->miibus);
727a8d7fc4aSZbigniew Bodek 		sc->phy_attached = 1;
728a8d7fc4aSZbigniew Bodek 
729a8d7fc4aSZbigniew Bodek 		/* Disable auto-negotiation in MAC - rely on PHY layer */
730a8d7fc4aSZbigniew Bodek 		mvneta_update_autoneg(sc, FALSE);
731a8d7fc4aSZbigniew Bodek 	} else if (sc->use_inband_status == TRUE) {
732a8d7fc4aSZbigniew Bodek 		/* In-band link status */
733a8d7fc4aSZbigniew Bodek 		ifmedia_init(&sc->mvneta_ifmedia, 0, mvneta_mediachange,
734a8d7fc4aSZbigniew Bodek 		    mvneta_mediastatus);
735a8d7fc4aSZbigniew Bodek 
736a8d7fc4aSZbigniew Bodek 		/* Configure media */
737a8d7fc4aSZbigniew Bodek 		ifmedia_add(&sc->mvneta_ifmedia, IFM_ETHER | IFM_1000_T | IFM_FDX,
738a8d7fc4aSZbigniew Bodek 		    0, NULL);
739a8d7fc4aSZbigniew Bodek 		ifmedia_add(&sc->mvneta_ifmedia, IFM_ETHER | IFM_100_TX, 0, NULL);
740a8d7fc4aSZbigniew Bodek 		ifmedia_add(&sc->mvneta_ifmedia, IFM_ETHER | IFM_100_TX | IFM_FDX,
741a8d7fc4aSZbigniew Bodek 		    0, NULL);
742a8d7fc4aSZbigniew Bodek 		ifmedia_add(&sc->mvneta_ifmedia, IFM_ETHER | IFM_10_T, 0, NULL);
743a8d7fc4aSZbigniew Bodek 		ifmedia_add(&sc->mvneta_ifmedia, IFM_ETHER | IFM_10_T | IFM_FDX,
744a8d7fc4aSZbigniew Bodek 		    0, NULL);
745a8d7fc4aSZbigniew Bodek 		ifmedia_add(&sc->mvneta_ifmedia, IFM_ETHER | IFM_AUTO, 0, NULL);
746a8d7fc4aSZbigniew Bodek 		ifmedia_set(&sc->mvneta_ifmedia, IFM_ETHER | IFM_AUTO);
747a8d7fc4aSZbigniew Bodek 
748a8d7fc4aSZbigniew Bodek 		/* Enable auto-negotiation */
749a8d7fc4aSZbigniew Bodek 		mvneta_update_autoneg(sc, TRUE);
750a8d7fc4aSZbigniew Bodek 
751a8d7fc4aSZbigniew Bodek 		mvneta_sc_lock(sc);
752a8d7fc4aSZbigniew Bodek 		if (MVNETA_IS_LINKUP(sc))
753a8d7fc4aSZbigniew Bodek 			mvneta_linkup(sc);
754a8d7fc4aSZbigniew Bodek 		else
755a8d7fc4aSZbigniew Bodek 			mvneta_linkdown(sc);
756a8d7fc4aSZbigniew Bodek 		mvneta_sc_unlock(sc);
757a8d7fc4aSZbigniew Bodek 
758a8d7fc4aSZbigniew Bodek 	} else {
759a8d7fc4aSZbigniew Bodek 		/* Fixed-link, use predefined values */
760e13a20daSLuiz Otavio O Souza 		mvneta_update_autoneg(sc, FALSE);
761a8d7fc4aSZbigniew Bodek 		ifmedia_init(&sc->mvneta_ifmedia, 0, mvneta_mediachange,
762a8d7fc4aSZbigniew Bodek 		    mvneta_mediastatus);
763a8d7fc4aSZbigniew Bodek 
764a8d7fc4aSZbigniew Bodek 		ifm_target = IFM_ETHER;
765a8d7fc4aSZbigniew Bodek 		switch (sc->phy_speed) {
766a8d7fc4aSZbigniew Bodek 		case 2500:
767a8d7fc4aSZbigniew Bodek 			if (sc->phy_mode != MVNETA_PHY_SGMII &&
768a8d7fc4aSZbigniew Bodek 			    sc->phy_mode != MVNETA_PHY_QSGMII) {
769a8d7fc4aSZbigniew Bodek 				device_printf(self,
770a8d7fc4aSZbigniew Bodek 				    "2.5G speed can work only in (Q)SGMII mode\n");
771a8d7fc4aSZbigniew Bodek 				ether_ifdetach(sc->ifp);
772a8d7fc4aSZbigniew Bodek 				mvneta_detach(self);
773a8d7fc4aSZbigniew Bodek 				return (ENXIO);
774a8d7fc4aSZbigniew Bodek 			}
775a8d7fc4aSZbigniew Bodek 			ifm_target |= IFM_2500_T;
776a8d7fc4aSZbigniew Bodek 			break;
777a8d7fc4aSZbigniew Bodek 		case 1000:
778a8d7fc4aSZbigniew Bodek 			ifm_target |= IFM_1000_T;
779a8d7fc4aSZbigniew Bodek 			break;
780a8d7fc4aSZbigniew Bodek 		case 100:
781a8d7fc4aSZbigniew Bodek 			ifm_target |= IFM_100_TX;
782a8d7fc4aSZbigniew Bodek 			break;
783a8d7fc4aSZbigniew Bodek 		case 10:
784a8d7fc4aSZbigniew Bodek 			ifm_target |= IFM_10_T;
785a8d7fc4aSZbigniew Bodek 			break;
786a8d7fc4aSZbigniew Bodek 		default:
787a8d7fc4aSZbigniew Bodek 			ether_ifdetach(sc->ifp);
788a8d7fc4aSZbigniew Bodek 			mvneta_detach(self);
789a8d7fc4aSZbigniew Bodek 			return (ENXIO);
790a8d7fc4aSZbigniew Bodek 		}
791a8d7fc4aSZbigniew Bodek 
792a8d7fc4aSZbigniew Bodek 		if (sc->phy_fdx)
793a8d7fc4aSZbigniew Bodek 			ifm_target |= IFM_FDX;
794a8d7fc4aSZbigniew Bodek 		else
795a8d7fc4aSZbigniew Bodek 			ifm_target |= IFM_HDX;
796a8d7fc4aSZbigniew Bodek 
797a8d7fc4aSZbigniew Bodek 		ifmedia_add(&sc->mvneta_ifmedia, ifm_target, 0, NULL);
798a8d7fc4aSZbigniew Bodek 		ifmedia_set(&sc->mvneta_ifmedia, ifm_target);
799a8d7fc4aSZbigniew Bodek 		if_link_state_change(sc->ifp, LINK_STATE_UP);
800a8d7fc4aSZbigniew Bodek 
801a8d7fc4aSZbigniew Bodek 		if (mvneta_has_switch(self)) {
8020fd68d72SMarcin Wojtas 			if (bootverbose)
8030fd68d72SMarcin Wojtas 				device_printf(self, "This device is attached to a switch\n");
8045b56413dSWarner Losh 			child = device_add_child(sc->dev, "mdio", DEVICE_UNIT_ANY);
805a8d7fc4aSZbigniew Bodek 			if (child == NULL) {
806a8d7fc4aSZbigniew Bodek 				ether_ifdetach(sc->ifp);
807a8d7fc4aSZbigniew Bodek 				mvneta_detach(self);
808a8d7fc4aSZbigniew Bodek 				return (ENXIO);
809a8d7fc4aSZbigniew Bodek 			}
81018250ec6SJohn Baldwin 			bus_attach_children(sc->dev);
81118250ec6SJohn Baldwin 			bus_attach_children(child);
812a8d7fc4aSZbigniew Bodek 		}
813a8d7fc4aSZbigniew Bodek 
814a8d7fc4aSZbigniew Bodek 		/* Configure MAC media */
815a8d7fc4aSZbigniew Bodek 		mvneta_update_media(sc, ifm_target);
816a8d7fc4aSZbigniew Bodek 	}
817a8d7fc4aSZbigniew Bodek 
818b831f9ceSHubert Mazur 	ether_ifattach(ifp, sc->enaddr);
819a8d7fc4aSZbigniew Bodek 
820a8d7fc4aSZbigniew Bodek 	callout_reset(&sc->tick_ch, 0, mvneta_tick, sc);
821a8d7fc4aSZbigniew Bodek 
822b831f9ceSHubert Mazur 	sysctl_mvneta_init(sc);
823a8d7fc4aSZbigniew Bodek 
824a8d7fc4aSZbigniew Bodek 	return (0);
825a8d7fc4aSZbigniew Bodek }
826a8d7fc4aSZbigniew Bodek 
827a8d7fc4aSZbigniew Bodek STATIC int
828a8d7fc4aSZbigniew Bodek mvneta_detach(device_t dev)
829a8d7fc4aSZbigniew Bodek {
830a8d7fc4aSZbigniew Bodek 	struct mvneta_softc *sc;
831a8d7fc4aSZbigniew Bodek 	int q;
832a8d7fc4aSZbigniew Bodek 
833a8d7fc4aSZbigniew Bodek 	sc = device_get_softc(dev);
834a8d7fc4aSZbigniew Bodek 
835b831f9ceSHubert Mazur 	if (device_is_attached(dev)) {
836a8d7fc4aSZbigniew Bodek 		mvneta_stop(sc);
837b831f9ceSHubert Mazur 		callout_drain(&sc->tick_ch);
838b831f9ceSHubert Mazur 		ether_ifdetach(sc->ifp);
839b831f9ceSHubert Mazur 	}
840a8d7fc4aSZbigniew Bodek 
841a8d7fc4aSZbigniew Bodek 	for (q = 0; q < MVNETA_RX_QNUM_MAX; q++)
842a8d7fc4aSZbigniew Bodek 		mvneta_ring_dealloc_rx_queue(sc, q);
843a8d7fc4aSZbigniew Bodek 	for (q = 0; q < MVNETA_TX_QNUM_MAX; q++)
844a8d7fc4aSZbigniew Bodek 		mvneta_ring_dealloc_tx_queue(sc, q);
845a8d7fc4aSZbigniew Bodek 
846*3ddaf820SJohn Baldwin 	bus_generic_detach(dev);
847b831f9ceSHubert Mazur 
848b831f9ceSHubert Mazur 	if (sc->ih_cookie[0] != NULL)
849b831f9ceSHubert Mazur 		bus_teardown_intr(dev, sc->res[1], sc->ih_cookie[0]);
850b831f9ceSHubert Mazur 
851a8d7fc4aSZbigniew Bodek 	if (sc->tx_dtag != NULL)
852a8d7fc4aSZbigniew Bodek 		bus_dma_tag_destroy(sc->tx_dtag);
853a8d7fc4aSZbigniew Bodek 	if (sc->rx_dtag != NULL)
854a8d7fc4aSZbigniew Bodek 		bus_dma_tag_destroy(sc->rx_dtag);
855a8d7fc4aSZbigniew Bodek 	if (sc->txmbuf_dtag != NULL)
856a8d7fc4aSZbigniew Bodek 		bus_dma_tag_destroy(sc->txmbuf_dtag);
8573599e81cSMarcin Wojtas 	if (sc->rxbuf_dtag != NULL)
8583599e81cSMarcin Wojtas 		bus_dma_tag_destroy(sc->rxbuf_dtag);
859a8d7fc4aSZbigniew Bodek 
860a8d7fc4aSZbigniew Bodek 	bus_release_resources(dev, res_spec, sc->res);
861b831f9ceSHubert Mazur 
862b831f9ceSHubert Mazur 	if (sc->ifp)
863b831f9ceSHubert Mazur 		if_free(sc->ifp);
864b831f9ceSHubert Mazur 
865b831f9ceSHubert Mazur 	if (mtx_initialized(&sc->mtx))
866b831f9ceSHubert Mazur 		mtx_destroy(&sc->mtx);
867b831f9ceSHubert Mazur 
868a8d7fc4aSZbigniew Bodek 	return (0);
869a8d7fc4aSZbigniew Bodek }
870a8d7fc4aSZbigniew Bodek 
871a8d7fc4aSZbigniew Bodek /*
872a8d7fc4aSZbigniew Bodek  * MII
873a8d7fc4aSZbigniew Bodek  */
874a8d7fc4aSZbigniew Bodek STATIC int
875a8d7fc4aSZbigniew Bodek mvneta_miibus_readreg(device_t dev, int phy, int reg)
876a8d7fc4aSZbigniew Bodek {
877a8d7fc4aSZbigniew Bodek 	struct mvneta_softc *sc;
878992fa62bSJustin Hibbits 	if_t ifp;
879a8d7fc4aSZbigniew Bodek 	uint32_t smi, val;
880a8d7fc4aSZbigniew Bodek 	int i;
881a8d7fc4aSZbigniew Bodek 
882a8d7fc4aSZbigniew Bodek 	sc = device_get_softc(dev);
883a8d7fc4aSZbigniew Bodek 	ifp = sc->ifp;
884a8d7fc4aSZbigniew Bodek 
885a8d7fc4aSZbigniew Bodek 	mtx_lock(&mii_mutex);
886a8d7fc4aSZbigniew Bodek 
887a8d7fc4aSZbigniew Bodek 	for (i = 0; i < MVNETA_PHY_TIMEOUT; i++) {
888a8d7fc4aSZbigniew Bodek 		if ((MVNETA_READ(sc, MVNETA_SMI) & MVNETA_SMI_BUSY) == 0)
889a8d7fc4aSZbigniew Bodek 			break;
890a8d7fc4aSZbigniew Bodek 		DELAY(1);
891a8d7fc4aSZbigniew Bodek 	}
892a8d7fc4aSZbigniew Bodek 	if (i == MVNETA_PHY_TIMEOUT) {
893a8d7fc4aSZbigniew Bodek 		if_printf(ifp, "SMI busy timeout\n");
894a8d7fc4aSZbigniew Bodek 		mtx_unlock(&mii_mutex);
895a8d7fc4aSZbigniew Bodek 		return (-1);
896a8d7fc4aSZbigniew Bodek 	}
897a8d7fc4aSZbigniew Bodek 
898a8d7fc4aSZbigniew Bodek 	smi = MVNETA_SMI_PHYAD(phy) |
899a8d7fc4aSZbigniew Bodek 	    MVNETA_SMI_REGAD(reg) | MVNETA_SMI_OPCODE_READ;
900a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_SMI, smi);
901a8d7fc4aSZbigniew Bodek 
902a8d7fc4aSZbigniew Bodek 	for (i = 0; i < MVNETA_PHY_TIMEOUT; i++) {
903a8d7fc4aSZbigniew Bodek 		if ((MVNETA_READ(sc, MVNETA_SMI) & MVNETA_SMI_BUSY) == 0)
904a8d7fc4aSZbigniew Bodek 			break;
905a8d7fc4aSZbigniew Bodek 		DELAY(1);
906a8d7fc4aSZbigniew Bodek 	}
907a8d7fc4aSZbigniew Bodek 
908a8d7fc4aSZbigniew Bodek 	if (i == MVNETA_PHY_TIMEOUT) {
909a8d7fc4aSZbigniew Bodek 		if_printf(ifp, "SMI busy timeout\n");
910a8d7fc4aSZbigniew Bodek 		mtx_unlock(&mii_mutex);
911a8d7fc4aSZbigniew Bodek 		return (-1);
912a8d7fc4aSZbigniew Bodek 	}
913a8d7fc4aSZbigniew Bodek 	for (i = 0; i < MVNETA_PHY_TIMEOUT; i++) {
914a8d7fc4aSZbigniew Bodek 		smi = MVNETA_READ(sc, MVNETA_SMI);
915a8d7fc4aSZbigniew Bodek 		if (smi & MVNETA_SMI_READVALID)
916a8d7fc4aSZbigniew Bodek 			break;
917a8d7fc4aSZbigniew Bodek 		DELAY(1);
918a8d7fc4aSZbigniew Bodek 	}
919a8d7fc4aSZbigniew Bodek 
920a8d7fc4aSZbigniew Bodek 	if (i == MVNETA_PHY_TIMEOUT) {
921a8d7fc4aSZbigniew Bodek 		if_printf(ifp, "SMI busy timeout\n");
922a8d7fc4aSZbigniew Bodek 		mtx_unlock(&mii_mutex);
923a8d7fc4aSZbigniew Bodek 		return (-1);
924a8d7fc4aSZbigniew Bodek 	}
925a8d7fc4aSZbigniew Bodek 
926a8d7fc4aSZbigniew Bodek 	mtx_unlock(&mii_mutex);
927a8d7fc4aSZbigniew Bodek 
928a8d7fc4aSZbigniew Bodek #ifdef MVNETA_KTR
929992fa62bSJustin Hibbits 	CTR3(KTR_SPARE2, "%s i=%d, timeout=%d\n", if_getname(ifp), i,
930a8d7fc4aSZbigniew Bodek 	    MVNETA_PHY_TIMEOUT);
931a8d7fc4aSZbigniew Bodek #endif
932a8d7fc4aSZbigniew Bodek 
933a8d7fc4aSZbigniew Bodek 	val = smi & MVNETA_SMI_DATA_MASK;
934a8d7fc4aSZbigniew Bodek 
935a8d7fc4aSZbigniew Bodek #ifdef MVNETA_KTR
936992fa62bSJustin Hibbits 	CTR4(KTR_SPARE2, "%s phy=%d, reg=%#x, val=%#x\n", if_getname(ifp), phy,
937a8d7fc4aSZbigniew Bodek 	    reg, val);
938a8d7fc4aSZbigniew Bodek #endif
939a8d7fc4aSZbigniew Bodek 	return (val);
940a8d7fc4aSZbigniew Bodek }
941a8d7fc4aSZbigniew Bodek 
942a8d7fc4aSZbigniew Bodek STATIC int
943a8d7fc4aSZbigniew Bodek mvneta_miibus_writereg(device_t dev, int phy, int reg, int val)
944a8d7fc4aSZbigniew Bodek {
945a8d7fc4aSZbigniew Bodek 	struct mvneta_softc *sc;
946992fa62bSJustin Hibbits 	if_t ifp;
947a8d7fc4aSZbigniew Bodek 	uint32_t smi;
948a8d7fc4aSZbigniew Bodek 	int i;
949a8d7fc4aSZbigniew Bodek 
950a8d7fc4aSZbigniew Bodek 	sc = device_get_softc(dev);
951a8d7fc4aSZbigniew Bodek 	ifp = sc->ifp;
952a8d7fc4aSZbigniew Bodek #ifdef MVNETA_KTR
953992fa62bSJustin Hibbits 	CTR4(KTR_SPARE2, "%s phy=%d, reg=%#x, val=%#x\n", if_name(ifp),
954a8d7fc4aSZbigniew Bodek 	    phy, reg, val);
955a8d7fc4aSZbigniew Bodek #endif
956a8d7fc4aSZbigniew Bodek 
957a8d7fc4aSZbigniew Bodek 	mtx_lock(&mii_mutex);
958a8d7fc4aSZbigniew Bodek 
959a8d7fc4aSZbigniew Bodek 	for (i = 0; i < MVNETA_PHY_TIMEOUT; i++) {
960a8d7fc4aSZbigniew Bodek 		if ((MVNETA_READ(sc, MVNETA_SMI) & MVNETA_SMI_BUSY) == 0)
961a8d7fc4aSZbigniew Bodek 			break;
962a8d7fc4aSZbigniew Bodek 		DELAY(1);
963a8d7fc4aSZbigniew Bodek 	}
964a8d7fc4aSZbigniew Bodek 	if (i == MVNETA_PHY_TIMEOUT) {
965a8d7fc4aSZbigniew Bodek 		if_printf(ifp, "SMI busy timeout\n");
966a8d7fc4aSZbigniew Bodek 		mtx_unlock(&mii_mutex);
967a8d7fc4aSZbigniew Bodek 		return (0);
968a8d7fc4aSZbigniew Bodek 	}
969a8d7fc4aSZbigniew Bodek 
970a8d7fc4aSZbigniew Bodek 	smi = MVNETA_SMI_PHYAD(phy) | MVNETA_SMI_REGAD(reg) |
971a8d7fc4aSZbigniew Bodek 	    MVNETA_SMI_OPCODE_WRITE | (val & MVNETA_SMI_DATA_MASK);
972a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_SMI, smi);
973a8d7fc4aSZbigniew Bodek 
974a8d7fc4aSZbigniew Bodek 	for (i = 0; i < MVNETA_PHY_TIMEOUT; i++) {
975a8d7fc4aSZbigniew Bodek 		if ((MVNETA_READ(sc, MVNETA_SMI) & MVNETA_SMI_BUSY) == 0)
976a8d7fc4aSZbigniew Bodek 			break;
977a8d7fc4aSZbigniew Bodek 		DELAY(1);
978a8d7fc4aSZbigniew Bodek 	}
979a8d7fc4aSZbigniew Bodek 
980a8d7fc4aSZbigniew Bodek 	mtx_unlock(&mii_mutex);
981a8d7fc4aSZbigniew Bodek 
982a8d7fc4aSZbigniew Bodek 	if (i == MVNETA_PHY_TIMEOUT)
983a8d7fc4aSZbigniew Bodek 		if_printf(ifp, "phy write timed out\n");
984a8d7fc4aSZbigniew Bodek 
985a8d7fc4aSZbigniew Bodek 	return (0);
986a8d7fc4aSZbigniew Bodek }
987a8d7fc4aSZbigniew Bodek 
988a8d7fc4aSZbigniew Bodek STATIC void
989a8d7fc4aSZbigniew Bodek mvneta_portup(struct mvneta_softc *sc)
990a8d7fc4aSZbigniew Bodek {
991a8d7fc4aSZbigniew Bodek 	int q;
992a8d7fc4aSZbigniew Bodek 
993a8d7fc4aSZbigniew Bodek 	for (q = 0; q < MVNETA_RX_QNUM_MAX; q++) {
994a8d7fc4aSZbigniew Bodek 		mvneta_rx_lockq(sc, q);
995a8d7fc4aSZbigniew Bodek 		mvneta_rx_queue_enable(sc->ifp, q);
996a8d7fc4aSZbigniew Bodek 		mvneta_rx_unlockq(sc, q);
997a8d7fc4aSZbigniew Bodek 	}
998a8d7fc4aSZbigniew Bodek 
999a8d7fc4aSZbigniew Bodek 	for (q = 0; q < MVNETA_TX_QNUM_MAX; q++) {
1000a8d7fc4aSZbigniew Bodek 		mvneta_tx_lockq(sc, q);
1001a8d7fc4aSZbigniew Bodek 		mvneta_tx_queue_enable(sc->ifp, q);
1002a8d7fc4aSZbigniew Bodek 		mvneta_tx_unlockq(sc, q);
1003a8d7fc4aSZbigniew Bodek 	}
1004a8d7fc4aSZbigniew Bodek 
1005a8d7fc4aSZbigniew Bodek }
1006a8d7fc4aSZbigniew Bodek 
1007a8d7fc4aSZbigniew Bodek STATIC void
1008a8d7fc4aSZbigniew Bodek mvneta_portdown(struct mvneta_softc *sc)
1009a8d7fc4aSZbigniew Bodek {
1010a8d7fc4aSZbigniew Bodek 	struct mvneta_rx_ring *rx;
1011a8d7fc4aSZbigniew Bodek 	struct mvneta_tx_ring *tx;
1012a8d7fc4aSZbigniew Bodek 	int q, cnt;
1013a8d7fc4aSZbigniew Bodek 	uint32_t reg;
1014a8d7fc4aSZbigniew Bodek 
1015a8d7fc4aSZbigniew Bodek 	for (q = 0; q < MVNETA_RX_QNUM_MAX; q++) {
1016a8d7fc4aSZbigniew Bodek 		rx = MVNETA_RX_RING(sc, q);
1017a8d7fc4aSZbigniew Bodek 		mvneta_rx_lockq(sc, q);
1018a8d7fc4aSZbigniew Bodek 		rx->queue_status = MVNETA_QUEUE_DISABLED;
1019a8d7fc4aSZbigniew Bodek 		mvneta_rx_unlockq(sc, q);
1020a8d7fc4aSZbigniew Bodek 	}
1021a8d7fc4aSZbigniew Bodek 
1022a8d7fc4aSZbigniew Bodek 	for (q = 0; q < MVNETA_TX_QNUM_MAX; q++) {
1023a8d7fc4aSZbigniew Bodek 		tx = MVNETA_TX_RING(sc, q);
1024a8d7fc4aSZbigniew Bodek 		mvneta_tx_lockq(sc, q);
1025a8d7fc4aSZbigniew Bodek 		tx->queue_status = MVNETA_QUEUE_DISABLED;
1026a8d7fc4aSZbigniew Bodek 		mvneta_tx_unlockq(sc, q);
1027a8d7fc4aSZbigniew Bodek 	}
1028a8d7fc4aSZbigniew Bodek 
1029a8d7fc4aSZbigniew Bodek 	/* Wait for all Rx activity to terminate. */
1030a8d7fc4aSZbigniew Bodek 	reg = MVNETA_READ(sc, MVNETA_RQC) & MVNETA_RQC_EN_MASK;
1031a8d7fc4aSZbigniew Bodek 	reg = MVNETA_RQC_DIS(reg);
1032a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_RQC, reg);
1033a8d7fc4aSZbigniew Bodek 	cnt = 0;
1034a8d7fc4aSZbigniew Bodek 	do {
1035a8d7fc4aSZbigniew Bodek 		if (cnt >= RX_DISABLE_TIMEOUT) {
1036a8d7fc4aSZbigniew Bodek 			if_printf(sc->ifp,
1037a8d7fc4aSZbigniew Bodek 			    "timeout for RX stopped. rqc 0x%x\n", reg);
1038a8d7fc4aSZbigniew Bodek 			break;
1039a8d7fc4aSZbigniew Bodek 		}
1040a8d7fc4aSZbigniew Bodek 		cnt++;
1041a8d7fc4aSZbigniew Bodek 		reg = MVNETA_READ(sc, MVNETA_RQC);
1042a8d7fc4aSZbigniew Bodek 	} while ((reg & MVNETA_RQC_EN_MASK) != 0);
1043a8d7fc4aSZbigniew Bodek 
1044a8d7fc4aSZbigniew Bodek 	/* Wait for all Tx activity to terminate. */
1045a8d7fc4aSZbigniew Bodek 	reg  = MVNETA_READ(sc, MVNETA_PIE);
1046a8d7fc4aSZbigniew Bodek 	reg &= ~MVNETA_PIE_TXPKTINTRPTENB_MASK;
1047a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PIE, reg);
1048a8d7fc4aSZbigniew Bodek 
1049a8d7fc4aSZbigniew Bodek 	reg  = MVNETA_READ(sc, MVNETA_PRXTXTIM);
1050a8d7fc4aSZbigniew Bodek 	reg &= ~MVNETA_PRXTXTI_TBTCQ_MASK;
1051a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PRXTXTIM, reg);
1052a8d7fc4aSZbigniew Bodek 
1053a8d7fc4aSZbigniew Bodek 	reg = MVNETA_READ(sc, MVNETA_TQC) & MVNETA_TQC_EN_MASK;
1054a8d7fc4aSZbigniew Bodek 	reg = MVNETA_TQC_DIS(reg);
1055a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_TQC, reg);
1056a8d7fc4aSZbigniew Bodek 	cnt = 0;
1057a8d7fc4aSZbigniew Bodek 	do {
1058a8d7fc4aSZbigniew Bodek 		if (cnt >= TX_DISABLE_TIMEOUT) {
1059a8d7fc4aSZbigniew Bodek 			if_printf(sc->ifp,
1060a8d7fc4aSZbigniew Bodek 			    "timeout for TX stopped. tqc 0x%x\n", reg);
1061a8d7fc4aSZbigniew Bodek 			break;
1062a8d7fc4aSZbigniew Bodek 		}
1063a8d7fc4aSZbigniew Bodek 		cnt++;
1064a8d7fc4aSZbigniew Bodek 		reg = MVNETA_READ(sc, MVNETA_TQC);
1065a8d7fc4aSZbigniew Bodek 	} while ((reg & MVNETA_TQC_EN_MASK) != 0);
1066a8d7fc4aSZbigniew Bodek 
1067a8d7fc4aSZbigniew Bodek 	/* Wait for all Tx FIFO is empty */
1068a8d7fc4aSZbigniew Bodek 	cnt = 0;
1069a8d7fc4aSZbigniew Bodek 	do {
1070a8d7fc4aSZbigniew Bodek 		if (cnt >= TX_FIFO_EMPTY_TIMEOUT) {
1071a8d7fc4aSZbigniew Bodek 			if_printf(sc->ifp,
1072a8d7fc4aSZbigniew Bodek 			    "timeout for TX FIFO drained. ps0 0x%x\n", reg);
1073a8d7fc4aSZbigniew Bodek 			break;
1074a8d7fc4aSZbigniew Bodek 		}
1075a8d7fc4aSZbigniew Bodek 		cnt++;
1076a8d7fc4aSZbigniew Bodek 		reg = MVNETA_READ(sc, MVNETA_PS0);
1077a8d7fc4aSZbigniew Bodek 	} while (((reg & MVNETA_PS0_TXFIFOEMP) == 0) &&
1078a8d7fc4aSZbigniew Bodek 	    ((reg & MVNETA_PS0_TXINPROG) != 0));
1079a8d7fc4aSZbigniew Bodek }
1080a8d7fc4aSZbigniew Bodek 
1081a8d7fc4aSZbigniew Bodek /*
1082a8d7fc4aSZbigniew Bodek  * Device Register Initialization
1083a8d7fc4aSZbigniew Bodek  *  reset device registers to device driver default value.
1084a8d7fc4aSZbigniew Bodek  *  the device is not enabled here.
1085a8d7fc4aSZbigniew Bodek  */
1086a8d7fc4aSZbigniew Bodek STATIC int
1087992fa62bSJustin Hibbits mvneta_initreg(if_t ifp)
1088a8d7fc4aSZbigniew Bodek {
1089a8d7fc4aSZbigniew Bodek 	struct mvneta_softc *sc;
1090caf552a6SMark Johnston 	int q;
1091a8d7fc4aSZbigniew Bodek 	uint32_t reg;
1092a8d7fc4aSZbigniew Bodek 
1093992fa62bSJustin Hibbits 	sc = if_getsoftc(ifp);
1094a8d7fc4aSZbigniew Bodek #ifdef MVNETA_KTR
1095992fa62bSJustin Hibbits 	CTR1(KTR_SPARE2, "%s initializing device register", if_name(ifp));
1096a8d7fc4aSZbigniew Bodek #endif
1097a8d7fc4aSZbigniew Bodek 
1098a8d7fc4aSZbigniew Bodek 	/* Disable Legacy WRR, Disable EJP, Release from reset. */
1099a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_TQC_1, 0);
1100a8d7fc4aSZbigniew Bodek 	/* Enable mbus retry. */
1101a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_MBUS_CONF, MVNETA_MBUS_RETRY_EN);
1102a8d7fc4aSZbigniew Bodek 
1103a8d7fc4aSZbigniew Bodek 	/* Init TX/RX Queue Registers */
1104a8d7fc4aSZbigniew Bodek 	for (q = 0; q < MVNETA_RX_QNUM_MAX; q++) {
1105a8d7fc4aSZbigniew Bodek 		mvneta_rx_lockq(sc, q);
1106a8d7fc4aSZbigniew Bodek 		if (mvneta_rx_queue_init(ifp, q) != 0) {
1107a8d7fc4aSZbigniew Bodek 			device_printf(sc->dev,
1108a8d7fc4aSZbigniew Bodek 			    "initialization failed: cannot initialize queue\n");
1109a8d7fc4aSZbigniew Bodek 			mvneta_rx_unlockq(sc, q);
1110a8d7fc4aSZbigniew Bodek 			return (ENOBUFS);
1111a8d7fc4aSZbigniew Bodek 		}
1112a8d7fc4aSZbigniew Bodek 		mvneta_rx_unlockq(sc, q);
1113a8d7fc4aSZbigniew Bodek 	}
1114a8d7fc4aSZbigniew Bodek 	for (q = 0; q < MVNETA_TX_QNUM_MAX; q++) {
1115a8d7fc4aSZbigniew Bodek 		mvneta_tx_lockq(sc, q);
1116a8d7fc4aSZbigniew Bodek 		if (mvneta_tx_queue_init(ifp, q) != 0) {
1117a8d7fc4aSZbigniew Bodek 			device_printf(sc->dev,
1118a8d7fc4aSZbigniew Bodek 			    "initialization failed: cannot initialize queue\n");
1119a8d7fc4aSZbigniew Bodek 			mvneta_tx_unlockq(sc, q);
1120a8d7fc4aSZbigniew Bodek 			return (ENOBUFS);
1121a8d7fc4aSZbigniew Bodek 		}
1122a8d7fc4aSZbigniew Bodek 		mvneta_tx_unlockq(sc, q);
1123a8d7fc4aSZbigniew Bodek 	}
1124a8d7fc4aSZbigniew Bodek 
1125a8d7fc4aSZbigniew Bodek 	/*
1126a8d7fc4aSZbigniew Bodek 	 * Ethernet Unit Control - disable automatic PHY management by HW.
1127a8d7fc4aSZbigniew Bodek 	 * In case the port uses SMI-controlled PHY, poll its status with
1128a8d7fc4aSZbigniew Bodek 	 * mii_tick() and update MAC settings accordingly.
1129a8d7fc4aSZbigniew Bodek 	 */
1130a8d7fc4aSZbigniew Bodek 	reg = MVNETA_READ(sc, MVNETA_EUC);
1131a8d7fc4aSZbigniew Bodek 	reg &= ~MVNETA_EUC_POLLING;
1132a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_EUC, reg);
1133a8d7fc4aSZbigniew Bodek 
1134a8d7fc4aSZbigniew Bodek 	/* EEE: Low Power Idle */
1135a8d7fc4aSZbigniew Bodek 	reg  = MVNETA_LPIC0_LILIMIT(MVNETA_LPI_LI);
1136a8d7fc4aSZbigniew Bodek 	reg |= MVNETA_LPIC0_TSLIMIT(MVNETA_LPI_TS);
1137a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_LPIC0, reg);
1138a8d7fc4aSZbigniew Bodek 
1139a8d7fc4aSZbigniew Bodek 	reg  = MVNETA_LPIC1_TWLIMIT(MVNETA_LPI_TW);
1140a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_LPIC1, reg);
1141a8d7fc4aSZbigniew Bodek 
1142a8d7fc4aSZbigniew Bodek 	reg = MVNETA_LPIC2_MUSTSET;
1143a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_LPIC2, reg);
1144a8d7fc4aSZbigniew Bodek 
1145a8d7fc4aSZbigniew Bodek 	/* Port MAC Control set 0 */
1146a8d7fc4aSZbigniew Bodek 	reg  = MVNETA_PMACC0_MUSTSET;	/* must write 0x1 */
1147a8d7fc4aSZbigniew Bodek 	reg &= ~MVNETA_PMACC0_PORTEN;	/* port is still disabled */
1148992fa62bSJustin Hibbits 	reg |= MVNETA_PMACC0_FRAMESIZELIMIT(if_getmtu(ifp) + MVNETA_ETHER_SIZE);
1149a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PMACC0, reg);
1150a8d7fc4aSZbigniew Bodek 
1151a8d7fc4aSZbigniew Bodek 	/* Port MAC Control set 2 */
1152a8d7fc4aSZbigniew Bodek 	reg = MVNETA_READ(sc, MVNETA_PMACC2);
1153a8d7fc4aSZbigniew Bodek 	switch (sc->phy_mode) {
1154a8d7fc4aSZbigniew Bodek 	case MVNETA_PHY_QSGMII:
1155a8d7fc4aSZbigniew Bodek 		reg |= (MVNETA_PMACC2_PCSEN | MVNETA_PMACC2_RGMIIEN);
1156a8d7fc4aSZbigniew Bodek 		MVNETA_WRITE(sc, MVNETA_PSERDESCFG, MVNETA_PSERDESCFG_QSGMII);
1157a8d7fc4aSZbigniew Bodek 		break;
1158a8d7fc4aSZbigniew Bodek 	case MVNETA_PHY_SGMII:
1159a8d7fc4aSZbigniew Bodek 		reg |= (MVNETA_PMACC2_PCSEN | MVNETA_PMACC2_RGMIIEN);
1160a8d7fc4aSZbigniew Bodek 		MVNETA_WRITE(sc, MVNETA_PSERDESCFG, MVNETA_PSERDESCFG_SGMII);
1161a8d7fc4aSZbigniew Bodek 		break;
1162a8d7fc4aSZbigniew Bodek 	case MVNETA_PHY_RGMII:
1163a8d7fc4aSZbigniew Bodek 	case MVNETA_PHY_RGMII_ID:
1164a8d7fc4aSZbigniew Bodek 		reg |= MVNETA_PMACC2_RGMIIEN;
1165a8d7fc4aSZbigniew Bodek 		break;
1166a8d7fc4aSZbigniew Bodek 	}
1167a8d7fc4aSZbigniew Bodek 	reg |= MVNETA_PMACC2_MUSTSET;
1168a8d7fc4aSZbigniew Bodek 	reg &= ~MVNETA_PMACC2_PORTMACRESET;
1169a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PMACC2, reg);
1170a8d7fc4aSZbigniew Bodek 
1171a8d7fc4aSZbigniew Bodek 	/* Port Configuration Extended: enable Tx CRC generation */
1172a8d7fc4aSZbigniew Bodek 	reg = MVNETA_READ(sc, MVNETA_PXCX);
1173a8d7fc4aSZbigniew Bodek 	reg &= ~MVNETA_PXCX_TXCRCDIS;
1174a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PXCX, reg);
1175a8d7fc4aSZbigniew Bodek 
1176a8d7fc4aSZbigniew Bodek 	/* clear MIB counter registers(clear by read) */
1177109260d2SMark Johnston 	mvneta_sc_lock(sc);
1178caf552a6SMark Johnston 	mvneta_clear_mib(sc);
1179109260d2SMark Johnston 	mvneta_sc_unlock(sc);
1180a8d7fc4aSZbigniew Bodek 
1181a8d7fc4aSZbigniew Bodek 	/* Set SDC register except IPGINT bits */
1182a8d7fc4aSZbigniew Bodek 	reg  = MVNETA_SDC_RXBSZ_16_64BITWORDS;
1183a8d7fc4aSZbigniew Bodek 	reg |= MVNETA_SDC_TXBSZ_16_64BITWORDS;
1184a8d7fc4aSZbigniew Bodek 	reg |= MVNETA_SDC_BLMR;
1185a8d7fc4aSZbigniew Bodek 	reg |= MVNETA_SDC_BLMT;
1186a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_SDC, reg);
1187a8d7fc4aSZbigniew Bodek 
1188a8d7fc4aSZbigniew Bodek 	return (0);
1189a8d7fc4aSZbigniew Bodek }
1190a8d7fc4aSZbigniew Bodek 
1191a8d7fc4aSZbigniew Bodek STATIC void
1192a8d7fc4aSZbigniew Bodek mvneta_dmamap_cb(void *arg, bus_dma_segment_t * segs, int nseg, int error)
1193a8d7fc4aSZbigniew Bodek {
1194a8d7fc4aSZbigniew Bodek 
1195a8d7fc4aSZbigniew Bodek 	if (error != 0)
1196a8d7fc4aSZbigniew Bodek 		return;
1197a8d7fc4aSZbigniew Bodek 	*(bus_addr_t *)arg = segs->ds_addr;
1198a8d7fc4aSZbigniew Bodek }
1199a8d7fc4aSZbigniew Bodek 
1200a8d7fc4aSZbigniew Bodek STATIC int
1201a8d7fc4aSZbigniew Bodek mvneta_ring_alloc_rx_queue(struct mvneta_softc *sc, int q)
1202a8d7fc4aSZbigniew Bodek {
1203a8d7fc4aSZbigniew Bodek 	struct mvneta_rx_ring *rx;
1204a8d7fc4aSZbigniew Bodek 	struct mvneta_buf *rxbuf;
1205a8d7fc4aSZbigniew Bodek 	bus_dmamap_t dmap;
1206a8d7fc4aSZbigniew Bodek 	int i, error;
1207a8d7fc4aSZbigniew Bodek 
1208a8d7fc4aSZbigniew Bodek 	if (q >= MVNETA_RX_QNUM_MAX)
1209a8d7fc4aSZbigniew Bodek 		return (EINVAL);
1210a8d7fc4aSZbigniew Bodek 
1211a8d7fc4aSZbigniew Bodek 	rx = MVNETA_RX_RING(sc, q);
1212a8d7fc4aSZbigniew Bodek 	mtx_init(&rx->ring_mtx, "mvneta_rx", NULL, MTX_DEF);
1213a8d7fc4aSZbigniew Bodek 	/* Allocate DMA memory for Rx descriptors */
1214a8d7fc4aSZbigniew Bodek 	error = bus_dmamem_alloc(sc->rx_dtag,
1215a8d7fc4aSZbigniew Bodek 	    (void**)&(rx->desc),
1216a8d7fc4aSZbigniew Bodek 	    BUS_DMA_NOWAIT | BUS_DMA_ZERO,
1217a8d7fc4aSZbigniew Bodek 	    &rx->desc_map);
1218a8d7fc4aSZbigniew Bodek 	if (error != 0 || rx->desc == NULL)
1219a8d7fc4aSZbigniew Bodek 		goto fail;
1220a8d7fc4aSZbigniew Bodek 	error = bus_dmamap_load(sc->rx_dtag, rx->desc_map,
1221a8d7fc4aSZbigniew Bodek 	    rx->desc,
1222a8d7fc4aSZbigniew Bodek 	    sizeof(struct mvneta_rx_desc) * MVNETA_RX_RING_CNT,
1223a8d7fc4aSZbigniew Bodek 	    mvneta_dmamap_cb, &rx->desc_pa, BUS_DMA_NOWAIT);
1224a8d7fc4aSZbigniew Bodek 	if (error != 0)
1225a8d7fc4aSZbigniew Bodek 		goto fail;
1226a8d7fc4aSZbigniew Bodek 
1227a8d7fc4aSZbigniew Bodek 	for (i = 0; i < MVNETA_RX_RING_CNT; i++) {
1228a8d7fc4aSZbigniew Bodek 		error = bus_dmamap_create(sc->rxbuf_dtag, 0, &dmap);
1229a8d7fc4aSZbigniew Bodek 		if (error != 0) {
1230a8d7fc4aSZbigniew Bodek 			device_printf(sc->dev,
1231a8d7fc4aSZbigniew Bodek 			    "Failed to create DMA map for Rx buffer num: %d\n", i);
1232a8d7fc4aSZbigniew Bodek 			goto fail;
1233a8d7fc4aSZbigniew Bodek 		}
1234a8d7fc4aSZbigniew Bodek 		rxbuf = &rx->rxbuf[i];
1235a8d7fc4aSZbigniew Bodek 		rxbuf->dmap = dmap;
1236a8d7fc4aSZbigniew Bodek 		rxbuf->m = NULL;
1237a8d7fc4aSZbigniew Bodek 	}
1238a8d7fc4aSZbigniew Bodek 
1239a8d7fc4aSZbigniew Bodek 	return (0);
1240a8d7fc4aSZbigniew Bodek fail:
1241b831f9ceSHubert Mazur 	mvneta_rx_lockq(sc, q);
1242b831f9ceSHubert Mazur 	mvneta_ring_flush_rx_queue(sc, q);
1243b831f9ceSHubert Mazur 	mvneta_rx_unlockq(sc, q);
1244a8d7fc4aSZbigniew Bodek 	mvneta_ring_dealloc_rx_queue(sc, q);
1245a8d7fc4aSZbigniew Bodek 	device_printf(sc->dev, "DMA Ring buffer allocation failure.\n");
1246a8d7fc4aSZbigniew Bodek 	return (error);
1247a8d7fc4aSZbigniew Bodek }
1248a8d7fc4aSZbigniew Bodek 
1249a8d7fc4aSZbigniew Bodek STATIC int
1250a8d7fc4aSZbigniew Bodek mvneta_ring_alloc_tx_queue(struct mvneta_softc *sc, int q)
1251a8d7fc4aSZbigniew Bodek {
1252a8d7fc4aSZbigniew Bodek 	struct mvneta_tx_ring *tx;
1253a8d7fc4aSZbigniew Bodek 	int error;
1254a8d7fc4aSZbigniew Bodek 
1255a8d7fc4aSZbigniew Bodek 	if (q >= MVNETA_TX_QNUM_MAX)
1256a8d7fc4aSZbigniew Bodek 		return (EINVAL);
1257a8d7fc4aSZbigniew Bodek 	tx = MVNETA_TX_RING(sc, q);
1258a8d7fc4aSZbigniew Bodek 	mtx_init(&tx->ring_mtx, "mvneta_tx", NULL, MTX_DEF);
1259a8d7fc4aSZbigniew Bodek 	error = bus_dmamem_alloc(sc->tx_dtag,
1260a8d7fc4aSZbigniew Bodek 	    (void**)&(tx->desc),
1261a8d7fc4aSZbigniew Bodek 	    BUS_DMA_NOWAIT | BUS_DMA_ZERO,
1262a8d7fc4aSZbigniew Bodek 	    &tx->desc_map);
1263a8d7fc4aSZbigniew Bodek 	if (error != 0 || tx->desc == NULL)
1264a8d7fc4aSZbigniew Bodek 		goto fail;
1265a8d7fc4aSZbigniew Bodek 	error = bus_dmamap_load(sc->tx_dtag, tx->desc_map,
1266a8d7fc4aSZbigniew Bodek 	    tx->desc,
1267a8d7fc4aSZbigniew Bodek 	    sizeof(struct mvneta_tx_desc) * MVNETA_TX_RING_CNT,
1268a8d7fc4aSZbigniew Bodek 	    mvneta_dmamap_cb, &tx->desc_pa, BUS_DMA_NOWAIT);
1269a8d7fc4aSZbigniew Bodek 	if (error != 0)
1270a8d7fc4aSZbigniew Bodek 		goto fail;
1271a8d7fc4aSZbigniew Bodek 
1272a8d7fc4aSZbigniew Bodek #ifdef MVNETA_MULTIQUEUE
1273a8d7fc4aSZbigniew Bodek 	tx->br = buf_ring_alloc(MVNETA_BUFRING_SIZE, M_DEVBUF, M_NOWAIT,
1274a8d7fc4aSZbigniew Bodek 	    &tx->ring_mtx);
1275a8d7fc4aSZbigniew Bodek 	if (tx->br == NULL) {
1276a8d7fc4aSZbigniew Bodek 		device_printf(sc->dev,
1277a8d7fc4aSZbigniew Bodek 		    "Could not setup buffer ring for TxQ(%d)\n", q);
1278a8d7fc4aSZbigniew Bodek 		error = ENOMEM;
1279a8d7fc4aSZbigniew Bodek 		goto fail;
1280a8d7fc4aSZbigniew Bodek 	}
1281a8d7fc4aSZbigniew Bodek #endif
1282a8d7fc4aSZbigniew Bodek 
1283a8d7fc4aSZbigniew Bodek 	return (0);
1284a8d7fc4aSZbigniew Bodek fail:
1285b831f9ceSHubert Mazur 	mvneta_tx_lockq(sc, q);
1286b831f9ceSHubert Mazur 	mvneta_ring_flush_tx_queue(sc, q);
1287b831f9ceSHubert Mazur 	mvneta_tx_unlockq(sc, q);
1288a8d7fc4aSZbigniew Bodek 	mvneta_ring_dealloc_tx_queue(sc, q);
1289a8d7fc4aSZbigniew Bodek 	device_printf(sc->dev, "DMA Ring buffer allocation failure.\n");
1290a8d7fc4aSZbigniew Bodek 	return (error);
1291a8d7fc4aSZbigniew Bodek }
1292a8d7fc4aSZbigniew Bodek 
1293a8d7fc4aSZbigniew Bodek STATIC void
1294a8d7fc4aSZbigniew Bodek mvneta_ring_dealloc_tx_queue(struct mvneta_softc *sc, int q)
1295a8d7fc4aSZbigniew Bodek {
1296a8d7fc4aSZbigniew Bodek 	struct mvneta_tx_ring *tx;
1297a8d7fc4aSZbigniew Bodek 	struct mvneta_buf *txbuf;
1298a8d7fc4aSZbigniew Bodek 	void *kva;
1299a8d7fc4aSZbigniew Bodek 	int error;
1300a8d7fc4aSZbigniew Bodek 	int i;
1301a8d7fc4aSZbigniew Bodek 
1302a8d7fc4aSZbigniew Bodek 	if (q >= MVNETA_TX_QNUM_MAX)
1303a8d7fc4aSZbigniew Bodek 		return;
1304a8d7fc4aSZbigniew Bodek 	tx = MVNETA_TX_RING(sc, q);
1305a8d7fc4aSZbigniew Bodek 
1306a8d7fc4aSZbigniew Bodek 	if (tx->taskq != NULL) {
1307a8d7fc4aSZbigniew Bodek 		/* Remove task */
1308a8d7fc4aSZbigniew Bodek 		while (taskqueue_cancel(tx->taskq, &tx->task, NULL) != 0)
1309a8d7fc4aSZbigniew Bodek 			taskqueue_drain(tx->taskq, &tx->task);
1310a8d7fc4aSZbigniew Bodek 	}
1311a8d7fc4aSZbigniew Bodek #ifdef MVNETA_MULTIQUEUE
1312a8d7fc4aSZbigniew Bodek 	if (tx->br != NULL)
1313a8d7fc4aSZbigniew Bodek 		drbr_free(tx->br, M_DEVBUF);
1314a8d7fc4aSZbigniew Bodek #endif
1315a8d7fc4aSZbigniew Bodek 
1316a8d7fc4aSZbigniew Bodek 	if (sc->txmbuf_dtag != NULL) {
1317a8d7fc4aSZbigniew Bodek 		for (i = 0; i < MVNETA_TX_RING_CNT; i++) {
1318a8d7fc4aSZbigniew Bodek 			txbuf = &tx->txbuf[i];
1319a8d7fc4aSZbigniew Bodek 			if (txbuf->dmap != NULL) {
1320a8d7fc4aSZbigniew Bodek 				error = bus_dmamap_destroy(sc->txmbuf_dtag,
1321a8d7fc4aSZbigniew Bodek 				    txbuf->dmap);
1322a8d7fc4aSZbigniew Bodek 				if (error != 0) {
1323a8d7fc4aSZbigniew Bodek 					panic("%s: map busy for Tx descriptor (Q%d, %d)",
1324a8d7fc4aSZbigniew Bodek 					    __func__, q, i);
1325a8d7fc4aSZbigniew Bodek 				}
1326a8d7fc4aSZbigniew Bodek 			}
1327a8d7fc4aSZbigniew Bodek 		}
1328a8d7fc4aSZbigniew Bodek 	}
1329a8d7fc4aSZbigniew Bodek 
1330a8d7fc4aSZbigniew Bodek 	if (tx->desc_pa != 0)
1331a8d7fc4aSZbigniew Bodek 		bus_dmamap_unload(sc->tx_dtag, tx->desc_map);
1332a8d7fc4aSZbigniew Bodek 
1333a8d7fc4aSZbigniew Bodek 	kva = (void *)tx->desc;
1334a8d7fc4aSZbigniew Bodek 	if (kva != NULL)
1335a8d7fc4aSZbigniew Bodek 		bus_dmamem_free(sc->tx_dtag, tx->desc, tx->desc_map);
1336a8d7fc4aSZbigniew Bodek 
1337a8d7fc4aSZbigniew Bodek 	if (mtx_name(&tx->ring_mtx) != NULL)
1338a8d7fc4aSZbigniew Bodek 		mtx_destroy(&tx->ring_mtx);
1339a8d7fc4aSZbigniew Bodek 
1340a8d7fc4aSZbigniew Bodek 	memset(tx, 0, sizeof(*tx));
1341a8d7fc4aSZbigniew Bodek }
1342a8d7fc4aSZbigniew Bodek 
1343a8d7fc4aSZbigniew Bodek STATIC void
1344a8d7fc4aSZbigniew Bodek mvneta_ring_dealloc_rx_queue(struct mvneta_softc *sc, int q)
1345a8d7fc4aSZbigniew Bodek {
1346a8d7fc4aSZbigniew Bodek 	struct mvneta_rx_ring *rx;
1347a8d7fc4aSZbigniew Bodek 	struct lro_ctrl	*lro;
1348a8d7fc4aSZbigniew Bodek 	void *kva;
1349a8d7fc4aSZbigniew Bodek 
1350a8d7fc4aSZbigniew Bodek 	if (q >= MVNETA_RX_QNUM_MAX)
1351a8d7fc4aSZbigniew Bodek 		return;
1352a8d7fc4aSZbigniew Bodek 
1353a8d7fc4aSZbigniew Bodek 	rx = MVNETA_RX_RING(sc, q);
1354a8d7fc4aSZbigniew Bodek 
1355a8d7fc4aSZbigniew Bodek 	if (rx->desc_pa != 0)
1356a8d7fc4aSZbigniew Bodek 		bus_dmamap_unload(sc->rx_dtag, rx->desc_map);
1357a8d7fc4aSZbigniew Bodek 
1358a8d7fc4aSZbigniew Bodek 	kva = (void *)rx->desc;
1359a8d7fc4aSZbigniew Bodek 	if (kva != NULL)
1360a8d7fc4aSZbigniew Bodek 		bus_dmamem_free(sc->rx_dtag, rx->desc, rx->desc_map);
1361a8d7fc4aSZbigniew Bodek 
1362a8d7fc4aSZbigniew Bodek 	lro = &rx->lro;
1363a8d7fc4aSZbigniew Bodek 	tcp_lro_free(lro);
1364a8d7fc4aSZbigniew Bodek 
1365a8d7fc4aSZbigniew Bodek 	if (mtx_name(&rx->ring_mtx) != NULL)
1366a8d7fc4aSZbigniew Bodek 		mtx_destroy(&rx->ring_mtx);
1367a8d7fc4aSZbigniew Bodek 
1368a8d7fc4aSZbigniew Bodek 	memset(rx, 0, sizeof(*rx));
1369a8d7fc4aSZbigniew Bodek }
1370a8d7fc4aSZbigniew Bodek 
1371a8d7fc4aSZbigniew Bodek STATIC int
1372a8d7fc4aSZbigniew Bodek mvneta_ring_init_rx_queue(struct mvneta_softc *sc, int q)
1373a8d7fc4aSZbigniew Bodek {
1374a8d7fc4aSZbigniew Bodek 	struct mvneta_rx_ring *rx;
1375a8d7fc4aSZbigniew Bodek 	struct lro_ctrl	*lro;
1376a8d7fc4aSZbigniew Bodek 	int error;
1377a8d7fc4aSZbigniew Bodek 
1378a8d7fc4aSZbigniew Bodek 	if (q >= MVNETA_RX_QNUM_MAX)
1379a8d7fc4aSZbigniew Bodek 		return (0);
1380a8d7fc4aSZbigniew Bodek 
1381a8d7fc4aSZbigniew Bodek 	rx = MVNETA_RX_RING(sc, q);
1382a8d7fc4aSZbigniew Bodek 	rx->dma = rx->cpu = 0;
1383a8d7fc4aSZbigniew Bodek 	rx->queue_th_received = MVNETA_RXTH_COUNT;
13844885d6f3SHubert Mazur 	rx->queue_th_time = (sc->clk_freq / 1000) / 10; /* 0.1 [ms] */
1385a8d7fc4aSZbigniew Bodek 
1386a8d7fc4aSZbigniew Bodek 	/* Initialize LRO */
1387a8d7fc4aSZbigniew Bodek 	rx->lro_enabled = FALSE;
1388992fa62bSJustin Hibbits 	if ((if_getcapenable(sc->ifp) & IFCAP_LRO) != 0) {
1389a8d7fc4aSZbigniew Bodek 		lro = &rx->lro;
1390a8d7fc4aSZbigniew Bodek 		error = tcp_lro_init(lro);
1391a8d7fc4aSZbigniew Bodek 		if (error != 0)
1392a8d7fc4aSZbigniew Bodek 			device_printf(sc->dev, "LRO Initialization failed!\n");
1393a8d7fc4aSZbigniew Bodek 		else {
1394a8d7fc4aSZbigniew Bodek 			rx->lro_enabled = TRUE;
1395a8d7fc4aSZbigniew Bodek 			lro->ifp = sc->ifp;
1396a8d7fc4aSZbigniew Bodek 		}
1397a8d7fc4aSZbigniew Bodek 	}
1398a8d7fc4aSZbigniew Bodek 
1399a8d7fc4aSZbigniew Bodek 	return (0);
1400a8d7fc4aSZbigniew Bodek }
1401a8d7fc4aSZbigniew Bodek 
1402a8d7fc4aSZbigniew Bodek STATIC int
1403a8d7fc4aSZbigniew Bodek mvneta_ring_init_tx_queue(struct mvneta_softc *sc, int q)
1404a8d7fc4aSZbigniew Bodek {
1405a8d7fc4aSZbigniew Bodek 	struct mvneta_tx_ring *tx;
1406a8d7fc4aSZbigniew Bodek 	struct mvneta_buf *txbuf;
1407a8d7fc4aSZbigniew Bodek 	int i, error;
1408a8d7fc4aSZbigniew Bodek 
1409a8d7fc4aSZbigniew Bodek 	if (q >= MVNETA_TX_QNUM_MAX)
1410a8d7fc4aSZbigniew Bodek 		return (0);
1411a8d7fc4aSZbigniew Bodek 
1412a8d7fc4aSZbigniew Bodek 	tx = MVNETA_TX_RING(sc, q);
1413a8d7fc4aSZbigniew Bodek 
1414a8d7fc4aSZbigniew Bodek 	/* Tx handle */
1415a8d7fc4aSZbigniew Bodek 	for (i = 0; i < MVNETA_TX_RING_CNT; i++) {
1416a8d7fc4aSZbigniew Bodek 		txbuf = &tx->txbuf[i];
1417a8d7fc4aSZbigniew Bodek 		txbuf->m = NULL;
1418a8d7fc4aSZbigniew Bodek 		/* Tx handle needs DMA map for busdma_load_mbuf() */
1419a8d7fc4aSZbigniew Bodek 		error = bus_dmamap_create(sc->txmbuf_dtag, 0,
1420a8d7fc4aSZbigniew Bodek 		    &txbuf->dmap);
1421a8d7fc4aSZbigniew Bodek 		if (error != 0) {
1422a8d7fc4aSZbigniew Bodek 			device_printf(sc->dev,
1423a8d7fc4aSZbigniew Bodek 			    "can't create dma map (tx ring %d)\n", i);
1424a8d7fc4aSZbigniew Bodek 			return (error);
1425a8d7fc4aSZbigniew Bodek 		}
1426a8d7fc4aSZbigniew Bodek 	}
1427a8d7fc4aSZbigniew Bodek 	tx->dma = tx->cpu = 0;
1428a8d7fc4aSZbigniew Bodek 	tx->used = 0;
1429a8d7fc4aSZbigniew Bodek 	tx->drv_error = 0;
1430a8d7fc4aSZbigniew Bodek 	tx->queue_status = MVNETA_QUEUE_DISABLED;
1431a8d7fc4aSZbigniew Bodek 	tx->queue_hung = FALSE;
1432a8d7fc4aSZbigniew Bodek 
1433a8d7fc4aSZbigniew Bodek 	tx->ifp = sc->ifp;
1434a8d7fc4aSZbigniew Bodek 	tx->qidx = q;
1435a8d7fc4aSZbigniew Bodek 	TASK_INIT(&tx->task, 0, mvneta_tx_task, tx);
1436a8d7fc4aSZbigniew Bodek 	tx->taskq = taskqueue_create_fast("mvneta_tx_taskq", M_WAITOK,
1437a8d7fc4aSZbigniew Bodek 	    taskqueue_thread_enqueue, &tx->taskq);
1438a8d7fc4aSZbigniew Bodek 	taskqueue_start_threads(&tx->taskq, 1, PI_NET, "%s: tx_taskq(%d)",
1439a8d7fc4aSZbigniew Bodek 	    device_get_nameunit(sc->dev), q);
1440a8d7fc4aSZbigniew Bodek 
1441a8d7fc4aSZbigniew Bodek 	return (0);
1442a8d7fc4aSZbigniew Bodek }
1443a8d7fc4aSZbigniew Bodek 
1444a8d7fc4aSZbigniew Bodek STATIC void
1445a8d7fc4aSZbigniew Bodek mvneta_ring_flush_tx_queue(struct mvneta_softc *sc, int q)
1446a8d7fc4aSZbigniew Bodek {
1447a8d7fc4aSZbigniew Bodek 	struct mvneta_tx_ring *tx;
1448a8d7fc4aSZbigniew Bodek 	struct mvneta_buf *txbuf;
1449a8d7fc4aSZbigniew Bodek 	int i;
1450a8d7fc4aSZbigniew Bodek 
1451a8d7fc4aSZbigniew Bodek 	tx = MVNETA_TX_RING(sc, q);
1452a8d7fc4aSZbigniew Bodek 	KASSERT_TX_MTX(sc, q);
1453a8d7fc4aSZbigniew Bodek 
1454a8d7fc4aSZbigniew Bodek 	/* Tx handle */
1455a8d7fc4aSZbigniew Bodek 	for (i = 0; i < MVNETA_TX_RING_CNT; i++) {
1456a8d7fc4aSZbigniew Bodek 		txbuf = &tx->txbuf[i];
1457a8d7fc4aSZbigniew Bodek 		bus_dmamap_unload(sc->txmbuf_dtag, txbuf->dmap);
1458a8d7fc4aSZbigniew Bodek 		if (txbuf->m != NULL) {
1459a8d7fc4aSZbigniew Bodek 			m_freem(txbuf->m);
1460a8d7fc4aSZbigniew Bodek 			txbuf->m = NULL;
1461a8d7fc4aSZbigniew Bodek 		}
1462a8d7fc4aSZbigniew Bodek 	}
1463a8d7fc4aSZbigniew Bodek 	tx->dma = tx->cpu = 0;
1464a8d7fc4aSZbigniew Bodek 	tx->used = 0;
1465a8d7fc4aSZbigniew Bodek }
1466a8d7fc4aSZbigniew Bodek 
1467a8d7fc4aSZbigniew Bodek STATIC void
1468a8d7fc4aSZbigniew Bodek mvneta_ring_flush_rx_queue(struct mvneta_softc *sc, int q)
1469a8d7fc4aSZbigniew Bodek {
1470a8d7fc4aSZbigniew Bodek 	struct mvneta_rx_ring *rx;
1471a8d7fc4aSZbigniew Bodek 	struct mvneta_buf *rxbuf;
1472a8d7fc4aSZbigniew Bodek 	int i;
1473a8d7fc4aSZbigniew Bodek 
1474a8d7fc4aSZbigniew Bodek 	rx = MVNETA_RX_RING(sc, q);
1475a8d7fc4aSZbigniew Bodek 	KASSERT_RX_MTX(sc, q);
1476a8d7fc4aSZbigniew Bodek 
1477a8d7fc4aSZbigniew Bodek 	/* Rx handle */
1478a8d7fc4aSZbigniew Bodek 	for (i = 0; i < MVNETA_RX_RING_CNT; i++) {
1479a8d7fc4aSZbigniew Bodek 		rxbuf = &rx->rxbuf[i];
1480a8d7fc4aSZbigniew Bodek 		mvneta_rx_buf_free(sc, rxbuf);
1481a8d7fc4aSZbigniew Bodek 	}
1482a8d7fc4aSZbigniew Bodek 	rx->dma = rx->cpu = 0;
1483a8d7fc4aSZbigniew Bodek }
1484a8d7fc4aSZbigniew Bodek 
1485a8d7fc4aSZbigniew Bodek /*
1486a8d7fc4aSZbigniew Bodek  * Rx/Tx Queue Control
1487a8d7fc4aSZbigniew Bodek  */
1488a8d7fc4aSZbigniew Bodek STATIC int
1489992fa62bSJustin Hibbits mvneta_rx_queue_init(if_t ifp, int q)
1490a8d7fc4aSZbigniew Bodek {
1491a8d7fc4aSZbigniew Bodek 	struct mvneta_softc *sc;
1492a8d7fc4aSZbigniew Bodek 	struct mvneta_rx_ring *rx;
1493a8d7fc4aSZbigniew Bodek 	uint32_t reg;
1494a8d7fc4aSZbigniew Bodek 
1495992fa62bSJustin Hibbits 	sc = if_getsoftc(ifp);
1496a8d7fc4aSZbigniew Bodek 	KASSERT_RX_MTX(sc, q);
1497a8d7fc4aSZbigniew Bodek 	rx =  MVNETA_RX_RING(sc, q);
1498a8d7fc4aSZbigniew Bodek 	DASSERT(rx->desc_pa != 0);
1499a8d7fc4aSZbigniew Bodek 
1500a8d7fc4aSZbigniew Bodek 	/* descriptor address */
1501a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PRXDQA(q), rx->desc_pa);
1502a8d7fc4aSZbigniew Bodek 
1503a8d7fc4aSZbigniew Bodek 	/* Rx buffer size and descriptor ring size */
150473f20bb3SMarcin Wojtas 	reg  = MVNETA_PRXDQS_BUFFERSIZE(sc->rx_frame_size >> 3);
1505a8d7fc4aSZbigniew Bodek 	reg |= MVNETA_PRXDQS_DESCRIPTORSQUEUESIZE(MVNETA_RX_RING_CNT);
1506a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PRXDQS(q), reg);
1507a8d7fc4aSZbigniew Bodek #ifdef MVNETA_KTR
1508992fa62bSJustin Hibbits 	CTR3(KTR_SPARE2, "%s PRXDQS(%d): %#x", if_name(ifp), q,
1509a8d7fc4aSZbigniew Bodek 	    MVNETA_READ(sc, MVNETA_PRXDQS(q)));
1510a8d7fc4aSZbigniew Bodek #endif
1511a8d7fc4aSZbigniew Bodek 	/* Rx packet offset address */
1512a8d7fc4aSZbigniew Bodek 	reg = MVNETA_PRXC_PACKETOFFSET(MVNETA_PACKET_OFFSET >> 3);
1513a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PRXC(q), reg);
1514a8d7fc4aSZbigniew Bodek #ifdef MVNETA_KTR
1515992fa62bSJustin Hibbits 	CTR3(KTR_SPARE2, "%s PRXC(%d): %#x", if_name(ifp), q,
1516a8d7fc4aSZbigniew Bodek 	    MVNETA_READ(sc, MVNETA_PRXC(q)));
1517a8d7fc4aSZbigniew Bodek #endif
1518a8d7fc4aSZbigniew Bodek 
1519a8d7fc4aSZbigniew Bodek 	/* if DMA is not working, register is not updated */
1520a8d7fc4aSZbigniew Bodek 	DASSERT(MVNETA_READ(sc, MVNETA_PRXDQA(q)) == rx->desc_pa);
1521a8d7fc4aSZbigniew Bodek 	return (0);
1522a8d7fc4aSZbigniew Bodek }
1523a8d7fc4aSZbigniew Bodek 
1524a8d7fc4aSZbigniew Bodek STATIC int
1525992fa62bSJustin Hibbits mvneta_tx_queue_init(if_t ifp, int q)
1526a8d7fc4aSZbigniew Bodek {
1527a8d7fc4aSZbigniew Bodek 	struct mvneta_softc *sc;
1528a8d7fc4aSZbigniew Bodek 	struct mvneta_tx_ring *tx;
1529a8d7fc4aSZbigniew Bodek 	uint32_t reg;
1530a8d7fc4aSZbigniew Bodek 
1531992fa62bSJustin Hibbits 	sc = if_getsoftc(ifp);
1532a8d7fc4aSZbigniew Bodek 	KASSERT_TX_MTX(sc, q);
1533a8d7fc4aSZbigniew Bodek 	tx = MVNETA_TX_RING(sc, q);
1534a8d7fc4aSZbigniew Bodek 	DASSERT(tx->desc_pa != 0);
1535a8d7fc4aSZbigniew Bodek 
1536a8d7fc4aSZbigniew Bodek 	/* descriptor address */
1537a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PTXDQA(q), tx->desc_pa);
1538a8d7fc4aSZbigniew Bodek 
1539a8d7fc4aSZbigniew Bodek 	/* descriptor ring size */
1540a8d7fc4aSZbigniew Bodek 	reg = MVNETA_PTXDQS_DQS(MVNETA_TX_RING_CNT);
1541a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PTXDQS(q), reg);
1542a8d7fc4aSZbigniew Bodek 
1543a8d7fc4aSZbigniew Bodek 	/* if DMA is not working, register is not updated */
1544a8d7fc4aSZbigniew Bodek 	DASSERT(MVNETA_READ(sc, MVNETA_PTXDQA(q)) == tx->desc_pa);
1545a8d7fc4aSZbigniew Bodek 	return (0);
1546a8d7fc4aSZbigniew Bodek }
1547a8d7fc4aSZbigniew Bodek 
1548a8d7fc4aSZbigniew Bodek STATIC int
1549992fa62bSJustin Hibbits mvneta_rx_queue_enable(if_t ifp, int q)
1550a8d7fc4aSZbigniew Bodek {
1551a8d7fc4aSZbigniew Bodek 	struct mvneta_softc *sc;
1552a8d7fc4aSZbigniew Bodek 	struct mvneta_rx_ring *rx;
1553a8d7fc4aSZbigniew Bodek 	uint32_t reg;
1554a8d7fc4aSZbigniew Bodek 
1555992fa62bSJustin Hibbits 	sc = if_getsoftc(ifp);
1556a8d7fc4aSZbigniew Bodek 	rx = MVNETA_RX_RING(sc, q);
1557a8d7fc4aSZbigniew Bodek 	KASSERT_RX_MTX(sc, q);
1558a8d7fc4aSZbigniew Bodek 
1559a8d7fc4aSZbigniew Bodek 	/* Set Rx interrupt threshold */
1560a8d7fc4aSZbigniew Bodek 	reg  = MVNETA_PRXDQTH_ODT(rx->queue_th_received);
1561a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PRXDQTH(q), reg);
1562a8d7fc4aSZbigniew Bodek 
1563a8d7fc4aSZbigniew Bodek 	reg  = MVNETA_PRXITTH_RITT(rx->queue_th_time);
1564a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PRXITTH(q), reg);
1565a8d7fc4aSZbigniew Bodek 
1566a8d7fc4aSZbigniew Bodek 	/* Unmask RXTX_TH Intr. */
1567a8d7fc4aSZbigniew Bodek 	reg = MVNETA_READ(sc, MVNETA_PRXTXTIM);
1568a8d7fc4aSZbigniew Bodek 	reg |= MVNETA_PRXTXTI_RBICTAPQ(q); /* Rx Buffer Interrupt Coalese */
1569a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PRXTXTIM, reg);
1570a8d7fc4aSZbigniew Bodek 
1571a8d7fc4aSZbigniew Bodek 	/* Enable Rx queue */
1572a8d7fc4aSZbigniew Bodek 	reg = MVNETA_READ(sc, MVNETA_RQC) & MVNETA_RQC_EN_MASK;
1573a8d7fc4aSZbigniew Bodek 	reg |= MVNETA_RQC_ENQ(q);
1574a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_RQC, reg);
1575a8d7fc4aSZbigniew Bodek 
1576a8d7fc4aSZbigniew Bodek 	rx->queue_status = MVNETA_QUEUE_WORKING;
1577a8d7fc4aSZbigniew Bodek 	return (0);
1578a8d7fc4aSZbigniew Bodek }
1579a8d7fc4aSZbigniew Bodek 
1580a8d7fc4aSZbigniew Bodek STATIC int
1581992fa62bSJustin Hibbits mvneta_tx_queue_enable(if_t ifp, int q)
1582a8d7fc4aSZbigniew Bodek {
1583a8d7fc4aSZbigniew Bodek 	struct mvneta_softc *sc;
1584a8d7fc4aSZbigniew Bodek 	struct mvneta_tx_ring *tx;
1585a8d7fc4aSZbigniew Bodek 
1586992fa62bSJustin Hibbits 	sc = if_getsoftc(ifp);
1587a8d7fc4aSZbigniew Bodek 	tx = MVNETA_TX_RING(sc, q);
1588a8d7fc4aSZbigniew Bodek 	KASSERT_TX_MTX(sc, q);
1589a8d7fc4aSZbigniew Bodek 
1590a8d7fc4aSZbigniew Bodek 	/* Enable Tx queue */
1591a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_TQC, MVNETA_TQC_ENQ(q));
1592a8d7fc4aSZbigniew Bodek 
1593a8d7fc4aSZbigniew Bodek 	tx->queue_status = MVNETA_QUEUE_IDLE;
1594a8d7fc4aSZbigniew Bodek 	tx->queue_hung = FALSE;
1595a8d7fc4aSZbigniew Bodek 	return (0);
1596a8d7fc4aSZbigniew Bodek }
1597a8d7fc4aSZbigniew Bodek 
1598a8d7fc4aSZbigniew Bodek STATIC __inline void
1599a8d7fc4aSZbigniew Bodek mvneta_rx_lockq(struct mvneta_softc *sc, int q)
1600a8d7fc4aSZbigniew Bodek {
1601a8d7fc4aSZbigniew Bodek 
1602a8d7fc4aSZbigniew Bodek 	DASSERT(q >= 0);
1603a8d7fc4aSZbigniew Bodek 	DASSERT(q < MVNETA_RX_QNUM_MAX);
1604a8d7fc4aSZbigniew Bodek 	mtx_lock(&sc->rx_ring[q].ring_mtx);
1605a8d7fc4aSZbigniew Bodek }
1606a8d7fc4aSZbigniew Bodek 
1607a8d7fc4aSZbigniew Bodek STATIC __inline void
1608a8d7fc4aSZbigniew Bodek mvneta_rx_unlockq(struct mvneta_softc *sc, int q)
1609a8d7fc4aSZbigniew Bodek {
1610a8d7fc4aSZbigniew Bodek 
1611a8d7fc4aSZbigniew Bodek 	DASSERT(q >= 0);
1612a8d7fc4aSZbigniew Bodek 	DASSERT(q < MVNETA_RX_QNUM_MAX);
1613a8d7fc4aSZbigniew Bodek 	mtx_unlock(&sc->rx_ring[q].ring_mtx);
1614a8d7fc4aSZbigniew Bodek }
1615a8d7fc4aSZbigniew Bodek 
1616a8d7fc4aSZbigniew Bodek STATIC __inline int __unused
1617a8d7fc4aSZbigniew Bodek mvneta_tx_trylockq(struct mvneta_softc *sc, int q)
1618a8d7fc4aSZbigniew Bodek {
1619a8d7fc4aSZbigniew Bodek 
1620a8d7fc4aSZbigniew Bodek 	DASSERT(q >= 0);
1621a8d7fc4aSZbigniew Bodek 	DASSERT(q < MVNETA_TX_QNUM_MAX);
1622a8d7fc4aSZbigniew Bodek 	return (mtx_trylock(&sc->tx_ring[q].ring_mtx));
1623a8d7fc4aSZbigniew Bodek }
1624a8d7fc4aSZbigniew Bodek 
1625a8d7fc4aSZbigniew Bodek STATIC __inline void
1626a8d7fc4aSZbigniew Bodek mvneta_tx_lockq(struct mvneta_softc *sc, int q)
1627a8d7fc4aSZbigniew Bodek {
1628a8d7fc4aSZbigniew Bodek 
1629a8d7fc4aSZbigniew Bodek 	DASSERT(q >= 0);
1630a8d7fc4aSZbigniew Bodek 	DASSERT(q < MVNETA_TX_QNUM_MAX);
1631a8d7fc4aSZbigniew Bodek 	mtx_lock(&sc->tx_ring[q].ring_mtx);
1632a8d7fc4aSZbigniew Bodek }
1633a8d7fc4aSZbigniew Bodek 
1634a8d7fc4aSZbigniew Bodek STATIC __inline void
1635a8d7fc4aSZbigniew Bodek mvneta_tx_unlockq(struct mvneta_softc *sc, int q)
1636a8d7fc4aSZbigniew Bodek {
1637a8d7fc4aSZbigniew Bodek 
1638a8d7fc4aSZbigniew Bodek 	DASSERT(q >= 0);
1639a8d7fc4aSZbigniew Bodek 	DASSERT(q < MVNETA_TX_QNUM_MAX);
1640a8d7fc4aSZbigniew Bodek 	mtx_unlock(&sc->tx_ring[q].ring_mtx);
1641a8d7fc4aSZbigniew Bodek }
1642a8d7fc4aSZbigniew Bodek 
1643a8d7fc4aSZbigniew Bodek /*
1644a8d7fc4aSZbigniew Bodek  * Interrupt Handlers
1645a8d7fc4aSZbigniew Bodek  */
1646a8d7fc4aSZbigniew Bodek STATIC void
1647a8d7fc4aSZbigniew Bodek mvneta_disable_intr(struct mvneta_softc *sc)
1648a8d7fc4aSZbigniew Bodek {
1649a8d7fc4aSZbigniew Bodek 
1650a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_EUIM, 0);
1651a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_EUIC, 0);
1652a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PRXTXTIM, 0);
1653a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PRXTXTIC, 0);
1654a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PRXTXIM, 0);
1655a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PRXTXIC, 0);
1656a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PMIM, 0);
1657a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PMIC, 0);
1658a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PIE, 0);
1659a8d7fc4aSZbigniew Bodek }
1660a8d7fc4aSZbigniew Bodek 
1661a8d7fc4aSZbigniew Bodek STATIC void
1662a8d7fc4aSZbigniew Bodek mvneta_enable_intr(struct mvneta_softc *sc)
1663a8d7fc4aSZbigniew Bodek {
1664a8d7fc4aSZbigniew Bodek 	uint32_t reg;
1665a8d7fc4aSZbigniew Bodek 
1666a8d7fc4aSZbigniew Bodek 	/* Enable Summary Bit to check all interrupt cause. */
1667a8d7fc4aSZbigniew Bodek 	reg = MVNETA_READ(sc, MVNETA_PRXTXTIM);
1668a8d7fc4aSZbigniew Bodek 	reg |= MVNETA_PRXTXTI_PMISCICSUMMARY;
1669a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PRXTXTIM, reg);
1670a8d7fc4aSZbigniew Bodek 
1671231237bbSSebastien Bini 	if (!sc->phy_attached || sc->use_inband_status) {
1672a8d7fc4aSZbigniew Bodek 		/* Enable Port MISC Intr. (via RXTX_TH_Summary bit) */
1673a8d7fc4aSZbigniew Bodek 		MVNETA_WRITE(sc, MVNETA_PMIM, MVNETA_PMI_PHYSTATUSCHNG |
1674a8d7fc4aSZbigniew Bodek 		    MVNETA_PMI_LINKCHANGE | MVNETA_PMI_PSCSYNCCHANGE);
1675a8d7fc4aSZbigniew Bodek 	}
1676a8d7fc4aSZbigniew Bodek 
1677a8d7fc4aSZbigniew Bodek 	/* Enable All Queue Interrupt */
1678a8d7fc4aSZbigniew Bodek 	reg  = MVNETA_READ(sc, MVNETA_PIE);
1679a8d7fc4aSZbigniew Bodek 	reg |= MVNETA_PIE_RXPKTINTRPTENB_MASK;
1680a8d7fc4aSZbigniew Bodek 	reg |= MVNETA_PIE_TXPKTINTRPTENB_MASK;
1681a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PIE, reg);
1682a8d7fc4aSZbigniew Bodek }
1683a8d7fc4aSZbigniew Bodek 
1684a8d7fc4aSZbigniew Bodek STATIC void
1685a8d7fc4aSZbigniew Bodek mvneta_rxtxth_intr(void *arg)
1686a8d7fc4aSZbigniew Bodek {
1687a8d7fc4aSZbigniew Bodek 	struct mvneta_softc *sc;
1688992fa62bSJustin Hibbits 	if_t ifp;
1689a8d7fc4aSZbigniew Bodek 	uint32_t ic, queues;
1690a8d7fc4aSZbigniew Bodek 
1691a8d7fc4aSZbigniew Bodek 	sc = arg;
1692a8d7fc4aSZbigniew Bodek 	ifp = sc->ifp;
1693a8d7fc4aSZbigniew Bodek #ifdef MVNETA_KTR
1694992fa62bSJustin Hibbits 	CTR1(KTR_SPARE2, "%s got RXTX_TH_Intr", if_name(ifp));
1695a8d7fc4aSZbigniew Bodek #endif
1696a8d7fc4aSZbigniew Bodek 	ic = MVNETA_READ(sc, MVNETA_PRXTXTIC);
1697a8d7fc4aSZbigniew Bodek 	if (ic == 0)
1698a8d7fc4aSZbigniew Bodek 		return;
1699a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PRXTXTIC, ~ic);
1700a8d7fc4aSZbigniew Bodek 
170119ae05aaSGordon Bergling 	/* Ack maintenance interrupt first */
1702a8d7fc4aSZbigniew Bodek 	if (__predict_false((ic & MVNETA_PRXTXTI_PMISCICSUMMARY) &&
1703231237bbSSebastien Bini 	    (!sc->phy_attached || sc->use_inband_status))) {
1704a8d7fc4aSZbigniew Bodek 		mvneta_sc_lock(sc);
1705a8d7fc4aSZbigniew Bodek 		mvneta_misc_intr(sc);
1706a8d7fc4aSZbigniew Bodek 		mvneta_sc_unlock(sc);
1707a8d7fc4aSZbigniew Bodek 	}
1708992fa62bSJustin Hibbits 	if (__predict_false(!(if_getdrvflags(ifp) & IFF_DRV_RUNNING)))
1709a8d7fc4aSZbigniew Bodek 		return;
1710a8d7fc4aSZbigniew Bodek 	/* RxTxTH interrupt */
1711a8d7fc4aSZbigniew Bodek 	queues = MVNETA_PRXTXTI_GET_RBICTAPQ(ic);
1712a8d7fc4aSZbigniew Bodek 	if (__predict_true(queues)) {
1713a8d7fc4aSZbigniew Bodek #ifdef MVNETA_KTR
1714992fa62bSJustin Hibbits 		CTR1(KTR_SPARE2, "%s got PRXTXTIC: +RXEOF", if_name(ifp));
1715a8d7fc4aSZbigniew Bodek #endif
1716a8d7fc4aSZbigniew Bodek 		/* At the moment the driver support only one RX queue. */
1717a8d7fc4aSZbigniew Bodek 		DASSERT(MVNETA_IS_QUEUE_SET(queues, 0));
1718a8d7fc4aSZbigniew Bodek 		mvneta_rx(sc, 0, 0);
1719a8d7fc4aSZbigniew Bodek 	}
1720a8d7fc4aSZbigniew Bodek }
1721a8d7fc4aSZbigniew Bodek 
1722a8d7fc4aSZbigniew Bodek STATIC int
1723a8d7fc4aSZbigniew Bodek mvneta_misc_intr(struct mvneta_softc *sc)
1724a8d7fc4aSZbigniew Bodek {
1725a8d7fc4aSZbigniew Bodek 	uint32_t ic;
1726a8d7fc4aSZbigniew Bodek 	int claimed = 0;
1727a8d7fc4aSZbigniew Bodek 
1728a8d7fc4aSZbigniew Bodek #ifdef MVNETA_KTR
1729992fa62bSJustin Hibbits 	CTR1(KTR_SPARE2, "%s got MISC_INTR", if_name(sc->ifp));
1730a8d7fc4aSZbigniew Bodek #endif
1731a8d7fc4aSZbigniew Bodek 	KASSERT_SC_MTX(sc);
1732a8d7fc4aSZbigniew Bodek 
1733a8d7fc4aSZbigniew Bodek 	for (;;) {
1734a8d7fc4aSZbigniew Bodek 		ic = MVNETA_READ(sc, MVNETA_PMIC);
1735a8d7fc4aSZbigniew Bodek 		ic &= MVNETA_READ(sc, MVNETA_PMIM);
1736a8d7fc4aSZbigniew Bodek 		if (ic == 0)
1737a8d7fc4aSZbigniew Bodek 			break;
1738a8d7fc4aSZbigniew Bodek 		MVNETA_WRITE(sc, MVNETA_PMIC, ~ic);
1739a8d7fc4aSZbigniew Bodek 		claimed = 1;
1740a8d7fc4aSZbigniew Bodek 
1741a8d7fc4aSZbigniew Bodek 		if (ic & (MVNETA_PMI_PHYSTATUSCHNG |
1742a8d7fc4aSZbigniew Bodek 		    MVNETA_PMI_LINKCHANGE | MVNETA_PMI_PSCSYNCCHANGE))
1743a8d7fc4aSZbigniew Bodek 			mvneta_link_isr(sc);
1744a8d7fc4aSZbigniew Bodek 	}
1745a8d7fc4aSZbigniew Bodek 	return (claimed);
1746a8d7fc4aSZbigniew Bodek }
1747a8d7fc4aSZbigniew Bodek 
1748a8d7fc4aSZbigniew Bodek STATIC void
1749a8d7fc4aSZbigniew Bodek mvneta_tick(void *arg)
1750a8d7fc4aSZbigniew Bodek {
1751a8d7fc4aSZbigniew Bodek 	struct mvneta_softc *sc;
1752a8d7fc4aSZbigniew Bodek 	struct mvneta_tx_ring *tx;
1753a8d7fc4aSZbigniew Bodek 	struct mvneta_rx_ring *rx;
1754a8d7fc4aSZbigniew Bodek 	int q;
1755a8d7fc4aSZbigniew Bodek 	uint32_t fc_prev, fc_curr;
1756a8d7fc4aSZbigniew Bodek 
1757a8d7fc4aSZbigniew Bodek 	sc = arg;
1758a8d7fc4aSZbigniew Bodek 
1759a8d7fc4aSZbigniew Bodek 	/*
1760a8d7fc4aSZbigniew Bodek 	 * This is done before mib update to get the right stats
1761a8d7fc4aSZbigniew Bodek 	 * for this tick.
1762a8d7fc4aSZbigniew Bodek 	 */
1763a8d7fc4aSZbigniew Bodek 	mvneta_tx_drain(sc);
1764a8d7fc4aSZbigniew Bodek 
1765a8d7fc4aSZbigniew Bodek 	/* Extract previous flow-control frame received counter. */
1766a8d7fc4aSZbigniew Bodek 	fc_prev = sc->sysctl_mib[MVNETA_MIB_FC_GOOD_IDX].counter;
1767a8d7fc4aSZbigniew Bodek 	/* Read mib registers (clear by read). */
1768a8d7fc4aSZbigniew Bodek 	mvneta_update_mib(sc);
1769a8d7fc4aSZbigniew Bodek 	/* Extract current flow-control frame received counter. */
1770a8d7fc4aSZbigniew Bodek 	fc_curr = sc->sysctl_mib[MVNETA_MIB_FC_GOOD_IDX].counter;
1771a8d7fc4aSZbigniew Bodek 
1772a8d7fc4aSZbigniew Bodek 
1773992fa62bSJustin Hibbits 	if (sc->phy_attached && if_getflags(sc->ifp) & IFF_UP) {
1774a8d7fc4aSZbigniew Bodek 		mvneta_sc_lock(sc);
1775a8d7fc4aSZbigniew Bodek 		mii_tick(sc->mii);
1776a8d7fc4aSZbigniew Bodek 
1777a8d7fc4aSZbigniew Bodek 		/* Adjust MAC settings */
1778a8d7fc4aSZbigniew Bodek 		mvneta_adjust_link(sc);
1779a8d7fc4aSZbigniew Bodek 		mvneta_sc_unlock(sc);
1780a8d7fc4aSZbigniew Bodek 	}
1781a8d7fc4aSZbigniew Bodek 
1782a8d7fc4aSZbigniew Bodek 	/*
1783a8d7fc4aSZbigniew Bodek 	 * We were unable to refill the rx queue and left the rx func, leaving
1784a8d7fc4aSZbigniew Bodek 	 * the ring without mbuf and no way to call the refill func.
1785a8d7fc4aSZbigniew Bodek 	 */
1786a8d7fc4aSZbigniew Bodek 	for (q = 0; q < MVNETA_RX_QNUM_MAX; q++) {
1787a8d7fc4aSZbigniew Bodek 		rx = MVNETA_RX_RING(sc, q);
1788a8d7fc4aSZbigniew Bodek 		if (rx->needs_refill == TRUE) {
1789a8d7fc4aSZbigniew Bodek 			mvneta_rx_lockq(sc, q);
1790a8d7fc4aSZbigniew Bodek 			mvneta_rx_queue_refill(sc, q);
1791a8d7fc4aSZbigniew Bodek 			mvneta_rx_unlockq(sc, q);
1792a8d7fc4aSZbigniew Bodek 		}
1793a8d7fc4aSZbigniew Bodek 	}
1794a8d7fc4aSZbigniew Bodek 
1795a8d7fc4aSZbigniew Bodek 	/*
1796a8d7fc4aSZbigniew Bodek 	 * Watchdog:
1797a8d7fc4aSZbigniew Bodek 	 * - check if queue is mark as hung.
1798a8d7fc4aSZbigniew Bodek 	 * - ignore hung status if we received some pause frame
1799a8d7fc4aSZbigniew Bodek 	 *   as hardware may have paused packet transmit.
1800a8d7fc4aSZbigniew Bodek 	 */
1801a8d7fc4aSZbigniew Bodek 	for (q = 0; q < MVNETA_TX_QNUM_MAX; q++) {
1802a8d7fc4aSZbigniew Bodek 		/*
1803a8d7fc4aSZbigniew Bodek 		 * We should take queue lock, but as we only read
1804a8d7fc4aSZbigniew Bodek 		 * queue status we can do it without lock, we may
1805a8d7fc4aSZbigniew Bodek 		 * only missdetect queue status for one tick.
1806a8d7fc4aSZbigniew Bodek 		 */
1807a8d7fc4aSZbigniew Bodek 		tx = MVNETA_TX_RING(sc, q);
1808a8d7fc4aSZbigniew Bodek 
1809a8d7fc4aSZbigniew Bodek 		if (tx->queue_hung && (fc_curr - fc_prev) == 0)
1810a8d7fc4aSZbigniew Bodek 			goto timeout;
1811a8d7fc4aSZbigniew Bodek 	}
1812a8d7fc4aSZbigniew Bodek 
1813a8d7fc4aSZbigniew Bodek 	callout_schedule(&sc->tick_ch, hz);
1814a8d7fc4aSZbigniew Bodek 	return;
1815a8d7fc4aSZbigniew Bodek 
1816a8d7fc4aSZbigniew Bodek timeout:
1817a8d7fc4aSZbigniew Bodek 	if_printf(sc->ifp, "watchdog timeout\n");
1818a8d7fc4aSZbigniew Bodek 
1819a8d7fc4aSZbigniew Bodek 	mvneta_sc_lock(sc);
1820a8d7fc4aSZbigniew Bodek 	sc->counter_watchdog++;
1821a8d7fc4aSZbigniew Bodek 	sc->counter_watchdog_mib++;
1822a8d7fc4aSZbigniew Bodek 	/* Trigger reinitialize sequence. */
1823a8d7fc4aSZbigniew Bodek 	mvneta_stop_locked(sc);
1824a8d7fc4aSZbigniew Bodek 	mvneta_init_locked(sc);
1825a8d7fc4aSZbigniew Bodek 	mvneta_sc_unlock(sc);
1826a8d7fc4aSZbigniew Bodek }
1827a8d7fc4aSZbigniew Bodek 
1828a8d7fc4aSZbigniew Bodek STATIC void
1829992fa62bSJustin Hibbits mvneta_qflush(if_t ifp)
1830a8d7fc4aSZbigniew Bodek {
1831a8d7fc4aSZbigniew Bodek #ifdef MVNETA_MULTIQUEUE
1832a8d7fc4aSZbigniew Bodek 	struct mvneta_softc *sc;
1833a8d7fc4aSZbigniew Bodek 	struct mvneta_tx_ring *tx;
1834a8d7fc4aSZbigniew Bodek 	struct mbuf *m;
1835a8d7fc4aSZbigniew Bodek 	size_t q;
1836a8d7fc4aSZbigniew Bodek 
1837992fa62bSJustin Hibbits 	sc = if_getsoftc(ifp);
1838a8d7fc4aSZbigniew Bodek 
1839a8d7fc4aSZbigniew Bodek 	for (q = 0; q < MVNETA_TX_QNUM_MAX; q++) {
1840a8d7fc4aSZbigniew Bodek 		tx = MVNETA_TX_RING(sc, q);
1841a8d7fc4aSZbigniew Bodek 		mvneta_tx_lockq(sc, q);
1842a8d7fc4aSZbigniew Bodek 		while ((m = buf_ring_dequeue_sc(tx->br)) != NULL)
1843a8d7fc4aSZbigniew Bodek 			m_freem(m);
1844a8d7fc4aSZbigniew Bodek 		mvneta_tx_unlockq(sc, q);
1845a8d7fc4aSZbigniew Bodek 	}
1846a8d7fc4aSZbigniew Bodek #endif
1847a8d7fc4aSZbigniew Bodek 	if_qflush(ifp);
1848a8d7fc4aSZbigniew Bodek }
1849a8d7fc4aSZbigniew Bodek 
1850a8d7fc4aSZbigniew Bodek STATIC void
1851a8d7fc4aSZbigniew Bodek mvneta_tx_task(void *arg, int pending)
1852a8d7fc4aSZbigniew Bodek {
1853a8d7fc4aSZbigniew Bodek 	struct mvneta_softc *sc;
1854a8d7fc4aSZbigniew Bodek 	struct mvneta_tx_ring *tx;
1855992fa62bSJustin Hibbits 	if_t ifp;
1856a8d7fc4aSZbigniew Bodek 	int error;
1857a8d7fc4aSZbigniew Bodek 
1858a8d7fc4aSZbigniew Bodek 	tx = arg;
1859a8d7fc4aSZbigniew Bodek 	ifp = tx->ifp;
1860992fa62bSJustin Hibbits 	sc = if_getsoftc(ifp);
1861a8d7fc4aSZbigniew Bodek 
1862a8d7fc4aSZbigniew Bodek 	mvneta_tx_lockq(sc, tx->qidx);
1863a8d7fc4aSZbigniew Bodek 	error = mvneta_xmit_locked(sc, tx->qidx);
1864a8d7fc4aSZbigniew Bodek 	mvneta_tx_unlockq(sc, tx->qidx);
1865a8d7fc4aSZbigniew Bodek 
1866a8d7fc4aSZbigniew Bodek 	/* Try again */
1867a8d7fc4aSZbigniew Bodek 	if (__predict_false(error != 0 && error != ENETDOWN)) {
1868a8d7fc4aSZbigniew Bodek 		pause("mvneta_tx_task_sleep", 1);
1869a8d7fc4aSZbigniew Bodek 		taskqueue_enqueue(tx->taskq, &tx->task);
1870a8d7fc4aSZbigniew Bodek 	}
1871a8d7fc4aSZbigniew Bodek }
1872a8d7fc4aSZbigniew Bodek 
1873a8d7fc4aSZbigniew Bodek STATIC int
1874a8d7fc4aSZbigniew Bodek mvneta_xmitfast_locked(struct mvneta_softc *sc, int q, struct mbuf **m)
1875a8d7fc4aSZbigniew Bodek {
1876a8d7fc4aSZbigniew Bodek 	struct mvneta_tx_ring *tx;
1877992fa62bSJustin Hibbits 	if_t ifp;
1878a8d7fc4aSZbigniew Bodek 	int error;
1879a8d7fc4aSZbigniew Bodek 
1880a8d7fc4aSZbigniew Bodek 	KASSERT_TX_MTX(sc, q);
1881a8d7fc4aSZbigniew Bodek 	tx = MVNETA_TX_RING(sc, q);
1882a8d7fc4aSZbigniew Bodek 	error = 0;
1883a8d7fc4aSZbigniew Bodek 
1884a8d7fc4aSZbigniew Bodek 	ifp = sc->ifp;
1885a8d7fc4aSZbigniew Bodek 
1886a8d7fc4aSZbigniew Bodek 	/* Dont enqueue packet if the queue is disabled. */
1887a8d7fc4aSZbigniew Bodek 	if (__predict_false(tx->queue_status == MVNETA_QUEUE_DISABLED)) {
1888a8d7fc4aSZbigniew Bodek 		m_freem(*m);
1889a8d7fc4aSZbigniew Bodek 		*m = NULL;
1890a8d7fc4aSZbigniew Bodek 		return (ENETDOWN);
1891a8d7fc4aSZbigniew Bodek 	}
1892a8d7fc4aSZbigniew Bodek 
1893a8d7fc4aSZbigniew Bodek 	/* Reclaim mbuf if above threshold. */
1894a8d7fc4aSZbigniew Bodek 	if (__predict_true(tx->used > MVNETA_TX_RECLAIM_COUNT))
1895a8d7fc4aSZbigniew Bodek 		mvneta_tx_queue_complete(sc, q);
1896a8d7fc4aSZbigniew Bodek 
1897a8d7fc4aSZbigniew Bodek 	/* Do not call transmit path if queue is already too full. */
1898a8d7fc4aSZbigniew Bodek 	if (__predict_false(tx->used >
1899a8d7fc4aSZbigniew Bodek 	    MVNETA_TX_RING_CNT - MVNETA_TX_SEGLIMIT))
1900a8d7fc4aSZbigniew Bodek 		return (ENOBUFS);
1901a8d7fc4aSZbigniew Bodek 
1902a8d7fc4aSZbigniew Bodek 	error = mvneta_tx_queue(sc, m, q);
1903a8d7fc4aSZbigniew Bodek 	if (__predict_false(error != 0))
1904a8d7fc4aSZbigniew Bodek 		return (error);
1905a8d7fc4aSZbigniew Bodek 
1906a8d7fc4aSZbigniew Bodek 	/* Send a copy of the frame to the BPF listener */
1907a8d7fc4aSZbigniew Bodek 	ETHER_BPF_MTAP(ifp, *m);
1908a8d7fc4aSZbigniew Bodek 
1909a8d7fc4aSZbigniew Bodek 	/* Set watchdog on */
1910a8d7fc4aSZbigniew Bodek 	tx->watchdog_time = ticks;
1911a8d7fc4aSZbigniew Bodek 	tx->queue_status = MVNETA_QUEUE_WORKING;
1912a8d7fc4aSZbigniew Bodek 
1913a8d7fc4aSZbigniew Bodek 	return (error);
1914a8d7fc4aSZbigniew Bodek }
1915a8d7fc4aSZbigniew Bodek 
1916a8d7fc4aSZbigniew Bodek #ifdef MVNETA_MULTIQUEUE
1917a8d7fc4aSZbigniew Bodek STATIC int
1918992fa62bSJustin Hibbits mvneta_transmit(if_t ifp, struct mbuf *m)
1919a8d7fc4aSZbigniew Bodek {
1920a8d7fc4aSZbigniew Bodek 	struct mvneta_softc *sc;
1921a8d7fc4aSZbigniew Bodek 	struct mvneta_tx_ring *tx;
1922a8d7fc4aSZbigniew Bodek 	int error;
1923a8d7fc4aSZbigniew Bodek 	int q;
1924a8d7fc4aSZbigniew Bodek 
1925992fa62bSJustin Hibbits 	sc = if_getsoftc(ifp);
1926a8d7fc4aSZbigniew Bodek 
1927a8d7fc4aSZbigniew Bodek 	/* Use default queue if there is no flow id as thread can migrate. */
1928a8d7fc4aSZbigniew Bodek 	if (__predict_true(M_HASHTYPE_GET(m) != M_HASHTYPE_NONE))
1929a8d7fc4aSZbigniew Bodek 		q = m->m_pkthdr.flowid % MVNETA_TX_QNUM_MAX;
1930a8d7fc4aSZbigniew Bodek 	else
1931a8d7fc4aSZbigniew Bodek 		q = 0;
1932a8d7fc4aSZbigniew Bodek 
1933a8d7fc4aSZbigniew Bodek 	tx = MVNETA_TX_RING(sc, q);
1934a8d7fc4aSZbigniew Bodek 
1935592fcf6cSGordon Bergling 	/* If buf_ring is full start transmit immediately. */
1936a8d7fc4aSZbigniew Bodek 	if (buf_ring_full(tx->br)) {
1937a8d7fc4aSZbigniew Bodek 		mvneta_tx_lockq(sc, q);
1938a8d7fc4aSZbigniew Bodek 		mvneta_xmit_locked(sc, q);
1939a8d7fc4aSZbigniew Bodek 		mvneta_tx_unlockq(sc, q);
1940a8d7fc4aSZbigniew Bodek 	}
1941a8d7fc4aSZbigniew Bodek 
1942a8d7fc4aSZbigniew Bodek 	/*
1943a8d7fc4aSZbigniew Bodek 	 * If the buf_ring is empty we will not reorder packets.
1944a8d7fc4aSZbigniew Bodek 	 * If the lock is available transmit without using buf_ring.
1945a8d7fc4aSZbigniew Bodek 	 */
1946a8d7fc4aSZbigniew Bodek 	if (buf_ring_empty(tx->br) && mvneta_tx_trylockq(sc, q) != 0) {
1947a8d7fc4aSZbigniew Bodek 		error = mvneta_xmitfast_locked(sc, q, &m);
1948a8d7fc4aSZbigniew Bodek 		mvneta_tx_unlockq(sc, q);
1949a8d7fc4aSZbigniew Bodek 		if (__predict_true(error == 0))
1950a8d7fc4aSZbigniew Bodek 			return (0);
1951a8d7fc4aSZbigniew Bodek 
1952a8d7fc4aSZbigniew Bodek 		/* Transmit can fail in fastpath. */
1953a8d7fc4aSZbigniew Bodek 		if (__predict_false(m == NULL))
1954a8d7fc4aSZbigniew Bodek 			return (error);
1955a8d7fc4aSZbigniew Bodek 	}
1956a8d7fc4aSZbigniew Bodek 
1957a8d7fc4aSZbigniew Bodek 	/* Enqueue then schedule taskqueue. */
1958a8d7fc4aSZbigniew Bodek 	error = drbr_enqueue(ifp, tx->br, m);
1959a8d7fc4aSZbigniew Bodek 	if (__predict_false(error != 0))
1960a8d7fc4aSZbigniew Bodek 		return (error);
1961a8d7fc4aSZbigniew Bodek 
1962a8d7fc4aSZbigniew Bodek 	taskqueue_enqueue(tx->taskq, &tx->task);
1963a8d7fc4aSZbigniew Bodek 	return (0);
1964a8d7fc4aSZbigniew Bodek }
1965a8d7fc4aSZbigniew Bodek 
1966a8d7fc4aSZbigniew Bodek STATIC int
1967a8d7fc4aSZbigniew Bodek mvneta_xmit_locked(struct mvneta_softc *sc, int q)
1968a8d7fc4aSZbigniew Bodek {
1969992fa62bSJustin Hibbits 	if_t ifp;
1970a8d7fc4aSZbigniew Bodek 	struct mvneta_tx_ring *tx;
1971a8d7fc4aSZbigniew Bodek 	struct mbuf *m;
1972a8d7fc4aSZbigniew Bodek 	int error;
1973a8d7fc4aSZbigniew Bodek 
1974a8d7fc4aSZbigniew Bodek 	KASSERT_TX_MTX(sc, q);
1975a8d7fc4aSZbigniew Bodek 	ifp = sc->ifp;
1976a8d7fc4aSZbigniew Bodek 	tx = MVNETA_TX_RING(sc, q);
1977a8d7fc4aSZbigniew Bodek 	error = 0;
1978a8d7fc4aSZbigniew Bodek 
1979a8d7fc4aSZbigniew Bodek 	while ((m = drbr_peek(ifp, tx->br)) != NULL) {
1980a8d7fc4aSZbigniew Bodek 		error = mvneta_xmitfast_locked(sc, q, &m);
1981a8d7fc4aSZbigniew Bodek 		if (__predict_false(error != 0)) {
1982a8d7fc4aSZbigniew Bodek 			if (m != NULL)
1983a8d7fc4aSZbigniew Bodek 				drbr_putback(ifp, tx->br, m);
1984a8d7fc4aSZbigniew Bodek 			else
1985a8d7fc4aSZbigniew Bodek 				drbr_advance(ifp, tx->br);
1986a8d7fc4aSZbigniew Bodek 			break;
1987a8d7fc4aSZbigniew Bodek 		}
1988a8d7fc4aSZbigniew Bodek 		drbr_advance(ifp, tx->br);
1989a8d7fc4aSZbigniew Bodek 	}
1990a8d7fc4aSZbigniew Bodek 
1991a8d7fc4aSZbigniew Bodek 	return (error);
1992a8d7fc4aSZbigniew Bodek }
1993a8d7fc4aSZbigniew Bodek #else /* !MVNETA_MULTIQUEUE */
1994a8d7fc4aSZbigniew Bodek STATIC void
1995992fa62bSJustin Hibbits mvneta_start(if_t ifp)
1996a8d7fc4aSZbigniew Bodek {
1997a8d7fc4aSZbigniew Bodek 	struct mvneta_softc *sc;
1998a8d7fc4aSZbigniew Bodek 	struct mvneta_tx_ring *tx;
1999a8d7fc4aSZbigniew Bodek 	int error;
2000a8d7fc4aSZbigniew Bodek 
2001992fa62bSJustin Hibbits 	sc = if_getsoftc(ifp);
2002a8d7fc4aSZbigniew Bodek 	tx = MVNETA_TX_RING(sc, 0);
2003a8d7fc4aSZbigniew Bodek 
2004a8d7fc4aSZbigniew Bodek 	mvneta_tx_lockq(sc, 0);
2005a8d7fc4aSZbigniew Bodek 	error = mvneta_xmit_locked(sc, 0);
2006a8d7fc4aSZbigniew Bodek 	mvneta_tx_unlockq(sc, 0);
2007a8d7fc4aSZbigniew Bodek 	/* Handle retransmit in the background taskq. */
2008a8d7fc4aSZbigniew Bodek 	if (__predict_false(error != 0 && error != ENETDOWN))
2009a8d7fc4aSZbigniew Bodek 		taskqueue_enqueue(tx->taskq, &tx->task);
2010a8d7fc4aSZbigniew Bodek }
2011a8d7fc4aSZbigniew Bodek 
2012a8d7fc4aSZbigniew Bodek STATIC int
2013a8d7fc4aSZbigniew Bodek mvneta_xmit_locked(struct mvneta_softc *sc, int q)
2014a8d7fc4aSZbigniew Bodek {
2015992fa62bSJustin Hibbits 	if_t ifp;
2016a8d7fc4aSZbigniew Bodek 	struct mbuf *m;
2017a8d7fc4aSZbigniew Bodek 	int error;
2018a8d7fc4aSZbigniew Bodek 
2019a8d7fc4aSZbigniew Bodek 	KASSERT_TX_MTX(sc, q);
2020a8d7fc4aSZbigniew Bodek 	ifp = sc->ifp;
2021a8d7fc4aSZbigniew Bodek 	error = 0;
2022a8d7fc4aSZbigniew Bodek 
2023992fa62bSJustin Hibbits 	while (!if_sendq_empty(ifp)) {
2024992fa62bSJustin Hibbits 		m = if_dequeue(ifp);
2025a8d7fc4aSZbigniew Bodek 		if (m == NULL)
2026a8d7fc4aSZbigniew Bodek 			break;
2027a8d7fc4aSZbigniew Bodek 
2028a8d7fc4aSZbigniew Bodek 		error = mvneta_xmitfast_locked(sc, q, &m);
2029a8d7fc4aSZbigniew Bodek 		if (__predict_false(error != 0)) {
2030a8d7fc4aSZbigniew Bodek 			if (m != NULL)
2031992fa62bSJustin Hibbits 				if_sendq_prepend(ifp, m);
2032a8d7fc4aSZbigniew Bodek 			break;
2033a8d7fc4aSZbigniew Bodek 		}
2034a8d7fc4aSZbigniew Bodek 	}
2035a8d7fc4aSZbigniew Bodek 
2036a8d7fc4aSZbigniew Bodek 	return (error);
2037a8d7fc4aSZbigniew Bodek }
2038a8d7fc4aSZbigniew Bodek #endif
2039a8d7fc4aSZbigniew Bodek 
2040a8d7fc4aSZbigniew Bodek STATIC int
2041992fa62bSJustin Hibbits mvneta_ioctl(if_t ifp, u_long cmd, caddr_t data)
2042a8d7fc4aSZbigniew Bodek {
2043a8d7fc4aSZbigniew Bodek 	struct mvneta_softc *sc;
2044a8d7fc4aSZbigniew Bodek 	struct mvneta_rx_ring *rx;
2045a8d7fc4aSZbigniew Bodek 	struct ifreq *ifr;
2046a8d7fc4aSZbigniew Bodek 	int error, mask;
2047a8d7fc4aSZbigniew Bodek 	uint32_t flags;
204897ecdc00SKornel Dulęba 	bool reinit;
2049a8d7fc4aSZbigniew Bodek 	int q;
2050a8d7fc4aSZbigniew Bodek 
2051a8d7fc4aSZbigniew Bodek 	error = 0;
205297ecdc00SKornel Dulęba 	reinit = false;
2053992fa62bSJustin Hibbits 	sc = if_getsoftc(ifp);
2054a8d7fc4aSZbigniew Bodek 	ifr = (struct ifreq *)data;
2055a8d7fc4aSZbigniew Bodek 	switch (cmd) {
2056a8d7fc4aSZbigniew Bodek 	case SIOCSIFFLAGS:
2057a8d7fc4aSZbigniew Bodek 		mvneta_sc_lock(sc);
2058992fa62bSJustin Hibbits 		if (if_getflags(ifp) & IFF_UP) {
2059992fa62bSJustin Hibbits 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
2060992fa62bSJustin Hibbits 				flags = if_getflags(ifp) ^ sc->mvneta_if_flags;
2061a8d7fc4aSZbigniew Bodek 
2062a8d7fc4aSZbigniew Bodek 				if (flags != 0)
2063992fa62bSJustin Hibbits 					sc->mvneta_if_flags = if_getflags(ifp);
2064a8d7fc4aSZbigniew Bodek 
2065a8d7fc4aSZbigniew Bodek 				if ((flags & IFF_PROMISC) != 0)
2066a8d7fc4aSZbigniew Bodek 					mvneta_filter_setup(sc);
2067a8d7fc4aSZbigniew Bodek 			} else {
2068a8d7fc4aSZbigniew Bodek 				mvneta_init_locked(sc);
2069992fa62bSJustin Hibbits 				sc->mvneta_if_flags = if_getflags(ifp);
2070a8d7fc4aSZbigniew Bodek 				if (sc->phy_attached)
2071a8d7fc4aSZbigniew Bodek 					mii_mediachg(sc->mii);
2072a8d7fc4aSZbigniew Bodek 				mvneta_sc_unlock(sc);
2073a8d7fc4aSZbigniew Bodek 				break;
2074a8d7fc4aSZbigniew Bodek 			}
2075992fa62bSJustin Hibbits 		} else if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
2076a8d7fc4aSZbigniew Bodek 			mvneta_stop_locked(sc);
2077a8d7fc4aSZbigniew Bodek 
2078992fa62bSJustin Hibbits 		sc->mvneta_if_flags = if_getflags(ifp);
2079a8d7fc4aSZbigniew Bodek 		mvneta_sc_unlock(sc);
2080a8d7fc4aSZbigniew Bodek 		break;
2081a8d7fc4aSZbigniew Bodek 	case SIOCSIFCAP:
2082992fa62bSJustin Hibbits 		if (if_getmtu(ifp) > sc->tx_csum_limit &&
2083a8d7fc4aSZbigniew Bodek 		    ifr->ifr_reqcap & IFCAP_TXCSUM)
2084a8d7fc4aSZbigniew Bodek 			ifr->ifr_reqcap &= ~IFCAP_TXCSUM;
2085992fa62bSJustin Hibbits 		mask = if_getcapenable(ifp) ^ ifr->ifr_reqcap;
2086a8d7fc4aSZbigniew Bodek 		if (mask & IFCAP_HWCSUM) {
2087992fa62bSJustin Hibbits 			if_setcapenablebit(ifp, IFCAP_HWCSUM & ifr->ifr_reqcap,
2088992fa62bSJustin Hibbits 			    IFCAP_HWCSUM);
2089992fa62bSJustin Hibbits 			if (if_getcapenable(ifp) & IFCAP_TXCSUM)
2090992fa62bSJustin Hibbits 				if_sethwassist(ifp, CSUM_IP | CSUM_TCP |
2091992fa62bSJustin Hibbits 				    CSUM_UDP);
2092a8d7fc4aSZbigniew Bodek 			else
2093992fa62bSJustin Hibbits 				if_sethwassist(ifp, 0);
2094a8d7fc4aSZbigniew Bodek 		}
2095a8d7fc4aSZbigniew Bodek 		if (mask & IFCAP_LRO) {
2096a8d7fc4aSZbigniew Bodek 			mvneta_sc_lock(sc);
2097992fa62bSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_LRO);
2098992fa62bSJustin Hibbits 			if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0) {
2099a8d7fc4aSZbigniew Bodek 				for (q = 0; q < MVNETA_RX_QNUM_MAX; q++) {
2100a8d7fc4aSZbigniew Bodek 					rx = MVNETA_RX_RING(sc, q);
2101a8d7fc4aSZbigniew Bodek 					rx->lro_enabled = !rx->lro_enabled;
2102a8d7fc4aSZbigniew Bodek 				}
2103a8d7fc4aSZbigniew Bodek 			}
2104a8d7fc4aSZbigniew Bodek 			mvneta_sc_unlock(sc);
2105a8d7fc4aSZbigniew Bodek 		}
2106a8d7fc4aSZbigniew Bodek 		VLAN_CAPABILITIES(ifp);
2107a8d7fc4aSZbigniew Bodek 		break;
2108a8d7fc4aSZbigniew Bodek 	case SIOCSIFMEDIA:
2109a8d7fc4aSZbigniew Bodek 		if ((IFM_SUBTYPE(ifr->ifr_media) == IFM_1000_T ||
2110a8d7fc4aSZbigniew Bodek 		    IFM_SUBTYPE(ifr->ifr_media) == IFM_2500_T) &&
2111a8d7fc4aSZbigniew Bodek 		    (ifr->ifr_media & IFM_FDX) == 0) {
2112a8d7fc4aSZbigniew Bodek 			device_printf(sc->dev,
2113a8d7fc4aSZbigniew Bodek 			    "%s half-duplex unsupported\n",
2114a8d7fc4aSZbigniew Bodek 			    IFM_SUBTYPE(ifr->ifr_media) == IFM_1000_T ?
2115a8d7fc4aSZbigniew Bodek 			    "1000Base-T" :
2116a8d7fc4aSZbigniew Bodek 			    "2500Base-T");
2117a8d7fc4aSZbigniew Bodek 			error = EINVAL;
2118a8d7fc4aSZbigniew Bodek 			break;
2119a8d7fc4aSZbigniew Bodek 		}
2120a8d7fc4aSZbigniew Bodek 	case SIOCGIFMEDIA: /* FALLTHROUGH */
2121db79a5f9SLuiz Otavio O Souza 	case SIOCGIFXMEDIA:
2122a8d7fc4aSZbigniew Bodek 		if (!sc->phy_attached)
2123a8d7fc4aSZbigniew Bodek 			error = ifmedia_ioctl(ifp, ifr, &sc->mvneta_ifmedia,
2124a8d7fc4aSZbigniew Bodek 			    cmd);
2125a8d7fc4aSZbigniew Bodek 		else
2126a8d7fc4aSZbigniew Bodek 			error = ifmedia_ioctl(ifp, ifr, &sc->mii->mii_media,
2127a8d7fc4aSZbigniew Bodek 			    cmd);
2128a8d7fc4aSZbigniew Bodek 		break;
2129a8d7fc4aSZbigniew Bodek 	case SIOCSIFMTU:
2130a8d7fc4aSZbigniew Bodek 		if (ifr->ifr_mtu < 68 || ifr->ifr_mtu > MVNETA_MAX_FRAME -
2131a8d7fc4aSZbigniew Bodek 		    MVNETA_ETHER_SIZE) {
2132a8d7fc4aSZbigniew Bodek 			error = EINVAL;
2133a8d7fc4aSZbigniew Bodek 		} else {
2134992fa62bSJustin Hibbits 			if_setmtu(ifp, ifr->ifr_mtu);
2135a8d7fc4aSZbigniew Bodek 			mvneta_sc_lock(sc);
2136992fa62bSJustin Hibbits 			if (if_getmtu(ifp) + MVNETA_ETHER_SIZE <= MCLBYTES) {
213773f20bb3SMarcin Wojtas 				sc->rx_frame_size = MCLBYTES;
213873f20bb3SMarcin Wojtas 			} else {
213973f20bb3SMarcin Wojtas 				sc->rx_frame_size = MJUM9BYTES;
214073f20bb3SMarcin Wojtas 			}
2141992fa62bSJustin Hibbits 			if (if_getmtu(ifp) > sc->tx_csum_limit) {
2142992fa62bSJustin Hibbits 				if_setcapenablebit(ifp, 0, IFCAP_TXCSUM);
2143992fa62bSJustin Hibbits 				if_sethwassist(ifp, 0);
2144a8d7fc4aSZbigniew Bodek 			} else {
2145992fa62bSJustin Hibbits 				if_setcapenablebit(ifp, IFCAP_TXCSUM, 0);
2146992fa62bSJustin Hibbits 				if_sethwassist(ifp, CSUM_IP | CSUM_TCP |
2147992fa62bSJustin Hibbits 					CSUM_UDP);
2148a8d7fc4aSZbigniew Bodek 			}
214973f20bb3SMarcin Wojtas 			/*
215073f20bb3SMarcin Wojtas 			 * Reinitialize RX queues.
215173f20bb3SMarcin Wojtas 			 * We need to update RX descriptor size.
215273f20bb3SMarcin Wojtas 			 */
2153992fa62bSJustin Hibbits 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
215497ecdc00SKornel Dulęba 				reinit = true;
21555438ef47SKornel Duleba 				mvneta_stop_locked(sc);
215697ecdc00SKornel Dulęba 			}
21575438ef47SKornel Duleba 
215873f20bb3SMarcin Wojtas 			for (q = 0; q < MVNETA_RX_QNUM_MAX; q++) {
215973f20bb3SMarcin Wojtas 				mvneta_rx_lockq(sc, q);
216073f20bb3SMarcin Wojtas 				if (mvneta_rx_queue_init(ifp, q) != 0) {
216173f20bb3SMarcin Wojtas 					device_printf(sc->dev,
216273f20bb3SMarcin Wojtas 					    "initialization failed:"
216373f20bb3SMarcin Wojtas 					    " cannot initialize queue\n");
216473f20bb3SMarcin Wojtas 					mvneta_rx_unlockq(sc, q);
216573f20bb3SMarcin Wojtas 					error = ENOBUFS;
216673f20bb3SMarcin Wojtas 					break;
216773f20bb3SMarcin Wojtas 				}
216873f20bb3SMarcin Wojtas 				mvneta_rx_unlockq(sc, q);
216973f20bb3SMarcin Wojtas 			}
217097ecdc00SKornel Dulęba 			if (reinit)
2171a8d7fc4aSZbigniew Bodek 				mvneta_init_locked(sc);
21725438ef47SKornel Duleba 
2173a8d7fc4aSZbigniew Bodek 			mvneta_sc_unlock(sc);
2174a8d7fc4aSZbigniew Bodek                 }
2175a8d7fc4aSZbigniew Bodek                 break;
2176a8d7fc4aSZbigniew Bodek 
2177a8d7fc4aSZbigniew Bodek 	default:
2178a8d7fc4aSZbigniew Bodek 		error = ether_ioctl(ifp, cmd, data);
2179a8d7fc4aSZbigniew Bodek 		break;
2180a8d7fc4aSZbigniew Bodek 	}
2181a8d7fc4aSZbigniew Bodek 
2182a8d7fc4aSZbigniew Bodek 	return (error);
2183a8d7fc4aSZbigniew Bodek }
2184a8d7fc4aSZbigniew Bodek 
2185a8d7fc4aSZbigniew Bodek STATIC void
2186a8d7fc4aSZbigniew Bodek mvneta_init_locked(void *arg)
2187a8d7fc4aSZbigniew Bodek {
2188a8d7fc4aSZbigniew Bodek 	struct mvneta_softc *sc;
2189992fa62bSJustin Hibbits 	if_t ifp;
2190a8d7fc4aSZbigniew Bodek 	uint32_t reg;
2191a8d7fc4aSZbigniew Bodek 	int q, cpu;
2192a8d7fc4aSZbigniew Bodek 
2193a8d7fc4aSZbigniew Bodek 	sc = arg;
2194a8d7fc4aSZbigniew Bodek 	ifp = sc->ifp;
2195a8d7fc4aSZbigniew Bodek 
2196a8d7fc4aSZbigniew Bodek 	if (!device_is_attached(sc->dev) ||
2197992fa62bSJustin Hibbits 	    (if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0)
2198a8d7fc4aSZbigniew Bodek 		return;
2199a8d7fc4aSZbigniew Bodek 
2200a8d7fc4aSZbigniew Bodek 	mvneta_disable_intr(sc);
2201a8d7fc4aSZbigniew Bodek 	callout_stop(&sc->tick_ch);
2202a8d7fc4aSZbigniew Bodek 
2203a8d7fc4aSZbigniew Bodek 	/* Get the latest mac address */
2204992fa62bSJustin Hibbits 	bcopy(if_getlladdr(ifp), sc->enaddr, ETHER_ADDR_LEN);
2205a8d7fc4aSZbigniew Bodek 	mvneta_set_mac_address(sc, sc->enaddr);
2206a8d7fc4aSZbigniew Bodek 	mvneta_filter_setup(sc);
2207a8d7fc4aSZbigniew Bodek 
2208a8d7fc4aSZbigniew Bodek 	/* Start DMA Engine */
2209a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PRXINIT, 0x00000000);
2210a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PTXINIT, 0x00000000);
2211a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PACC, MVNETA_PACC_ACCELERATIONMODE_EDM);
2212a8d7fc4aSZbigniew Bodek 
2213a8d7fc4aSZbigniew Bodek 	/* Enable port */
2214a8d7fc4aSZbigniew Bodek 	reg  = MVNETA_READ(sc, MVNETA_PMACC0);
2215a8d7fc4aSZbigniew Bodek 	reg |= MVNETA_PMACC0_PORTEN;
221673f20bb3SMarcin Wojtas 	reg &= ~MVNETA_PMACC0_FRAMESIZELIMIT_MASK;
2217992fa62bSJustin Hibbits 	reg |= MVNETA_PMACC0_FRAMESIZELIMIT(if_getmtu(ifp) + MVNETA_ETHER_SIZE);
2218a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PMACC0, reg);
2219a8d7fc4aSZbigniew Bodek 
2220a8d7fc4aSZbigniew Bodek 	/* Allow access to each TXQ/RXQ from both CPU's */
2221a8d7fc4aSZbigniew Bodek 	for (cpu = 0; cpu < mp_ncpus; ++cpu)
2222a8d7fc4aSZbigniew Bodek 		MVNETA_WRITE(sc, MVNETA_PCP2Q(cpu),
2223a8d7fc4aSZbigniew Bodek 		    MVNETA_PCP2Q_TXQEN_MASK | MVNETA_PCP2Q_RXQEN_MASK);
2224a8d7fc4aSZbigniew Bodek 
2225a8d7fc4aSZbigniew Bodek 	for (q = 0; q < MVNETA_RX_QNUM_MAX; q++) {
2226a8d7fc4aSZbigniew Bodek 		mvneta_rx_lockq(sc, q);
2227a8d7fc4aSZbigniew Bodek 		mvneta_rx_queue_refill(sc, q);
2228a8d7fc4aSZbigniew Bodek 		mvneta_rx_unlockq(sc, q);
2229a8d7fc4aSZbigniew Bodek 	}
2230a8d7fc4aSZbigniew Bodek 
2231a8d7fc4aSZbigniew Bodek 	if (!sc->phy_attached)
2232a8d7fc4aSZbigniew Bodek 		mvneta_linkup(sc);
2233a8d7fc4aSZbigniew Bodek 
2234a8d7fc4aSZbigniew Bodek 	/* Enable interrupt */
2235a8d7fc4aSZbigniew Bodek 	mvneta_enable_intr(sc);
2236a8d7fc4aSZbigniew Bodek 
2237a8d7fc4aSZbigniew Bodek 	/* Set Counter */
2238a8d7fc4aSZbigniew Bodek 	callout_schedule(&sc->tick_ch, hz);
2239a8d7fc4aSZbigniew Bodek 
2240992fa62bSJustin Hibbits 	if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
2241a8d7fc4aSZbigniew Bodek }
2242a8d7fc4aSZbigniew Bodek 
2243a8d7fc4aSZbigniew Bodek STATIC void
2244a8d7fc4aSZbigniew Bodek mvneta_init(void *arg)
2245a8d7fc4aSZbigniew Bodek {
2246a8d7fc4aSZbigniew Bodek 	struct mvneta_softc *sc;
2247a8d7fc4aSZbigniew Bodek 
2248a8d7fc4aSZbigniew Bodek 	sc = arg;
2249a8d7fc4aSZbigniew Bodek 	mvneta_sc_lock(sc);
2250a8d7fc4aSZbigniew Bodek 	mvneta_init_locked(sc);
2251a8d7fc4aSZbigniew Bodek 	if (sc->phy_attached)
2252a8d7fc4aSZbigniew Bodek 		mii_mediachg(sc->mii);
2253a8d7fc4aSZbigniew Bodek 	mvneta_sc_unlock(sc);
2254a8d7fc4aSZbigniew Bodek }
2255a8d7fc4aSZbigniew Bodek 
2256a8d7fc4aSZbigniew Bodek /* ARGSUSED */
2257a8d7fc4aSZbigniew Bodek STATIC void
2258a8d7fc4aSZbigniew Bodek mvneta_stop_locked(struct mvneta_softc *sc)
2259a8d7fc4aSZbigniew Bodek {
2260992fa62bSJustin Hibbits 	if_t ifp;
2261a8d7fc4aSZbigniew Bodek 	uint32_t reg;
2262a8d7fc4aSZbigniew Bodek 	int q;
2263a8d7fc4aSZbigniew Bodek 
2264a8d7fc4aSZbigniew Bodek 	ifp = sc->ifp;
2265992fa62bSJustin Hibbits 	if (ifp == NULL || (if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
2266a8d7fc4aSZbigniew Bodek 		return;
2267a8d7fc4aSZbigniew Bodek 
2268a8d7fc4aSZbigniew Bodek 	mvneta_disable_intr(sc);
2269a8d7fc4aSZbigniew Bodek 
2270a8d7fc4aSZbigniew Bodek 	callout_stop(&sc->tick_ch);
2271a8d7fc4aSZbigniew Bodek 
2272992fa62bSJustin Hibbits 	if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
2273a8d7fc4aSZbigniew Bodek 
2274a8d7fc4aSZbigniew Bodek 	/* Link down */
2275a8d7fc4aSZbigniew Bodek 	if (sc->linkup == TRUE)
2276a8d7fc4aSZbigniew Bodek 		mvneta_linkdown(sc);
2277a8d7fc4aSZbigniew Bodek 
2278a8d7fc4aSZbigniew Bodek 	/* Reset the MAC Port Enable bit */
2279a8d7fc4aSZbigniew Bodek 	reg = MVNETA_READ(sc, MVNETA_PMACC0);
2280a8d7fc4aSZbigniew Bodek 	reg &= ~MVNETA_PMACC0_PORTEN;
2281a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PMACC0, reg);
2282a8d7fc4aSZbigniew Bodek 
2283a8d7fc4aSZbigniew Bodek 	/* Disable each of queue */
2284a8d7fc4aSZbigniew Bodek 	for (q = 0; q < MVNETA_RX_QNUM_MAX; q++) {
2285a8d7fc4aSZbigniew Bodek 		mvneta_rx_lockq(sc, q);
2286a8d7fc4aSZbigniew Bodek 		mvneta_ring_flush_rx_queue(sc, q);
2287a8d7fc4aSZbigniew Bodek 		mvneta_rx_unlockq(sc, q);
2288a8d7fc4aSZbigniew Bodek 	}
2289a8d7fc4aSZbigniew Bodek 
2290a8d7fc4aSZbigniew Bodek 	/*
2291a8d7fc4aSZbigniew Bodek 	 * Hold Reset state of DMA Engine
2292a8d7fc4aSZbigniew Bodek 	 * (must write 0x0 to restart it)
2293a8d7fc4aSZbigniew Bodek 	 */
2294a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PRXINIT, 0x00000001);
2295a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PTXINIT, 0x00000001);
2296a8d7fc4aSZbigniew Bodek 
2297a8d7fc4aSZbigniew Bodek 	for (q = 0; q < MVNETA_TX_QNUM_MAX; q++) {
2298a8d7fc4aSZbigniew Bodek 		mvneta_tx_lockq(sc, q);
2299a8d7fc4aSZbigniew Bodek 		mvneta_ring_flush_tx_queue(sc, q);
2300a8d7fc4aSZbigniew Bodek 		mvneta_tx_unlockq(sc, q);
2301a8d7fc4aSZbigniew Bodek 	}
2302a8d7fc4aSZbigniew Bodek }
2303a8d7fc4aSZbigniew Bodek 
2304a8d7fc4aSZbigniew Bodek STATIC void
2305a8d7fc4aSZbigniew Bodek mvneta_stop(struct mvneta_softc *sc)
2306a8d7fc4aSZbigniew Bodek {
2307a8d7fc4aSZbigniew Bodek 
2308a8d7fc4aSZbigniew Bodek 	mvneta_sc_lock(sc);
2309a8d7fc4aSZbigniew Bodek 	mvneta_stop_locked(sc);
2310a8d7fc4aSZbigniew Bodek 	mvneta_sc_unlock(sc);
2311a8d7fc4aSZbigniew Bodek }
2312a8d7fc4aSZbigniew Bodek 
2313a8d7fc4aSZbigniew Bodek STATIC int
2314992fa62bSJustin Hibbits mvneta_mediachange(if_t ifp)
2315a8d7fc4aSZbigniew Bodek {
2316a8d7fc4aSZbigniew Bodek 	struct mvneta_softc *sc;
2317a8d7fc4aSZbigniew Bodek 
2318992fa62bSJustin Hibbits 	sc = if_getsoftc(ifp);
2319a8d7fc4aSZbigniew Bodek 
2320a8d7fc4aSZbigniew Bodek 	if (!sc->phy_attached && !sc->use_inband_status) {
2321a8d7fc4aSZbigniew Bodek 		/* We shouldn't be here */
2322a8d7fc4aSZbigniew Bodek 		if_printf(ifp, "Cannot change media in fixed-link mode!\n");
2323a8d7fc4aSZbigniew Bodek 		return (0);
2324a8d7fc4aSZbigniew Bodek 	}
2325a8d7fc4aSZbigniew Bodek 
2326a8d7fc4aSZbigniew Bodek 	if (sc->use_inband_status) {
2327a8d7fc4aSZbigniew Bodek 		mvneta_update_media(sc, sc->mvneta_ifmedia.ifm_media);
2328a8d7fc4aSZbigniew Bodek 		return (0);
2329a8d7fc4aSZbigniew Bodek 	}
2330a8d7fc4aSZbigniew Bodek 
2331a8d7fc4aSZbigniew Bodek 	mvneta_sc_lock(sc);
2332a8d7fc4aSZbigniew Bodek 
2333a8d7fc4aSZbigniew Bodek 	/* Update PHY */
2334a8d7fc4aSZbigniew Bodek 	mii_mediachg(sc->mii);
2335a8d7fc4aSZbigniew Bodek 
2336a8d7fc4aSZbigniew Bodek 	mvneta_sc_unlock(sc);
2337a8d7fc4aSZbigniew Bodek 
2338a8d7fc4aSZbigniew Bodek 	return (0);
2339a8d7fc4aSZbigniew Bodek }
2340a8d7fc4aSZbigniew Bodek 
2341a8d7fc4aSZbigniew Bodek STATIC void
2342a8d7fc4aSZbigniew Bodek mvneta_get_media(struct mvneta_softc *sc, struct ifmediareq *ifmr)
2343a8d7fc4aSZbigniew Bodek {
2344a8d7fc4aSZbigniew Bodek 	uint32_t psr;
2345a8d7fc4aSZbigniew Bodek 
2346a8d7fc4aSZbigniew Bodek 	psr = MVNETA_READ(sc, MVNETA_PSR);
2347a8d7fc4aSZbigniew Bodek 
2348a8d7fc4aSZbigniew Bodek 	/* Speed */
2349a8d7fc4aSZbigniew Bodek 	if (psr & MVNETA_PSR_GMIISPEED)
2350a8d7fc4aSZbigniew Bodek 		ifmr->ifm_active = IFM_ETHER_SUBTYPE_SET(IFM_1000_T);
2351a8d7fc4aSZbigniew Bodek 	else if (psr & MVNETA_PSR_MIISPEED)
2352a8d7fc4aSZbigniew Bodek 		ifmr->ifm_active = IFM_ETHER_SUBTYPE_SET(IFM_100_TX);
2353a8d7fc4aSZbigniew Bodek 	else if (psr & MVNETA_PSR_LINKUP)
2354a8d7fc4aSZbigniew Bodek 		ifmr->ifm_active = IFM_ETHER_SUBTYPE_SET(IFM_10_T);
2355a8d7fc4aSZbigniew Bodek 
2356a8d7fc4aSZbigniew Bodek 	/* Duplex */
2357a8d7fc4aSZbigniew Bodek 	if (psr & MVNETA_PSR_FULLDX)
2358a8d7fc4aSZbigniew Bodek 		ifmr->ifm_active |= IFM_FDX;
2359a8d7fc4aSZbigniew Bodek 
2360a8d7fc4aSZbigniew Bodek 	/* Link */
2361a8d7fc4aSZbigniew Bodek 	ifmr->ifm_status = IFM_AVALID;
2362a8d7fc4aSZbigniew Bodek 	if (psr & MVNETA_PSR_LINKUP)
2363a8d7fc4aSZbigniew Bodek 		ifmr->ifm_status |= IFM_ACTIVE;
2364a8d7fc4aSZbigniew Bodek }
2365a8d7fc4aSZbigniew Bodek 
2366a8d7fc4aSZbigniew Bodek STATIC void
2367992fa62bSJustin Hibbits mvneta_mediastatus(if_t ifp, struct ifmediareq *ifmr)
2368a8d7fc4aSZbigniew Bodek {
2369a8d7fc4aSZbigniew Bodek 	struct mvneta_softc *sc;
2370a8d7fc4aSZbigniew Bodek 	struct mii_data *mii;
2371a8d7fc4aSZbigniew Bodek 
2372992fa62bSJustin Hibbits 	sc = if_getsoftc(ifp);
2373a8d7fc4aSZbigniew Bodek 
2374a8d7fc4aSZbigniew Bodek 	if (!sc->phy_attached && !sc->use_inband_status) {
2375a8d7fc4aSZbigniew Bodek 		ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE;
2376a8d7fc4aSZbigniew Bodek 		return;
2377a8d7fc4aSZbigniew Bodek 	}
2378a8d7fc4aSZbigniew Bodek 
2379a8d7fc4aSZbigniew Bodek 	mvneta_sc_lock(sc);
2380a8d7fc4aSZbigniew Bodek 
2381a8d7fc4aSZbigniew Bodek 	if (sc->use_inband_status) {
2382a8d7fc4aSZbigniew Bodek 		mvneta_get_media(sc, ifmr);
2383a8d7fc4aSZbigniew Bodek 		mvneta_sc_unlock(sc);
2384a8d7fc4aSZbigniew Bodek 		return;
2385a8d7fc4aSZbigniew Bodek 	}
2386a8d7fc4aSZbigniew Bodek 
2387a8d7fc4aSZbigniew Bodek 	mii = sc->mii;
2388a8d7fc4aSZbigniew Bodek 	mii_pollstat(mii);
2389a8d7fc4aSZbigniew Bodek 
2390a8d7fc4aSZbigniew Bodek 	ifmr->ifm_active = mii->mii_media_active;
2391a8d7fc4aSZbigniew Bodek 	ifmr->ifm_status = mii->mii_media_status;
2392a8d7fc4aSZbigniew Bodek 
2393a8d7fc4aSZbigniew Bodek 	mvneta_sc_unlock(sc);
2394a8d7fc4aSZbigniew Bodek }
2395a8d7fc4aSZbigniew Bodek 
2396a8d7fc4aSZbigniew Bodek /*
2397a8d7fc4aSZbigniew Bodek  * Link State Notify
2398a8d7fc4aSZbigniew Bodek  */
2399a8d7fc4aSZbigniew Bodek STATIC void
2400a8d7fc4aSZbigniew Bodek mvneta_update_autoneg(struct mvneta_softc *sc, int enable)
2401a8d7fc4aSZbigniew Bodek {
2402a8d7fc4aSZbigniew Bodek 	int reg;
2403a8d7fc4aSZbigniew Bodek 
2404a8d7fc4aSZbigniew Bodek 	if (enable) {
2405a8d7fc4aSZbigniew Bodek 		reg = MVNETA_READ(sc, MVNETA_PANC);
2406a8d7fc4aSZbigniew Bodek 		reg &= ~(MVNETA_PANC_FORCELINKFAIL | MVNETA_PANC_FORCELINKPASS |
2407a8d7fc4aSZbigniew Bodek 		    MVNETA_PANC_ANFCEN);
2408a8d7fc4aSZbigniew Bodek 		reg |= MVNETA_PANC_ANDUPLEXEN | MVNETA_PANC_ANSPEEDEN |
2409a8d7fc4aSZbigniew Bodek 		    MVNETA_PANC_INBANDANEN;
2410a8d7fc4aSZbigniew Bodek 		MVNETA_WRITE(sc, MVNETA_PANC, reg);
2411a8d7fc4aSZbigniew Bodek 
2412a8d7fc4aSZbigniew Bodek 		reg = MVNETA_READ(sc, MVNETA_PMACC2);
2413a8d7fc4aSZbigniew Bodek 		reg |= MVNETA_PMACC2_INBANDANMODE;
2414a8d7fc4aSZbigniew Bodek 		MVNETA_WRITE(sc, MVNETA_PMACC2, reg);
2415a8d7fc4aSZbigniew Bodek 
2416a8d7fc4aSZbigniew Bodek 		reg = MVNETA_READ(sc, MVNETA_PSOMSCD);
2417a8d7fc4aSZbigniew Bodek 		reg |= MVNETA_PSOMSCD_ENABLE;
2418a8d7fc4aSZbigniew Bodek 		MVNETA_WRITE(sc, MVNETA_PSOMSCD, reg);
2419a8d7fc4aSZbigniew Bodek 	} else {
2420a8d7fc4aSZbigniew Bodek 		reg = MVNETA_READ(sc, MVNETA_PANC);
2421a8d7fc4aSZbigniew Bodek 		reg &= ~(MVNETA_PANC_FORCELINKFAIL | MVNETA_PANC_FORCELINKPASS |
2422a8d7fc4aSZbigniew Bodek 		    MVNETA_PANC_ANDUPLEXEN | MVNETA_PANC_ANSPEEDEN |
2423a8d7fc4aSZbigniew Bodek 		    MVNETA_PANC_INBANDANEN);
2424a8d7fc4aSZbigniew Bodek 		MVNETA_WRITE(sc, MVNETA_PANC, reg);
2425a8d7fc4aSZbigniew Bodek 
2426a8d7fc4aSZbigniew Bodek 		reg = MVNETA_READ(sc, MVNETA_PMACC2);
2427a8d7fc4aSZbigniew Bodek 		reg &= ~MVNETA_PMACC2_INBANDANMODE;
2428a8d7fc4aSZbigniew Bodek 		MVNETA_WRITE(sc, MVNETA_PMACC2, reg);
2429a8d7fc4aSZbigniew Bodek 
2430a8d7fc4aSZbigniew Bodek 		reg = MVNETA_READ(sc, MVNETA_PSOMSCD);
2431a8d7fc4aSZbigniew Bodek 		reg &= ~MVNETA_PSOMSCD_ENABLE;
2432a8d7fc4aSZbigniew Bodek 		MVNETA_WRITE(sc, MVNETA_PSOMSCD, reg);
2433a8d7fc4aSZbigniew Bodek 	}
2434a8d7fc4aSZbigniew Bodek }
2435a8d7fc4aSZbigniew Bodek 
2436a8d7fc4aSZbigniew Bodek STATIC int
2437a8d7fc4aSZbigniew Bodek mvneta_update_media(struct mvneta_softc *sc, int media)
2438a8d7fc4aSZbigniew Bodek {
2439a8d7fc4aSZbigniew Bodek 	int reg, err;
2440a8d7fc4aSZbigniew Bodek 	boolean_t running;
2441a8d7fc4aSZbigniew Bodek 
2442a8d7fc4aSZbigniew Bodek 	err = 0;
2443a8d7fc4aSZbigniew Bodek 
2444a8d7fc4aSZbigniew Bodek 	mvneta_sc_lock(sc);
2445a8d7fc4aSZbigniew Bodek 
2446a8d7fc4aSZbigniew Bodek 	mvneta_linkreset(sc);
2447a8d7fc4aSZbigniew Bodek 
2448992fa62bSJustin Hibbits 	running = (if_getdrvflags(sc->ifp) & IFF_DRV_RUNNING) != 0;
2449a8d7fc4aSZbigniew Bodek 	if (running)
2450a8d7fc4aSZbigniew Bodek 		mvneta_stop_locked(sc);
2451a8d7fc4aSZbigniew Bodek 
2452a8d7fc4aSZbigniew Bodek 	sc->autoneg = (IFM_SUBTYPE(media) == IFM_AUTO);
2453a8d7fc4aSZbigniew Bodek 
2454231237bbSSebastien Bini 	if (!sc->phy_attached || sc->use_inband_status)
2455a8d7fc4aSZbigniew Bodek 		mvneta_update_autoneg(sc, IFM_SUBTYPE(media) == IFM_AUTO);
2456a8d7fc4aSZbigniew Bodek 
2457a8d7fc4aSZbigniew Bodek 	mvneta_update_eee(sc);
2458a8d7fc4aSZbigniew Bodek 	mvneta_update_fc(sc);
2459a8d7fc4aSZbigniew Bodek 
2460a8d7fc4aSZbigniew Bodek 	if (IFM_SUBTYPE(media) != IFM_AUTO) {
2461a8d7fc4aSZbigniew Bodek 		reg = MVNETA_READ(sc, MVNETA_PANC);
2462a8d7fc4aSZbigniew Bodek 		reg &= ~(MVNETA_PANC_SETGMIISPEED |
2463a8d7fc4aSZbigniew Bodek 		    MVNETA_PANC_SETMIISPEED |
2464a8d7fc4aSZbigniew Bodek 		    MVNETA_PANC_SETFULLDX);
2465a8d7fc4aSZbigniew Bodek 		if (IFM_SUBTYPE(media) == IFM_1000_T ||
2466a8d7fc4aSZbigniew Bodek 		    IFM_SUBTYPE(media) == IFM_2500_T) {
2467a8d7fc4aSZbigniew Bodek 			if ((media & IFM_FDX) == 0) {
2468a8d7fc4aSZbigniew Bodek 				device_printf(sc->dev,
2469a8d7fc4aSZbigniew Bodek 				    "%s half-duplex unsupported\n",
2470a8d7fc4aSZbigniew Bodek 				    IFM_SUBTYPE(media) == IFM_1000_T ?
2471a8d7fc4aSZbigniew Bodek 				    "1000Base-T" :
2472a8d7fc4aSZbigniew Bodek 				    "2500Base-T");
2473a8d7fc4aSZbigniew Bodek 				err = EINVAL;
2474a8d7fc4aSZbigniew Bodek 				goto out;
2475a8d7fc4aSZbigniew Bodek 			}
2476a8d7fc4aSZbigniew Bodek 			reg |= MVNETA_PANC_SETGMIISPEED;
2477a8d7fc4aSZbigniew Bodek 		} else if (IFM_SUBTYPE(media) == IFM_100_TX)
2478a8d7fc4aSZbigniew Bodek 			reg |= MVNETA_PANC_SETMIISPEED;
2479a8d7fc4aSZbigniew Bodek 
2480a8d7fc4aSZbigniew Bodek 		if (media & IFM_FDX)
2481a8d7fc4aSZbigniew Bodek 			reg |= MVNETA_PANC_SETFULLDX;
2482a8d7fc4aSZbigniew Bodek 
2483a8d7fc4aSZbigniew Bodek 		MVNETA_WRITE(sc, MVNETA_PANC, reg);
2484a8d7fc4aSZbigniew Bodek 	}
2485a8d7fc4aSZbigniew Bodek out:
2486a8d7fc4aSZbigniew Bodek 	if (running)
2487a8d7fc4aSZbigniew Bodek 		mvneta_init_locked(sc);
2488a8d7fc4aSZbigniew Bodek 	mvneta_sc_unlock(sc);
2489a8d7fc4aSZbigniew Bodek 	return (err);
2490a8d7fc4aSZbigniew Bodek }
2491a8d7fc4aSZbigniew Bodek 
2492a8d7fc4aSZbigniew Bodek STATIC void
2493a8d7fc4aSZbigniew Bodek mvneta_adjust_link(struct mvneta_softc *sc)
2494a8d7fc4aSZbigniew Bodek {
2495a8d7fc4aSZbigniew Bodek 	boolean_t phy_linkup;
2496a8d7fc4aSZbigniew Bodek 	int reg;
2497a8d7fc4aSZbigniew Bodek 
2498a8d7fc4aSZbigniew Bodek 	/* Update eee/fc */
2499a8d7fc4aSZbigniew Bodek 	mvneta_update_eee(sc);
2500a8d7fc4aSZbigniew Bodek 	mvneta_update_fc(sc);
2501a8d7fc4aSZbigniew Bodek 
2502a8d7fc4aSZbigniew Bodek 	/* Check for link change */
2503a8d7fc4aSZbigniew Bodek 	phy_linkup = (sc->mii->mii_media_status &
2504a8d7fc4aSZbigniew Bodek 	    (IFM_AVALID | IFM_ACTIVE)) == (IFM_AVALID | IFM_ACTIVE);
2505a8d7fc4aSZbigniew Bodek 
2506a8d7fc4aSZbigniew Bodek 	if (sc->linkup != phy_linkup)
2507a8d7fc4aSZbigniew Bodek 		mvneta_linkupdate(sc, phy_linkup);
2508a8d7fc4aSZbigniew Bodek 
2509a8d7fc4aSZbigniew Bodek 	/* Don't update media on disabled link */
2510a8d7fc4aSZbigniew Bodek 	if (!phy_linkup)
2511a8d7fc4aSZbigniew Bodek 		return;
2512a8d7fc4aSZbigniew Bodek 
2513a8d7fc4aSZbigniew Bodek 	/* Check for media type change */
2514a8d7fc4aSZbigniew Bodek 	if (sc->mvneta_media != sc->mii->mii_media_active) {
2515a8d7fc4aSZbigniew Bodek 		sc->mvneta_media = sc->mii->mii_media_active;
2516a8d7fc4aSZbigniew Bodek 
2517a8d7fc4aSZbigniew Bodek 		reg = MVNETA_READ(sc, MVNETA_PANC);
2518a8d7fc4aSZbigniew Bodek 		reg &= ~(MVNETA_PANC_SETGMIISPEED |
2519a8d7fc4aSZbigniew Bodek 		    MVNETA_PANC_SETMIISPEED |
2520a8d7fc4aSZbigniew Bodek 		    MVNETA_PANC_SETFULLDX);
2521a8d7fc4aSZbigniew Bodek 		if (IFM_SUBTYPE(sc->mvneta_media) == IFM_1000_T ||
2522a8d7fc4aSZbigniew Bodek 		    IFM_SUBTYPE(sc->mvneta_media) == IFM_2500_T) {
2523a8d7fc4aSZbigniew Bodek 			reg |= MVNETA_PANC_SETGMIISPEED;
2524a8d7fc4aSZbigniew Bodek 		} else if (IFM_SUBTYPE(sc->mvneta_media) == IFM_100_TX)
2525a8d7fc4aSZbigniew Bodek 			reg |= MVNETA_PANC_SETMIISPEED;
2526a8d7fc4aSZbigniew Bodek 
2527a8d7fc4aSZbigniew Bodek 		if (sc->mvneta_media & IFM_FDX)
2528a8d7fc4aSZbigniew Bodek 			reg |= MVNETA_PANC_SETFULLDX;
2529a8d7fc4aSZbigniew Bodek 
2530a8d7fc4aSZbigniew Bodek 		MVNETA_WRITE(sc, MVNETA_PANC, reg);
2531a8d7fc4aSZbigniew Bodek 	}
2532a8d7fc4aSZbigniew Bodek }
2533a8d7fc4aSZbigniew Bodek 
2534a8d7fc4aSZbigniew Bodek STATIC void
2535a8d7fc4aSZbigniew Bodek mvneta_link_isr(struct mvneta_softc *sc)
2536a8d7fc4aSZbigniew Bodek {
2537a8d7fc4aSZbigniew Bodek 	int linkup;
2538a8d7fc4aSZbigniew Bodek 
2539a8d7fc4aSZbigniew Bodek 	KASSERT_SC_MTX(sc);
2540a8d7fc4aSZbigniew Bodek 
2541a8d7fc4aSZbigniew Bodek 	linkup = MVNETA_IS_LINKUP(sc) ? TRUE : FALSE;
2542a8d7fc4aSZbigniew Bodek 	if (sc->linkup == linkup)
2543a8d7fc4aSZbigniew Bodek 		return;
2544a8d7fc4aSZbigniew Bodek 
2545a8d7fc4aSZbigniew Bodek 	if (linkup == TRUE)
2546a8d7fc4aSZbigniew Bodek 		mvneta_linkup(sc);
2547a8d7fc4aSZbigniew Bodek 	else
2548a8d7fc4aSZbigniew Bodek 		mvneta_linkdown(sc);
2549a8d7fc4aSZbigniew Bodek 
2550a8d7fc4aSZbigniew Bodek #ifdef DEBUG
2551b831f9ceSHubert Mazur 	device_printf(sc->dev,
2552992fa62bSJustin Hibbits 	    "%s: link %s\n", if_name(sc->ifp), linkup ? "up" : "down");
2553a8d7fc4aSZbigniew Bodek #endif
2554a8d7fc4aSZbigniew Bodek }
2555a8d7fc4aSZbigniew Bodek 
2556a8d7fc4aSZbigniew Bodek STATIC void
2557a8d7fc4aSZbigniew Bodek mvneta_linkupdate(struct mvneta_softc *sc, boolean_t linkup)
2558a8d7fc4aSZbigniew Bodek {
2559a8d7fc4aSZbigniew Bodek 
2560a8d7fc4aSZbigniew Bodek 	KASSERT_SC_MTX(sc);
2561a8d7fc4aSZbigniew Bodek 
2562a8d7fc4aSZbigniew Bodek 	if (linkup == TRUE)
2563a8d7fc4aSZbigniew Bodek 		mvneta_linkup(sc);
2564a8d7fc4aSZbigniew Bodek 	else
2565a8d7fc4aSZbigniew Bodek 		mvneta_linkdown(sc);
2566a8d7fc4aSZbigniew Bodek 
2567a8d7fc4aSZbigniew Bodek #ifdef DEBUG
2568b831f9ceSHubert Mazur 	device_printf(sc->dev,
2569992fa62bSJustin Hibbits 	    "%s: link %s\n", if_name(sc->ifp), linkup ? "up" : "down");
2570a8d7fc4aSZbigniew Bodek #endif
2571a8d7fc4aSZbigniew Bodek }
2572a8d7fc4aSZbigniew Bodek 
2573a8d7fc4aSZbigniew Bodek STATIC void
2574a8d7fc4aSZbigniew Bodek mvneta_update_eee(struct mvneta_softc *sc)
2575a8d7fc4aSZbigniew Bodek {
2576a8d7fc4aSZbigniew Bodek 	uint32_t reg;
2577a8d7fc4aSZbigniew Bodek 
2578a8d7fc4aSZbigniew Bodek 	KASSERT_SC_MTX(sc);
2579a8d7fc4aSZbigniew Bodek 
2580a8d7fc4aSZbigniew Bodek 	/* set EEE parameters */
2581a8d7fc4aSZbigniew Bodek 	reg = MVNETA_READ(sc, MVNETA_LPIC1);
2582a8d7fc4aSZbigniew Bodek 	if (sc->cf_lpi)
2583a8d7fc4aSZbigniew Bodek 		reg |= MVNETA_LPIC1_LPIRE;
2584a8d7fc4aSZbigniew Bodek 	else
2585a8d7fc4aSZbigniew Bodek 		reg &= ~MVNETA_LPIC1_LPIRE;
2586a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_LPIC1, reg);
2587a8d7fc4aSZbigniew Bodek }
2588a8d7fc4aSZbigniew Bodek 
2589a8d7fc4aSZbigniew Bodek STATIC void
2590a8d7fc4aSZbigniew Bodek mvneta_update_fc(struct mvneta_softc *sc)
2591a8d7fc4aSZbigniew Bodek {
2592a8d7fc4aSZbigniew Bodek 	uint32_t reg;
2593a8d7fc4aSZbigniew Bodek 
2594a8d7fc4aSZbigniew Bodek 	KASSERT_SC_MTX(sc);
2595a8d7fc4aSZbigniew Bodek 
2596a8d7fc4aSZbigniew Bodek 	reg  = MVNETA_READ(sc, MVNETA_PANC);
2597a8d7fc4aSZbigniew Bodek 	if (sc->cf_fc) {
2598a8d7fc4aSZbigniew Bodek 		/* Flow control negotiation */
2599a8d7fc4aSZbigniew Bodek 		reg |= MVNETA_PANC_PAUSEADV;
2600a8d7fc4aSZbigniew Bodek 		reg |= MVNETA_PANC_ANFCEN;
2601a8d7fc4aSZbigniew Bodek 	} else {
2602a8d7fc4aSZbigniew Bodek 		/* Disable flow control negotiation */
2603a8d7fc4aSZbigniew Bodek 		reg &= ~MVNETA_PANC_PAUSEADV;
2604a8d7fc4aSZbigniew Bodek 		reg &= ~MVNETA_PANC_ANFCEN;
2605a8d7fc4aSZbigniew Bodek 	}
2606a8d7fc4aSZbigniew Bodek 
2607a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PANC, reg);
2608a8d7fc4aSZbigniew Bodek }
2609a8d7fc4aSZbigniew Bodek 
2610a8d7fc4aSZbigniew Bodek STATIC void
2611a8d7fc4aSZbigniew Bodek mvneta_linkup(struct mvneta_softc *sc)
2612a8d7fc4aSZbigniew Bodek {
2613a8d7fc4aSZbigniew Bodek 	uint32_t reg;
2614a8d7fc4aSZbigniew Bodek 
2615a8d7fc4aSZbigniew Bodek 	KASSERT_SC_MTX(sc);
2616a8d7fc4aSZbigniew Bodek 
2617231237bbSSebastien Bini 	if (!sc->phy_attached || !sc->use_inband_status) {
2618a8d7fc4aSZbigniew Bodek 		reg  = MVNETA_READ(sc, MVNETA_PANC);
2619a8d7fc4aSZbigniew Bodek 		reg |= MVNETA_PANC_FORCELINKPASS;
2620a8d7fc4aSZbigniew Bodek 		reg &= ~MVNETA_PANC_FORCELINKFAIL;
2621a8d7fc4aSZbigniew Bodek 		MVNETA_WRITE(sc, MVNETA_PANC, reg);
2622a8d7fc4aSZbigniew Bodek 	}
2623a8d7fc4aSZbigniew Bodek 
2624a8d7fc4aSZbigniew Bodek 	mvneta_qflush(sc->ifp);
2625a8d7fc4aSZbigniew Bodek 	mvneta_portup(sc);
2626a8d7fc4aSZbigniew Bodek 	sc->linkup = TRUE;
2627a8d7fc4aSZbigniew Bodek 	if_link_state_change(sc->ifp, LINK_STATE_UP);
2628a8d7fc4aSZbigniew Bodek }
2629a8d7fc4aSZbigniew Bodek 
2630a8d7fc4aSZbigniew Bodek STATIC void
2631a8d7fc4aSZbigniew Bodek mvneta_linkdown(struct mvneta_softc *sc)
2632a8d7fc4aSZbigniew Bodek {
2633a8d7fc4aSZbigniew Bodek 	uint32_t reg;
2634a8d7fc4aSZbigniew Bodek 
2635a8d7fc4aSZbigniew Bodek 	KASSERT_SC_MTX(sc);
2636a8d7fc4aSZbigniew Bodek 
2637231237bbSSebastien Bini 	if (!sc->phy_attached || !sc->use_inband_status) {
2638a8d7fc4aSZbigniew Bodek 		reg  = MVNETA_READ(sc, MVNETA_PANC);
2639a8d7fc4aSZbigniew Bodek 		reg &= ~MVNETA_PANC_FORCELINKPASS;
2640a8d7fc4aSZbigniew Bodek 		reg |= MVNETA_PANC_FORCELINKFAIL;
2641a8d7fc4aSZbigniew Bodek 		MVNETA_WRITE(sc, MVNETA_PANC, reg);
2642a8d7fc4aSZbigniew Bodek 	}
2643a8d7fc4aSZbigniew Bodek 
2644a8d7fc4aSZbigniew Bodek 	mvneta_portdown(sc);
2645a8d7fc4aSZbigniew Bodek 	mvneta_qflush(sc->ifp);
2646a8d7fc4aSZbigniew Bodek 	sc->linkup = FALSE;
2647a8d7fc4aSZbigniew Bodek 	if_link_state_change(sc->ifp, LINK_STATE_DOWN);
2648a8d7fc4aSZbigniew Bodek }
2649a8d7fc4aSZbigniew Bodek 
2650a8d7fc4aSZbigniew Bodek STATIC void
2651a8d7fc4aSZbigniew Bodek mvneta_linkreset(struct mvneta_softc *sc)
2652a8d7fc4aSZbigniew Bodek {
2653a8d7fc4aSZbigniew Bodek 	struct mii_softc *mii;
2654a8d7fc4aSZbigniew Bodek 
2655a8d7fc4aSZbigniew Bodek 	if (sc->phy_attached) {
2656a8d7fc4aSZbigniew Bodek 		/* Force reset PHY */
2657a8d7fc4aSZbigniew Bodek 		mii = LIST_FIRST(&sc->mii->mii_phys);
2658a8d7fc4aSZbigniew Bodek 		if (mii)
2659a8d7fc4aSZbigniew Bodek 			mii_phy_reset(mii);
2660a8d7fc4aSZbigniew Bodek 	}
2661a8d7fc4aSZbigniew Bodek }
2662a8d7fc4aSZbigniew Bodek 
2663a8d7fc4aSZbigniew Bodek /*
2664a8d7fc4aSZbigniew Bodek  * Tx Subroutines
2665a8d7fc4aSZbigniew Bodek  */
2666a8d7fc4aSZbigniew Bodek STATIC int
2667a8d7fc4aSZbigniew Bodek mvneta_tx_queue(struct mvneta_softc *sc, struct mbuf **mbufp, int q)
2668a8d7fc4aSZbigniew Bodek {
2669992fa62bSJustin Hibbits 	if_t ifp;
2670a8d7fc4aSZbigniew Bodek 	bus_dma_segment_t txsegs[MVNETA_TX_SEGLIMIT];
2671a8d7fc4aSZbigniew Bodek 	struct mbuf *mtmp, *mbuf;
2672a8d7fc4aSZbigniew Bodek 	struct mvneta_tx_ring *tx;
2673a8d7fc4aSZbigniew Bodek 	struct mvneta_buf *txbuf;
2674a8d7fc4aSZbigniew Bodek 	struct mvneta_tx_desc *t;
2675a8d7fc4aSZbigniew Bodek 	uint32_t ptxsu;
267627f889cfSJohn Baldwin 	int used, error, i, txnsegs;
2677a8d7fc4aSZbigniew Bodek 
2678a8d7fc4aSZbigniew Bodek 	mbuf = *mbufp;
2679a8d7fc4aSZbigniew Bodek 	tx = MVNETA_TX_RING(sc, q);
2680a8d7fc4aSZbigniew Bodek 	DASSERT(tx->used >= 0);
2681a8d7fc4aSZbigniew Bodek 	DASSERT(tx->used <= MVNETA_TX_RING_CNT);
2682a8d7fc4aSZbigniew Bodek 	t = NULL;
2683a8d7fc4aSZbigniew Bodek 	ifp = sc->ifp;
2684a8d7fc4aSZbigniew Bodek 
2685a8d7fc4aSZbigniew Bodek 	if (__predict_false(mbuf->m_flags & M_VLANTAG)) {
2686a8d7fc4aSZbigniew Bodek 		mbuf = ether_vlanencap(mbuf, mbuf->m_pkthdr.ether_vtag);
2687a8d7fc4aSZbigniew Bodek 		if (mbuf == NULL) {
2688a8d7fc4aSZbigniew Bodek 			tx->drv_error++;
2689a8d7fc4aSZbigniew Bodek 			*mbufp = NULL;
2690a8d7fc4aSZbigniew Bodek 			return (ENOBUFS);
2691a8d7fc4aSZbigniew Bodek 		}
2692a8d7fc4aSZbigniew Bodek 		mbuf->m_flags &= ~M_VLANTAG;
2693a8d7fc4aSZbigniew Bodek 		*mbufp = mbuf;
2694a8d7fc4aSZbigniew Bodek 	}
2695a8d7fc4aSZbigniew Bodek 
2696a8d7fc4aSZbigniew Bodek 	if (__predict_false(mbuf->m_next != NULL &&
2697a8d7fc4aSZbigniew Bodek 	    (mbuf->m_pkthdr.csum_flags &
2698a8d7fc4aSZbigniew Bodek 	    (CSUM_IP | CSUM_TCP | CSUM_UDP)) != 0)) {
2699a8d7fc4aSZbigniew Bodek 		if (M_WRITABLE(mbuf) == 0) {
2700a8d7fc4aSZbigniew Bodek 			mtmp = m_dup(mbuf, M_NOWAIT);
2701a8d7fc4aSZbigniew Bodek 			m_freem(mbuf);
2702a8d7fc4aSZbigniew Bodek 			if (mtmp == NULL) {
2703a8d7fc4aSZbigniew Bodek 				tx->drv_error++;
2704a8d7fc4aSZbigniew Bodek 				*mbufp = NULL;
2705a8d7fc4aSZbigniew Bodek 				return (ENOBUFS);
2706a8d7fc4aSZbigniew Bodek 			}
2707a8d7fc4aSZbigniew Bodek 			*mbufp = mbuf = mtmp;
2708a8d7fc4aSZbigniew Bodek 		}
2709a8d7fc4aSZbigniew Bodek 	}
2710a8d7fc4aSZbigniew Bodek 
2711a8d7fc4aSZbigniew Bodek 	/* load mbuf using dmamap of 1st descriptor */
2712a8d7fc4aSZbigniew Bodek 	txbuf = &tx->txbuf[tx->cpu];
2713a8d7fc4aSZbigniew Bodek 	error = bus_dmamap_load_mbuf_sg(sc->txmbuf_dtag,
2714a8d7fc4aSZbigniew Bodek 	    txbuf->dmap, mbuf, txsegs, &txnsegs,
2715a8d7fc4aSZbigniew Bodek 	    BUS_DMA_NOWAIT);
2716a8d7fc4aSZbigniew Bodek 	if (__predict_false(error != 0)) {
2717a8d7fc4aSZbigniew Bodek #ifdef MVNETA_KTR
2718992fa62bSJustin Hibbits 		CTR3(KTR_SPARE2, "%s:%u bus_dmamap_load_mbuf_sg error=%d", if_name(ifp), q, error);
2719a8d7fc4aSZbigniew Bodek #endif
2720a8d7fc4aSZbigniew Bodek 		/* This is the only recoverable error (except EFBIG). */
2721a8d7fc4aSZbigniew Bodek 		if (error != ENOMEM) {
2722a8d7fc4aSZbigniew Bodek 			tx->drv_error++;
2723a8d7fc4aSZbigniew Bodek 			m_freem(mbuf);
2724a8d7fc4aSZbigniew Bodek 			*mbufp = NULL;
2725a8d7fc4aSZbigniew Bodek 			return (ENOBUFS);
2726a8d7fc4aSZbigniew Bodek 		}
2727a8d7fc4aSZbigniew Bodek 		return (error);
2728a8d7fc4aSZbigniew Bodek 	}
2729a8d7fc4aSZbigniew Bodek 
2730a8d7fc4aSZbigniew Bodek 	if (__predict_false(txnsegs <= 0
2731a8d7fc4aSZbigniew Bodek 	    || (txnsegs + tx->used) > MVNETA_TX_RING_CNT)) {
2732a8d7fc4aSZbigniew Bodek 		/* we have no enough descriptors or mbuf is broken */
2733a8d7fc4aSZbigniew Bodek #ifdef MVNETA_KTR
2734a8d7fc4aSZbigniew Bodek 		CTR3(KTR_SPARE2, "%s:%u not enough descriptors txnsegs=%d",
2735992fa62bSJustin Hibbits 		    if_name(ifp), q, txnsegs);
2736a8d7fc4aSZbigniew Bodek #endif
2737a8d7fc4aSZbigniew Bodek 		bus_dmamap_unload(sc->txmbuf_dtag, txbuf->dmap);
2738a8d7fc4aSZbigniew Bodek 		return (ENOBUFS);
2739a8d7fc4aSZbigniew Bodek 	}
2740a8d7fc4aSZbigniew Bodek 	DASSERT(txbuf->m == NULL);
2741a8d7fc4aSZbigniew Bodek 
2742a8d7fc4aSZbigniew Bodek 	/* remember mbuf using 1st descriptor */
2743a8d7fc4aSZbigniew Bodek 	txbuf->m = mbuf;
2744a8d7fc4aSZbigniew Bodek 	bus_dmamap_sync(sc->txmbuf_dtag, txbuf->dmap,
2745a8d7fc4aSZbigniew Bodek 	    BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
2746a8d7fc4aSZbigniew Bodek 
2747a8d7fc4aSZbigniew Bodek 	/* load to tx descriptors */
2748a8d7fc4aSZbigniew Bodek 	used = 0;
2749a8d7fc4aSZbigniew Bodek 	for (i = 0; i < txnsegs; i++) {
2750a8d7fc4aSZbigniew Bodek 		t = &tx->desc[tx->cpu];
2751a8d7fc4aSZbigniew Bodek 		t->command = 0;
2752a8d7fc4aSZbigniew Bodek 		t->l4ichk = 0;
2753a8d7fc4aSZbigniew Bodek 		t->flags = 0;
2754a8d7fc4aSZbigniew Bodek 		if (__predict_true(i == 0)) {
2755a8d7fc4aSZbigniew Bodek 			/* 1st descriptor */
2756a8d7fc4aSZbigniew Bodek 			t->command |= MVNETA_TX_CMD_W_PACKET_OFFSET(0);
2757a8d7fc4aSZbigniew Bodek 			t->command |= MVNETA_TX_CMD_F;
2758a8d7fc4aSZbigniew Bodek 			mvneta_tx_set_csumflag(ifp, t, mbuf);
2759a8d7fc4aSZbigniew Bodek 		}
2760a8d7fc4aSZbigniew Bodek 		t->bufptr_pa = txsegs[i].ds_addr;
2761a8d7fc4aSZbigniew Bodek 		t->bytecnt = txsegs[i].ds_len;
2762a8d7fc4aSZbigniew Bodek 		tx->cpu = tx_counter_adv(tx->cpu, 1);
2763a8d7fc4aSZbigniew Bodek 
2764a8d7fc4aSZbigniew Bodek 		tx->used++;
2765a8d7fc4aSZbigniew Bodek 		used++;
2766a8d7fc4aSZbigniew Bodek 	}
2767a8d7fc4aSZbigniew Bodek 	/* t is last descriptor here */
2768a8d7fc4aSZbigniew Bodek 	DASSERT(t != NULL);
2769a8d7fc4aSZbigniew Bodek 	t->command |= MVNETA_TX_CMD_L|MVNETA_TX_CMD_PADDING;
2770a8d7fc4aSZbigniew Bodek 
2771a8d7fc4aSZbigniew Bodek 	bus_dmamap_sync(sc->tx_dtag, tx->desc_map,
2772a8d7fc4aSZbigniew Bodek 	    BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
2773a8d7fc4aSZbigniew Bodek 
2774a8d7fc4aSZbigniew Bodek 	while (__predict_false(used > 255)) {
2775a8d7fc4aSZbigniew Bodek 		ptxsu = MVNETA_PTXSU_NOWD(255);
2776a8d7fc4aSZbigniew Bodek 		MVNETA_WRITE(sc, MVNETA_PTXSU(q), ptxsu);
2777a8d7fc4aSZbigniew Bodek 		used -= 255;
2778a8d7fc4aSZbigniew Bodek 	}
2779a8d7fc4aSZbigniew Bodek 	if (__predict_true(used > 0)) {
2780a8d7fc4aSZbigniew Bodek 		ptxsu = MVNETA_PTXSU_NOWD(used);
2781a8d7fc4aSZbigniew Bodek 		MVNETA_WRITE(sc, MVNETA_PTXSU(q), ptxsu);
2782a8d7fc4aSZbigniew Bodek 	}
2783a8d7fc4aSZbigniew Bodek 	return (0);
2784a8d7fc4aSZbigniew Bodek }
2785a8d7fc4aSZbigniew Bodek 
2786a8d7fc4aSZbigniew Bodek STATIC void
2787992fa62bSJustin Hibbits mvneta_tx_set_csumflag(if_t ifp,
2788a8d7fc4aSZbigniew Bodek     struct mvneta_tx_desc *t, struct mbuf *m)
2789a8d7fc4aSZbigniew Bodek {
2790a8d7fc4aSZbigniew Bodek 	struct ether_header *eh;
2791ea68079fSArnaud Ysmal 	struct ether_vlan_header *evh;
2792a8d7fc4aSZbigniew Bodek 	int csum_flags;
2793a8d7fc4aSZbigniew Bodek 	uint32_t iphl, ipoff;
2794a8d7fc4aSZbigniew Bodek 	struct ip *ip;
2795a8d7fc4aSZbigniew Bodek 
2796a8d7fc4aSZbigniew Bodek 	iphl = ipoff = 0;
2797992fa62bSJustin Hibbits 	csum_flags = if_gethwassist(ifp) & m->m_pkthdr.csum_flags;
2798a8d7fc4aSZbigniew Bodek 	eh = mtod(m, struct ether_header *);
279973f20bb3SMarcin Wojtas 
2800a8d7fc4aSZbigniew Bodek 	switch (ntohs(eh->ether_type)) {
2801a8d7fc4aSZbigniew Bodek 	case ETHERTYPE_IP:
2802a8d7fc4aSZbigniew Bodek 		ipoff = ETHER_HDR_LEN;
2803a8d7fc4aSZbigniew Bodek 		break;
2804a8d7fc4aSZbigniew Bodek 	case ETHERTYPE_VLAN:
2805a8d7fc4aSZbigniew Bodek 		ipoff = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
2806ea68079fSArnaud Ysmal 		evh = mtod(m, struct ether_vlan_header *);
2807ea68079fSArnaud Ysmal 		if (ntohs(evh->evl_proto) == ETHERTYPE_VLAN)
2808ea68079fSArnaud Ysmal 			ipoff += ETHER_VLAN_ENCAP_LEN;
2809a8d7fc4aSZbigniew Bodek 		break;
2810acdc9154SMarcin Wojtas 	default:
2811acdc9154SMarcin Wojtas 		csum_flags = 0;
2812a8d7fc4aSZbigniew Bodek 	}
2813a8d7fc4aSZbigniew Bodek 
2814a8d7fc4aSZbigniew Bodek 	if (__predict_true(csum_flags & (CSUM_IP|CSUM_IP_TCP|CSUM_IP_UDP))) {
2815a8d7fc4aSZbigniew Bodek 		ip = (struct ip *)(m->m_data + ipoff);
2816a8d7fc4aSZbigniew Bodek 		iphl = ip->ip_hl<<2;
2817a8d7fc4aSZbigniew Bodek 		t->command |= MVNETA_TX_CMD_L3_IP4;
2818a8d7fc4aSZbigniew Bodek 	} else {
2819a8d7fc4aSZbigniew Bodek 		t->command |= MVNETA_TX_CMD_L4_CHECKSUM_NONE;
2820a8d7fc4aSZbigniew Bodek 		return;
2821a8d7fc4aSZbigniew Bodek 	}
2822a8d7fc4aSZbigniew Bodek 
2823a8d7fc4aSZbigniew Bodek 
2824a8d7fc4aSZbigniew Bodek 	/* L3 */
2825a8d7fc4aSZbigniew Bodek 	if (csum_flags & CSUM_IP) {
2826a8d7fc4aSZbigniew Bodek 		t->command |= MVNETA_TX_CMD_IP4_CHECKSUM;
2827a8d7fc4aSZbigniew Bodek 	}
2828a8d7fc4aSZbigniew Bodek 
2829a8d7fc4aSZbigniew Bodek 	/* L4 */
2830a8d7fc4aSZbigniew Bodek 	if (csum_flags & CSUM_IP_TCP) {
2831a8d7fc4aSZbigniew Bodek 		t->command |= MVNETA_TX_CMD_L4_CHECKSUM_NOFRAG;
2832a8d7fc4aSZbigniew Bodek 		t->command |= MVNETA_TX_CMD_L4_TCP;
2833a8d7fc4aSZbigniew Bodek 	} else if (csum_flags & CSUM_IP_UDP) {
2834a8d7fc4aSZbigniew Bodek 		t->command |= MVNETA_TX_CMD_L4_CHECKSUM_NOFRAG;
2835a8d7fc4aSZbigniew Bodek 		t->command |= MVNETA_TX_CMD_L4_UDP;
2836a8d7fc4aSZbigniew Bodek 	} else
2837a8d7fc4aSZbigniew Bodek 		t->command |= MVNETA_TX_CMD_L4_CHECKSUM_NONE;
2838a8d7fc4aSZbigniew Bodek 
2839a8d7fc4aSZbigniew Bodek 	t->l4ichk = 0;
2840a8d7fc4aSZbigniew Bodek 	t->command |= MVNETA_TX_CMD_IP_HEADER_LEN(iphl >> 2);
2841a8d7fc4aSZbigniew Bodek 	t->command |= MVNETA_TX_CMD_L3_OFFSET(ipoff);
2842a8d7fc4aSZbigniew Bodek }
2843a8d7fc4aSZbigniew Bodek 
2844a8d7fc4aSZbigniew Bodek STATIC void
2845a8d7fc4aSZbigniew Bodek mvneta_tx_queue_complete(struct mvneta_softc *sc, int q)
2846a8d7fc4aSZbigniew Bodek {
2847a8d7fc4aSZbigniew Bodek 	struct mvneta_tx_ring *tx;
2848a8d7fc4aSZbigniew Bodek 	struct mvneta_buf *txbuf;
2849fcd0ea3aSJohn Baldwin 	struct mvneta_tx_desc *t __diagused;
2850a8d7fc4aSZbigniew Bodek 	uint32_t ptxs, ptxsu, ndesc;
2851a8d7fc4aSZbigniew Bodek 	int i;
2852a8d7fc4aSZbigniew Bodek 
2853a8d7fc4aSZbigniew Bodek 	KASSERT_TX_MTX(sc, q);
2854a8d7fc4aSZbigniew Bodek 
2855a8d7fc4aSZbigniew Bodek 	tx = MVNETA_TX_RING(sc, q);
2856a8d7fc4aSZbigniew Bodek 	if (__predict_false(tx->queue_status == MVNETA_QUEUE_DISABLED))
2857a8d7fc4aSZbigniew Bodek 		return;
2858a8d7fc4aSZbigniew Bodek 
2859a8d7fc4aSZbigniew Bodek 	ptxs = MVNETA_READ(sc, MVNETA_PTXS(q));
2860a8d7fc4aSZbigniew Bodek 	ndesc = MVNETA_PTXS_GET_TBC(ptxs);
2861a8d7fc4aSZbigniew Bodek 
2862a8d7fc4aSZbigniew Bodek 	if (__predict_false(ndesc == 0)) {
2863a8d7fc4aSZbigniew Bodek 		if (tx->used == 0)
2864a8d7fc4aSZbigniew Bodek 			tx->queue_status = MVNETA_QUEUE_IDLE;
2865a8d7fc4aSZbigniew Bodek 		else if (tx->queue_status == MVNETA_QUEUE_WORKING &&
2866a8d7fc4aSZbigniew Bodek 		    ((ticks - tx->watchdog_time) > MVNETA_WATCHDOG))
2867a8d7fc4aSZbigniew Bodek 			tx->queue_hung = TRUE;
2868a8d7fc4aSZbigniew Bodek 		return;
2869a8d7fc4aSZbigniew Bodek 	}
2870a8d7fc4aSZbigniew Bodek 
2871a8d7fc4aSZbigniew Bodek #ifdef MVNETA_KTR
2872a8d7fc4aSZbigniew Bodek 	CTR3(KTR_SPARE2, "%s:%u tx_complete begin ndesc=%u",
2873992fa62bSJustin Hibbits 	    if_name(sc->ifp), q, ndesc);
2874a8d7fc4aSZbigniew Bodek #endif
2875a8d7fc4aSZbigniew Bodek 
2876a8d7fc4aSZbigniew Bodek 	bus_dmamap_sync(sc->tx_dtag, tx->desc_map,
2877a8d7fc4aSZbigniew Bodek 	    BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
2878a8d7fc4aSZbigniew Bodek 
2879a8d7fc4aSZbigniew Bodek 	for (i = 0; i < ndesc; i++) {
2880a8d7fc4aSZbigniew Bodek 		t = &tx->desc[tx->dma];
2881a8d7fc4aSZbigniew Bodek #ifdef MVNETA_KTR
2882a8d7fc4aSZbigniew Bodek 		if (t->flags & MVNETA_TX_F_ES)
2883a8d7fc4aSZbigniew Bodek 			CTR3(KTR_SPARE2, "%s tx error queue %d desc %d",
2884992fa62bSJustin Hibbits 			    if_name(sc->ifp), q, tx->dma);
2885a8d7fc4aSZbigniew Bodek #endif
2886a8d7fc4aSZbigniew Bodek 		txbuf = &tx->txbuf[tx->dma];
2887a8d7fc4aSZbigniew Bodek 		if (__predict_true(txbuf->m != NULL)) {
2888a8d7fc4aSZbigniew Bodek 			DASSERT((t->command & MVNETA_TX_CMD_F) != 0);
2889a8d7fc4aSZbigniew Bodek 			bus_dmamap_unload(sc->txmbuf_dtag, txbuf->dmap);
2890a8d7fc4aSZbigniew Bodek 			m_freem(txbuf->m);
2891a8d7fc4aSZbigniew Bodek 			txbuf->m = NULL;
2892a8d7fc4aSZbigniew Bodek 		}
2893a8d7fc4aSZbigniew Bodek 		else
2894a8d7fc4aSZbigniew Bodek 			DASSERT((t->flags & MVNETA_TX_CMD_F) == 0);
2895a8d7fc4aSZbigniew Bodek 		tx->dma = tx_counter_adv(tx->dma, 1);
2896a8d7fc4aSZbigniew Bodek 		tx->used--;
2897a8d7fc4aSZbigniew Bodek 	}
2898a8d7fc4aSZbigniew Bodek 	DASSERT(tx->used >= 0);
2899a8d7fc4aSZbigniew Bodek 	DASSERT(tx->used <= MVNETA_TX_RING_CNT);
2900a8d7fc4aSZbigniew Bodek 	while (__predict_false(ndesc > 255)) {
2901a8d7fc4aSZbigniew Bodek 		ptxsu = MVNETA_PTXSU_NORB(255);
2902a8d7fc4aSZbigniew Bodek 		MVNETA_WRITE(sc, MVNETA_PTXSU(q), ptxsu);
2903a8d7fc4aSZbigniew Bodek 		ndesc -= 255;
2904a8d7fc4aSZbigniew Bodek 	}
2905a8d7fc4aSZbigniew Bodek 	if (__predict_true(ndesc > 0)) {
2906a8d7fc4aSZbigniew Bodek 		ptxsu = MVNETA_PTXSU_NORB(ndesc);
2907a8d7fc4aSZbigniew Bodek 		MVNETA_WRITE(sc, MVNETA_PTXSU(q), ptxsu);
2908a8d7fc4aSZbigniew Bodek 	}
2909a8d7fc4aSZbigniew Bodek #ifdef MVNETA_KTR
2910a8d7fc4aSZbigniew Bodek 	CTR5(KTR_SPARE2, "%s:%u tx_complete tx_cpu=%d tx_dma=%d tx_used=%d",
2911992fa62bSJustin Hibbits 	    if_name(sc->ifp), q, tx->cpu, tx->dma, tx->used);
2912a8d7fc4aSZbigniew Bodek #endif
2913a8d7fc4aSZbigniew Bodek 
2914a8d7fc4aSZbigniew Bodek 	tx->watchdog_time = ticks;
2915a8d7fc4aSZbigniew Bodek 
2916a8d7fc4aSZbigniew Bodek 	if (tx->used == 0)
2917a8d7fc4aSZbigniew Bodek 		tx->queue_status = MVNETA_QUEUE_IDLE;
2918a8d7fc4aSZbigniew Bodek }
2919a8d7fc4aSZbigniew Bodek 
2920a8d7fc4aSZbigniew Bodek /*
2921a8d7fc4aSZbigniew Bodek  * Do a final TX complete when TX is idle.
2922a8d7fc4aSZbigniew Bodek  */
2923a8d7fc4aSZbigniew Bodek STATIC void
2924a8d7fc4aSZbigniew Bodek mvneta_tx_drain(struct mvneta_softc *sc)
2925a8d7fc4aSZbigniew Bodek {
2926a8d7fc4aSZbigniew Bodek 	struct mvneta_tx_ring *tx;
2927a8d7fc4aSZbigniew Bodek 	int q;
2928a8d7fc4aSZbigniew Bodek 
2929a8d7fc4aSZbigniew Bodek 	/*
2930a8d7fc4aSZbigniew Bodek 	 * Handle trailing mbuf on TX queue.
2931a8d7fc4aSZbigniew Bodek 	 * Check is done lockess to avoid TX path contention.
2932a8d7fc4aSZbigniew Bodek 	 */
2933a8d7fc4aSZbigniew Bodek 	for (q = 0; q < MVNETA_TX_QNUM_MAX; q++) {
2934a8d7fc4aSZbigniew Bodek 		tx = MVNETA_TX_RING(sc, q);
2935a8d7fc4aSZbigniew Bodek 		if ((ticks - tx->watchdog_time) > MVNETA_WATCHDOG_TXCOMP &&
2936a8d7fc4aSZbigniew Bodek 		    tx->used > 0) {
2937a8d7fc4aSZbigniew Bodek 			mvneta_tx_lockq(sc, q);
2938a8d7fc4aSZbigniew Bodek 			mvneta_tx_queue_complete(sc, q);
2939a8d7fc4aSZbigniew Bodek 			mvneta_tx_unlockq(sc, q);
2940a8d7fc4aSZbigniew Bodek 		}
2941a8d7fc4aSZbigniew Bodek 	}
2942a8d7fc4aSZbigniew Bodek }
2943a8d7fc4aSZbigniew Bodek 
2944a8d7fc4aSZbigniew Bodek /*
2945a8d7fc4aSZbigniew Bodek  * Rx Subroutines
2946a8d7fc4aSZbigniew Bodek  */
2947a8d7fc4aSZbigniew Bodek STATIC int
2948a8d7fc4aSZbigniew Bodek mvneta_rx(struct mvneta_softc *sc, int q, int count)
2949a8d7fc4aSZbigniew Bodek {
2950a8d7fc4aSZbigniew Bodek 	uint32_t prxs, npkt;
2951a8d7fc4aSZbigniew Bodek 	int more;
2952a8d7fc4aSZbigniew Bodek 
2953a8d7fc4aSZbigniew Bodek 	more = 0;
2954a8d7fc4aSZbigniew Bodek 	mvneta_rx_lockq(sc, q);
2955a8d7fc4aSZbigniew Bodek 	prxs = MVNETA_READ(sc, MVNETA_PRXS(q));
2956a8d7fc4aSZbigniew Bodek 	npkt = MVNETA_PRXS_GET_ODC(prxs);
2957a8d7fc4aSZbigniew Bodek 	if (__predict_false(npkt == 0))
2958a8d7fc4aSZbigniew Bodek 		goto out;
2959a8d7fc4aSZbigniew Bodek 
2960a8d7fc4aSZbigniew Bodek 	if (count > 0 && npkt > count) {
2961a8d7fc4aSZbigniew Bodek 		more = 1;
2962a8d7fc4aSZbigniew Bodek 		npkt = count;
2963a8d7fc4aSZbigniew Bodek 	}
2964a8d7fc4aSZbigniew Bodek 	mvneta_rx_queue(sc, q, npkt);
2965a8d7fc4aSZbigniew Bodek out:
2966a8d7fc4aSZbigniew Bodek 	mvneta_rx_unlockq(sc, q);
2967a8d7fc4aSZbigniew Bodek 	return more;
2968a8d7fc4aSZbigniew Bodek }
2969a8d7fc4aSZbigniew Bodek 
2970a8d7fc4aSZbigniew Bodek /*
2971a8d7fc4aSZbigniew Bodek  * Helper routine for updating PRXSU register of a given queue.
2972a8d7fc4aSZbigniew Bodek  * Handles number of processed descriptors bigger than maximum acceptable value.
2973a8d7fc4aSZbigniew Bodek  */
2974a8d7fc4aSZbigniew Bodek STATIC __inline void
2975a8d7fc4aSZbigniew Bodek mvneta_prxsu_update(struct mvneta_softc *sc, int q, int processed)
2976a8d7fc4aSZbigniew Bodek {
2977a8d7fc4aSZbigniew Bodek 	uint32_t prxsu;
2978a8d7fc4aSZbigniew Bodek 
2979a8d7fc4aSZbigniew Bodek 	while (__predict_false(processed > 255)) {
2980a8d7fc4aSZbigniew Bodek 		prxsu = MVNETA_PRXSU_NOOFPROCESSEDDESCRIPTORS(255);
2981a8d7fc4aSZbigniew Bodek 		MVNETA_WRITE(sc, MVNETA_PRXSU(q), prxsu);
2982a8d7fc4aSZbigniew Bodek 		processed -= 255;
2983a8d7fc4aSZbigniew Bodek 	}
2984a8d7fc4aSZbigniew Bodek 	prxsu = MVNETA_PRXSU_NOOFPROCESSEDDESCRIPTORS(processed);
2985a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PRXSU(q), prxsu);
2986a8d7fc4aSZbigniew Bodek }
2987a8d7fc4aSZbigniew Bodek 
2988a8d7fc4aSZbigniew Bodek static __inline void
2989a8d7fc4aSZbigniew Bodek mvneta_prefetch(void *p)
2990a8d7fc4aSZbigniew Bodek {
2991a8d7fc4aSZbigniew Bodek 
2992a8d7fc4aSZbigniew Bodek 	__builtin_prefetch(p);
2993a8d7fc4aSZbigniew Bodek }
2994a8d7fc4aSZbigniew Bodek 
2995a8d7fc4aSZbigniew Bodek STATIC void
2996a8d7fc4aSZbigniew Bodek mvneta_rx_queue(struct mvneta_softc *sc, int q, int npkt)
2997a8d7fc4aSZbigniew Bodek {
2998992fa62bSJustin Hibbits 	if_t ifp;
2999a8d7fc4aSZbigniew Bodek 	struct mvneta_rx_ring *rx;
3000a8d7fc4aSZbigniew Bodek 	struct mvneta_rx_desc *r;
3001a8d7fc4aSZbigniew Bodek 	struct mvneta_buf *rxbuf;
3002a8d7fc4aSZbigniew Bodek 	struct mbuf *m;
3003a8d7fc4aSZbigniew Bodek 	void *pktbuf;
3004a8d7fc4aSZbigniew Bodek 	int i, pktlen, processed, ndma;
3005a8d7fc4aSZbigniew Bodek 
3006a8d7fc4aSZbigniew Bodek 	KASSERT_RX_MTX(sc, q);
3007a8d7fc4aSZbigniew Bodek 
3008a8d7fc4aSZbigniew Bodek 	ifp = sc->ifp;
3009a8d7fc4aSZbigniew Bodek 	rx = MVNETA_RX_RING(sc, q);
3010a8d7fc4aSZbigniew Bodek 	processed = 0;
3011a8d7fc4aSZbigniew Bodek 
3012a8d7fc4aSZbigniew Bodek 	if (__predict_false(rx->queue_status == MVNETA_QUEUE_DISABLED))
3013a8d7fc4aSZbigniew Bodek 		return;
3014a8d7fc4aSZbigniew Bodek 
3015a8d7fc4aSZbigniew Bodek 	bus_dmamap_sync(sc->rx_dtag, rx->desc_map,
3016a8d7fc4aSZbigniew Bodek 	    BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
3017a8d7fc4aSZbigniew Bodek 
3018a8d7fc4aSZbigniew Bodek 	for (i = 0; i < npkt; i++) {
3019a8d7fc4aSZbigniew Bodek 		/* Prefetch next desc, rxbuf. */
3020a8d7fc4aSZbigniew Bodek 		ndma = rx_counter_adv(rx->dma, 1);
3021a8d7fc4aSZbigniew Bodek 		mvneta_prefetch(&rx->desc[ndma]);
3022a8d7fc4aSZbigniew Bodek 		mvneta_prefetch(&rx->rxbuf[ndma]);
3023a8d7fc4aSZbigniew Bodek 
3024a8d7fc4aSZbigniew Bodek 		/* get descriptor and packet */
3025a8d7fc4aSZbigniew Bodek 		r = &rx->desc[rx->dma];
3026a8d7fc4aSZbigniew Bodek 		rxbuf = &rx->rxbuf[rx->dma];
3027a8d7fc4aSZbigniew Bodek 		m = rxbuf->m;
3028a8d7fc4aSZbigniew Bodek 		rxbuf->m = NULL;
3029a8d7fc4aSZbigniew Bodek 		DASSERT(m != NULL);
3030a8d7fc4aSZbigniew Bodek 		bus_dmamap_sync(sc->rxbuf_dtag, rxbuf->dmap,
3031a8d7fc4aSZbigniew Bodek 		    BUS_DMASYNC_POSTREAD);
3032a8d7fc4aSZbigniew Bodek 		bus_dmamap_unload(sc->rxbuf_dtag, rxbuf->dmap);
3033a8d7fc4aSZbigniew Bodek 		/* Prefetch mbuf header. */
3034a8d7fc4aSZbigniew Bodek 		mvneta_prefetch(m);
3035a8d7fc4aSZbigniew Bodek 
3036a8d7fc4aSZbigniew Bodek 		processed++;
3037a8d7fc4aSZbigniew Bodek 		/* Drop desc with error status or not in a single buffer. */
3038a8d7fc4aSZbigniew Bodek 		DASSERT((r->status & (MVNETA_RX_F|MVNETA_RX_L)) ==
3039a8d7fc4aSZbigniew Bodek 		    (MVNETA_RX_F|MVNETA_RX_L));
3040a8d7fc4aSZbigniew Bodek 		if (__predict_false((r->status & MVNETA_RX_ES) ||
3041a8d7fc4aSZbigniew Bodek 		    (r->status & (MVNETA_RX_F|MVNETA_RX_L)) !=
3042a8d7fc4aSZbigniew Bodek 		    (MVNETA_RX_F|MVNETA_RX_L)))
3043a8d7fc4aSZbigniew Bodek 			goto rx_error;
3044a8d7fc4aSZbigniew Bodek 
3045a8d7fc4aSZbigniew Bodek 		/*
3046a8d7fc4aSZbigniew Bodek 		 * [ OFF | MH | PKT | CRC ]
3047a8d7fc4aSZbigniew Bodek 		 * bytecnt cover MH, PKT, CRC
3048a8d7fc4aSZbigniew Bodek 		 */
3049a8d7fc4aSZbigniew Bodek 		pktlen = r->bytecnt - ETHER_CRC_LEN - MVNETA_HWHEADER_SIZE;
3050e7843f1dSMarcin Wojtas 		pktbuf = (uint8_t *)rx->rxbuf_virt_addr[rx->dma] + MVNETA_PACKET_OFFSET +
3051a8d7fc4aSZbigniew Bodek                     MVNETA_HWHEADER_SIZE;
3052a8d7fc4aSZbigniew Bodek 
3053a8d7fc4aSZbigniew Bodek 		/* Prefetch mbuf data. */
3054a8d7fc4aSZbigniew Bodek 		mvneta_prefetch(pktbuf);
3055a8d7fc4aSZbigniew Bodek 
3056a8d7fc4aSZbigniew Bodek 		/* Write value to mbuf (avoid read). */
3057a8d7fc4aSZbigniew Bodek 		m->m_data = pktbuf;
3058a8d7fc4aSZbigniew Bodek 		m->m_len = m->m_pkthdr.len = pktlen;
3059a8d7fc4aSZbigniew Bodek 		m->m_pkthdr.rcvif = ifp;
3060a8d7fc4aSZbigniew Bodek 		mvneta_rx_set_csumflag(ifp, r, m);
3061a8d7fc4aSZbigniew Bodek 
3062a8d7fc4aSZbigniew Bodek 		/* Increase rx_dma before releasing the lock. */
3063a8d7fc4aSZbigniew Bodek 		rx->dma = ndma;
3064a8d7fc4aSZbigniew Bodek 
3065a8d7fc4aSZbigniew Bodek 		if (__predict_false(rx->lro_enabled &&
3066a8d7fc4aSZbigniew Bodek 		    ((r->status & MVNETA_RX_L3_IP) != 0) &&
3067a8d7fc4aSZbigniew Bodek 		    ((r->status & MVNETA_RX_L4_MASK) == MVNETA_RX_L4_TCP) &&
3068a8d7fc4aSZbigniew Bodek 		    (m->m_pkthdr.csum_flags &
3069a8d7fc4aSZbigniew Bodek 		    (CSUM_DATA_VALID | CSUM_PSEUDO_HDR)) ==
3070a8d7fc4aSZbigniew Bodek 		    (CSUM_DATA_VALID | CSUM_PSEUDO_HDR))) {
3071a8d7fc4aSZbigniew Bodek 			if (rx->lro.lro_cnt != 0) {
3072a8d7fc4aSZbigniew Bodek 				if (tcp_lro_rx(&rx->lro, m, 0) == 0)
3073a8d7fc4aSZbigniew Bodek 					goto rx_done;
3074a8d7fc4aSZbigniew Bodek 			}
3075a8d7fc4aSZbigniew Bodek 		}
3076a8d7fc4aSZbigniew Bodek 
3077a8d7fc4aSZbigniew Bodek 		mvneta_rx_unlockq(sc, q);
3078992fa62bSJustin Hibbits 		if_input(ifp, m);
3079a8d7fc4aSZbigniew Bodek 		mvneta_rx_lockq(sc, q);
3080a8d7fc4aSZbigniew Bodek 		/*
3081a8d7fc4aSZbigniew Bodek 		 * Check whether this queue has been disabled in the
3082a8d7fc4aSZbigniew Bodek 		 * meantime. If yes, then clear LRO and exit.
3083a8d7fc4aSZbigniew Bodek 		 */
3084a8d7fc4aSZbigniew Bodek 		if(__predict_false(rx->queue_status == MVNETA_QUEUE_DISABLED))
3085a8d7fc4aSZbigniew Bodek 			goto rx_lro;
3086a8d7fc4aSZbigniew Bodek rx_done:
3087a8d7fc4aSZbigniew Bodek 		/* Refresh receive ring to avoid stall and minimize jitter. */
3088a8d7fc4aSZbigniew Bodek 		if (processed >= MVNETA_RX_REFILL_COUNT) {
3089a8d7fc4aSZbigniew Bodek 			mvneta_prxsu_update(sc, q, processed);
3090a8d7fc4aSZbigniew Bodek 			mvneta_rx_queue_refill(sc, q);
3091a8d7fc4aSZbigniew Bodek 			processed = 0;
3092a8d7fc4aSZbigniew Bodek 		}
3093a8d7fc4aSZbigniew Bodek 		continue;
3094a8d7fc4aSZbigniew Bodek rx_error:
3095a8d7fc4aSZbigniew Bodek 		m_freem(m);
3096a8d7fc4aSZbigniew Bodek 		rx->dma = ndma;
3097a8d7fc4aSZbigniew Bodek 		/* Refresh receive ring to avoid stall and minimize jitter. */
3098a8d7fc4aSZbigniew Bodek 		if (processed >= MVNETA_RX_REFILL_COUNT) {
3099a8d7fc4aSZbigniew Bodek 			mvneta_prxsu_update(sc, q, processed);
3100a8d7fc4aSZbigniew Bodek 			mvneta_rx_queue_refill(sc, q);
3101a8d7fc4aSZbigniew Bodek 			processed = 0;
3102a8d7fc4aSZbigniew Bodek 		}
3103a8d7fc4aSZbigniew Bodek 	}
3104a8d7fc4aSZbigniew Bodek #ifdef MVNETA_KTR
3105992fa62bSJustin Hibbits 	CTR3(KTR_SPARE2, "%s:%u %u packets received", if_name(ifp), q, npkt);
3106a8d7fc4aSZbigniew Bodek #endif
3107a8d7fc4aSZbigniew Bodek 	/* DMA status update */
3108a8d7fc4aSZbigniew Bodek 	mvneta_prxsu_update(sc, q, processed);
3109a8d7fc4aSZbigniew Bodek 	/* Refill the rest of buffers if there are any to refill */
3110a8d7fc4aSZbigniew Bodek 	mvneta_rx_queue_refill(sc, q);
3111a8d7fc4aSZbigniew Bodek 
3112a8d7fc4aSZbigniew Bodek rx_lro:
3113a8d7fc4aSZbigniew Bodek 	/*
3114a8d7fc4aSZbigniew Bodek 	 * Flush any outstanding LRO work
3115a8d7fc4aSZbigniew Bodek 	 */
31165203dcceSMichael Tuexen 	tcp_lro_flush_all(&rx->lro);
3117a8d7fc4aSZbigniew Bodek }
3118a8d7fc4aSZbigniew Bodek 
3119a8d7fc4aSZbigniew Bodek STATIC void
3120a8d7fc4aSZbigniew Bodek mvneta_rx_buf_free(struct mvneta_softc *sc, struct mvneta_buf *rxbuf)
3121a8d7fc4aSZbigniew Bodek {
3122a8d7fc4aSZbigniew Bodek 
3123a8d7fc4aSZbigniew Bodek 	bus_dmamap_unload(sc->rxbuf_dtag, rxbuf->dmap);
3124a8d7fc4aSZbigniew Bodek 	/* This will remove all data at once */
3125a8d7fc4aSZbigniew Bodek 	m_freem(rxbuf->m);
3126a8d7fc4aSZbigniew Bodek }
3127a8d7fc4aSZbigniew Bodek 
3128a8d7fc4aSZbigniew Bodek STATIC void
3129a8d7fc4aSZbigniew Bodek mvneta_rx_queue_refill(struct mvneta_softc *sc, int q)
3130a8d7fc4aSZbigniew Bodek {
3131a8d7fc4aSZbigniew Bodek 	struct mvneta_rx_ring *rx;
3132a8d7fc4aSZbigniew Bodek 	struct mvneta_rx_desc *r;
3133a8d7fc4aSZbigniew Bodek 	struct mvneta_buf *rxbuf;
3134a8d7fc4aSZbigniew Bodek 	bus_dma_segment_t segs;
3135a8d7fc4aSZbigniew Bodek 	struct mbuf *m;
3136a8d7fc4aSZbigniew Bodek 	uint32_t prxs, prxsu, ndesc;
3137a8d7fc4aSZbigniew Bodek 	int npkt, refill, nsegs, error;
3138a8d7fc4aSZbigniew Bodek 
3139a8d7fc4aSZbigniew Bodek 	KASSERT_RX_MTX(sc, q);
3140a8d7fc4aSZbigniew Bodek 
3141a8d7fc4aSZbigniew Bodek 	rx = MVNETA_RX_RING(sc, q);
3142a8d7fc4aSZbigniew Bodek 	prxs = MVNETA_READ(sc, MVNETA_PRXS(q));
3143a8d7fc4aSZbigniew Bodek 	ndesc = MVNETA_PRXS_GET_NODC(prxs) + MVNETA_PRXS_GET_ODC(prxs);
3144a8d7fc4aSZbigniew Bodek 	refill = MVNETA_RX_RING_CNT - ndesc;
3145a8d7fc4aSZbigniew Bodek #ifdef MVNETA_KTR
3146992fa62bSJustin Hibbits 	CTR3(KTR_SPARE2, "%s:%u refill %u packets", if_name(sc->ifp), q,
3147a8d7fc4aSZbigniew Bodek 	    refill);
3148a8d7fc4aSZbigniew Bodek #endif
3149a8d7fc4aSZbigniew Bodek 	if (__predict_false(refill <= 0))
3150a8d7fc4aSZbigniew Bodek 		return;
3151a8d7fc4aSZbigniew Bodek 
3152a8d7fc4aSZbigniew Bodek 	for (npkt = 0; npkt < refill; npkt++) {
3153a8d7fc4aSZbigniew Bodek 		rxbuf = &rx->rxbuf[rx->cpu];
315473f20bb3SMarcin Wojtas 		m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, sc->rx_frame_size);
3155a8d7fc4aSZbigniew Bodek 		if (__predict_false(m == NULL)) {
3156a8d7fc4aSZbigniew Bodek 			error = ENOBUFS;
3157a8d7fc4aSZbigniew Bodek 			break;
3158a8d7fc4aSZbigniew Bodek 		}
3159a8d7fc4aSZbigniew Bodek 		m->m_len = m->m_pkthdr.len = m->m_ext.ext_size;
3160a8d7fc4aSZbigniew Bodek 
3161a8d7fc4aSZbigniew Bodek 		error = bus_dmamap_load_mbuf_sg(sc->rxbuf_dtag, rxbuf->dmap,
3162a8d7fc4aSZbigniew Bodek 		    m, &segs, &nsegs, BUS_DMA_NOWAIT);
3163a8d7fc4aSZbigniew Bodek 		if (__predict_false(error != 0 || nsegs != 1)) {
3164a8d7fc4aSZbigniew Bodek 			KASSERT(1, ("Failed to load Rx mbuf DMA map"));
3165a8d7fc4aSZbigniew Bodek 			m_freem(m);
3166a8d7fc4aSZbigniew Bodek 			break;
3167a8d7fc4aSZbigniew Bodek 		}
3168a8d7fc4aSZbigniew Bodek 
3169a8d7fc4aSZbigniew Bodek 		/* Add the packet to the ring */
3170a8d7fc4aSZbigniew Bodek 		rxbuf->m = m;
3171a8d7fc4aSZbigniew Bodek 		r = &rx->desc[rx->cpu];
3172a8d7fc4aSZbigniew Bodek 		r->bufptr_pa = segs.ds_addr;
3173e7843f1dSMarcin Wojtas 		rx->rxbuf_virt_addr[rx->cpu] = m->m_data;
3174a8d7fc4aSZbigniew Bodek 
3175a8d7fc4aSZbigniew Bodek 		rx->cpu = rx_counter_adv(rx->cpu, 1);
3176a8d7fc4aSZbigniew Bodek 	}
3177a8d7fc4aSZbigniew Bodek 	if (npkt == 0) {
3178a8d7fc4aSZbigniew Bodek 		if (refill == MVNETA_RX_RING_CNT)
3179a8d7fc4aSZbigniew Bodek 			rx->needs_refill = TRUE;
3180a8d7fc4aSZbigniew Bodek 		return;
3181a8d7fc4aSZbigniew Bodek 	}
3182a8d7fc4aSZbigniew Bodek 
3183a8d7fc4aSZbigniew Bodek 	rx->needs_refill = FALSE;
3184a8d7fc4aSZbigniew Bodek 	bus_dmamap_sync(sc->rx_dtag, rx->desc_map, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
3185a8d7fc4aSZbigniew Bodek 
3186a8d7fc4aSZbigniew Bodek 	while (__predict_false(npkt > 255)) {
3187a8d7fc4aSZbigniew Bodek 		prxsu = MVNETA_PRXSU_NOOFNEWDESCRIPTORS(255);
3188a8d7fc4aSZbigniew Bodek 		MVNETA_WRITE(sc, MVNETA_PRXSU(q), prxsu);
3189a8d7fc4aSZbigniew Bodek 		npkt -= 255;
3190a8d7fc4aSZbigniew Bodek 	}
3191a8d7fc4aSZbigniew Bodek 	if (__predict_true(npkt > 0)) {
3192a8d7fc4aSZbigniew Bodek 		prxsu = MVNETA_PRXSU_NOOFNEWDESCRIPTORS(npkt);
3193a8d7fc4aSZbigniew Bodek 		MVNETA_WRITE(sc, MVNETA_PRXSU(q), prxsu);
3194a8d7fc4aSZbigniew Bodek 	}
3195a8d7fc4aSZbigniew Bodek }
3196a8d7fc4aSZbigniew Bodek 
3197a8d7fc4aSZbigniew Bodek STATIC __inline void
3198992fa62bSJustin Hibbits mvneta_rx_set_csumflag(if_t ifp,
3199a8d7fc4aSZbigniew Bodek     struct mvneta_rx_desc *r, struct mbuf *m)
3200a8d7fc4aSZbigniew Bodek {
3201a8d7fc4aSZbigniew Bodek 	uint32_t csum_flags;
3202a8d7fc4aSZbigniew Bodek 
3203a8d7fc4aSZbigniew Bodek 	csum_flags = 0;
3204a8d7fc4aSZbigniew Bodek 	if (__predict_false((r->status &
3205a8d7fc4aSZbigniew Bodek 	    (MVNETA_RX_IP_HEADER_OK|MVNETA_RX_L3_IP)) == 0))
3206a8d7fc4aSZbigniew Bodek 		return; /* not a IP packet */
3207a8d7fc4aSZbigniew Bodek 
3208a8d7fc4aSZbigniew Bodek 	/* L3 */
3209a8d7fc4aSZbigniew Bodek 	if (__predict_true((r->status & MVNETA_RX_IP_HEADER_OK) ==
3210a8d7fc4aSZbigniew Bodek 	    MVNETA_RX_IP_HEADER_OK))
3211a8d7fc4aSZbigniew Bodek 		csum_flags |= CSUM_L3_CALC|CSUM_L3_VALID;
3212a8d7fc4aSZbigniew Bodek 
3213a8d7fc4aSZbigniew Bodek 	if (__predict_true((r->status & (MVNETA_RX_IP_HEADER_OK|MVNETA_RX_L3_IP)) ==
3214a8d7fc4aSZbigniew Bodek 	    (MVNETA_RX_IP_HEADER_OK|MVNETA_RX_L3_IP))) {
3215a8d7fc4aSZbigniew Bodek 		/* L4 */
3216a8d7fc4aSZbigniew Bodek 		switch (r->status & MVNETA_RX_L4_MASK) {
3217a8d7fc4aSZbigniew Bodek 		case MVNETA_RX_L4_TCP:
3218a8d7fc4aSZbigniew Bodek 		case MVNETA_RX_L4_UDP:
3219a8d7fc4aSZbigniew Bodek 			csum_flags |= CSUM_L4_CALC;
3220a8d7fc4aSZbigniew Bodek 			if (__predict_true((r->status &
3221a8d7fc4aSZbigniew Bodek 			    MVNETA_RX_L4_CHECKSUM_OK) == MVNETA_RX_L4_CHECKSUM_OK)) {
3222a8d7fc4aSZbigniew Bodek 				csum_flags |= CSUM_L4_VALID;
3223a8d7fc4aSZbigniew Bodek 				m->m_pkthdr.csum_data = htons(0xffff);
3224a8d7fc4aSZbigniew Bodek 			}
3225a8d7fc4aSZbigniew Bodek 			break;
3226a8d7fc4aSZbigniew Bodek 		case MVNETA_RX_L4_OTH:
3227a8d7fc4aSZbigniew Bodek 		default:
3228a8d7fc4aSZbigniew Bodek 			break;
3229a8d7fc4aSZbigniew Bodek 		}
3230a8d7fc4aSZbigniew Bodek 	}
3231a8d7fc4aSZbigniew Bodek 	m->m_pkthdr.csum_flags = csum_flags;
3232a8d7fc4aSZbigniew Bodek }
3233a8d7fc4aSZbigniew Bodek 
3234a8d7fc4aSZbigniew Bodek /*
3235a8d7fc4aSZbigniew Bodek  * MAC address filter
3236a8d7fc4aSZbigniew Bodek  */
3237a8d7fc4aSZbigniew Bodek STATIC void
3238a8d7fc4aSZbigniew Bodek mvneta_filter_setup(struct mvneta_softc *sc)
3239a8d7fc4aSZbigniew Bodek {
3240992fa62bSJustin Hibbits 	if_t ifp;
3241a8d7fc4aSZbigniew Bodek 	uint32_t dfut[MVNETA_NDFUT], dfsmt[MVNETA_NDFSMT], dfomt[MVNETA_NDFOMT];
3242a8d7fc4aSZbigniew Bodek 	uint32_t pxc;
3243a8d7fc4aSZbigniew Bodek 	int i;
3244a8d7fc4aSZbigniew Bodek 
3245a8d7fc4aSZbigniew Bodek 	KASSERT_SC_MTX(sc);
3246a8d7fc4aSZbigniew Bodek 
3247a8d7fc4aSZbigniew Bodek 	memset(dfut, 0, sizeof(dfut));
3248a8d7fc4aSZbigniew Bodek 	memset(dfsmt, 0, sizeof(dfsmt));
3249a8d7fc4aSZbigniew Bodek 	memset(dfomt, 0, sizeof(dfomt));
3250a8d7fc4aSZbigniew Bodek 
3251a8d7fc4aSZbigniew Bodek 	ifp = sc->ifp;
3252992fa62bSJustin Hibbits 	if_setflagbits(ifp, IFF_ALLMULTI, 0);
3253992fa62bSJustin Hibbits 	if (if_getflags(ifp) & (IFF_ALLMULTI | IFF_PROMISC)) {
3254a8d7fc4aSZbigniew Bodek 		for (i = 0; i < MVNETA_NDFSMT; i++) {
3255a8d7fc4aSZbigniew Bodek 			dfsmt[i] = dfomt[i] =
3256a8d7fc4aSZbigniew Bodek 			    MVNETA_DF(0, MVNETA_DF_QUEUE(0) | MVNETA_DF_PASS) |
3257a8d7fc4aSZbigniew Bodek 			    MVNETA_DF(1, MVNETA_DF_QUEUE(0) | MVNETA_DF_PASS) |
3258a8d7fc4aSZbigniew Bodek 			    MVNETA_DF(2, MVNETA_DF_QUEUE(0) | MVNETA_DF_PASS) |
3259a8d7fc4aSZbigniew Bodek 			    MVNETA_DF(3, MVNETA_DF_QUEUE(0) | MVNETA_DF_PASS);
3260a8d7fc4aSZbigniew Bodek 		}
3261a8d7fc4aSZbigniew Bodek 	}
3262a8d7fc4aSZbigniew Bodek 
3263a8d7fc4aSZbigniew Bodek 	pxc = MVNETA_READ(sc, MVNETA_PXC);
3264a8d7fc4aSZbigniew Bodek 	pxc &= ~(MVNETA_PXC_UPM | MVNETA_PXC_RXQ_MASK | MVNETA_PXC_RXQARP_MASK |
3265a8d7fc4aSZbigniew Bodek 	    MVNETA_PXC_TCPQ_MASK | MVNETA_PXC_UDPQ_MASK | MVNETA_PXC_BPDUQ_MASK);
3266a8d7fc4aSZbigniew Bodek 	pxc |= MVNETA_PXC_RXQ(MVNETA_RX_QNUM_MAX-1);
3267a8d7fc4aSZbigniew Bodek 	pxc |= MVNETA_PXC_RXQARP(MVNETA_RX_QNUM_MAX-1);
3268a8d7fc4aSZbigniew Bodek 	pxc |= MVNETA_PXC_TCPQ(MVNETA_RX_QNUM_MAX-1);
3269a8d7fc4aSZbigniew Bodek 	pxc |= MVNETA_PXC_UDPQ(MVNETA_RX_QNUM_MAX-1);
3270a8d7fc4aSZbigniew Bodek 	pxc |= MVNETA_PXC_BPDUQ(MVNETA_RX_QNUM_MAX-1);
3271a8d7fc4aSZbigniew Bodek 	pxc |= MVNETA_PXC_RB | MVNETA_PXC_RBIP | MVNETA_PXC_RBARP;
3272992fa62bSJustin Hibbits 	if (if_getflags(ifp) & IFF_BROADCAST) {
3273a8d7fc4aSZbigniew Bodek 		pxc &= ~(MVNETA_PXC_RB | MVNETA_PXC_RBIP | MVNETA_PXC_RBARP);
3274a8d7fc4aSZbigniew Bodek 	}
3275992fa62bSJustin Hibbits 	if (if_getflags(ifp) & IFF_PROMISC) {
3276a8d7fc4aSZbigniew Bodek 		pxc |= MVNETA_PXC_UPM;
3277a8d7fc4aSZbigniew Bodek 	}
3278a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PXC, pxc);
3279a8d7fc4aSZbigniew Bodek 
3280a8d7fc4aSZbigniew Bodek 	/* Set Destination Address Filter Unicast Table */
3281992fa62bSJustin Hibbits 	if (if_getflags(ifp) & IFF_PROMISC) {
3282a8d7fc4aSZbigniew Bodek 		/* pass all unicast addresses */
3283a8d7fc4aSZbigniew Bodek 		for (i = 0; i < MVNETA_NDFUT; i++) {
3284a8d7fc4aSZbigniew Bodek 			dfut[i] =
3285a8d7fc4aSZbigniew Bodek 			    MVNETA_DF(0, MVNETA_DF_QUEUE(0) | MVNETA_DF_PASS) |
3286a8d7fc4aSZbigniew Bodek 			    MVNETA_DF(1, MVNETA_DF_QUEUE(0) | MVNETA_DF_PASS) |
3287a8d7fc4aSZbigniew Bodek 			    MVNETA_DF(2, MVNETA_DF_QUEUE(0) | MVNETA_DF_PASS) |
3288a8d7fc4aSZbigniew Bodek 			    MVNETA_DF(3, MVNETA_DF_QUEUE(0) | MVNETA_DF_PASS);
3289a8d7fc4aSZbigniew Bodek 		}
3290a8d7fc4aSZbigniew Bodek 	} else {
3291a8d7fc4aSZbigniew Bodek 		i = sc->enaddr[5] & 0xf;		/* last nibble */
3292a8d7fc4aSZbigniew Bodek 		dfut[i>>2] = MVNETA_DF(i&3, MVNETA_DF_QUEUE(0) | MVNETA_DF_PASS);
3293a8d7fc4aSZbigniew Bodek 	}
3294a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE_REGION(sc, MVNETA_DFUT(0), dfut, MVNETA_NDFUT);
3295a8d7fc4aSZbigniew Bodek 
3296a8d7fc4aSZbigniew Bodek 	/* Set Destination Address Filter Multicast Tables */
3297a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE_REGION(sc, MVNETA_DFSMT(0), dfsmt, MVNETA_NDFSMT);
3298a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE_REGION(sc, MVNETA_DFOMT(0), dfomt, MVNETA_NDFOMT);
3299a8d7fc4aSZbigniew Bodek }
3300a8d7fc4aSZbigniew Bodek 
3301a8d7fc4aSZbigniew Bodek /*
3302a8d7fc4aSZbigniew Bodek  * sysctl(9)
3303a8d7fc4aSZbigniew Bodek  */
3304a8d7fc4aSZbigniew Bodek STATIC int
3305a8d7fc4aSZbigniew Bodek sysctl_read_mib(SYSCTL_HANDLER_ARGS)
3306a8d7fc4aSZbigniew Bodek {
3307a8d7fc4aSZbigniew Bodek 	struct mvneta_sysctl_mib *arg;
3308a8d7fc4aSZbigniew Bodek 	struct mvneta_softc *sc;
3309a8d7fc4aSZbigniew Bodek 	uint64_t val;
3310a8d7fc4aSZbigniew Bodek 
3311a8d7fc4aSZbigniew Bodek 	arg = (struct mvneta_sysctl_mib *)arg1;
3312a8d7fc4aSZbigniew Bodek 	if (arg == NULL)
3313a8d7fc4aSZbigniew Bodek 		return (EINVAL);
3314a8d7fc4aSZbigniew Bodek 
3315a8d7fc4aSZbigniew Bodek 	sc = arg->sc;
3316a8d7fc4aSZbigniew Bodek 	if (sc == NULL)
3317a8d7fc4aSZbigniew Bodek 		return (EINVAL);
3318a8d7fc4aSZbigniew Bodek 	if (arg->index < 0 || arg->index > MVNETA_PORTMIB_NOCOUNTER)
3319a8d7fc4aSZbigniew Bodek 		return (EINVAL);
3320a8d7fc4aSZbigniew Bodek 
3321a8d7fc4aSZbigniew Bodek 	mvneta_sc_lock(sc);
3322a8d7fc4aSZbigniew Bodek 	val = arg->counter;
3323a8d7fc4aSZbigniew Bodek 	mvneta_sc_unlock(sc);
3324a8d7fc4aSZbigniew Bodek 	return sysctl_handle_64(oidp, &val, 0, req);
3325a8d7fc4aSZbigniew Bodek }
3326a8d7fc4aSZbigniew Bodek 
3327a8d7fc4aSZbigniew Bodek 
3328a8d7fc4aSZbigniew Bodek STATIC int
3329a8d7fc4aSZbigniew Bodek sysctl_clear_mib(SYSCTL_HANDLER_ARGS)
3330a8d7fc4aSZbigniew Bodek {
3331a8d7fc4aSZbigniew Bodek 	struct mvneta_softc *sc;
3332a8d7fc4aSZbigniew Bodek 	int err, val;
3333a8d7fc4aSZbigniew Bodek 
3334a8d7fc4aSZbigniew Bodek 	val = 0;
3335a8d7fc4aSZbigniew Bodek 	sc = (struct mvneta_softc *)arg1;
3336a8d7fc4aSZbigniew Bodek 	if (sc == NULL)
3337a8d7fc4aSZbigniew Bodek 		return (EINVAL);
3338a8d7fc4aSZbigniew Bodek 
3339a8d7fc4aSZbigniew Bodek 	err = sysctl_handle_int(oidp, &val, 0, req);
3340a8d7fc4aSZbigniew Bodek 	if (err != 0)
3341a8d7fc4aSZbigniew Bodek 		return (err);
3342a8d7fc4aSZbigniew Bodek 
3343a8d7fc4aSZbigniew Bodek 	if (val < 0 || val > 1)
3344a8d7fc4aSZbigniew Bodek 		return (EINVAL);
3345a8d7fc4aSZbigniew Bodek 
3346a8d7fc4aSZbigniew Bodek 	if (val == 1) {
3347a8d7fc4aSZbigniew Bodek 		mvneta_sc_lock(sc);
3348a8d7fc4aSZbigniew Bodek 		mvneta_clear_mib(sc);
3349a8d7fc4aSZbigniew Bodek 		mvneta_sc_unlock(sc);
3350a8d7fc4aSZbigniew Bodek 	}
3351a8d7fc4aSZbigniew Bodek 
3352a8d7fc4aSZbigniew Bodek 	return (0);
3353a8d7fc4aSZbigniew Bodek }
3354a8d7fc4aSZbigniew Bodek 
3355a8d7fc4aSZbigniew Bodek STATIC int
3356a8d7fc4aSZbigniew Bodek sysctl_set_queue_rxthtime(SYSCTL_HANDLER_ARGS)
3357a8d7fc4aSZbigniew Bodek {
3358a8d7fc4aSZbigniew Bodek 	struct mvneta_sysctl_queue *arg;
3359a8d7fc4aSZbigniew Bodek 	struct mvneta_rx_ring *rx;
3360a8d7fc4aSZbigniew Bodek 	struct mvneta_softc *sc;
3361a8d7fc4aSZbigniew Bodek 	uint32_t reg, time_mvtclk;
3362a8d7fc4aSZbigniew Bodek 	int err, time_us;
3363a8d7fc4aSZbigniew Bodek 
3364a8d7fc4aSZbigniew Bodek 	rx = NULL;
3365a8d7fc4aSZbigniew Bodek 	arg = (struct mvneta_sysctl_queue *)arg1;
3366a8d7fc4aSZbigniew Bodek 	if (arg == NULL)
3367a8d7fc4aSZbigniew Bodek 		return (EINVAL);
3368a8d7fc4aSZbigniew Bodek 	if (arg->queue < 0 || arg->queue > MVNETA_RX_RING_CNT)
3369a8d7fc4aSZbigniew Bodek 		return (EINVAL);
3370a8d7fc4aSZbigniew Bodek 	if (arg->rxtx != MVNETA_SYSCTL_RX)
3371a8d7fc4aSZbigniew Bodek 		return (EINVAL);
3372a8d7fc4aSZbigniew Bodek 
3373a8d7fc4aSZbigniew Bodek 	sc = arg->sc;
3374a8d7fc4aSZbigniew Bodek 	if (sc == NULL)
3375a8d7fc4aSZbigniew Bodek 		return (EINVAL);
3376a8d7fc4aSZbigniew Bodek 
3377a8d7fc4aSZbigniew Bodek 	/* read queue length */
3378a8d7fc4aSZbigniew Bodek 	mvneta_sc_lock(sc);
3379a8d7fc4aSZbigniew Bodek 	mvneta_rx_lockq(sc, arg->queue);
3380a8d7fc4aSZbigniew Bodek 	rx = MVNETA_RX_RING(sc, arg->queue);
3381a8d7fc4aSZbigniew Bodek 	time_mvtclk = rx->queue_th_time;
33824885d6f3SHubert Mazur 	time_us = ((uint64_t)time_mvtclk * 1000ULL * 1000ULL) / sc->clk_freq;
3383a8d7fc4aSZbigniew Bodek 	mvneta_rx_unlockq(sc, arg->queue);
3384a8d7fc4aSZbigniew Bodek 	mvneta_sc_unlock(sc);
3385a8d7fc4aSZbigniew Bodek 
3386a8d7fc4aSZbigniew Bodek 	err = sysctl_handle_int(oidp, &time_us, 0, req);
3387a8d7fc4aSZbigniew Bodek 	if (err != 0)
3388a8d7fc4aSZbigniew Bodek 		return (err);
3389a8d7fc4aSZbigniew Bodek 
3390a8d7fc4aSZbigniew Bodek 	mvneta_sc_lock(sc);
3391a8d7fc4aSZbigniew Bodek 	mvneta_rx_lockq(sc, arg->queue);
3392a8d7fc4aSZbigniew Bodek 
3393a8d7fc4aSZbigniew Bodek 	/* update queue length (0[sec] - 1[sec]) */
3394a8d7fc4aSZbigniew Bodek 	if (time_us < 0 || time_us > (1000 * 1000)) {
3395a8d7fc4aSZbigniew Bodek 		mvneta_rx_unlockq(sc, arg->queue);
3396a8d7fc4aSZbigniew Bodek 		mvneta_sc_unlock(sc);
3397a8d7fc4aSZbigniew Bodek 		return (EINVAL);
3398a8d7fc4aSZbigniew Bodek 	}
33994885d6f3SHubert Mazur 	time_mvtclk = sc->clk_freq * (uint64_t)time_us / (1000ULL * 1000ULL);
3400a8d7fc4aSZbigniew Bodek 	rx->queue_th_time = time_mvtclk;
3401a8d7fc4aSZbigniew Bodek 	reg = MVNETA_PRXITTH_RITT(rx->queue_th_time);
3402a8d7fc4aSZbigniew Bodek 	MVNETA_WRITE(sc, MVNETA_PRXITTH(arg->queue), reg);
3403a8d7fc4aSZbigniew Bodek 	mvneta_rx_unlockq(sc, arg->queue);
3404a8d7fc4aSZbigniew Bodek 	mvneta_sc_unlock(sc);
3405a8d7fc4aSZbigniew Bodek 
3406a8d7fc4aSZbigniew Bodek 	return (0);
3407a8d7fc4aSZbigniew Bodek }
3408a8d7fc4aSZbigniew Bodek 
3409a8d7fc4aSZbigniew Bodek STATIC void
3410a8d7fc4aSZbigniew Bodek sysctl_mvneta_init(struct mvneta_softc *sc)
3411a8d7fc4aSZbigniew Bodek {
3412a8d7fc4aSZbigniew Bodek 	struct sysctl_ctx_list *ctx;
3413a8d7fc4aSZbigniew Bodek 	struct sysctl_oid_list *children;
3414a8d7fc4aSZbigniew Bodek 	struct sysctl_oid_list *rxchildren;
3415a8d7fc4aSZbigniew Bodek 	struct sysctl_oid_list *qchildren, *mchildren;
3416a8d7fc4aSZbigniew Bodek 	struct sysctl_oid *tree;
3417a8d7fc4aSZbigniew Bodek 	int i, q;
3418a8d7fc4aSZbigniew Bodek 	struct mvneta_sysctl_queue *rxarg;
3419a8d7fc4aSZbigniew Bodek #define	MVNETA_SYSCTL_NAME(num) "queue" # num
3420a8d7fc4aSZbigniew Bodek 	static const char *sysctl_queue_names[] = {
3421a8d7fc4aSZbigniew Bodek 		MVNETA_SYSCTL_NAME(0), MVNETA_SYSCTL_NAME(1),
3422a8d7fc4aSZbigniew Bodek 		MVNETA_SYSCTL_NAME(2), MVNETA_SYSCTL_NAME(3),
3423a8d7fc4aSZbigniew Bodek 		MVNETA_SYSCTL_NAME(4), MVNETA_SYSCTL_NAME(5),
3424a8d7fc4aSZbigniew Bodek 		MVNETA_SYSCTL_NAME(6), MVNETA_SYSCTL_NAME(7),
3425a8d7fc4aSZbigniew Bodek 	};
3426a8d7fc4aSZbigniew Bodek #undef MVNETA_SYSCTL_NAME
3427a8d7fc4aSZbigniew Bodek 
34283bc4bd5eSNick Hibma #ifndef NO_SYSCTL_DESCR
3429a8d7fc4aSZbigniew Bodek #define	MVNETA_SYSCTL_DESCR(num) "configuration parameters for queue " # num
3430a8d7fc4aSZbigniew Bodek 	static const char *sysctl_queue_descrs[] = {
3431a8d7fc4aSZbigniew Bodek 		MVNETA_SYSCTL_DESCR(0), MVNETA_SYSCTL_DESCR(1),
3432a8d7fc4aSZbigniew Bodek 		MVNETA_SYSCTL_DESCR(2), MVNETA_SYSCTL_DESCR(3),
3433a8d7fc4aSZbigniew Bodek 		MVNETA_SYSCTL_DESCR(4), MVNETA_SYSCTL_DESCR(5),
3434a8d7fc4aSZbigniew Bodek 		MVNETA_SYSCTL_DESCR(6), MVNETA_SYSCTL_DESCR(7),
3435a8d7fc4aSZbigniew Bodek 	};
3436a8d7fc4aSZbigniew Bodek #undef MVNETA_SYSCTL_DESCR
34373bc4bd5eSNick Hibma #endif
3438a8d7fc4aSZbigniew Bodek 
3439a8d7fc4aSZbigniew Bodek 
3440a8d7fc4aSZbigniew Bodek 	ctx = device_get_sysctl_ctx(sc->dev);
3441a8d7fc4aSZbigniew Bodek 	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
3442a8d7fc4aSZbigniew Bodek 
3443a8d7fc4aSZbigniew Bodek 	tree = SYSCTL_ADD_NODE(ctx, children, OID_AUTO, "rx",
34447029da5cSPawel Biernacki 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "NETA RX");
3445a8d7fc4aSZbigniew Bodek 	rxchildren = SYSCTL_CHILDREN(tree);
3446a8d7fc4aSZbigniew Bodek 	tree = SYSCTL_ADD_NODE(ctx, children, OID_AUTO, "mib",
34477029da5cSPawel Biernacki 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "NETA MIB");
3448a8d7fc4aSZbigniew Bodek 	mchildren = SYSCTL_CHILDREN(tree);
3449a8d7fc4aSZbigniew Bodek 
3450a8d7fc4aSZbigniew Bodek 
3451a8d7fc4aSZbigniew Bodek 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "flow_control",
3452a8d7fc4aSZbigniew Bodek 	    CTLFLAG_RW, &sc->cf_fc, 0, "flow control");
3453a8d7fc4aSZbigniew Bodek 	SYSCTL_ADD_INT(ctx, children, OID_AUTO, "lpi",
3454a8d7fc4aSZbigniew Bodek 	    CTLFLAG_RW, &sc->cf_lpi, 0, "Low Power Idle");
3455a8d7fc4aSZbigniew Bodek 
3456a8d7fc4aSZbigniew Bodek 	/*
3457a8d7fc4aSZbigniew Bodek 	 * MIB access
3458a8d7fc4aSZbigniew Bodek 	 */
3459a8d7fc4aSZbigniew Bodek 	/* dev.mvneta.[unit].mib.<mibs> */
3460a8d7fc4aSZbigniew Bodek 	for (i = 0; i < MVNETA_PORTMIB_NOCOUNTER; i++) {
3461a8d7fc4aSZbigniew Bodek 		struct mvneta_sysctl_mib *mib_arg = &sc->sysctl_mib[i];
3462a8d7fc4aSZbigniew Bodek 
3463a8d7fc4aSZbigniew Bodek 		mib_arg->sc = sc;
3464a8d7fc4aSZbigniew Bodek 		mib_arg->index = i;
34653bc4bd5eSNick Hibma 		SYSCTL_ADD_PROC(ctx, mchildren, OID_AUTO,
34663bc4bd5eSNick Hibma 		    mvneta_mib_list[i].sysctl_name,
34677029da5cSPawel Biernacki 		    CTLTYPE_U64 | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
34687029da5cSPawel Biernacki 		    (void *)mib_arg, 0, sysctl_read_mib, "I",
34697029da5cSPawel Biernacki 		    mvneta_mib_list[i].desc);
3470a8d7fc4aSZbigniew Bodek 	}
3471a8d7fc4aSZbigniew Bodek 	SYSCTL_ADD_UQUAD(ctx, mchildren, OID_AUTO, "rx_discard",
3472a8d7fc4aSZbigniew Bodek 	    CTLFLAG_RD, &sc->counter_pdfc, "Port Rx Discard Frame Counter");
3473a8d7fc4aSZbigniew Bodek 	SYSCTL_ADD_UQUAD(ctx, mchildren, OID_AUTO, "overrun",
3474a8d7fc4aSZbigniew Bodek 	    CTLFLAG_RD, &sc->counter_pofc, "Port Overrun Frame Counter");
3475a8d7fc4aSZbigniew Bodek 	SYSCTL_ADD_UINT(ctx, mchildren, OID_AUTO, "watchdog",
3476a8d7fc4aSZbigniew Bodek 	    CTLFLAG_RD, &sc->counter_watchdog, 0, "TX Watchdog Counter");
3477a8d7fc4aSZbigniew Bodek 
3478a8d7fc4aSZbigniew Bodek 	SYSCTL_ADD_PROC(ctx, mchildren, OID_AUTO, "reset",
34797029da5cSPawel Biernacki 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
34807029da5cSPawel Biernacki 	    (void *)sc, 0, sysctl_clear_mib, "I", "Reset MIB counters");
3481a8d7fc4aSZbigniew Bodek 
3482a8d7fc4aSZbigniew Bodek 	for (q = 0; q < MVNETA_RX_QNUM_MAX; q++) {
3483a8d7fc4aSZbigniew Bodek 		rxarg = &sc->sysctl_rx_queue[q];
3484a8d7fc4aSZbigniew Bodek 
3485a8d7fc4aSZbigniew Bodek 		rxarg->sc = sc;
3486a8d7fc4aSZbigniew Bodek 		rxarg->queue = q;
3487a8d7fc4aSZbigniew Bodek 		rxarg->rxtx = MVNETA_SYSCTL_RX;
3488a8d7fc4aSZbigniew Bodek 
3489a8d7fc4aSZbigniew Bodek 		/* hw.mvneta.mvneta[unit].rx.[queue] */
3490a8d7fc4aSZbigniew Bodek 		tree = SYSCTL_ADD_NODE(ctx, rxchildren, OID_AUTO,
34917029da5cSPawel Biernacki 		    sysctl_queue_names[q], CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
3492a8d7fc4aSZbigniew Bodek 		    sysctl_queue_descrs[q]);
3493a8d7fc4aSZbigniew Bodek 		qchildren = SYSCTL_CHILDREN(tree);
3494a8d7fc4aSZbigniew Bodek 
3495a8d7fc4aSZbigniew Bodek 		/* hw.mvneta.mvneta[unit].rx.[queue].threshold_timer_us */
3496a8d7fc4aSZbigniew Bodek 		SYSCTL_ADD_PROC(ctx, qchildren, OID_AUTO, "threshold_timer_us",
34977029da5cSPawel Biernacki 		    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, rxarg, 0,
3498a8d7fc4aSZbigniew Bodek 		    sysctl_set_queue_rxthtime, "I",
3499a8d7fc4aSZbigniew Bodek 		    "interrupt coalescing threshold timer [us]");
3500a8d7fc4aSZbigniew Bodek 	}
3501a8d7fc4aSZbigniew Bodek }
3502a8d7fc4aSZbigniew Bodek 
3503a8d7fc4aSZbigniew Bodek /*
3504a8d7fc4aSZbigniew Bodek  * MIB
3505a8d7fc4aSZbigniew Bodek  */
3506caf552a6SMark Johnston STATIC uint64_t
3507caf552a6SMark Johnston mvneta_read_mib(struct mvneta_softc *sc, int index)
3508caf552a6SMark Johnston {
3509caf552a6SMark Johnston 	struct mvneta_mib_def *mib;
3510caf552a6SMark Johnston 	uint64_t val;
3511caf552a6SMark Johnston 
3512caf552a6SMark Johnston 	mib = &mvneta_mib_list[index];
3513caf552a6SMark Johnston 	val = MVNETA_READ_MIB(sc, mib->regnum);
3514caf552a6SMark Johnston 	if (mib->reg64)
3515caf552a6SMark Johnston 		val |= (uint64_t)MVNETA_READ_MIB(sc, mib->regnum + 4) << 32;
3516caf552a6SMark Johnston 	return (val);
3517caf552a6SMark Johnston }
3518caf552a6SMark Johnston 
3519a8d7fc4aSZbigniew Bodek STATIC void
3520a8d7fc4aSZbigniew Bodek mvneta_clear_mib(struct mvneta_softc *sc)
3521a8d7fc4aSZbigniew Bodek {
3522a8d7fc4aSZbigniew Bodek 	int i;
3523a8d7fc4aSZbigniew Bodek 
3524a8d7fc4aSZbigniew Bodek 	KASSERT_SC_MTX(sc);
3525a8d7fc4aSZbigniew Bodek 
3526a8d7fc4aSZbigniew Bodek 	for (i = 0; i < nitems(mvneta_mib_list); i++) {
3527caf552a6SMark Johnston 		(void)mvneta_read_mib(sc, i);
3528a8d7fc4aSZbigniew Bodek 		sc->sysctl_mib[i].counter = 0;
3529a8d7fc4aSZbigniew Bodek 	}
3530a8d7fc4aSZbigniew Bodek 	MVNETA_READ(sc, MVNETA_PDFC);
3531a8d7fc4aSZbigniew Bodek 	sc->counter_pdfc = 0;
3532a8d7fc4aSZbigniew Bodek 	MVNETA_READ(sc, MVNETA_POFC);
3533a8d7fc4aSZbigniew Bodek 	sc->counter_pofc = 0;
3534a8d7fc4aSZbigniew Bodek 	sc->counter_watchdog = 0;
3535a8d7fc4aSZbigniew Bodek }
3536a8d7fc4aSZbigniew Bodek 
3537a8d7fc4aSZbigniew Bodek STATIC void
3538a8d7fc4aSZbigniew Bodek mvneta_update_mib(struct mvneta_softc *sc)
3539a8d7fc4aSZbigniew Bodek {
3540a8d7fc4aSZbigniew Bodek 	struct mvneta_tx_ring *tx;
3541a8d7fc4aSZbigniew Bodek 	int i;
3542a8d7fc4aSZbigniew Bodek 	uint64_t val;
3543a8d7fc4aSZbigniew Bodek 	uint32_t reg;
3544a8d7fc4aSZbigniew Bodek 
3545a8d7fc4aSZbigniew Bodek 	for (i = 0; i < nitems(mvneta_mib_list); i++) {
3546a8d7fc4aSZbigniew Bodek 
3547caf552a6SMark Johnston 		val = mvneta_read_mib(sc, i);
3548a8d7fc4aSZbigniew Bodek 		if (val == 0)
3549a8d7fc4aSZbigniew Bodek 			continue;
3550a8d7fc4aSZbigniew Bodek 
3551a8d7fc4aSZbigniew Bodek 		sc->sysctl_mib[i].counter += val;
3552a8d7fc4aSZbigniew Bodek 		switch (mvneta_mib_list[i].regnum) {
3553a8d7fc4aSZbigniew Bodek 			case MVNETA_MIB_RX_GOOD_OCT:
3554a8d7fc4aSZbigniew Bodek 				if_inc_counter(sc->ifp, IFCOUNTER_IBYTES, val);
3555a8d7fc4aSZbigniew Bodek 				break;
3556a8d7fc4aSZbigniew Bodek 			case MVNETA_MIB_RX_BAD_FRAME:
3557a8d7fc4aSZbigniew Bodek 				if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, val);
3558a8d7fc4aSZbigniew Bodek 				break;
3559a8d7fc4aSZbigniew Bodek 			case MVNETA_MIB_RX_GOOD_FRAME:
3560a8d7fc4aSZbigniew Bodek 				if_inc_counter(sc->ifp, IFCOUNTER_IPACKETS, val);
3561a8d7fc4aSZbigniew Bodek 				break;
3562a8d7fc4aSZbigniew Bodek 			case MVNETA_MIB_RX_MCAST_FRAME:
3563a8d7fc4aSZbigniew Bodek 				if_inc_counter(sc->ifp, IFCOUNTER_IMCASTS, val);
3564a8d7fc4aSZbigniew Bodek 				break;
3565a8d7fc4aSZbigniew Bodek 			case MVNETA_MIB_TX_GOOD_OCT:
3566a8d7fc4aSZbigniew Bodek 				if_inc_counter(sc->ifp, IFCOUNTER_OBYTES, val);
3567a8d7fc4aSZbigniew Bodek 				break;
3568a8d7fc4aSZbigniew Bodek 			case MVNETA_MIB_TX_GOOD_FRAME:
3569a8d7fc4aSZbigniew Bodek 				if_inc_counter(sc->ifp, IFCOUNTER_OPACKETS, val);
3570a8d7fc4aSZbigniew Bodek 				break;
3571a8d7fc4aSZbigniew Bodek 			case MVNETA_MIB_TX_MCAST_FRAME:
3572a8d7fc4aSZbigniew Bodek 				if_inc_counter(sc->ifp, IFCOUNTER_OMCASTS, val);
3573a8d7fc4aSZbigniew Bodek 				break;
3574a8d7fc4aSZbigniew Bodek 			case MVNETA_MIB_MAC_COL:
3575a8d7fc4aSZbigniew Bodek 				if_inc_counter(sc->ifp, IFCOUNTER_COLLISIONS, val);
3576a8d7fc4aSZbigniew Bodek 				break;
3577a8d7fc4aSZbigniew Bodek 			case MVNETA_MIB_TX_MAC_TRNS_ERR:
3578a8d7fc4aSZbigniew Bodek 			case MVNETA_MIB_TX_EXCES_COL:
3579a8d7fc4aSZbigniew Bodek 			case MVNETA_MIB_MAC_LATE_COL:
3580a8d7fc4aSZbigniew Bodek 				if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, val);
3581a8d7fc4aSZbigniew Bodek 				break;
3582a8d7fc4aSZbigniew Bodek 		}
3583a8d7fc4aSZbigniew Bodek 	}
3584a8d7fc4aSZbigniew Bodek 
3585a8d7fc4aSZbigniew Bodek 	reg = MVNETA_READ(sc, MVNETA_PDFC);
3586a8d7fc4aSZbigniew Bodek 	sc->counter_pdfc += reg;
3587a8d7fc4aSZbigniew Bodek 	if_inc_counter(sc->ifp, IFCOUNTER_IQDROPS, reg);
3588a8d7fc4aSZbigniew Bodek 	reg = MVNETA_READ(sc, MVNETA_POFC);
3589a8d7fc4aSZbigniew Bodek 	sc->counter_pofc += reg;
3590a8d7fc4aSZbigniew Bodek 	if_inc_counter(sc->ifp, IFCOUNTER_IQDROPS, reg);
3591a8d7fc4aSZbigniew Bodek 
3592a8d7fc4aSZbigniew Bodek 	/* TX watchdog. */
3593a8d7fc4aSZbigniew Bodek 	if (sc->counter_watchdog_mib > 0) {
3594a8d7fc4aSZbigniew Bodek 		if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, sc->counter_watchdog_mib);
3595a8d7fc4aSZbigniew Bodek 		sc->counter_watchdog_mib = 0;
3596a8d7fc4aSZbigniew Bodek 	}
3597a8d7fc4aSZbigniew Bodek 	/*
3598a8d7fc4aSZbigniew Bodek 	 * TX driver errors:
3599a8d7fc4aSZbigniew Bodek 	 * We do not take queue locks to not disrupt TX path.
3600a8d7fc4aSZbigniew Bodek 	 * We may only miss one drv error which will be fixed at
3601a8d7fc4aSZbigniew Bodek 	 * next mib update. We may also clear counter when TX path
3602a8d7fc4aSZbigniew Bodek 	 * is incrementing it but we only do it if counter was not zero
3603a8d7fc4aSZbigniew Bodek 	 * thus we may only loose one error.
3604a8d7fc4aSZbigniew Bodek 	 */
3605a8d7fc4aSZbigniew Bodek 	for (i = 0; i < MVNETA_TX_QNUM_MAX; i++) {
3606a8d7fc4aSZbigniew Bodek 		tx = MVNETA_TX_RING(sc, i);
3607a8d7fc4aSZbigniew Bodek 
3608a8d7fc4aSZbigniew Bodek 		if (tx->drv_error > 0) {
3609a8d7fc4aSZbigniew Bodek 			if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, tx->drv_error);
3610a8d7fc4aSZbigniew Bodek 			tx->drv_error = 0;
3611a8d7fc4aSZbigniew Bodek 		}
3612a8d7fc4aSZbigniew Bodek 	}
3613a8d7fc4aSZbigniew Bodek }
3614