sfxge_port.c revision 227569
1227569Sphilip/*-
2227569Sphilip * Copyright (c) 2010-2011 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
9227569Sphilip * modification, are permitted provided that the following conditions
10227569Sphilip * are met:
11227569Sphilip * 1. Redistributions of source code must retain the above copyright
12227569Sphilip *    notice, this list of conditions and the following disclaimer.
13227569Sphilip * 2. Redistributions in binary form must reproduce the above copyright
14227569Sphilip *    notice, this list of conditions and the following disclaimer in the
15227569Sphilip *    documentation and/or other materials provided with the distribution.
16227569Sphilip *
17227569Sphilip * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18227569Sphilip * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19227569Sphilip * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20227569Sphilip * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21227569Sphilip * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22227569Sphilip * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23227569Sphilip * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24227569Sphilip * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25227569Sphilip * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26227569Sphilip * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27227569Sphilip * SUCH DAMAGE.
28227569Sphilip */
29227569Sphilip
30227569Sphilip#include <sys/cdefs.h>
31227569Sphilip__FBSDID("$FreeBSD: head/sys/dev/sfxge/sfxge_port.c 227569 2011-11-16 17:11:13Z philip $");
32227569Sphilip
33227569Sphilip#include <sys/types.h>
34227569Sphilip#include <net/ethernet.h>
35227569Sphilip#include <net/if_dl.h>
36227569Sphilip
37227569Sphilip#include "common/efx.h"
38227569Sphilip
39227569Sphilip#include "sfxge.h"
40227569Sphilip
41227569Sphilipstatic int
42227569Sphilipsfxge_mac_stat_update(struct sfxge_softc *sc)
43227569Sphilip{
44227569Sphilip	struct sfxge_port *port = &sc->port;
45227569Sphilip	efsys_mem_t *esmp = &(port->mac_stats.dma_buf);
46227569Sphilip	clock_t now;
47227569Sphilip	unsigned int count;
48227569Sphilip	int rc;
49227569Sphilip
50227569Sphilip	mtx_lock(&port->lock);
51227569Sphilip
52227569Sphilip	if (port->init_state != SFXGE_PORT_STARTED) {
53227569Sphilip		rc = 0;
54227569Sphilip		goto out;
55227569Sphilip	}
56227569Sphilip
57227569Sphilip	now = ticks;
58227569Sphilip	if (now - port->mac_stats.update_time < hz) {
59227569Sphilip		rc = 0;
60227569Sphilip		goto out;
61227569Sphilip	}
62227569Sphilip
63227569Sphilip	port->mac_stats.update_time = now;
64227569Sphilip
65227569Sphilip	/* If we're unlucky enough to read statistics wduring the DMA, wait
66227569Sphilip	 * up to 10ms for it to finish (typically takes <500us) */
67227569Sphilip	for (count = 0; count < 100; ++count) {
68227569Sphilip		EFSYS_PROBE1(wait, unsigned int, count);
69227569Sphilip
70227569Sphilip		/* Synchronize the DMA memory for reading */
71227569Sphilip		bus_dmamap_sync(esmp->esm_tag, esmp->esm_map,
72227569Sphilip		    BUS_DMASYNC_POSTREAD);
73227569Sphilip
74227569Sphilip		/* Try to update the cached counters */
75227569Sphilip		if ((rc = efx_mac_stats_update(sc->enp, esmp,
76227569Sphilip                    port->mac_stats.decode_buf, NULL)) != EAGAIN)
77227569Sphilip			goto out;
78227569Sphilip
79227569Sphilip		DELAY(100);
80227569Sphilip	}
81227569Sphilip
82227569Sphilip	rc = ETIMEDOUT;
83227569Sphilipout:
84227569Sphilip	mtx_unlock(&port->lock);
85227569Sphilip	return rc;
86227569Sphilip}
87227569Sphilip
88227569Sphilipstatic int
89227569Sphilipsfxge_mac_stat_handler(SYSCTL_HANDLER_ARGS)
90227569Sphilip{
91227569Sphilip	struct sfxge_softc *sc = arg1;
92227569Sphilip	unsigned int id = arg2;
93227569Sphilip	int rc;
94227569Sphilip
95227569Sphilip	if ((rc = sfxge_mac_stat_update(sc)) != 0)
96227569Sphilip		return rc;
97227569Sphilip
98227569Sphilip	return SYSCTL_OUT(req,
99227569Sphilip			  (uint64_t *)sc->port.mac_stats.decode_buf + id,
100227569Sphilip			  sizeof(uint64_t));
101227569Sphilip}
102227569Sphilip
103227569Sphilipstatic void
104227569Sphilipsfxge_mac_stat_init(struct sfxge_softc *sc)
105227569Sphilip{
106227569Sphilip	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev);
107227569Sphilip	struct sysctl_oid_list *stat_list;
108227569Sphilip	unsigned int id;
109227569Sphilip	const char *name;
110227569Sphilip
111227569Sphilip	stat_list = SYSCTL_CHILDREN(sc->stats_node);
112227569Sphilip
113227569Sphilip	/* Initialise the named stats */
114227569Sphilip	for (id = 0; id < EFX_MAC_NSTATS; id++) {
115227569Sphilip		name = efx_mac_stat_name(sc->enp, id);
116227569Sphilip		SYSCTL_ADD_PROC(
117227569Sphilip			ctx, stat_list,
118227569Sphilip			OID_AUTO, name, CTLTYPE_U64|CTLFLAG_RD,
119227569Sphilip			sc, id, sfxge_mac_stat_handler, "Q",
120227569Sphilip			"");
121227569Sphilip	}
122227569Sphilip}
123227569Sphilip
124227569Sphilip#ifdef SFXGE_HAVE_PAUSE_MEDIAOPTS
125227569Sphilip
126227569Sphilipstatic unsigned int
127227569Sphilipsfxge_port_wanted_fc(struct sfxge_softc *sc)
128227569Sphilip{
129227569Sphilip	struct ifmedia_entry *ifm = sc->media.ifm_cur;
130227569Sphilip
131227569Sphilip	if (ifm->ifm_media == (IFM_ETHER | IFM_AUTO))
132227569Sphilip		return EFX_FCNTL_RESPOND | EFX_FCNTL_GENERATE;
133227569Sphilip	return ((ifm->ifm_media & IFM_ETH_RXPAUSE) ? EFX_FCNTL_RESPOND : 0) |
134227569Sphilip		((ifm->ifm_media & IFM_ETH_TXPAUSE) ? EFX_FCNTL_GENERATE : 0);
135227569Sphilip}
136227569Sphilip
137227569Sphilipstatic unsigned int
138227569Sphilipsfxge_port_link_fc_ifm(struct sfxge_softc *sc)
139227569Sphilip{
140227569Sphilip	unsigned int wanted_fc, link_fc;
141227569Sphilip
142227569Sphilip	efx_mac_fcntl_get(sc->enp, &wanted_fc, &link_fc);
143227569Sphilip	return ((link_fc & EFX_FCNTL_RESPOND) ? IFM_ETH_RXPAUSE : 0) |
144227569Sphilip		((link_fc & EFX_FCNTL_GENERATE) ? IFM_ETH_TXPAUSE : 0);
145227569Sphilip}
146227569Sphilip
147227569Sphilip#else /* !SFXGE_HAVE_PAUSE_MEDIAOPTS */
148227569Sphilip
149227569Sphilipstatic unsigned int
150227569Sphilipsfxge_port_wanted_fc(struct sfxge_softc *sc)
151227569Sphilip{
152227569Sphilip	return sc->port.wanted_fc;
153227569Sphilip}
154227569Sphilip
155227569Sphilipstatic unsigned int
156227569Sphilipsfxge_port_link_fc_ifm(struct sfxge_softc *sc)
157227569Sphilip{
158227569Sphilip	return 0;
159227569Sphilip}
160227569Sphilip
161227569Sphilipstatic int
162227569Sphilipsfxge_port_wanted_fc_handler(SYSCTL_HANDLER_ARGS)
163227569Sphilip{
164227569Sphilip	struct sfxge_softc *sc;
165227569Sphilip	struct sfxge_port *port;
166227569Sphilip	unsigned int fcntl;
167227569Sphilip	int error;
168227569Sphilip
169227569Sphilip	sc = arg1;
170227569Sphilip	port = &sc->port;
171227569Sphilip
172227569Sphilip	mtx_lock(&port->lock);
173227569Sphilip
174227569Sphilip	if (req->newptr) {
175227569Sphilip		if ((error = SYSCTL_IN(req, &fcntl, sizeof(fcntl))) != 0)
176227569Sphilip			goto out;
177227569Sphilip
178227569Sphilip		if (port->wanted_fc == fcntl)
179227569Sphilip			goto out;
180227569Sphilip
181227569Sphilip		port->wanted_fc = fcntl;
182227569Sphilip
183227569Sphilip		if (port->init_state != SFXGE_PORT_STARTED)
184227569Sphilip			goto out;
185227569Sphilip
186227569Sphilip		error = efx_mac_fcntl_set(sc->enp, port->wanted_fc, B_TRUE);
187227569Sphilip	} else {
188227569Sphilip		error = SYSCTL_OUT(req, &port->wanted_fc,
189227569Sphilip				   sizeof(port->wanted_fc));
190227569Sphilip	}
191227569Sphilip
192227569Sphilipout:
193227569Sphilip	mtx_unlock(&port->lock);
194227569Sphilip
195227569Sphilip	return (error);
196227569Sphilip}
197227569Sphilip
198227569Sphilipstatic int
199227569Sphilipsfxge_port_link_fc_handler(SYSCTL_HANDLER_ARGS)
200227569Sphilip{
201227569Sphilip	struct sfxge_softc *sc;
202227569Sphilip	struct sfxge_port *port;
203227569Sphilip	unsigned int wanted_fc, link_fc;
204227569Sphilip	int error;
205227569Sphilip
206227569Sphilip	sc = arg1;
207227569Sphilip	port = &sc->port;
208227569Sphilip
209227569Sphilip	mtx_lock(&port->lock);
210227569Sphilip	if (port->init_state == SFXGE_PORT_STARTED && SFXGE_LINK_UP(sc))
211227569Sphilip		efx_mac_fcntl_get(sc->enp, &wanted_fc, &link_fc);
212227569Sphilip	else
213227569Sphilip		link_fc = 0;
214227569Sphilip	error = SYSCTL_OUT(req, &link_fc, sizeof(link_fc));
215227569Sphilip	mtx_unlock(&port->lock);
216227569Sphilip
217227569Sphilip	return (error);
218227569Sphilip}
219227569Sphilip
220227569Sphilip#endif /* SFXGE_HAVE_PAUSE_MEDIAOPTS */
221227569Sphilip
222227569Sphilipstatic const int sfxge_link_speed_kbit[EFX_LINK_NMODES] = {
223227569Sphilip	[EFX_LINK_10HDX]	= 10000,
224227569Sphilip	[EFX_LINK_10FDX]	= 10000,
225227569Sphilip	[EFX_LINK_100HDX]	= 100000,
226227569Sphilip	[EFX_LINK_100FDX]	= 100000,
227227569Sphilip	[EFX_LINK_1000HDX]	= 1000000,
228227569Sphilip	[EFX_LINK_1000FDX]	= 1000000,
229227569Sphilip	[EFX_LINK_10000FDX]	= 10000000,
230227569Sphilip};
231227569Sphilip
232227569Sphilipvoid
233227569Sphilipsfxge_mac_link_update(struct sfxge_softc *sc, efx_link_mode_t mode)
234227569Sphilip{
235227569Sphilip	struct sfxge_port *port;
236227569Sphilip	int link_state;
237227569Sphilip
238227569Sphilip	port = &sc->port;
239227569Sphilip
240227569Sphilip	if (port->link_mode == mode)
241227569Sphilip		return;
242227569Sphilip
243227569Sphilip	port->link_mode = mode;
244227569Sphilip
245227569Sphilip	/* Push link state update to the OS */
246227569Sphilip	link_state = (port->link_mode != EFX_LINK_DOWN ?
247227569Sphilip		      LINK_STATE_UP : LINK_STATE_DOWN);
248227569Sphilip	sc->ifnet->if_baudrate = sfxge_link_speed_kbit[port->link_mode];
249227569Sphilip	if_link_state_change(sc->ifnet, link_state);
250227569Sphilip}
251227569Sphilip
252227569Sphilipstatic void
253227569Sphilipsfxge_mac_poll_work(void *arg, int npending)
254227569Sphilip{
255227569Sphilip	struct sfxge_softc *sc;
256227569Sphilip	efx_nic_t *enp;
257227569Sphilip	struct sfxge_port *port;
258227569Sphilip	efx_link_mode_t mode;
259227569Sphilip
260227569Sphilip	sc = (struct sfxge_softc *)arg;
261227569Sphilip	enp = sc->enp;
262227569Sphilip	port = &sc->port;
263227569Sphilip
264227569Sphilip	mtx_lock(&port->lock);
265227569Sphilip
266227569Sphilip	if (port->init_state != SFXGE_PORT_STARTED)
267227569Sphilip		goto done;
268227569Sphilip
269227569Sphilip	/* This may sleep waiting for MCDI completion */
270227569Sphilip	(void)efx_port_poll(enp, &mode);
271227569Sphilip	sfxge_mac_link_update(sc, mode);
272227569Sphilip
273227569Sphilipdone:
274227569Sphilip	mtx_unlock(&port->lock);
275227569Sphilip}
276227569Sphilip
277227569Sphilipstatic int
278227569Sphilipsfxge_mac_filter_set_locked(struct sfxge_softc *sc)
279227569Sphilip{
280227569Sphilip	unsigned int bucket[EFX_MAC_HASH_BITS];
281227569Sphilip	struct ifnet *ifp = sc->ifnet;
282227569Sphilip	struct ifmultiaddr *ifma;
283227569Sphilip	struct sockaddr_dl *sa;
284227569Sphilip	efx_nic_t *enp = sc->enp;
285227569Sphilip	unsigned int index;
286227569Sphilip	int rc;
287227569Sphilip
288227569Sphilip	/* Set promisc-unicast and broadcast filter bits */
289227569Sphilip	if ((rc = efx_mac_filter_set(enp, !!(ifp->if_flags & IFF_PROMISC),
290227569Sphilip				     B_TRUE)) != 0)
291227569Sphilip		return rc;
292227569Sphilip
293227569Sphilip	/* Set multicast hash filter */
294227569Sphilip	if (ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) {
295227569Sphilip		for (index = 0; index < EFX_MAC_HASH_BITS; index++)
296227569Sphilip			bucket[index] = 1;
297227569Sphilip	} else {
298227569Sphilip		/* Broadcast frames also go through the multicast
299227569Sphilip		 * filter, and the broadcast address hashes to
300227569Sphilip		 * 0xff. */
301227569Sphilip		bucket[0xff] = 1;
302227569Sphilip
303227569Sphilip		IF_ADDR_LOCK(ifp);
304227569Sphilip		TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
305227569Sphilip			if (ifma->ifma_addr->sa_family == AF_LINK) {
306227569Sphilip				sa = (struct sockaddr_dl *)ifma->ifma_addr;
307227569Sphilip				index = ether_crc32_le(LLADDR(sa), 6) & 0xff;
308227569Sphilip				bucket[index] = 1;
309227569Sphilip			}
310227569Sphilip		}
311227569Sphilip		IF_ADDR_UNLOCK(ifp);
312227569Sphilip	}
313227569Sphilip	return efx_mac_hash_set(enp, bucket);
314227569Sphilip}
315227569Sphilip
316227569Sphilipint
317227569Sphilipsfxge_mac_filter_set(struct sfxge_softc *sc)
318227569Sphilip{
319227569Sphilip	struct sfxge_port *port = &sc->port;
320227569Sphilip	int rc;
321227569Sphilip
322227569Sphilip	KASSERT(port->init_state == SFXGE_PORT_STARTED, ("port not started"));
323227569Sphilip
324227569Sphilip	mtx_lock(&port->lock);
325227569Sphilip	rc = sfxge_mac_filter_set_locked(sc);
326227569Sphilip	mtx_unlock(&port->lock);
327227569Sphilip	return rc;
328227569Sphilip}
329227569Sphilip
330227569Sphilipvoid
331227569Sphilipsfxge_port_stop(struct sfxge_softc *sc)
332227569Sphilip{
333227569Sphilip	struct sfxge_port *port;
334227569Sphilip	efx_nic_t *enp;
335227569Sphilip
336227569Sphilip	port = &sc->port;
337227569Sphilip	enp = sc->enp;
338227569Sphilip
339227569Sphilip	mtx_lock(&port->lock);
340227569Sphilip
341227569Sphilip	KASSERT(port->init_state == SFXGE_PORT_STARTED,
342227569Sphilip	    ("port not started"));
343227569Sphilip
344227569Sphilip	port->init_state = SFXGE_PORT_INITIALIZED;
345227569Sphilip
346227569Sphilip	port->mac_stats.update_time = 0;
347227569Sphilip
348227569Sphilip	/* This may call MCDI */
349227569Sphilip	(void)efx_mac_drain(enp, B_TRUE);
350227569Sphilip
351227569Sphilip	(void)efx_mac_stats_periodic(enp, &port->mac_stats.dma_buf, 0, B_FALSE);
352227569Sphilip
353227569Sphilip	port->link_mode = EFX_LINK_UNKNOWN;
354227569Sphilip
355227569Sphilip	/* Destroy the common code port object. */
356227569Sphilip	efx_port_fini(sc->enp);
357227569Sphilip
358227569Sphilip	mtx_unlock(&port->lock);
359227569Sphilip}
360227569Sphilip
361227569Sphilipint
362227569Sphilipsfxge_port_start(struct sfxge_softc *sc)
363227569Sphilip{
364227569Sphilip	uint8_t mac_addr[ETHER_ADDR_LEN];
365227569Sphilip	struct ifnet *ifp = sc->ifnet;
366227569Sphilip	struct sfxge_port *port;
367227569Sphilip	efx_nic_t *enp;
368227569Sphilip	size_t pdu;
369227569Sphilip	int rc;
370227569Sphilip
371227569Sphilip	port = &sc->port;
372227569Sphilip	enp = sc->enp;
373227569Sphilip
374227569Sphilip	mtx_lock(&port->lock);
375227569Sphilip
376227569Sphilip	KASSERT(port->init_state == SFXGE_PORT_INITIALIZED,
377227569Sphilip	    ("port not initialized"));
378227569Sphilip
379227569Sphilip	/* Initialize the port object in the common code. */
380227569Sphilip	if ((rc = efx_port_init(sc->enp)) != 0)
381227569Sphilip		goto fail;
382227569Sphilip
383227569Sphilip	/* Set the SDU */
384227569Sphilip	pdu = EFX_MAC_PDU(ifp->if_mtu);
385227569Sphilip	if ((rc = efx_mac_pdu_set(enp, pdu)) != 0)
386227569Sphilip		goto fail2;
387227569Sphilip
388227569Sphilip	if ((rc = efx_mac_fcntl_set(enp, sfxge_port_wanted_fc(sc), B_TRUE))
389227569Sphilip	    != 0)
390227569Sphilip		goto fail2;
391227569Sphilip
392227569Sphilip	/* Set the unicast address */
393227569Sphilip	IF_ADDR_LOCK(ifp);
394227569Sphilip	bcopy(LLADDR((struct sockaddr_dl *)ifp->if_addr->ifa_addr),
395227569Sphilip	      mac_addr, sizeof(mac_addr));
396227569Sphilip	IF_ADDR_UNLOCK(ifp);
397227569Sphilip	if ((rc = efx_mac_addr_set(enp, mac_addr)) != 0)
398227569Sphilip		goto fail;
399227569Sphilip
400227569Sphilip	sfxge_mac_filter_set_locked(sc);
401227569Sphilip
402227569Sphilip	/* Update MAC stats by DMA every second */
403227569Sphilip	if ((rc = efx_mac_stats_periodic(enp, &port->mac_stats.dma_buf,
404227569Sphilip            1000, B_FALSE)) != 0)
405227569Sphilip		goto fail2;
406227569Sphilip
407227569Sphilip	if ((rc = efx_mac_drain(enp, B_FALSE)) != 0)
408227569Sphilip		goto fail3;
409227569Sphilip
410227569Sphilip	if ((rc = efx_phy_adv_cap_set(sc->enp, sc->media.ifm_cur->ifm_data))
411227569Sphilip	    != 0)
412227569Sphilip		goto fail4;
413227569Sphilip
414227569Sphilip	port->init_state = SFXGE_PORT_STARTED;
415227569Sphilip
416227569Sphilip	/* Single poll in case there were missing initial events */
417227569Sphilip	mtx_unlock(&port->lock);
418227569Sphilip	sfxge_mac_poll_work(sc, 0);
419227569Sphilip
420227569Sphilip	return (0);
421227569Sphilip
422227569Sphilipfail4:
423227569Sphilip	(void)efx_mac_drain(enp, B_TRUE);
424227569Sphilipfail3:
425227569Sphilip	(void)efx_mac_stats_periodic(enp, &port->mac_stats.dma_buf,
426227569Sphilip            0, B_FALSE);
427227569Sphilipfail2:
428227569Sphilip	efx_port_fini(sc->enp);
429227569Sphilipfail:
430227569Sphilip	mtx_unlock(&port->lock);
431227569Sphilip
432227569Sphilip	return (rc);
433227569Sphilip}
434227569Sphilip
435227569Sphilipstatic int
436227569Sphilipsfxge_phy_stat_update(struct sfxge_softc *sc)
437227569Sphilip{
438227569Sphilip	struct sfxge_port *port = &sc->port;
439227569Sphilip	efsys_mem_t *esmp = &port->phy_stats.dma_buf;
440227569Sphilip	clock_t now;
441227569Sphilip	unsigned int count;
442227569Sphilip	int rc;
443227569Sphilip
444227569Sphilip	mtx_lock(&port->lock);
445227569Sphilip
446227569Sphilip	if (port->init_state != SFXGE_PORT_STARTED) {
447227569Sphilip		rc = 0;
448227569Sphilip		goto out;
449227569Sphilip	}
450227569Sphilip
451227569Sphilip	now = ticks;
452227569Sphilip	if (now - port->phy_stats.update_time < hz) {
453227569Sphilip		rc = 0;
454227569Sphilip		goto out;
455227569Sphilip	}
456227569Sphilip
457227569Sphilip	port->phy_stats.update_time = now;
458227569Sphilip
459227569Sphilip	/* If we're unlucky enough to read statistics wduring the DMA, wait
460227569Sphilip	 * up to 10ms for it to finish (typically takes <500us) */
461227569Sphilip	for (count = 0; count < 100; ++count) {
462227569Sphilip		EFSYS_PROBE1(wait, unsigned int, count);
463227569Sphilip
464227569Sphilip		/* Synchronize the DMA memory for reading */
465227569Sphilip		bus_dmamap_sync(esmp->esm_tag, esmp->esm_map,
466227569Sphilip		    BUS_DMASYNC_POSTREAD);
467227569Sphilip
468227569Sphilip		/* Try to update the cached counters */
469227569Sphilip		if ((rc = efx_phy_stats_update(sc->enp, esmp,
470227569Sphilip		    port->phy_stats.decode_buf)) != EAGAIN)
471227569Sphilip			goto out;
472227569Sphilip
473227569Sphilip		DELAY(100);
474227569Sphilip	}
475227569Sphilip
476227569Sphilip	rc = ETIMEDOUT;
477227569Sphilipout:
478227569Sphilip	mtx_unlock(&port->lock);
479227569Sphilip	return rc;
480227569Sphilip}
481227569Sphilip
482227569Sphilipstatic int
483227569Sphilipsfxge_phy_stat_handler(SYSCTL_HANDLER_ARGS)
484227569Sphilip{
485227569Sphilip	struct sfxge_softc *sc = arg1;
486227569Sphilip	unsigned int id = arg2;
487227569Sphilip	int rc;
488227569Sphilip
489227569Sphilip	if ((rc = sfxge_phy_stat_update(sc)) != 0)
490227569Sphilip		return rc;
491227569Sphilip
492227569Sphilip	return SYSCTL_OUT(req,
493227569Sphilip			  (uint32_t *)sc->port.phy_stats.decode_buf + id,
494227569Sphilip			  sizeof(uint32_t));
495227569Sphilip}
496227569Sphilip
497227569Sphilipstatic void
498227569Sphilipsfxge_phy_stat_init(struct sfxge_softc *sc)
499227569Sphilip{
500227569Sphilip	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev);
501227569Sphilip	struct sysctl_oid_list *stat_list;
502227569Sphilip	unsigned int id;
503227569Sphilip	const char *name;
504227569Sphilip	uint64_t stat_mask = efx_nic_cfg_get(sc->enp)->enc_phy_stat_mask;
505227569Sphilip
506227569Sphilip	stat_list = SYSCTL_CHILDREN(sc->stats_node);
507227569Sphilip
508227569Sphilip	/* Initialise the named stats */
509227569Sphilip	for (id = 0; id < EFX_PHY_NSTATS; id++) {
510227569Sphilip		if (!(stat_mask & ((uint64_t)1 << id)))
511227569Sphilip			continue;
512227569Sphilip		name = efx_phy_stat_name(sc->enp, id);
513227569Sphilip		SYSCTL_ADD_PROC(
514227569Sphilip			ctx, stat_list,
515227569Sphilip			OID_AUTO, name, CTLTYPE_UINT|CTLFLAG_RD,
516227569Sphilip			sc, id, sfxge_phy_stat_handler,
517227569Sphilip			id == EFX_PHY_STAT_OUI ? "IX" : "IU",
518227569Sphilip			"");
519227569Sphilip	}
520227569Sphilip}
521227569Sphilip
522227569Sphilipvoid
523227569Sphilipsfxge_port_fini(struct sfxge_softc *sc)
524227569Sphilip{
525227569Sphilip	struct sfxge_port *port;
526227569Sphilip	efsys_mem_t *esmp;
527227569Sphilip
528227569Sphilip	port = &sc->port;
529227569Sphilip	esmp = &port->mac_stats.dma_buf;
530227569Sphilip
531227569Sphilip	KASSERT(port->init_state == SFXGE_PORT_INITIALIZED,
532227569Sphilip	    ("Port not initialized"));
533227569Sphilip
534227569Sphilip	port->init_state = SFXGE_PORT_UNINITIALIZED;
535227569Sphilip
536227569Sphilip	port->link_mode = EFX_LINK_UNKNOWN;
537227569Sphilip
538227569Sphilip	/* Finish with PHY DMA memory */
539227569Sphilip	sfxge_dma_free(&port->phy_stats.dma_buf);
540227569Sphilip	free(port->phy_stats.decode_buf, M_SFXGE);
541227569Sphilip
542227569Sphilip	sfxge_dma_free(esmp);
543227569Sphilip	free(port->mac_stats.decode_buf, M_SFXGE);
544227569Sphilip
545227569Sphilip	mtx_destroy(&port->lock);
546227569Sphilip
547227569Sphilip	port->sc = NULL;
548227569Sphilip}
549227569Sphilip
550227569Sphilipint
551227569Sphilipsfxge_port_init(struct sfxge_softc *sc)
552227569Sphilip{
553227569Sphilip	struct sfxge_port *port;
554227569Sphilip	struct sysctl_ctx_list *sysctl_ctx;
555227569Sphilip	struct sysctl_oid *sysctl_tree;
556227569Sphilip	efsys_mem_t *mac_stats_buf, *phy_stats_buf;
557227569Sphilip	int rc;
558227569Sphilip
559227569Sphilip	port = &sc->port;
560227569Sphilip	mac_stats_buf = &port->mac_stats.dma_buf;
561227569Sphilip	phy_stats_buf = &port->phy_stats.dma_buf;
562227569Sphilip
563227569Sphilip	KASSERT(port->init_state == SFXGE_PORT_UNINITIALIZED,
564227569Sphilip	    ("Port already initialized"));
565227569Sphilip
566227569Sphilip	port->sc = sc;
567227569Sphilip
568227569Sphilip	mtx_init(&port->lock, "sfxge_port", NULL, MTX_DEF);
569227569Sphilip
570227569Sphilip	port->phy_stats.decode_buf = malloc(EFX_PHY_NSTATS * sizeof(uint32_t),
571227569Sphilip					    M_SFXGE, M_WAITOK | M_ZERO);
572227569Sphilip	if ((rc = sfxge_dma_alloc(sc, EFX_PHY_STATS_SIZE, phy_stats_buf)) != 0)
573227569Sphilip		goto fail;
574227569Sphilip	bzero(phy_stats_buf->esm_base, phy_stats_buf->esm_size);
575227569Sphilip	sfxge_phy_stat_init(sc);
576227569Sphilip
577227569Sphilip	sysctl_ctx = device_get_sysctl_ctx(sc->dev);
578227569Sphilip	sysctl_tree = device_get_sysctl_tree(sc->dev);
579227569Sphilip
580227569Sphilip#ifndef SFXGE_HAVE_PAUSE_MEDIAOPTS
581227569Sphilip	/* If flow control cannot be configured or reported through
582227569Sphilip	 * ifmedia, provide sysctls for it. */
583227569Sphilip	port->wanted_fc = EFX_FCNTL_RESPOND | EFX_FCNTL_GENERATE;
584227569Sphilip	SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
585227569Sphilip	    "wanted_fc", CTLTYPE_UINT|CTLFLAG_RW, sc, 0,
586227569Sphilip	    sfxge_port_wanted_fc_handler, "IU", "wanted flow control mode");
587227569Sphilip	SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO,
588227569Sphilip	    "link_fc", CTLTYPE_UINT|CTLFLAG_RD, sc, 0,
589227569Sphilip	    sfxge_port_link_fc_handler, "IU", "link flow control mode");
590227569Sphilip#endif
591227569Sphilip
592227569Sphilip	port->mac_stats.decode_buf = malloc(EFX_MAC_NSTATS * sizeof(uint64_t),
593227569Sphilip					    M_SFXGE, M_WAITOK | M_ZERO);
594227569Sphilip	if ((rc = sfxge_dma_alloc(sc, EFX_MAC_STATS_SIZE, mac_stats_buf)) != 0)
595227569Sphilip		goto fail2;
596227569Sphilip	bzero(mac_stats_buf->esm_base, mac_stats_buf->esm_size);
597227569Sphilip	sfxge_mac_stat_init(sc);
598227569Sphilip
599227569Sphilip	port->init_state = SFXGE_PORT_INITIALIZED;
600227569Sphilip
601227569Sphilip	return (0);
602227569Sphilip
603227569Sphilipfail2:
604227569Sphilip	free(port->mac_stats.decode_buf, M_SFXGE);
605227569Sphilip	sfxge_dma_free(phy_stats_buf);
606227569Sphilipfail:
607227569Sphilip	free(port->phy_stats.decode_buf, M_SFXGE);
608227569Sphilip	(void)mtx_destroy(&port->lock);
609227569Sphilip	port->sc = NULL;
610227569Sphilip	return rc;
611227569Sphilip}
612227569Sphilip
613227569Sphilipstatic int sfxge_link_mode[EFX_PHY_MEDIA_NTYPES][EFX_LINK_NMODES] = {
614227569Sphilip	[EFX_PHY_MEDIA_CX4] = {
615227569Sphilip		[EFX_LINK_10000FDX]	= IFM_ETHER | IFM_FDX | IFM_10G_CX4,
616227569Sphilip	},
617227569Sphilip	[EFX_PHY_MEDIA_KX4] = {
618227569Sphilip		[EFX_LINK_10000FDX]	= IFM_ETHER | IFM_FDX | IFM_10G_KX4,
619227569Sphilip	},
620227569Sphilip	[EFX_PHY_MEDIA_XFP] = {
621227569Sphilip		/* Don't know the module type, but assume SR for now. */
622227569Sphilip		[EFX_LINK_10000FDX]	= IFM_ETHER | IFM_FDX | IFM_10G_SR,
623227569Sphilip	},
624227569Sphilip	[EFX_PHY_MEDIA_SFP_PLUS] = {
625227569Sphilip		/* Don't know the module type, but assume SX/SR for now. */
626227569Sphilip		[EFX_LINK_1000FDX]	= IFM_ETHER | IFM_FDX | IFM_1000_SX,
627227569Sphilip		[EFX_LINK_10000FDX]	= IFM_ETHER | IFM_FDX | IFM_10G_SR,
628227569Sphilip	},
629227569Sphilip	[EFX_PHY_MEDIA_BASE_T] = {
630227569Sphilip		[EFX_LINK_10HDX]	= IFM_ETHER | IFM_HDX | IFM_10_T,
631227569Sphilip		[EFX_LINK_10FDX]	= IFM_ETHER | IFM_FDX | IFM_10_T,
632227569Sphilip		[EFX_LINK_100HDX]	= IFM_ETHER | IFM_HDX | IFM_100_TX,
633227569Sphilip		[EFX_LINK_100FDX]	= IFM_ETHER | IFM_FDX | IFM_100_TX,
634227569Sphilip		[EFX_LINK_1000HDX]	= IFM_ETHER | IFM_HDX | IFM_1000_T,
635227569Sphilip		[EFX_LINK_1000FDX]	= IFM_ETHER | IFM_FDX | IFM_1000_T,
636227569Sphilip		[EFX_LINK_10000FDX]	= IFM_ETHER | IFM_FDX | IFM_10G_T,
637227569Sphilip	},
638227569Sphilip};
639227569Sphilip
640227569Sphilipstatic void
641227569Sphilipsfxge_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
642227569Sphilip{
643227569Sphilip	struct sfxge_softc *sc;
644227569Sphilip	efx_phy_media_type_t medium_type;
645227569Sphilip	efx_link_mode_t mode;
646227569Sphilip
647227569Sphilip	sc = ifp->if_softc;
648227569Sphilip	sx_xlock(&sc->softc_lock);
649227569Sphilip
650227569Sphilip	ifmr->ifm_status = IFM_AVALID;
651227569Sphilip	ifmr->ifm_active = IFM_ETHER;
652227569Sphilip
653227569Sphilip	if (SFXGE_RUNNING(sc) && SFXGE_LINK_UP(sc)) {
654227569Sphilip		ifmr->ifm_status |= IFM_ACTIVE;
655227569Sphilip
656227569Sphilip		efx_phy_media_type_get(sc->enp, &medium_type);
657227569Sphilip		mode = sc->port.link_mode;
658227569Sphilip		ifmr->ifm_active |= sfxge_link_mode[medium_type][mode];
659227569Sphilip		ifmr->ifm_active |= sfxge_port_link_fc_ifm(sc);
660227569Sphilip	}
661227569Sphilip
662227569Sphilip	sx_xunlock(&sc->softc_lock);
663227569Sphilip}
664227569Sphilip
665227569Sphilipstatic int
666227569Sphilipsfxge_media_change(struct ifnet *ifp)
667227569Sphilip{
668227569Sphilip	struct sfxge_softc *sc;
669227569Sphilip	struct ifmedia_entry *ifm;
670227569Sphilip	int rc;
671227569Sphilip
672227569Sphilip	sc = ifp->if_softc;
673227569Sphilip	ifm = sc->media.ifm_cur;
674227569Sphilip
675227569Sphilip	sx_xlock(&sc->softc_lock);
676227569Sphilip
677227569Sphilip	if (!SFXGE_RUNNING(sc)) {
678227569Sphilip		rc = 0;
679227569Sphilip		goto out;
680227569Sphilip	}
681227569Sphilip
682227569Sphilip	rc = efx_mac_fcntl_set(sc->enp, sfxge_port_wanted_fc(sc), B_TRUE);
683227569Sphilip	if (rc != 0)
684227569Sphilip		goto out;
685227569Sphilip
686227569Sphilip	rc = efx_phy_adv_cap_set(sc->enp, ifm->ifm_data);
687227569Sphilipout:
688227569Sphilip	sx_xunlock(&sc->softc_lock);
689227569Sphilip
690227569Sphilip	return rc;
691227569Sphilip}
692227569Sphilip
693227569Sphilipint sfxge_port_ifmedia_init(struct sfxge_softc *sc)
694227569Sphilip{
695227569Sphilip	efx_phy_media_type_t medium_type;
696227569Sphilip	uint32_t cap_mask, mode_cap_mask;
697227569Sphilip	efx_link_mode_t mode;
698227569Sphilip	int mode_ifm, best_mode_ifm = 0;
699227569Sphilip	int rc;
700227569Sphilip
701227569Sphilip	/* We need port state to initialise the ifmedia list. */
702227569Sphilip	if ((rc = efx_nic_init(sc->enp)) != 0)
703227569Sphilip		goto out;
704227569Sphilip	if ((rc = efx_port_init(sc->enp)) != 0)
705227569Sphilip		goto out2;
706227569Sphilip
707227569Sphilip	/*
708227569Sphilip	 * Register ifconfig callbacks for querying and setting the
709227569Sphilip	 * link mode and link status.
710227569Sphilip	 */
711227569Sphilip	ifmedia_init(&sc->media, IFM_IMASK, sfxge_media_change,
712227569Sphilip	    sfxge_media_status);
713227569Sphilip
714227569Sphilip	/*
715227569Sphilip	 * Map firmware medium type and capabilities to ifmedia types.
716227569Sphilip	 * ifmedia does not distinguish between forcing the link mode
717227569Sphilip	 * and disabling auto-negotiation.  1000BASE-T and 10GBASE-T
718227569Sphilip	 * require AN even if only one link mode is enabled, and for
719227569Sphilip	 * 100BASE-TX it is useful even if the link mode is forced.
720227569Sphilip	 * Therefore we never disable auto-negotiation.
721227569Sphilip	 *
722227569Sphilip	 * Also enable and advertise flow control by default.
723227569Sphilip	 */
724227569Sphilip
725227569Sphilip	efx_phy_media_type_get(sc->enp, &medium_type);
726227569Sphilip	efx_phy_adv_cap_get(sc->enp, EFX_PHY_CAP_PERM, &cap_mask);
727227569Sphilip
728227569Sphilip	EFX_STATIC_ASSERT(EFX_LINK_10HDX == EFX_PHY_CAP_10HDX + 1);
729227569Sphilip	EFX_STATIC_ASSERT(EFX_LINK_10FDX == EFX_PHY_CAP_10FDX + 1);
730227569Sphilip	EFX_STATIC_ASSERT(EFX_LINK_100HDX == EFX_PHY_CAP_100HDX + 1);
731227569Sphilip	EFX_STATIC_ASSERT(EFX_LINK_100FDX == EFX_PHY_CAP_100FDX + 1);
732227569Sphilip	EFX_STATIC_ASSERT(EFX_LINK_1000HDX == EFX_PHY_CAP_1000HDX + 1);
733227569Sphilip	EFX_STATIC_ASSERT(EFX_LINK_1000FDX == EFX_PHY_CAP_1000FDX + 1);
734227569Sphilip	EFX_STATIC_ASSERT(EFX_LINK_10000FDX == EFX_PHY_CAP_10000FDX + 1);
735227569Sphilip
736227569Sphilip	for (mode = EFX_LINK_10HDX; mode <= EFX_LINK_10000FDX; mode++) {
737227569Sphilip		mode_cap_mask = 1 << (mode - 1);
738227569Sphilip		mode_ifm = sfxge_link_mode[medium_type][mode];
739227569Sphilip
740227569Sphilip		if ((cap_mask & mode_cap_mask) && mode_ifm) {
741227569Sphilip			mode_cap_mask |= cap_mask & (1 << EFX_PHY_CAP_AN);
742227569Sphilip
743227569Sphilip#ifdef SFXGE_HAVE_PAUSE_MEDIAOPTS
744227569Sphilip			/* No flow-control */
745227569Sphilip			ifmedia_add(&sc->media, mode_ifm, mode_cap_mask, NULL);
746227569Sphilip
747227569Sphilip			/* Respond-only.  If using AN, we implicitly
748227569Sphilip			 * offer symmetric as well, but that doesn't
749227569Sphilip			 * mean we *have* to generate pause frames.
750227569Sphilip			 */
751227569Sphilip			mode_cap_mask |= cap_mask & ((1 << EFX_PHY_CAP_PAUSE) |
752227569Sphilip						     (1 << EFX_PHY_CAP_ASYM));
753227569Sphilip			mode_ifm |= IFM_ETH_RXPAUSE;
754227569Sphilip			ifmedia_add(&sc->media, mode_ifm, mode_cap_mask, NULL);
755227569Sphilip
756227569Sphilip			/* Symmetric */
757227569Sphilip			mode_cap_mask &= ~(1 << EFX_PHY_CAP_ASYM);
758227569Sphilip			mode_ifm |= IFM_ETH_TXPAUSE;
759227569Sphilip#else /* !SFXGE_HAVE_PAUSE_MEDIAOPTS */
760227569Sphilip			mode_cap_mask |= cap_mask & (1 << EFX_PHY_CAP_PAUSE);
761227569Sphilip#endif
762227569Sphilip			ifmedia_add(&sc->media, mode_ifm, mode_cap_mask, NULL);
763227569Sphilip
764227569Sphilip			/* Link modes are numbered in order of speed,
765227569Sphilip			 * so assume the last one available is the best.
766227569Sphilip			 */
767227569Sphilip			best_mode_ifm = mode_ifm;
768227569Sphilip		}
769227569Sphilip	}
770227569Sphilip
771227569Sphilip	if (cap_mask & (1 << EFX_PHY_CAP_AN)) {
772227569Sphilip		/* Add autoselect mode. */
773227569Sphilip		mode_ifm = IFM_ETHER | IFM_AUTO;
774227569Sphilip		ifmedia_add(&sc->media, mode_ifm,
775227569Sphilip			    cap_mask & ~(1 << EFX_PHY_CAP_ASYM), NULL);
776227569Sphilip		best_mode_ifm = mode_ifm;
777227569Sphilip	}
778227569Sphilip
779227569Sphilip	if (best_mode_ifm)
780227569Sphilip		ifmedia_set(&sc->media, best_mode_ifm);
781227569Sphilip
782227569Sphilip	/* Now discard port state until interface is started. */
783227569Sphilip	efx_port_fini(sc->enp);
784227569Sphilipout2:
785227569Sphilip	efx_nic_fini(sc->enp);
786227569Sphilipout:
787227569Sphilip	return rc;
788227569Sphilip}
789