xref: /freebsd/sys/dev/sfxge/common/efx_mcdi.c (revision 58983e4b0253ad38a3e1ef2166fedd3133fdb552)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2008-2016 Solarflare Communications Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice,
11  *    this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright notice,
13  *    this list of conditions and the following disclaimer in the documentation
14  *    and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * The views and conclusions contained in the software and documentation are
29  * those of the authors and should not be interpreted as representing official
30  * policies, either expressed or implied, of the FreeBSD Project.
31  */
32 
33 #include <sys/cdefs.h>
34 #include "efx.h"
35 #include "efx_impl.h"
36 
37 #if EFSYS_OPT_MCDI
38 
39 /*
40  * There are three versions of the MCDI interface:
41  *  - MCDIv0: Siena BootROM. Transport uses MCDIv1 headers.
42  *  - MCDIv1: Siena firmware and Huntington BootROM.
43  *  - MCDIv2: EF10 firmware (Huntington/Medford) and Medford BootROM.
44  *            Transport uses MCDIv2 headers.
45  *
46  * MCDIv2 Header NOT_EPOCH flag
47  * ----------------------------
48  * A new epoch begins at initial startup or after an MC reboot, and defines when
49  * the MC should reject stale MCDI requests.
50  *
51  * The first MCDI request sent by the host should contain NOT_EPOCH=0, and all
52  * subsequent requests (until the next MC reboot) should contain NOT_EPOCH=1.
53  *
54  * After rebooting the MC will fail all requests with NOT_EPOCH=1 by writing a
55  * response with ERROR=1 and DATALEN=0 until a request is seen with NOT_EPOCH=0.
56  */
57 
58 #if EFSYS_OPT_SIENA
59 
60 static const efx_mcdi_ops_t	__efx_mcdi_siena_ops = {
61 	siena_mcdi_init,		/* emco_init */
62 	siena_mcdi_send_request,	/* emco_send_request */
63 	siena_mcdi_poll_reboot,		/* emco_poll_reboot */
64 	siena_mcdi_poll_response,	/* emco_poll_response */
65 	siena_mcdi_read_response,	/* emco_read_response */
66 	siena_mcdi_fini,		/* emco_fini */
67 	siena_mcdi_feature_supported,	/* emco_feature_supported */
68 	siena_mcdi_get_timeout,		/* emco_get_timeout */
69 };
70 
71 #endif	/* EFSYS_OPT_SIENA */
72 
73 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
74 
75 static const efx_mcdi_ops_t	__efx_mcdi_ef10_ops = {
76 	ef10_mcdi_init,			/* emco_init */
77 	ef10_mcdi_send_request,		/* emco_send_request */
78 	ef10_mcdi_poll_reboot,		/* emco_poll_reboot */
79 	ef10_mcdi_poll_response,	/* emco_poll_response */
80 	ef10_mcdi_read_response,	/* emco_read_response */
81 	ef10_mcdi_fini,			/* emco_fini */
82 	ef10_mcdi_feature_supported,	/* emco_feature_supported */
83 	ef10_mcdi_get_timeout,		/* emco_get_timeout */
84 };
85 
86 #endif	/* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
87 
88 	__checkReturn	efx_rc_t
89 efx_mcdi_init(
90 	__in		efx_nic_t *enp,
91 	__in		const efx_mcdi_transport_t *emtp)
92 {
93 	const efx_mcdi_ops_t *emcop;
94 	efx_rc_t rc;
95 
96 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
97 	EFSYS_ASSERT3U(enp->en_mod_flags, ==, 0);
98 
99 	switch (enp->en_family) {
100 #if EFSYS_OPT_SIENA
101 	case EFX_FAMILY_SIENA:
102 		emcop = &__efx_mcdi_siena_ops;
103 		break;
104 #endif	/* EFSYS_OPT_SIENA */
105 
106 #if EFSYS_OPT_HUNTINGTON
107 	case EFX_FAMILY_HUNTINGTON:
108 		emcop = &__efx_mcdi_ef10_ops;
109 		break;
110 #endif	/* EFSYS_OPT_HUNTINGTON */
111 
112 #if EFSYS_OPT_MEDFORD
113 	case EFX_FAMILY_MEDFORD:
114 		emcop = &__efx_mcdi_ef10_ops;
115 		break;
116 #endif	/* EFSYS_OPT_MEDFORD */
117 
118 #if EFSYS_OPT_MEDFORD2
119 	case EFX_FAMILY_MEDFORD2:
120 		emcop = &__efx_mcdi_ef10_ops;
121 		break;
122 #endif	/* EFSYS_OPT_MEDFORD2 */
123 
124 	default:
125 		EFSYS_ASSERT(0);
126 		rc = ENOTSUP;
127 		goto fail1;
128 	}
129 
130 	if (enp->en_features & EFX_FEATURE_MCDI_DMA) {
131 		/* MCDI requires a DMA buffer in host memory */
132 		if ((emtp == NULL) || (emtp->emt_dma_mem) == NULL) {
133 			rc = EINVAL;
134 			goto fail2;
135 		}
136 	}
137 	enp->en_mcdi.em_emtp = emtp;
138 
139 	if (emcop != NULL && emcop->emco_init != NULL) {
140 		if ((rc = emcop->emco_init(enp, emtp)) != 0)
141 			goto fail3;
142 	}
143 
144 	enp->en_mcdi.em_emcop = emcop;
145 	enp->en_mod_flags |= EFX_MOD_MCDI;
146 
147 	return (0);
148 
149 fail3:
150 	EFSYS_PROBE(fail3);
151 fail2:
152 	EFSYS_PROBE(fail2);
153 fail1:
154 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
155 
156 	enp->en_mcdi.em_emcop = NULL;
157 	enp->en_mcdi.em_emtp = NULL;
158 	enp->en_mod_flags &= ~EFX_MOD_MCDI;
159 
160 	return (rc);
161 }
162 
163 			void
164 efx_mcdi_fini(
165 	__in		efx_nic_t *enp)
166 {
167 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
168 	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
169 
170 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
171 	EFSYS_ASSERT3U(enp->en_mod_flags, ==, EFX_MOD_MCDI);
172 
173 	if (emcop != NULL && emcop->emco_fini != NULL)
174 		emcop->emco_fini(enp);
175 
176 	emip->emi_port = 0;
177 	emip->emi_aborted = 0;
178 
179 	enp->en_mcdi.em_emcop = NULL;
180 	enp->en_mod_flags &= ~EFX_MOD_MCDI;
181 }
182 
183 			void
184 efx_mcdi_new_epoch(
185 	__in		efx_nic_t *enp)
186 {
187 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
188 	efsys_lock_state_t state;
189 
190 	/* Start a new epoch (allow fresh MCDI requests to succeed) */
191 	EFSYS_LOCK(enp->en_eslp, state);
192 	emip->emi_new_epoch = B_TRUE;
193 	EFSYS_UNLOCK(enp->en_eslp, state);
194 }
195 
196 static			void
197 efx_mcdi_send_request(
198 	__in		efx_nic_t *enp,
199 	__in		void *hdrp,
200 	__in		size_t hdr_len,
201 	__in		void *sdup,
202 	__in		size_t sdu_len)
203 {
204 	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
205 
206 	emcop->emco_send_request(enp, hdrp, hdr_len, sdup, sdu_len);
207 }
208 
209 static			efx_rc_t
210 efx_mcdi_poll_reboot(
211 	__in		efx_nic_t *enp)
212 {
213 	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
214 	efx_rc_t rc;
215 
216 	rc = emcop->emco_poll_reboot(enp);
217 	return (rc);
218 }
219 
220 static			boolean_t
221 efx_mcdi_poll_response(
222 	__in		efx_nic_t *enp)
223 {
224 	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
225 	boolean_t available;
226 
227 	available = emcop->emco_poll_response(enp);
228 	return (available);
229 }
230 
231 static			void
232 efx_mcdi_read_response(
233 	__in		efx_nic_t *enp,
234 	__out		void *bufferp,
235 	__in		size_t offset,
236 	__in		size_t length)
237 {
238 	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
239 
240 	emcop->emco_read_response(enp, bufferp, offset, length);
241 }
242 
243 			void
244 efx_mcdi_request_start(
245 	__in		efx_nic_t *enp,
246 	__in		efx_mcdi_req_t *emrp,
247 	__in		boolean_t ev_cpl)
248 {
249 #if EFSYS_OPT_MCDI_LOGGING
250 	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
251 #endif
252 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
253 	efx_dword_t hdr[2];
254 	size_t hdr_len;
255 	unsigned int max_version;
256 	unsigned int seq;
257 	unsigned int xflags;
258 	boolean_t new_epoch;
259 	efsys_lock_state_t state;
260 
261 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
262 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
263 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
264 
265 	/*
266 	 * efx_mcdi_request_start() is naturally serialised against both
267 	 * efx_mcdi_request_poll() and efx_mcdi_ev_cpl()/efx_mcdi_ev_death(),
268 	 * by virtue of there only being one outstanding MCDI request.
269 	 * Unfortunately, upper layers may also call efx_mcdi_request_abort()
270 	 * at any time, to timeout a pending mcdi request, That request may
271 	 * then subsequently complete, meaning efx_mcdi_ev_cpl() or
272 	 * efx_mcdi_ev_death() may end up running in parallel with
273 	 * efx_mcdi_request_start(). This race is handled by ensuring that
274 	 * %emi_pending_req, %emi_ev_cpl and %emi_seq are protected by the
275 	 * en_eslp lock.
276 	 */
277 	EFSYS_LOCK(enp->en_eslp, state);
278 	EFSYS_ASSERT(emip->emi_pending_req == NULL);
279 	emip->emi_pending_req = emrp;
280 	emip->emi_ev_cpl = ev_cpl;
281 	emip->emi_poll_cnt = 0;
282 	seq = emip->emi_seq++ & EFX_MASK32(MCDI_HEADER_SEQ);
283 	new_epoch = emip->emi_new_epoch;
284 	max_version = emip->emi_max_version;
285 	EFSYS_UNLOCK(enp->en_eslp, state);
286 
287 	xflags = 0;
288 	if (ev_cpl)
289 		xflags |= MCDI_HEADER_XFLAGS_EVREQ;
290 
291 	/*
292 	 * Huntington firmware supports MCDIv2, but the Huntington BootROM only
293 	 * supports MCDIv1. Use MCDIv1 headers for MCDIv1 commands where
294 	 * possible to support this.
295 	 */
296 	if ((max_version >= 2) &&
297 	    ((emrp->emr_cmd > MC_CMD_CMD_SPACE_ESCAPE_7) ||
298 	    (emrp->emr_in_length > MCDI_CTL_SDU_LEN_MAX_V1) ||
299 	    (emrp->emr_out_length > MCDI_CTL_SDU_LEN_MAX_V1))) {
300 		/* Construct MCDI v2 header */
301 		hdr_len = sizeof (hdr);
302 		EFX_POPULATE_DWORD_8(hdr[0],
303 		    MCDI_HEADER_CODE, MC_CMD_V2_EXTN,
304 		    MCDI_HEADER_RESYNC, 1,
305 		    MCDI_HEADER_DATALEN, 0,
306 		    MCDI_HEADER_SEQ, seq,
307 		    MCDI_HEADER_NOT_EPOCH, new_epoch ? 0 : 1,
308 		    MCDI_HEADER_ERROR, 0,
309 		    MCDI_HEADER_RESPONSE, 0,
310 		    MCDI_HEADER_XFLAGS, xflags);
311 
312 		EFX_POPULATE_DWORD_2(hdr[1],
313 		    MC_CMD_V2_EXTN_IN_EXTENDED_CMD, emrp->emr_cmd,
314 		    MC_CMD_V2_EXTN_IN_ACTUAL_LEN, emrp->emr_in_length);
315 	} else {
316 		/* Construct MCDI v1 header */
317 		hdr_len = sizeof (hdr[0]);
318 		EFX_POPULATE_DWORD_8(hdr[0],
319 		    MCDI_HEADER_CODE, emrp->emr_cmd,
320 		    MCDI_HEADER_RESYNC, 1,
321 		    MCDI_HEADER_DATALEN, emrp->emr_in_length,
322 		    MCDI_HEADER_SEQ, seq,
323 		    MCDI_HEADER_NOT_EPOCH, new_epoch ? 0 : 1,
324 		    MCDI_HEADER_ERROR, 0,
325 		    MCDI_HEADER_RESPONSE, 0,
326 		    MCDI_HEADER_XFLAGS, xflags);
327 	}
328 
329 #if EFSYS_OPT_MCDI_LOGGING
330 	if (emtp->emt_logger != NULL) {
331 		emtp->emt_logger(emtp->emt_context, EFX_LOG_MCDI_REQUEST,
332 		    &hdr, hdr_len,
333 		    emrp->emr_in_buf, emrp->emr_in_length);
334 	}
335 #endif /* EFSYS_OPT_MCDI_LOGGING */
336 
337 	efx_mcdi_send_request(enp, &hdr[0], hdr_len,
338 	    emrp->emr_in_buf, emrp->emr_in_length);
339 }
340 
341 static			void
342 efx_mcdi_read_response_header(
343 	__in		efx_nic_t *enp,
344 	__inout		efx_mcdi_req_t *emrp)
345 {
346 #if EFSYS_OPT_MCDI_LOGGING
347 	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
348 #endif /* EFSYS_OPT_MCDI_LOGGING */
349 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
350 	efx_dword_t hdr[2];
351 	unsigned int hdr_len;
352 	unsigned int data_len;
353 	unsigned int seq;
354 	unsigned int cmd;
355 	unsigned int error;
356 	efx_rc_t rc;
357 
358 	EFSYS_ASSERT(emrp != NULL);
359 
360 	efx_mcdi_read_response(enp, &hdr[0], 0, sizeof (hdr[0]));
361 	hdr_len = sizeof (hdr[0]);
362 
363 	cmd = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_CODE);
364 	seq = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_SEQ);
365 	error = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_ERROR);
366 
367 	if (cmd != MC_CMD_V2_EXTN) {
368 		data_len = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_DATALEN);
369 	} else {
370 		efx_mcdi_read_response(enp, &hdr[1], hdr_len, sizeof (hdr[1]));
371 		hdr_len += sizeof (hdr[1]);
372 
373 		cmd = EFX_DWORD_FIELD(hdr[1], MC_CMD_V2_EXTN_IN_EXTENDED_CMD);
374 		data_len =
375 		    EFX_DWORD_FIELD(hdr[1], MC_CMD_V2_EXTN_IN_ACTUAL_LEN);
376 	}
377 
378 	if (error && (data_len == 0)) {
379 		/* The MC has rebooted since the request was sent. */
380 		EFSYS_SPIN(EFX_MCDI_STATUS_SLEEP_US);
381 		efx_mcdi_poll_reboot(enp);
382 		rc = EIO;
383 		goto fail1;
384 	}
385 	if ((cmd != emrp->emr_cmd) ||
386 	    (seq != ((emip->emi_seq - 1) & EFX_MASK32(MCDI_HEADER_SEQ)))) {
387 		/* Response is for a different request */
388 		rc = EIO;
389 		goto fail2;
390 	}
391 	if (error) {
392 		efx_dword_t err[2];
393 		unsigned int err_len = MIN(data_len, sizeof (err));
394 		int err_code = MC_CMD_ERR_EPROTO;
395 		int err_arg = 0;
396 
397 		/* Read error code (and arg num for MCDI v2 commands) */
398 		efx_mcdi_read_response(enp, &err, hdr_len, err_len);
399 
400 		if (err_len >= (MC_CMD_ERR_CODE_OFST + sizeof (efx_dword_t)))
401 			err_code = EFX_DWORD_FIELD(err[0], EFX_DWORD_0);
402 #ifdef WITH_MCDI_V2
403 		if (err_len >= (MC_CMD_ERR_ARG_OFST + sizeof (efx_dword_t)))
404 			err_arg = EFX_DWORD_FIELD(err[1], EFX_DWORD_0);
405 #endif
406 		emrp->emr_err_code = err_code;
407 		emrp->emr_err_arg = err_arg;
408 
409 #if EFSYS_OPT_MCDI_PROXY_AUTH
410 		if ((err_code == MC_CMD_ERR_PROXY_PENDING) &&
411 		    (err_len == sizeof (err))) {
412 			/*
413 			 * The MCDI request would normally fail with EPERM, but
414 			 * firmware has forwarded it to an authorization agent
415 			 * attached to a privileged PF.
416 			 *
417 			 * Save the authorization request handle. The client
418 			 * must wait for a PROXY_RESPONSE event, or timeout.
419 			 */
420 			emrp->emr_proxy_handle = err_arg;
421 		}
422 #endif /* EFSYS_OPT_MCDI_PROXY_AUTH */
423 
424 #if EFSYS_OPT_MCDI_LOGGING
425 		if (emtp->emt_logger != NULL) {
426 			emtp->emt_logger(emtp->emt_context,
427 			    EFX_LOG_MCDI_RESPONSE,
428 			    &hdr, hdr_len,
429 			    &err, err_len);
430 		}
431 #endif /* EFSYS_OPT_MCDI_LOGGING */
432 
433 		if (!emrp->emr_quiet) {
434 			EFSYS_PROBE3(mcdi_err_arg, int, emrp->emr_cmd,
435 			    int, err_code, int, err_arg);
436 		}
437 
438 		rc = efx_mcdi_request_errcode(err_code);
439 		goto fail3;
440 	}
441 
442 	emrp->emr_rc = 0;
443 	emrp->emr_out_length_used = data_len;
444 #if EFSYS_OPT_MCDI_PROXY_AUTH
445 	emrp->emr_proxy_handle = 0;
446 #endif /* EFSYS_OPT_MCDI_PROXY_AUTH */
447 	return;
448 
449 fail3:
450 fail2:
451 fail1:
452 	emrp->emr_rc = rc;
453 	emrp->emr_out_length_used = 0;
454 }
455 
456 static			void
457 efx_mcdi_finish_response(
458 	__in		efx_nic_t *enp,
459 	__in		efx_mcdi_req_t *emrp)
460 {
461 #if EFSYS_OPT_MCDI_LOGGING
462 	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
463 #endif /* EFSYS_OPT_MCDI_LOGGING */
464 	efx_dword_t hdr[2];
465 	unsigned int hdr_len;
466 	size_t bytes;
467 
468 	if (emrp->emr_out_buf == NULL)
469 		return;
470 
471 	/* Read the command header to detect MCDI response format */
472 	hdr_len = sizeof (hdr[0]);
473 	efx_mcdi_read_response(enp, &hdr[0], 0, hdr_len);
474 	if (EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_CODE) == MC_CMD_V2_EXTN) {
475 		/*
476 		 * Read the actual payload length. The length given in the event
477 		 * is only correct for responses with the V1 format.
478 		 */
479 		efx_mcdi_read_response(enp, &hdr[1], hdr_len, sizeof (hdr[1]));
480 		hdr_len += sizeof (hdr[1]);
481 
482 		emrp->emr_out_length_used = EFX_DWORD_FIELD(hdr[1],
483 					    MC_CMD_V2_EXTN_IN_ACTUAL_LEN);
484 	}
485 
486 	/* Copy payload out into caller supplied buffer */
487 	bytes = MIN(emrp->emr_out_length_used, emrp->emr_out_length);
488 	efx_mcdi_read_response(enp, emrp->emr_out_buf, hdr_len, bytes);
489 
490 #if EFSYS_OPT_MCDI_LOGGING
491 	if (emtp->emt_logger != NULL) {
492 		emtp->emt_logger(emtp->emt_context,
493 		    EFX_LOG_MCDI_RESPONSE,
494 		    &hdr, hdr_len,
495 		    emrp->emr_out_buf, bytes);
496 	}
497 #endif /* EFSYS_OPT_MCDI_LOGGING */
498 }
499 
500 	__checkReturn	boolean_t
501 efx_mcdi_request_poll(
502 	__in		efx_nic_t *enp)
503 {
504 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
505 	efx_mcdi_req_t *emrp;
506 	efsys_lock_state_t state;
507 	efx_rc_t rc;
508 
509 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
510 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
511 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
512 
513 	/* Serialise against post-watchdog efx_mcdi_ev* */
514 	EFSYS_LOCK(enp->en_eslp, state);
515 
516 	EFSYS_ASSERT(emip->emi_pending_req != NULL);
517 	EFSYS_ASSERT(!emip->emi_ev_cpl);
518 	emrp = emip->emi_pending_req;
519 
520 	/* Check if hardware is unavailable */
521 	if (efx_nic_hw_unavailable(enp)) {
522 		EFSYS_UNLOCK(enp->en_eslp, state);
523 		return (B_FALSE);
524 	}
525 
526 	/* Check for reboot atomically w.r.t efx_mcdi_request_start */
527 	if (emip->emi_poll_cnt++ == 0) {
528 		if ((rc = efx_mcdi_poll_reboot(enp)) != 0) {
529 			emip->emi_pending_req = NULL;
530 			EFSYS_UNLOCK(enp->en_eslp, state);
531 
532 			/* Reboot/Assertion */
533 			if (rc == EIO || rc == EINTR)
534 				efx_mcdi_raise_exception(enp, emrp, rc);
535 
536 			goto fail1;
537 		}
538 	}
539 
540 	/* Check if a response is available */
541 	if (efx_mcdi_poll_response(enp) == B_FALSE) {
542 		EFSYS_UNLOCK(enp->en_eslp, state);
543 		return (B_FALSE);
544 	}
545 
546 	/* Read the response header */
547 	efx_mcdi_read_response_header(enp, emrp);
548 
549 	/* Request complete */
550 	emip->emi_pending_req = NULL;
551 
552 	/* Ensure stale MCDI requests fail after an MC reboot. */
553 	emip->emi_new_epoch = B_FALSE;
554 
555 	EFSYS_UNLOCK(enp->en_eslp, state);
556 
557 	if ((rc = emrp->emr_rc) != 0)
558 		goto fail2;
559 
560 	efx_mcdi_finish_response(enp, emrp);
561 	return (B_TRUE);
562 
563 fail2:
564 	if (!emrp->emr_quiet)
565 		EFSYS_PROBE(fail2);
566 fail1:
567 	if (!emrp->emr_quiet)
568 		EFSYS_PROBE1(fail1, efx_rc_t, rc);
569 
570 	return (B_TRUE);
571 }
572 
573 	__checkReturn	boolean_t
574 efx_mcdi_request_abort(
575 	__in		efx_nic_t *enp)
576 {
577 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
578 	efx_mcdi_req_t *emrp;
579 	boolean_t aborted;
580 	efsys_lock_state_t state;
581 
582 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
583 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
584 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
585 
586 	/*
587 	 * efx_mcdi_ev_* may have already completed this event, and be
588 	 * spinning/blocked on the upper layer lock. So it *is* legitimate
589 	 * to for emi_pending_req to be NULL. If there is a pending event
590 	 * completed request, then provide a "credit" to allow
591 	 * efx_mcdi_ev_cpl() to accept a single spurious completion.
592 	 */
593 	EFSYS_LOCK(enp->en_eslp, state);
594 	emrp = emip->emi_pending_req;
595 	aborted = (emrp != NULL);
596 	if (aborted) {
597 		emip->emi_pending_req = NULL;
598 
599 		/* Error the request */
600 		emrp->emr_out_length_used = 0;
601 		emrp->emr_rc = ETIMEDOUT;
602 
603 		/* Provide a credit for seqno/emr_pending_req mismatches */
604 		if (emip->emi_ev_cpl)
605 			++emip->emi_aborted;
606 
607 		/*
608 		 * The upper layer has called us, so we don't
609 		 * need to complete the request.
610 		 */
611 	}
612 	EFSYS_UNLOCK(enp->en_eslp, state);
613 
614 	return (aborted);
615 }
616 
617 			void
618 efx_mcdi_get_timeout(
619 	__in		efx_nic_t *enp,
620 	__in		efx_mcdi_req_t *emrp,
621 	__out		uint32_t *timeoutp)
622 {
623 	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
624 
625 	emcop->emco_get_timeout(enp, emrp, timeoutp);
626 }
627 
628 	__checkReturn	efx_rc_t
629 efx_mcdi_request_errcode(
630 	__in		unsigned int err)
631 {
632 
633 	switch (err) {
634 		/* MCDI v1 */
635 	case MC_CMD_ERR_EPERM:
636 		return (EACCES);
637 	case MC_CMD_ERR_ENOENT:
638 		return (ENOENT);
639 	case MC_CMD_ERR_EINTR:
640 		return (EINTR);
641 	case MC_CMD_ERR_EACCES:
642 		return (EACCES);
643 	case MC_CMD_ERR_EBUSY:
644 		return (EBUSY);
645 	case MC_CMD_ERR_EINVAL:
646 		return (EINVAL);
647 	case MC_CMD_ERR_EDEADLK:
648 		return (EDEADLK);
649 	case MC_CMD_ERR_ENOSYS:
650 		return (ENOTSUP);
651 	case MC_CMD_ERR_ETIME:
652 		return (ETIMEDOUT);
653 	case MC_CMD_ERR_ENOTSUP:
654 		return (ENOTSUP);
655 	case MC_CMD_ERR_EALREADY:
656 		return (EALREADY);
657 
658 		/* MCDI v2 */
659 	case MC_CMD_ERR_EEXIST:
660 		return (EEXIST);
661 #ifdef MC_CMD_ERR_EAGAIN
662 	case MC_CMD_ERR_EAGAIN:
663 		return (EAGAIN);
664 #endif
665 #ifdef MC_CMD_ERR_ENOSPC
666 	case MC_CMD_ERR_ENOSPC:
667 		return (ENOSPC);
668 #endif
669 	case MC_CMD_ERR_ERANGE:
670 		return (ERANGE);
671 
672 	case MC_CMD_ERR_ALLOC_FAIL:
673 		return (ENOMEM);
674 	case MC_CMD_ERR_NO_VADAPTOR:
675 		return (ENOENT);
676 	case MC_CMD_ERR_NO_EVB_PORT:
677 		return (ENOENT);
678 	case MC_CMD_ERR_NO_VSWITCH:
679 		return (ENODEV);
680 	case MC_CMD_ERR_VLAN_LIMIT:
681 		return (EINVAL);
682 	case MC_CMD_ERR_BAD_PCI_FUNC:
683 		return (ENODEV);
684 	case MC_CMD_ERR_BAD_VLAN_MODE:
685 		return (EINVAL);
686 	case MC_CMD_ERR_BAD_VSWITCH_TYPE:
687 		return (EINVAL);
688 	case MC_CMD_ERR_BAD_VPORT_TYPE:
689 		return (EINVAL);
690 	case MC_CMD_ERR_MAC_EXIST:
691 		return (EEXIST);
692 
693 	case MC_CMD_ERR_PROXY_PENDING:
694 		return (EAGAIN);
695 
696 	default:
697 		EFSYS_PROBE1(mc_pcol_error, int, err);
698 		return (EIO);
699 	}
700 }
701 
702 			void
703 efx_mcdi_raise_exception(
704 	__in		efx_nic_t *enp,
705 	__in_opt	efx_mcdi_req_t *emrp,
706 	__in		int rc)
707 {
708 	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
709 	efx_mcdi_exception_t exception;
710 
711 	/* Reboot or Assertion failure only */
712 	EFSYS_ASSERT(rc == EIO || rc == EINTR);
713 
714 	/*
715 	 * If MC_CMD_REBOOT causes a reboot (dependent on parameters),
716 	 * then the EIO is not worthy of an exception.
717 	 */
718 	if (emrp != NULL && emrp->emr_cmd == MC_CMD_REBOOT && rc == EIO)
719 		return;
720 
721 	exception = (rc == EIO)
722 		? EFX_MCDI_EXCEPTION_MC_REBOOT
723 		: EFX_MCDI_EXCEPTION_MC_BADASSERT;
724 
725 	emtp->emt_exception(emtp->emt_context, exception);
726 }
727 
728 			void
729 efx_mcdi_execute(
730 	__in		efx_nic_t *enp,
731 	__inout		efx_mcdi_req_t *emrp)
732 {
733 	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
734 
735 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
736 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
737 
738 	emrp->emr_quiet = B_FALSE;
739 	emtp->emt_execute(emtp->emt_context, emrp);
740 }
741 
742 			void
743 efx_mcdi_execute_quiet(
744 	__in		efx_nic_t *enp,
745 	__inout		efx_mcdi_req_t *emrp)
746 {
747 	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
748 
749 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
750 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
751 
752 	emrp->emr_quiet = B_TRUE;
753 	emtp->emt_execute(emtp->emt_context, emrp);
754 }
755 
756 			void
757 efx_mcdi_ev_cpl(
758 	__in		efx_nic_t *enp,
759 	__in		unsigned int seq,
760 	__in		unsigned int outlen,
761 	__in		int errcode)
762 {
763 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
764 	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
765 	efx_mcdi_req_t *emrp;
766 	efsys_lock_state_t state;
767 
768 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
769 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
770 
771 	/*
772 	 * Serialise against efx_mcdi_request_poll()/efx_mcdi_request_start()
773 	 * when we're completing an aborted request.
774 	 */
775 	EFSYS_LOCK(enp->en_eslp, state);
776 	if (emip->emi_pending_req == NULL || !emip->emi_ev_cpl ||
777 	    (seq != ((emip->emi_seq - 1) & EFX_MASK32(MCDI_HEADER_SEQ)))) {
778 		EFSYS_ASSERT(emip->emi_aborted > 0);
779 		if (emip->emi_aborted > 0)
780 			--emip->emi_aborted;
781 		EFSYS_UNLOCK(enp->en_eslp, state);
782 		return;
783 	}
784 
785 	emrp = emip->emi_pending_req;
786 	emip->emi_pending_req = NULL;
787 	EFSYS_UNLOCK(enp->en_eslp, state);
788 
789 	if (emip->emi_max_version >= 2) {
790 		/* MCDIv2 response details do not fit into an event. */
791 		efx_mcdi_read_response_header(enp, emrp);
792 	} else {
793 		if (errcode != 0) {
794 			if (!emrp->emr_quiet) {
795 				EFSYS_PROBE2(mcdi_err, int, emrp->emr_cmd,
796 				    int, errcode);
797 			}
798 			emrp->emr_out_length_used = 0;
799 			emrp->emr_rc = efx_mcdi_request_errcode(errcode);
800 		} else {
801 			emrp->emr_out_length_used = outlen;
802 			emrp->emr_rc = 0;
803 		}
804 	}
805 	if (emrp->emr_rc == 0)
806 		efx_mcdi_finish_response(enp, emrp);
807 
808 	emtp->emt_ev_cpl(emtp->emt_context);
809 }
810 
811 #if EFSYS_OPT_MCDI_PROXY_AUTH
812 
813 	__checkReturn	efx_rc_t
814 efx_mcdi_get_proxy_handle(
815 	__in		efx_nic_t *enp,
816 	__in		efx_mcdi_req_t *emrp,
817 	__out		uint32_t *handlep)
818 {
819 	efx_rc_t rc;
820 
821 	_NOTE(ARGUNUSED(enp))
822 
823 	/*
824 	 * Return proxy handle from MCDI request that returned with error
825 	 * MC_MCD_ERR_PROXY_PENDING. This handle is used to wait for a matching
826 	 * PROXY_RESPONSE event.
827 	 */
828 	if ((emrp == NULL) || (handlep == NULL)) {
829 		rc = EINVAL;
830 		goto fail1;
831 	}
832 	if ((emrp->emr_rc != 0) &&
833 	    (emrp->emr_err_code == MC_CMD_ERR_PROXY_PENDING)) {
834 		*handlep = emrp->emr_proxy_handle;
835 		rc = 0;
836 	} else {
837 		*handlep = 0;
838 		rc = ENOENT;
839 	}
840 	return (rc);
841 
842 fail1:
843 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
844 	return (rc);
845 }
846 
847 			void
848 efx_mcdi_ev_proxy_response(
849 	__in		efx_nic_t *enp,
850 	__in		unsigned int handle,
851 	__in		unsigned int status)
852 {
853 	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
854 	efx_rc_t rc;
855 
856 	/*
857 	 * Handle results of an authorization request for a privileged MCDI
858 	 * command. If authorization was granted then we must re-issue the
859 	 * original MCDI request. If authorization failed or timed out,
860 	 * then the original MCDI request should be completed with the
861 	 * result code from this event.
862 	 */
863 	rc = (status == 0) ? 0 : efx_mcdi_request_errcode(status);
864 
865 	emtp->emt_ev_proxy_response(emtp->emt_context, handle, rc);
866 }
867 #endif /* EFSYS_OPT_MCDI_PROXY_AUTH */
868 
869 			void
870 efx_mcdi_ev_death(
871 	__in		efx_nic_t *enp,
872 	__in		int rc)
873 {
874 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
875 	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
876 	efx_mcdi_req_t *emrp = NULL;
877 	boolean_t ev_cpl;
878 	efsys_lock_state_t state;
879 
880 	/*
881 	 * The MCDI request (if there is one) has been terminated, either
882 	 * by a BADASSERT or REBOOT event.
883 	 *
884 	 * If there is an outstanding event-completed MCDI operation, then we
885 	 * will never receive the completion event (because both MCDI
886 	 * completions and BADASSERT events are sent to the same evq). So
887 	 * complete this MCDI op.
888 	 *
889 	 * This function might run in parallel with efx_mcdi_request_poll()
890 	 * for poll completed mcdi requests, and also with
891 	 * efx_mcdi_request_start() for post-watchdog completions.
892 	 */
893 	EFSYS_LOCK(enp->en_eslp, state);
894 	emrp = emip->emi_pending_req;
895 	ev_cpl = emip->emi_ev_cpl;
896 	if (emrp != NULL && emip->emi_ev_cpl) {
897 		emip->emi_pending_req = NULL;
898 
899 		emrp->emr_out_length_used = 0;
900 		emrp->emr_rc = rc;
901 		++emip->emi_aborted;
902 	}
903 
904 	/*
905 	 * Since we're running in parallel with a request, consume the
906 	 * status word before dropping the lock.
907 	 */
908 	if (rc == EIO || rc == EINTR) {
909 		EFSYS_SPIN(EFX_MCDI_STATUS_SLEEP_US);
910 		(void) efx_mcdi_poll_reboot(enp);
911 		emip->emi_new_epoch = B_TRUE;
912 	}
913 
914 	EFSYS_UNLOCK(enp->en_eslp, state);
915 
916 	efx_mcdi_raise_exception(enp, emrp, rc);
917 
918 	if (emrp != NULL && ev_cpl)
919 		emtp->emt_ev_cpl(emtp->emt_context);
920 }
921 
922 	__checkReturn		efx_rc_t
923 efx_mcdi_version(
924 	__in			efx_nic_t *enp,
925 	__out_ecount_opt(4)	uint16_t versionp[4],
926 	__out_opt		uint32_t *buildp,
927 	__out_opt		efx_mcdi_boot_t *statusp)
928 {
929 	efx_mcdi_req_t req;
930 	EFX_MCDI_DECLARE_BUF(payload,
931 		MAX(MC_CMD_GET_VERSION_IN_LEN, MC_CMD_GET_BOOT_STATUS_IN_LEN),
932 		MAX(MC_CMD_GET_VERSION_OUT_LEN,
933 			MC_CMD_GET_BOOT_STATUS_OUT_LEN));
934 	efx_word_t *ver_words;
935 	uint16_t version[4];
936 	uint32_t build;
937 	efx_mcdi_boot_t status;
938 	efx_rc_t rc;
939 
940 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
941 
942 	req.emr_cmd = MC_CMD_GET_VERSION;
943 	req.emr_in_buf = payload;
944 	req.emr_in_length = MC_CMD_GET_VERSION_IN_LEN;
945 	req.emr_out_buf = payload;
946 	req.emr_out_length = MC_CMD_GET_VERSION_OUT_LEN;
947 
948 	efx_mcdi_execute(enp, &req);
949 
950 	if (req.emr_rc != 0) {
951 		rc = req.emr_rc;
952 		goto fail1;
953 	}
954 
955 	/* bootrom support */
956 	if (req.emr_out_length_used == MC_CMD_GET_VERSION_V0_OUT_LEN) {
957 		version[0] = version[1] = version[2] = version[3] = 0;
958 		build = MCDI_OUT_DWORD(req, GET_VERSION_OUT_FIRMWARE);
959 
960 		goto version;
961 	}
962 
963 	if (req.emr_out_length_used < MC_CMD_GET_VERSION_OUT_LEN) {
964 		rc = EMSGSIZE;
965 		goto fail2;
966 	}
967 
968 	ver_words = MCDI_OUT2(req, efx_word_t, GET_VERSION_OUT_VERSION);
969 	version[0] = EFX_WORD_FIELD(ver_words[0], EFX_WORD_0);
970 	version[1] = EFX_WORD_FIELD(ver_words[1], EFX_WORD_0);
971 	version[2] = EFX_WORD_FIELD(ver_words[2], EFX_WORD_0);
972 	version[3] = EFX_WORD_FIELD(ver_words[3], EFX_WORD_0);
973 	build = MCDI_OUT_DWORD(req, GET_VERSION_OUT_FIRMWARE);
974 
975 version:
976 	/* The bootrom doesn't understand BOOT_STATUS */
977 	if (MC_FW_VERSION_IS_BOOTLOADER(build)) {
978 		status = EFX_MCDI_BOOT_ROM;
979 		goto out;
980 	}
981 
982 	(void) memset(payload, 0, sizeof (payload));
983 	req.emr_cmd = MC_CMD_GET_BOOT_STATUS;
984 	req.emr_in_buf = payload;
985 	req.emr_in_length = MC_CMD_GET_BOOT_STATUS_IN_LEN;
986 	req.emr_out_buf = payload;
987 	req.emr_out_length = MC_CMD_GET_BOOT_STATUS_OUT_LEN;
988 
989 	efx_mcdi_execute_quiet(enp, &req);
990 
991 	if (req.emr_rc == EACCES) {
992 		/* Unprivileged functions cannot access BOOT_STATUS */
993 		status = EFX_MCDI_BOOT_PRIMARY;
994 		version[0] = version[1] = version[2] = version[3] = 0;
995 		build = 0;
996 		goto out;
997 	}
998 
999 	if (req.emr_rc != 0) {
1000 		rc = req.emr_rc;
1001 		goto fail3;
1002 	}
1003 
1004 	if (req.emr_out_length_used < MC_CMD_GET_BOOT_STATUS_OUT_LEN) {
1005 		rc = EMSGSIZE;
1006 		goto fail4;
1007 	}
1008 
1009 	if (MCDI_OUT_DWORD_FIELD(req, GET_BOOT_STATUS_OUT_FLAGS,
1010 	    GET_BOOT_STATUS_OUT_FLAGS_PRIMARY))
1011 		status = EFX_MCDI_BOOT_PRIMARY;
1012 	else
1013 		status = EFX_MCDI_BOOT_SECONDARY;
1014 
1015 out:
1016 	if (versionp != NULL)
1017 		memcpy(versionp, version, sizeof (version));
1018 	if (buildp != NULL)
1019 		*buildp = build;
1020 	if (statusp != NULL)
1021 		*statusp = status;
1022 
1023 	return (0);
1024 
1025 fail4:
1026 	EFSYS_PROBE(fail4);
1027 fail3:
1028 	EFSYS_PROBE(fail3);
1029 fail2:
1030 	EFSYS_PROBE(fail2);
1031 fail1:
1032 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1033 
1034 	return (rc);
1035 }
1036 
1037 	__checkReturn	efx_rc_t
1038 efx_mcdi_get_capabilities(
1039 	__in		efx_nic_t *enp,
1040 	__out_opt	uint32_t *flagsp,
1041 	__out_opt	uint16_t *rx_dpcpu_fw_idp,
1042 	__out_opt	uint16_t *tx_dpcpu_fw_idp,
1043 	__out_opt	uint32_t *flags2p,
1044 	__out_opt	uint32_t *tso2ncp)
1045 {
1046 	efx_mcdi_req_t req;
1047 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_CAPABILITIES_IN_LEN,
1048 		MC_CMD_GET_CAPABILITIES_V2_OUT_LEN);
1049 	boolean_t v2_capable;
1050 	efx_rc_t rc;
1051 
1052 	req.emr_cmd = MC_CMD_GET_CAPABILITIES;
1053 	req.emr_in_buf = payload;
1054 	req.emr_in_length = MC_CMD_GET_CAPABILITIES_IN_LEN;
1055 	req.emr_out_buf = payload;
1056 	req.emr_out_length = MC_CMD_GET_CAPABILITIES_V2_OUT_LEN;
1057 
1058 	efx_mcdi_execute_quiet(enp, &req);
1059 
1060 	if (req.emr_rc != 0) {
1061 		rc = req.emr_rc;
1062 		goto fail1;
1063 	}
1064 
1065 	if (req.emr_out_length_used < MC_CMD_GET_CAPABILITIES_OUT_LEN) {
1066 		rc = EMSGSIZE;
1067 		goto fail2;
1068 	}
1069 
1070 	if (flagsp != NULL)
1071 		*flagsp = MCDI_OUT_DWORD(req, GET_CAPABILITIES_OUT_FLAGS1);
1072 
1073 	if (rx_dpcpu_fw_idp != NULL)
1074 		*rx_dpcpu_fw_idp = MCDI_OUT_WORD(req,
1075 					GET_CAPABILITIES_OUT_RX_DPCPU_FW_ID);
1076 
1077 	if (tx_dpcpu_fw_idp != NULL)
1078 		*tx_dpcpu_fw_idp = MCDI_OUT_WORD(req,
1079 					GET_CAPABILITIES_OUT_TX_DPCPU_FW_ID);
1080 
1081 	if (req.emr_out_length_used < MC_CMD_GET_CAPABILITIES_V2_OUT_LEN)
1082 		v2_capable = B_FALSE;
1083 	else
1084 		v2_capable = B_TRUE;
1085 
1086 	if (flags2p != NULL) {
1087 		*flags2p = (v2_capable) ?
1088 			MCDI_OUT_DWORD(req, GET_CAPABILITIES_V2_OUT_FLAGS2) :
1089 			0;
1090 	}
1091 
1092 	if (tso2ncp != NULL) {
1093 		*tso2ncp = (v2_capable) ?
1094 			MCDI_OUT_WORD(req,
1095 				GET_CAPABILITIES_V2_OUT_TX_TSO_V2_N_CONTEXTS) :
1096 			0;
1097 	}
1098 
1099 	return (0);
1100 
1101 fail2:
1102 	EFSYS_PROBE(fail2);
1103 fail1:
1104 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1105 
1106 	return (rc);
1107 }
1108 
1109 static	__checkReturn	efx_rc_t
1110 efx_mcdi_do_reboot(
1111 	__in		efx_nic_t *enp,
1112 	__in		boolean_t after_assertion)
1113 {
1114 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_REBOOT_IN_LEN,
1115 		MC_CMD_REBOOT_OUT_LEN);
1116 	efx_mcdi_req_t req;
1117 	efx_rc_t rc;
1118 
1119 	/*
1120 	 * We could require the caller to have caused en_mod_flags=0 to
1121 	 * call this function. This doesn't help the other port though,
1122 	 * who's about to get the MC ripped out from underneath them.
1123 	 * Since they have to cope with the subsequent fallout of MCDI
1124 	 * failures, we should as well.
1125 	 */
1126 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
1127 
1128 	req.emr_cmd = MC_CMD_REBOOT;
1129 	req.emr_in_buf = payload;
1130 	req.emr_in_length = MC_CMD_REBOOT_IN_LEN;
1131 	req.emr_out_buf = payload;
1132 	req.emr_out_length = MC_CMD_REBOOT_OUT_LEN;
1133 
1134 	MCDI_IN_SET_DWORD(req, REBOOT_IN_FLAGS,
1135 	    (after_assertion ? MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION : 0));
1136 
1137 	efx_mcdi_execute_quiet(enp, &req);
1138 
1139 	if (req.emr_rc == EACCES) {
1140 		/* Unprivileged functions cannot reboot the MC. */
1141 		goto out;
1142 	}
1143 
1144 	/* A successful reboot request returns EIO. */
1145 	if (req.emr_rc != 0 && req.emr_rc != EIO) {
1146 		rc = req.emr_rc;
1147 		goto fail1;
1148 	}
1149 
1150 out:
1151 	return (0);
1152 
1153 fail1:
1154 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1155 
1156 	return (rc);
1157 }
1158 
1159 	__checkReturn	efx_rc_t
1160 efx_mcdi_reboot(
1161 	__in		efx_nic_t *enp)
1162 {
1163 	return (efx_mcdi_do_reboot(enp, B_FALSE));
1164 }
1165 
1166 	__checkReturn	efx_rc_t
1167 efx_mcdi_exit_assertion_handler(
1168 	__in		efx_nic_t *enp)
1169 {
1170 	return (efx_mcdi_do_reboot(enp, B_TRUE));
1171 }
1172 
1173 	__checkReturn	efx_rc_t
1174 efx_mcdi_read_assertion(
1175 	__in		efx_nic_t *enp)
1176 {
1177 	efx_mcdi_req_t req;
1178 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_ASSERTS_IN_LEN,
1179 		MC_CMD_GET_ASSERTS_OUT_LEN);
1180 #ifdef KDTRACE_HOOKS
1181 	const char *reason;
1182 #else
1183 	const char *reason __unused;
1184 #endif
1185 	unsigned int flags;
1186 	unsigned int index;
1187 	unsigned int ofst;
1188 	int retry;
1189 	efx_rc_t rc;
1190 
1191 	/*
1192 	 * Before we attempt to chat to the MC, we should verify that the MC
1193 	 * isn't in its assertion handler, either due to a previous reboot,
1194 	 * or because we're reinitializing due to an eec_exception().
1195 	 *
1196 	 * Use GET_ASSERTS to read any assertion state that may be present.
1197 	 * Retry this command twice. Once because a boot-time assertion failure
1198 	 * might cause the 1st MCDI request to fail. And once again because
1199 	 * we might race with efx_mcdi_exit_assertion_handler() running on
1200 	 * partner port(s) on the same NIC.
1201 	 */
1202 	retry = 2;
1203 	do {
1204 		(void) memset(payload, 0, sizeof (payload));
1205 		req.emr_cmd = MC_CMD_GET_ASSERTS;
1206 		req.emr_in_buf = payload;
1207 		req.emr_in_length = MC_CMD_GET_ASSERTS_IN_LEN;
1208 		req.emr_out_buf = payload;
1209 		req.emr_out_length = MC_CMD_GET_ASSERTS_OUT_LEN;
1210 
1211 		MCDI_IN_SET_DWORD(req, GET_ASSERTS_IN_CLEAR, 1);
1212 		efx_mcdi_execute_quiet(enp, &req);
1213 
1214 	} while ((req.emr_rc == EINTR || req.emr_rc == EIO) && retry-- > 0);
1215 
1216 	if (req.emr_rc != 0) {
1217 		if (req.emr_rc == EACCES) {
1218 			/* Unprivileged functions cannot clear assertions. */
1219 			goto out;
1220 		}
1221 		rc = req.emr_rc;
1222 		goto fail1;
1223 	}
1224 
1225 	if (req.emr_out_length_used < MC_CMD_GET_ASSERTS_OUT_LEN) {
1226 		rc = EMSGSIZE;
1227 		goto fail2;
1228 	}
1229 
1230 	/* Print out any assertion state recorded */
1231 	flags = MCDI_OUT_DWORD(req, GET_ASSERTS_OUT_GLOBAL_FLAGS);
1232 	if (flags == MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS)
1233 		return (0);
1234 
1235 	reason = (flags == MC_CMD_GET_ASSERTS_FLAGS_SYS_FAIL)
1236 		? "system-level assertion"
1237 		: (flags == MC_CMD_GET_ASSERTS_FLAGS_THR_FAIL)
1238 		? "thread-level assertion"
1239 		: (flags == MC_CMD_GET_ASSERTS_FLAGS_WDOG_FIRED)
1240 		? "watchdog reset"
1241 		: (flags == MC_CMD_GET_ASSERTS_FLAGS_ADDR_TRAP)
1242 		? "illegal address trap"
1243 		: "unknown assertion";
1244 	EFSYS_PROBE3(mcpu_assertion,
1245 	    const char *, reason, unsigned int,
1246 	    MCDI_OUT_DWORD(req, GET_ASSERTS_OUT_SAVED_PC_OFFS),
1247 	    unsigned int,
1248 	    MCDI_OUT_DWORD(req, GET_ASSERTS_OUT_THREAD_OFFS));
1249 
1250 	/* Print out the registers (r1 ... r31) */
1251 	ofst = MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_OFST;
1252 	for (index = 1;
1253 		index < 1 + MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_NUM;
1254 		index++) {
1255 		EFSYS_PROBE2(mcpu_register, unsigned int, index, unsigned int,
1256 			    EFX_DWORD_FIELD(*MCDI_OUT(req, efx_dword_t, ofst),
1257 					    EFX_DWORD_0));
1258 		ofst += sizeof (efx_dword_t);
1259 	}
1260 	EFSYS_ASSERT(ofst <= MC_CMD_GET_ASSERTS_OUT_LEN);
1261 
1262 out:
1263 	return (0);
1264 
1265 fail2:
1266 	EFSYS_PROBE(fail2);
1267 fail1:
1268 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1269 
1270 	return (rc);
1271 }
1272 
1273 /*
1274  * Internal routines for specific MCDI requests.
1275  */
1276 
1277 	__checkReturn	efx_rc_t
1278 efx_mcdi_drv_attach(
1279 	__in		efx_nic_t *enp,
1280 	__in		boolean_t attach)
1281 {
1282 	efx_mcdi_req_t req;
1283 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_DRV_ATTACH_IN_LEN,
1284 		MC_CMD_DRV_ATTACH_EXT_OUT_LEN);
1285 	efx_rc_t rc;
1286 
1287 	req.emr_cmd = MC_CMD_DRV_ATTACH;
1288 	req.emr_in_buf = payload;
1289 	req.emr_in_length = MC_CMD_DRV_ATTACH_IN_LEN;
1290 	req.emr_out_buf = payload;
1291 	req.emr_out_length = MC_CMD_DRV_ATTACH_EXT_OUT_LEN;
1292 
1293 	/*
1294 	 * Typically, client drivers use DONT_CARE for the datapath firmware
1295 	 * type to ensure that the driver can attach to an unprivileged
1296 	 * function. The datapath firmware type to use is controlled by the
1297 	 * 'sfboot' utility.
1298 	 * If a client driver wishes to attach with a specific datapath firmware
1299 	 * type, that can be passed in second argument of efx_nic_probe API. One
1300 	 * such example is the ESXi native driver that attempts attaching with
1301 	 * FULL_FEATURED datapath firmware type first and fall backs to
1302 	 * DONT_CARE datapath firmware type if MC_CMD_DRV_ATTACH fails.
1303 	 */
1304 	MCDI_IN_POPULATE_DWORD_2(req, DRV_ATTACH_IN_NEW_STATE,
1305 	    DRV_ATTACH_IN_ATTACH, attach ? 1 : 0,
1306 	    DRV_ATTACH_IN_SUBVARIANT_AWARE, EFSYS_OPT_FW_SUBVARIANT_AWARE);
1307 	MCDI_IN_SET_DWORD(req, DRV_ATTACH_IN_UPDATE, 1);
1308 	MCDI_IN_SET_DWORD(req, DRV_ATTACH_IN_FIRMWARE_ID, enp->efv);
1309 
1310 	efx_mcdi_execute(enp, &req);
1311 
1312 	if (req.emr_rc != 0) {
1313 		rc = req.emr_rc;
1314 		goto fail1;
1315 	}
1316 
1317 	if (req.emr_out_length_used < MC_CMD_DRV_ATTACH_OUT_LEN) {
1318 		rc = EMSGSIZE;
1319 		goto fail2;
1320 	}
1321 
1322 	return (0);
1323 
1324 fail2:
1325 	EFSYS_PROBE(fail2);
1326 fail1:
1327 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1328 
1329 	return (rc);
1330 }
1331 
1332 	__checkReturn		efx_rc_t
1333 efx_mcdi_get_board_cfg(
1334 	__in			efx_nic_t *enp,
1335 	__out_opt		uint32_t *board_typep,
1336 	__out_opt		efx_dword_t *capabilitiesp,
1337 	__out_ecount_opt(6)	uint8_t mac_addrp[6])
1338 {
1339 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1340 	efx_mcdi_req_t req;
1341 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_BOARD_CFG_IN_LEN,
1342 		MC_CMD_GET_BOARD_CFG_OUT_LENMIN);
1343 	efx_rc_t rc;
1344 
1345 	req.emr_cmd = MC_CMD_GET_BOARD_CFG;
1346 	req.emr_in_buf = payload;
1347 	req.emr_in_length = MC_CMD_GET_BOARD_CFG_IN_LEN;
1348 	req.emr_out_buf = payload;
1349 	req.emr_out_length = MC_CMD_GET_BOARD_CFG_OUT_LENMIN;
1350 
1351 	efx_mcdi_execute(enp, &req);
1352 
1353 	if (req.emr_rc != 0) {
1354 		rc = req.emr_rc;
1355 		goto fail1;
1356 	}
1357 
1358 	if (req.emr_out_length_used < MC_CMD_GET_BOARD_CFG_OUT_LENMIN) {
1359 		rc = EMSGSIZE;
1360 		goto fail2;
1361 	}
1362 
1363 	if (mac_addrp != NULL) {
1364 		uint8_t *addrp;
1365 
1366 		if (emip->emi_port == 1) {
1367 			addrp = MCDI_OUT2(req, uint8_t,
1368 			    GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0);
1369 		} else if (emip->emi_port == 2) {
1370 			addrp = MCDI_OUT2(req, uint8_t,
1371 			    GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1);
1372 		} else {
1373 			rc = EINVAL;
1374 			goto fail3;
1375 		}
1376 
1377 		EFX_MAC_ADDR_COPY(mac_addrp, addrp);
1378 	}
1379 
1380 	if (capabilitiesp != NULL) {
1381 		if (emip->emi_port == 1) {
1382 			*capabilitiesp = *MCDI_OUT2(req, efx_dword_t,
1383 			    GET_BOARD_CFG_OUT_CAPABILITIES_PORT0);
1384 		} else if (emip->emi_port == 2) {
1385 			*capabilitiesp = *MCDI_OUT2(req, efx_dword_t,
1386 			    GET_BOARD_CFG_OUT_CAPABILITIES_PORT1);
1387 		} else {
1388 			rc = EINVAL;
1389 			goto fail4;
1390 		}
1391 	}
1392 
1393 	if (board_typep != NULL) {
1394 		*board_typep = MCDI_OUT_DWORD(req,
1395 		    GET_BOARD_CFG_OUT_BOARD_TYPE);
1396 	}
1397 
1398 	return (0);
1399 
1400 fail4:
1401 	EFSYS_PROBE(fail4);
1402 fail3:
1403 	EFSYS_PROBE(fail3);
1404 fail2:
1405 	EFSYS_PROBE(fail2);
1406 fail1:
1407 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1408 
1409 	return (rc);
1410 }
1411 
1412 	__checkReturn	efx_rc_t
1413 efx_mcdi_get_resource_limits(
1414 	__in		efx_nic_t *enp,
1415 	__out_opt	uint32_t *nevqp,
1416 	__out_opt	uint32_t *nrxqp,
1417 	__out_opt	uint32_t *ntxqp)
1418 {
1419 	efx_mcdi_req_t req;
1420 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_RESOURCE_LIMITS_IN_LEN,
1421 		MC_CMD_GET_RESOURCE_LIMITS_OUT_LEN);
1422 	efx_rc_t rc;
1423 
1424 	req.emr_cmd = MC_CMD_GET_RESOURCE_LIMITS;
1425 	req.emr_in_buf = payload;
1426 	req.emr_in_length = MC_CMD_GET_RESOURCE_LIMITS_IN_LEN;
1427 	req.emr_out_buf = payload;
1428 	req.emr_out_length = MC_CMD_GET_RESOURCE_LIMITS_OUT_LEN;
1429 
1430 	efx_mcdi_execute(enp, &req);
1431 
1432 	if (req.emr_rc != 0) {
1433 		rc = req.emr_rc;
1434 		goto fail1;
1435 	}
1436 
1437 	if (req.emr_out_length_used < MC_CMD_GET_RESOURCE_LIMITS_OUT_LEN) {
1438 		rc = EMSGSIZE;
1439 		goto fail2;
1440 	}
1441 
1442 	if (nevqp != NULL)
1443 		*nevqp = MCDI_OUT_DWORD(req, GET_RESOURCE_LIMITS_OUT_EVQ);
1444 	if (nrxqp != NULL)
1445 		*nrxqp = MCDI_OUT_DWORD(req, GET_RESOURCE_LIMITS_OUT_RXQ);
1446 	if (ntxqp != NULL)
1447 		*ntxqp = MCDI_OUT_DWORD(req, GET_RESOURCE_LIMITS_OUT_TXQ);
1448 
1449 	return (0);
1450 
1451 fail2:
1452 	EFSYS_PROBE(fail2);
1453 fail1:
1454 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1455 
1456 	return (rc);
1457 }
1458 
1459 	__checkReturn	efx_rc_t
1460 efx_mcdi_get_phy_cfg(
1461 	__in		efx_nic_t *enp)
1462 {
1463 	efx_port_t *epp = &(enp->en_port);
1464 	efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
1465 	efx_mcdi_req_t req;
1466 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_PHY_CFG_IN_LEN,
1467 		MC_CMD_GET_PHY_CFG_OUT_LEN);
1468 #if EFSYS_OPT_NAMES
1469 	const char *namep;
1470 	size_t namelen;
1471 #endif
1472 	uint32_t phy_media_type;
1473 	efx_rc_t rc;
1474 
1475 	req.emr_cmd = MC_CMD_GET_PHY_CFG;
1476 	req.emr_in_buf = payload;
1477 	req.emr_in_length = MC_CMD_GET_PHY_CFG_IN_LEN;
1478 	req.emr_out_buf = payload;
1479 	req.emr_out_length = MC_CMD_GET_PHY_CFG_OUT_LEN;
1480 
1481 	efx_mcdi_execute(enp, &req);
1482 
1483 	if (req.emr_rc != 0) {
1484 		rc = req.emr_rc;
1485 		goto fail1;
1486 	}
1487 
1488 	if (req.emr_out_length_used < MC_CMD_GET_PHY_CFG_OUT_LEN) {
1489 		rc = EMSGSIZE;
1490 		goto fail2;
1491 	}
1492 
1493 	encp->enc_phy_type = MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_TYPE);
1494 #if EFSYS_OPT_NAMES
1495 	namep = MCDI_OUT2(req, char, GET_PHY_CFG_OUT_NAME);
1496 	namelen = MIN(sizeof (encp->enc_phy_name) - 1,
1497 		    strnlen(namep, MC_CMD_GET_PHY_CFG_OUT_NAME_LEN));
1498 	(void) memset(encp->enc_phy_name, 0,
1499 	    sizeof (encp->enc_phy_name));
1500 	memcpy(encp->enc_phy_name, namep, namelen);
1501 #endif	/* EFSYS_OPT_NAMES */
1502 	(void) memset(encp->enc_phy_revision, 0,
1503 	    sizeof (encp->enc_phy_revision));
1504 	memcpy(encp->enc_phy_revision,
1505 		MCDI_OUT2(req, char, GET_PHY_CFG_OUT_REVISION),
1506 		MIN(sizeof (encp->enc_phy_revision) - 1,
1507 		    MC_CMD_GET_PHY_CFG_OUT_REVISION_LEN));
1508 #if EFSYS_OPT_PHY_LED_CONTROL
1509 	encp->enc_led_mask = ((1 << EFX_PHY_LED_DEFAULT) |
1510 			    (1 << EFX_PHY_LED_OFF) |
1511 			    (1 << EFX_PHY_LED_ON));
1512 #endif	/* EFSYS_OPT_PHY_LED_CONTROL */
1513 
1514 	/* Get the media type of the fixed port, if recognised. */
1515 	EFX_STATIC_ASSERT(MC_CMD_MEDIA_XAUI == EFX_PHY_MEDIA_XAUI);
1516 	EFX_STATIC_ASSERT(MC_CMD_MEDIA_CX4 == EFX_PHY_MEDIA_CX4);
1517 	EFX_STATIC_ASSERT(MC_CMD_MEDIA_KX4 == EFX_PHY_MEDIA_KX4);
1518 	EFX_STATIC_ASSERT(MC_CMD_MEDIA_XFP == EFX_PHY_MEDIA_XFP);
1519 	EFX_STATIC_ASSERT(MC_CMD_MEDIA_SFP_PLUS == EFX_PHY_MEDIA_SFP_PLUS);
1520 	EFX_STATIC_ASSERT(MC_CMD_MEDIA_BASE_T == EFX_PHY_MEDIA_BASE_T);
1521 	EFX_STATIC_ASSERT(MC_CMD_MEDIA_QSFP_PLUS == EFX_PHY_MEDIA_QSFP_PLUS);
1522 	phy_media_type = MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_MEDIA_TYPE);
1523 	epp->ep_fixed_port_type = (efx_phy_media_type_t) phy_media_type;
1524 	if (epp->ep_fixed_port_type >= EFX_PHY_MEDIA_NTYPES)
1525 		epp->ep_fixed_port_type = EFX_PHY_MEDIA_INVALID;
1526 
1527 	epp->ep_phy_cap_mask =
1528 		MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_SUPPORTED_CAP);
1529 #if EFSYS_OPT_PHY_FLAGS
1530 	encp->enc_phy_flags_mask = MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_FLAGS);
1531 #endif	/* EFSYS_OPT_PHY_FLAGS */
1532 
1533 	encp->enc_port = (uint8_t)MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_PRT);
1534 
1535 	/* Populate internal state */
1536 	encp->enc_mcdi_mdio_channel =
1537 		(uint8_t)MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_CHANNEL);
1538 
1539 #if EFSYS_OPT_PHY_STATS
1540 	encp->enc_mcdi_phy_stat_mask =
1541 		MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_STATS_MASK);
1542 #endif	/* EFSYS_OPT_PHY_STATS */
1543 
1544 #if EFSYS_OPT_BIST
1545 	encp->enc_bist_mask = 0;
1546 	if (MCDI_OUT_DWORD_FIELD(req, GET_PHY_CFG_OUT_FLAGS,
1547 	    GET_PHY_CFG_OUT_BIST_CABLE_SHORT))
1548 		encp->enc_bist_mask |= (1 << EFX_BIST_TYPE_PHY_CABLE_SHORT);
1549 	if (MCDI_OUT_DWORD_FIELD(req, GET_PHY_CFG_OUT_FLAGS,
1550 	    GET_PHY_CFG_OUT_BIST_CABLE_LONG))
1551 		encp->enc_bist_mask |= (1 << EFX_BIST_TYPE_PHY_CABLE_LONG);
1552 	if (MCDI_OUT_DWORD_FIELD(req, GET_PHY_CFG_OUT_FLAGS,
1553 	    GET_PHY_CFG_OUT_BIST))
1554 		encp->enc_bist_mask |= (1 << EFX_BIST_TYPE_PHY_NORMAL);
1555 #endif  /* EFSYS_OPT_BIST */
1556 
1557 	return (0);
1558 
1559 fail2:
1560 	EFSYS_PROBE(fail2);
1561 fail1:
1562 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1563 
1564 	return (rc);
1565 }
1566 
1567 	__checkReturn		efx_rc_t
1568 efx_mcdi_firmware_update_supported(
1569 	__in			efx_nic_t *enp,
1570 	__out			boolean_t *supportedp)
1571 {
1572 	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
1573 	efx_rc_t rc;
1574 
1575 	if (emcop != NULL) {
1576 		if ((rc = emcop->emco_feature_supported(enp,
1577 			    EFX_MCDI_FEATURE_FW_UPDATE, supportedp)) != 0)
1578 			goto fail1;
1579 	} else {
1580 		/* Earlier devices always supported updates */
1581 		*supportedp = B_TRUE;
1582 	}
1583 
1584 	return (0);
1585 
1586 fail1:
1587 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1588 
1589 	return (rc);
1590 }
1591 
1592 	__checkReturn		efx_rc_t
1593 efx_mcdi_macaddr_change_supported(
1594 	__in			efx_nic_t *enp,
1595 	__out			boolean_t *supportedp)
1596 {
1597 	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
1598 	efx_rc_t rc;
1599 
1600 	if (emcop != NULL) {
1601 		if ((rc = emcop->emco_feature_supported(enp,
1602 			    EFX_MCDI_FEATURE_MACADDR_CHANGE, supportedp)) != 0)
1603 			goto fail1;
1604 	} else {
1605 		/* Earlier devices always supported MAC changes */
1606 		*supportedp = B_TRUE;
1607 	}
1608 
1609 	return (0);
1610 
1611 fail1:
1612 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1613 
1614 	return (rc);
1615 }
1616 
1617 	__checkReturn		efx_rc_t
1618 efx_mcdi_link_control_supported(
1619 	__in			efx_nic_t *enp,
1620 	__out			boolean_t *supportedp)
1621 {
1622 	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
1623 	efx_rc_t rc;
1624 
1625 	if (emcop != NULL) {
1626 		if ((rc = emcop->emco_feature_supported(enp,
1627 			    EFX_MCDI_FEATURE_LINK_CONTROL, supportedp)) != 0)
1628 			goto fail1;
1629 	} else {
1630 		/* Earlier devices always supported link control */
1631 		*supportedp = B_TRUE;
1632 	}
1633 
1634 	return (0);
1635 
1636 fail1:
1637 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1638 
1639 	return (rc);
1640 }
1641 
1642 	__checkReturn		efx_rc_t
1643 efx_mcdi_mac_spoofing_supported(
1644 	__in			efx_nic_t *enp,
1645 	__out			boolean_t *supportedp)
1646 {
1647 	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
1648 	efx_rc_t rc;
1649 
1650 	if (emcop != NULL) {
1651 		if ((rc = emcop->emco_feature_supported(enp,
1652 			    EFX_MCDI_FEATURE_MAC_SPOOFING, supportedp)) != 0)
1653 			goto fail1;
1654 	} else {
1655 		/* Earlier devices always supported MAC spoofing */
1656 		*supportedp = B_TRUE;
1657 	}
1658 
1659 	return (0);
1660 
1661 fail1:
1662 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1663 
1664 	return (rc);
1665 }
1666 
1667 #if EFSYS_OPT_BIST
1668 
1669 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
1670 /*
1671  * Enter bist offline mode. This is a fw mode which puts the NIC into a state
1672  * where memory BIST tests can be run and not much else can interfere or happen.
1673  * A reboot is required to exit this mode.
1674  */
1675 	__checkReturn		efx_rc_t
1676 efx_mcdi_bist_enable_offline(
1677 	__in			efx_nic_t *enp)
1678 {
1679 	efx_mcdi_req_t req;
1680 	efx_rc_t rc;
1681 
1682 	EFX_STATIC_ASSERT(MC_CMD_ENABLE_OFFLINE_BIST_IN_LEN == 0);
1683 	EFX_STATIC_ASSERT(MC_CMD_ENABLE_OFFLINE_BIST_OUT_LEN == 0);
1684 
1685 	req.emr_cmd = MC_CMD_ENABLE_OFFLINE_BIST;
1686 	req.emr_in_buf = NULL;
1687 	req.emr_in_length = 0;
1688 	req.emr_out_buf = NULL;
1689 	req.emr_out_length = 0;
1690 
1691 	efx_mcdi_execute(enp, &req);
1692 
1693 	if (req.emr_rc != 0) {
1694 		rc = req.emr_rc;
1695 		goto fail1;
1696 	}
1697 
1698 	return (0);
1699 
1700 fail1:
1701 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1702 
1703 	return (rc);
1704 }
1705 #endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
1706 
1707 	__checkReturn		efx_rc_t
1708 efx_mcdi_bist_start(
1709 	__in			efx_nic_t *enp,
1710 	__in			efx_bist_type_t type)
1711 {
1712 	efx_mcdi_req_t req;
1713 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_START_BIST_IN_LEN,
1714 		MC_CMD_START_BIST_OUT_LEN);
1715 	efx_rc_t rc;
1716 
1717 	req.emr_cmd = MC_CMD_START_BIST;
1718 	req.emr_in_buf = payload;
1719 	req.emr_in_length = MC_CMD_START_BIST_IN_LEN;
1720 	req.emr_out_buf = payload;
1721 	req.emr_out_length = MC_CMD_START_BIST_OUT_LEN;
1722 
1723 	switch (type) {
1724 	case EFX_BIST_TYPE_PHY_NORMAL:
1725 		MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE, MC_CMD_PHY_BIST);
1726 		break;
1727 	case EFX_BIST_TYPE_PHY_CABLE_SHORT:
1728 		MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
1729 		    MC_CMD_PHY_BIST_CABLE_SHORT);
1730 		break;
1731 	case EFX_BIST_TYPE_PHY_CABLE_LONG:
1732 		MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
1733 		    MC_CMD_PHY_BIST_CABLE_LONG);
1734 		break;
1735 	case EFX_BIST_TYPE_MC_MEM:
1736 		MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
1737 		    MC_CMD_MC_MEM_BIST);
1738 		break;
1739 	case EFX_BIST_TYPE_SAT_MEM:
1740 		MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
1741 		    MC_CMD_PORT_MEM_BIST);
1742 		break;
1743 	case EFX_BIST_TYPE_REG:
1744 		MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
1745 		    MC_CMD_REG_BIST);
1746 		break;
1747 	default:
1748 		EFSYS_ASSERT(0);
1749 	}
1750 
1751 	efx_mcdi_execute(enp, &req);
1752 
1753 	if (req.emr_rc != 0) {
1754 		rc = req.emr_rc;
1755 		goto fail1;
1756 	}
1757 
1758 	return (0);
1759 
1760 fail1:
1761 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1762 
1763 	return (rc);
1764 }
1765 
1766 #endif /* EFSYS_OPT_BIST */
1767 
1768 /* Enable logging of some events (e.g. link state changes) */
1769 	__checkReturn	efx_rc_t
1770 efx_mcdi_log_ctrl(
1771 	__in		efx_nic_t *enp)
1772 {
1773 	efx_mcdi_req_t req;
1774 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_LOG_CTRL_IN_LEN,
1775 		MC_CMD_LOG_CTRL_OUT_LEN);
1776 	efx_rc_t rc;
1777 
1778 	req.emr_cmd = MC_CMD_LOG_CTRL;
1779 	req.emr_in_buf = payload;
1780 	req.emr_in_length = MC_CMD_LOG_CTRL_IN_LEN;
1781 	req.emr_out_buf = payload;
1782 	req.emr_out_length = MC_CMD_LOG_CTRL_OUT_LEN;
1783 
1784 	MCDI_IN_SET_DWORD(req, LOG_CTRL_IN_LOG_DEST,
1785 		    MC_CMD_LOG_CTRL_IN_LOG_DEST_EVQ);
1786 	MCDI_IN_SET_DWORD(req, LOG_CTRL_IN_LOG_DEST_EVQ, 0);
1787 
1788 	efx_mcdi_execute(enp, &req);
1789 
1790 	if (req.emr_rc != 0) {
1791 		rc = req.emr_rc;
1792 		goto fail1;
1793 	}
1794 
1795 	return (0);
1796 
1797 fail1:
1798 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1799 
1800 	return (rc);
1801 }
1802 
1803 #if EFSYS_OPT_MAC_STATS
1804 
1805 typedef enum efx_stats_action_e {
1806 	EFX_STATS_CLEAR,
1807 	EFX_STATS_UPLOAD,
1808 	EFX_STATS_ENABLE_NOEVENTS,
1809 	EFX_STATS_ENABLE_EVENTS,
1810 	EFX_STATS_DISABLE,
1811 } efx_stats_action_t;
1812 
1813 static	__checkReturn	efx_rc_t
1814 efx_mcdi_mac_stats(
1815 	__in		efx_nic_t *enp,
1816 	__in_opt	efsys_mem_t *esmp,
1817 	__in		efx_stats_action_t action,
1818 	__in		uint16_t period_ms)
1819 {
1820 	efx_mcdi_req_t req;
1821 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_MAC_STATS_IN_LEN,
1822 		MC_CMD_MAC_STATS_V2_OUT_DMA_LEN);
1823 	int clear = (action == EFX_STATS_CLEAR);
1824 	int upload = (action == EFX_STATS_UPLOAD);
1825 	int enable = (action == EFX_STATS_ENABLE_NOEVENTS);
1826 	int events = (action == EFX_STATS_ENABLE_EVENTS);
1827 	int disable = (action == EFX_STATS_DISABLE);
1828 	efx_rc_t rc;
1829 
1830 	req.emr_cmd = MC_CMD_MAC_STATS;
1831 	req.emr_in_buf = payload;
1832 	req.emr_in_length = MC_CMD_MAC_STATS_IN_LEN;
1833 	req.emr_out_buf = payload;
1834 	req.emr_out_length = MC_CMD_MAC_STATS_V2_OUT_DMA_LEN;
1835 
1836 	MCDI_IN_POPULATE_DWORD_6(req, MAC_STATS_IN_CMD,
1837 	    MAC_STATS_IN_DMA, upload,
1838 	    MAC_STATS_IN_CLEAR, clear,
1839 	    MAC_STATS_IN_PERIODIC_CHANGE, enable | events | disable,
1840 	    MAC_STATS_IN_PERIODIC_ENABLE, enable | events,
1841 	    MAC_STATS_IN_PERIODIC_NOEVENT, !events,
1842 	    MAC_STATS_IN_PERIOD_MS, (enable | events) ? period_ms : 0);
1843 
1844 	if (enable || events || upload) {
1845 		const efx_nic_cfg_t *encp = &enp->en_nic_cfg;
1846 		uint32_t bytes;
1847 
1848 		/* Periodic stats or stats upload require a DMA buffer */
1849 		if (esmp == NULL) {
1850 			rc = EINVAL;
1851 			goto fail1;
1852 		}
1853 
1854 		if (encp->enc_mac_stats_nstats < MC_CMD_MAC_NSTATS) {
1855 			/* MAC stats count too small for legacy MAC stats */
1856 			rc = ENOSPC;
1857 			goto fail2;
1858 		}
1859 
1860 		bytes = encp->enc_mac_stats_nstats * sizeof (efx_qword_t);
1861 
1862 		if (EFSYS_MEM_SIZE(esmp) < bytes) {
1863 			/* DMA buffer too small */
1864 			rc = ENOSPC;
1865 			goto fail3;
1866 		}
1867 
1868 		MCDI_IN_SET_DWORD(req, MAC_STATS_IN_DMA_ADDR_LO,
1869 			    EFSYS_MEM_ADDR(esmp) & 0xffffffff);
1870 		MCDI_IN_SET_DWORD(req, MAC_STATS_IN_DMA_ADDR_HI,
1871 			    EFSYS_MEM_ADDR(esmp) >> 32);
1872 		MCDI_IN_SET_DWORD(req, MAC_STATS_IN_DMA_LEN, bytes);
1873 	}
1874 
1875 	/*
1876 	 * NOTE: Do not use EVB_PORT_ID_ASSIGNED when disabling periodic stats,
1877 	 *	 as this may fail (and leave periodic DMA enabled) if the
1878 	 *	 vadapter has already been deleted.
1879 	 */
1880 	MCDI_IN_SET_DWORD(req, MAC_STATS_IN_PORT_ID,
1881 	    (disable ? EVB_PORT_ID_NULL : enp->en_vport_id));
1882 
1883 	efx_mcdi_execute(enp, &req);
1884 
1885 	if (req.emr_rc != 0) {
1886 		/* EF10: Expect ENOENT if no DMA queues are initialised */
1887 		if ((req.emr_rc != ENOENT) ||
1888 		    (enp->en_rx_qcount + enp->en_tx_qcount != 0)) {
1889 			rc = req.emr_rc;
1890 			goto fail4;
1891 		}
1892 	}
1893 
1894 	return (0);
1895 
1896 fail4:
1897 	EFSYS_PROBE(fail4);
1898 fail3:
1899 	EFSYS_PROBE(fail3);
1900 fail2:
1901 	EFSYS_PROBE(fail2);
1902 fail1:
1903 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1904 
1905 	return (rc);
1906 }
1907 
1908 	__checkReturn	efx_rc_t
1909 efx_mcdi_mac_stats_clear(
1910 	__in		efx_nic_t *enp)
1911 {
1912 	efx_rc_t rc;
1913 
1914 	if ((rc = efx_mcdi_mac_stats(enp, NULL, EFX_STATS_CLEAR, 0)) != 0)
1915 		goto fail1;
1916 
1917 	return (0);
1918 
1919 fail1:
1920 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1921 
1922 	return (rc);
1923 }
1924 
1925 	__checkReturn	efx_rc_t
1926 efx_mcdi_mac_stats_upload(
1927 	__in		efx_nic_t *enp,
1928 	__in		efsys_mem_t *esmp)
1929 {
1930 	efx_rc_t rc;
1931 
1932 	/*
1933 	 * The MC DMAs aggregate statistics for our convenience, so we can
1934 	 * avoid having to pull the statistics buffer into the cache to
1935 	 * maintain cumulative statistics.
1936 	 */
1937 	if ((rc = efx_mcdi_mac_stats(enp, esmp, EFX_STATS_UPLOAD, 0)) != 0)
1938 		goto fail1;
1939 
1940 	return (0);
1941 
1942 fail1:
1943 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1944 
1945 	return (rc);
1946 }
1947 
1948 	__checkReturn	efx_rc_t
1949 efx_mcdi_mac_stats_periodic(
1950 	__in		efx_nic_t *enp,
1951 	__in		efsys_mem_t *esmp,
1952 	__in		uint16_t period_ms,
1953 	__in		boolean_t events)
1954 {
1955 	efx_rc_t rc;
1956 
1957 	/*
1958 	 * The MC DMAs aggregate statistics for our convenience, so we can
1959 	 * avoid having to pull the statistics buffer into the cache to
1960 	 * maintain cumulative statistics.
1961 	 * Huntington uses a fixed 1sec period.
1962 	 * Medford uses a fixed 1sec period before v6.2.1.1033 firmware.
1963 	 */
1964 	if (period_ms == 0)
1965 		rc = efx_mcdi_mac_stats(enp, NULL, EFX_STATS_DISABLE, 0);
1966 	else if (events)
1967 		rc = efx_mcdi_mac_stats(enp, esmp, EFX_STATS_ENABLE_EVENTS,
1968 		    period_ms);
1969 	else
1970 		rc = efx_mcdi_mac_stats(enp, esmp, EFX_STATS_ENABLE_NOEVENTS,
1971 		    period_ms);
1972 
1973 	if (rc != 0)
1974 		goto fail1;
1975 
1976 	return (0);
1977 
1978 fail1:
1979 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1980 
1981 	return (rc);
1982 }
1983 
1984 #endif	/* EFSYS_OPT_MAC_STATS */
1985 
1986 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
1987 
1988 /*
1989  * This function returns the pf and vf number of a function.  If it is a pf the
1990  * vf number is 0xffff.  The vf number is the index of the vf on that
1991  * function. So if you have 3 vfs on pf 0 the 3 vfs will return (pf=0,vf=0),
1992  * (pf=0,vf=1), (pf=0,vf=2) aand the pf will return (pf=0, vf=0xffff).
1993  */
1994 	__checkReturn		efx_rc_t
1995 efx_mcdi_get_function_info(
1996 	__in			efx_nic_t *enp,
1997 	__out			uint32_t *pfp,
1998 	__out_opt		uint32_t *vfp)
1999 {
2000 	efx_mcdi_req_t req;
2001 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_FUNCTION_INFO_IN_LEN,
2002 		MC_CMD_GET_FUNCTION_INFO_OUT_LEN);
2003 	efx_rc_t rc;
2004 
2005 	req.emr_cmd = MC_CMD_GET_FUNCTION_INFO;
2006 	req.emr_in_buf = payload;
2007 	req.emr_in_length = MC_CMD_GET_FUNCTION_INFO_IN_LEN;
2008 	req.emr_out_buf = payload;
2009 	req.emr_out_length = MC_CMD_GET_FUNCTION_INFO_OUT_LEN;
2010 
2011 	efx_mcdi_execute(enp, &req);
2012 
2013 	if (req.emr_rc != 0) {
2014 		rc = req.emr_rc;
2015 		goto fail1;
2016 	}
2017 
2018 	if (req.emr_out_length_used < MC_CMD_GET_FUNCTION_INFO_OUT_LEN) {
2019 		rc = EMSGSIZE;
2020 		goto fail2;
2021 	}
2022 
2023 	*pfp = MCDI_OUT_DWORD(req, GET_FUNCTION_INFO_OUT_PF);
2024 	if (vfp != NULL)
2025 		*vfp = MCDI_OUT_DWORD(req, GET_FUNCTION_INFO_OUT_VF);
2026 
2027 	return (0);
2028 
2029 fail2:
2030 	EFSYS_PROBE(fail2);
2031 fail1:
2032 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2033 
2034 	return (rc);
2035 }
2036 
2037 	__checkReturn		efx_rc_t
2038 efx_mcdi_privilege_mask(
2039 	__in			efx_nic_t *enp,
2040 	__in			uint32_t pf,
2041 	__in			uint32_t vf,
2042 	__out			uint32_t *maskp)
2043 {
2044 	efx_mcdi_req_t req;
2045 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_PRIVILEGE_MASK_IN_LEN,
2046 		MC_CMD_PRIVILEGE_MASK_OUT_LEN);
2047 	efx_rc_t rc;
2048 
2049 	req.emr_cmd = MC_CMD_PRIVILEGE_MASK;
2050 	req.emr_in_buf = payload;
2051 	req.emr_in_length = MC_CMD_PRIVILEGE_MASK_IN_LEN;
2052 	req.emr_out_buf = payload;
2053 	req.emr_out_length = MC_CMD_PRIVILEGE_MASK_OUT_LEN;
2054 
2055 	MCDI_IN_POPULATE_DWORD_2(req, PRIVILEGE_MASK_IN_FUNCTION,
2056 	    PRIVILEGE_MASK_IN_FUNCTION_PF, pf,
2057 	    PRIVILEGE_MASK_IN_FUNCTION_VF, vf);
2058 
2059 	efx_mcdi_execute(enp, &req);
2060 
2061 	if (req.emr_rc != 0) {
2062 		rc = req.emr_rc;
2063 		goto fail1;
2064 	}
2065 
2066 	if (req.emr_out_length_used < MC_CMD_PRIVILEGE_MASK_OUT_LEN) {
2067 		rc = EMSGSIZE;
2068 		goto fail2;
2069 	}
2070 
2071 	*maskp = MCDI_OUT_DWORD(req, PRIVILEGE_MASK_OUT_OLD_MASK);
2072 
2073 	return (0);
2074 
2075 fail2:
2076 	EFSYS_PROBE(fail2);
2077 fail1:
2078 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2079 
2080 	return (rc);
2081 }
2082 
2083 #endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
2084 
2085 	__checkReturn		efx_rc_t
2086 efx_mcdi_set_workaround(
2087 	__in			efx_nic_t *enp,
2088 	__in			uint32_t type,
2089 	__in			boolean_t enabled,
2090 	__out_opt		uint32_t *flagsp)
2091 {
2092 	efx_mcdi_req_t req;
2093 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_WORKAROUND_IN_LEN,
2094 		MC_CMD_WORKAROUND_EXT_OUT_LEN);
2095 	efx_rc_t rc;
2096 
2097 	req.emr_cmd = MC_CMD_WORKAROUND;
2098 	req.emr_in_buf = payload;
2099 	req.emr_in_length = MC_CMD_WORKAROUND_IN_LEN;
2100 	req.emr_out_buf = payload;
2101 	req.emr_out_length = MC_CMD_WORKAROUND_OUT_LEN;
2102 
2103 	MCDI_IN_SET_DWORD(req, WORKAROUND_IN_TYPE, type);
2104 	MCDI_IN_SET_DWORD(req, WORKAROUND_IN_ENABLED, enabled ? 1 : 0);
2105 
2106 	efx_mcdi_execute_quiet(enp, &req);
2107 
2108 	if (req.emr_rc != 0) {
2109 		rc = req.emr_rc;
2110 		goto fail1;
2111 	}
2112 
2113 	if (flagsp != NULL) {
2114 		if (req.emr_out_length_used >= MC_CMD_WORKAROUND_EXT_OUT_LEN)
2115 			*flagsp = MCDI_OUT_DWORD(req, WORKAROUND_EXT_OUT_FLAGS);
2116 		else
2117 			*flagsp = 0;
2118 	}
2119 
2120 	return (0);
2121 
2122 fail1:
2123 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2124 
2125 	return (rc);
2126 }
2127 
2128 	__checkReturn		efx_rc_t
2129 efx_mcdi_get_workarounds(
2130 	__in			efx_nic_t *enp,
2131 	__out_opt		uint32_t *implementedp,
2132 	__out_opt		uint32_t *enabledp)
2133 {
2134 	efx_mcdi_req_t req;
2135 	EFX_MCDI_DECLARE_BUF(payload, 0, MC_CMD_GET_WORKAROUNDS_OUT_LEN);
2136 	efx_rc_t rc;
2137 
2138 	req.emr_cmd = MC_CMD_GET_WORKAROUNDS;
2139 	req.emr_in_buf = NULL;
2140 	req.emr_in_length = 0;
2141 	req.emr_out_buf = payload;
2142 	req.emr_out_length = MC_CMD_GET_WORKAROUNDS_OUT_LEN;
2143 
2144 	efx_mcdi_execute(enp, &req);
2145 
2146 	if (req.emr_rc != 0) {
2147 		rc = req.emr_rc;
2148 		goto fail1;
2149 	}
2150 
2151 	if (implementedp != NULL) {
2152 		*implementedp =
2153 		    MCDI_OUT_DWORD(req, GET_WORKAROUNDS_OUT_IMPLEMENTED);
2154 	}
2155 
2156 	if (enabledp != NULL) {
2157 		*enabledp = MCDI_OUT_DWORD(req, GET_WORKAROUNDS_OUT_ENABLED);
2158 	}
2159 
2160 	return (0);
2161 
2162 fail1:
2163 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2164 
2165 	return (rc);
2166 }
2167 
2168 /*
2169  * Size of media information page in accordance with SFF-8472 and SFF-8436.
2170  * It is used in MCDI interface as well.
2171  */
2172 #define	EFX_PHY_MEDIA_INFO_PAGE_SIZE		0x80
2173 
2174 /*
2175  * Transceiver identifiers from SFF-8024 Table 4-1.
2176  */
2177 #define	EFX_SFF_TRANSCEIVER_ID_SFP		0x03 /* SFP/SFP+/SFP28 */
2178 #define	EFX_SFF_TRANSCEIVER_ID_QSFP		0x0c /* QSFP */
2179 #define	EFX_SFF_TRANSCEIVER_ID_QSFP_PLUS	0x0d /* QSFP+ or later */
2180 #define	EFX_SFF_TRANSCEIVER_ID_QSFP28		0x11 /* QSFP28 or later */
2181 
2182 static	__checkReturn		efx_rc_t
2183 efx_mcdi_get_phy_media_info(
2184 	__in			efx_nic_t *enp,
2185 	__in			uint32_t mcdi_page,
2186 	__in			uint8_t offset,
2187 	__in			uint8_t len,
2188 	__out_bcount(len)	uint8_t *data)
2189 {
2190 	efx_mcdi_req_t req;
2191 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN,
2192 		MC_CMD_GET_PHY_MEDIA_INFO_OUT_LEN(
2193 			EFX_PHY_MEDIA_INFO_PAGE_SIZE));
2194 	efx_rc_t rc;
2195 
2196 	EFSYS_ASSERT((uint32_t)offset + len <= EFX_PHY_MEDIA_INFO_PAGE_SIZE);
2197 
2198 	req.emr_cmd = MC_CMD_GET_PHY_MEDIA_INFO;
2199 	req.emr_in_buf = payload;
2200 	req.emr_in_length = MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN;
2201 	req.emr_out_buf = payload;
2202 	req.emr_out_length =
2203 	    MC_CMD_GET_PHY_MEDIA_INFO_OUT_LEN(EFX_PHY_MEDIA_INFO_PAGE_SIZE);
2204 
2205 	MCDI_IN_SET_DWORD(req, GET_PHY_MEDIA_INFO_IN_PAGE, mcdi_page);
2206 
2207 	efx_mcdi_execute(enp, &req);
2208 
2209 	if (req.emr_rc != 0) {
2210 		rc = req.emr_rc;
2211 		goto fail1;
2212 	}
2213 
2214 	if (req.emr_out_length_used !=
2215 	    MC_CMD_GET_PHY_MEDIA_INFO_OUT_LEN(EFX_PHY_MEDIA_INFO_PAGE_SIZE)) {
2216 		rc = EMSGSIZE;
2217 		goto fail2;
2218 	}
2219 
2220 	if (MCDI_OUT_DWORD(req, GET_PHY_MEDIA_INFO_OUT_DATALEN) !=
2221 	    EFX_PHY_MEDIA_INFO_PAGE_SIZE) {
2222 		rc = EIO;
2223 		goto fail3;
2224 	}
2225 
2226 	memcpy(data,
2227 	    MCDI_OUT2(req, uint8_t, GET_PHY_MEDIA_INFO_OUT_DATA) + offset,
2228 	    len);
2229 
2230 	return (0);
2231 
2232 fail3:
2233 	EFSYS_PROBE(fail3);
2234 fail2:
2235 	EFSYS_PROBE(fail2);
2236 fail1:
2237 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2238 
2239 	return (rc);
2240 }
2241 
2242 	__checkReturn		efx_rc_t
2243 efx_mcdi_phy_module_get_info(
2244 	__in			efx_nic_t *enp,
2245 	__in			uint8_t dev_addr,
2246 	__in			size_t offset,
2247 	__in			size_t len,
2248 	__out_bcount(len)	uint8_t *data)
2249 {
2250 	efx_port_t *epp = &(enp->en_port);
2251 	efx_rc_t rc;
2252 	uint32_t mcdi_lower_page;
2253 	uint32_t mcdi_upper_page;
2254 	uint8_t id;
2255 
2256 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
2257 
2258 	/*
2259 	 * Map device address to MC_CMD_GET_PHY_MEDIA_INFO pages.
2260 	 * Offset plus length interface allows to access page 0 only.
2261 	 * I.e. non-zero upper pages are not accessible.
2262 	 * See SFF-8472 section 4 Memory Organization and SFF-8436 section 7.6
2263 	 * QSFP+ Memory Map for details on how information is structured
2264 	 * and accessible.
2265 	 */
2266 	switch (epp->ep_fixed_port_type) {
2267 	case EFX_PHY_MEDIA_SFP_PLUS:
2268 	case EFX_PHY_MEDIA_QSFP_PLUS:
2269 		/* Port type supports modules */
2270 		break;
2271 	default:
2272 		rc = ENOTSUP;
2273 		goto fail1;
2274 	}
2275 
2276 	/*
2277 	 * For all supported port types, MCDI page 0 offset 0 holds the
2278 	 * transceiver identifier. Probe to determine the data layout.
2279 	 * Definitions from SFF-8024 Table 4-1.
2280 	 */
2281 	rc = efx_mcdi_get_phy_media_info(enp, 0, 0, sizeof (id), &id);
2282 	if (rc != 0)
2283 		goto fail2;
2284 
2285 	switch (id) {
2286 	case EFX_SFF_TRANSCEIVER_ID_SFP:
2287 		/*
2288 		 * In accordance with SFF-8472 Diagnostic Monitoring
2289 		 * Interface for Optical Transceivers section 4 Memory
2290 		 * Organization two 2-wire addresses are defined.
2291 		 */
2292 		switch (dev_addr) {
2293 		/* Base information */
2294 		case EFX_PHY_MEDIA_INFO_DEV_ADDR_SFP_BASE:
2295 			/*
2296 			 * MCDI page 0 should be used to access lower
2297 			 * page 0 (0x00 - 0x7f) at the device address 0xA0.
2298 			 */
2299 			mcdi_lower_page = 0;
2300 			/*
2301 			 * MCDI page 1 should be used to access  upper
2302 			 * page 0 (0x80 - 0xff) at the device address 0xA0.
2303 			 */
2304 			mcdi_upper_page = 1;
2305 			break;
2306 		/* Diagnostics */
2307 		case EFX_PHY_MEDIA_INFO_DEV_ADDR_SFP_DDM:
2308 			/*
2309 			 * MCDI page 2 should be used to access lower
2310 			 * page 0 (0x00 - 0x7f) at the device address 0xA2.
2311 			 */
2312 			mcdi_lower_page = 2;
2313 			/*
2314 			 * MCDI page 3 should be used to access upper
2315 			 * page 0 (0x80 - 0xff) at the device address 0xA2.
2316 			 */
2317 			mcdi_upper_page = 3;
2318 			break;
2319 		default:
2320 			rc = ENOTSUP;
2321 			goto fail3;
2322 		}
2323 		break;
2324 	case EFX_SFF_TRANSCEIVER_ID_QSFP:
2325 	case EFX_SFF_TRANSCEIVER_ID_QSFP_PLUS:
2326 	case EFX_SFF_TRANSCEIVER_ID_QSFP28:
2327 		switch (dev_addr) {
2328 		case EFX_PHY_MEDIA_INFO_DEV_ADDR_QSFP:
2329 			/*
2330 			 * MCDI page -1 should be used to access lower page 0
2331 			 * (0x00 - 0x7f).
2332 			 */
2333 			mcdi_lower_page = (uint32_t)-1;
2334 			/*
2335 			 * MCDI page 0 should be used to access upper page 0
2336 			 * (0x80h - 0xff).
2337 			 */
2338 			mcdi_upper_page = 0;
2339 			break;
2340 		default:
2341 			rc = ENOTSUP;
2342 			goto fail3;
2343 		}
2344 		break;
2345 	default:
2346 		rc = ENOTSUP;
2347 		goto fail3;
2348 	}
2349 
2350 	EFX_STATIC_ASSERT(EFX_PHY_MEDIA_INFO_PAGE_SIZE <= 0xFF);
2351 
2352 	if (offset < EFX_PHY_MEDIA_INFO_PAGE_SIZE) {
2353 		size_t read_len =
2354 		    MIN(len, EFX_PHY_MEDIA_INFO_PAGE_SIZE - offset);
2355 
2356 		rc = efx_mcdi_get_phy_media_info(enp,
2357 		    mcdi_lower_page, (uint8_t)offset, (uint8_t)read_len, data);
2358 		if (rc != 0)
2359 			goto fail4;
2360 
2361 		data += read_len;
2362 		len -= read_len;
2363 
2364 		offset = 0;
2365 	} else {
2366 		offset -= EFX_PHY_MEDIA_INFO_PAGE_SIZE;
2367 	}
2368 
2369 	if (len > 0) {
2370 		EFSYS_ASSERT3U(len, <=, EFX_PHY_MEDIA_INFO_PAGE_SIZE);
2371 		EFSYS_ASSERT3U(offset, <, EFX_PHY_MEDIA_INFO_PAGE_SIZE);
2372 
2373 		rc = efx_mcdi_get_phy_media_info(enp,
2374 		    mcdi_upper_page, (uint8_t)offset, (uint8_t)len, data);
2375 		if (rc != 0)
2376 			goto fail5;
2377 	}
2378 
2379 	return (0);
2380 
2381 fail5:
2382 	EFSYS_PROBE(fail5);
2383 fail4:
2384 	EFSYS_PROBE(fail4);
2385 fail3:
2386 	EFSYS_PROBE(fail3);
2387 fail2:
2388 	EFSYS_PROBE(fail2);
2389 fail1:
2390 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2391 
2392 	return (rc);
2393 }
2394 
2395 #endif	/* EFSYS_OPT_MCDI */
2396