xref: /freebsd/sys/dev/sfxge/common/efx_mcdi.c (revision 788ca347b816afd83b2885e0c79aeeb88649b2ab)
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 #ifndef EFX_GRACEFUL_MC_REBOOT
217 	/*
218 	 * This function is not being used properly.
219 	 * Until its callers are fixed, it should always return 0.
220 	 */
221 	_NOTE(ARGUNUSED(enp))
222 	return (0);
223 #else
224 	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
225 	unsigned int rebootr;
226 	efx_dword_t dword;
227 	uint32_t value;
228 
229 	EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
230 	rebootr = ((emip->emi_port == 1)
231 	    ? MC_SMEM_P0_STATUS_OFST >> 2
232 	    : MC_SMEM_P1_STATUS_OFST >> 2);
233 
234 	EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
235 	value = EFX_DWORD_FIELD(dword, EFX_DWORD_0);
236 
237 	if (value == 0)
238 		return (0);
239 
240 	EFX_ZERO_DWORD(dword);
241 	EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
242 
243 	if (value == MC_STATUS_DWORD_ASSERT)
244 		return (EINTR);
245 	else
246 		return (EIO);
247 #endif
248 }
249 
250 	__checkReturn	boolean_t
251 efx_mcdi_request_poll(
252 	__in		efx_nic_t *enp)
253 {
254 	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
255 	efx_mcdi_req_t *emrp;
256 	efx_dword_t dword;
257 	unsigned int pdur;
258 	unsigned int seq;
259 	unsigned int length;
260 	int state;
261 	int rc;
262 
263 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
264 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
265 	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
266 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
267 
268 	/* Serialise against post-watchdog efx_mcdi_ev* */
269 	EFSYS_LOCK(enp->en_eslp, state);
270 
271 	EFSYS_ASSERT(emip->emi_pending_req != NULL);
272 	EFSYS_ASSERT(!emip->emi_ev_cpl);
273 	emrp = emip->emi_pending_req;
274 
275 	/* Check for reboot atomically w.r.t efx_mcdi_request_start */
276 	if (emip->emi_poll_cnt++ == 0) {
277 		if ((rc = efx_mcdi_poll_reboot(enp)) != 0) {
278 			emip->emi_pending_req = NULL;
279 			EFSYS_UNLOCK(enp->en_eslp, state);
280 
281 			goto fail1;
282 		}
283 	}
284 
285 	EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
286 	pdur = (emip->emi_port == 1)
287 	    ? MC_SMEM_P0_PDU_OFST >> 2
288 	    : MC_SMEM_P1_PDU_OFST >> 2;
289 
290 	/* Read the command header */
291 	EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, pdur, &dword, B_FALSE);
292 	if (EFX_DWORD_FIELD(dword, MCDI_HEADER_RESPONSE) == 0) {
293 		EFSYS_UNLOCK(enp->en_eslp, state);
294 		return (B_FALSE);
295 	}
296 
297 	/* Request complete */
298 	emip->emi_pending_req = NULL;
299 	seq = (emip->emi_seq - 1) & 0xf;
300 
301 	/* Check for synchronous reboot */
302 	if (EFX_DWORD_FIELD(dword, MCDI_HEADER_ERROR) != 0 &&
303 	    EFX_DWORD_FIELD(dword, MCDI_HEADER_DATALEN) == 0) {
304 		/* Consume status word */
305 		EFSYS_SPIN(MCDI_STATUS_SLEEP_US);
306 		efx_mcdi_poll_reboot(enp);
307 		EFSYS_UNLOCK(enp->en_eslp, state);
308 		rc = EIO;
309 		goto fail2;
310 	}
311 
312 	EFSYS_UNLOCK(enp->en_eslp, state);
313 
314 	/* Check that the returned data is consistent */
315 	if (EFX_DWORD_FIELD(dword, MCDI_HEADER_CODE) != emrp->emr_cmd ||
316 	    EFX_DWORD_FIELD(dword, MCDI_HEADER_SEQ) != seq) {
317 		/* Response is for a different request */
318 		rc = EIO;
319 		goto fail3;
320 	}
321 
322 	length = EFX_DWORD_FIELD(dword, MCDI_HEADER_DATALEN);
323 	if (EFX_DWORD_FIELD(dword, MCDI_HEADER_ERROR)) {
324 		efx_dword_t errdword;
325 		int errcode;
326 
327 		EFSYS_ASSERT3U(length, ==, 4);
328 		EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM,
329 		    pdur + 1 + (MC_CMD_ERR_CODE_OFST >> 2),
330 		    &errdword, B_FALSE);
331 		errcode = EFX_DWORD_FIELD(errdword, EFX_DWORD_0);
332 		rc = efx_mcdi_request_errcode(errcode);
333 		EFSYS_PROBE2(mcdi_err, int, emrp->emr_cmd, int, errcode);
334 		goto fail4;
335 
336 	} else {
337 		emrp->emr_out_length_used = length;
338 		emrp->emr_rc = 0;
339 		efx_mcdi_request_copyout(enp, emrp);
340 	}
341 
342 	goto out;
343 
344 fail4:
345 	EFSYS_PROBE(fail4);
346 fail3:
347 	EFSYS_PROBE(fail3);
348 fail2:
349 	EFSYS_PROBE(fail2);
350 fail1:
351 	EFSYS_PROBE1(fail1, int, rc);
352 
353 	/* Fill out error state */
354 	emrp->emr_rc = rc;
355 	emrp->emr_out_length_used = 0;
356 
357 	/* Reboot/Assertion */
358 	if (rc == EIO || rc == EINTR)
359 		efx_mcdi_raise_exception(enp, emrp, rc);
360 
361 out:
362 	return (B_TRUE);
363 }
364 
365 			void
366 efx_mcdi_execute(
367 	__in		efx_nic_t *enp,
368 	__in		efx_mcdi_req_t *emrp)
369 {
370 	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
371 	const efx_mcdi_transport_t *emtp = emip->emi_mtp;
372 
373 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
374 	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
375 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
376 
377 	emtp->emt_execute(emtp->emt_context, emrp);
378 }
379 
380 			void
381 efx_mcdi_ev_cpl(
382 	__in		efx_nic_t *enp,
383 	__in		unsigned int seq,
384 	__in		unsigned int outlen,
385 	__in		int errcode)
386 {
387 	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
388 	const efx_mcdi_transport_t *emtp = emip->emi_mtp;
389 	efx_mcdi_req_t *emrp;
390 	int state;
391 
392 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
393 	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
394 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
395 
396 	/*
397 	 * Serialise against efx_mcdi_request_poll()/efx_mcdi_request_start()
398 	 * when we're completing an aborted request.
399 	 */
400 	EFSYS_LOCK(enp->en_eslp, state);
401 	if (emip->emi_pending_req == NULL || !emip->emi_ev_cpl ||
402 	    (seq != ((emip->emi_seq - 1) & 0xf))) {
403 		EFSYS_ASSERT(emip->emi_aborted > 0);
404 		if (emip->emi_aborted > 0)
405 			--emip->emi_aborted;
406 		EFSYS_UNLOCK(enp->en_eslp, state);
407 		return;
408 	}
409 
410 	emrp = emip->emi_pending_req;
411 	emip->emi_pending_req = NULL;
412 	EFSYS_UNLOCK(enp->en_eslp, state);
413 
414 	/*
415 	 * Fill out the remaining hdr fields, and copyout the payload
416 	 * if the user supplied an output buffer.
417 	 */
418 	if (errcode != 0) {
419 		EFSYS_PROBE2(mcdi_err, int, emrp->emr_cmd,
420 		    int, errcode);
421 		emrp->emr_out_length_used = 0;
422 		emrp->emr_rc = efx_mcdi_request_errcode(errcode);
423 	} else {
424 		emrp->emr_out_length_used = outlen;
425 		emrp->emr_rc = 0;
426 		efx_mcdi_request_copyout(enp, emrp);
427 	}
428 
429 	emtp->emt_ev_cpl(emtp->emt_context);
430 }
431 
432 			void
433 efx_mcdi_ev_death(
434 	__in		efx_nic_t *enp,
435 	__in		int rc)
436 {
437 	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
438 	const efx_mcdi_transport_t *emtp = emip->emi_mtp;
439 	efx_mcdi_req_t *emrp = NULL;
440 	boolean_t ev_cpl;
441 	int state;
442 
443 	/*
444 	 * The MCDI request (if there is one) has been terminated, either
445 	 * by a BADASSERT or REBOOT event.
446 	 *
447 	 * If there is an outstanding event-completed MCDI operation, then we
448 	 * will never receive the completion event (because both MCDI
449 	 * completions and BADASSERT events are sent to the same evq). So
450 	 * complete this MCDI op.
451 	 *
452 	 * This function might run in parallel with efx_mcdi_request_poll()
453 	 * for poll completed mcdi requests, and also with
454 	 * efx_mcdi_request_start() for post-watchdog completions.
455 	 */
456 	EFSYS_LOCK(enp->en_eslp, state);
457 	emrp = emip->emi_pending_req;
458 	ev_cpl = emip->emi_ev_cpl;
459 	if (emrp != NULL && emip->emi_ev_cpl) {
460 		emip->emi_pending_req = NULL;
461 
462 		emrp->emr_out_length_used = 0;
463 		emrp->emr_rc = rc;
464 		++emip->emi_aborted;
465 	}
466 
467 	/*
468 	 * Since we're running in parallel with a request, consume the
469 	 * status word before dropping the lock.
470 	 */
471 	if (rc == EIO || rc == EINTR) {
472 		EFSYS_SPIN(MCDI_STATUS_SLEEP_US);
473 		(void) efx_mcdi_poll_reboot(enp);
474 	}
475 
476 	EFSYS_UNLOCK(enp->en_eslp, state);
477 
478 	efx_mcdi_raise_exception(enp, emrp, rc);
479 
480 	if (emrp != NULL && ev_cpl)
481 		emtp->emt_ev_cpl(emtp->emt_context);
482 }
483 
484 	__checkReturn		int
485 efx_mcdi_version(
486 	__in			efx_nic_t *enp,
487 	__out_ecount_opt(4)	uint16_t versionp[4],
488 	__out_opt		uint32_t *buildp,
489 	__out_opt		efx_mcdi_boot_t *statusp)
490 {
491 	uint8_t outbuf[MAX(MC_CMD_GET_VERSION_OUT_LEN,
492 		    MC_CMD_GET_BOOT_STATUS_OUT_LEN)];
493 	efx_mcdi_req_t req;
494 	efx_word_t *ver_words;
495 	uint16_t version[4];
496 	uint32_t build;
497 	efx_mcdi_boot_t status;
498 	int rc;
499 
500 	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
501 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
502 
503 	EFX_STATIC_ASSERT(MC_CMD_GET_VERSION_IN_LEN == 0);
504 	req.emr_cmd = MC_CMD_GET_VERSION;
505 	req.emr_in_buf = NULL;
506 	req.emr_in_length = 0;
507 	req.emr_out_buf = outbuf;
508 	req.emr_out_length = MC_CMD_GET_VERSION_OUT_LEN;
509 
510 	efx_mcdi_execute(enp, &req);
511 
512 	if (req.emr_rc != 0) {
513 		rc = req.emr_rc;
514 		goto fail1;
515 	}
516 
517 	/* bootrom support */
518 	if (req.emr_out_length_used == MC_CMD_GET_VERSION_V0_OUT_LEN) {
519 		version[0] = version[1] = version[2] = version[3] = 0;
520 		build = MCDI_OUT_DWORD(req, GET_VERSION_OUT_FIRMWARE);
521 
522 		goto version;
523 	}
524 
525 	if (req.emr_out_length_used < MC_CMD_GET_VERSION_OUT_LEN) {
526 		rc = EMSGSIZE;
527 		goto fail2;
528 	}
529 
530 	ver_words = MCDI_OUT2(req, efx_word_t, GET_VERSION_OUT_VERSION);
531 	version[0] = EFX_WORD_FIELD(ver_words[0], EFX_WORD_0);
532 	version[1] = EFX_WORD_FIELD(ver_words[1], EFX_WORD_0);
533 	version[2] = EFX_WORD_FIELD(ver_words[2], EFX_WORD_0);
534 	version[3] = EFX_WORD_FIELD(ver_words[3], EFX_WORD_0);
535 	build = MCDI_OUT_DWORD(req, GET_VERSION_OUT_FIRMWARE);
536 
537 version:
538 	/* The bootrom doesn't understand BOOT_STATUS */
539 	if (build == MC_CMD_GET_VERSION_OUT_FIRMWARE_SIENA_BOOTROM) {
540 		status = EFX_MCDI_BOOT_ROM;
541 		goto out;
542 	}
543 
544 	req.emr_cmd = MC_CMD_GET_BOOT_STATUS;
545 	EFX_STATIC_ASSERT(MC_CMD_GET_BOOT_STATUS_IN_LEN == 0);
546 	req.emr_in_buf = NULL;
547 	req.emr_in_length = 0;
548 	req.emr_out_buf = outbuf;
549 	req.emr_out_length = MC_CMD_GET_BOOT_STATUS_OUT_LEN;
550 
551 	efx_mcdi_execute(enp, &req);
552 
553 	if (req.emr_rc != 0) {
554 		rc = req.emr_rc;
555 		goto fail3;
556 	}
557 
558 	if (req.emr_out_length_used < MC_CMD_GET_BOOT_STATUS_OUT_LEN) {
559 		rc = EMSGSIZE;
560 		goto fail4;
561 	}
562 
563 	if (MCDI_OUT_DWORD_FIELD(req, GET_BOOT_STATUS_OUT_FLAGS,
564 	    GET_BOOT_STATUS_OUT_FLAGS_PRIMARY))
565 		status = EFX_MCDI_BOOT_PRIMARY;
566 	else
567 		status = EFX_MCDI_BOOT_SECONDARY;
568 
569 out:
570 	if (versionp != NULL)
571 		memcpy(versionp, version, sizeof (version));
572 	if (buildp != NULL)
573 		*buildp = build;
574 	if (statusp != NULL)
575 		*statusp = status;
576 
577 	return (0);
578 
579 fail4:
580 	EFSYS_PROBE(fail4);
581 fail3:
582 	EFSYS_PROBE(fail3);
583 fail2:
584 	EFSYS_PROBE(fail2);
585 fail1:
586 	EFSYS_PROBE1(fail1, int, rc);
587 
588 	return (rc);
589 }
590 
591 	__checkReturn	int
592 efx_mcdi_init(
593 	__in		efx_nic_t *enp,
594 	__in		const efx_mcdi_transport_t *mtp)
595 {
596 	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
597 	efx_oword_t oword;
598 	unsigned int portnum;
599 	int rc;
600 
601 	EFSYS_ASSERT3U(enp->en_mod_flags, ==, 0);
602 	enp->en_mod_flags |= EFX_MOD_MCDI;
603 
604 	if (enp->en_family == EFX_FAMILY_FALCON)
605 		return (0);
606 
607 	emip->emi_mtp = mtp;
608 
609 	/* Determine the port number to use for MCDI */
610 	EFX_BAR_READO(enp, FR_AZ_CS_DEBUG_REG, &oword);
611 	portnum = EFX_OWORD_FIELD(oword, FRF_CZ_CS_PORT_NUM);
612 
613 	if (portnum == 0) {
614 		/* Presumably booted from ROM; only MCDI port 1 will work */
615 		emip->emi_port = 1;
616 	} else if (portnum <= 2) {
617 		emip->emi_port = portnum;
618 	} else {
619 		rc = EINVAL;
620 		goto fail1;
621 	}
622 
623 	/*
624 	 * Wipe the atomic reboot status so subsequent MCDI requests succeed.
625 	 * BOOT_STATUS is preserved so eno_nic_probe() can boot out of the
626 	 * assertion handler.
627 	 */
628 	(void) efx_mcdi_poll_reboot(enp);
629 
630 	return (0);
631 
632 fail1:
633 	EFSYS_PROBE1(fail1, int, rc);
634 
635 	enp->en_mod_flags &= ~EFX_MOD_MCDI;
636 
637 	return (rc);
638 }
639 
640 
641 	__checkReturn	int
642 efx_mcdi_reboot(
643 	__in		efx_nic_t *enp)
644 {
645 	uint8_t payload[MC_CMD_REBOOT_IN_LEN];
646 	efx_mcdi_req_t req;
647 	int rc;
648 
649 	/*
650 	 * We could require the caller to have caused en_mod_flags=0 to
651 	 * call this function. This doesn't help the other port though,
652 	 * who's about to get the MC ripped out from underneath them.
653 	 * Since they have to cope with the subsequent fallout of MCDI
654 	 * failures, we should as well.
655 	 */
656 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
657 
658 	req.emr_cmd = MC_CMD_REBOOT;
659 	req.emr_in_buf = payload;
660 	req.emr_in_length = MC_CMD_REBOOT_IN_LEN;
661 	req.emr_out_buf = NULL;
662 	req.emr_out_length = 0;
663 
664 	MCDI_IN_SET_DWORD(req, REBOOT_IN_FLAGS, 0);
665 
666 	efx_mcdi_execute(enp, &req);
667 
668 	/* Invert EIO */
669 	if (req.emr_rc != EIO) {
670 		rc = EIO;
671 		goto fail1;
672 	}
673 
674 	return (0);
675 
676 fail1:
677 	EFSYS_PROBE1(fail1, int, rc);
678 
679 	return (rc);
680 }
681 
682 	__checkReturn	boolean_t
683 efx_mcdi_request_abort(
684 	__in		efx_nic_t *enp)
685 {
686 	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
687 	efx_mcdi_req_t *emrp;
688 	boolean_t aborted;
689 	int state;
690 
691 	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
692 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
693 
694 	/*
695 	 * efx_mcdi_ev_* may have already completed this event, and be
696 	 * spinning/blocked on the upper layer lock. So it *is* legitimate
697 	 * to for emi_pending_req to be NULL. If there is a pending event
698 	 * completed request, then provide a "credit" to allow
699 	 * efx_mcdi_ev_cpl() to accept a single spurious completion.
700 	 */
701 	EFSYS_LOCK(enp->en_eslp, state);
702 	emrp = emip->emi_pending_req;
703 	aborted = (emrp != NULL);
704 	if (aborted) {
705 		emip->emi_pending_req = NULL;
706 
707 		/* Error the request */
708 		emrp->emr_out_length_used = 0;
709 		emrp->emr_rc = ETIMEDOUT;
710 
711 		/* Provide a credit for seqno/emr_pending_req mismatches */
712 		if (emip->emi_ev_cpl)
713 			++emip->emi_aborted;
714 
715 		/*
716 		 * The upper layer has called us, so we don't
717 		 * need to complete the request.
718 		 */
719 	}
720 	EFSYS_UNLOCK(enp->en_eslp, state);
721 
722 	return (aborted);
723 }
724 
725 			void
726 efx_mcdi_fini(
727 	__in		efx_nic_t *enp)
728 {
729 	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
730 
731 	EFSYS_ASSERT3U(enp->en_mod_flags, ==, EFX_MOD_MCDI);
732 	enp->en_mod_flags &= ~EFX_MOD_MCDI;
733 
734 	if (~(enp->en_features) & EFX_FEATURE_MCDI)
735 		return;
736 
737 	emip->emi_mtp = NULL;
738 	emip->emi_port = 0;
739 	emip->emi_aborted = 0;
740 }
741 
742 #endif	/* EFSYS_OPT_MCDI */
743