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