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