--- /dev/null	2013-08-22 18:57:28.000000000 -0400
+++ new/usr/src/uts/common/io/sfxge/efx_mcdi.c	2013-08-22 18:57:28.153305276 -0400
@@ -0,0 +1,740 @@
+/*
+ * Copyright 2008-2013 Solarflare Communications Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "efsys.h"
+#include "efx.h"
+#include "efx_types.h"
+#include "efx_regs.h"
+#include "efx_regs_mcdi.h"
+#include "efx_impl.h"
+
+#if EFSYS_OPT_MCDI
+
+/*
+ * A reboot/assertion causes the MCDI status word to be set after the
+ * command word is set or a REBOOT event is sent. If we notice a reboot
+ * via these mechanisms then wait 10ms for the status word to be set.
+ */
+#define	MCDI_STATUS_SLEEP_US	10000
+
+			void
+efx_mcdi_request_start(
+	__in		efx_nic_t *enp,
+	__in		efx_mcdi_req_t *emrp,
+	__in		boolean_t ev_cpl)
+{
+	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
+	efx_dword_t dword;
+	unsigned int seq;
+	unsigned int xflags;
+	unsigned int pdur;
+	unsigned int dbr;
+	unsigned int pos;
+	int state;
+
+	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
+	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
+	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
+
+	switch (emip->emi_port)	{
+	case 1:
+		pdur = MC_SMEM_P0_PDU_OFST >> 2;
+		dbr = MC_SMEM_P0_DOORBELL_OFST >> 2;
+		break;
+	case 2:
+		pdur = MC_SMEM_P1_PDU_OFST >> 2;
+		dbr = MC_SMEM_P1_DOORBELL_OFST >> 2;
+		break;
+	default:
+		EFSYS_ASSERT(0);
+		pdur = dbr = 0;
+	};
+
+	/*
+	 * efx_mcdi_request_start() is naturally serialised against both
+	 * efx_mcdi_request_poll() and efx_mcdi_ev_cpl()/efx_mcdi_ev_death(),
+	 * by virtue of there only being one oustanding MCDI request.
+	 * Unfortunately, upper layers may also call efx_mcdi_request_abort()
+	 * at any time, to timeout a pending mcdi request, That request may
+	 * then subsequently complete, meaning efx_mcdi_ev_cpl() or
+	 * efx_mcdi_ev_death() may end up running in parallel with
+	 * efx_mcdi_request_start(). This race is handled by ensuring that
+	 * %emi_pending_req, %emi_ev_cpl and %emi_seq are protected by the
+	 * en_eslp lock.
+	 */
+	EFSYS_LOCK(enp->en_eslp, state);
+	EFSYS_ASSERT(emip->emi_pending_req == NULL);
+	emip->emi_pending_req = emrp;
+	emip->emi_ev_cpl = ev_cpl;
+	emip->emi_poll_cnt = 0;
+	seq = emip->emi_seq++ & 0xf;
+	EFSYS_UNLOCK(enp->en_eslp, state);
+
+	xflags = 0;
+	if (ev_cpl)
+		xflags |= MCDI_HEADER_XFLAGS_EVREQ;
+
+	/* Construct the header in shared memory */
+	EFX_POPULATE_DWORD_6(dword,
+			    MCDI_HEADER_CODE, emrp->emr_cmd,
+			    MCDI_HEADER_RESYNC, 1,
+			    MCDI_HEADER_DATALEN, emrp->emr_in_length,
+			    MCDI_HEADER_SEQ, seq,
+			    MCDI_HEADER_RESPONSE, 0,
+			    MCDI_HEADER_XFLAGS, xflags);
+	EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, pdur, &dword, B_TRUE);
+
+	for (pos = 0; pos < emrp->emr_in_length; pos += sizeof (efx_dword_t)) {
+		memcpy(&dword, MCDI_IN(*emrp, efx_dword_t, pos),
+		    MIN(sizeof (dword), emrp->emr_in_length - pos));
+		EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM,
+		    pdur + 1 + (pos >> 2), &dword, B_FALSE);
+	}
+
+	/* Ring the doorbell */
+	EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 0xd004be11);
+	EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, dbr, &dword, B_FALSE);
+}
+
+static			void
+efx_mcdi_request_copyout(
+	__in		efx_nic_t *enp,
+	__in		efx_mcdi_req_t *emrp)
+{
+	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
+	unsigned int pos;
+	unsigned int pdur;
+	efx_dword_t data;
+
+	pdur = (emip->emi_port == 1)
+	    ? MC_SMEM_P0_PDU_OFST >> 2
+	    : MC_SMEM_P1_PDU_OFST >> 2;
+
+	/* Copy payload out if caller supplied buffer */
+	if (emrp->emr_out_buf != NULL) {
+		size_t bytes = MIN(emrp->emr_out_length_used,
+				    emrp->emr_out_length);
+		for (pos = 0; pos < bytes; pos += sizeof (efx_dword_t)) {
+			EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM,
+			    pdur + 1 + (pos >> 2), &data, B_FALSE);
+			memcpy(MCDI_OUT(*emrp, efx_dword_t, pos), &data,
+			    MIN(sizeof (data), bytes - pos));
+		}
+	}
+}
+
+static			int
+efx_mcdi_request_errcode(
+	__in		unsigned int err)
+{
+
+	switch (err) {
+	case MC_CMD_ERR_ENOENT:
+		return (ENOENT);
+	case MC_CMD_ERR_EINTR:
+		return (EINTR);
+	case MC_CMD_ERR_EACCES:
+		return (EACCES);
+	case MC_CMD_ERR_EBUSY:
+		return (EBUSY);
+	case MC_CMD_ERR_EINVAL:
+		return (EINVAL);
+	case MC_CMD_ERR_EDEADLK:
+		return (EDEADLK);
+	case MC_CMD_ERR_ENOSYS:
+		return (ENOTSUP);
+	case MC_CMD_ERR_ETIME:
+		return (ETIMEDOUT);
+#ifdef WITH_MCDI_V2
+	case MC_CMD_ERR_EAGAIN:
+		return (EAGAIN);
+	case MC_CMD_ERR_ENOSPC:
+		return (ENOSPC);
+#endif
+	default:
+		EFSYS_PROBE1(mc_pcol_error, int, err);
+		return (EIO);
+	}
+}
+
+static			void
+efx_mcdi_raise_exception(
+	__in		efx_nic_t *enp,
+	__in_opt	efx_mcdi_req_t *emrp,
+	__in		int rc)
+{
+	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
+	const efx_mcdi_transport_t *emtp = emip->emi_mtp;
+	efx_mcdi_exception_t exception;
+
+	/* Reboot or Assertion failure only */
+	EFSYS_ASSERT(rc == EIO || rc == EINTR);
+
+	/*
+	 * If MC_CMD_REBOOT causes a reboot (dependent on parameters),
+	 * then the EIO is not worthy of an exception.
+	 */
+	if (emrp != NULL && emrp->emr_cmd == MC_CMD_REBOOT && rc == EIO)
+		return;
+
+	exception = (rc == EIO)
+		? EFX_MCDI_EXCEPTION_MC_REBOOT
+		: EFX_MCDI_EXCEPTION_MC_BADASSERT;
+
+	emtp->emt_exception(emtp->emt_context, exception);
+}
+
+static			int
+efx_mcdi_poll_reboot(
+	__in		efx_nic_t *enp)
+{
+#if 1
+	/*
+	 * XXX Bug 25922, bug 26099: This function is not being used
+	 * properly.  Until its callers are fixed, it should always
+	 * return 0.
+	 */
+	_NOTE(ARGUNUSED(enp))
+	return (0);
+#else
+	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
+	unsigned int rebootr;
+	efx_dword_t dword;
+	uint32_t value;
+
+	EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
+	rebootr = ((emip->emi_port == 1)
+	    ? MC_SMEM_P0_STATUS_OFST >> 2
+	    : MC_SMEM_P1_STATUS_OFST >> 2);
+
+	EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
+	value = EFX_DWORD_FIELD(dword, EFX_DWORD_0);
+
+	if (value == 0)
+		return (0);
+
+	EFX_ZERO_DWORD(dword);
+	EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
+
+	if (value == MC_STATUS_DWORD_ASSERT)
+		return (EINTR);
+	else
+		return (EIO);
+#endif
+}
+
+	__checkReturn	boolean_t
+efx_mcdi_request_poll(
+	__in		efx_nic_t *enp)
+{
+	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
+	efx_mcdi_req_t *emrp;
+	efx_dword_t dword;
+	unsigned int pdur;
+	unsigned int seq;
+	unsigned int length;
+	int state;
+	int rc;
+
+	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
+	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
+	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
+	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
+
+	/* Serialise against post-watchdog efx_mcdi_ev* */
+	EFSYS_LOCK(enp->en_eslp, state);
+
+	EFSYS_ASSERT(emip->emi_pending_req != NULL);
+	EFSYS_ASSERT(!emip->emi_ev_cpl);
+	emrp = emip->emi_pending_req;
+
+	/* Check for reboot atomically w.r.t efx_mcdi_request_start */
+	if (emip->emi_poll_cnt++ == 0) {
+		if ((rc = efx_mcdi_poll_reboot(enp)) != 0) {
+			emip->emi_pending_req = NULL;
+			EFSYS_UNLOCK(enp->en_eslp, state);
+
+			goto fail1;
+		}
+	}
+
+	EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
+	pdur = (emip->emi_port == 1)
+	    ? MC_SMEM_P0_PDU_OFST >> 2
+	    : MC_SMEM_P1_PDU_OFST >> 2;
+
+	/* Read the command header */
+	EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, pdur, &dword, B_FALSE);
+	if (EFX_DWORD_FIELD(dword, MCDI_HEADER_RESPONSE) == 0) {
+		EFSYS_UNLOCK(enp->en_eslp, state);
+		return (B_FALSE);
+	}
+
+	/* Request complete */
+	emip->emi_pending_req = NULL;
+	seq = (emip->emi_seq - 1) & 0xf;
+
+	/* Check for synchronous reboot */
+	if (EFX_DWORD_FIELD(dword, MCDI_HEADER_ERROR) != 0 &&
+	    EFX_DWORD_FIELD(dword, MCDI_HEADER_DATALEN) == 0) {
+		/* Consume status word */
+		EFSYS_SPIN(MCDI_STATUS_SLEEP_US);
+		efx_mcdi_poll_reboot(enp);
+		EFSYS_UNLOCK(enp->en_eslp, state);
+		rc = EIO;
+		goto fail2;
+	}
+
+	EFSYS_UNLOCK(enp->en_eslp, state);
+
+	/* Check that the returned data is consistent */
+	if (EFX_DWORD_FIELD(dword, MCDI_HEADER_CODE) != emrp->emr_cmd ||
+	    EFX_DWORD_FIELD(dword, MCDI_HEADER_SEQ) != seq) {
+		/* Response is for a different request */
+		rc = EIO;
+		goto fail3;
+	}
+
+	length = EFX_DWORD_FIELD(dword, MCDI_HEADER_DATALEN);
+	if (EFX_DWORD_FIELD(dword, MCDI_HEADER_ERROR)) {
+		efx_dword_t errdword;
+		int errcode;
+
+		EFSYS_ASSERT3U(length, ==, 4);
+		EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM,
+		    pdur + 1 + (MC_CMD_ERR_CODE_OFST >> 2),
+		    &errdword, B_FALSE);
+		errcode = EFX_DWORD_FIELD(errdword, EFX_DWORD_0);
+		rc = efx_mcdi_request_errcode(errcode);
+		EFSYS_PROBE2(mcdi_err, int, emrp->emr_cmd, int, errcode);
+		goto fail4;
+
+	} else {
+		emrp->emr_out_length_used = length;
+		emrp->emr_rc = 0;
+		efx_mcdi_request_copyout(enp, emrp);
+	}
+
+	goto out;
+
+fail4:
+	EFSYS_PROBE(fail4);
+fail3:
+	EFSYS_PROBE(fail3);
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, int, rc);
+
+	/* Fill out error state */
+	emrp->emr_rc = rc;
+	emrp->emr_out_length_used = 0;
+
+	/* Reboot/Assertion */
+	if (rc == EIO || rc == EINTR)
+		efx_mcdi_raise_exception(enp, emrp, rc);
+
+out:
+	return (B_TRUE);
+}
+
+			void
+efx_mcdi_execute(
+	__in		efx_nic_t *enp,
+	__in		efx_mcdi_req_t *emrp)
+{
+	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
+	const efx_mcdi_transport_t *emtp = emip->emi_mtp;
+
+	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
+	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
+	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
+
+	emtp->emt_execute(emtp->emt_context, emrp);
+}
+
+			void
+efx_mcdi_ev_cpl(
+	__in		efx_nic_t *enp,
+	__in		unsigned int seq,
+	__in		unsigned int outlen,
+	__in		int errcode)
+{
+	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
+	const efx_mcdi_transport_t *emtp = emip->emi_mtp;
+	efx_mcdi_req_t *emrp;
+	int state;
+
+	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
+	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
+	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
+
+	/*
+	 * Serialise against efx_mcdi_request_poll()/efx_mcdi_request_start()
+	 * when we're completing an aborted request.
+	 */
+	EFSYS_LOCK(enp->en_eslp, state);
+	if (emip->emi_pending_req == NULL || !emip->emi_ev_cpl ||
+	    (seq != ((emip->emi_seq - 1) & 0xf))) {
+		EFSYS_ASSERT(emip->emi_aborted > 0);
+		if (emip->emi_aborted > 0)
+			--emip->emi_aborted;
+		EFSYS_UNLOCK(enp->en_eslp, state);
+		return;
+	}
+
+	emrp = emip->emi_pending_req;
+	emip->emi_pending_req = NULL;
+	EFSYS_UNLOCK(enp->en_eslp, state);
+
+	/*
+	 * Fill out the remaining hdr fields, and copyout the payload
+	 * if the user supplied an output buffer.
+	 */
+	if (errcode != 0) {
+		EFSYS_PROBE2(mcdi_err, int, emrp->emr_cmd,
+		    int, errcode);
+		emrp->emr_out_length_used = 0;
+		emrp->emr_rc = efx_mcdi_request_errcode(errcode);
+	} else {
+		emrp->emr_out_length_used = outlen;
+		emrp->emr_rc = 0;
+		efx_mcdi_request_copyout(enp, emrp);
+	}
+
+	emtp->emt_ev_cpl(emtp->emt_context);
+}
+
+			void
+efx_mcdi_ev_death(
+	__in		efx_nic_t *enp,
+	__in		int rc)
+{
+	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
+	const efx_mcdi_transport_t *emtp = emip->emi_mtp;
+	efx_mcdi_req_t *emrp = NULL;
+	boolean_t ev_cpl;
+	int state;
+
+	/*
+	 * The MCDI request (if there is one) has been terminated, either
+	 * by a BADASSERT or REBOOT event.
+	 *
+	 * If there is an oustanding event-completed MCDI operation, then we
+	 * will never receive the completion event (because both MCDI
+	 * completions and BADASSERT events are sent to the same evq). So
+	 * complete this MCDI op.
+	 *
+	 * This function might run in parallel with efx_mcdi_request_poll()
+	 * for poll completed mcdi requests, and also with
+	 * efx_mcdi_request_start() for post-watchdog completions.
+	 */
+	EFSYS_LOCK(enp->en_eslp, state);
+	emrp = emip->emi_pending_req;
+	ev_cpl = emip->emi_ev_cpl;
+	if (emrp != NULL && emip->emi_ev_cpl) {
+		emip->emi_pending_req = NULL;
+
+		emrp->emr_out_length_used = 0;
+		emrp->emr_rc = rc;
+		++emip->emi_aborted;
+	}
+
+	/*
+	 * Since we're running in parallel with a request, consume the
+	 * status word before dropping the lock.
+	 */
+	if (rc == EIO || rc == EINTR) {
+		EFSYS_SPIN(MCDI_STATUS_SLEEP_US);
+		(void) efx_mcdi_poll_reboot(enp);
+	}
+
+	EFSYS_UNLOCK(enp->en_eslp, state);
+
+	efx_mcdi_raise_exception(enp, emrp, rc);
+
+	if (emrp != NULL && ev_cpl)
+		emtp->emt_ev_cpl(emtp->emt_context);
+}
+
+	__checkReturn		int
+efx_mcdi_version(
+	__in			efx_nic_t *enp,
+	__out_ecount_opt(4)	uint16_t versionp[4],
+	__out_opt		uint32_t *buildp,
+	__out_opt		efx_mcdi_boot_t *statusp)
+{
+	uint8_t outbuf[MAX(MC_CMD_GET_VERSION_OUT_LEN,
+		    MC_CMD_GET_BOOT_STATUS_OUT_LEN)];
+	efx_mcdi_req_t req;
+	efx_word_t *ver_words;
+	uint16_t version[4];
+	uint32_t build;
+	efx_mcdi_boot_t status;
+	int rc;
+
+	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
+	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
+
+	EFX_STATIC_ASSERT(MC_CMD_GET_VERSION_IN_LEN == 0);
+	req.emr_cmd = MC_CMD_GET_VERSION;
+	req.emr_in_buf = NULL;
+	req.emr_in_length = 0;
+	req.emr_out_buf = outbuf;
+	req.emr_out_length = MC_CMD_GET_VERSION_OUT_LEN;
+
+	efx_mcdi_execute(enp, &req);
+
+	if (req.emr_rc != 0) {
+		rc = req.emr_rc;
+		goto fail1;
+	}
+
+	/* bootrom support */
+	if (req.emr_out_length_used == MC_CMD_GET_VERSION_V0_OUT_LEN) {
+		version[0] = version[1] = version[2] = version[3] = 0;
+		build = MCDI_OUT_DWORD(req, GET_VERSION_OUT_FIRMWARE);
+
+		goto version;
+	}
+
+	if (req.emr_out_length_used < MC_CMD_GET_VERSION_OUT_LEN) {
+		rc = EMSGSIZE;
+		goto fail2;
+	}
+
+	ver_words = MCDI_OUT2(req, efx_word_t, GET_VERSION_OUT_VERSION);
+	version[0] = EFX_WORD_FIELD(ver_words[0], EFX_WORD_0);
+	version[1] = EFX_WORD_FIELD(ver_words[1], EFX_WORD_0);
+	version[2] = EFX_WORD_FIELD(ver_words[2], EFX_WORD_0);
+	version[3] = EFX_WORD_FIELD(ver_words[3], EFX_WORD_0);
+	build = MCDI_OUT_DWORD(req, GET_VERSION_OUT_FIRMWARE);
+
+version:
+	/* The bootrom doesn't understand BOOT_STATUS */
+	if (build == MC_CMD_GET_VERSION_OUT_FIRMWARE_BOOTROM) {
+		status = EFX_MCDI_BOOT_ROM;
+		goto out;
+	}
+
+	req.emr_cmd = MC_CMD_GET_BOOT_STATUS;
+	EFX_STATIC_ASSERT(MC_CMD_GET_BOOT_STATUS_IN_LEN == 0);
+	req.emr_in_buf = NULL;
+	req.emr_in_length = 0;
+	req.emr_out_buf = outbuf;
+	req.emr_out_length = MC_CMD_GET_BOOT_STATUS_OUT_LEN;
+
+	efx_mcdi_execute(enp, &req);
+
+	if (req.emr_rc != 0) {
+		rc = req.emr_rc;
+		goto fail3;
+	}
+
+	if (req.emr_out_length_used < MC_CMD_GET_BOOT_STATUS_OUT_LEN) {
+		rc = EMSGSIZE;
+		goto fail4;
+	}
+
+	if (MCDI_OUT_DWORD_FIELD(req, GET_BOOT_STATUS_OUT_FLAGS,
+	    GET_BOOT_STATUS_OUT_FLAGS_PRIMARY))
+		status = EFX_MCDI_BOOT_PRIMARY;
+	else
+		status = EFX_MCDI_BOOT_SECONDARY;
+
+out:
+	if (versionp != NULL)
+		memcpy(versionp, version, sizeof (version));
+	if (buildp != NULL)
+		*buildp = build;
+	if (statusp != NULL)
+		*statusp = status;
+
+	return (0);
+
+fail4:
+	EFSYS_PROBE(fail4);
+fail3:
+	EFSYS_PROBE(fail3);
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, int, rc);
+
+	return (rc);
+}
+
+	__checkReturn	int
+efx_mcdi_init(
+	__in		efx_nic_t *enp,
+	__in		const efx_mcdi_transport_t *mtp)
+{
+	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
+	efx_oword_t oword;
+	unsigned int portnum;
+	int rc;
+
+	EFSYS_ASSERT3U(enp->en_mod_flags, ==, 0);
+	enp->en_mod_flags |= EFX_MOD_MCDI;
+
+	if (enp->en_family == EFX_FAMILY_FALCON)
+		return (0);
+
+	emip->emi_mtp = mtp;
+
+	/* Determine the port number to use for MCDI */
+	EFX_BAR_READO(enp, FR_AZ_CS_DEBUG_REG, &oword);
+	portnum = EFX_OWORD_FIELD(oword, FRF_CZ_CS_PORT_NUM);
+
+	if (portnum == 0) {
+		/* Presumably booted from ROM; only MCDI port 1 will work */
+		emip->emi_port = 1;
+	} else if (portnum <= 2) {
+		emip->emi_port = portnum;
+	} else {
+		rc = EINVAL;
+		goto fail1;
+	}
+
+	/*
+	 * Wipe the atomic reboot status so subsequent MCDI requests succeed.
+	 * BOOT_STATUS is preserved so eno_nic_probe() can boot out of the
+	 * assertion handler.
+	 */
+	(void) efx_mcdi_poll_reboot(enp);
+
+	return (0);
+
+fail1:
+	EFSYS_PROBE1(fail1, int, rc);
+
+	enp->en_mod_flags &= ~EFX_MOD_MCDI;
+
+	return (rc);
+}
+
+
+	__checkReturn	int
+efx_mcdi_reboot(
+	__in		efx_nic_t *enp)
+{
+	uint8_t payload[MC_CMD_REBOOT_IN_LEN];
+	efx_mcdi_req_t req;
+	int rc;
+
+	/*
+	 * We could require the caller to have caused en_mod_flags=0 to
+	 * call this function. This doesn't help the other port though,
+	 * who's about to get the MC ripped out from underneath them.
+	 * Since they have to cope with the subsequent fallout of MCDI
+	 * failures, we should as well.
+	 */
+	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
+
+	req.emr_cmd = MC_CMD_REBOOT;
+	req.emr_in_buf = payload;
+	req.emr_in_length = MC_CMD_REBOOT_IN_LEN;
+	req.emr_out_buf = NULL;
+	req.emr_out_length = 0;
+
+	MCDI_IN_SET_DWORD(req, REBOOT_IN_FLAGS, 0);
+
+	efx_mcdi_execute(enp, &req);
+
+	/* Invert EIO */
+	if (req.emr_rc != EIO) {
+		rc = EIO;
+		goto fail1;
+	}
+
+	return (0);
+
+fail1:
+	EFSYS_PROBE1(fail1, int, rc);
+
+	return (rc);
+}
+
+	__checkReturn	boolean_t
+efx_mcdi_request_abort(
+	__in		efx_nic_t *enp)
+{
+	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
+	efx_mcdi_req_t *emrp;
+	boolean_t aborted;
+	int state;
+
+	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
+	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
+
+	/*
+	 * efx_mcdi_ev_* may have already completed this event, and be
+	 * spinning/blocked on the upper layer lock. So it *is* legitimate
+	 * to for emi_pending_req to be NULL. If there is a pending event
+	 * completed request, then provide a "credit" to allow
+	 * efx_mcdi_ev_cpl() to accept a single spurious completion.
+	 */
+	EFSYS_LOCK(enp->en_eslp, state);
+	emrp = emip->emi_pending_req;
+	aborted = (emrp != NULL);
+	if (aborted) {
+		emip->emi_pending_req = NULL;
+
+		/* Error the request */
+		emrp->emr_out_length_used = 0;
+		emrp->emr_rc = ETIMEDOUT;
+
+		/* Provide a credit for seqno/emr_pending_req mismatches */
+		if (emip->emi_ev_cpl)
+			++emip->emi_aborted;
+
+		/*
+		 * The upper layer has called us, so we don't
+		 * need to complete the request.
+		 */
+	}
+	EFSYS_UNLOCK(enp->en_eslp, state);
+
+	return (aborted);
+}
+
+			void
+efx_mcdi_fini(
+	__in		efx_nic_t *enp)
+{
+	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
+
+	EFSYS_ASSERT3U(enp->en_mod_flags, ==, EFX_MOD_MCDI);
+	enp->en_mod_flags &= ~EFX_MOD_MCDI;
+
+	if (~(enp->en_features) & EFX_FEATURE_MCDI)
+		return;
+
+	emip->emi_mtp = NULL;
+	emip->emi_port = 0;
+	emip->emi_aborted = 0;
+}
+
+#endif	/* EFSYS_OPT_MCDI */
