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