xref: /illumos-gate/usr/src/uts/common/io/sfxge/common/ef10_phy.c (revision 49ef7e0638c8b771d8a136eae78b1c0f99acc8e0)
1 /*
2  * Copyright (c) 2012-2015 Solarflare Communications Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright notice,
11  *    this list of conditions and the following disclaimer in the documentation
12  *    and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * The views and conclusions contained in the software and documentation are
27  * those of the authors and should not be interpreted as representing official
28  * policies, either expressed or implied, of the FreeBSD Project.
29  */
30 
31 #include "efx.h"
32 #include "efx_impl.h"
33 
34 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
35 
36 static			void
37 mcdi_phy_decode_cap(
38 	__in		uint32_t mcdi_cap,
39 	__out		uint32_t *maskp)
40 {
41 	uint32_t mask;
42 
43 	mask = 0;
44 	if (mcdi_cap & (1 << MC_CMD_PHY_CAP_10HDX_LBN))
45 		mask |= (1 << EFX_PHY_CAP_10HDX);
46 	if (mcdi_cap & (1 << MC_CMD_PHY_CAP_10FDX_LBN))
47 		mask |= (1 << EFX_PHY_CAP_10FDX);
48 	if (mcdi_cap & (1 << MC_CMD_PHY_CAP_100HDX_LBN))
49 		mask |= (1 << EFX_PHY_CAP_100HDX);
50 	if (mcdi_cap & (1 << MC_CMD_PHY_CAP_100FDX_LBN))
51 		mask |= (1 << EFX_PHY_CAP_100FDX);
52 	if (mcdi_cap & (1 << MC_CMD_PHY_CAP_1000HDX_LBN))
53 		mask |= (1 << EFX_PHY_CAP_1000HDX);
54 	if (mcdi_cap & (1 << MC_CMD_PHY_CAP_1000FDX_LBN))
55 		mask |= (1 << EFX_PHY_CAP_1000FDX);
56 	if (mcdi_cap & (1 << MC_CMD_PHY_CAP_10000FDX_LBN))
57 		mask |= (1 << EFX_PHY_CAP_10000FDX);
58 	if (mcdi_cap & (1 << MC_CMD_PHY_CAP_40000FDX_LBN))
59 		mask |= (1 << EFX_PHY_CAP_40000FDX);
60 	if (mcdi_cap & (1 << MC_CMD_PHY_CAP_PAUSE_LBN))
61 		mask |= (1 << EFX_PHY_CAP_PAUSE);
62 	if (mcdi_cap & (1 << MC_CMD_PHY_CAP_ASYM_LBN))
63 		mask |= (1 << EFX_PHY_CAP_ASYM);
64 	if (mcdi_cap & (1 << MC_CMD_PHY_CAP_AN_LBN))
65 		mask |= (1 << EFX_PHY_CAP_AN);
66 
67 	*maskp = mask;
68 }
69 
70 static			void
71 mcdi_phy_decode_link_mode(
72 	__in		efx_nic_t *enp,
73 	__in		uint32_t link_flags,
74 	__in		unsigned int speed,
75 	__in		unsigned int fcntl,
76 	__out		efx_link_mode_t *link_modep,
77 	__out		unsigned int *fcntlp)
78 {
79 	boolean_t fd = !!(link_flags &
80 		    (1 << MC_CMD_GET_LINK_OUT_FULL_DUPLEX_LBN));
81 	boolean_t up = !!(link_flags &
82 		    (1 << MC_CMD_GET_LINK_OUT_LINK_UP_LBN));
83 
84 	_NOTE(ARGUNUSED(enp))
85 
86 	if (!up)
87 		*link_modep = EFX_LINK_DOWN;
88 	else if (speed == 40000 && fd)
89 		*link_modep = EFX_LINK_40000FDX;
90 	else if (speed == 10000 && fd)
91 		*link_modep = EFX_LINK_10000FDX;
92 	else if (speed == 1000)
93 		*link_modep = fd ? EFX_LINK_1000FDX : EFX_LINK_1000HDX;
94 	else if (speed == 100)
95 		*link_modep = fd ? EFX_LINK_100FDX : EFX_LINK_100HDX;
96 	else if (speed == 10)
97 		*link_modep = fd ? EFX_LINK_10FDX : EFX_LINK_10HDX;
98 	else
99 		*link_modep = EFX_LINK_UNKNOWN;
100 
101 	if (fcntl == MC_CMD_FCNTL_OFF)
102 		*fcntlp = 0;
103 	else if (fcntl == MC_CMD_FCNTL_RESPOND)
104 		*fcntlp = EFX_FCNTL_RESPOND;
105 	else if (fcntl == MC_CMD_FCNTL_GENERATE)
106 		*fcntlp = EFX_FCNTL_GENERATE;
107 	else if (fcntl == MC_CMD_FCNTL_BIDIR)
108 		*fcntlp = EFX_FCNTL_RESPOND | EFX_FCNTL_GENERATE;
109 	else {
110 		EFSYS_PROBE1(mc_pcol_error, int, fcntl);
111 		*fcntlp = 0;
112 	}
113 }
114 
115 
116 			void
117 ef10_phy_link_ev(
118 	__in		efx_nic_t *enp,
119 	__in		efx_qword_t *eqp,
120 	__out		efx_link_mode_t *link_modep)
121 {
122 	efx_port_t *epp = &(enp->en_port);
123 	unsigned int link_flags;
124 	unsigned int speed;
125 	unsigned int fcntl;
126 	efx_link_mode_t link_mode;
127 	uint32_t lp_cap_mask;
128 
129 	/*
130 	 * Convert the LINKCHANGE speed enumeration into mbit/s, in the
131 	 * same way as GET_LINK encodes the speed
132 	 */
133 	switch (MCDI_EV_FIELD(eqp, LINKCHANGE_SPEED)) {
134 	case MCDI_EVENT_LINKCHANGE_SPEED_100M:
135 		speed = 100;
136 		break;
137 	case MCDI_EVENT_LINKCHANGE_SPEED_1G:
138 		speed = 1000;
139 		break;
140 	case MCDI_EVENT_LINKCHANGE_SPEED_10G:
141 		speed = 10000;
142 		break;
143 	case MCDI_EVENT_LINKCHANGE_SPEED_40G:
144 		speed = 40000;
145 		break;
146 	default:
147 		speed = 0;
148 		break;
149 	}
150 
151 	link_flags = MCDI_EV_FIELD(eqp, LINKCHANGE_LINK_FLAGS);
152 	mcdi_phy_decode_link_mode(enp, link_flags, speed,
153 				    MCDI_EV_FIELD(eqp, LINKCHANGE_FCNTL),
154 				    &link_mode, &fcntl);
155 	mcdi_phy_decode_cap(MCDI_EV_FIELD(eqp, LINKCHANGE_LP_CAP),
156 			    &lp_cap_mask);
157 
158 	/*
159 	 * It's safe to update ep_lp_cap_mask without the driver's port lock
160 	 * because presumably any concurrently running efx_port_poll() is
161 	 * only going to arrive at the same value.
162 	 *
163 	 * ep_fcntl has two meanings. It's either the link common fcntl
164 	 * (if the PHY supports AN), or it's the forced link state. If
165 	 * the former, it's safe to update the value for the same reason as
166 	 * for ep_lp_cap_mask. If the latter, then just ignore the value,
167 	 * because we can race with efx_mac_fcntl_set().
168 	 */
169 	epp->ep_lp_cap_mask = lp_cap_mask;
170 	epp->ep_fcntl = fcntl;
171 
172 	*link_modep = link_mode;
173 }
174 
175 	__checkReturn	efx_rc_t
176 ef10_phy_power(
177 	__in		efx_nic_t *enp,
178 	__in		boolean_t power)
179 {
180 	efx_rc_t rc;
181 
182 	if (!power)
183 		return (0);
184 
185 	/* Check if the PHY is a zombie */
186 	if ((rc = ef10_phy_verify(enp)) != 0)
187 		goto fail1;
188 
189 	enp->en_reset_flags |= EFX_RESET_PHY;
190 
191 	return (0);
192 
193 fail1:
194 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
195 
196 	return (rc);
197 }
198 
199 	__checkReturn	efx_rc_t
200 ef10_phy_get_link(
201 	__in		efx_nic_t *enp,
202 	__out		ef10_link_state_t *elsp)
203 {
204 	efx_mcdi_req_t req;
205 	uint8_t payload[MAX(MC_CMD_GET_LINK_IN_LEN,
206 			    MC_CMD_GET_LINK_OUT_LEN)];
207 	efx_rc_t rc;
208 
209 	(void) memset(payload, 0, sizeof (payload));
210 	req.emr_cmd = MC_CMD_GET_LINK;
211 	req.emr_in_buf = payload;
212 	req.emr_in_length = MC_CMD_GET_LINK_IN_LEN;
213 	req.emr_out_buf = payload;
214 	req.emr_out_length = MC_CMD_GET_LINK_OUT_LEN;
215 
216 	efx_mcdi_execute(enp, &req);
217 
218 	if (req.emr_rc != 0) {
219 		rc = req.emr_rc;
220 		goto fail1;
221 	}
222 
223 	if (req.emr_out_length_used < MC_CMD_GET_LINK_OUT_LEN) {
224 		rc = EMSGSIZE;
225 		goto fail2;
226 	}
227 
228 	mcdi_phy_decode_cap(MCDI_OUT_DWORD(req, GET_LINK_OUT_CAP),
229 			    &elsp->els_adv_cap_mask);
230 	mcdi_phy_decode_cap(MCDI_OUT_DWORD(req, GET_LINK_OUT_LP_CAP),
231 			    &elsp->els_lp_cap_mask);
232 
233 	mcdi_phy_decode_link_mode(enp, MCDI_OUT_DWORD(req, GET_LINK_OUT_FLAGS),
234 			    MCDI_OUT_DWORD(req, GET_LINK_OUT_LINK_SPEED),
235 			    MCDI_OUT_DWORD(req, GET_LINK_OUT_FCNTL),
236 			    &elsp->els_link_mode, &elsp->els_fcntl);
237 
238 #if EFSYS_OPT_LOOPBACK
239 	/* Assert the MC_CMD_LOOPBACK and EFX_LOOPBACK namespace agree */
240 	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_NONE == EFX_LOOPBACK_OFF);
241 	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_DATA == EFX_LOOPBACK_DATA);
242 	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GMAC == EFX_LOOPBACK_GMAC);
243 	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XGMII == EFX_LOOPBACK_XGMII);
244 	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XGXS == EFX_LOOPBACK_XGXS);
245 	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XAUI == EFX_LOOPBACK_XAUI);
246 	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GMII == EFX_LOOPBACK_GMII);
247 	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SGMII == EFX_LOOPBACK_SGMII);
248 	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XGBR == EFX_LOOPBACK_XGBR);
249 	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XFI == EFX_LOOPBACK_XFI);
250 	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XAUI_FAR == EFX_LOOPBACK_XAUI_FAR);
251 	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GMII_FAR == EFX_LOOPBACK_GMII_FAR);
252 	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SGMII_FAR == EFX_LOOPBACK_SGMII_FAR);
253 	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XFI_FAR == EFX_LOOPBACK_XFI_FAR);
254 	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GPHY == EFX_LOOPBACK_GPHY);
255 	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PHYXS == EFX_LOOPBACK_PHY_XS);
256 	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PCS == EFX_LOOPBACK_PCS);
257 	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PMAPMD == EFX_LOOPBACK_PMA_PMD);
258 
259 	elsp->els_loopback = MCDI_OUT_DWORD(req, GET_LINK_OUT_LOOPBACK_MODE);
260 #endif	/* EFSYS_OPT_LOOPBACK */
261 
262 	elsp->els_mac_up = MCDI_OUT_DWORD(req, GET_LINK_OUT_MAC_FAULT) == 0;
263 
264 	return (0);
265 
266 fail2:
267 	EFSYS_PROBE(fail2);
268 fail1:
269 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
270 
271 	return (rc);
272 }
273 
274 	__checkReturn	efx_rc_t
275 ef10_phy_reconfigure(
276 	__in		efx_nic_t *enp)
277 {
278 	efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
279 	efx_port_t *epp = &(enp->en_port);
280 	efx_mcdi_req_t req;
281 	uint8_t payload[MAX(MC_CMD_SET_LINK_IN_LEN,
282 			    MC_CMD_SET_LINK_OUT_LEN)];
283 	uint32_t cap_mask;
284 	unsigned int led_mode;
285 	unsigned int speed;
286 	efx_rc_t rc;
287 
288 	if (~encp->enc_func_flags & EFX_NIC_FUNC_LINKCTRL)
289 		goto out;
290 
291 	(void) memset(payload, 0, sizeof (payload));
292 	req.emr_cmd = MC_CMD_SET_LINK;
293 	req.emr_in_buf = payload;
294 	req.emr_in_length = MC_CMD_SET_LINK_IN_LEN;
295 	req.emr_out_buf = payload;
296 	req.emr_out_length = MC_CMD_SET_LINK_OUT_LEN;
297 
298 	cap_mask = epp->ep_adv_cap_mask;
299 	MCDI_IN_POPULATE_DWORD_10(req, SET_LINK_IN_CAP,
300 		PHY_CAP_10HDX, (cap_mask >> EFX_PHY_CAP_10HDX) & 0x1,
301 		PHY_CAP_10FDX, (cap_mask >> EFX_PHY_CAP_10FDX) & 0x1,
302 		PHY_CAP_100HDX, (cap_mask >> EFX_PHY_CAP_100HDX) & 0x1,
303 		PHY_CAP_100FDX, (cap_mask >> EFX_PHY_CAP_100FDX) & 0x1,
304 		PHY_CAP_1000HDX, (cap_mask >> EFX_PHY_CAP_1000HDX) & 0x1,
305 		PHY_CAP_1000FDX, (cap_mask >> EFX_PHY_CAP_1000FDX) & 0x1,
306 		PHY_CAP_10000FDX, (cap_mask >> EFX_PHY_CAP_10000FDX) & 0x1,
307 		PHY_CAP_PAUSE, (cap_mask >> EFX_PHY_CAP_PAUSE) & 0x1,
308 		PHY_CAP_ASYM, (cap_mask >> EFX_PHY_CAP_ASYM) & 0x1,
309 		PHY_CAP_AN, (cap_mask >> EFX_PHY_CAP_AN) & 0x1);
310 	/* Too many fields for for POPULATE macros, so insert this afterwards */
311 	MCDI_IN_SET_DWORD_FIELD(req, SET_LINK_IN_CAP,
312 	    PHY_CAP_40000FDX, (cap_mask >> EFX_PHY_CAP_40000FDX) & 0x1);
313 
314 #if EFSYS_OPT_LOOPBACK
315 	MCDI_IN_SET_DWORD(req, SET_LINK_IN_LOOPBACK_MODE,
316 		    epp->ep_loopback_type);
317 	switch (epp->ep_loopback_link_mode) {
318 	case EFX_LINK_100FDX:
319 		speed = 100;
320 		break;
321 	case EFX_LINK_1000FDX:
322 		speed = 1000;
323 		break;
324 	case EFX_LINK_10000FDX:
325 		speed = 10000;
326 		break;
327 	case EFX_LINK_40000FDX:
328 		speed = 40000;
329 		break;
330 	default:
331 		speed = 0;
332 	}
333 #else
334 	MCDI_IN_SET_DWORD(req, SET_LINK_IN_LOOPBACK_MODE, MC_CMD_LOOPBACK_NONE);
335 	speed = 0;
336 #endif	/* EFSYS_OPT_LOOPBACK */
337 	MCDI_IN_SET_DWORD(req, SET_LINK_IN_LOOPBACK_SPEED, speed);
338 
339 #if EFSYS_OPT_PHY_FLAGS
340 	MCDI_IN_SET_DWORD(req, SET_LINK_IN_FLAGS, epp->ep_phy_flags);
341 #else
342 	MCDI_IN_SET_DWORD(req, SET_LINK_IN_FLAGS, 0);
343 #endif	/* EFSYS_OPT_PHY_FLAGS */
344 
345 	efx_mcdi_execute(enp, &req);
346 
347 	if (req.emr_rc != 0) {
348 		rc = req.emr_rc;
349 		goto fail1;
350 	}
351 
352 	/* And set the blink mode */
353 	(void) memset(payload, 0, sizeof (payload));
354 	req.emr_cmd = MC_CMD_SET_ID_LED;
355 	req.emr_in_buf = payload;
356 	req.emr_in_length = MC_CMD_SET_ID_LED_IN_LEN;
357 	req.emr_out_buf = payload;
358 	req.emr_out_length = MC_CMD_SET_ID_LED_OUT_LEN;
359 
360 #if EFSYS_OPT_PHY_LED_CONTROL
361 	switch (epp->ep_phy_led_mode) {
362 	case EFX_PHY_LED_DEFAULT:
363 		led_mode = MC_CMD_LED_DEFAULT;
364 		break;
365 	case EFX_PHY_LED_OFF:
366 		led_mode = MC_CMD_LED_OFF;
367 		break;
368 	case EFX_PHY_LED_ON:
369 		led_mode = MC_CMD_LED_ON;
370 		break;
371 	default:
372 		EFSYS_ASSERT(0);
373 		led_mode = MC_CMD_LED_DEFAULT;
374 	}
375 
376 	MCDI_IN_SET_DWORD(req, SET_ID_LED_IN_STATE, led_mode);
377 #else
378 	MCDI_IN_SET_DWORD(req, SET_ID_LED_IN_STATE, MC_CMD_LED_DEFAULT);
379 #endif	/* EFSYS_OPT_PHY_LED_CONTROL */
380 
381 	efx_mcdi_execute(enp, &req);
382 
383 	if (req.emr_rc != 0) {
384 		rc = req.emr_rc;
385 		goto fail2;
386 	}
387 out:
388 	return (0);
389 
390 fail2:
391 	EFSYS_PROBE(fail2);
392 fail1:
393 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
394 
395 	return (rc);
396 }
397 
398 	__checkReturn	efx_rc_t
399 ef10_phy_verify(
400 	__in		efx_nic_t *enp)
401 {
402 	efx_mcdi_req_t req;
403 	uint8_t payload[MAX(MC_CMD_GET_PHY_STATE_IN_LEN,
404 			    MC_CMD_GET_PHY_STATE_OUT_LEN)];
405 	uint32_t state;
406 	efx_rc_t rc;
407 
408 	(void) memset(payload, 0, sizeof (payload));
409 	req.emr_cmd = MC_CMD_GET_PHY_STATE;
410 	req.emr_in_buf = payload;
411 	req.emr_in_length = MC_CMD_GET_PHY_STATE_IN_LEN;
412 	req.emr_out_buf = payload;
413 	req.emr_out_length = MC_CMD_GET_PHY_STATE_OUT_LEN;
414 
415 	efx_mcdi_execute(enp, &req);
416 
417 	if (req.emr_rc != 0) {
418 		rc = req.emr_rc;
419 		goto fail1;
420 	}
421 
422 	if (req.emr_out_length_used < MC_CMD_GET_PHY_STATE_OUT_LEN) {
423 		rc = EMSGSIZE;
424 		goto fail2;
425 	}
426 
427 	state = MCDI_OUT_DWORD(req, GET_PHY_STATE_OUT_STATE);
428 	if (state != MC_CMD_PHY_STATE_OK) {
429 		if (state != MC_CMD_PHY_STATE_ZOMBIE)
430 			EFSYS_PROBE1(mc_pcol_error, int, state);
431 		rc = ENOTACTIVE;
432 		goto fail3;
433 	}
434 
435 	return (0);
436 
437 fail3:
438 	EFSYS_PROBE(fail3);
439 fail2:
440 	EFSYS_PROBE(fail2);
441 fail1:
442 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
443 
444 	return (rc);
445 }
446 
447 	__checkReturn	efx_rc_t
448 ef10_phy_oui_get(
449 	__in		efx_nic_t *enp,
450 	__out		uint32_t *ouip)
451 {
452 	_NOTE(ARGUNUSED(enp, ouip))
453 
454 	return (ENOTSUP);
455 }
456 
457 #if EFSYS_OPT_PHY_STATS
458 
459 	__checkReturn				efx_rc_t
460 ef10_phy_stats_update(
461 	__in					efx_nic_t *enp,
462 	__in					efsys_mem_t *esmp,
463 	__inout_ecount(EFX_PHY_NSTATS)		uint32_t *stat)
464 {
465 	/* TBD: no stats support in firmware yet */
466 	_NOTE(ARGUNUSED(enp, esmp))
467 	(void) memset(stat, 0, EFX_PHY_NSTATS * sizeof (*stat));
468 
469 	return (0);
470 }
471 
472 #endif	/* EFSYS_OPT_PHY_STATS */
473 
474 #endif	/* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */
475