xref: /freebsd/sys/dev/sfxge/sfxge_port.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
1e948693eSPhilip Paeps /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
4929c7febSAndrew Rybchenko  * Copyright (c) 2010-2016 Solarflare Communications Inc.
5e948693eSPhilip Paeps  * All rights reserved.
6e948693eSPhilip Paeps  *
7e948693eSPhilip Paeps  * This software was developed in part by Philip Paeps under contract for
8e948693eSPhilip Paeps  * Solarflare Communications, Inc.
9e948693eSPhilip Paeps  *
10e948693eSPhilip Paeps  * Redistribution and use in source and binary forms, with or without
113c838a9fSAndrew Rybchenko  * modification, are permitted provided that the following conditions are met:
12e948693eSPhilip Paeps  *
133c838a9fSAndrew Rybchenko  * 1. Redistributions of source code must retain the above copyright notice,
143c838a9fSAndrew Rybchenko  *    this list of conditions and the following disclaimer.
153c838a9fSAndrew Rybchenko  * 2. Redistributions in binary form must reproduce the above copyright notice,
163c838a9fSAndrew Rybchenko  *    this list of conditions and the following disclaimer in the documentation
173c838a9fSAndrew Rybchenko  *    and/or other materials provided with the distribution.
183c838a9fSAndrew Rybchenko  *
193c838a9fSAndrew Rybchenko  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
203c838a9fSAndrew Rybchenko  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
213c838a9fSAndrew Rybchenko  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
223c838a9fSAndrew Rybchenko  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
233c838a9fSAndrew Rybchenko  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
243c838a9fSAndrew Rybchenko  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
253c838a9fSAndrew Rybchenko  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
263c838a9fSAndrew Rybchenko  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
273c838a9fSAndrew Rybchenko  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
283c838a9fSAndrew Rybchenko  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
293c838a9fSAndrew Rybchenko  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
303c838a9fSAndrew Rybchenko  *
313c838a9fSAndrew Rybchenko  * The views and conclusions contained in the software and documentation are
323c838a9fSAndrew Rybchenko  * those of the authors and should not be interpreted as representing official
333c838a9fSAndrew Rybchenko  * policies, either expressed or implied, of the FreeBSD Project.
34e948693eSPhilip Paeps  */
35e948693eSPhilip Paeps 
36e948693eSPhilip Paeps #include <sys/types.h>
3702602e0eSPhilip Paeps #include <sys/limits.h>
38e948693eSPhilip Paeps #include <net/ethernet.h>
39e948693eSPhilip Paeps #include <net/if_dl.h>
40e948693eSPhilip Paeps 
41e948693eSPhilip Paeps #include "common/efx.h"
42e948693eSPhilip Paeps 
43e948693eSPhilip Paeps #include "sfxge.h"
44e948693eSPhilip Paeps 
4558223d5bSAndrew Rybchenko #define	SFXGE_PARAM_STATS_UPDATE_PERIOD_MS \
4658223d5bSAndrew Rybchenko 	SFXGE_PARAM(stats_update_period_ms)
4758223d5bSAndrew Rybchenko static int sfxge_stats_update_period_ms = SFXGE_STATS_UPDATE_PERIOD_MS;
4858223d5bSAndrew Rybchenko TUNABLE_INT(SFXGE_PARAM_STATS_UPDATE_PERIOD_MS,
4958223d5bSAndrew Rybchenko 	    &sfxge_stats_update_period_ms);
5058223d5bSAndrew Rybchenko SYSCTL_INT(_hw_sfxge, OID_AUTO, stats_update_period_ms, CTLFLAG_RDTUN,
5158223d5bSAndrew Rybchenko 	   &sfxge_stats_update_period_ms, 0,
5258223d5bSAndrew Rybchenko 	   "netstat interface statistics update period in milliseconds");
5358223d5bSAndrew Rybchenko 
54c379e930SAndrew Rybchenko static int sfxge_phy_cap_mask(struct sfxge_softc *, int, uint32_t *);
55c379e930SAndrew Rybchenko 
56e948693eSPhilip Paeps static int
sfxge_mac_stat_update(struct sfxge_softc * sc)57e948693eSPhilip Paeps sfxge_mac_stat_update(struct sfxge_softc *sc)
58e948693eSPhilip Paeps {
59e948693eSPhilip Paeps 	struct sfxge_port *port = &sc->port;
60e948693eSPhilip Paeps 	efsys_mem_t *esmp = &(port->mac_stats.dma_buf);
61e948693eSPhilip Paeps 	clock_t now;
6262f5c496SAndrew Rybchenko 	unsigned int min_ticks;
63e948693eSPhilip Paeps 	unsigned int count;
64e948693eSPhilip Paeps 	int rc;
65e948693eSPhilip Paeps 
66d8e7a280SAndrew Rybchenko 	SFXGE_PORT_LOCK_ASSERT_OWNED(port);
67e948693eSPhilip Paeps 
68851128b8SAndrew Rybchenko 	if (__predict_false(port->init_state != SFXGE_PORT_STARTED)) {
69e948693eSPhilip Paeps 		rc = 0;
70e948693eSPhilip Paeps 		goto out;
71e948693eSPhilip Paeps 	}
72e948693eSPhilip Paeps 
7358223d5bSAndrew Rybchenko 	min_ticks = (unsigned int)hz * port->stats_update_period_ms / 1000;
7462f5c496SAndrew Rybchenko 
75e948693eSPhilip Paeps 	now = ticks;
7662f5c496SAndrew Rybchenko 	if ((unsigned int)(now - port->mac_stats.update_time) < min_ticks) {
77e948693eSPhilip Paeps 		rc = 0;
78e948693eSPhilip Paeps 		goto out;
79e948693eSPhilip Paeps 	}
80e948693eSPhilip Paeps 
81e948693eSPhilip Paeps 	port->mac_stats.update_time = now;
82e948693eSPhilip Paeps 
83e948693eSPhilip Paeps 	/* If we're unlucky enough to read statistics wduring the DMA, wait
84e948693eSPhilip Paeps 	 * up to 10ms for it to finish (typically takes <500us) */
85e948693eSPhilip Paeps 	for (count = 0; count < 100; ++count) {
86e948693eSPhilip Paeps 		EFSYS_PROBE1(wait, unsigned int, count);
87e948693eSPhilip Paeps 
88e948693eSPhilip Paeps 		/* Try to update the cached counters */
89e948693eSPhilip Paeps 		if ((rc = efx_mac_stats_update(sc->enp, esmp,
90e948693eSPhilip Paeps 		    port->mac_stats.decode_buf, NULL)) != EAGAIN)
91e948693eSPhilip Paeps 			goto out;
92e948693eSPhilip Paeps 
93e948693eSPhilip Paeps 		DELAY(100);
94e948693eSPhilip Paeps 	}
95e948693eSPhilip Paeps 
96e948693eSPhilip Paeps 	rc = ETIMEDOUT;
97e948693eSPhilip Paeps out:
98b7b0edd1SGeorge V. Neville-Neil 	return (rc);
99e948693eSPhilip Paeps }
100e948693eSPhilip Paeps 
1013d8fce27SAndrew Rybchenko uint64_t
sfxge_get_counter(if_t ifp,ift_counter c)10204abf87bSJustin Hibbits sfxge_get_counter(if_t ifp, ift_counter c)
1033d8fce27SAndrew Rybchenko {
10404abf87bSJustin Hibbits 	struct sfxge_softc *sc = if_getsoftc(ifp);
1053d8fce27SAndrew Rybchenko 	uint64_t *mac_stats;
1063d8fce27SAndrew Rybchenko 	uint64_t val;
1073d8fce27SAndrew Rybchenko 
1083d8fce27SAndrew Rybchenko 	SFXGE_PORT_LOCK(&sc->port);
1093d8fce27SAndrew Rybchenko 
1103d8fce27SAndrew Rybchenko 	/* Ignore error and use old values */
1113d8fce27SAndrew Rybchenko 	(void)sfxge_mac_stat_update(sc);
1123d8fce27SAndrew Rybchenko 
1133d8fce27SAndrew Rybchenko 	mac_stats = (uint64_t *)sc->port.mac_stats.decode_buf;
1143d8fce27SAndrew Rybchenko 
1153d8fce27SAndrew Rybchenko 	switch (c) {
1163d8fce27SAndrew Rybchenko 	case IFCOUNTER_IPACKETS:
1173d8fce27SAndrew Rybchenko 		val = mac_stats[EFX_MAC_RX_PKTS];
1183d8fce27SAndrew Rybchenko 		break;
1193d8fce27SAndrew Rybchenko 	case IFCOUNTER_IERRORS:
1203d8fce27SAndrew Rybchenko 		val = mac_stats[EFX_MAC_RX_ERRORS];
1213d8fce27SAndrew Rybchenko 		break;
1223d8fce27SAndrew Rybchenko 	case IFCOUNTER_OPACKETS:
1233d8fce27SAndrew Rybchenko 		val = mac_stats[EFX_MAC_TX_PKTS];
1243d8fce27SAndrew Rybchenko 		break;
1253d8fce27SAndrew Rybchenko 	case IFCOUNTER_OERRORS:
1263d8fce27SAndrew Rybchenko 		val = mac_stats[EFX_MAC_TX_ERRORS];
1273d8fce27SAndrew Rybchenko 		break;
1283d8fce27SAndrew Rybchenko 	case IFCOUNTER_COLLISIONS:
1293d8fce27SAndrew Rybchenko 		val = mac_stats[EFX_MAC_TX_SGL_COL_PKTS] +
1303d8fce27SAndrew Rybchenko 		      mac_stats[EFX_MAC_TX_MULT_COL_PKTS] +
1313d8fce27SAndrew Rybchenko 		      mac_stats[EFX_MAC_TX_EX_COL_PKTS] +
1323d8fce27SAndrew Rybchenko 		      mac_stats[EFX_MAC_TX_LATE_COL_PKTS];
1333d8fce27SAndrew Rybchenko 		break;
1343d8fce27SAndrew Rybchenko 	case IFCOUNTER_IBYTES:
1353d8fce27SAndrew Rybchenko 		val = mac_stats[EFX_MAC_RX_OCTETS];
1363d8fce27SAndrew Rybchenko 		break;
1373d8fce27SAndrew Rybchenko 	case IFCOUNTER_OBYTES:
1383d8fce27SAndrew Rybchenko 		val = mac_stats[EFX_MAC_TX_OCTETS];
1393d8fce27SAndrew Rybchenko 		break;
1403d8fce27SAndrew Rybchenko 	case IFCOUNTER_OMCASTS:
1413d8fce27SAndrew Rybchenko 		val = mac_stats[EFX_MAC_TX_MULTICST_PKTS] +
1423d8fce27SAndrew Rybchenko 		      mac_stats[EFX_MAC_TX_BRDCST_PKTS];
1433d8fce27SAndrew Rybchenko 		break;
1443d8fce27SAndrew Rybchenko 	case IFCOUNTER_OQDROPS:
1453d8fce27SAndrew Rybchenko 		SFXGE_PORT_UNLOCK(&sc->port);
1463d8fce27SAndrew Rybchenko 		return (sfxge_tx_get_drops(sc));
1473d8fce27SAndrew Rybchenko 	case IFCOUNTER_IMCASTS:
1483d8fce27SAndrew Rybchenko 		/* if_imcasts is maintained in net/if_ethersubr.c */
1493d8fce27SAndrew Rybchenko 	case IFCOUNTER_IQDROPS:
1503d8fce27SAndrew Rybchenko 		/* if_iqdrops is maintained in net/if_ethersubr.c */
1513d8fce27SAndrew Rybchenko 	case IFCOUNTER_NOPROTO:
1523d8fce27SAndrew Rybchenko 		/* if_noproto is maintained in net/if_ethersubr.c */
1533d8fce27SAndrew Rybchenko 	default:
1543d8fce27SAndrew Rybchenko 		SFXGE_PORT_UNLOCK(&sc->port);
1553d8fce27SAndrew Rybchenko 		return (if_get_counter_default(ifp, c));
1563d8fce27SAndrew Rybchenko 	}
1573d8fce27SAndrew Rybchenko 
1583d8fce27SAndrew Rybchenko 	SFXGE_PORT_UNLOCK(&sc->port);
1593d8fce27SAndrew Rybchenko 
1603d8fce27SAndrew Rybchenko 	return (val);
1613d8fce27SAndrew Rybchenko }
1623d8fce27SAndrew Rybchenko 
163e948693eSPhilip Paeps static int
sfxge_mac_stat_handler(SYSCTL_HANDLER_ARGS)164e948693eSPhilip Paeps sfxge_mac_stat_handler(SYSCTL_HANDLER_ARGS)
165e948693eSPhilip Paeps {
166e948693eSPhilip Paeps 	struct sfxge_softc *sc = arg1;
167e948693eSPhilip Paeps 	unsigned int id = arg2;
168e948693eSPhilip Paeps 	int rc;
169d67580d1SAndrew Rybchenko 	uint64_t val;
170e948693eSPhilip Paeps 
171d8e7a280SAndrew Rybchenko 	SFXGE_PORT_LOCK(&sc->port);
172d67580d1SAndrew Rybchenko 	if ((rc = sfxge_mac_stat_update(sc)) == 0)
173d67580d1SAndrew Rybchenko 		val = ((uint64_t *)sc->port.mac_stats.decode_buf)[id];
174d8e7a280SAndrew Rybchenko 	SFXGE_PORT_UNLOCK(&sc->port);
175d67580d1SAndrew Rybchenko 
176d67580d1SAndrew Rybchenko 	if (rc == 0)
177d67580d1SAndrew Rybchenko 		rc = SYSCTL_OUT(req, &val, sizeof(val));
178d8e7a280SAndrew Rybchenko 	return (rc);
179e948693eSPhilip Paeps }
180e948693eSPhilip Paeps 
181e948693eSPhilip Paeps static void
sfxge_mac_stat_init(struct sfxge_softc * sc)182e948693eSPhilip Paeps sfxge_mac_stat_init(struct sfxge_softc *sc)
183e948693eSPhilip Paeps {
184e948693eSPhilip Paeps 	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev);
185e948693eSPhilip Paeps 	struct sysctl_oid_list *stat_list;
186e948693eSPhilip Paeps 	unsigned int id;
187e948693eSPhilip Paeps 	const char *name;
188e948693eSPhilip Paeps 
189e948693eSPhilip Paeps 	stat_list = SYSCTL_CHILDREN(sc->stats_node);
190e948693eSPhilip Paeps 
191e948693eSPhilip Paeps 	/* Initialise the named stats */
192e948693eSPhilip Paeps 	for (id = 0; id < EFX_MAC_NSTATS; id++) {
193e948693eSPhilip Paeps 		name = efx_mac_stat_name(sc->enp, id);
1947029da5cSPawel Biernacki 		SYSCTL_ADD_PROC(ctx, stat_list, OID_AUTO, name,
1957029da5cSPawel Biernacki 		    CTLTYPE_U64 | CTLFLAG_RD | CTLFLAG_MPSAFE,
1967029da5cSPawel Biernacki 		    sc, id, sfxge_mac_stat_handler, "Q", "");
197e948693eSPhilip Paeps 	}
198e948693eSPhilip Paeps }
199e948693eSPhilip Paeps 
200e948693eSPhilip Paeps #ifdef SFXGE_HAVE_PAUSE_MEDIAOPTS
201e948693eSPhilip Paeps 
202e948693eSPhilip Paeps static unsigned int
sfxge_port_wanted_fc(struct sfxge_softc * sc)203e948693eSPhilip Paeps sfxge_port_wanted_fc(struct sfxge_softc *sc)
204e948693eSPhilip Paeps {
205e948693eSPhilip Paeps 	struct ifmedia_entry *ifm = sc->media.ifm_cur;
206e948693eSPhilip Paeps 
207e948693eSPhilip Paeps 	if (ifm->ifm_media == (IFM_ETHER | IFM_AUTO))
208b7b0edd1SGeorge V. Neville-Neil 		return (EFX_FCNTL_RESPOND | EFX_FCNTL_GENERATE);
209b7b0edd1SGeorge V. Neville-Neil 	return (((ifm->ifm_media & IFM_ETH_RXPAUSE) ? EFX_FCNTL_RESPOND : 0) |
210b7b0edd1SGeorge V. Neville-Neil 		((ifm->ifm_media & IFM_ETH_TXPAUSE) ? EFX_FCNTL_GENERATE : 0));
211e948693eSPhilip Paeps }
212e948693eSPhilip Paeps 
213e948693eSPhilip Paeps static unsigned int
sfxge_port_link_fc_ifm(struct sfxge_softc * sc)214e948693eSPhilip Paeps sfxge_port_link_fc_ifm(struct sfxge_softc *sc)
215e948693eSPhilip Paeps {
216e948693eSPhilip Paeps 	unsigned int wanted_fc, link_fc;
217e948693eSPhilip Paeps 
218e948693eSPhilip Paeps 	efx_mac_fcntl_get(sc->enp, &wanted_fc, &link_fc);
219e948693eSPhilip Paeps 	return ((link_fc & EFX_FCNTL_RESPOND) ? IFM_ETH_RXPAUSE : 0) |
220e948693eSPhilip Paeps 		((link_fc & EFX_FCNTL_GENERATE) ? IFM_ETH_TXPAUSE : 0);
221e948693eSPhilip Paeps }
222e948693eSPhilip Paeps 
223e948693eSPhilip Paeps #else /* !SFXGE_HAVE_PAUSE_MEDIAOPTS */
224e948693eSPhilip Paeps 
225e948693eSPhilip Paeps static unsigned int
sfxge_port_wanted_fc(struct sfxge_softc * sc)226e948693eSPhilip Paeps sfxge_port_wanted_fc(struct sfxge_softc *sc)
227e948693eSPhilip Paeps {
228b7b0edd1SGeorge V. Neville-Neil 	return (sc->port.wanted_fc);
229e948693eSPhilip Paeps }
230e948693eSPhilip Paeps 
231e948693eSPhilip Paeps static unsigned int
sfxge_port_link_fc_ifm(struct sfxge_softc * sc)232e948693eSPhilip Paeps sfxge_port_link_fc_ifm(struct sfxge_softc *sc)
233e948693eSPhilip Paeps {
234b7b0edd1SGeorge V. Neville-Neil 	return (0);
235e948693eSPhilip Paeps }
236e948693eSPhilip Paeps 
237e948693eSPhilip Paeps static int
sfxge_port_wanted_fc_handler(SYSCTL_HANDLER_ARGS)238e948693eSPhilip Paeps sfxge_port_wanted_fc_handler(SYSCTL_HANDLER_ARGS)
239e948693eSPhilip Paeps {
240e948693eSPhilip Paeps 	struct sfxge_softc *sc;
241e948693eSPhilip Paeps 	struct sfxge_port *port;
242e948693eSPhilip Paeps 	unsigned int fcntl;
243e948693eSPhilip Paeps 	int error;
244e948693eSPhilip Paeps 
245e948693eSPhilip Paeps 	sc = arg1;
246e948693eSPhilip Paeps 	port = &sc->port;
247e948693eSPhilip Paeps 
248b7b0edd1SGeorge V. Neville-Neil 	if (req->newptr != NULL) {
249e948693eSPhilip Paeps 		if ((error = SYSCTL_IN(req, &fcntl, sizeof(fcntl))) != 0)
250d67580d1SAndrew Rybchenko 			return (error);
251e948693eSPhilip Paeps 
252d67580d1SAndrew Rybchenko 		SFXGE_PORT_LOCK(port);
253e948693eSPhilip Paeps 
254d67580d1SAndrew Rybchenko 		if (port->wanted_fc != fcntl) {
255cc5a55a2SAndrew Rybchenko 			if (port->init_state == SFXGE_PORT_STARTED)
256d67580d1SAndrew Rybchenko 				error = efx_mac_fcntl_set(sc->enp,
257d67580d1SAndrew Rybchenko 							  port->wanted_fc,
258d67580d1SAndrew Rybchenko 							  B_TRUE);
259d67580d1SAndrew Rybchenko 			if (error == 0)
260e948693eSPhilip Paeps 				port->wanted_fc = fcntl;
261e948693eSPhilip Paeps 		}
262e948693eSPhilip Paeps 
263763cab71SAndrew Rybchenko 		SFXGE_PORT_UNLOCK(port);
264d67580d1SAndrew Rybchenko 	} else {
265d67580d1SAndrew Rybchenko 		SFXGE_PORT_LOCK(port);
266d67580d1SAndrew Rybchenko 		fcntl = port->wanted_fc;
267d67580d1SAndrew Rybchenko 		SFXGE_PORT_UNLOCK(port);
268d67580d1SAndrew Rybchenko 
269d67580d1SAndrew Rybchenko 		error = SYSCTL_OUT(req, &fcntl, sizeof(fcntl));
270d67580d1SAndrew Rybchenko 	}
271e948693eSPhilip Paeps 
272e948693eSPhilip Paeps 	return (error);
273e948693eSPhilip Paeps }
274e948693eSPhilip Paeps 
275e948693eSPhilip Paeps static int
sfxge_port_link_fc_handler(SYSCTL_HANDLER_ARGS)276e948693eSPhilip Paeps sfxge_port_link_fc_handler(SYSCTL_HANDLER_ARGS)
277e948693eSPhilip Paeps {
278e948693eSPhilip Paeps 	struct sfxge_softc *sc;
279e948693eSPhilip Paeps 	struct sfxge_port *port;
280e948693eSPhilip Paeps 	unsigned int wanted_fc, link_fc;
281e948693eSPhilip Paeps 
282e948693eSPhilip Paeps 	sc = arg1;
283e948693eSPhilip Paeps 	port = &sc->port;
284e948693eSPhilip Paeps 
285763cab71SAndrew Rybchenko 	SFXGE_PORT_LOCK(port);
286851128b8SAndrew Rybchenko 	if (__predict_true(port->init_state == SFXGE_PORT_STARTED) &&
287851128b8SAndrew Rybchenko 	    SFXGE_LINK_UP(sc))
288e948693eSPhilip Paeps 		efx_mac_fcntl_get(sc->enp, &wanted_fc, &link_fc);
289e948693eSPhilip Paeps 	else
290e948693eSPhilip Paeps 		link_fc = 0;
291763cab71SAndrew Rybchenko 	SFXGE_PORT_UNLOCK(port);
292e948693eSPhilip Paeps 
293d67580d1SAndrew Rybchenko 	return (SYSCTL_OUT(req, &link_fc, sizeof(link_fc)));
294e948693eSPhilip Paeps }
295e948693eSPhilip Paeps 
296e948693eSPhilip Paeps #endif /* SFXGE_HAVE_PAUSE_MEDIAOPTS */
297e948693eSPhilip Paeps 
298974577b5SGeorge V. Neville-Neil static const uint64_t sfxge_link_baudrate[EFX_LINK_NMODES] = {
29902602e0eSPhilip Paeps 	[EFX_LINK_10HDX]	= IF_Mbps(10),
30002602e0eSPhilip Paeps 	[EFX_LINK_10FDX]	= IF_Mbps(10),
30102602e0eSPhilip Paeps 	[EFX_LINK_100HDX]	= IF_Mbps(100),
30202602e0eSPhilip Paeps 	[EFX_LINK_100FDX]	= IF_Mbps(100),
30302602e0eSPhilip Paeps 	[EFX_LINK_1000HDX]	= IF_Gbps(1),
30402602e0eSPhilip Paeps 	[EFX_LINK_1000FDX]	= IF_Gbps(1),
305974577b5SGeorge V. Neville-Neil 	[EFX_LINK_10000FDX]	= IF_Gbps(10),
306405f7a36SAndrew Rybchenko 	[EFX_LINK_25000FDX]	= IF_Gbps(25),
3073c838a9fSAndrew Rybchenko 	[EFX_LINK_40000FDX]	= IF_Gbps(40),
308405f7a36SAndrew Rybchenko 	[EFX_LINK_50000FDX]	= IF_Gbps(50),
309405f7a36SAndrew Rybchenko 	[EFX_LINK_100000FDX]	= IF_Gbps(100),
310e948693eSPhilip Paeps };
311e948693eSPhilip Paeps 
312e948693eSPhilip Paeps void
sfxge_mac_link_update(struct sfxge_softc * sc,efx_link_mode_t mode)313e948693eSPhilip Paeps sfxge_mac_link_update(struct sfxge_softc *sc, efx_link_mode_t mode)
314e948693eSPhilip Paeps {
315e948693eSPhilip Paeps 	struct sfxge_port *port;
316e948693eSPhilip Paeps 	int link_state;
317e948693eSPhilip Paeps 
318e948693eSPhilip Paeps 	port = &sc->port;
319e948693eSPhilip Paeps 
320e948693eSPhilip Paeps 	if (port->link_mode == mode)
321e948693eSPhilip Paeps 		return;
322e948693eSPhilip Paeps 
323e948693eSPhilip Paeps 	port->link_mode = mode;
324e948693eSPhilip Paeps 
325e948693eSPhilip Paeps 	/* Push link state update to the OS */
326f788eb2dSAndrew Rybchenko 	link_state = (SFXGE_LINK_UP(sc) ? LINK_STATE_UP : LINK_STATE_DOWN);
32704abf87bSJustin Hibbits 	if_setbaudrate(sc->ifnet, sfxge_link_baudrate[port->link_mode]);
328e948693eSPhilip Paeps 	if_link_state_change(sc->ifnet, link_state);
329e948693eSPhilip Paeps }
330e948693eSPhilip Paeps 
331e948693eSPhilip Paeps static void
sfxge_mac_poll_work(void * arg,int npending)332e948693eSPhilip Paeps sfxge_mac_poll_work(void *arg, int npending)
333e948693eSPhilip Paeps {
334e948693eSPhilip Paeps 	struct sfxge_softc *sc;
335e948693eSPhilip Paeps 	efx_nic_t *enp;
336e948693eSPhilip Paeps 	struct sfxge_port *port;
337e948693eSPhilip Paeps 	efx_link_mode_t mode;
338e948693eSPhilip Paeps 
339e948693eSPhilip Paeps 	sc = (struct sfxge_softc *)arg;
340e948693eSPhilip Paeps 	enp = sc->enp;
341e948693eSPhilip Paeps 	port = &sc->port;
342e948693eSPhilip Paeps 
343763cab71SAndrew Rybchenko 	SFXGE_PORT_LOCK(port);
344e948693eSPhilip Paeps 
345851128b8SAndrew Rybchenko 	if (__predict_false(port->init_state != SFXGE_PORT_STARTED))
346e948693eSPhilip Paeps 		goto done;
347e948693eSPhilip Paeps 
348e948693eSPhilip Paeps 	/* This may sleep waiting for MCDI completion */
349e948693eSPhilip Paeps 	(void)efx_port_poll(enp, &mode);
350e948693eSPhilip Paeps 	sfxge_mac_link_update(sc, mode);
351e948693eSPhilip Paeps 
352e948693eSPhilip Paeps done:
353763cab71SAndrew Rybchenko 	SFXGE_PORT_UNLOCK(port);
354e948693eSPhilip Paeps }
355e948693eSPhilip Paeps 
35696b224edSGleb Smirnoff static u_int
sfxge_copy_maddr(void * arg,struct sockaddr_dl * sdl,u_int cnt)35796b224edSGleb Smirnoff sfxge_copy_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
35896b224edSGleb Smirnoff {
35996b224edSGleb Smirnoff 	uint8_t *mcast_addr = arg;
36096b224edSGleb Smirnoff 
36196b224edSGleb Smirnoff 	if (cnt == EFX_MAC_MULTICAST_LIST_MAX)
36296b224edSGleb Smirnoff 		return (0);
36396b224edSGleb Smirnoff 
36496b224edSGleb Smirnoff 	memcpy(mcast_addr + (cnt * EFX_MAC_ADDR_LEN), LLADDR(sdl),
36596b224edSGleb Smirnoff 	    EFX_MAC_ADDR_LEN);
36696b224edSGleb Smirnoff 
36796b224edSGleb Smirnoff 	return (1);
36896b224edSGleb Smirnoff }
36996b224edSGleb Smirnoff 
370e948693eSPhilip Paeps static int
sfxge_mac_multicast_list_set(struct sfxge_softc * sc)3713c838a9fSAndrew Rybchenko sfxge_mac_multicast_list_set(struct sfxge_softc *sc)
372e948693eSPhilip Paeps {
37304abf87bSJustin Hibbits 	if_t ifp = sc->ifnet;
3743c838a9fSAndrew Rybchenko 	struct sfxge_port *port = &sc->port;
3753c838a9fSAndrew Rybchenko 	int rc = 0;
376e948693eSPhilip Paeps 
3773c838a9fSAndrew Rybchenko 	mtx_assert(&port->lock, MA_OWNED);
378e948693eSPhilip Paeps 
37996b224edSGleb Smirnoff 	port->mcast_count = if_foreach_llmaddr(ifp, sfxge_copy_maddr,
38096b224edSGleb Smirnoff 	    port->mcast_addrs);
3813c838a9fSAndrew Rybchenko 	if (port->mcast_count == EFX_MAC_MULTICAST_LIST_MAX) {
38296b224edSGleb Smirnoff 		device_printf(sc->dev, "Too many multicast addresses\n");
3833c838a9fSAndrew Rybchenko 		rc = EINVAL;
3843c838a9fSAndrew Rybchenko 	}
3853c838a9fSAndrew Rybchenko 
3863c838a9fSAndrew Rybchenko 	if (rc == 0) {
3873c838a9fSAndrew Rybchenko 		rc = efx_mac_multicast_list_set(sc->enp, port->mcast_addrs,
3883c838a9fSAndrew Rybchenko 						port->mcast_count);
3893c838a9fSAndrew Rybchenko 		if (rc != 0)
3903c838a9fSAndrew Rybchenko 			device_printf(sc->dev,
3913c838a9fSAndrew Rybchenko 			    "Cannot set multicast address list\n");
392e948693eSPhilip Paeps 	}
3933c838a9fSAndrew Rybchenko 
3943c838a9fSAndrew Rybchenko 	return (rc);
3953c838a9fSAndrew Rybchenko }
3963c838a9fSAndrew Rybchenko 
3973c838a9fSAndrew Rybchenko static int
sfxge_mac_filter_set_locked(struct sfxge_softc * sc)3983c838a9fSAndrew Rybchenko sfxge_mac_filter_set_locked(struct sfxge_softc *sc)
3993c838a9fSAndrew Rybchenko {
40004abf87bSJustin Hibbits 	if_t ifp = sc->ifnet;
4013c838a9fSAndrew Rybchenko 	struct sfxge_port *port = &sc->port;
4023c838a9fSAndrew Rybchenko 	boolean_t all_mulcst;
4033c838a9fSAndrew Rybchenko 	int rc;
4043c838a9fSAndrew Rybchenko 
4053c838a9fSAndrew Rybchenko 	mtx_assert(&port->lock, MA_OWNED);
4063c838a9fSAndrew Rybchenko 
40704abf87bSJustin Hibbits 	all_mulcst = !!(if_getflags(ifp) & (IFF_PROMISC | IFF_ALLMULTI));
4083c838a9fSAndrew Rybchenko 
4093c838a9fSAndrew Rybchenko 	rc = sfxge_mac_multicast_list_set(sc);
4103c838a9fSAndrew Rybchenko 	/* Fallback to all multicast if cannot set multicast list */
4113c838a9fSAndrew Rybchenko 	if (rc != 0)
4123c838a9fSAndrew Rybchenko 		all_mulcst = B_TRUE;
4133c838a9fSAndrew Rybchenko 
41404abf87bSJustin Hibbits 	rc = efx_mac_filter_set(sc->enp, !!(if_getflags(ifp) & IFF_PROMISC),
4153c838a9fSAndrew Rybchenko 				(port->mcast_count > 0), all_mulcst, B_TRUE);
4163c838a9fSAndrew Rybchenko 
4173c838a9fSAndrew Rybchenko 	return (rc);
418e948693eSPhilip Paeps }
419e948693eSPhilip Paeps 
420e948693eSPhilip Paeps int
sfxge_mac_filter_set(struct sfxge_softc * sc)421e948693eSPhilip Paeps sfxge_mac_filter_set(struct sfxge_softc *sc)
422e948693eSPhilip Paeps {
423e948693eSPhilip Paeps 	struct sfxge_port *port = &sc->port;
424e948693eSPhilip Paeps 	int rc;
425e948693eSPhilip Paeps 
426763cab71SAndrew Rybchenko 	SFXGE_PORT_LOCK(port);
427092a1783SGeorge V. Neville-Neil 	/*
428092a1783SGeorge V. Neville-Neil 	 * The function may be called without softc_lock held in the
429092a1783SGeorge V. Neville-Neil 	 * case of SIOCADDMULTI and SIOCDELMULTI ioctls. ioctl handler
430092a1783SGeorge V. Neville-Neil 	 * checks IFF_DRV_RUNNING flag which implies port started, but
431092a1783SGeorge V. Neville-Neil 	 * it is not guaranteed to remain. softc_lock shared lock can't
432092a1783SGeorge V. Neville-Neil 	 * be held in the case of these ioctls processing, since it
433092a1783SGeorge V. Neville-Neil 	 * results in failure where kernel complains that non-sleepable
434092a1783SGeorge V. Neville-Neil 	 * lock is held in sleeping thread. Both problems are repeatable
435092a1783SGeorge V. Neville-Neil 	 * on LAG with LACP proto bring up.
436092a1783SGeorge V. Neville-Neil 	 */
437851128b8SAndrew Rybchenko 	if (__predict_true(port->init_state == SFXGE_PORT_STARTED))
438e948693eSPhilip Paeps 		rc = sfxge_mac_filter_set_locked(sc);
439092a1783SGeorge V. Neville-Neil 	else
440092a1783SGeorge V. Neville-Neil 		rc = 0;
441763cab71SAndrew Rybchenko 	SFXGE_PORT_UNLOCK(port);
442b7b0edd1SGeorge V. Neville-Neil 	return (rc);
443e948693eSPhilip Paeps }
444e948693eSPhilip Paeps 
445e948693eSPhilip Paeps void
sfxge_port_stop(struct sfxge_softc * sc)446e948693eSPhilip Paeps sfxge_port_stop(struct sfxge_softc *sc)
447e948693eSPhilip Paeps {
448e948693eSPhilip Paeps 	struct sfxge_port *port;
449e948693eSPhilip Paeps 	efx_nic_t *enp;
450e948693eSPhilip Paeps 
451e948693eSPhilip Paeps 	port = &sc->port;
452e948693eSPhilip Paeps 	enp = sc->enp;
453e948693eSPhilip Paeps 
454763cab71SAndrew Rybchenko 	SFXGE_PORT_LOCK(port);
455e948693eSPhilip Paeps 
456e948693eSPhilip Paeps 	KASSERT(port->init_state == SFXGE_PORT_STARTED,
457e948693eSPhilip Paeps 	    ("port not started"));
458e948693eSPhilip Paeps 
459e948693eSPhilip Paeps 	port->init_state = SFXGE_PORT_INITIALIZED;
460e948693eSPhilip Paeps 
461e948693eSPhilip Paeps 	port->mac_stats.update_time = 0;
462e948693eSPhilip Paeps 
463e948693eSPhilip Paeps 	/* This may call MCDI */
464e948693eSPhilip Paeps 	(void)efx_mac_drain(enp, B_TRUE);
465e948693eSPhilip Paeps 
466e948693eSPhilip Paeps 	(void)efx_mac_stats_periodic(enp, &port->mac_stats.dma_buf, 0, B_FALSE);
467e948693eSPhilip Paeps 
468e948693eSPhilip Paeps 	port->link_mode = EFX_LINK_UNKNOWN;
469e948693eSPhilip Paeps 
470e948693eSPhilip Paeps 	/* Destroy the common code port object. */
4713c838a9fSAndrew Rybchenko 	efx_port_fini(enp);
4723c838a9fSAndrew Rybchenko 
4733c838a9fSAndrew Rybchenko 	efx_filter_fini(enp);
474e948693eSPhilip Paeps 
475763cab71SAndrew Rybchenko 	SFXGE_PORT_UNLOCK(port);
476e948693eSPhilip Paeps }
477e948693eSPhilip Paeps 
478e948693eSPhilip Paeps int
sfxge_port_start(struct sfxge_softc * sc)479e948693eSPhilip Paeps sfxge_port_start(struct sfxge_softc *sc)
480e948693eSPhilip Paeps {
481e948693eSPhilip Paeps 	uint8_t mac_addr[ETHER_ADDR_LEN];
48296b224edSGleb Smirnoff 	struct epoch_tracker et;
48304abf87bSJustin Hibbits 	if_t ifp = sc->ifnet;
484e948693eSPhilip Paeps 	struct sfxge_port *port;
485e948693eSPhilip Paeps 	efx_nic_t *enp;
486e948693eSPhilip Paeps 	size_t pdu;
487e948693eSPhilip Paeps 	int rc;
488c379e930SAndrew Rybchenko 	uint32_t phy_cap_mask;
489e948693eSPhilip Paeps 
490e948693eSPhilip Paeps 	port = &sc->port;
491e948693eSPhilip Paeps 	enp = sc->enp;
492e948693eSPhilip Paeps 
493763cab71SAndrew Rybchenko 	SFXGE_PORT_LOCK(port);
494e948693eSPhilip Paeps 
495e948693eSPhilip Paeps 	KASSERT(port->init_state == SFXGE_PORT_INITIALIZED,
496e948693eSPhilip Paeps 	    ("port not initialized"));
497e948693eSPhilip Paeps 
4983c838a9fSAndrew Rybchenko 	/* Initialise the required filtering */
4993c838a9fSAndrew Rybchenko 	if ((rc = efx_filter_init(enp)) != 0)
5003c838a9fSAndrew Rybchenko 		goto fail_filter_init;
5013c838a9fSAndrew Rybchenko 
502e948693eSPhilip Paeps 	/* Initialize the port object in the common code. */
503e948693eSPhilip Paeps 	if ((rc = efx_port_init(sc->enp)) != 0)
504e948693eSPhilip Paeps 		goto fail;
505e948693eSPhilip Paeps 
506e948693eSPhilip Paeps 	/* Set the SDU */
50704abf87bSJustin Hibbits 	pdu = EFX_MAC_PDU(if_getmtu(ifp));
508e948693eSPhilip Paeps 	if ((rc = efx_mac_pdu_set(enp, pdu)) != 0)
509e948693eSPhilip Paeps 		goto fail2;
510e948693eSPhilip Paeps 
511e948693eSPhilip Paeps 	if ((rc = efx_mac_fcntl_set(enp, sfxge_port_wanted_fc(sc), B_TRUE))
512e948693eSPhilip Paeps 	    != 0)
5133c838a9fSAndrew Rybchenko 		goto fail3;
514e948693eSPhilip Paeps 
515e948693eSPhilip Paeps 	/* Set the unicast address */
51696b224edSGleb Smirnoff 	NET_EPOCH_ENTER(et);
51704abf87bSJustin Hibbits 	bcopy(if_getlladdr(ifp), mac_addr, sizeof(mac_addr));
51896b224edSGleb Smirnoff 	NET_EPOCH_EXIT(et);
519e948693eSPhilip Paeps 	if ((rc = efx_mac_addr_set(enp, mac_addr)) != 0)
5203c838a9fSAndrew Rybchenko 		goto fail4;
521e948693eSPhilip Paeps 
522e948693eSPhilip Paeps 	sfxge_mac_filter_set_locked(sc);
523e948693eSPhilip Paeps 
52462f5c496SAndrew Rybchenko 	/* Update MAC stats by DMA every period */
525e948693eSPhilip Paeps 	if ((rc = efx_mac_stats_periodic(enp, &port->mac_stats.dma_buf,
52658223d5bSAndrew Rybchenko 					 port->stats_update_period_ms,
52762f5c496SAndrew Rybchenko 					 B_FALSE)) != 0)
5283c838a9fSAndrew Rybchenko 		goto fail6;
529e948693eSPhilip Paeps 
530e948693eSPhilip Paeps 	if ((rc = efx_mac_drain(enp, B_FALSE)) != 0)
5313c838a9fSAndrew Rybchenko 		goto fail8;
532e948693eSPhilip Paeps 
533c379e930SAndrew Rybchenko 	if ((rc = sfxge_phy_cap_mask(sc, sc->media.ifm_cur->ifm_media,
534c379e930SAndrew Rybchenko 				     &phy_cap_mask)) != 0)
5353c838a9fSAndrew Rybchenko 		goto fail9;
536e948693eSPhilip Paeps 
537c379e930SAndrew Rybchenko 	if ((rc = efx_phy_adv_cap_set(sc->enp, phy_cap_mask)) != 0)
5383c838a9fSAndrew Rybchenko 		goto fail10;
539c379e930SAndrew Rybchenko 
540e948693eSPhilip Paeps 	port->init_state = SFXGE_PORT_STARTED;
541e948693eSPhilip Paeps 
542e948693eSPhilip Paeps 	/* Single poll in case there were missing initial events */
543763cab71SAndrew Rybchenko 	SFXGE_PORT_UNLOCK(port);
544e948693eSPhilip Paeps 	sfxge_mac_poll_work(sc, 0);
545e948693eSPhilip Paeps 
546e948693eSPhilip Paeps 	return (0);
547e948693eSPhilip Paeps 
5483c838a9fSAndrew Rybchenko fail10:
5493c838a9fSAndrew Rybchenko fail9:
550e948693eSPhilip Paeps 	(void)efx_mac_drain(enp, B_TRUE);
5513c838a9fSAndrew Rybchenko fail8:
5523c838a9fSAndrew Rybchenko 	(void)efx_mac_stats_periodic(enp, &port->mac_stats.dma_buf, 0, B_FALSE);
5533c838a9fSAndrew Rybchenko fail6:
5543c838a9fSAndrew Rybchenko fail4:
555e948693eSPhilip Paeps fail3:
5563c838a9fSAndrew Rybchenko 
557e948693eSPhilip Paeps fail2:
5583c838a9fSAndrew Rybchenko 	efx_port_fini(enp);
559e948693eSPhilip Paeps fail:
5603c838a9fSAndrew Rybchenko 	efx_filter_fini(enp);
5613c838a9fSAndrew Rybchenko fail_filter_init:
562763cab71SAndrew Rybchenko 	SFXGE_PORT_UNLOCK(port);
563e948693eSPhilip Paeps 
564e948693eSPhilip Paeps 	return (rc);
565e948693eSPhilip Paeps }
566e948693eSPhilip Paeps 
567e948693eSPhilip Paeps static int
sfxge_phy_stat_update(struct sfxge_softc * sc)568e948693eSPhilip Paeps sfxge_phy_stat_update(struct sfxge_softc *sc)
569e948693eSPhilip Paeps {
570e948693eSPhilip Paeps 	struct sfxge_port *port = &sc->port;
571e948693eSPhilip Paeps 	efsys_mem_t *esmp = &port->phy_stats.dma_buf;
572e948693eSPhilip Paeps 	clock_t now;
573e948693eSPhilip Paeps 	unsigned int count;
574e948693eSPhilip Paeps 	int rc;
575e948693eSPhilip Paeps 
576d8e7a280SAndrew Rybchenko 	SFXGE_PORT_LOCK_ASSERT_OWNED(port);
577e948693eSPhilip Paeps 
578851128b8SAndrew Rybchenko 	if (__predict_false(port->init_state != SFXGE_PORT_STARTED)) {
579e948693eSPhilip Paeps 		rc = 0;
580e948693eSPhilip Paeps 		goto out;
581e948693eSPhilip Paeps 	}
582e948693eSPhilip Paeps 
583e948693eSPhilip Paeps 	now = ticks;
584af58aa46SAndrew Rybchenko 	if ((unsigned int)(now - port->phy_stats.update_time) < (unsigned int)hz) {
585e948693eSPhilip Paeps 		rc = 0;
586e948693eSPhilip Paeps 		goto out;
587e948693eSPhilip Paeps 	}
588e948693eSPhilip Paeps 
589e948693eSPhilip Paeps 	port->phy_stats.update_time = now;
590e948693eSPhilip Paeps 
591e948693eSPhilip Paeps 	/* If we're unlucky enough to read statistics wduring the DMA, wait
592e948693eSPhilip Paeps 	 * up to 10ms for it to finish (typically takes <500us) */
593e948693eSPhilip Paeps 	for (count = 0; count < 100; ++count) {
594e948693eSPhilip Paeps 		EFSYS_PROBE1(wait, unsigned int, count);
595e948693eSPhilip Paeps 
596e948693eSPhilip Paeps 		/* Synchronize the DMA memory for reading */
597e948693eSPhilip Paeps 		bus_dmamap_sync(esmp->esm_tag, esmp->esm_map,
598e948693eSPhilip Paeps 		    BUS_DMASYNC_POSTREAD);
599e948693eSPhilip Paeps 
600e948693eSPhilip Paeps 		/* Try to update the cached counters */
601e948693eSPhilip Paeps 		if ((rc = efx_phy_stats_update(sc->enp, esmp,
602e948693eSPhilip Paeps 		    port->phy_stats.decode_buf)) != EAGAIN)
603e948693eSPhilip Paeps 			goto out;
604e948693eSPhilip Paeps 
605e948693eSPhilip Paeps 		DELAY(100);
606e948693eSPhilip Paeps 	}
607e948693eSPhilip Paeps 
608e948693eSPhilip Paeps 	rc = ETIMEDOUT;
609e948693eSPhilip Paeps out:
610b7b0edd1SGeorge V. Neville-Neil 	return (rc);
611e948693eSPhilip Paeps }
612e948693eSPhilip Paeps 
613e948693eSPhilip Paeps static int
sfxge_phy_stat_handler(SYSCTL_HANDLER_ARGS)614e948693eSPhilip Paeps sfxge_phy_stat_handler(SYSCTL_HANDLER_ARGS)
615e948693eSPhilip Paeps {
616e948693eSPhilip Paeps 	struct sfxge_softc *sc = arg1;
617e948693eSPhilip Paeps 	unsigned int id = arg2;
618e948693eSPhilip Paeps 	int rc;
619d67580d1SAndrew Rybchenko 	uint32_t val;
620e948693eSPhilip Paeps 
621d8e7a280SAndrew Rybchenko 	SFXGE_PORT_LOCK(&sc->port);
622d67580d1SAndrew Rybchenko 	if ((rc = sfxge_phy_stat_update(sc)) == 0)
623d67580d1SAndrew Rybchenko 		val = ((uint32_t *)sc->port.phy_stats.decode_buf)[id];
624d8e7a280SAndrew Rybchenko 	SFXGE_PORT_UNLOCK(&sc->port);
625d67580d1SAndrew Rybchenko 
626d67580d1SAndrew Rybchenko 	if (rc == 0)
627d67580d1SAndrew Rybchenko 		rc = SYSCTL_OUT(req, &val, sizeof(val));
628d8e7a280SAndrew Rybchenko 	return (rc);
629e948693eSPhilip Paeps }
630e948693eSPhilip Paeps 
631e948693eSPhilip Paeps static void
sfxge_phy_stat_init(struct sfxge_softc * sc)632e948693eSPhilip Paeps sfxge_phy_stat_init(struct sfxge_softc *sc)
633e948693eSPhilip Paeps {
634e948693eSPhilip Paeps 	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev);
635e948693eSPhilip Paeps 	struct sysctl_oid_list *stat_list;
636e948693eSPhilip Paeps 	unsigned int id;
637e948693eSPhilip Paeps 	const char *name;
638e948693eSPhilip Paeps 	uint64_t stat_mask = efx_nic_cfg_get(sc->enp)->enc_phy_stat_mask;
639e948693eSPhilip Paeps 
640e948693eSPhilip Paeps 	stat_list = SYSCTL_CHILDREN(sc->stats_node);
641e948693eSPhilip Paeps 
642e948693eSPhilip Paeps 	/* Initialise the named stats */
643e948693eSPhilip Paeps 	for (id = 0; id < EFX_PHY_NSTATS; id++) {
644e948693eSPhilip Paeps 		if (!(stat_mask & ((uint64_t)1 << id)))
645e948693eSPhilip Paeps 			continue;
646e948693eSPhilip Paeps 		name = efx_phy_stat_name(sc->enp, id);
6477029da5cSPawel Biernacki 		SYSCTL_ADD_PROC(ctx, stat_list, OID_AUTO, name,
6487029da5cSPawel Biernacki 		    CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_MPSAFE,
649e948693eSPhilip Paeps 		    sc, id, sfxge_phy_stat_handler,
6507029da5cSPawel Biernacki 		    id == EFX_PHY_STAT_OUI ? "IX" : "IU", "");
651e948693eSPhilip Paeps 	}
652e948693eSPhilip Paeps }
653e948693eSPhilip Paeps 
654e948693eSPhilip Paeps void
sfxge_port_fini(struct sfxge_softc * sc)655e948693eSPhilip Paeps sfxge_port_fini(struct sfxge_softc *sc)
656e948693eSPhilip Paeps {
657e948693eSPhilip Paeps 	struct sfxge_port *port;
658e948693eSPhilip Paeps 	efsys_mem_t *esmp;
659e948693eSPhilip Paeps 
660e948693eSPhilip Paeps 	port = &sc->port;
661e948693eSPhilip Paeps 	esmp = &port->mac_stats.dma_buf;
662e948693eSPhilip Paeps 
663e948693eSPhilip Paeps 	KASSERT(port->init_state == SFXGE_PORT_INITIALIZED,
664e948693eSPhilip Paeps 	    ("Port not initialized"));
665e948693eSPhilip Paeps 
666e948693eSPhilip Paeps 	port->init_state = SFXGE_PORT_UNINITIALIZED;
667e948693eSPhilip Paeps 
668e948693eSPhilip Paeps 	port->link_mode = EFX_LINK_UNKNOWN;
669e948693eSPhilip Paeps 
670e948693eSPhilip Paeps 	/* Finish with PHY DMA memory */
671e948693eSPhilip Paeps 	sfxge_dma_free(&port->phy_stats.dma_buf);
672e948693eSPhilip Paeps 	free(port->phy_stats.decode_buf, M_SFXGE);
673e948693eSPhilip Paeps 
674e948693eSPhilip Paeps 	sfxge_dma_free(esmp);
675e948693eSPhilip Paeps 	free(port->mac_stats.decode_buf, M_SFXGE);
676e948693eSPhilip Paeps 
677763cab71SAndrew Rybchenko 	SFXGE_PORT_LOCK_DESTROY(port);
678e948693eSPhilip Paeps 
679e948693eSPhilip Paeps 	port->sc = NULL;
680e948693eSPhilip Paeps }
681e948693eSPhilip Paeps 
68258223d5bSAndrew Rybchenko static uint16_t
sfxge_port_stats_update_period_ms(struct sfxge_softc * sc)68358223d5bSAndrew Rybchenko sfxge_port_stats_update_period_ms(struct sfxge_softc *sc)
68458223d5bSAndrew Rybchenko {
68558223d5bSAndrew Rybchenko 	int period_ms = sfxge_stats_update_period_ms;
68658223d5bSAndrew Rybchenko 
68758223d5bSAndrew Rybchenko 	if (period_ms < 0) {
68858223d5bSAndrew Rybchenko 		device_printf(sc->dev,
68958223d5bSAndrew Rybchenko 			"treat negative stats update period %d as 0 (disable)\n",
69058223d5bSAndrew Rybchenko 			 period_ms);
69158223d5bSAndrew Rybchenko 		period_ms = 0;
69258223d5bSAndrew Rybchenko 	} else if (period_ms > UINT16_MAX) {
69358223d5bSAndrew Rybchenko 		device_printf(sc->dev,
69458223d5bSAndrew Rybchenko 			"treat too big stats update period %d as %u\n",
69558223d5bSAndrew Rybchenko 			period_ms, UINT16_MAX);
69658223d5bSAndrew Rybchenko 		period_ms = UINT16_MAX;
69758223d5bSAndrew Rybchenko 	}
69858223d5bSAndrew Rybchenko 
69958223d5bSAndrew Rybchenko 	return period_ms;
70058223d5bSAndrew Rybchenko }
70158223d5bSAndrew Rybchenko 
702a0e88689SAndrew Rybchenko static int
sfxge_port_stats_update_period_ms_handler(SYSCTL_HANDLER_ARGS)703a0e88689SAndrew Rybchenko sfxge_port_stats_update_period_ms_handler(SYSCTL_HANDLER_ARGS)
704a0e88689SAndrew Rybchenko {
705a0e88689SAndrew Rybchenko 	struct sfxge_softc *sc;
706a0e88689SAndrew Rybchenko 	struct sfxge_port *port;
707a0e88689SAndrew Rybchenko 	unsigned int period_ms;
708a0e88689SAndrew Rybchenko 	int error;
709a0e88689SAndrew Rybchenko 
710a0e88689SAndrew Rybchenko 	sc = arg1;
711a0e88689SAndrew Rybchenko 	port = &sc->port;
712a0e88689SAndrew Rybchenko 
713a0e88689SAndrew Rybchenko 	if (req->newptr != NULL) {
714a0e88689SAndrew Rybchenko 		error = SYSCTL_IN(req, &period_ms, sizeof(period_ms));
715a0e88689SAndrew Rybchenko 		if (error != 0)
716a0e88689SAndrew Rybchenko 			return (error);
717a0e88689SAndrew Rybchenko 
718a0e88689SAndrew Rybchenko 		if (period_ms > UINT16_MAX)
719a0e88689SAndrew Rybchenko 			return (EINVAL);
720a0e88689SAndrew Rybchenko 
721a0e88689SAndrew Rybchenko 		SFXGE_PORT_LOCK(port);
722a0e88689SAndrew Rybchenko 
723a0e88689SAndrew Rybchenko 		if (port->stats_update_period_ms != period_ms) {
724a0e88689SAndrew Rybchenko 			if (port->init_state == SFXGE_PORT_STARTED)
725a0e88689SAndrew Rybchenko 				error = efx_mac_stats_periodic(sc->enp,
726a0e88689SAndrew Rybchenko 						&port->mac_stats.dma_buf,
727a0e88689SAndrew Rybchenko 						period_ms, B_FALSE);
728a0e88689SAndrew Rybchenko 			if (error == 0)
729a0e88689SAndrew Rybchenko 				port->stats_update_period_ms = period_ms;
730a0e88689SAndrew Rybchenko 		}
731a0e88689SAndrew Rybchenko 
732a0e88689SAndrew Rybchenko 		SFXGE_PORT_UNLOCK(port);
733a0e88689SAndrew Rybchenko 	} else {
734a0e88689SAndrew Rybchenko 		SFXGE_PORT_LOCK(port);
735a0e88689SAndrew Rybchenko 		period_ms = port->stats_update_period_ms;
736a0e88689SAndrew Rybchenko 		SFXGE_PORT_UNLOCK(port);
737a0e88689SAndrew Rybchenko 
738a0e88689SAndrew Rybchenko 		error = SYSCTL_OUT(req, &period_ms, sizeof(period_ms));
739a0e88689SAndrew Rybchenko 	}
740a0e88689SAndrew Rybchenko 
741a0e88689SAndrew Rybchenko 	return (error);
742a0e88689SAndrew Rybchenko }
743a0e88689SAndrew Rybchenko 
744e948693eSPhilip Paeps int
sfxge_port_init(struct sfxge_softc * sc)745e948693eSPhilip Paeps sfxge_port_init(struct sfxge_softc *sc)
746e948693eSPhilip Paeps {
747e948693eSPhilip Paeps 	struct sfxge_port *port;
748e948693eSPhilip Paeps 	struct sysctl_ctx_list *sysctl_ctx;
749e948693eSPhilip Paeps 	struct sysctl_oid *sysctl_tree;
750e948693eSPhilip Paeps 	efsys_mem_t *mac_stats_buf, *phy_stats_buf;
7519361c4adSAndrew Rybchenko 	uint32_t mac_nstats;
7529361c4adSAndrew Rybchenko 	size_t mac_stats_size;
753e948693eSPhilip Paeps 	int rc;
754e948693eSPhilip Paeps 
755e948693eSPhilip Paeps 	port = &sc->port;
756e948693eSPhilip Paeps 	mac_stats_buf = &port->mac_stats.dma_buf;
757e948693eSPhilip Paeps 	phy_stats_buf = &port->phy_stats.dma_buf;
758e948693eSPhilip Paeps 
759e948693eSPhilip Paeps 	KASSERT(port->init_state == SFXGE_PORT_UNINITIALIZED,
760e948693eSPhilip Paeps 	    ("Port already initialized"));
761e948693eSPhilip Paeps 
762e948693eSPhilip Paeps 	port->sc = sc;
763e948693eSPhilip Paeps 
76433d45dc5SAndrew Rybchenko 	SFXGE_PORT_LOCK_INIT(port, device_get_nameunit(sc->dev));
765e948693eSPhilip Paeps 
7663c838a9fSAndrew Rybchenko 	DBGPRINT(sc->dev, "alloc PHY stats");
767e948693eSPhilip Paeps 	port->phy_stats.decode_buf = malloc(EFX_PHY_NSTATS * sizeof(uint32_t),
768e948693eSPhilip Paeps 					    M_SFXGE, M_WAITOK | M_ZERO);
769e948693eSPhilip Paeps 	if ((rc = sfxge_dma_alloc(sc, EFX_PHY_STATS_SIZE, phy_stats_buf)) != 0)
770e948693eSPhilip Paeps 		goto fail;
771e948693eSPhilip Paeps 	sfxge_phy_stat_init(sc);
772e948693eSPhilip Paeps 
7733c838a9fSAndrew Rybchenko 	DBGPRINT(sc->dev, "init sysctl");
774e948693eSPhilip Paeps 	sysctl_ctx = device_get_sysctl_ctx(sc->dev);
775e948693eSPhilip Paeps 	sysctl_tree = device_get_sysctl_tree(sc->dev);
776e948693eSPhilip Paeps 
777e948693eSPhilip Paeps #ifndef SFXGE_HAVE_PAUSE_MEDIAOPTS
778e948693eSPhilip Paeps 	/* If flow control cannot be configured or reported through
779e948693eSPhilip Paeps 	 * ifmedia, provide sysctls for it. */
780e948693eSPhilip Paeps 	port->wanted_fc = EFX_FCNTL_RESPOND | EFX_FCNTL_GENERATE;
781e948693eSPhilip Paeps 	SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
7827029da5cSPawel Biernacki 	    "wanted_fc", CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
783e948693eSPhilip Paeps 	    sfxge_port_wanted_fc_handler, "IU", "wanted flow control mode");
784e948693eSPhilip Paeps 	SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
7857029da5cSPawel Biernacki 	    "link_fc", CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
786e948693eSPhilip Paeps 	    sfxge_port_link_fc_handler, "IU", "link flow control mode");
787e948693eSPhilip Paeps #endif
788e948693eSPhilip Paeps 
7893c838a9fSAndrew Rybchenko 	DBGPRINT(sc->dev, "alloc MAC stats");
790e948693eSPhilip Paeps 	port->mac_stats.decode_buf = malloc(EFX_MAC_NSTATS * sizeof(uint64_t),
791e948693eSPhilip Paeps 					    M_SFXGE, M_WAITOK | M_ZERO);
7929361c4adSAndrew Rybchenko 	mac_nstats = efx_nic_cfg_get(sc->enp)->enc_mac_stats_nstats;
793ec30f0beSAndrew Rybchenko 	mac_stats_size = EFX_P2ROUNDUP(size_t, mac_nstats * sizeof(uint64_t),
794ec30f0beSAndrew Rybchenko 				       EFX_BUF_SIZE);
7959361c4adSAndrew Rybchenko 	if ((rc = sfxge_dma_alloc(sc, mac_stats_size, mac_stats_buf)) != 0)
796e948693eSPhilip Paeps 		goto fail2;
79758223d5bSAndrew Rybchenko 	port->stats_update_period_ms = sfxge_port_stats_update_period_ms(sc);
798e948693eSPhilip Paeps 	sfxge_mac_stat_init(sc);
799e948693eSPhilip Paeps 
800a0e88689SAndrew Rybchenko 	SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
8017029da5cSPawel Biernacki 	    "stats_update_period_ms",
8027029da5cSPawel Biernacki 	    CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0,
803a0e88689SAndrew Rybchenko 	    sfxge_port_stats_update_period_ms_handler, "IU",
804a0e88689SAndrew Rybchenko 	    "interface statistics refresh period");
805a0e88689SAndrew Rybchenko 
806e948693eSPhilip Paeps 	port->init_state = SFXGE_PORT_INITIALIZED;
807e948693eSPhilip Paeps 
8083c838a9fSAndrew Rybchenko 	DBGPRINT(sc->dev, "success");
809e948693eSPhilip Paeps 	return (0);
810e948693eSPhilip Paeps 
811e948693eSPhilip Paeps fail2:
812e948693eSPhilip Paeps 	free(port->mac_stats.decode_buf, M_SFXGE);
813e948693eSPhilip Paeps 	sfxge_dma_free(phy_stats_buf);
814e948693eSPhilip Paeps fail:
815e948693eSPhilip Paeps 	free(port->phy_stats.decode_buf, M_SFXGE);
816763cab71SAndrew Rybchenko 	SFXGE_PORT_LOCK_DESTROY(port);
817e948693eSPhilip Paeps 	port->sc = NULL;
8183c838a9fSAndrew Rybchenko 	DBGPRINT(sc->dev, "failed %d", rc);
819b7b0edd1SGeorge V. Neville-Neil 	return (rc);
820e948693eSPhilip Paeps }
821e948693eSPhilip Paeps 
822ab2310e8SAndrew Rybchenko static const int sfxge_link_mode[EFX_PHY_MEDIA_NTYPES][EFX_LINK_NMODES] = {
823e948693eSPhilip Paeps 	[EFX_PHY_MEDIA_CX4] = {
824e948693eSPhilip Paeps 		[EFX_LINK_10000FDX]	= IFM_ETHER | IFM_FDX | IFM_10G_CX4,
825e948693eSPhilip Paeps 	},
826e948693eSPhilip Paeps 	[EFX_PHY_MEDIA_KX4] = {
827e948693eSPhilip Paeps 		[EFX_LINK_10000FDX]	= IFM_ETHER | IFM_FDX | IFM_10G_KX4,
828e948693eSPhilip Paeps 	},
829e948693eSPhilip Paeps 	[EFX_PHY_MEDIA_XFP] = {
830e948693eSPhilip Paeps 		/* Don't know the module type, but assume SR for now. */
831e948693eSPhilip Paeps 		[EFX_LINK_10000FDX]	= IFM_ETHER | IFM_FDX | IFM_10G_SR,
832e948693eSPhilip Paeps 	},
8333c838a9fSAndrew Rybchenko 	[EFX_PHY_MEDIA_QSFP_PLUS] = {
8343c838a9fSAndrew Rybchenko 		/* Don't know the module type, but assume SR for now. */
8353c838a9fSAndrew Rybchenko 		[EFX_LINK_10000FDX]	= IFM_ETHER | IFM_FDX | IFM_10G_SR,
836405f7a36SAndrew Rybchenko 		[EFX_LINK_25000FDX]	= IFM_ETHER | IFM_FDX | IFM_25G_SR,
8373c838a9fSAndrew Rybchenko 		[EFX_LINK_40000FDX]	= IFM_ETHER | IFM_FDX | IFM_40G_CR4,
838405f7a36SAndrew Rybchenko 		[EFX_LINK_50000FDX]	= IFM_ETHER | IFM_FDX | IFM_50G_SR,
839405f7a36SAndrew Rybchenko 		[EFX_LINK_100000FDX]	= IFM_ETHER | IFM_FDX | IFM_100G_SR2,
8403c838a9fSAndrew Rybchenko 	},
841e948693eSPhilip Paeps 	[EFX_PHY_MEDIA_SFP_PLUS] = {
842e948693eSPhilip Paeps 		/* Don't know the module type, but assume SX/SR for now. */
843e948693eSPhilip Paeps 		[EFX_LINK_1000FDX]	= IFM_ETHER | IFM_FDX | IFM_1000_SX,
844e948693eSPhilip Paeps 		[EFX_LINK_10000FDX]	= IFM_ETHER | IFM_FDX | IFM_10G_SR,
845405f7a36SAndrew Rybchenko 		[EFX_LINK_25000FDX]	= IFM_ETHER | IFM_FDX | IFM_25G_SR,
846e948693eSPhilip Paeps 	},
847e948693eSPhilip Paeps 	[EFX_PHY_MEDIA_BASE_T] = {
848e948693eSPhilip Paeps 		[EFX_LINK_10HDX]	= IFM_ETHER | IFM_HDX | IFM_10_T,
849e948693eSPhilip Paeps 		[EFX_LINK_10FDX]	= IFM_ETHER | IFM_FDX | IFM_10_T,
850e948693eSPhilip Paeps 		[EFX_LINK_100HDX]	= IFM_ETHER | IFM_HDX | IFM_100_TX,
851e948693eSPhilip Paeps 		[EFX_LINK_100FDX]	= IFM_ETHER | IFM_FDX | IFM_100_TX,
852e948693eSPhilip Paeps 		[EFX_LINK_1000HDX]	= IFM_ETHER | IFM_HDX | IFM_1000_T,
853e948693eSPhilip Paeps 		[EFX_LINK_1000FDX]	= IFM_ETHER | IFM_FDX | IFM_1000_T,
854e948693eSPhilip Paeps 		[EFX_LINK_10000FDX]	= IFM_ETHER | IFM_FDX | IFM_10G_T,
855e948693eSPhilip Paeps 	},
856e948693eSPhilip Paeps };
857e948693eSPhilip Paeps 
858e948693eSPhilip Paeps static void
sfxge_media_status(if_t ifp,struct ifmediareq * ifmr)85904abf87bSJustin Hibbits sfxge_media_status(if_t ifp, struct ifmediareq *ifmr)
860e948693eSPhilip Paeps {
861e948693eSPhilip Paeps 	struct sfxge_softc *sc;
862e948693eSPhilip Paeps 	efx_phy_media_type_t medium_type;
863e948693eSPhilip Paeps 	efx_link_mode_t mode;
864e948693eSPhilip Paeps 
86504abf87bSJustin Hibbits 	sc = if_getsoftc(ifp);
866763cab71SAndrew Rybchenko 	SFXGE_ADAPTER_LOCK(sc);
867e948693eSPhilip Paeps 
868e948693eSPhilip Paeps 	ifmr->ifm_status = IFM_AVALID;
869e948693eSPhilip Paeps 	ifmr->ifm_active = IFM_ETHER;
870e948693eSPhilip Paeps 
871e948693eSPhilip Paeps 	if (SFXGE_RUNNING(sc) && SFXGE_LINK_UP(sc)) {
872e948693eSPhilip Paeps 		ifmr->ifm_status |= IFM_ACTIVE;
873e948693eSPhilip Paeps 
874e948693eSPhilip Paeps 		efx_phy_media_type_get(sc->enp, &medium_type);
875e948693eSPhilip Paeps 		mode = sc->port.link_mode;
876e948693eSPhilip Paeps 		ifmr->ifm_active |= sfxge_link_mode[medium_type][mode];
877e948693eSPhilip Paeps 		ifmr->ifm_active |= sfxge_port_link_fc_ifm(sc);
878e948693eSPhilip Paeps 	}
879e948693eSPhilip Paeps 
880763cab71SAndrew Rybchenko 	SFXGE_ADAPTER_UNLOCK(sc);
881e948693eSPhilip Paeps }
882e948693eSPhilip Paeps 
883c379e930SAndrew Rybchenko static efx_phy_cap_type_t
sfxge_link_mode_to_phy_cap(efx_link_mode_t mode)884c379e930SAndrew Rybchenko sfxge_link_mode_to_phy_cap(efx_link_mode_t mode)
885c379e930SAndrew Rybchenko {
886c379e930SAndrew Rybchenko 	switch (mode) {
887c379e930SAndrew Rybchenko 	case EFX_LINK_10HDX:
888c379e930SAndrew Rybchenko 		return (EFX_PHY_CAP_10HDX);
889c379e930SAndrew Rybchenko 	case EFX_LINK_10FDX:
890c379e930SAndrew Rybchenko 		return (EFX_PHY_CAP_10FDX);
891c379e930SAndrew Rybchenko 	case EFX_LINK_100HDX:
892c379e930SAndrew Rybchenko 		return (EFX_PHY_CAP_100HDX);
893c379e930SAndrew Rybchenko 	case EFX_LINK_100FDX:
894c379e930SAndrew Rybchenko 		return (EFX_PHY_CAP_100FDX);
895c379e930SAndrew Rybchenko 	case EFX_LINK_1000HDX:
896c379e930SAndrew Rybchenko 		return (EFX_PHY_CAP_1000HDX);
897c379e930SAndrew Rybchenko 	case EFX_LINK_1000FDX:
898c379e930SAndrew Rybchenko 		return (EFX_PHY_CAP_1000FDX);
899c379e930SAndrew Rybchenko 	case EFX_LINK_10000FDX:
900c379e930SAndrew Rybchenko 		return (EFX_PHY_CAP_10000FDX);
901405f7a36SAndrew Rybchenko 	case EFX_LINK_25000FDX:
902405f7a36SAndrew Rybchenko 		return (EFX_PHY_CAP_25000FDX);
9033c838a9fSAndrew Rybchenko 	case EFX_LINK_40000FDX:
9043c838a9fSAndrew Rybchenko 		return (EFX_PHY_CAP_40000FDX);
905405f7a36SAndrew Rybchenko 	case EFX_LINK_50000FDX:
906405f7a36SAndrew Rybchenko 		return (EFX_PHY_CAP_50000FDX);
907405f7a36SAndrew Rybchenko 	case EFX_LINK_100000FDX:
908405f7a36SAndrew Rybchenko 		return (EFX_PHY_CAP_100000FDX);
909c379e930SAndrew Rybchenko 	default:
910c379e930SAndrew Rybchenko 		return (EFX_PHY_CAP_INVALID);
911c379e930SAndrew Rybchenko 	}
912c379e930SAndrew Rybchenko }
913c379e930SAndrew Rybchenko 
914c379e930SAndrew Rybchenko static int
sfxge_phy_cap_mask(struct sfxge_softc * sc,int ifmedia,uint32_t * phy_cap_mask)915c379e930SAndrew Rybchenko sfxge_phy_cap_mask(struct sfxge_softc *sc, int ifmedia, uint32_t *phy_cap_mask)
916c379e930SAndrew Rybchenko {
91711d89392SAndrew Rybchenko 	/* Get global options (duplex), type and subtype bits */
91811d89392SAndrew Rybchenko 	int ifmedia_masked = ifmedia & (IFM_GMASK | IFM_NMASK | IFM_TMASK);
919c379e930SAndrew Rybchenko 	efx_phy_media_type_t medium_type;
920c379e930SAndrew Rybchenko 	boolean_t mode_found = B_FALSE;
921c379e930SAndrew Rybchenko 	uint32_t cap_mask, mode_cap_mask;
922c379e930SAndrew Rybchenko 	efx_link_mode_t mode;
923c379e930SAndrew Rybchenko 	efx_phy_cap_type_t phy_cap;
924c379e930SAndrew Rybchenko 
925c379e930SAndrew Rybchenko 	efx_phy_media_type_get(sc->enp, &medium_type);
926c379e930SAndrew Rybchenko 	if (medium_type >= nitems(sfxge_link_mode)) {
927c379e930SAndrew Rybchenko 		if_printf(sc->ifnet, "unexpected media type %d\n", medium_type);
928c379e930SAndrew Rybchenko 		return (EINVAL);
929c379e930SAndrew Rybchenko 	}
930c379e930SAndrew Rybchenko 
931c379e930SAndrew Rybchenko 	efx_phy_adv_cap_get(sc->enp, EFX_PHY_CAP_PERM, &cap_mask);
932c379e930SAndrew Rybchenko 
933c379e930SAndrew Rybchenko 	for (mode = EFX_LINK_10HDX; mode < EFX_LINK_NMODES; mode++) {
93411d89392SAndrew Rybchenko 		if (ifmedia_masked == sfxge_link_mode[medium_type][mode]) {
935c379e930SAndrew Rybchenko 			mode_found = B_TRUE;
936c379e930SAndrew Rybchenko 			break;
937c379e930SAndrew Rybchenko 		}
938c379e930SAndrew Rybchenko 	}
939c379e930SAndrew Rybchenko 
940c379e930SAndrew Rybchenko 	if (!mode_found) {
941c379e930SAndrew Rybchenko 		/*
942c379e930SAndrew Rybchenko 		 * If media is not in the table, it must be IFM_AUTO.
943c379e930SAndrew Rybchenko 		 */
944c379e930SAndrew Rybchenko 		KASSERT((cap_mask & (1 << EFX_PHY_CAP_AN)) &&
94511d89392SAndrew Rybchenko 		    ifmedia_masked == (IFM_ETHER | IFM_AUTO),
94611d89392SAndrew Rybchenko 		    ("%s: no mode for media %#x", __func__, ifmedia));
947c379e930SAndrew Rybchenko 		*phy_cap_mask = (cap_mask & ~(1 << EFX_PHY_CAP_ASYM));
948c379e930SAndrew Rybchenko 		return (0);
949c379e930SAndrew Rybchenko 	}
950c379e930SAndrew Rybchenko 
951c379e930SAndrew Rybchenko 	phy_cap = sfxge_link_mode_to_phy_cap(mode);
952c379e930SAndrew Rybchenko 	if (phy_cap == EFX_PHY_CAP_INVALID) {
953c379e930SAndrew Rybchenko 		if_printf(sc->ifnet,
954c379e930SAndrew Rybchenko 			  "cannot map link mode %d to phy capability\n",
955c379e930SAndrew Rybchenko 			  mode);
956c379e930SAndrew Rybchenko 		return (EINVAL);
957c379e930SAndrew Rybchenko 	}
958c379e930SAndrew Rybchenko 
959c379e930SAndrew Rybchenko 	mode_cap_mask = (1 << phy_cap);
960c379e930SAndrew Rybchenko 	mode_cap_mask |= cap_mask & (1 << EFX_PHY_CAP_AN);
961c379e930SAndrew Rybchenko #ifdef SFXGE_HAVE_PAUSE_MEDIAOPTS
962c379e930SAndrew Rybchenko 	if (ifmedia & IFM_ETH_RXPAUSE)
963c379e930SAndrew Rybchenko 		mode_cap_mask |= cap_mask & (1 << EFX_PHY_CAP_PAUSE);
964c379e930SAndrew Rybchenko 	if (!(ifmedia & IFM_ETH_TXPAUSE))
965c379e930SAndrew Rybchenko 		mode_cap_mask |= cap_mask & (1 << EFX_PHY_CAP_ASYM);
966c379e930SAndrew Rybchenko #else
967c379e930SAndrew Rybchenko 	mode_cap_mask |= cap_mask & (1 << EFX_PHY_CAP_PAUSE);
968c379e930SAndrew Rybchenko #endif
969c379e930SAndrew Rybchenko 
970c379e930SAndrew Rybchenko 	*phy_cap_mask = mode_cap_mask;
971c379e930SAndrew Rybchenko 	return (0);
972c379e930SAndrew Rybchenko }
973c379e930SAndrew Rybchenko 
974e948693eSPhilip Paeps static int
sfxge_media_change(if_t ifp)97504abf87bSJustin Hibbits sfxge_media_change(if_t ifp)
976e948693eSPhilip Paeps {
977e948693eSPhilip Paeps 	struct sfxge_softc *sc;
978e948693eSPhilip Paeps 	struct ifmedia_entry *ifm;
979e948693eSPhilip Paeps 	int rc;
980c379e930SAndrew Rybchenko 	uint32_t phy_cap_mask;
981e948693eSPhilip Paeps 
98204abf87bSJustin Hibbits 	sc = if_getsoftc(ifp);
983e948693eSPhilip Paeps 	ifm = sc->media.ifm_cur;
984e948693eSPhilip Paeps 
985763cab71SAndrew Rybchenko 	SFXGE_ADAPTER_LOCK(sc);
986e948693eSPhilip Paeps 
987e948693eSPhilip Paeps 	if (!SFXGE_RUNNING(sc)) {
988e948693eSPhilip Paeps 		rc = 0;
989e948693eSPhilip Paeps 		goto out;
990e948693eSPhilip Paeps 	}
991e948693eSPhilip Paeps 
992e948693eSPhilip Paeps 	rc = efx_mac_fcntl_set(sc->enp, sfxge_port_wanted_fc(sc), B_TRUE);
993e948693eSPhilip Paeps 	if (rc != 0)
994e948693eSPhilip Paeps 		goto out;
995e948693eSPhilip Paeps 
996c379e930SAndrew Rybchenko 	if ((rc = sfxge_phy_cap_mask(sc, ifm->ifm_media, &phy_cap_mask)) != 0)
997c379e930SAndrew Rybchenko 		goto out;
998c379e930SAndrew Rybchenko 
999c379e930SAndrew Rybchenko 	rc = efx_phy_adv_cap_set(sc->enp, phy_cap_mask);
1000e948693eSPhilip Paeps out:
1001763cab71SAndrew Rybchenko 	SFXGE_ADAPTER_UNLOCK(sc);
1002e948693eSPhilip Paeps 
1003b7b0edd1SGeorge V. Neville-Neil 	return (rc);
1004e948693eSPhilip Paeps }
1005e948693eSPhilip Paeps 
sfxge_port_ifmedia_init(struct sfxge_softc * sc)1006e948693eSPhilip Paeps int sfxge_port_ifmedia_init(struct sfxge_softc *sc)
1007e948693eSPhilip Paeps {
1008e948693eSPhilip Paeps 	efx_phy_media_type_t medium_type;
1009e948693eSPhilip Paeps 	uint32_t cap_mask, mode_cap_mask;
1010e948693eSPhilip Paeps 	efx_link_mode_t mode;
1011c379e930SAndrew Rybchenko 	efx_phy_cap_type_t phy_cap;
1012e948693eSPhilip Paeps 	int mode_ifm, best_mode_ifm = 0;
1013e948693eSPhilip Paeps 	int rc;
1014e948693eSPhilip Paeps 
10153c838a9fSAndrew Rybchenko 	/*
10163c838a9fSAndrew Rybchenko 	 * We need port state to initialise the ifmedia list.
10173c838a9fSAndrew Rybchenko 	 * It requires initialized NIC what is already done in
10183c838a9fSAndrew Rybchenko 	 * sfxge_create() when resources are estimated.
10193c838a9fSAndrew Rybchenko 	 */
10203c838a9fSAndrew Rybchenko 	if ((rc = efx_filter_init(sc->enp)) != 0)
10213c838a9fSAndrew Rybchenko 		goto out1;
1022e948693eSPhilip Paeps 	if ((rc = efx_port_init(sc->enp)) != 0)
1023e948693eSPhilip Paeps 		goto out2;
1024e948693eSPhilip Paeps 
1025e948693eSPhilip Paeps 	/*
1026e948693eSPhilip Paeps 	 * Register ifconfig callbacks for querying and setting the
1027e948693eSPhilip Paeps 	 * link mode and link status.
1028e948693eSPhilip Paeps 	 */
1029e948693eSPhilip Paeps 	ifmedia_init(&sc->media, IFM_IMASK, sfxge_media_change,
1030e948693eSPhilip Paeps 	    sfxge_media_status);
1031e948693eSPhilip Paeps 
1032e948693eSPhilip Paeps 	/*
1033e948693eSPhilip Paeps 	 * Map firmware medium type and capabilities to ifmedia types.
1034e948693eSPhilip Paeps 	 * ifmedia does not distinguish between forcing the link mode
1035e948693eSPhilip Paeps 	 * and disabling auto-negotiation.  1000BASE-T and 10GBASE-T
1036e948693eSPhilip Paeps 	 * require AN even if only one link mode is enabled, and for
1037e948693eSPhilip Paeps 	 * 100BASE-TX it is useful even if the link mode is forced.
1038e948693eSPhilip Paeps 	 * Therefore we never disable auto-negotiation.
1039e948693eSPhilip Paeps 	 *
1040e948693eSPhilip Paeps 	 * Also enable and advertise flow control by default.
1041e948693eSPhilip Paeps 	 */
1042e948693eSPhilip Paeps 
1043e948693eSPhilip Paeps 	efx_phy_media_type_get(sc->enp, &medium_type);
1044e948693eSPhilip Paeps 	efx_phy_adv_cap_get(sc->enp, EFX_PHY_CAP_PERM, &cap_mask);
1045e948693eSPhilip Paeps 
1046c379e930SAndrew Rybchenko 	for (mode = EFX_LINK_10HDX; mode < EFX_LINK_NMODES; mode++) {
1047c379e930SAndrew Rybchenko 		phy_cap = sfxge_link_mode_to_phy_cap(mode);
1048c379e930SAndrew Rybchenko 		if (phy_cap == EFX_PHY_CAP_INVALID)
1049c379e930SAndrew Rybchenko 			continue;
1050e948693eSPhilip Paeps 
1051c379e930SAndrew Rybchenko 		mode_cap_mask = (1 << phy_cap);
1052e948693eSPhilip Paeps 		mode_ifm = sfxge_link_mode[medium_type][mode];
1053e948693eSPhilip Paeps 
1054e948693eSPhilip Paeps 		if ((cap_mask & mode_cap_mask) && mode_ifm) {
1055c379e930SAndrew Rybchenko 			/* No flow-control */
1056c379e930SAndrew Rybchenko 			ifmedia_add(&sc->media, mode_ifm, 0, NULL);
1057e948693eSPhilip Paeps 
1058e948693eSPhilip Paeps #ifdef SFXGE_HAVE_PAUSE_MEDIAOPTS
1059e948693eSPhilip Paeps 			/* Respond-only.  If using AN, we implicitly
1060e948693eSPhilip Paeps 			 * offer symmetric as well, but that doesn't
1061e948693eSPhilip Paeps 			 * mean we *have* to generate pause frames.
1062e948693eSPhilip Paeps 			 */
1063e948693eSPhilip Paeps 			mode_ifm |= IFM_ETH_RXPAUSE;
1064c379e930SAndrew Rybchenko 			ifmedia_add(&sc->media, mode_ifm, 0, NULL);
1065e948693eSPhilip Paeps 
1066e948693eSPhilip Paeps 			/* Symmetric */
1067e948693eSPhilip Paeps 			mode_ifm |= IFM_ETH_TXPAUSE;
1068c379e930SAndrew Rybchenko 			ifmedia_add(&sc->media, mode_ifm, 0, NULL);
1069e948693eSPhilip Paeps #endif
1070e948693eSPhilip Paeps 
1071e948693eSPhilip Paeps 			/* Link modes are numbered in order of speed,
1072e948693eSPhilip Paeps 			 * so assume the last one available is the best.
1073e948693eSPhilip Paeps 			 */
1074e948693eSPhilip Paeps 			best_mode_ifm = mode_ifm;
1075e948693eSPhilip Paeps 		}
1076e948693eSPhilip Paeps 	}
1077e948693eSPhilip Paeps 
1078e948693eSPhilip Paeps 	if (cap_mask & (1 << EFX_PHY_CAP_AN)) {
1079e948693eSPhilip Paeps 		/* Add autoselect mode. */
1080e948693eSPhilip Paeps 		mode_ifm = IFM_ETHER | IFM_AUTO;
1081c379e930SAndrew Rybchenko 		ifmedia_add(&sc->media, mode_ifm, 0, NULL);
1082e948693eSPhilip Paeps 		best_mode_ifm = mode_ifm;
1083e948693eSPhilip Paeps 	}
1084e948693eSPhilip Paeps 
1085b7b0edd1SGeorge V. Neville-Neil 	if (best_mode_ifm != 0)
1086e948693eSPhilip Paeps 		ifmedia_set(&sc->media, best_mode_ifm);
1087e948693eSPhilip Paeps 
1088e948693eSPhilip Paeps 	/* Now discard port state until interface is started. */
1089e948693eSPhilip Paeps 	efx_port_fini(sc->enp);
1090e948693eSPhilip Paeps out2:
10913c838a9fSAndrew Rybchenko 	efx_filter_fini(sc->enp);
10923c838a9fSAndrew Rybchenko out1:
1093b7b0edd1SGeorge V. Neville-Neil 	return (rc);
1094e948693eSPhilip Paeps }
1095