xref: /freebsd/sys/dev/sfxge/common/efx_mcdi.c (revision ab1e0d2410ece7d391a5b1e2cbc9d1e9857c2fdb)
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 	/*
818 	 * Return proxy handle from MCDI request that returned with error
819 	 * MC_MCD_ERR_PROXY_PENDING. This handle is used to wait for a matching
820 	 * PROXY_RESPONSE event.
821 	 */
822 	if ((emrp == NULL) || (handlep == NULL)) {
823 		rc = EINVAL;
824 		goto fail1;
825 	}
826 	if ((emrp->emr_rc != 0) &&
827 	    (emrp->emr_err_code == MC_CMD_ERR_PROXY_PENDING)) {
828 		*handlep = emrp->emr_proxy_handle;
829 		rc = 0;
830 	} else {
831 		*handlep = 0;
832 		rc = ENOENT;
833 	}
834 	return (rc);
835 
836 fail1:
837 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
838 	return (rc);
839 }
840 
841 			void
842 efx_mcdi_ev_proxy_response(
843 	__in		efx_nic_t *enp,
844 	__in		unsigned int handle,
845 	__in		unsigned int status)
846 {
847 	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
848 	efx_rc_t rc;
849 
850 	/*
851 	 * Handle results of an authorization request for a privileged MCDI
852 	 * command. If authorization was granted then we must re-issue the
853 	 * original MCDI request. If authorization failed or timed out,
854 	 * then the original MCDI request should be completed with the
855 	 * result code from this event.
856 	 */
857 	rc = (status == 0) ? 0 : efx_mcdi_request_errcode(status);
858 
859 	emtp->emt_ev_proxy_response(emtp->emt_context, handle, rc);
860 }
861 #endif /* EFSYS_OPT_MCDI_PROXY_AUTH */
862 
863 			void
864 efx_mcdi_ev_death(
865 	__in		efx_nic_t *enp,
866 	__in		int rc)
867 {
868 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
869 	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
870 	efx_mcdi_req_t *emrp = NULL;
871 	boolean_t ev_cpl;
872 	efsys_lock_state_t state;
873 
874 	/*
875 	 * The MCDI request (if there is one) has been terminated, either
876 	 * by a BADASSERT or REBOOT event.
877 	 *
878 	 * If there is an outstanding event-completed MCDI operation, then we
879 	 * will never receive the completion event (because both MCDI
880 	 * completions and BADASSERT events are sent to the same evq). So
881 	 * complete this MCDI op.
882 	 *
883 	 * This function might run in parallel with efx_mcdi_request_poll()
884 	 * for poll completed mcdi requests, and also with
885 	 * efx_mcdi_request_start() for post-watchdog completions.
886 	 */
887 	EFSYS_LOCK(enp->en_eslp, state);
888 	emrp = emip->emi_pending_req;
889 	ev_cpl = emip->emi_ev_cpl;
890 	if (emrp != NULL && emip->emi_ev_cpl) {
891 		emip->emi_pending_req = NULL;
892 
893 		emrp->emr_out_length_used = 0;
894 		emrp->emr_rc = rc;
895 		++emip->emi_aborted;
896 	}
897 
898 	/*
899 	 * Since we're running in parallel with a request, consume the
900 	 * status word before dropping the lock.
901 	 */
902 	if (rc == EIO || rc == EINTR) {
903 		EFSYS_SPIN(EFX_MCDI_STATUS_SLEEP_US);
904 		(void) efx_mcdi_poll_reboot(enp);
905 		emip->emi_new_epoch = B_TRUE;
906 	}
907 
908 	EFSYS_UNLOCK(enp->en_eslp, state);
909 
910 	efx_mcdi_raise_exception(enp, emrp, rc);
911 
912 	if (emrp != NULL && ev_cpl)
913 		emtp->emt_ev_cpl(emtp->emt_context);
914 }
915 
916 	__checkReturn		efx_rc_t
917 efx_mcdi_version(
918 	__in			efx_nic_t *enp,
919 	__out_ecount_opt(4)	uint16_t versionp[4],
920 	__out_opt		uint32_t *buildp,
921 	__out_opt		efx_mcdi_boot_t *statusp)
922 {
923 	efx_mcdi_req_t req;
924 	uint8_t payload[MAX(MAX(MC_CMD_GET_VERSION_IN_LEN,
925 				MC_CMD_GET_VERSION_OUT_LEN),
926 			    MAX(MC_CMD_GET_BOOT_STATUS_IN_LEN,
927 				MC_CMD_GET_BOOT_STATUS_OUT_LEN))];
928 	efx_word_t *ver_words;
929 	uint16_t version[4];
930 	uint32_t build;
931 	efx_mcdi_boot_t status;
932 	efx_rc_t rc;
933 
934 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
935 
936 	(void) memset(payload, 0, sizeof (payload));
937 	req.emr_cmd = MC_CMD_GET_VERSION;
938 	req.emr_in_buf = payload;
939 	req.emr_in_length = MC_CMD_GET_VERSION_IN_LEN;
940 	req.emr_out_buf = payload;
941 	req.emr_out_length = MC_CMD_GET_VERSION_OUT_LEN;
942 
943 	efx_mcdi_execute(enp, &req);
944 
945 	if (req.emr_rc != 0) {
946 		rc = req.emr_rc;
947 		goto fail1;
948 	}
949 
950 	/* bootrom support */
951 	if (req.emr_out_length_used == MC_CMD_GET_VERSION_V0_OUT_LEN) {
952 		version[0] = version[1] = version[2] = version[3] = 0;
953 		build = MCDI_OUT_DWORD(req, GET_VERSION_OUT_FIRMWARE);
954 
955 		goto version;
956 	}
957 
958 	if (req.emr_out_length_used < MC_CMD_GET_VERSION_OUT_LEN) {
959 		rc = EMSGSIZE;
960 		goto fail2;
961 	}
962 
963 	ver_words = MCDI_OUT2(req, efx_word_t, GET_VERSION_OUT_VERSION);
964 	version[0] = EFX_WORD_FIELD(ver_words[0], EFX_WORD_0);
965 	version[1] = EFX_WORD_FIELD(ver_words[1], EFX_WORD_0);
966 	version[2] = EFX_WORD_FIELD(ver_words[2], EFX_WORD_0);
967 	version[3] = EFX_WORD_FIELD(ver_words[3], EFX_WORD_0);
968 	build = MCDI_OUT_DWORD(req, GET_VERSION_OUT_FIRMWARE);
969 
970 version:
971 	/* The bootrom doesn't understand BOOT_STATUS */
972 	if (MC_FW_VERSION_IS_BOOTLOADER(build)) {
973 		status = EFX_MCDI_BOOT_ROM;
974 		goto out;
975 	}
976 
977 	(void) memset(payload, 0, sizeof (payload));
978 	req.emr_cmd = MC_CMD_GET_BOOT_STATUS;
979 	req.emr_in_buf = payload;
980 	req.emr_in_length = MC_CMD_GET_BOOT_STATUS_IN_LEN;
981 	req.emr_out_buf = payload;
982 	req.emr_out_length = MC_CMD_GET_BOOT_STATUS_OUT_LEN;
983 
984 	efx_mcdi_execute_quiet(enp, &req);
985 
986 	if (req.emr_rc == EACCES) {
987 		/* Unprivileged functions cannot access BOOT_STATUS */
988 		status = EFX_MCDI_BOOT_PRIMARY;
989 		version[0] = version[1] = version[2] = version[3] = 0;
990 		build = 0;
991 		goto out;
992 	}
993 
994 	if (req.emr_rc != 0) {
995 		rc = req.emr_rc;
996 		goto fail3;
997 	}
998 
999 	if (req.emr_out_length_used < MC_CMD_GET_BOOT_STATUS_OUT_LEN) {
1000 		rc = EMSGSIZE;
1001 		goto fail4;
1002 	}
1003 
1004 	if (MCDI_OUT_DWORD_FIELD(req, GET_BOOT_STATUS_OUT_FLAGS,
1005 	    GET_BOOT_STATUS_OUT_FLAGS_PRIMARY))
1006 		status = EFX_MCDI_BOOT_PRIMARY;
1007 	else
1008 		status = EFX_MCDI_BOOT_SECONDARY;
1009 
1010 out:
1011 	if (versionp != NULL)
1012 		memcpy(versionp, version, sizeof (version));
1013 	if (buildp != NULL)
1014 		*buildp = build;
1015 	if (statusp != NULL)
1016 		*statusp = status;
1017 
1018 	return (0);
1019 
1020 fail4:
1021 	EFSYS_PROBE(fail4);
1022 fail3:
1023 	EFSYS_PROBE(fail3);
1024 fail2:
1025 	EFSYS_PROBE(fail2);
1026 fail1:
1027 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1028 
1029 	return (rc);
1030 }
1031 
1032 	__checkReturn	efx_rc_t
1033 efx_mcdi_get_capabilities(
1034 	__in		efx_nic_t *enp,
1035 	__out_opt	uint32_t *flagsp,
1036 	__out_opt	uint16_t *rx_dpcpu_fw_idp,
1037 	__out_opt	uint16_t *tx_dpcpu_fw_idp,
1038 	__out_opt	uint32_t *flags2p,
1039 	__out_opt	uint32_t *tso2ncp)
1040 {
1041 	efx_mcdi_req_t req;
1042 	uint8_t payload[MAX(MC_CMD_GET_CAPABILITIES_IN_LEN,
1043 			    MC_CMD_GET_CAPABILITIES_V2_OUT_LEN)];
1044 	boolean_t v2_capable;
1045 	efx_rc_t rc;
1046 
1047 	(void) memset(payload, 0, sizeof (payload));
1048 	req.emr_cmd = MC_CMD_GET_CAPABILITIES;
1049 	req.emr_in_buf = payload;
1050 	req.emr_in_length = MC_CMD_GET_CAPABILITIES_IN_LEN;
1051 	req.emr_out_buf = payload;
1052 	req.emr_out_length = MC_CMD_GET_CAPABILITIES_V2_OUT_LEN;
1053 
1054 	efx_mcdi_execute_quiet(enp, &req);
1055 
1056 	if (req.emr_rc != 0) {
1057 		rc = req.emr_rc;
1058 		goto fail1;
1059 	}
1060 
1061 	if (req.emr_out_length_used < MC_CMD_GET_CAPABILITIES_OUT_LEN) {
1062 		rc = EMSGSIZE;
1063 		goto fail2;
1064 	}
1065 
1066 	if (flagsp != NULL)
1067 		*flagsp = MCDI_OUT_DWORD(req, GET_CAPABILITIES_OUT_FLAGS1);
1068 
1069 	if (rx_dpcpu_fw_idp != NULL)
1070 		*rx_dpcpu_fw_idp = MCDI_OUT_WORD(req,
1071 					GET_CAPABILITIES_OUT_RX_DPCPU_FW_ID);
1072 
1073 	if (tx_dpcpu_fw_idp != NULL)
1074 		*tx_dpcpu_fw_idp = MCDI_OUT_WORD(req,
1075 					GET_CAPABILITIES_OUT_TX_DPCPU_FW_ID);
1076 
1077 	if (req.emr_out_length_used < MC_CMD_GET_CAPABILITIES_V2_OUT_LEN)
1078 		v2_capable = B_FALSE;
1079 	else
1080 		v2_capable = B_TRUE;
1081 
1082 	if (flags2p != NULL) {
1083 		*flags2p = (v2_capable) ?
1084 			MCDI_OUT_DWORD(req, GET_CAPABILITIES_V2_OUT_FLAGS2) :
1085 			0;
1086 	}
1087 
1088 	if (tso2ncp != NULL) {
1089 		*tso2ncp = (v2_capable) ?
1090 			MCDI_OUT_WORD(req,
1091 				GET_CAPABILITIES_V2_OUT_TX_TSO_V2_N_CONTEXTS) :
1092 			0;
1093 	}
1094 
1095 	return (0);
1096 
1097 fail2:
1098 	EFSYS_PROBE(fail2);
1099 fail1:
1100 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1101 
1102 	return (rc);
1103 }
1104 
1105 static	__checkReturn	efx_rc_t
1106 efx_mcdi_do_reboot(
1107 	__in		efx_nic_t *enp,
1108 	__in		boolean_t after_assertion)
1109 {
1110 	uint8_t payload[MAX(MC_CMD_REBOOT_IN_LEN, MC_CMD_REBOOT_OUT_LEN)];
1111 	efx_mcdi_req_t req;
1112 	efx_rc_t rc;
1113 
1114 	/*
1115 	 * We could require the caller to have caused en_mod_flags=0 to
1116 	 * call this function. This doesn't help the other port though,
1117 	 * who's about to get the MC ripped out from underneath them.
1118 	 * Since they have to cope with the subsequent fallout of MCDI
1119 	 * failures, we should as well.
1120 	 */
1121 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
1122 
1123 	(void) memset(payload, 0, sizeof (payload));
1124 	req.emr_cmd = MC_CMD_REBOOT;
1125 	req.emr_in_buf = payload;
1126 	req.emr_in_length = MC_CMD_REBOOT_IN_LEN;
1127 	req.emr_out_buf = payload;
1128 	req.emr_out_length = MC_CMD_REBOOT_OUT_LEN;
1129 
1130 	MCDI_IN_SET_DWORD(req, REBOOT_IN_FLAGS,
1131 	    (after_assertion ? MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION : 0));
1132 
1133 	efx_mcdi_execute_quiet(enp, &req);
1134 
1135 	if (req.emr_rc == EACCES) {
1136 		/* Unprivileged functions cannot reboot the MC. */
1137 		goto out;
1138 	}
1139 
1140 	/* A successful reboot request returns EIO. */
1141 	if (req.emr_rc != 0 && req.emr_rc != EIO) {
1142 		rc = req.emr_rc;
1143 		goto fail1;
1144 	}
1145 
1146 out:
1147 	return (0);
1148 
1149 fail1:
1150 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1151 
1152 	return (rc);
1153 }
1154 
1155 	__checkReturn	efx_rc_t
1156 efx_mcdi_reboot(
1157 	__in		efx_nic_t *enp)
1158 {
1159 	return (efx_mcdi_do_reboot(enp, B_FALSE));
1160 }
1161 
1162 	__checkReturn	efx_rc_t
1163 efx_mcdi_exit_assertion_handler(
1164 	__in		efx_nic_t *enp)
1165 {
1166 	return (efx_mcdi_do_reboot(enp, B_TRUE));
1167 }
1168 
1169 	__checkReturn	efx_rc_t
1170 efx_mcdi_read_assertion(
1171 	__in		efx_nic_t *enp)
1172 {
1173 	efx_mcdi_req_t req;
1174 	uint8_t payload[MAX(MC_CMD_GET_ASSERTS_IN_LEN,
1175 			    MC_CMD_GET_ASSERTS_OUT_LEN)];
1176 	const char *reason;
1177 	unsigned int flags;
1178 	unsigned int index;
1179 	unsigned int ofst;
1180 	int retry;
1181 	efx_rc_t rc;
1182 
1183 	/*
1184 	 * Before we attempt to chat to the MC, we should verify that the MC
1185 	 * isn't in its assertion handler, either due to a previous reboot,
1186 	 * or because we're reinitializing due to an eec_exception().
1187 	 *
1188 	 * Use GET_ASSERTS to read any assertion state that may be present.
1189 	 * Retry this command twice. Once because a boot-time assertion failure
1190 	 * might cause the 1st MCDI request to fail. And once again because
1191 	 * we might race with efx_mcdi_exit_assertion_handler() running on
1192 	 * partner port(s) on the same NIC.
1193 	 */
1194 	retry = 2;
1195 	do {
1196 		(void) memset(payload, 0, sizeof (payload));
1197 		req.emr_cmd = MC_CMD_GET_ASSERTS;
1198 		req.emr_in_buf = payload;
1199 		req.emr_in_length = MC_CMD_GET_ASSERTS_IN_LEN;
1200 		req.emr_out_buf = payload;
1201 		req.emr_out_length = MC_CMD_GET_ASSERTS_OUT_LEN;
1202 
1203 		MCDI_IN_SET_DWORD(req, GET_ASSERTS_IN_CLEAR, 1);
1204 		efx_mcdi_execute_quiet(enp, &req);
1205 
1206 	} while ((req.emr_rc == EINTR || req.emr_rc == EIO) && retry-- > 0);
1207 
1208 	if (req.emr_rc != 0) {
1209 		if (req.emr_rc == EACCES) {
1210 			/* Unprivileged functions cannot clear assertions. */
1211 			goto out;
1212 		}
1213 		rc = req.emr_rc;
1214 		goto fail1;
1215 	}
1216 
1217 	if (req.emr_out_length_used < MC_CMD_GET_ASSERTS_OUT_LEN) {
1218 		rc = EMSGSIZE;
1219 		goto fail2;
1220 	}
1221 
1222 	/* Print out any assertion state recorded */
1223 	flags = MCDI_OUT_DWORD(req, GET_ASSERTS_OUT_GLOBAL_FLAGS);
1224 	if (flags == MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS)
1225 		return (0);
1226 
1227 	reason = (flags == MC_CMD_GET_ASSERTS_FLAGS_SYS_FAIL)
1228 		? "system-level assertion"
1229 		: (flags == MC_CMD_GET_ASSERTS_FLAGS_THR_FAIL)
1230 		? "thread-level assertion"
1231 		: (flags == MC_CMD_GET_ASSERTS_FLAGS_WDOG_FIRED)
1232 		? "watchdog reset"
1233 		: (flags == MC_CMD_GET_ASSERTS_FLAGS_ADDR_TRAP)
1234 		? "illegal address trap"
1235 		: "unknown assertion";
1236 	EFSYS_PROBE3(mcpu_assertion,
1237 	    const char *, reason, unsigned int,
1238 	    MCDI_OUT_DWORD(req, GET_ASSERTS_OUT_SAVED_PC_OFFS),
1239 	    unsigned int,
1240 	    MCDI_OUT_DWORD(req, GET_ASSERTS_OUT_THREAD_OFFS));
1241 
1242 	/* Print out the registers (r1 ... r31) */
1243 	ofst = MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_OFST;
1244 	for (index = 1;
1245 		index < 1 + MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_NUM;
1246 		index++) {
1247 		EFSYS_PROBE2(mcpu_register, unsigned int, index, unsigned int,
1248 			    EFX_DWORD_FIELD(*MCDI_OUT(req, efx_dword_t, ofst),
1249 					    EFX_DWORD_0));
1250 		ofst += sizeof (efx_dword_t);
1251 	}
1252 	EFSYS_ASSERT(ofst <= MC_CMD_GET_ASSERTS_OUT_LEN);
1253 
1254 out:
1255 	return (0);
1256 
1257 fail2:
1258 	EFSYS_PROBE(fail2);
1259 fail1:
1260 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1261 
1262 	return (rc);
1263 }
1264 
1265 
1266 /*
1267  * Internal routines for for specific MCDI requests.
1268  */
1269 
1270 	__checkReturn	efx_rc_t
1271 efx_mcdi_drv_attach(
1272 	__in		efx_nic_t *enp,
1273 	__in		boolean_t attach)
1274 {
1275 	efx_mcdi_req_t req;
1276 	uint8_t payload[MAX(MC_CMD_DRV_ATTACH_IN_LEN,
1277 			    MC_CMD_DRV_ATTACH_EXT_OUT_LEN)];
1278 	efx_rc_t rc;
1279 
1280 	(void) memset(payload, 0, sizeof (payload));
1281 	req.emr_cmd = MC_CMD_DRV_ATTACH;
1282 	req.emr_in_buf = payload;
1283 	req.emr_in_length = MC_CMD_DRV_ATTACH_IN_LEN;
1284 	req.emr_out_buf = payload;
1285 	req.emr_out_length = MC_CMD_DRV_ATTACH_EXT_OUT_LEN;
1286 
1287 	/*
1288 	 * Use DONT_CARE for the datapath firmware type to ensure that the
1289 	 * driver can attach to an unprivileged function. The datapath firmware
1290 	 * type to use is controlled by the 'sfboot' utility.
1291 	 */
1292 	MCDI_IN_SET_DWORD(req, DRV_ATTACH_IN_NEW_STATE, attach ? 1 : 0);
1293 	MCDI_IN_SET_DWORD(req, DRV_ATTACH_IN_UPDATE, 1);
1294 	MCDI_IN_SET_DWORD(req, DRV_ATTACH_IN_FIRMWARE_ID, MC_CMD_FW_DONT_CARE);
1295 
1296 	efx_mcdi_execute(enp, &req);
1297 
1298 	if (req.emr_rc != 0) {
1299 		rc = req.emr_rc;
1300 		goto fail1;
1301 	}
1302 
1303 	if (req.emr_out_length_used < MC_CMD_DRV_ATTACH_OUT_LEN) {
1304 		rc = EMSGSIZE;
1305 		goto fail2;
1306 	}
1307 
1308 	return (0);
1309 
1310 fail2:
1311 	EFSYS_PROBE(fail2);
1312 fail1:
1313 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1314 
1315 	return (rc);
1316 }
1317 
1318 	__checkReturn		efx_rc_t
1319 efx_mcdi_get_board_cfg(
1320 	__in			efx_nic_t *enp,
1321 	__out_opt		uint32_t *board_typep,
1322 	__out_opt		efx_dword_t *capabilitiesp,
1323 	__out_ecount_opt(6)	uint8_t mac_addrp[6])
1324 {
1325 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1326 	efx_mcdi_req_t req;
1327 	uint8_t payload[MAX(MC_CMD_GET_BOARD_CFG_IN_LEN,
1328 			    MC_CMD_GET_BOARD_CFG_OUT_LENMIN)];
1329 	efx_rc_t rc;
1330 
1331 	(void) memset(payload, 0, sizeof (payload));
1332 	req.emr_cmd = MC_CMD_GET_BOARD_CFG;
1333 	req.emr_in_buf = payload;
1334 	req.emr_in_length = MC_CMD_GET_BOARD_CFG_IN_LEN;
1335 	req.emr_out_buf = payload;
1336 	req.emr_out_length = MC_CMD_GET_BOARD_CFG_OUT_LENMIN;
1337 
1338 	efx_mcdi_execute(enp, &req);
1339 
1340 	if (req.emr_rc != 0) {
1341 		rc = req.emr_rc;
1342 		goto fail1;
1343 	}
1344 
1345 	if (req.emr_out_length_used < MC_CMD_GET_BOARD_CFG_OUT_LENMIN) {
1346 		rc = EMSGSIZE;
1347 		goto fail2;
1348 	}
1349 
1350 	if (mac_addrp != NULL) {
1351 		uint8_t *addrp;
1352 
1353 		if (emip->emi_port == 1) {
1354 			addrp = MCDI_OUT2(req, uint8_t,
1355 			    GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0);
1356 		} else if (emip->emi_port == 2) {
1357 			addrp = MCDI_OUT2(req, uint8_t,
1358 			    GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1);
1359 		} else {
1360 			rc = EINVAL;
1361 			goto fail3;
1362 		}
1363 
1364 		EFX_MAC_ADDR_COPY(mac_addrp, addrp);
1365 	}
1366 
1367 	if (capabilitiesp != NULL) {
1368 		if (emip->emi_port == 1) {
1369 			*capabilitiesp = *MCDI_OUT2(req, efx_dword_t,
1370 			    GET_BOARD_CFG_OUT_CAPABILITIES_PORT0);
1371 		} else if (emip->emi_port == 2) {
1372 			*capabilitiesp = *MCDI_OUT2(req, efx_dword_t,
1373 			    GET_BOARD_CFG_OUT_CAPABILITIES_PORT1);
1374 		} else {
1375 			rc = EINVAL;
1376 			goto fail4;
1377 		}
1378 	}
1379 
1380 	if (board_typep != NULL) {
1381 		*board_typep = MCDI_OUT_DWORD(req,
1382 		    GET_BOARD_CFG_OUT_BOARD_TYPE);
1383 	}
1384 
1385 	return (0);
1386 
1387 fail4:
1388 	EFSYS_PROBE(fail4);
1389 fail3:
1390 	EFSYS_PROBE(fail3);
1391 fail2:
1392 	EFSYS_PROBE(fail2);
1393 fail1:
1394 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1395 
1396 	return (rc);
1397 }
1398 
1399 	__checkReturn	efx_rc_t
1400 efx_mcdi_get_resource_limits(
1401 	__in		efx_nic_t *enp,
1402 	__out_opt	uint32_t *nevqp,
1403 	__out_opt	uint32_t *nrxqp,
1404 	__out_opt	uint32_t *ntxqp)
1405 {
1406 	efx_mcdi_req_t req;
1407 	uint8_t payload[MAX(MC_CMD_GET_RESOURCE_LIMITS_IN_LEN,
1408 			    MC_CMD_GET_RESOURCE_LIMITS_OUT_LEN)];
1409 	efx_rc_t rc;
1410 
1411 	(void) memset(payload, 0, sizeof (payload));
1412 	req.emr_cmd = MC_CMD_GET_RESOURCE_LIMITS;
1413 	req.emr_in_buf = payload;
1414 	req.emr_in_length = MC_CMD_GET_RESOURCE_LIMITS_IN_LEN;
1415 	req.emr_out_buf = payload;
1416 	req.emr_out_length = MC_CMD_GET_RESOURCE_LIMITS_OUT_LEN;
1417 
1418 	efx_mcdi_execute(enp, &req);
1419 
1420 	if (req.emr_rc != 0) {
1421 		rc = req.emr_rc;
1422 		goto fail1;
1423 	}
1424 
1425 	if (req.emr_out_length_used < MC_CMD_GET_RESOURCE_LIMITS_OUT_LEN) {
1426 		rc = EMSGSIZE;
1427 		goto fail2;
1428 	}
1429 
1430 	if (nevqp != NULL)
1431 		*nevqp = MCDI_OUT_DWORD(req, GET_RESOURCE_LIMITS_OUT_EVQ);
1432 	if (nrxqp != NULL)
1433 		*nrxqp = MCDI_OUT_DWORD(req, GET_RESOURCE_LIMITS_OUT_RXQ);
1434 	if (ntxqp != NULL)
1435 		*ntxqp = MCDI_OUT_DWORD(req, GET_RESOURCE_LIMITS_OUT_TXQ);
1436 
1437 	return (0);
1438 
1439 fail2:
1440 	EFSYS_PROBE(fail2);
1441 fail1:
1442 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1443 
1444 	return (rc);
1445 }
1446 
1447 	__checkReturn	efx_rc_t
1448 efx_mcdi_get_phy_cfg(
1449 	__in		efx_nic_t *enp)
1450 {
1451 	efx_port_t *epp = &(enp->en_port);
1452 	efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
1453 	efx_mcdi_req_t req;
1454 	uint8_t payload[MAX(MC_CMD_GET_PHY_CFG_IN_LEN,
1455 			    MC_CMD_GET_PHY_CFG_OUT_LEN)];
1456 	efx_rc_t rc;
1457 
1458 	(void) memset(payload, 0, sizeof (payload));
1459 	req.emr_cmd = MC_CMD_GET_PHY_CFG;
1460 	req.emr_in_buf = payload;
1461 	req.emr_in_length = MC_CMD_GET_PHY_CFG_IN_LEN;
1462 	req.emr_out_buf = payload;
1463 	req.emr_out_length = MC_CMD_GET_PHY_CFG_OUT_LEN;
1464 
1465 	efx_mcdi_execute(enp, &req);
1466 
1467 	if (req.emr_rc != 0) {
1468 		rc = req.emr_rc;
1469 		goto fail1;
1470 	}
1471 
1472 	if (req.emr_out_length_used < MC_CMD_GET_PHY_CFG_OUT_LEN) {
1473 		rc = EMSGSIZE;
1474 		goto fail2;
1475 	}
1476 
1477 	encp->enc_phy_type = MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_TYPE);
1478 #if EFSYS_OPT_NAMES
1479 	(void) strncpy(encp->enc_phy_name,
1480 		MCDI_OUT2(req, char, GET_PHY_CFG_OUT_NAME),
1481 		MIN(sizeof (encp->enc_phy_name) - 1,
1482 		    MC_CMD_GET_PHY_CFG_OUT_NAME_LEN));
1483 #endif	/* EFSYS_OPT_NAMES */
1484 	(void) memset(encp->enc_phy_revision, 0,
1485 	    sizeof (encp->enc_phy_revision));
1486 	memcpy(encp->enc_phy_revision,
1487 		MCDI_OUT2(req, char, GET_PHY_CFG_OUT_REVISION),
1488 		MIN(sizeof (encp->enc_phy_revision) - 1,
1489 		    MC_CMD_GET_PHY_CFG_OUT_REVISION_LEN));
1490 #if EFSYS_OPT_PHY_LED_CONTROL
1491 	encp->enc_led_mask = ((1 << EFX_PHY_LED_DEFAULT) |
1492 			    (1 << EFX_PHY_LED_OFF) |
1493 			    (1 << EFX_PHY_LED_ON));
1494 #endif	/* EFSYS_OPT_PHY_LED_CONTROL */
1495 
1496 	/* Get the media type of the fixed port, if recognised. */
1497 	EFX_STATIC_ASSERT(MC_CMD_MEDIA_XAUI == EFX_PHY_MEDIA_XAUI);
1498 	EFX_STATIC_ASSERT(MC_CMD_MEDIA_CX4 == EFX_PHY_MEDIA_CX4);
1499 	EFX_STATIC_ASSERT(MC_CMD_MEDIA_KX4 == EFX_PHY_MEDIA_KX4);
1500 	EFX_STATIC_ASSERT(MC_CMD_MEDIA_XFP == EFX_PHY_MEDIA_XFP);
1501 	EFX_STATIC_ASSERT(MC_CMD_MEDIA_SFP_PLUS == EFX_PHY_MEDIA_SFP_PLUS);
1502 	EFX_STATIC_ASSERT(MC_CMD_MEDIA_BASE_T == EFX_PHY_MEDIA_BASE_T);
1503 	EFX_STATIC_ASSERT(MC_CMD_MEDIA_QSFP_PLUS == EFX_PHY_MEDIA_QSFP_PLUS);
1504 	epp->ep_fixed_port_type =
1505 		MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_MEDIA_TYPE);
1506 	if (epp->ep_fixed_port_type >= EFX_PHY_MEDIA_NTYPES)
1507 		epp->ep_fixed_port_type = EFX_PHY_MEDIA_INVALID;
1508 
1509 	epp->ep_phy_cap_mask =
1510 		MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_SUPPORTED_CAP);
1511 #if EFSYS_OPT_PHY_FLAGS
1512 	encp->enc_phy_flags_mask = MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_FLAGS);
1513 #endif	/* EFSYS_OPT_PHY_FLAGS */
1514 
1515 	encp->enc_port = (uint8_t)MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_PRT);
1516 
1517 	/* Populate internal state */
1518 	encp->enc_mcdi_mdio_channel =
1519 		(uint8_t)MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_CHANNEL);
1520 
1521 #if EFSYS_OPT_PHY_STATS
1522 	encp->enc_mcdi_phy_stat_mask =
1523 		MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_STATS_MASK);
1524 #endif	/* EFSYS_OPT_PHY_STATS */
1525 
1526 #if EFSYS_OPT_BIST
1527 	encp->enc_bist_mask = 0;
1528 	if (MCDI_OUT_DWORD_FIELD(req, GET_PHY_CFG_OUT_FLAGS,
1529 	    GET_PHY_CFG_OUT_BIST_CABLE_SHORT))
1530 		encp->enc_bist_mask |= (1 << EFX_BIST_TYPE_PHY_CABLE_SHORT);
1531 	if (MCDI_OUT_DWORD_FIELD(req, GET_PHY_CFG_OUT_FLAGS,
1532 	    GET_PHY_CFG_OUT_BIST_CABLE_LONG))
1533 		encp->enc_bist_mask |= (1 << EFX_BIST_TYPE_PHY_CABLE_LONG);
1534 	if (MCDI_OUT_DWORD_FIELD(req, GET_PHY_CFG_OUT_FLAGS,
1535 	    GET_PHY_CFG_OUT_BIST))
1536 		encp->enc_bist_mask |= (1 << EFX_BIST_TYPE_PHY_NORMAL);
1537 #endif  /* EFSYS_OPT_BIST */
1538 
1539 	return (0);
1540 
1541 fail2:
1542 	EFSYS_PROBE(fail2);
1543 fail1:
1544 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1545 
1546 	return (rc);
1547 }
1548 
1549 	__checkReturn		efx_rc_t
1550 efx_mcdi_firmware_update_supported(
1551 	__in			efx_nic_t *enp,
1552 	__out			boolean_t *supportedp)
1553 {
1554 	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
1555 	efx_rc_t rc;
1556 
1557 	if (emcop != NULL) {
1558 		if ((rc = emcop->emco_feature_supported(enp,
1559 			    EFX_MCDI_FEATURE_FW_UPDATE, supportedp)) != 0)
1560 			goto fail1;
1561 	} else {
1562 		/* Earlier devices always supported updates */
1563 		*supportedp = B_TRUE;
1564 	}
1565 
1566 	return (0);
1567 
1568 fail1:
1569 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1570 
1571 	return (rc);
1572 }
1573 
1574 	__checkReturn		efx_rc_t
1575 efx_mcdi_macaddr_change_supported(
1576 	__in			efx_nic_t *enp,
1577 	__out			boolean_t *supportedp)
1578 {
1579 	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
1580 	efx_rc_t rc;
1581 
1582 	if (emcop != NULL) {
1583 		if ((rc = emcop->emco_feature_supported(enp,
1584 			    EFX_MCDI_FEATURE_MACADDR_CHANGE, supportedp)) != 0)
1585 			goto fail1;
1586 	} else {
1587 		/* Earlier devices always supported MAC changes */
1588 		*supportedp = B_TRUE;
1589 	}
1590 
1591 	return (0);
1592 
1593 fail1:
1594 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1595 
1596 	return (rc);
1597 }
1598 
1599 	__checkReturn		efx_rc_t
1600 efx_mcdi_link_control_supported(
1601 	__in			efx_nic_t *enp,
1602 	__out			boolean_t *supportedp)
1603 {
1604 	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
1605 	efx_rc_t rc;
1606 
1607 	if (emcop != NULL) {
1608 		if ((rc = emcop->emco_feature_supported(enp,
1609 			    EFX_MCDI_FEATURE_LINK_CONTROL, supportedp)) != 0)
1610 			goto fail1;
1611 	} else {
1612 		/* Earlier devices always supported link control */
1613 		*supportedp = B_TRUE;
1614 	}
1615 
1616 	return (0);
1617 
1618 fail1:
1619 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1620 
1621 	return (rc);
1622 }
1623 
1624 	__checkReturn		efx_rc_t
1625 efx_mcdi_mac_spoofing_supported(
1626 	__in			efx_nic_t *enp,
1627 	__out			boolean_t *supportedp)
1628 {
1629 	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
1630 	efx_rc_t rc;
1631 
1632 	if (emcop != NULL) {
1633 		if ((rc = emcop->emco_feature_supported(enp,
1634 			    EFX_MCDI_FEATURE_MAC_SPOOFING, supportedp)) != 0)
1635 			goto fail1;
1636 	} else {
1637 		/* Earlier devices always supported MAC spoofing */
1638 		*supportedp = B_TRUE;
1639 	}
1640 
1641 	return (0);
1642 
1643 fail1:
1644 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1645 
1646 	return (rc);
1647 }
1648 
1649 #if EFSYS_OPT_BIST
1650 
1651 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
1652 /*
1653  * Enter bist offline mode. This is a fw mode which puts the NIC into a state
1654  * where memory BIST tests can be run and not much else can interfere or happen.
1655  * A reboot is required to exit this mode.
1656  */
1657 	__checkReturn		efx_rc_t
1658 efx_mcdi_bist_enable_offline(
1659 	__in			efx_nic_t *enp)
1660 {
1661 	efx_mcdi_req_t req;
1662 	efx_rc_t rc;
1663 
1664 	EFX_STATIC_ASSERT(MC_CMD_ENABLE_OFFLINE_BIST_IN_LEN == 0);
1665 	EFX_STATIC_ASSERT(MC_CMD_ENABLE_OFFLINE_BIST_OUT_LEN == 0);
1666 
1667 	req.emr_cmd = MC_CMD_ENABLE_OFFLINE_BIST;
1668 	req.emr_in_buf = NULL;
1669 	req.emr_in_length = 0;
1670 	req.emr_out_buf = NULL;
1671 	req.emr_out_length = 0;
1672 
1673 	efx_mcdi_execute(enp, &req);
1674 
1675 	if (req.emr_rc != 0) {
1676 		rc = req.emr_rc;
1677 		goto fail1;
1678 	}
1679 
1680 	return (0);
1681 
1682 fail1:
1683 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1684 
1685 	return (rc);
1686 }
1687 #endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */
1688 
1689 	__checkReturn		efx_rc_t
1690 efx_mcdi_bist_start(
1691 	__in			efx_nic_t *enp,
1692 	__in			efx_bist_type_t type)
1693 {
1694 	efx_mcdi_req_t req;
1695 	uint8_t payload[MAX(MC_CMD_START_BIST_IN_LEN,
1696 			    MC_CMD_START_BIST_OUT_LEN)];
1697 	efx_rc_t rc;
1698 
1699 	(void) memset(payload, 0, sizeof (payload));
1700 	req.emr_cmd = MC_CMD_START_BIST;
1701 	req.emr_in_buf = payload;
1702 	req.emr_in_length = MC_CMD_START_BIST_IN_LEN;
1703 	req.emr_out_buf = payload;
1704 	req.emr_out_length = MC_CMD_START_BIST_OUT_LEN;
1705 
1706 	switch (type) {
1707 	case EFX_BIST_TYPE_PHY_NORMAL:
1708 		MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE, MC_CMD_PHY_BIST);
1709 		break;
1710 	case EFX_BIST_TYPE_PHY_CABLE_SHORT:
1711 		MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
1712 		    MC_CMD_PHY_BIST_CABLE_SHORT);
1713 		break;
1714 	case EFX_BIST_TYPE_PHY_CABLE_LONG:
1715 		MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
1716 		    MC_CMD_PHY_BIST_CABLE_LONG);
1717 		break;
1718 	case EFX_BIST_TYPE_MC_MEM:
1719 		MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
1720 		    MC_CMD_MC_MEM_BIST);
1721 		break;
1722 	case EFX_BIST_TYPE_SAT_MEM:
1723 		MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
1724 		    MC_CMD_PORT_MEM_BIST);
1725 		break;
1726 	case EFX_BIST_TYPE_REG:
1727 		MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
1728 		    MC_CMD_REG_BIST);
1729 		break;
1730 	default:
1731 		EFSYS_ASSERT(0);
1732 	}
1733 
1734 	efx_mcdi_execute(enp, &req);
1735 
1736 	if (req.emr_rc != 0) {
1737 		rc = req.emr_rc;
1738 		goto fail1;
1739 	}
1740 
1741 	return (0);
1742 
1743 fail1:
1744 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1745 
1746 	return (rc);
1747 }
1748 
1749 #endif /* EFSYS_OPT_BIST */
1750 
1751 
1752 /* Enable logging of some events (e.g. link state changes) */
1753 	__checkReturn	efx_rc_t
1754 efx_mcdi_log_ctrl(
1755 	__in		efx_nic_t *enp)
1756 {
1757 	efx_mcdi_req_t req;
1758 	uint8_t payload[MAX(MC_CMD_LOG_CTRL_IN_LEN,
1759 			    MC_CMD_LOG_CTRL_OUT_LEN)];
1760 	efx_rc_t rc;
1761 
1762 	(void) memset(payload, 0, sizeof (payload));
1763 	req.emr_cmd = MC_CMD_LOG_CTRL;
1764 	req.emr_in_buf = payload;
1765 	req.emr_in_length = MC_CMD_LOG_CTRL_IN_LEN;
1766 	req.emr_out_buf = payload;
1767 	req.emr_out_length = MC_CMD_LOG_CTRL_OUT_LEN;
1768 
1769 	MCDI_IN_SET_DWORD(req, LOG_CTRL_IN_LOG_DEST,
1770 		    MC_CMD_LOG_CTRL_IN_LOG_DEST_EVQ);
1771 	MCDI_IN_SET_DWORD(req, LOG_CTRL_IN_LOG_DEST_EVQ, 0);
1772 
1773 	efx_mcdi_execute(enp, &req);
1774 
1775 	if (req.emr_rc != 0) {
1776 		rc = req.emr_rc;
1777 		goto fail1;
1778 	}
1779 
1780 	return (0);
1781 
1782 fail1:
1783 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1784 
1785 	return (rc);
1786 }
1787 
1788 
1789 #if EFSYS_OPT_MAC_STATS
1790 
1791 typedef enum efx_stats_action_e {
1792 	EFX_STATS_CLEAR,
1793 	EFX_STATS_UPLOAD,
1794 	EFX_STATS_ENABLE_NOEVENTS,
1795 	EFX_STATS_ENABLE_EVENTS,
1796 	EFX_STATS_DISABLE,
1797 } efx_stats_action_t;
1798 
1799 static	__checkReturn	efx_rc_t
1800 efx_mcdi_mac_stats(
1801 	__in		efx_nic_t *enp,
1802 	__in_opt	efsys_mem_t *esmp,
1803 	__in		efx_stats_action_t action,
1804 	__in		uint16_t period_ms)
1805 {
1806 	efx_mcdi_req_t req;
1807 	uint8_t payload[MAX(MC_CMD_MAC_STATS_IN_LEN,
1808 			    MC_CMD_MAC_STATS_OUT_DMA_LEN)];
1809 	int clear = (action == EFX_STATS_CLEAR);
1810 	int upload = (action == EFX_STATS_UPLOAD);
1811 	int enable = (action == EFX_STATS_ENABLE_NOEVENTS);
1812 	int events = (action == EFX_STATS_ENABLE_EVENTS);
1813 	int disable = (action == EFX_STATS_DISABLE);
1814 	efx_rc_t rc;
1815 
1816 	(void) memset(payload, 0, sizeof (payload));
1817 	req.emr_cmd = MC_CMD_MAC_STATS;
1818 	req.emr_in_buf = payload;
1819 	req.emr_in_length = MC_CMD_MAC_STATS_IN_LEN;
1820 	req.emr_out_buf = payload;
1821 	req.emr_out_length = MC_CMD_MAC_STATS_OUT_DMA_LEN;
1822 
1823 	MCDI_IN_POPULATE_DWORD_6(req, MAC_STATS_IN_CMD,
1824 	    MAC_STATS_IN_DMA, upload,
1825 	    MAC_STATS_IN_CLEAR, clear,
1826 	    MAC_STATS_IN_PERIODIC_CHANGE, enable | events | disable,
1827 	    MAC_STATS_IN_PERIODIC_ENABLE, enable | events,
1828 	    MAC_STATS_IN_PERIODIC_NOEVENT, !events,
1829 	    MAC_STATS_IN_PERIOD_MS, (enable | events) ? period_ms : 0);
1830 
1831 	if (esmp != NULL) {
1832 		int bytes = MC_CMD_MAC_NSTATS * sizeof (uint64_t);
1833 
1834 		EFX_STATIC_ASSERT(MC_CMD_MAC_NSTATS * sizeof (uint64_t) <=
1835 		    EFX_MAC_STATS_SIZE);
1836 
1837 		MCDI_IN_SET_DWORD(req, MAC_STATS_IN_DMA_ADDR_LO,
1838 			    EFSYS_MEM_ADDR(esmp) & 0xffffffff);
1839 		MCDI_IN_SET_DWORD(req, MAC_STATS_IN_DMA_ADDR_HI,
1840 			    EFSYS_MEM_ADDR(esmp) >> 32);
1841 		MCDI_IN_SET_DWORD(req, MAC_STATS_IN_DMA_LEN, bytes);
1842 	} else {
1843 		EFSYS_ASSERT(!upload && !enable && !events);
1844 	}
1845 
1846 	/*
1847 	 * NOTE: Do not use EVB_PORT_ID_ASSIGNED when disabling periodic stats,
1848 	 *	 as this may fail (and leave periodic DMA enabled) if the
1849 	 *	 vadapter has already been deleted.
1850 	 */
1851 	MCDI_IN_SET_DWORD(req, MAC_STATS_IN_PORT_ID,
1852 	    (disable ? EVB_PORT_ID_NULL : enp->en_vport_id));
1853 
1854 	efx_mcdi_execute(enp, &req);
1855 
1856 	if (req.emr_rc != 0) {
1857 		/* EF10: Expect ENOENT if no DMA queues are initialised */
1858 		if ((req.emr_rc != ENOENT) ||
1859 		    (enp->en_rx_qcount + enp->en_tx_qcount != 0)) {
1860 			rc = req.emr_rc;
1861 			goto fail1;
1862 		}
1863 	}
1864 
1865 	return (0);
1866 
1867 fail1:
1868 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1869 
1870 	return (rc);
1871 }
1872 
1873 	__checkReturn	efx_rc_t
1874 efx_mcdi_mac_stats_clear(
1875 	__in		efx_nic_t *enp)
1876 {
1877 	efx_rc_t rc;
1878 
1879 	if ((rc = efx_mcdi_mac_stats(enp, NULL, EFX_STATS_CLEAR, 0)) != 0)
1880 		goto fail1;
1881 
1882 	return (0);
1883 
1884 fail1:
1885 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1886 
1887 	return (rc);
1888 }
1889 
1890 	__checkReturn	efx_rc_t
1891 efx_mcdi_mac_stats_upload(
1892 	__in		efx_nic_t *enp,
1893 	__in		efsys_mem_t *esmp)
1894 {
1895 	efx_rc_t rc;
1896 
1897 	/*
1898 	 * The MC DMAs aggregate statistics for our convenience, so we can
1899 	 * avoid having to pull the statistics buffer into the cache to
1900 	 * maintain cumulative statistics.
1901 	 */
1902 	if ((rc = efx_mcdi_mac_stats(enp, esmp, EFX_STATS_UPLOAD, 0)) != 0)
1903 		goto fail1;
1904 
1905 	return (0);
1906 
1907 fail1:
1908 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1909 
1910 	return (rc);
1911 }
1912 
1913 	__checkReturn	efx_rc_t
1914 efx_mcdi_mac_stats_periodic(
1915 	__in		efx_nic_t *enp,
1916 	__in		efsys_mem_t *esmp,
1917 	__in		uint16_t period_ms,
1918 	__in		boolean_t events)
1919 {
1920 	efx_rc_t rc;
1921 
1922 	/*
1923 	 * The MC DMAs aggregate statistics for our convenience, so we can
1924 	 * avoid having to pull the statistics buffer into the cache to
1925 	 * maintain cumulative statistics.
1926 	 * Huntington uses a fixed 1sec period.
1927 	 * Medford uses a fixed 1sec period before v6.2.1.1033 firmware.
1928 	 */
1929 	if (period_ms == 0)
1930 		rc = efx_mcdi_mac_stats(enp, NULL, EFX_STATS_DISABLE, 0);
1931 	else if (events)
1932 		rc = efx_mcdi_mac_stats(enp, esmp, EFX_STATS_ENABLE_EVENTS,
1933 		    period_ms);
1934 	else
1935 		rc = efx_mcdi_mac_stats(enp, esmp, EFX_STATS_ENABLE_NOEVENTS,
1936 		    period_ms);
1937 
1938 	if (rc != 0)
1939 		goto fail1;
1940 
1941 	return (0);
1942 
1943 fail1:
1944 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1945 
1946 	return (rc);
1947 }
1948 
1949 #endif	/* EFSYS_OPT_MAC_STATS */
1950 
1951 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
1952 
1953 /*
1954  * This function returns the pf and vf number of a function.  If it is a pf the
1955  * vf number is 0xffff.  The vf number is the index of the vf on that
1956  * function. So if you have 3 vfs on pf 0 the 3 vfs will return (pf=0,vf=0),
1957  * (pf=0,vf=1), (pf=0,vf=2) aand the pf will return (pf=0, vf=0xffff).
1958  */
1959 	__checkReturn		efx_rc_t
1960 efx_mcdi_get_function_info(
1961 	__in			efx_nic_t *enp,
1962 	__out			uint32_t *pfp,
1963 	__out_opt		uint32_t *vfp)
1964 {
1965 	efx_mcdi_req_t req;
1966 	uint8_t payload[MAX(MC_CMD_GET_FUNCTION_INFO_IN_LEN,
1967 			    MC_CMD_GET_FUNCTION_INFO_OUT_LEN)];
1968 	efx_rc_t rc;
1969 
1970 	(void) memset(payload, 0, sizeof (payload));
1971 	req.emr_cmd = MC_CMD_GET_FUNCTION_INFO;
1972 	req.emr_in_buf = payload;
1973 	req.emr_in_length = MC_CMD_GET_FUNCTION_INFO_IN_LEN;
1974 	req.emr_out_buf = payload;
1975 	req.emr_out_length = MC_CMD_GET_FUNCTION_INFO_OUT_LEN;
1976 
1977 	efx_mcdi_execute(enp, &req);
1978 
1979 	if (req.emr_rc != 0) {
1980 		rc = req.emr_rc;
1981 		goto fail1;
1982 	}
1983 
1984 	if (req.emr_out_length_used < MC_CMD_GET_FUNCTION_INFO_OUT_LEN) {
1985 		rc = EMSGSIZE;
1986 		goto fail2;
1987 	}
1988 
1989 	*pfp = MCDI_OUT_DWORD(req, GET_FUNCTION_INFO_OUT_PF);
1990 	if (vfp != NULL)
1991 		*vfp = MCDI_OUT_DWORD(req, GET_FUNCTION_INFO_OUT_VF);
1992 
1993 	return (0);
1994 
1995 fail2:
1996 	EFSYS_PROBE(fail2);
1997 fail1:
1998 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1999 
2000 	return (rc);
2001 }
2002 
2003 	__checkReturn		efx_rc_t
2004 efx_mcdi_privilege_mask(
2005 	__in			efx_nic_t *enp,
2006 	__in			uint32_t pf,
2007 	__in			uint32_t vf,
2008 	__out			uint32_t *maskp)
2009 {
2010 	efx_mcdi_req_t req;
2011 	uint8_t payload[MAX(MC_CMD_PRIVILEGE_MASK_IN_LEN,
2012 			    MC_CMD_PRIVILEGE_MASK_OUT_LEN)];
2013 	efx_rc_t rc;
2014 
2015 	(void) memset(payload, 0, sizeof (payload));
2016 	req.emr_cmd = MC_CMD_PRIVILEGE_MASK;
2017 	req.emr_in_buf = payload;
2018 	req.emr_in_length = MC_CMD_PRIVILEGE_MASK_IN_LEN;
2019 	req.emr_out_buf = payload;
2020 	req.emr_out_length = MC_CMD_PRIVILEGE_MASK_OUT_LEN;
2021 
2022 	MCDI_IN_POPULATE_DWORD_2(req, PRIVILEGE_MASK_IN_FUNCTION,
2023 	    PRIVILEGE_MASK_IN_FUNCTION_PF, pf,
2024 	    PRIVILEGE_MASK_IN_FUNCTION_VF, vf);
2025 
2026 	efx_mcdi_execute(enp, &req);
2027 
2028 	if (req.emr_rc != 0) {
2029 		rc = req.emr_rc;
2030 		goto fail1;
2031 	}
2032 
2033 	if (req.emr_out_length_used < MC_CMD_PRIVILEGE_MASK_OUT_LEN) {
2034 		rc = EMSGSIZE;
2035 		goto fail2;
2036 	}
2037 
2038 	*maskp = MCDI_OUT_DWORD(req, PRIVILEGE_MASK_OUT_OLD_MASK);
2039 
2040 	return (0);
2041 
2042 fail2:
2043 	EFSYS_PROBE(fail2);
2044 fail1:
2045 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2046 
2047 	return (rc);
2048 }
2049 
2050 #endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */
2051 
2052 	__checkReturn		efx_rc_t
2053 efx_mcdi_set_workaround(
2054 	__in			efx_nic_t *enp,
2055 	__in			uint32_t type,
2056 	__in			boolean_t enabled,
2057 	__out_opt		uint32_t *flagsp)
2058 {
2059 	efx_mcdi_req_t req;
2060 	uint8_t payload[MAX(MC_CMD_WORKAROUND_IN_LEN,
2061 			    MC_CMD_WORKAROUND_EXT_OUT_LEN)];
2062 	efx_rc_t rc;
2063 
2064 	(void) memset(payload, 0, sizeof (payload));
2065 	req.emr_cmd = MC_CMD_WORKAROUND;
2066 	req.emr_in_buf = payload;
2067 	req.emr_in_length = MC_CMD_WORKAROUND_IN_LEN;
2068 	req.emr_out_buf = payload;
2069 	req.emr_out_length = MC_CMD_WORKAROUND_OUT_LEN;
2070 
2071 	MCDI_IN_SET_DWORD(req, WORKAROUND_IN_TYPE, type);
2072 	MCDI_IN_SET_DWORD(req, WORKAROUND_IN_ENABLED, enabled ? 1 : 0);
2073 
2074 	efx_mcdi_execute_quiet(enp, &req);
2075 
2076 	if (req.emr_rc != 0) {
2077 		rc = req.emr_rc;
2078 		goto fail1;
2079 	}
2080 
2081 	if (flagsp != NULL) {
2082 		if (req.emr_out_length_used >= MC_CMD_WORKAROUND_EXT_OUT_LEN)
2083 			*flagsp = MCDI_OUT_DWORD(req, WORKAROUND_EXT_OUT_FLAGS);
2084 		else
2085 			*flagsp = 0;
2086 	}
2087 
2088 	return (0);
2089 
2090 fail1:
2091 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2092 
2093 	return (rc);
2094 }
2095 
2096 
2097 	__checkReturn		efx_rc_t
2098 efx_mcdi_get_workarounds(
2099 	__in			efx_nic_t *enp,
2100 	__out_opt		uint32_t *implementedp,
2101 	__out_opt		uint32_t *enabledp)
2102 {
2103 	efx_mcdi_req_t req;
2104 	uint8_t payload[MC_CMD_GET_WORKAROUNDS_OUT_LEN];
2105 	efx_rc_t rc;
2106 
2107 	(void) memset(payload, 0, sizeof (payload));
2108 	req.emr_cmd = MC_CMD_GET_WORKAROUNDS;
2109 	req.emr_in_buf = NULL;
2110 	req.emr_in_length = 0;
2111 	req.emr_out_buf = payload;
2112 	req.emr_out_length = MC_CMD_GET_WORKAROUNDS_OUT_LEN;
2113 
2114 	efx_mcdi_execute(enp, &req);
2115 
2116 	if (req.emr_rc != 0) {
2117 		rc = req.emr_rc;
2118 		goto fail1;
2119 	}
2120 
2121 	if (implementedp != NULL) {
2122 		*implementedp =
2123 		    MCDI_OUT_DWORD(req, GET_WORKAROUNDS_OUT_IMPLEMENTED);
2124 	}
2125 
2126 	if (enabledp != NULL) {
2127 		*enabledp = MCDI_OUT_DWORD(req, GET_WORKAROUNDS_OUT_ENABLED);
2128 	}
2129 
2130 	return (0);
2131 
2132 fail1:
2133 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2134 
2135 	return (rc);
2136 }
2137 
2138 /*
2139  * Size of media information page in accordance with SFF-8472 and SFF-8436.
2140  * It is used in MCDI interface as well.
2141  */
2142 #define	EFX_PHY_MEDIA_INFO_PAGE_SIZE		0x80
2143 
2144 static	__checkReturn		efx_rc_t
2145 efx_mcdi_get_phy_media_info(
2146 	__in			efx_nic_t *enp,
2147 	__in			uint32_t mcdi_page,
2148 	__in			uint8_t offset,
2149 	__in			uint8_t len,
2150 	__out_bcount(len)	uint8_t *data)
2151 {
2152 	efx_mcdi_req_t req;
2153 	uint8_t payload[MAX(MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN,
2154 			    MC_CMD_GET_PHY_MEDIA_INFO_OUT_LEN(
2155 				EFX_PHY_MEDIA_INFO_PAGE_SIZE))];
2156 	efx_rc_t rc;
2157 
2158 	EFSYS_ASSERT((uint32_t)offset + len <= EFX_PHY_MEDIA_INFO_PAGE_SIZE);
2159 
2160 	(void) memset(payload, 0, sizeof (payload));
2161 	req.emr_cmd = MC_CMD_GET_PHY_MEDIA_INFO;
2162 	req.emr_in_buf = payload;
2163 	req.emr_in_length = MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN;
2164 	req.emr_out_buf = payload;
2165 	req.emr_out_length =
2166 	    MC_CMD_GET_PHY_MEDIA_INFO_OUT_LEN(EFX_PHY_MEDIA_INFO_PAGE_SIZE);
2167 
2168 	MCDI_IN_SET_DWORD(req, GET_PHY_MEDIA_INFO_IN_PAGE, mcdi_page);
2169 
2170 	efx_mcdi_execute(enp, &req);
2171 
2172 	if (req.emr_rc != 0) {
2173 		rc = req.emr_rc;
2174 		goto fail1;
2175 	}
2176 
2177 	if (req.emr_out_length_used !=
2178 	    MC_CMD_GET_PHY_MEDIA_INFO_OUT_LEN(EFX_PHY_MEDIA_INFO_PAGE_SIZE)) {
2179 		rc = EMSGSIZE;
2180 		goto fail2;
2181 	}
2182 
2183 	if (MCDI_OUT_DWORD(req, GET_PHY_MEDIA_INFO_OUT_DATALEN) !=
2184 	    EFX_PHY_MEDIA_INFO_PAGE_SIZE) {
2185 		rc = EIO;
2186 		goto fail3;
2187 	}
2188 
2189 	memcpy(data,
2190 	    MCDI_OUT2(req, uint8_t, GET_PHY_MEDIA_INFO_OUT_DATA) + offset,
2191 	    len);
2192 
2193 	return (0);
2194 
2195 fail3:
2196 	EFSYS_PROBE(fail3);
2197 fail2:
2198 	EFSYS_PROBE(fail2);
2199 fail1:
2200 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2201 
2202 	return (rc);
2203 }
2204 
2205 /*
2206  * 2-wire device address of the base information in accordance with SFF-8472
2207  * Diagnostic Monitoring Interface for Optical Transceivers section
2208  * 4 Memory Organization.
2209  */
2210 #define	EFX_PHY_MEDIA_INFO_DEV_ADDR_SFP_BASE	0xA0
2211 
2212 /*
2213  * 2-wire device address of the digital diagnostics monitoring interface
2214  * in accordance with SFF-8472 Diagnostic Monitoring Interface for Optical
2215  * Transceivers section 4 Memory Organization.
2216  */
2217 #define	EFX_PHY_MEDIA_INFO_DEV_ADDR_SFP_DDM	0xA2
2218 
2219 /*
2220  * Hard wired 2-wire device address for QSFP+ in accordance with SFF-8436
2221  * QSFP+ 10 Gbs 4X PLUGGABLE TRANSCEIVER section 7.4 Device Addressing and
2222  * Operation.
2223  */
2224 #define	EFX_PHY_MEDIA_INFO_DEV_ADDR_QSFP	0xA0
2225 
2226 	__checkReturn		efx_rc_t
2227 efx_mcdi_phy_module_get_info(
2228 	__in			efx_nic_t *enp,
2229 	__in			uint8_t dev_addr,
2230 	__in			uint8_t offset,
2231 	__in			uint8_t len,
2232 	__out_bcount(len)	uint8_t *data)
2233 {
2234 	efx_port_t *epp = &(enp->en_port);
2235 	efx_rc_t rc;
2236 	uint32_t mcdi_lower_page;
2237 	uint32_t mcdi_upper_page;
2238 
2239 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
2240 
2241 	/*
2242 	 * Map device address to MC_CMD_GET_PHY_MEDIA_INFO pages.
2243 	 * Offset plus length interface allows to access page 0 only.
2244 	 * I.e. non-zero upper pages are not accessible.
2245 	 * See SFF-8472 section 4 Memory Organization and SFF-8436 section 7.6
2246 	 * QSFP+ Memory Map for details on how information is structured
2247 	 * and accessible.
2248 	 */
2249 	switch (epp->ep_fixed_port_type) {
2250 	case EFX_PHY_MEDIA_SFP_PLUS:
2251 		/*
2252 		 * In accordance with SFF-8472 Diagnostic Monitoring
2253 		 * Interface for Optical Transceivers section 4 Memory
2254 		 * Organization two 2-wire addresses are defined.
2255 		 */
2256 		switch (dev_addr) {
2257 		/* Base information */
2258 		case EFX_PHY_MEDIA_INFO_DEV_ADDR_SFP_BASE:
2259 			/*
2260 			 * MCDI page 0 should be used to access lower
2261 			 * page 0 (0x00 - 0x7f) at the device address 0xA0.
2262 			 */
2263 			mcdi_lower_page = 0;
2264 			/*
2265 			 * MCDI page 1 should be used to access  upper
2266 			 * page 0 (0x80 - 0xff) at the device address 0xA0.
2267 			 */
2268 			mcdi_upper_page = 1;
2269 			break;
2270 		/* Diagnostics */
2271 		case EFX_PHY_MEDIA_INFO_DEV_ADDR_SFP_DDM:
2272 			/*
2273 			 * MCDI page 2 should be used to access lower
2274 			 * page 0 (0x00 - 0x7f) at the device address 0xA2.
2275 			 */
2276 			mcdi_lower_page = 2;
2277 			/*
2278 			 * MCDI page 3 should be used to access upper
2279 			 * page 0 (0x80 - 0xff) at the device address 0xA2.
2280 			 */
2281 			mcdi_upper_page = 3;
2282 			break;
2283 		default:
2284 			rc = ENOTSUP;
2285 			goto fail1;
2286 		}
2287 		break;
2288 	case EFX_PHY_MEDIA_QSFP_PLUS:
2289 		switch (dev_addr) {
2290 		case EFX_PHY_MEDIA_INFO_DEV_ADDR_QSFP:
2291 			/*
2292 			 * MCDI page -1 should be used to access lower page 0
2293 			 * (0x00 - 0x7f).
2294 			 */
2295 			mcdi_lower_page = (uint32_t)-1;
2296 			/*
2297 			 * MCDI page 0 should be used to access upper page 0
2298 			 * (0x80h - 0xff).
2299 			 */
2300 			mcdi_upper_page = 0;
2301 			break;
2302 		default:
2303 			rc = ENOTSUP;
2304 			goto fail1;
2305 		}
2306 		break;
2307 	default:
2308 		rc = ENOTSUP;
2309 		goto fail1;
2310 	}
2311 
2312 	if (offset < EFX_PHY_MEDIA_INFO_PAGE_SIZE) {
2313 		uint8_t read_len =
2314 		    MIN(len, EFX_PHY_MEDIA_INFO_PAGE_SIZE - offset);
2315 
2316 		rc = efx_mcdi_get_phy_media_info(enp,
2317 		    mcdi_lower_page, offset, read_len, data);
2318 		if (rc != 0)
2319 			goto fail2;
2320 
2321 		data += read_len;
2322 		len -= read_len;
2323 
2324 		offset = 0;
2325 	} else {
2326 		offset -= EFX_PHY_MEDIA_INFO_PAGE_SIZE;
2327 	}
2328 
2329 	if (len > 0) {
2330 		EFSYS_ASSERT3U(len, <=, EFX_PHY_MEDIA_INFO_PAGE_SIZE);
2331 		EFSYS_ASSERT3U(offset, <, EFX_PHY_MEDIA_INFO_PAGE_SIZE);
2332 
2333 		rc = efx_mcdi_get_phy_media_info(enp,
2334 		    mcdi_upper_page, offset, len, data);
2335 		if (rc != 0)
2336 			goto fail3;
2337 	}
2338 
2339 	return (0);
2340 
2341 fail3:
2342 	EFSYS_PROBE(fail3);
2343 fail2:
2344 	EFSYS_PROBE(fail2);
2345 fail1:
2346 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2347 
2348 	return (rc);
2349 }
2350 
2351 #endif	/* EFSYS_OPT_MCDI */
2352