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