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