xref: /freebsd/sys/dev/sfxge/common/efx_mcdi.c (revision 7661de35d15f582ab33e3bd6b8d909601557e436)
1 /*-
2  * Copyright 2008-2009 Solarflare Communications Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28 
29 #include "efsys.h"
30 #include "efx.h"
31 #include "efx_types.h"
32 #include "efx_regs.h"
33 #include "efx_regs_mcdi.h"
34 #include "efx_impl.h"
35 
36 #if EFSYS_OPT_MCDI
37 
38 /* Shared memory layout */
39 
40 #define	MCDI_P1_DBL_OFST	0x0
41 #define	MCDI_P2_DBL_OFST	0x1
42 #define	MCDI_P1_PDU_OFST	0x2
43 #define	MCDI_P2_PDU_OFST	0x42
44 #define	MCDI_P1_REBOOT_OFST	0x1fe
45 #define	MCDI_P2_REBOOT_OFST	0x1ff
46 
47 /* A reboot/assertion causes the MCDI status word to be set after the
48  * command word is set or a REBOOT event is sent. If we notice a reboot
49  * via these mechanisms then wait 10ms for the status word to be set.
50  */
51 #define	MCDI_STATUS_SLEEP_US	10000
52 
53 			void
54 efx_mcdi_request_start(
55 	__in		efx_nic_t *enp,
56 	__in		efx_mcdi_req_t *emrp,
57 	__in		boolean_t ev_cpl)
58 {
59 	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
60 	efx_dword_t dword;
61 	unsigned int seq;
62 	unsigned int xflags;
63 	unsigned int pdur;
64 	unsigned int dbr;
65 	unsigned int pos;
66 	int state;
67 
68 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
69 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
70 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
71 
72 	switch (emip->emi_port)	{
73 	case 1:
74 		pdur = MCDI_P1_PDU_OFST;
75 		dbr = MCDI_P1_DBL_OFST;
76 		break;
77 	case 2:
78 		pdur = MCDI_P2_PDU_OFST;
79 		dbr = MCDI_P2_DBL_OFST;
80 		break;
81 	default:
82 		EFSYS_ASSERT(0);
83 		pdur = dbr = 0;
84 	};
85 
86 	/*
87 	 * efx_mcdi_request_start() is naturally serialised against both
88 	 * efx_mcdi_request_poll() and efx_mcdi_ev_cpl()/efx_mcdi_ev_death(),
89 	 * by virtue of there only being one outstanding MCDI request.
90 	 * Unfortunately, upper layers may also call efx_mcdi_request_abort()
91 	 * at any time, to timeout a pending mcdi request, That request may
92 	 * then subsequently complete, meaning efx_mcdi_ev_cpl() or
93 	 * efx_mcdi_ev_death() may end up running in parallel with
94 	 * efx_mcdi_request_start(). This race is handled by ensuring that
95 	 * %emi_pending_req, %emi_ev_cpl and %emi_seq are protected by the
96 	 * en_eslp lock.
97 	 */
98 	EFSYS_LOCK(enp->en_eslp, state);
99 	EFSYS_ASSERT(emip->emi_pending_req == NULL);
100 	emip->emi_pending_req = emrp;
101 	emip->emi_ev_cpl = ev_cpl;
102 	emip->emi_poll_cnt = 0;
103 	seq = emip->emi_seq++ & 0xf;
104 	EFSYS_UNLOCK(enp->en_eslp, state);
105 
106 	xflags = 0;
107 	if (ev_cpl)
108 		xflags |= MCDI_HEADER_XFLAGS_EVREQ;
109 
110 	/* Construct the header in shared memory */
111 	EFX_POPULATE_DWORD_6(dword,
112 			    MCDI_HEADER_CODE, emrp->emr_cmd,
113 			    MCDI_HEADER_RESYNC, 1,
114 			    MCDI_HEADER_DATALEN, emrp->emr_in_length,
115 			    MCDI_HEADER_SEQ, seq,
116 			    MCDI_HEADER_RESPONSE, 0,
117 			    MCDI_HEADER_XFLAGS, xflags);
118 	EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, pdur, &dword, B_TRUE);
119 
120 	for (pos = 0; pos < emrp->emr_in_length; pos += sizeof (efx_dword_t)) {
121 		memcpy(&dword, MCDI_IN(*emrp, efx_dword_t, pos),
122 		    MIN(sizeof (dword), emrp->emr_in_length - pos));
123 		EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM,
124 		    pdur + 1 + (pos >> 2), &dword, B_FALSE);
125 	}
126 
127 	/* Ring the doorbell */
128 	EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 0xd004be11);
129 	EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, dbr, &dword, B_FALSE);
130 }
131 
132 static			void
133 efx_mcdi_request_copyout(
134 	__in		efx_nic_t *enp,
135 	__in		efx_mcdi_req_t *emrp)
136 {
137 	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
138 	unsigned int pos;
139 	unsigned int pdur;
140 	efx_dword_t data;
141 
142 	pdur = (emip->emi_port == 1) ? MCDI_P1_PDU_OFST : MCDI_P2_PDU_OFST;
143 
144 	/* Copy payload out if caller supplied buffer */
145 	if (emrp->emr_out_buf != NULL) {
146 		size_t bytes = MIN(emrp->emr_out_length_used,
147 				    emrp->emr_out_length);
148 		for (pos = 0; pos < bytes; pos += sizeof (efx_dword_t)) {
149 			EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM,
150 			    pdur + 1 + (pos >> 2), &data, B_FALSE);
151 			memcpy(MCDI_OUT(*emrp, efx_dword_t, pos), &data,
152 			    MIN(sizeof (data), bytes - pos));
153 		}
154 	}
155 }
156 
157 static			int
158 efx_mcdi_request_errcode(
159 	__in		unsigned int err)
160 {
161 
162 	switch (err) {
163 	case MC_CMD_ERR_ENOENT:
164 		return (ENOENT);
165 	case MC_CMD_ERR_EINTR:
166 		return (EINTR);
167 	case MC_CMD_ERR_EACCES:
168 		return (EACCES);
169 	case MC_CMD_ERR_EBUSY:
170 		return (EBUSY);
171 	case MC_CMD_ERR_EINVAL:
172 		return (EINVAL);
173 	case MC_CMD_ERR_EDEADLK:
174 		return (EDEADLK);
175 	case MC_CMD_ERR_ENOSYS:
176 		return (ENOTSUP);
177 	case MC_CMD_ERR_ETIME:
178 		return (ETIMEDOUT);
179 #ifdef WITH_MCDI_V2
180 	case MC_CMD_ERR_EAGAIN:
181 		return (EAGAIN);
182 	case MC_CMD_ERR_ENOSPC:
183 		return (ENOSPC);
184 #endif
185 	default:
186 		EFSYS_PROBE1(mc_pcol_error, int, err);
187 		return (EIO);
188 	}
189 }
190 
191 static			void
192 efx_mcdi_raise_exception(
193 	__in		efx_nic_t *enp,
194 	__in_opt	efx_mcdi_req_t *emrp,
195 	__in		int rc)
196 {
197 	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
198 	const efx_mcdi_transport_t *emtp = emip->emi_mtp;
199 	efx_mcdi_exception_t exception;
200 
201 	/* Reboot or Assertion failure only */
202 	EFSYS_ASSERT(rc == EIO || rc == EINTR);
203 
204 	/*
205 	 * If MC_CMD_REBOOT causes a reboot (dependent on parameters),
206 	 * then the EIO is not worthy of an exception.
207 	 */
208 	if (emrp != NULL && emrp->emr_cmd == MC_CMD_REBOOT && rc == EIO)
209 		return;
210 
211 	exception = (rc == EIO)
212 		? EFX_MCDI_EXCEPTION_MC_REBOOT
213 		: EFX_MCDI_EXCEPTION_MC_BADASSERT;
214 
215 	emtp->emt_exception(emtp->emt_context, exception);
216 }
217 
218 static			int
219 efx_mcdi_poll_reboot(
220 	__in		efx_nic_t *enp)
221 {
222 	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
223 	unsigned int rebootr;
224 	efx_dword_t dword;
225 	uint32_t value;
226 
227 	EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
228 	rebootr = ((emip->emi_port == 1)
229 	    ? MCDI_P1_REBOOT_OFST
230 	    : MCDI_P2_REBOOT_OFST);
231 
232 	EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
233 	value = EFX_DWORD_FIELD(dword, EFX_DWORD_0);
234 
235 	if (value == 0)
236 		return (0);
237 
238 	EFX_ZERO_DWORD(dword);
239 	EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
240 
241 	if (value == MC_STATUS_DWORD_ASSERT)
242 		return (EINTR);
243 	else
244 		return (EIO);
245 }
246 
247 	__checkReturn	boolean_t
248 efx_mcdi_request_poll(
249 	__in		efx_nic_t *enp)
250 {
251 	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
252 	efx_mcdi_req_t *emrp;
253 	efx_dword_t dword;
254 	unsigned int pdur;
255 	unsigned int seq;
256 	unsigned int length;
257 	int state;
258 	int rc;
259 
260 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
261 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
262 	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
263 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
264 
265 	/* Serialise against post-watchdog efx_mcdi_ev* */
266 	EFSYS_LOCK(enp->en_eslp, state);
267 
268 	EFSYS_ASSERT(emip->emi_pending_req != NULL);
269 	EFSYS_ASSERT(!emip->emi_ev_cpl);
270 	emrp = emip->emi_pending_req;
271 
272 	/* Check for reboot atomically w.r.t efx_mcdi_request_start */
273 	if (emip->emi_poll_cnt++ == 0) {
274 		if ((rc = efx_mcdi_poll_reboot(enp)) != 0) {
275 			emip->emi_pending_req = NULL;
276 			EFSYS_UNLOCK(enp->en_eslp, state);
277 
278 			goto fail1;
279 		}
280 	}
281 
282 	EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
283 	pdur = (emip->emi_port == 1) ? MCDI_P1_PDU_OFST : MCDI_P2_PDU_OFST;
284 
285 	/* Read the command header */
286 	EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, pdur, &dword, B_FALSE);
287 	if (EFX_DWORD_FIELD(dword, MCDI_HEADER_RESPONSE) == 0) {
288 		EFSYS_UNLOCK(enp->en_eslp, state);
289 		return (B_FALSE);
290 	}
291 
292 	/* Request complete */
293 	emip->emi_pending_req = NULL;
294 	seq = (emip->emi_seq - 1) & 0xf;
295 
296 	/* Check for synchronous reboot */
297 	if (EFX_DWORD_FIELD(dword, MCDI_HEADER_ERROR) != 0 &&
298 	    EFX_DWORD_FIELD(dword, MCDI_HEADER_DATALEN) == 0) {
299 		/* Consume status word */
300 		EFSYS_SPIN(MCDI_STATUS_SLEEP_US);
301 		efx_mcdi_poll_reboot(enp);
302 		EFSYS_UNLOCK(enp->en_eslp, state);
303 		rc = EIO;
304 		goto fail2;
305 	}
306 
307 	EFSYS_UNLOCK(enp->en_eslp, state);
308 
309 	/* Check that the returned data is consistent */
310 	if (EFX_DWORD_FIELD(dword, MCDI_HEADER_CODE) != emrp->emr_cmd ||
311 	    EFX_DWORD_FIELD(dword, MCDI_HEADER_SEQ) != seq) {
312 		/* Response is for a different request */
313 		rc = EIO;
314 		goto fail3;
315 	}
316 
317 	length = EFX_DWORD_FIELD(dword, MCDI_HEADER_DATALEN);
318 	if (EFX_DWORD_FIELD(dword, MCDI_HEADER_ERROR)) {
319 		efx_dword_t errdword;
320 		int errcode;
321 
322 		EFSYS_ASSERT3U(length, ==, 4);
323 		EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM,
324 		    pdur + 1 + (MC_CMD_ERR_CODE_OFST >> 2),
325 		    &errdword, B_FALSE);
326 		errcode = EFX_DWORD_FIELD(errdword, EFX_DWORD_0);
327 		rc = efx_mcdi_request_errcode(errcode);
328 		EFSYS_PROBE2(mcdi_err, int, emrp->emr_cmd, int, errcode);
329 		goto fail4;
330 
331 	} else {
332 		emrp->emr_out_length_used = length;
333 		emrp->emr_rc = 0;
334 		efx_mcdi_request_copyout(enp, emrp);
335 	}
336 
337 	goto out;
338 
339 fail4:
340 	EFSYS_PROBE(fail4);
341 fail3:
342 	EFSYS_PROBE(fail3);
343 fail2:
344 	EFSYS_PROBE(fail2);
345 fail1:
346 	EFSYS_PROBE1(fail1, int, rc);
347 
348 	/* Fill out error state */
349 	emrp->emr_rc = rc;
350 	emrp->emr_out_length_used = 0;
351 
352 	/* Reboot/Assertion */
353 	if (rc == EIO || rc == EINTR)
354 		efx_mcdi_raise_exception(enp, emrp, rc);
355 
356 out:
357 	return (B_TRUE);
358 }
359 
360 			void
361 efx_mcdi_execute(
362 	__in		efx_nic_t *enp,
363 	__in		efx_mcdi_req_t *emrp)
364 {
365 	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
366 	const efx_mcdi_transport_t *emtp = emip->emi_mtp;
367 
368 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
369 	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
370 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
371 
372 	emtp->emt_execute(emtp->emt_context, emrp);
373 }
374 
375 			void
376 efx_mcdi_ev_cpl(
377 	__in		efx_nic_t *enp,
378 	__in		unsigned int seq,
379 	__in		unsigned int outlen,
380 	__in		int errcode)
381 {
382 	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
383 	const efx_mcdi_transport_t *emtp = emip->emi_mtp;
384 	efx_mcdi_req_t *emrp;
385 	int state;
386 
387 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
388 	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
389 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
390 
391 	/*
392 	 * Serialise against efx_mcdi_request_poll()/efx_mcdi_request_start()
393 	 * when we're completing an aborted request.
394 	 */
395 	EFSYS_LOCK(enp->en_eslp, state);
396 	if (emip->emi_pending_req == NULL || !emip->emi_ev_cpl ||
397 	    (seq != ((emip->emi_seq - 1) & 0xf))) {
398 		EFSYS_ASSERT(emip->emi_aborted > 0);
399 		if (emip->emi_aborted > 0)
400 			--emip->emi_aborted;
401 		EFSYS_UNLOCK(enp->en_eslp, state);
402 		return;
403 	}
404 
405 	emrp = emip->emi_pending_req;
406 	emip->emi_pending_req = NULL;
407 	EFSYS_UNLOCK(enp->en_eslp, state);
408 
409 	/*
410 	 * Fill out the remaining hdr fields, and copyout the payload
411 	 * if the user supplied an output buffer.
412 	 */
413 	if (errcode != 0) {
414 		EFSYS_PROBE2(mcdi_err, int, emrp->emr_cmd,
415 		    int, errcode);
416 		emrp->emr_out_length_used = 0;
417 		emrp->emr_rc = efx_mcdi_request_errcode(errcode);
418 	} else {
419 		emrp->emr_out_length_used = outlen;
420 		emrp->emr_rc = 0;
421 		efx_mcdi_request_copyout(enp, emrp);
422 	}
423 
424 	emtp->emt_ev_cpl(emtp->emt_context);
425 }
426 
427 			void
428 efx_mcdi_ev_death(
429 	__in		efx_nic_t *enp,
430 	__in		int rc)
431 {
432 	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
433 	const efx_mcdi_transport_t *emtp = emip->emi_mtp;
434 	efx_mcdi_req_t *emrp = NULL;
435 	boolean_t ev_cpl;
436 	int state;
437 
438 	/*
439 	 * The MCDI request (if there is one) has been terminated, either
440 	 * by a BADASSERT or REBOOT event.
441 	 *
442 	 * If there is an outstanding event-completed MCDI operation, then we
443 	 * will never receive the completion event (because both MCDI
444 	 * completions and BADASSERT events are sent to the same evq). So
445 	 * complete this MCDI op.
446 	 *
447 	 * This function might run in parallel with efx_mcdi_request_poll()
448 	 * for poll completed mcdi requests, and also with
449 	 * efx_mcdi_request_start() for post-watchdog completions.
450 	 */
451 	EFSYS_LOCK(enp->en_eslp, state);
452 	emrp = emip->emi_pending_req;
453 	ev_cpl = emip->emi_ev_cpl;
454 	if (emrp != NULL && emip->emi_ev_cpl) {
455 		emip->emi_pending_req = NULL;
456 
457 		emrp->emr_out_length_used = 0;
458 		emrp->emr_rc = rc;
459 		++emip->emi_aborted;
460 	}
461 
462 	/* Since we're running in parallel with a request, consume the
463 	 * status word before dropping the lock.
464 	 */
465 	if (rc == EIO || rc == EINTR) {
466 		EFSYS_SPIN(MCDI_STATUS_SLEEP_US);
467 		(void) efx_mcdi_poll_reboot(enp);
468 	}
469 
470 	EFSYS_UNLOCK(enp->en_eslp, state);
471 
472 	efx_mcdi_raise_exception(enp, emrp, rc);
473 
474 	if (emrp != NULL && ev_cpl)
475 		emtp->emt_ev_cpl(emtp->emt_context);
476 }
477 
478 	__checkReturn		int
479 efx_mcdi_version(
480 	__in			efx_nic_t *enp,
481 	__out_ecount_opt(4)	uint16_t versionp[4],
482 	__out_opt		uint32_t *buildp,
483 	__out_opt		efx_mcdi_boot_t *statusp)
484 {
485 	uint8_t outbuf[MAX(MC_CMD_GET_VERSION_OUT_LEN,
486 		    MC_CMD_GET_BOOT_STATUS_OUT_LEN)];
487 	efx_mcdi_req_t req;
488 	efx_word_t *ver_words;
489 	uint16_t version[4];
490 	uint32_t build;
491 	efx_mcdi_boot_t status;
492 	int rc;
493 
494 	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
495 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
496 
497 	EFX_STATIC_ASSERT(MC_CMD_GET_VERSION_IN_LEN == 0);
498 	req.emr_cmd = MC_CMD_GET_VERSION;
499 	req.emr_in_buf = NULL;
500 	req.emr_in_length = 0;
501 	req.emr_out_buf = outbuf;
502 	req.emr_out_length = MC_CMD_GET_VERSION_OUT_LEN;
503 
504 	efx_mcdi_execute(enp, &req);
505 
506 	if (req.emr_rc != 0) {
507 		rc = req.emr_rc;
508 		goto fail1;
509 	}
510 
511 	/* bootrom support */
512 	if (req.emr_out_length_used == MC_CMD_GET_VERSION_V0_OUT_LEN) {
513 		version[0] = version[1] = version[2] = version[3] = 0;
514 		build = MCDI_OUT_DWORD(req, GET_VERSION_OUT_FIRMWARE);
515 
516 		goto version;
517 	}
518 
519 	if (req.emr_out_length_used < MC_CMD_GET_VERSION_OUT_LEN) {
520 		rc = EMSGSIZE;
521 		goto fail2;
522 	}
523 
524 	ver_words = MCDI_OUT2(req, efx_word_t, GET_VERSION_OUT_VERSION);
525 	version[0] = EFX_WORD_FIELD(ver_words[0], EFX_WORD_0);
526 	version[1] = EFX_WORD_FIELD(ver_words[1], EFX_WORD_0);
527 	version[2] = EFX_WORD_FIELD(ver_words[2], EFX_WORD_0);
528 	version[3] = EFX_WORD_FIELD(ver_words[3], EFX_WORD_0);
529 	build = MCDI_OUT_DWORD(req, GET_VERSION_OUT_FIRMWARE);
530 
531 version:
532 	/* The bootrom doesn't understand BOOT_STATUS */
533 	if (build == MC_CMD_GET_VERSION_OUT_FIRMWARE_BOOTROM) {
534 		status = EFX_MCDI_BOOT_ROM;
535 		goto out;
536 	}
537 
538 	req.emr_cmd = MC_CMD_GET_BOOT_STATUS;
539 	EFX_STATIC_ASSERT(MC_CMD_GET_BOOT_STATUS_IN_LEN == 0);
540 	req.emr_in_buf = NULL;
541 	req.emr_in_length = 0;
542 	req.emr_out_buf = outbuf;
543 	req.emr_out_length = MC_CMD_GET_BOOT_STATUS_OUT_LEN;
544 
545 	efx_mcdi_execute(enp, &req);
546 
547 	if (req.emr_rc != 0) {
548 		rc = req.emr_rc;
549 		goto fail3;
550 	}
551 
552 	if (req.emr_out_length_used < MC_CMD_GET_BOOT_STATUS_OUT_LEN) {
553 		rc = EMSGSIZE;
554 		goto fail4;
555 	}
556 
557 	if (MCDI_OUT_DWORD_FIELD(req, GET_BOOT_STATUS_OUT_FLAGS,
558 	    GET_BOOT_STATUS_OUT_FLAGS_PRIMARY))
559 		status = EFX_MCDI_BOOT_PRIMARY;
560 	else
561 		status = EFX_MCDI_BOOT_SECONDARY;
562 
563 out:
564 	if (versionp != NULL)
565 		memcpy(versionp, version, sizeof (version));
566 	if (buildp != NULL)
567 		*buildp = build;
568 	if (statusp != NULL)
569 		*statusp = status;
570 
571 	return (0);
572 
573 fail4:
574 	EFSYS_PROBE(fail4);
575 fail3:
576 	EFSYS_PROBE(fail3);
577 fail2:
578 	EFSYS_PROBE(fail2);
579 fail1:
580 	EFSYS_PROBE1(fail1, int, rc);
581 
582 	return (rc);
583 }
584 
585 	__checkReturn	int
586 efx_mcdi_init(
587 	__in		efx_nic_t *enp,
588 	__in		const efx_mcdi_transport_t *mtp)
589 {
590 	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
591 	efx_oword_t oword;
592 	unsigned int portnum;
593 	int rc;
594 
595 	EFSYS_ASSERT3U(enp->en_mod_flags, ==, 0);
596 	enp->en_mod_flags |= EFX_MOD_MCDI;
597 
598 	if (enp->en_family == EFX_FAMILY_FALCON)
599 		return (0);
600 
601 	emip->emi_mtp = mtp;
602 
603 	/* Determine the port number to use for MCDI */
604 	EFX_BAR_READO(enp, FR_AZ_CS_DEBUG_REG, &oword);
605 	portnum = EFX_OWORD_FIELD(oword, FRF_CZ_CS_PORT_NUM);
606 
607 	if (portnum == 0) {
608 		/* Presumably booted from ROM; only MCDI port 1 will work */
609 		emip->emi_port = 1;
610 	} else if (portnum <= 2) {
611 		emip->emi_port = portnum;
612 	} else {
613 		rc = EINVAL;
614 		goto fail1;
615 	}
616 
617 	/*
618 	 * Wipe the atomic reboot status so subsequent MCDI requests succeed.
619 	 * BOOT_STATUS is preserved so eno_nic_probe() can boot out of the
620 	 * assertion handler.
621 	 */
622 	(void) efx_mcdi_poll_reboot(enp);
623 
624 	return (0);
625 
626 fail1:
627 	EFSYS_PROBE1(fail1, int, rc);
628 
629 	enp->en_mod_flags &= ~EFX_MOD_MCDI;
630 
631 	return (rc);
632 }
633 
634 
635 	__checkReturn	int
636 efx_mcdi_reboot(
637 	__in		efx_nic_t *enp)
638 {
639 	uint8_t payload[MC_CMD_REBOOT_IN_LEN];
640 	efx_mcdi_req_t req;
641 	int rc;
642 
643 	/*
644 	 * We could require the caller to have caused en_mod_flags=0 to
645 	 * call this function. This doesn't help the other port though,
646 	 * who's about to get the MC ripped out from underneath them.
647 	 * Since they have to cope with the subsequent fallout of MCDI
648 	 * failures, we should as well.
649 	 */
650 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
651 
652 	req.emr_cmd = MC_CMD_REBOOT;
653 	req.emr_in_buf = payload;
654 	req.emr_in_length = MC_CMD_REBOOT_IN_LEN;
655 	req.emr_out_buf = NULL;
656 	req.emr_out_length = 0;
657 
658 	MCDI_IN_SET_DWORD(req, REBOOT_IN_FLAGS, 0);
659 
660 	efx_mcdi_execute(enp, &req);
661 
662 	/* Invert EIO */
663 	if (req.emr_rc != EIO) {
664 		rc = EIO;
665 		goto fail1;
666 	}
667 
668 	return (0);
669 
670 fail1:
671 	EFSYS_PROBE1(fail1, int, rc);
672 
673 	return (rc);
674 }
675 
676 	__checkReturn	boolean_t
677 efx_mcdi_request_abort(
678 	__in		efx_nic_t *enp)
679 {
680 	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
681 	efx_mcdi_req_t *emrp;
682 	boolean_t aborted;
683 	int state;
684 
685 	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
686 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
687 
688 	/*
689 	 * efx_mcdi_ev_* may have already completed this event, and be
690 	 * spinning/blocked on the upper layer lock. So it *is* legitimate
691 	 * to for emi_pending_req to be NULL. If there is a pending event
692 	 * completed request, then provide a "credit" to allow
693 	 * efx_mcdi_ev_cpl() to accept a single spurious completion.
694 	 */
695 	EFSYS_LOCK(enp->en_eslp, state);
696 	emrp = emip->emi_pending_req;
697 	aborted = (emrp != NULL);
698 	if (aborted) {
699 		emip->emi_pending_req = NULL;
700 
701 		/* Error the request */
702 		emrp->emr_out_length_used = 0;
703 		emrp->emr_rc = ETIMEDOUT;
704 
705 		/* Provide a credit for seqno/emr_pending_req mismatches */
706 		if (emip->emi_ev_cpl)
707 			++emip->emi_aborted;
708 
709 		/*
710 		 * The upper layer has called us, so we don't
711 		 * need to complete the request.
712 		 */
713 	}
714 	EFSYS_UNLOCK(enp->en_eslp, state);
715 
716 	return (aborted);
717 }
718 
719 			void
720 efx_mcdi_fini(
721 	__in		efx_nic_t *enp)
722 {
723 	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
724 
725 	EFSYS_ASSERT3U(enp->en_mod_flags, ==, EFX_MOD_MCDI);
726 	enp->en_mod_flags &= ~EFX_MOD_MCDI;
727 
728 	if (~(enp->en_features) & EFX_FEATURE_MCDI)
729 		return;
730 
731 	emip->emi_mtp = NULL;
732 	emip->emi_port = 0;
733 	emip->emi_aborted = 0;
734 }
735 
736 #endif	/* EFSYS_OPT_MCDI */
737