efx_mcdi.c revision 227569
1134459Siedowse/*-
2134459Siedowse * Copyright 2008-2009 Solarflare Communications Inc.  All rights reserved.
3134459Siedowse *
4134459Siedowse * Redistribution and use in source and binary forms, with or without
5134459Siedowse * modification, are permitted provided that the following conditions
6134459Siedowse * 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 "efsys.h"
27#include "efx.h"
28#include "efx_types.h"
29#include "efx_regs.h"
30#include "efx_regs_mcdi.h"
31#include "efx_impl.h"
32
33#if EFSYS_OPT_MCDI
34
35/* Shared memory layout */
36
37#define	MCDI_P1_DBL_OFST	0x0
38#define	MCDI_P2_DBL_OFST	0x1
39#define	MCDI_P1_PDU_OFST	0x2
40#define	MCDI_P2_PDU_OFST	0x42
41#define	MCDI_P1_REBOOT_OFST	0x1fe
42#define	MCDI_P2_REBOOT_OFST	0x1ff
43
44/* A reboot/assertion causes the MCDI status word to be set after the
45 * command word is set or a REBOOT event is sent. If we notice a reboot
46 * via these mechanisms then wait 10ms for the status word to be set.
47 */
48#define	MCDI_STATUS_SLEEP_US	10000
49
50			void
51efx_mcdi_request_start(
52	__in		efx_nic_t *enp,
53	__in		efx_mcdi_req_t *emrp,
54	__in		boolean_t ev_cpl)
55{
56	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
57	efx_dword_t dword;
58	unsigned int seq;
59	unsigned int xflags;
60	unsigned int pdur;
61	unsigned int dbr;
62	unsigned int pos;
63	int state;
64
65	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
66	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
67	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
68
69	switch (emip->emi_port)	{
70	case 1:
71		pdur = MCDI_P1_PDU_OFST;
72		dbr = MCDI_P1_DBL_OFST;
73		break;
74	case 2:
75		pdur = MCDI_P2_PDU_OFST;
76		dbr = MCDI_P2_DBL_OFST;
77		break;
78	default:
79		EFSYS_ASSERT(0);
80		pdur = dbr = 0;
81	};
82
83	/*
84	 * efx_mcdi_request_start() is naturally serialised against both
85	 * efx_mcdi_request_poll() and efx_mcdi_ev_cpl()/efx_mcdi_ev_death(),
86	 * by virtue of there only being one oustanding MCDI request.
87	 * Unfortunately, upper layers may also call efx_mcdi_request_abort()
88	 * at any time, to timeout a pending mcdi request, That request may
89	 * then subsequently complete, meaning efx_mcdi_ev_cpl() or
90	 * efx_mcdi_ev_death() may end up running in parallel with
91	 * efx_mcdi_request_start(). This race is handled by ensuring that
92	 * %emi_pending_req, %emi_ev_cpl and %emi_seq are protected by the
93	 * en_eslp lock.
94	 */
95	EFSYS_LOCK(enp->en_eslp, state);
96	EFSYS_ASSERT(emip->emi_pending_req == NULL);
97	emip->emi_pending_req = emrp;
98	emip->emi_ev_cpl = ev_cpl;
99	emip->emi_poll_cnt = 0;
100	seq = emip->emi_seq++ & 0xf;
101	EFSYS_UNLOCK(enp->en_eslp, state);
102
103	xflags = 0;
104	if (ev_cpl)
105		xflags |= MCDI_HEADER_XFLAGS_EVREQ;
106
107	/* Construct the header in shared memory */
108	EFX_POPULATE_DWORD_6(dword,
109			    MCDI_HEADER_CODE, emrp->emr_cmd,
110			    MCDI_HEADER_RESYNC, 1,
111			    MCDI_HEADER_DATALEN, emrp->emr_in_length,
112			    MCDI_HEADER_SEQ, seq,
113			    MCDI_HEADER_RESPONSE, 0,
114			    MCDI_HEADER_XFLAGS, xflags);
115	EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, pdur, &dword, B_TRUE);
116
117	for (pos = 0; pos < emrp->emr_in_length; pos += sizeof (efx_dword_t)) {
118		memcpy(&dword, MCDI_IN(*emrp, efx_dword_t, pos),
119		    MIN(sizeof (dword), emrp->emr_in_length - pos));
120		EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM,
121		    pdur + 1 + (pos >> 2), &dword, B_FALSE);
122	}
123
124	/* Ring the doorbell */
125	EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 0xd004be11);
126	EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, dbr, &dword, B_FALSE);
127}
128
129static			void
130efx_mcdi_request_copyout(
131	__in		efx_nic_t *enp,
132	__in		efx_mcdi_req_t *emrp)
133{
134	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
135	unsigned int pos;
136	unsigned int pdur;
137	efx_dword_t data;
138
139	pdur = (emip->emi_port == 1) ? MCDI_P1_PDU_OFST : MCDI_P2_PDU_OFST;
140
141	/* Copy payload out if caller supplied buffer */
142	if (emrp->emr_out_buf != NULL) {
143		size_t bytes = MIN(emrp->emr_out_length_used,
144				    emrp->emr_out_length);
145		for (pos = 0; pos < bytes; pos += sizeof (efx_dword_t)) {
146			EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM,
147			    pdur + 1 + (pos >> 2), &data, B_FALSE);
148			memcpy(MCDI_OUT(*emrp, efx_dword_t, pos), &data,
149			    MIN(sizeof (data), bytes - pos));
150		}
151	}
152}
153
154static			int
155efx_mcdi_request_errcode(
156	__in		unsigned int err)
157{
158
159	switch (err) {
160	case MC_CMD_ERR_ENOENT:
161		return (ENOENT);
162	case MC_CMD_ERR_EINTR:
163		return (EINTR);
164	case MC_CMD_ERR_EACCES:
165		return (EACCES);
166	case MC_CMD_ERR_EBUSY:
167		return (EBUSY);
168	case MC_CMD_ERR_EINVAL:
169		return (EINVAL);
170	case MC_CMD_ERR_EDEADLK:
171		return (EDEADLK);
172	case MC_CMD_ERR_ENOSYS:
173		return (ENOTSUP);
174	case MC_CMD_ERR_ETIME:
175		return (ETIMEDOUT);
176#ifdef WITH_MCDI_V2
177	case MC_CMD_ERR_EAGAIN:
178		return (EAGAIN);
179	case MC_CMD_ERR_ENOSPC:
180		return (ENOSPC);
181#endif
182	default:
183		EFSYS_PROBE1(mc_pcol_error, int, err);
184		return (EIO);
185	}
186}
187
188static			void
189efx_mcdi_raise_exception(
190	__in		efx_nic_t *enp,
191	__in_opt	efx_mcdi_req_t *emrp,
192	__in		int rc)
193{
194	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
195	const efx_mcdi_transport_t *emtp = emip->emi_mtp;
196	efx_mcdi_exception_t exception;
197
198	/* Reboot or Assertion failure only */
199	EFSYS_ASSERT(rc == EIO || rc == EINTR);
200
201	/*
202	 * If MC_CMD_REBOOT causes a reboot (dependent on parameters),
203	 * then the EIO is not worthy of an exception.
204	 */
205	if (emrp != NULL && emrp->emr_cmd == MC_CMD_REBOOT && rc == EIO)
206		return;
207
208	exception = (rc == EIO)
209		? EFX_MCDI_EXCEPTION_MC_REBOOT
210		: EFX_MCDI_EXCEPTION_MC_BADASSERT;
211
212	emtp->emt_exception(emtp->emt_context, exception);
213}
214
215static			int
216efx_mcdi_poll_reboot(
217	__in		efx_nic_t *enp)
218{
219	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
220	unsigned int rebootr;
221	efx_dword_t dword;
222	uint32_t value;
223
224	EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
225	rebootr = ((emip->emi_port == 1)
226	    ? MCDI_P1_REBOOT_OFST
227	    : MCDI_P2_REBOOT_OFST);
228
229	EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
230	value = EFX_DWORD_FIELD(dword, EFX_DWORD_0);
231
232	if (value == 0)
233		return (0);
234
235	EFX_ZERO_DWORD(dword);
236	EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
237
238	if (value == MC_STATUS_DWORD_ASSERT)
239		return (EINTR);
240	else
241		return (EIO);
242}
243
244	__checkReturn	boolean_t
245efx_mcdi_request_poll(
246	__in		efx_nic_t *enp)
247{
248	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
249	efx_mcdi_req_t *emrp;
250	efx_dword_t dword;
251	unsigned int pdur;
252	unsigned int seq;
253	unsigned int length;
254	int state;
255	int rc;
256
257	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
258	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
259	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
260	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
261
262	/* Serialise against post-watchdog efx_mcdi_ev* */
263	EFSYS_LOCK(enp->en_eslp, state);
264
265	EFSYS_ASSERT(emip->emi_pending_req != NULL);
266	EFSYS_ASSERT(!emip->emi_ev_cpl);
267	emrp = emip->emi_pending_req;
268
269	/* Check for reboot atomically w.r.t efx_mcdi_request_start */
270	if (emip->emi_poll_cnt++ == 0) {
271		if ((rc = efx_mcdi_poll_reboot(enp)) != 0) {
272			emip->emi_pending_req = NULL;
273			EFSYS_UNLOCK(enp->en_eslp, state);
274
275			goto fail1;
276		}
277	}
278
279	EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
280	pdur = (emip->emi_port == 1) ? MCDI_P1_PDU_OFST : MCDI_P2_PDU_OFST;
281
282	/* Read the command header */
283	EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, pdur, &dword, B_FALSE);
284	if (EFX_DWORD_FIELD(dword, MCDI_HEADER_RESPONSE) == 0) {
285		EFSYS_UNLOCK(enp->en_eslp, state);
286		return (B_FALSE);
287	}
288
289	/* Request complete */
290	emip->emi_pending_req = NULL;
291	seq = (emip->emi_seq - 1) & 0xf;
292
293	/* Check for synchronous reboot */
294	if (EFX_DWORD_FIELD(dword, MCDI_HEADER_ERROR) != 0 &&
295	    EFX_DWORD_FIELD(dword, MCDI_HEADER_DATALEN) == 0) {
296		/* Consume status word */
297		EFSYS_SPIN(MCDI_STATUS_SLEEP_US);
298		efx_mcdi_poll_reboot(enp);
299		EFSYS_UNLOCK(enp->en_eslp, state);
300		rc = EIO;
301		goto fail2;
302	}
303
304	EFSYS_UNLOCK(enp->en_eslp, state);
305
306	/* Check that the returned data is consistent */
307	if (EFX_DWORD_FIELD(dword, MCDI_HEADER_CODE) != emrp->emr_cmd ||
308	    EFX_DWORD_FIELD(dword, MCDI_HEADER_SEQ) != seq) {
309		/* Response is for a different request */
310		rc = EIO;
311		goto fail3;
312	}
313
314	length = EFX_DWORD_FIELD(dword, MCDI_HEADER_DATALEN);
315	if (EFX_DWORD_FIELD(dword, MCDI_HEADER_ERROR)) {
316		efx_dword_t errdword;
317		int errcode;
318
319		EFSYS_ASSERT3U(length, ==, 4);
320		EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM,
321		    pdur + 1 + (MC_CMD_ERR_CODE_OFST >> 2),
322		    &errdword, B_FALSE);
323		errcode = EFX_DWORD_FIELD(errdword, EFX_DWORD_0);
324		rc = efx_mcdi_request_errcode(errcode);
325		EFSYS_PROBE2(mcdi_err, int, emrp->emr_cmd, int, errcode);
326		goto fail4;
327
328	} else {
329		emrp->emr_out_length_used = length;
330		emrp->emr_rc = 0;
331		efx_mcdi_request_copyout(enp, emrp);
332	}
333
334	goto out;
335
336fail4:
337	EFSYS_PROBE(fail4);
338fail3:
339	EFSYS_PROBE(fail3);
340fail2:
341	EFSYS_PROBE(fail2);
342fail1:
343	EFSYS_PROBE1(fail1, int, rc);
344
345	/* Fill out error state */
346	emrp->emr_rc = rc;
347	emrp->emr_out_length_used = 0;
348
349	/* Reboot/Assertion */
350	if (rc == EIO || rc == EINTR)
351		efx_mcdi_raise_exception(enp, emrp, rc);
352
353out:
354	return (B_TRUE);
355}
356
357			void
358efx_mcdi_execute(
359	__in		efx_nic_t *enp,
360	__in		efx_mcdi_req_t *emrp)
361{
362	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
363	const efx_mcdi_transport_t *emtp = emip->emi_mtp;
364
365	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
366	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
367	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
368
369	emtp->emt_execute(emtp->emt_context, emrp);
370}
371
372			void
373efx_mcdi_ev_cpl(
374	__in		efx_nic_t *enp,
375	__in		unsigned int seq,
376	__in		unsigned int outlen,
377	__in		int errcode)
378{
379	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
380	const efx_mcdi_transport_t *emtp = emip->emi_mtp;
381	efx_mcdi_req_t *emrp;
382	int state;
383
384	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
385	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
386	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
387
388	/*
389	 * Serialise against efx_mcdi_request_poll()/efx_mcdi_request_start()
390	 * when we're completing an aborted request.
391	 */
392	EFSYS_LOCK(enp->en_eslp, state);
393	if (emip->emi_pending_req == NULL || !emip->emi_ev_cpl ||
394	    (seq != ((emip->emi_seq - 1) & 0xf))) {
395		EFSYS_ASSERT(emip->emi_aborted > 0);
396		if (emip->emi_aborted > 0)
397			--emip->emi_aborted;
398		EFSYS_UNLOCK(enp->en_eslp, state);
399		return;
400	}
401
402	emrp = emip->emi_pending_req;
403	emip->emi_pending_req = NULL;
404	EFSYS_UNLOCK(enp->en_eslp, state);
405
406	/*
407	 * Fill out the remaining hdr fields, and copyout the payload
408	 * if the user supplied an output buffer.
409	 */
410	if (errcode != 0) {
411		EFSYS_PROBE2(mcdi_err, int, emrp->emr_cmd,
412		    int, errcode);
413		emrp->emr_out_length_used = 0;
414		emrp->emr_rc = efx_mcdi_request_errcode(errcode);
415	} else {
416		emrp->emr_out_length_used = outlen;
417		emrp->emr_rc = 0;
418		efx_mcdi_request_copyout(enp, emrp);
419	}
420
421	emtp->emt_ev_cpl(emtp->emt_context);
422}
423
424			void
425efx_mcdi_ev_death(
426	__in		efx_nic_t *enp,
427	__in		int rc)
428{
429	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
430	const efx_mcdi_transport_t *emtp = emip->emi_mtp;
431	efx_mcdi_req_t *emrp = NULL;
432	boolean_t ev_cpl;
433	int state;
434
435	/*
436	 * The MCDI request (if there is one) has been terminated, either
437	 * by a BADASSERT or REBOOT event.
438	 *
439	 * If there is an oustanding event-completed MCDI operation, then we
440	 * will never receive the completion event (because both MCDI
441	 * completions and BADASSERT events are sent to the same evq). So
442	 * complete this MCDI op.
443	 *
444	 * This function might run in parallel with efx_mcdi_request_poll()
445	 * for poll completed mcdi requests, and also with
446	 * efx_mcdi_request_start() for post-watchdog completions.
447	 */
448	EFSYS_LOCK(enp->en_eslp, state);
449	emrp = emip->emi_pending_req;
450	ev_cpl = emip->emi_ev_cpl;
451	if (emrp != NULL && emip->emi_ev_cpl) {
452		emip->emi_pending_req = NULL;
453
454		emrp->emr_out_length_used = 0;
455		emrp->emr_rc = rc;
456		++emip->emi_aborted;
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
476efx_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
528version:
529	/* The bootrom doesn't understand BOOT_STATUS */
530	if (build == MC_CMD_GET_VERSION_OUT_FIRMWARE_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
560out:
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
570fail4:
571	EFSYS_PROBE(fail4);
572fail3:
573	EFSYS_PROBE(fail3);
574fail2:
575	EFSYS_PROBE(fail2);
576fail1:
577	EFSYS_PROBE1(fail1, int, rc);
578
579	return (rc);
580}
581
582	__checkReturn	int
583efx_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
623fail1:
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
633efx_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
667fail1:
668	EFSYS_PROBE1(fail1, int, rc);
669
670	return (rc);
671}
672
673	__checkReturn	boolean_t
674efx_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
717efx_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