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
54efx_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
132static			void
133efx_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
157static			int
158efx_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
191static			void
192efx_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
218static			int
219efx_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
248efx_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
339fail4:
340	EFSYS_PROBE(fail4);
341fail3:
342	EFSYS_PROBE(fail3);
343fail2:
344	EFSYS_PROBE(fail2);
345fail1:
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
356out:
357	return (B_TRUE);
358}
359
360			void
361efx_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
376efx_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
428efx_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
479efx_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
531version:
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
563out:
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
573fail4:
574	EFSYS_PROBE(fail4);
575fail3:
576	EFSYS_PROBE(fail3);
577fail2:
578	EFSYS_PROBE(fail2);
579fail1:
580	EFSYS_PROBE1(fail1, int, rc);
581
582	return (rc);
583}
584
585	__checkReturn	int
586efx_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
626fail1:
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
636efx_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
670fail1:
671	EFSYS_PROBE1(fail1, int, rc);
672
673	return (rc);
674}
675
676	__checkReturn	boolean_t
677efx_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
720efx_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