1227569Sphilip/*-
2300607Sarybchik * Copyright (c) 2010-2016 Solarflare Communications Inc.
3227569Sphilip * All rights reserved.
4227569Sphilip *
5227569Sphilip * This software was developed in part by Philip Paeps under contract for
6227569Sphilip * Solarflare Communications, Inc.
7227569Sphilip *
8227569Sphilip * Redistribution and use in source and binary forms, with or without
9283514Sarybchik * modification, are permitted provided that the following conditions are met:
10227569Sphilip *
11283514Sarybchik * 1. Redistributions of source code must retain the above copyright notice,
12283514Sarybchik *    this list of conditions and the following disclaimer.
13283514Sarybchik * 2. Redistributions in binary form must reproduce the above copyright notice,
14283514Sarybchik *    this list of conditions and the following disclaimer in the documentation
15283514Sarybchik *    and/or other materials provided with the distribution.
16283514Sarybchik *
17283514Sarybchik * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18283514Sarybchik * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
19283514Sarybchik * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20283514Sarybchik * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21283514Sarybchik * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22283514Sarybchik * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23283514Sarybchik * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24283514Sarybchik * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25283514Sarybchik * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26283514Sarybchik * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27283514Sarybchik * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28283514Sarybchik *
29283514Sarybchik * The views and conclusions contained in the software and documentation are
30283514Sarybchik * those of the authors and should not be interpreted as representing official
31283514Sarybchik * policies, either expressed or implied, of the FreeBSD Project.
32227569Sphilip */
33227569Sphilip
34227569Sphilip#include <sys/cdefs.h>
35227569Sphilip__FBSDID("$FreeBSD: releng/11.0/sys/dev/sfxge/sfxge_port.c 301724 2016-06-09 12:29:03Z arybchik $");
36227569Sphilip
37227569Sphilip#include <sys/types.h>
38227699Sphilip#include <sys/limits.h>
39227569Sphilip#include <net/ethernet.h>
40227569Sphilip#include <net/if_dl.h>
41227569Sphilip
42227569Sphilip#include "common/efx.h"
43227569Sphilip
44227569Sphilip#include "sfxge.h"
45227569Sphilip
46283007Sarybchikstatic int sfxge_phy_cap_mask(struct sfxge_softc *, int, uint32_t *);
47283007Sarybchik
48227569Sphilipstatic int
49227569Sphilipsfxge_mac_stat_update(struct sfxge_softc *sc)
50227569Sphilip{
51227569Sphilip	struct sfxge_port *port = &sc->port;
52227569Sphilip	efsys_mem_t *esmp = &(port->mac_stats.dma_buf);
53227569Sphilip	clock_t now;
54227569Sphilip	unsigned int count;
55227569Sphilip	int rc;
56227569Sphilip
57278248Sarybchik	SFXGE_PORT_LOCK_ASSERT_OWNED(port);
58227569Sphilip
59279351Sarybchik	if (__predict_false(port->init_state != SFXGE_PORT_STARTED)) {
60227569Sphilip		rc = 0;
61227569Sphilip		goto out;
62227569Sphilip	}
63227569Sphilip
64227569Sphilip	now = ticks;
65301724Sarybchik	if ((unsigned int)(now - port->mac_stats.update_time) < (unsigned int)hz) {
66227569Sphilip		rc = 0;
67227569Sphilip		goto out;
68227569Sphilip	}
69227569Sphilip
70227569Sphilip	port->mac_stats.update_time = now;
71227569Sphilip
72227569Sphilip	/* If we're unlucky enough to read statistics wduring the DMA, wait
73227569Sphilip	 * up to 10ms for it to finish (typically takes <500us) */
74227569Sphilip	for (count = 0; count < 100; ++count) {
75227569Sphilip		EFSYS_PROBE1(wait, unsigned int, count);
76227569Sphilip
77227569Sphilip		/* Try to update the cached counters */
78227569Sphilip		if ((rc = efx_mac_stats_update(sc->enp, esmp,
79272325Sgnn		    port->mac_stats.decode_buf, NULL)) != EAGAIN)
80227569Sphilip			goto out;
81227569Sphilip
82227569Sphilip		DELAY(100);
83227569Sphilip	}
84227569Sphilip
85227569Sphilip	rc = ETIMEDOUT;
86227569Sphilipout:
87272325Sgnn	return (rc);
88227569Sphilip}
89227569Sphilip
90279184Sarybchikuint64_t
91279184Sarybchiksfxge_get_counter(struct ifnet *ifp, ift_counter c)
92279184Sarybchik{
93279184Sarybchik	struct sfxge_softc *sc = ifp->if_softc;
94279184Sarybchik	uint64_t *mac_stats;
95279184Sarybchik	uint64_t val;
96279184Sarybchik
97279184Sarybchik	SFXGE_PORT_LOCK(&sc->port);
98279184Sarybchik
99279184Sarybchik	/* Ignore error and use old values */
100279184Sarybchik	(void)sfxge_mac_stat_update(sc);
101279184Sarybchik
102279184Sarybchik	mac_stats = (uint64_t *)sc->port.mac_stats.decode_buf;
103279184Sarybchik
104279184Sarybchik	switch (c) {
105279184Sarybchik	case IFCOUNTER_IPACKETS:
106279184Sarybchik		val = mac_stats[EFX_MAC_RX_PKTS];
107279184Sarybchik		break;
108279184Sarybchik	case IFCOUNTER_IERRORS:
109279184Sarybchik		val = mac_stats[EFX_MAC_RX_ERRORS];
110279184Sarybchik		break;
111279184Sarybchik	case IFCOUNTER_OPACKETS:
112279184Sarybchik		val = mac_stats[EFX_MAC_TX_PKTS];
113279184Sarybchik		break;
114279184Sarybchik	case IFCOUNTER_OERRORS:
115279184Sarybchik		val = mac_stats[EFX_MAC_TX_ERRORS];
116279184Sarybchik		break;
117279184Sarybchik	case IFCOUNTER_COLLISIONS:
118279184Sarybchik		val = mac_stats[EFX_MAC_TX_SGL_COL_PKTS] +
119279184Sarybchik		      mac_stats[EFX_MAC_TX_MULT_COL_PKTS] +
120279184Sarybchik		      mac_stats[EFX_MAC_TX_EX_COL_PKTS] +
121279184Sarybchik		      mac_stats[EFX_MAC_TX_LATE_COL_PKTS];
122279184Sarybchik		break;
123279184Sarybchik	case IFCOUNTER_IBYTES:
124279184Sarybchik		val = mac_stats[EFX_MAC_RX_OCTETS];
125279184Sarybchik		break;
126279184Sarybchik	case IFCOUNTER_OBYTES:
127279184Sarybchik		val = mac_stats[EFX_MAC_TX_OCTETS];
128279184Sarybchik		break;
129279184Sarybchik	case IFCOUNTER_OMCASTS:
130279184Sarybchik		val = mac_stats[EFX_MAC_TX_MULTICST_PKTS] +
131279184Sarybchik		      mac_stats[EFX_MAC_TX_BRDCST_PKTS];
132279184Sarybchik		break;
133279184Sarybchik	case IFCOUNTER_OQDROPS:
134279184Sarybchik		SFXGE_PORT_UNLOCK(&sc->port);
135279184Sarybchik		return (sfxge_tx_get_drops(sc));
136279184Sarybchik	case IFCOUNTER_IMCASTS:
137279184Sarybchik		/* if_imcasts is maintained in net/if_ethersubr.c */
138279184Sarybchik	case IFCOUNTER_IQDROPS:
139279184Sarybchik		/* if_iqdrops is maintained in net/if_ethersubr.c */
140279184Sarybchik	case IFCOUNTER_NOPROTO:
141279184Sarybchik		/* if_noproto is maintained in net/if_ethersubr.c */
142279184Sarybchik	default:
143279184Sarybchik		SFXGE_PORT_UNLOCK(&sc->port);
144279184Sarybchik		return (if_get_counter_default(ifp, c));
145279184Sarybchik	}
146279184Sarybchik
147279184Sarybchik	SFXGE_PORT_UNLOCK(&sc->port);
148279184Sarybchik
149279184Sarybchik	return (val);
150279184Sarybchik}
151279184Sarybchik
152227569Sphilipstatic int
153227569Sphilipsfxge_mac_stat_handler(SYSCTL_HANDLER_ARGS)
154227569Sphilip{
155227569Sphilip	struct sfxge_softc *sc = arg1;
156227569Sphilip	unsigned int id = arg2;
157227569Sphilip	int rc;
158278838Sarybchik	uint64_t val;
159227569Sphilip
160278248Sarybchik	SFXGE_PORT_LOCK(&sc->port);
161278838Sarybchik	if ((rc = sfxge_mac_stat_update(sc)) == 0)
162278838Sarybchik		val = ((uint64_t *)sc->port.mac_stats.decode_buf)[id];
163278838Sarybchik	SFXGE_PORT_UNLOCK(&sc->port);
164227569Sphilip
165278838Sarybchik	if (rc == 0)
166278838Sarybchik		rc = SYSCTL_OUT(req, &val, sizeof(val));
167278248Sarybchik	return (rc);
168227569Sphilip}
169227569Sphilip
170227569Sphilipstatic void
171227569Sphilipsfxge_mac_stat_init(struct sfxge_softc *sc)
172227569Sphilip{
173227569Sphilip	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev);
174227569Sphilip	struct sysctl_oid_list *stat_list;
175227569Sphilip	unsigned int id;
176227569Sphilip	const char *name;
177227569Sphilip
178227569Sphilip	stat_list = SYSCTL_CHILDREN(sc->stats_node);
179227569Sphilip
180227569Sphilip	/* Initialise the named stats */
181227569Sphilip	for (id = 0; id < EFX_MAC_NSTATS; id++) {
182227569Sphilip		name = efx_mac_stat_name(sc->enp, id);
183227569Sphilip		SYSCTL_ADD_PROC(
184227569Sphilip			ctx, stat_list,
185227569Sphilip			OID_AUTO, name, CTLTYPE_U64|CTLFLAG_RD,
186227569Sphilip			sc, id, sfxge_mac_stat_handler, "Q",
187227569Sphilip			"");
188227569Sphilip	}
189227569Sphilip}
190227569Sphilip
191227569Sphilip#ifdef SFXGE_HAVE_PAUSE_MEDIAOPTS
192227569Sphilip
193227569Sphilipstatic unsigned int
194227569Sphilipsfxge_port_wanted_fc(struct sfxge_softc *sc)
195227569Sphilip{
196227569Sphilip	struct ifmedia_entry *ifm = sc->media.ifm_cur;
197227569Sphilip
198227569Sphilip	if (ifm->ifm_media == (IFM_ETHER | IFM_AUTO))
199272325Sgnn		return (EFX_FCNTL_RESPOND | EFX_FCNTL_GENERATE);
200272325Sgnn	return (((ifm->ifm_media & IFM_ETH_RXPAUSE) ? EFX_FCNTL_RESPOND : 0) |
201272325Sgnn		((ifm->ifm_media & IFM_ETH_TXPAUSE) ? EFX_FCNTL_GENERATE : 0));
202227569Sphilip}
203227569Sphilip
204227569Sphilipstatic unsigned int
205227569Sphilipsfxge_port_link_fc_ifm(struct sfxge_softc *sc)
206227569Sphilip{
207227569Sphilip	unsigned int wanted_fc, link_fc;
208227569Sphilip
209227569Sphilip	efx_mac_fcntl_get(sc->enp, &wanted_fc, &link_fc);
210227569Sphilip	return ((link_fc & EFX_FCNTL_RESPOND) ? IFM_ETH_RXPAUSE : 0) |
211227569Sphilip		((link_fc & EFX_FCNTL_GENERATE) ? IFM_ETH_TXPAUSE : 0);
212227569Sphilip}
213227569Sphilip
214227569Sphilip#else /* !SFXGE_HAVE_PAUSE_MEDIAOPTS */
215227569Sphilip
216227569Sphilipstatic unsigned int
217227569Sphilipsfxge_port_wanted_fc(struct sfxge_softc *sc)
218227569Sphilip{
219272325Sgnn	return (sc->port.wanted_fc);
220227569Sphilip}
221227569Sphilip
222227569Sphilipstatic unsigned int
223227569Sphilipsfxge_port_link_fc_ifm(struct sfxge_softc *sc)
224227569Sphilip{
225272325Sgnn	return (0);
226227569Sphilip}
227227569Sphilip
228227569Sphilipstatic int
229227569Sphilipsfxge_port_wanted_fc_handler(SYSCTL_HANDLER_ARGS)
230227569Sphilip{
231227569Sphilip	struct sfxge_softc *sc;
232227569Sphilip	struct sfxge_port *port;
233227569Sphilip	unsigned int fcntl;
234227569Sphilip	int error;
235227569Sphilip
236227569Sphilip	sc = arg1;
237227569Sphilip	port = &sc->port;
238227569Sphilip
239272325Sgnn	if (req->newptr != NULL) {
240227569Sphilip		if ((error = SYSCTL_IN(req, &fcntl, sizeof(fcntl))) != 0)
241278838Sarybchik			return (error);
242227569Sphilip
243278838Sarybchik		SFXGE_PORT_LOCK(port);
244227569Sphilip
245278838Sarybchik		if (port->wanted_fc != fcntl) {
246280380Sarybchik			if (port->init_state == SFXGE_PORT_STARTED)
247278838Sarybchik				error = efx_mac_fcntl_set(sc->enp,
248278838Sarybchik							  port->wanted_fc,
249278838Sarybchik							  B_TRUE);
250278838Sarybchik			if (error == 0)
251278838Sarybchik				port->wanted_fc = fcntl;
252278838Sarybchik		}
253227569Sphilip
254278838Sarybchik		SFXGE_PORT_UNLOCK(port);
255278838Sarybchik	} else {
256278838Sarybchik		SFXGE_PORT_LOCK(port);
257278838Sarybchik		fcntl = port->wanted_fc;
258278838Sarybchik		SFXGE_PORT_UNLOCK(port);
259227569Sphilip
260278838Sarybchik		error = SYSCTL_OUT(req, &fcntl, sizeof(fcntl));
261227569Sphilip	}
262227569Sphilip
263227569Sphilip	return (error);
264227569Sphilip}
265227569Sphilip
266227569Sphilipstatic int
267227569Sphilipsfxge_port_link_fc_handler(SYSCTL_HANDLER_ARGS)
268227569Sphilip{
269227569Sphilip	struct sfxge_softc *sc;
270227569Sphilip	struct sfxge_port *port;
271227569Sphilip	unsigned int wanted_fc, link_fc;
272227569Sphilip
273227569Sphilip	sc = arg1;
274227569Sphilip	port = &sc->port;
275227569Sphilip
276278221Sarybchik	SFXGE_PORT_LOCK(port);
277279351Sarybchik	if (__predict_true(port->init_state == SFXGE_PORT_STARTED) &&
278279351Sarybchik	    SFXGE_LINK_UP(sc))
279227569Sphilip		efx_mac_fcntl_get(sc->enp, &wanted_fc, &link_fc);
280227569Sphilip	else
281227569Sphilip		link_fc = 0;
282278221Sarybchik	SFXGE_PORT_UNLOCK(port);
283227569Sphilip
284278838Sarybchik	return (SYSCTL_OUT(req, &link_fc, sizeof(link_fc)));
285227569Sphilip}
286227569Sphilip
287227569Sphilip#endif /* SFXGE_HAVE_PAUSE_MEDIAOPTS */
288227569Sphilip
289272452Sgnnstatic const uint64_t sfxge_link_baudrate[EFX_LINK_NMODES] = {
290227699Sphilip	[EFX_LINK_10HDX]	= IF_Mbps(10),
291227699Sphilip	[EFX_LINK_10FDX]	= IF_Mbps(10),
292227699Sphilip	[EFX_LINK_100HDX]	= IF_Mbps(100),
293227699Sphilip	[EFX_LINK_100FDX]	= IF_Mbps(100),
294227699Sphilip	[EFX_LINK_1000HDX]	= IF_Gbps(1),
295227699Sphilip	[EFX_LINK_1000FDX]	= IF_Gbps(1),
296279267Sarybchik	[EFX_LINK_10000FDX]	= IF_Gbps(10),
297283514Sarybchik	[EFX_LINK_40000FDX]	= IF_Gbps(40),
298227569Sphilip};
299227569Sphilip
300227569Sphilipvoid
301227569Sphilipsfxge_mac_link_update(struct sfxge_softc *sc, efx_link_mode_t mode)
302227569Sphilip{
303227569Sphilip	struct sfxge_port *port;
304227569Sphilip	int link_state;
305272325Sgnn
306227569Sphilip	port = &sc->port;
307227569Sphilip
308227569Sphilip	if (port->link_mode == mode)
309227569Sphilip		return;
310227569Sphilip
311227569Sphilip	port->link_mode = mode;
312227569Sphilip
313227569Sphilip	/* Push link state update to the OS */
314227569Sphilip	link_state = (port->link_mode != EFX_LINK_DOWN ?
315227569Sphilip		      LINK_STATE_UP : LINK_STATE_DOWN);
316227699Sphilip	sc->ifnet->if_baudrate = sfxge_link_baudrate[port->link_mode];
317227569Sphilip	if_link_state_change(sc->ifnet, link_state);
318227569Sphilip}
319227569Sphilip
320227569Sphilipstatic void
321227569Sphilipsfxge_mac_poll_work(void *arg, int npending)
322227569Sphilip{
323227569Sphilip	struct sfxge_softc *sc;
324227569Sphilip	efx_nic_t *enp;
325227569Sphilip	struct sfxge_port *port;
326227569Sphilip	efx_link_mode_t mode;
327227569Sphilip
328227569Sphilip	sc = (struct sfxge_softc *)arg;
329227569Sphilip	enp = sc->enp;
330227569Sphilip	port = &sc->port;
331227569Sphilip
332278221Sarybchik	SFXGE_PORT_LOCK(port);
333227569Sphilip
334279351Sarybchik	if (__predict_false(port->init_state != SFXGE_PORT_STARTED))
335227569Sphilip		goto done;
336227569Sphilip
337227569Sphilip	/* This may sleep waiting for MCDI completion */
338227569Sphilip	(void)efx_port_poll(enp, &mode);
339227569Sphilip	sfxge_mac_link_update(sc, mode);
340227569Sphilip
341227569Sphilipdone:
342278221Sarybchik	SFXGE_PORT_UNLOCK(port);
343227569Sphilip}
344227569Sphilip
345227569Sphilipstatic int
346283514Sarybchiksfxge_mac_multicast_list_set(struct sfxge_softc *sc)
347227569Sphilip{
348227569Sphilip	struct ifnet *ifp = sc->ifnet;
349283514Sarybchik	struct sfxge_port *port = &sc->port;
350283514Sarybchik	uint8_t *mcast_addr = port->mcast_addrs;
351227569Sphilip	struct ifmultiaddr *ifma;
352227569Sphilip	struct sockaddr_dl *sa;
353283514Sarybchik	int rc = 0;
354227569Sphilip
355283514Sarybchik	mtx_assert(&port->lock, MA_OWNED);
356227569Sphilip
357283514Sarybchik	port->mcast_count = 0;
358283514Sarybchik	if_maddr_rlock(ifp);
359283514Sarybchik	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
360283514Sarybchik		if (ifma->ifma_addr->sa_family == AF_LINK) {
361283514Sarybchik			if (port->mcast_count == EFX_MAC_MULTICAST_LIST_MAX) {
362283514Sarybchik				device_printf(sc->dev,
363283514Sarybchik				    "Too many multicast addresses\n");
364283514Sarybchik				rc = EINVAL;
365283514Sarybchik				break;
366283514Sarybchik			}
367227569Sphilip
368283514Sarybchik			sa = (struct sockaddr_dl *)ifma->ifma_addr;
369283514Sarybchik			memcpy(mcast_addr, LLADDR(sa), EFX_MAC_ADDR_LEN);
370283514Sarybchik			mcast_addr += EFX_MAC_ADDR_LEN;
371283514Sarybchik			++port->mcast_count;
372227569Sphilip		}
373227569Sphilip	}
374283514Sarybchik	if_maddr_runlock(ifp);
375283514Sarybchik
376283514Sarybchik	if (rc == 0) {
377283514Sarybchik		rc = efx_mac_multicast_list_set(sc->enp, port->mcast_addrs,
378283514Sarybchik						port->mcast_count);
379283514Sarybchik		if (rc != 0)
380283514Sarybchik			device_printf(sc->dev,
381283514Sarybchik			    "Cannot set multicast address list\n");
382283514Sarybchik	}
383283514Sarybchik
384283514Sarybchik	return (rc);
385227569Sphilip}
386227569Sphilip
387283514Sarybchikstatic int
388283514Sarybchiksfxge_mac_filter_set_locked(struct sfxge_softc *sc)
389283514Sarybchik{
390283514Sarybchik	struct ifnet *ifp = sc->ifnet;
391283514Sarybchik	struct sfxge_port *port = &sc->port;
392283514Sarybchik	boolean_t all_mulcst;
393283514Sarybchik	int rc;
394283514Sarybchik
395283514Sarybchik	mtx_assert(&port->lock, MA_OWNED);
396283514Sarybchik
397283514Sarybchik	all_mulcst = !!(ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI));
398283514Sarybchik
399283514Sarybchik	rc = sfxge_mac_multicast_list_set(sc);
400283514Sarybchik	/* Fallback to all multicast if cannot set multicast list */
401283514Sarybchik	if (rc != 0)
402283514Sarybchik		all_mulcst = B_TRUE;
403283514Sarybchik
404283514Sarybchik	rc = efx_mac_filter_set(sc->enp, !!(ifp->if_flags & IFF_PROMISC),
405283514Sarybchik				(port->mcast_count > 0), all_mulcst, B_TRUE);
406283514Sarybchik
407283514Sarybchik	return (rc);
408283514Sarybchik}
409283514Sarybchik
410227569Sphilipint
411227569Sphilipsfxge_mac_filter_set(struct sfxge_softc *sc)
412227569Sphilip{
413227569Sphilip	struct sfxge_port *port = &sc->port;
414227569Sphilip	int rc;
415227569Sphilip
416278221Sarybchik	SFXGE_PORT_LOCK(port);
417264772Sgnn	/*
418264772Sgnn	 * The function may be called without softc_lock held in the
419264772Sgnn	 * case of SIOCADDMULTI and SIOCDELMULTI ioctls. ioctl handler
420264772Sgnn	 * checks IFF_DRV_RUNNING flag which implies port started, but
421264772Sgnn	 * it is not guaranteed to remain. softc_lock shared lock can't
422264772Sgnn	 * be held in the case of these ioctls processing, since it
423264772Sgnn	 * results in failure where kernel complains that non-sleepable
424264772Sgnn	 * lock is held in sleeping thread. Both problems are repeatable
425264772Sgnn	 * on LAG with LACP proto bring up.
426264772Sgnn	 */
427279351Sarybchik	if (__predict_true(port->init_state == SFXGE_PORT_STARTED))
428264772Sgnn		rc = sfxge_mac_filter_set_locked(sc);
429264772Sgnn	else
430264772Sgnn		rc = 0;
431278221Sarybchik	SFXGE_PORT_UNLOCK(port);
432272325Sgnn	return (rc);
433227569Sphilip}
434227569Sphilip
435227569Sphilipvoid
436227569Sphilipsfxge_port_stop(struct sfxge_softc *sc)
437227569Sphilip{
438227569Sphilip	struct sfxge_port *port;
439227569Sphilip	efx_nic_t *enp;
440227569Sphilip
441227569Sphilip	port = &sc->port;
442227569Sphilip	enp = sc->enp;
443227569Sphilip
444278221Sarybchik	SFXGE_PORT_LOCK(port);
445227569Sphilip
446227569Sphilip	KASSERT(port->init_state == SFXGE_PORT_STARTED,
447227569Sphilip	    ("port not started"));
448227569Sphilip
449227569Sphilip	port->init_state = SFXGE_PORT_INITIALIZED;
450227569Sphilip
451227569Sphilip	port->mac_stats.update_time = 0;
452227569Sphilip
453227569Sphilip	/* This may call MCDI */
454227569Sphilip	(void)efx_mac_drain(enp, B_TRUE);
455227569Sphilip
456227569Sphilip	(void)efx_mac_stats_periodic(enp, &port->mac_stats.dma_buf, 0, B_FALSE);
457227569Sphilip
458227569Sphilip	port->link_mode = EFX_LINK_UNKNOWN;
459227569Sphilip
460227569Sphilip	/* Destroy the common code port object. */
461283514Sarybchik	efx_port_fini(enp);
462227569Sphilip
463283514Sarybchik	efx_filter_fini(enp);
464283514Sarybchik
465278221Sarybchik	SFXGE_PORT_UNLOCK(port);
466227569Sphilip}
467227569Sphilip
468227569Sphilipint
469227569Sphilipsfxge_port_start(struct sfxge_softc *sc)
470227569Sphilip{
471227569Sphilip	uint8_t mac_addr[ETHER_ADDR_LEN];
472227569Sphilip	struct ifnet *ifp = sc->ifnet;
473227569Sphilip	struct sfxge_port *port;
474227569Sphilip	efx_nic_t *enp;
475227569Sphilip	size_t pdu;
476227569Sphilip	int rc;
477283007Sarybchik	uint32_t phy_cap_mask;
478227569Sphilip
479227569Sphilip	port = &sc->port;
480227569Sphilip	enp = sc->enp;
481227569Sphilip
482278221Sarybchik	SFXGE_PORT_LOCK(port);
483227569Sphilip
484227569Sphilip	KASSERT(port->init_state == SFXGE_PORT_INITIALIZED,
485227569Sphilip	    ("port not initialized"));
486227569Sphilip
487283514Sarybchik	/* Initialise the required filtering */
488283514Sarybchik	if ((rc = efx_filter_init(enp)) != 0)
489283514Sarybchik		goto fail_filter_init;
490283514Sarybchik
491227569Sphilip	/* Initialize the port object in the common code. */
492227569Sphilip	if ((rc = efx_port_init(sc->enp)) != 0)
493227569Sphilip		goto fail;
494227569Sphilip
495227569Sphilip	/* Set the SDU */
496227569Sphilip	pdu = EFX_MAC_PDU(ifp->if_mtu);
497227569Sphilip	if ((rc = efx_mac_pdu_set(enp, pdu)) != 0)
498227569Sphilip		goto fail2;
499227569Sphilip
500227569Sphilip	if ((rc = efx_mac_fcntl_set(enp, sfxge_port_wanted_fc(sc), B_TRUE))
501227569Sphilip	    != 0)
502283514Sarybchik		goto fail3;
503227569Sphilip
504227569Sphilip	/* Set the unicast address */
505229613Sjhb	if_addr_rlock(ifp);
506227569Sphilip	bcopy(LLADDR((struct sockaddr_dl *)ifp->if_addr->ifa_addr),
507227569Sphilip	      mac_addr, sizeof(mac_addr));
508229613Sjhb	if_addr_runlock(ifp);
509227569Sphilip	if ((rc = efx_mac_addr_set(enp, mac_addr)) != 0)
510283514Sarybchik		goto fail4;
511227569Sphilip
512227569Sphilip	sfxge_mac_filter_set_locked(sc);
513227569Sphilip
514227569Sphilip	/* Update MAC stats by DMA every second */
515227569Sphilip	if ((rc = efx_mac_stats_periodic(enp, &port->mac_stats.dma_buf,
516283514Sarybchik					 1000, B_FALSE)) != 0)
517283514Sarybchik		goto fail6;
518227569Sphilip
519227569Sphilip	if ((rc = efx_mac_drain(enp, B_FALSE)) != 0)
520283514Sarybchik		goto fail8;
521227569Sphilip
522283007Sarybchik	if ((rc = sfxge_phy_cap_mask(sc, sc->media.ifm_cur->ifm_media,
523283007Sarybchik				     &phy_cap_mask)) != 0)
524283514Sarybchik		goto fail9;
525227569Sphilip
526283007Sarybchik	if ((rc = efx_phy_adv_cap_set(sc->enp, phy_cap_mask)) != 0)
527283514Sarybchik		goto fail10;
528283007Sarybchik
529227569Sphilip	port->init_state = SFXGE_PORT_STARTED;
530227569Sphilip
531227569Sphilip	/* Single poll in case there were missing initial events */
532278221Sarybchik	SFXGE_PORT_UNLOCK(port);
533227569Sphilip	sfxge_mac_poll_work(sc, 0);
534227569Sphilip
535227569Sphilip	return (0);
536227569Sphilip
537283514Sarybchikfail10:
538283514Sarybchikfail9:
539283514Sarybchik	(void)efx_mac_drain(enp, B_TRUE);
540283514Sarybchikfail8:
541283514Sarybchik	(void)efx_mac_stats_periodic(enp, &port->mac_stats.dma_buf, 0, B_FALSE);
542283514Sarybchikfail6:
543227569Sphilipfail4:
544227569Sphilipfail3:
545283514Sarybchik
546227569Sphilipfail2:
547283514Sarybchik	efx_port_fini(enp);
548227569Sphilipfail:
549283514Sarybchik	efx_filter_fini(enp);
550283514Sarybchikfail_filter_init:
551278221Sarybchik	SFXGE_PORT_UNLOCK(port);
552227569Sphilip
553227569Sphilip	return (rc);
554227569Sphilip}
555227569Sphilip
556227569Sphilipstatic int
557227569Sphilipsfxge_phy_stat_update(struct sfxge_softc *sc)
558227569Sphilip{
559227569Sphilip	struct sfxge_port *port = &sc->port;
560227569Sphilip	efsys_mem_t *esmp = &port->phy_stats.dma_buf;
561227569Sphilip	clock_t now;
562227569Sphilip	unsigned int count;
563227569Sphilip	int rc;
564227569Sphilip
565278248Sarybchik	SFXGE_PORT_LOCK_ASSERT_OWNED(port);
566227569Sphilip
567279351Sarybchik	if (__predict_false(port->init_state != SFXGE_PORT_STARTED)) {
568227569Sphilip		rc = 0;
569227569Sphilip		goto out;
570227569Sphilip	}
571227569Sphilip
572227569Sphilip	now = ticks;
573301724Sarybchik	if ((unsigned int)(now - port->phy_stats.update_time) < (unsigned int)hz) {
574227569Sphilip		rc = 0;
575227569Sphilip		goto out;
576227569Sphilip	}
577227569Sphilip
578227569Sphilip	port->phy_stats.update_time = now;
579227569Sphilip
580227569Sphilip	/* If we're unlucky enough to read statistics wduring the DMA, wait
581227569Sphilip	 * up to 10ms for it to finish (typically takes <500us) */
582227569Sphilip	for (count = 0; count < 100; ++count) {
583227569Sphilip		EFSYS_PROBE1(wait, unsigned int, count);
584227569Sphilip
585227569Sphilip		/* Synchronize the DMA memory for reading */
586227569Sphilip		bus_dmamap_sync(esmp->esm_tag, esmp->esm_map,
587227569Sphilip		    BUS_DMASYNC_POSTREAD);
588227569Sphilip
589227569Sphilip		/* Try to update the cached counters */
590227569Sphilip		if ((rc = efx_phy_stats_update(sc->enp, esmp,
591227569Sphilip		    port->phy_stats.decode_buf)) != EAGAIN)
592227569Sphilip			goto out;
593227569Sphilip
594227569Sphilip		DELAY(100);
595227569Sphilip	}
596227569Sphilip
597227569Sphilip	rc = ETIMEDOUT;
598227569Sphilipout:
599272325Sgnn	return (rc);
600227569Sphilip}
601227569Sphilip
602227569Sphilipstatic int
603227569Sphilipsfxge_phy_stat_handler(SYSCTL_HANDLER_ARGS)
604227569Sphilip{
605227569Sphilip	struct sfxge_softc *sc = arg1;
606227569Sphilip	unsigned int id = arg2;
607227569Sphilip	int rc;
608278838Sarybchik	uint32_t val;
609227569Sphilip
610278248Sarybchik	SFXGE_PORT_LOCK(&sc->port);
611278838Sarybchik	if ((rc = sfxge_phy_stat_update(sc)) == 0)
612278838Sarybchik		val = ((uint32_t *)sc->port.phy_stats.decode_buf)[id];
613278838Sarybchik	SFXGE_PORT_UNLOCK(&sc->port);
614227569Sphilip
615278838Sarybchik	if (rc == 0)
616278838Sarybchik		rc = SYSCTL_OUT(req, &val, sizeof(val));
617278248Sarybchik	return (rc);
618227569Sphilip}
619227569Sphilip
620227569Sphilipstatic void
621227569Sphilipsfxge_phy_stat_init(struct sfxge_softc *sc)
622227569Sphilip{
623227569Sphilip	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev);
624227569Sphilip	struct sysctl_oid_list *stat_list;
625227569Sphilip	unsigned int id;
626227569Sphilip	const char *name;
627227569Sphilip	uint64_t stat_mask = efx_nic_cfg_get(sc->enp)->enc_phy_stat_mask;
628227569Sphilip
629227569Sphilip	stat_list = SYSCTL_CHILDREN(sc->stats_node);
630227569Sphilip
631227569Sphilip	/* Initialise the named stats */
632227569Sphilip	for (id = 0; id < EFX_PHY_NSTATS; id++) {
633227569Sphilip		if (!(stat_mask & ((uint64_t)1 << id)))
634227569Sphilip			continue;
635227569Sphilip		name = efx_phy_stat_name(sc->enp, id);
636227569Sphilip		SYSCTL_ADD_PROC(
637227569Sphilip			ctx, stat_list,
638227569Sphilip			OID_AUTO, name, CTLTYPE_UINT|CTLFLAG_RD,
639227569Sphilip			sc, id, sfxge_phy_stat_handler,
640227569Sphilip			id == EFX_PHY_STAT_OUI ? "IX" : "IU",
641227569Sphilip			"");
642227569Sphilip	}
643227569Sphilip}
644227569Sphilip
645227569Sphilipvoid
646227569Sphilipsfxge_port_fini(struct sfxge_softc *sc)
647227569Sphilip{
648227569Sphilip	struct sfxge_port *port;
649227569Sphilip	efsys_mem_t *esmp;
650227569Sphilip
651227569Sphilip	port = &sc->port;
652227569Sphilip	esmp = &port->mac_stats.dma_buf;
653227569Sphilip
654227569Sphilip	KASSERT(port->init_state == SFXGE_PORT_INITIALIZED,
655227569Sphilip	    ("Port not initialized"));
656227569Sphilip
657227569Sphilip	port->init_state = SFXGE_PORT_UNINITIALIZED;
658227569Sphilip
659227569Sphilip	port->link_mode = EFX_LINK_UNKNOWN;
660227569Sphilip
661227569Sphilip	/* Finish with PHY DMA memory */
662227569Sphilip	sfxge_dma_free(&port->phy_stats.dma_buf);
663227569Sphilip	free(port->phy_stats.decode_buf, M_SFXGE);
664227569Sphilip
665227569Sphilip	sfxge_dma_free(esmp);
666227569Sphilip	free(port->mac_stats.decode_buf, M_SFXGE);
667227569Sphilip
668278221Sarybchik	SFXGE_PORT_LOCK_DESTROY(port);
669227569Sphilip
670227569Sphilip	port->sc = NULL;
671227569Sphilip}
672227569Sphilip
673227569Sphilipint
674227569Sphilipsfxge_port_init(struct sfxge_softc *sc)
675227569Sphilip{
676227569Sphilip	struct sfxge_port *port;
677227569Sphilip	struct sysctl_ctx_list *sysctl_ctx;
678227569Sphilip	struct sysctl_oid *sysctl_tree;
679227569Sphilip	efsys_mem_t *mac_stats_buf, *phy_stats_buf;
680227569Sphilip	int rc;
681227569Sphilip
682227569Sphilip	port = &sc->port;
683227569Sphilip	mac_stats_buf = &port->mac_stats.dma_buf;
684227569Sphilip	phy_stats_buf = &port->phy_stats.dma_buf;
685227569Sphilip
686227569Sphilip	KASSERT(port->init_state == SFXGE_PORT_UNINITIALIZED,
687227569Sphilip	    ("Port already initialized"));
688227569Sphilip
689227569Sphilip	port->sc = sc;
690227569Sphilip
691278250Sarybchik	SFXGE_PORT_LOCK_INIT(port, device_get_nameunit(sc->dev));
692227569Sphilip
693283514Sarybchik	DBGPRINT(sc->dev, "alloc PHY stats");
694227569Sphilip	port->phy_stats.decode_buf = malloc(EFX_PHY_NSTATS * sizeof(uint32_t),
695227569Sphilip					    M_SFXGE, M_WAITOK | M_ZERO);
696227569Sphilip	if ((rc = sfxge_dma_alloc(sc, EFX_PHY_STATS_SIZE, phy_stats_buf)) != 0)
697227569Sphilip		goto fail;
698227569Sphilip	sfxge_phy_stat_init(sc);
699227569Sphilip
700283514Sarybchik	DBGPRINT(sc->dev, "init sysctl");
701227569Sphilip	sysctl_ctx = device_get_sysctl_ctx(sc->dev);
702227569Sphilip	sysctl_tree = device_get_sysctl_tree(sc->dev);
703227569Sphilip
704227569Sphilip#ifndef SFXGE_HAVE_PAUSE_MEDIAOPTS
705227569Sphilip	/* If flow control cannot be configured or reported through
706227569Sphilip	 * ifmedia, provide sysctls for it. */
707227569Sphilip	port->wanted_fc = EFX_FCNTL_RESPOND | EFX_FCNTL_GENERATE;
708227569Sphilip	SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
709227569Sphilip	    "wanted_fc", CTLTYPE_UINT|CTLFLAG_RW, sc, 0,
710227569Sphilip	    sfxge_port_wanted_fc_handler, "IU", "wanted flow control mode");
711227569Sphilip	SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
712227569Sphilip	    "link_fc", CTLTYPE_UINT|CTLFLAG_RD, sc, 0,
713227569Sphilip	    sfxge_port_link_fc_handler, "IU", "link flow control mode");
714227569Sphilip#endif
715227569Sphilip
716283514Sarybchik	DBGPRINT(sc->dev, "alloc MAC stats");
717227569Sphilip	port->mac_stats.decode_buf = malloc(EFX_MAC_NSTATS * sizeof(uint64_t),
718227569Sphilip					    M_SFXGE, M_WAITOK | M_ZERO);
719227569Sphilip	if ((rc = sfxge_dma_alloc(sc, EFX_MAC_STATS_SIZE, mac_stats_buf)) != 0)
720227569Sphilip		goto fail2;
721227569Sphilip	sfxge_mac_stat_init(sc);
722227569Sphilip
723227569Sphilip	port->init_state = SFXGE_PORT_INITIALIZED;
724227569Sphilip
725283514Sarybchik	DBGPRINT(sc->dev, "success");
726227569Sphilip	return (0);
727227569Sphilip
728227569Sphilipfail2:
729227569Sphilip	free(port->mac_stats.decode_buf, M_SFXGE);
730227569Sphilip	sfxge_dma_free(phy_stats_buf);
731227569Sphilipfail:
732227569Sphilip	free(port->phy_stats.decode_buf, M_SFXGE);
733278221Sarybchik	SFXGE_PORT_LOCK_DESTROY(port);
734227569Sphilip	port->sc = NULL;
735283514Sarybchik	DBGPRINT(sc->dev, "failed %d", rc);
736272325Sgnn	return (rc);
737227569Sphilip}
738227569Sphilip
739282897Sarybchikstatic const int sfxge_link_mode[EFX_PHY_MEDIA_NTYPES][EFX_LINK_NMODES] = {
740227569Sphilip	[EFX_PHY_MEDIA_CX4] = {
741227569Sphilip		[EFX_LINK_10000FDX]	= IFM_ETHER | IFM_FDX | IFM_10G_CX4,
742227569Sphilip	},
743227569Sphilip	[EFX_PHY_MEDIA_KX4] = {
744227569Sphilip		[EFX_LINK_10000FDX]	= IFM_ETHER | IFM_FDX | IFM_10G_KX4,
745227569Sphilip	},
746227569Sphilip	[EFX_PHY_MEDIA_XFP] = {
747227569Sphilip		/* Don't know the module type, but assume SR for now. */
748227569Sphilip		[EFX_LINK_10000FDX]	= IFM_ETHER | IFM_FDX | IFM_10G_SR,
749227569Sphilip	},
750283514Sarybchik	[EFX_PHY_MEDIA_QSFP_PLUS] = {
751283514Sarybchik		/* Don't know the module type, but assume SR for now. */
752283514Sarybchik		[EFX_LINK_10000FDX]	= IFM_ETHER | IFM_FDX | IFM_10G_SR,
753283514Sarybchik		[EFX_LINK_40000FDX]	= IFM_ETHER | IFM_FDX | IFM_40G_CR4,
754283514Sarybchik	},
755227569Sphilip	[EFX_PHY_MEDIA_SFP_PLUS] = {
756227569Sphilip		/* Don't know the module type, but assume SX/SR for now. */
757227569Sphilip		[EFX_LINK_1000FDX]	= IFM_ETHER | IFM_FDX | IFM_1000_SX,
758227569Sphilip		[EFX_LINK_10000FDX]	= IFM_ETHER | IFM_FDX | IFM_10G_SR,
759227569Sphilip	},
760227569Sphilip	[EFX_PHY_MEDIA_BASE_T] = {
761227569Sphilip		[EFX_LINK_10HDX]	= IFM_ETHER | IFM_HDX | IFM_10_T,
762227569Sphilip		[EFX_LINK_10FDX]	= IFM_ETHER | IFM_FDX | IFM_10_T,
763227569Sphilip		[EFX_LINK_100HDX]	= IFM_ETHER | IFM_HDX | IFM_100_TX,
764227569Sphilip		[EFX_LINK_100FDX]	= IFM_ETHER | IFM_FDX | IFM_100_TX,
765227569Sphilip		[EFX_LINK_1000HDX]	= IFM_ETHER | IFM_HDX | IFM_1000_T,
766227569Sphilip		[EFX_LINK_1000FDX]	= IFM_ETHER | IFM_FDX | IFM_1000_T,
767227569Sphilip		[EFX_LINK_10000FDX]	= IFM_ETHER | IFM_FDX | IFM_10G_T,
768227569Sphilip	},
769227569Sphilip};
770227569Sphilip
771227569Sphilipstatic void
772227569Sphilipsfxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
773227569Sphilip{
774227569Sphilip	struct sfxge_softc *sc;
775227569Sphilip	efx_phy_media_type_t medium_type;
776227569Sphilip	efx_link_mode_t mode;
777227569Sphilip
778227569Sphilip	sc = ifp->if_softc;
779278221Sarybchik	SFXGE_ADAPTER_LOCK(sc);
780227569Sphilip
781227569Sphilip	ifmr->ifm_status = IFM_AVALID;
782227569Sphilip	ifmr->ifm_active = IFM_ETHER;
783227569Sphilip
784227569Sphilip	if (SFXGE_RUNNING(sc) && SFXGE_LINK_UP(sc)) {
785227569Sphilip		ifmr->ifm_status |= IFM_ACTIVE;
786227569Sphilip
787227569Sphilip		efx_phy_media_type_get(sc->enp, &medium_type);
788227569Sphilip		mode = sc->port.link_mode;
789227569Sphilip		ifmr->ifm_active |= sfxge_link_mode[medium_type][mode];
790227569Sphilip		ifmr->ifm_active |= sfxge_port_link_fc_ifm(sc);
791227569Sphilip	}
792227569Sphilip
793278221Sarybchik	SFXGE_ADAPTER_UNLOCK(sc);
794227569Sphilip}
795227569Sphilip
796283007Sarybchikstatic efx_phy_cap_type_t
797283007Sarybchiksfxge_link_mode_to_phy_cap(efx_link_mode_t mode)
798283007Sarybchik{
799283007Sarybchik	switch (mode) {
800283007Sarybchik	case EFX_LINK_10HDX:
801283007Sarybchik		return (EFX_PHY_CAP_10HDX);
802283007Sarybchik	case EFX_LINK_10FDX:
803283007Sarybchik		return (EFX_PHY_CAP_10FDX);
804283007Sarybchik	case EFX_LINK_100HDX:
805283007Sarybchik		return (EFX_PHY_CAP_100HDX);
806283007Sarybchik	case EFX_LINK_100FDX:
807283007Sarybchik		return (EFX_PHY_CAP_100FDX);
808283007Sarybchik	case EFX_LINK_1000HDX:
809283007Sarybchik		return (EFX_PHY_CAP_1000HDX);
810283007Sarybchik	case EFX_LINK_1000FDX:
811283007Sarybchik		return (EFX_PHY_CAP_1000FDX);
812283007Sarybchik	case EFX_LINK_10000FDX:
813283007Sarybchik		return (EFX_PHY_CAP_10000FDX);
814283514Sarybchik	case EFX_LINK_40000FDX:
815283514Sarybchik		return (EFX_PHY_CAP_40000FDX);
816283007Sarybchik	default:
817283007Sarybchik		EFSYS_ASSERT(B_FALSE);
818283007Sarybchik		return (EFX_PHY_CAP_INVALID);
819283007Sarybchik	}
820283007Sarybchik}
821283007Sarybchik
822227569Sphilipstatic int
823283007Sarybchiksfxge_phy_cap_mask(struct sfxge_softc *sc, int ifmedia, uint32_t *phy_cap_mask)
824283007Sarybchik{
825283599Sarybchik	/* Get global options (duplex), type and subtype bits */
826283599Sarybchik	int ifmedia_masked = ifmedia & (IFM_GMASK | IFM_NMASK | IFM_TMASK);
827283007Sarybchik	efx_phy_media_type_t medium_type;
828283007Sarybchik	boolean_t mode_found = B_FALSE;
829283007Sarybchik	uint32_t cap_mask, mode_cap_mask;
830283007Sarybchik	efx_link_mode_t mode;
831283007Sarybchik	efx_phy_cap_type_t phy_cap;
832283007Sarybchik
833283007Sarybchik	efx_phy_media_type_get(sc->enp, &medium_type);
834283007Sarybchik	if (medium_type >= nitems(sfxge_link_mode)) {
835283007Sarybchik		if_printf(sc->ifnet, "unexpected media type %d\n", medium_type);
836283007Sarybchik		return (EINVAL);
837283007Sarybchik	}
838283007Sarybchik
839283007Sarybchik	efx_phy_adv_cap_get(sc->enp, EFX_PHY_CAP_PERM, &cap_mask);
840283007Sarybchik
841283007Sarybchik	for (mode = EFX_LINK_10HDX; mode < EFX_LINK_NMODES; mode++) {
842283599Sarybchik		if (ifmedia_masked == sfxge_link_mode[medium_type][mode]) {
843283007Sarybchik			mode_found = B_TRUE;
844283007Sarybchik			break;
845283007Sarybchik		}
846283007Sarybchik	}
847283007Sarybchik
848283007Sarybchik	if (!mode_found) {
849283007Sarybchik		/*
850283007Sarybchik		 * If media is not in the table, it must be IFM_AUTO.
851283007Sarybchik		 */
852283007Sarybchik		KASSERT((cap_mask & (1 << EFX_PHY_CAP_AN)) &&
853283599Sarybchik		    ifmedia_masked == (IFM_ETHER | IFM_AUTO),
854283599Sarybchik		    ("%s: no mode for media %#x", __func__, ifmedia));
855283007Sarybchik		*phy_cap_mask = (cap_mask & ~(1 << EFX_PHY_CAP_ASYM));
856283007Sarybchik		return (0);
857283007Sarybchik	}
858283007Sarybchik
859283007Sarybchik	phy_cap = sfxge_link_mode_to_phy_cap(mode);
860283007Sarybchik	if (phy_cap == EFX_PHY_CAP_INVALID) {
861283007Sarybchik		if_printf(sc->ifnet,
862283007Sarybchik			  "cannot map link mode %d to phy capability\n",
863283007Sarybchik			  mode);
864283007Sarybchik		return (EINVAL);
865283007Sarybchik	}
866283007Sarybchik
867283007Sarybchik	mode_cap_mask = (1 << phy_cap);
868283007Sarybchik	mode_cap_mask |= cap_mask & (1 << EFX_PHY_CAP_AN);
869283007Sarybchik#ifdef SFXGE_HAVE_PAUSE_MEDIAOPTS
870283007Sarybchik	if (ifmedia & IFM_ETH_RXPAUSE)
871283007Sarybchik		mode_cap_mask |= cap_mask & (1 << EFX_PHY_CAP_PAUSE);
872283007Sarybchik	if (!(ifmedia & IFM_ETH_TXPAUSE))
873283007Sarybchik		mode_cap_mask |= cap_mask & (1 << EFX_PHY_CAP_ASYM);
874283007Sarybchik#else
875283007Sarybchik	mode_cap_mask |= cap_mask & (1 << EFX_PHY_CAP_PAUSE);
876283007Sarybchik#endif
877283007Sarybchik
878283007Sarybchik	*phy_cap_mask = mode_cap_mask;
879283007Sarybchik	return (0);
880283007Sarybchik}
881283007Sarybchik
882283007Sarybchikstatic int
883227569Sphilipsfxge_media_change(struct ifnet *ifp)
884227569Sphilip{
885227569Sphilip	struct sfxge_softc *sc;
886227569Sphilip	struct ifmedia_entry *ifm;
887227569Sphilip	int rc;
888283007Sarybchik	uint32_t phy_cap_mask;
889227569Sphilip
890227569Sphilip	sc = ifp->if_softc;
891227569Sphilip	ifm = sc->media.ifm_cur;
892227569Sphilip
893278221Sarybchik	SFXGE_ADAPTER_LOCK(sc);
894227569Sphilip
895227569Sphilip	if (!SFXGE_RUNNING(sc)) {
896227569Sphilip		rc = 0;
897227569Sphilip		goto out;
898227569Sphilip	}
899227569Sphilip
900227569Sphilip	rc = efx_mac_fcntl_set(sc->enp, sfxge_port_wanted_fc(sc), B_TRUE);
901227569Sphilip	if (rc != 0)
902227569Sphilip		goto out;
903227569Sphilip
904283007Sarybchik	if ((rc = sfxge_phy_cap_mask(sc, ifm->ifm_media, &phy_cap_mask)) != 0)
905283007Sarybchik		goto out;
906283007Sarybchik
907283007Sarybchik	rc = efx_phy_adv_cap_set(sc->enp, phy_cap_mask);
908227569Sphilipout:
909278221Sarybchik	SFXGE_ADAPTER_UNLOCK(sc);
910227569Sphilip
911272325Sgnn	return (rc);
912227569Sphilip}
913227569Sphilip
914227569Sphilipint sfxge_port_ifmedia_init(struct sfxge_softc *sc)
915227569Sphilip{
916227569Sphilip	efx_phy_media_type_t medium_type;
917227569Sphilip	uint32_t cap_mask, mode_cap_mask;
918227569Sphilip	efx_link_mode_t mode;
919283007Sarybchik	efx_phy_cap_type_t phy_cap;
920227569Sphilip	int mode_ifm, best_mode_ifm = 0;
921227569Sphilip	int rc;
922227569Sphilip
923283514Sarybchik	/*
924283514Sarybchik	 * We need port state to initialise the ifmedia list.
925283514Sarybchik	 * It requires initialized NIC what is already done in
926283514Sarybchik	 * sfxge_create() when resources are estimated.
927283514Sarybchik	 */
928283514Sarybchik	if ((rc = efx_filter_init(sc->enp)) != 0)
929283514Sarybchik		goto out1;
930227569Sphilip	if ((rc = efx_port_init(sc->enp)) != 0)
931227569Sphilip		goto out2;
932227569Sphilip
933227569Sphilip	/*
934227569Sphilip	 * Register ifconfig callbacks for querying and setting the
935227569Sphilip	 * link mode and link status.
936227569Sphilip	 */
937227569Sphilip	ifmedia_init(&sc->media, IFM_IMASK, sfxge_media_change,
938227569Sphilip	    sfxge_media_status);
939227569Sphilip
940227569Sphilip	/*
941227569Sphilip	 * Map firmware medium type and capabilities to ifmedia types.
942227569Sphilip	 * ifmedia does not distinguish between forcing the link mode
943227569Sphilip	 * and disabling auto-negotiation.  1000BASE-T and 10GBASE-T
944227569Sphilip	 * require AN even if only one link mode is enabled, and for
945227569Sphilip	 * 100BASE-TX it is useful even if the link mode is forced.
946227569Sphilip	 * Therefore we never disable auto-negotiation.
947227569Sphilip	 *
948227569Sphilip	 * Also enable and advertise flow control by default.
949227569Sphilip	 */
950227569Sphilip
951227569Sphilip	efx_phy_media_type_get(sc->enp, &medium_type);
952227569Sphilip	efx_phy_adv_cap_get(sc->enp, EFX_PHY_CAP_PERM, &cap_mask);
953227569Sphilip
954283007Sarybchik	for (mode = EFX_LINK_10HDX; mode < EFX_LINK_NMODES; mode++) {
955283007Sarybchik		phy_cap = sfxge_link_mode_to_phy_cap(mode);
956283007Sarybchik		if (phy_cap == EFX_PHY_CAP_INVALID)
957283007Sarybchik			continue;
958227569Sphilip
959283007Sarybchik		mode_cap_mask = (1 << phy_cap);
960227569Sphilip		mode_ifm = sfxge_link_mode[medium_type][mode];
961227569Sphilip
962227569Sphilip		if ((cap_mask & mode_cap_mask) && mode_ifm) {
963283007Sarybchik			/* No flow-control */
964283007Sarybchik			ifmedia_add(&sc->media, mode_ifm, 0, NULL);
965227569Sphilip
966227569Sphilip#ifdef SFXGE_HAVE_PAUSE_MEDIAOPTS
967227569Sphilip			/* Respond-only.  If using AN, we implicitly
968227569Sphilip			 * offer symmetric as well, but that doesn't
969227569Sphilip			 * mean we *have* to generate pause frames.
970227569Sphilip			 */
971227569Sphilip			mode_ifm |= IFM_ETH_RXPAUSE;
972283007Sarybchik			ifmedia_add(&sc->media, mode_ifm, 0, NULL);
973227569Sphilip
974227569Sphilip			/* Symmetric */
975227569Sphilip			mode_ifm |= IFM_ETH_TXPAUSE;
976283007Sarybchik			ifmedia_add(&sc->media, mode_ifm, 0, NULL);
977227569Sphilip#endif
978227569Sphilip
979227569Sphilip			/* Link modes are numbered in order of speed,
980227569Sphilip			 * so assume the last one available is the best.
981227569Sphilip			 */
982227569Sphilip			best_mode_ifm = mode_ifm;
983227569Sphilip		}
984227569Sphilip	}
985227569Sphilip
986227569Sphilip	if (cap_mask & (1 << EFX_PHY_CAP_AN)) {
987227569Sphilip		/* Add autoselect mode. */
988227569Sphilip		mode_ifm = IFM_ETHER | IFM_AUTO;
989283007Sarybchik		ifmedia_add(&sc->media, mode_ifm, 0, NULL);
990227569Sphilip		best_mode_ifm = mode_ifm;
991227569Sphilip	}
992227569Sphilip
993272325Sgnn	if (best_mode_ifm != 0)
994227569Sphilip		ifmedia_set(&sc->media, best_mode_ifm);
995227569Sphilip
996227569Sphilip	/* Now discard port state until interface is started. */
997227569Sphilip	efx_port_fini(sc->enp);
998227569Sphilipout2:
999283514Sarybchik	efx_filter_fini(sc->enp);
1000283514Sarybchikout1:
1001272325Sgnn	return (rc);
1002227569Sphilip}
1003