[dpdk-dev,06/56] net/sfc: import libefx MCDI implementation

Message ID 1479740470-6723-7-git-send-email-arybchenko@solarflare.com (mailing list archive)
State Changes Requested, archived
Delegated to: Ferruh Yigit
Headers

Checks

Context Check Description
checkpatch/checkpatch warning coding style issues

Commit Message

Andrew Rybchenko Nov. 21, 2016, 3 p.m. UTC
  Implement interface to talk to NIC managment CPU. Provide
helpers to fill in MCDI requests, execute it and process
recevied response.

MCDI request is prepared in either PCI BAR mapped memory
(SFN5xxx/SFN6xxx) or DMA-mapped memory (SFN7xxx/SFN8xxx) and,
doorbell is pressed (memory-mapped register) to execute it.

Events about MCDI completion are delivered to house-keeping
event queue, but usage of these events is optional and MCDI
buffer may be simply polled waiting for completion
indication set.

From Solarflare Communications Inc.

Signed-off-by: Andrew Rybchenko <arybchenko@solarflare.com>
---
 drivers/net/sfc/efx/base/efx.h       |   59 ++
 drivers/net/sfc/efx/base/efx_check.h |    5 +
 drivers/net/sfc/efx/base/efx_ev.c    |   10 +
 drivers/net/sfc/efx/base/efx_impl.h  |   47 +
 drivers/net/sfc/efx/base/efx_mcdi.c  | 1725 ++++++++++++++++++++++++++++++++++
 drivers/net/sfc/efx/base/efx_mcdi.h  |  371 ++++++++
 drivers/net/sfc/efx/base/efx_nic.c   |    6 +
 7 files changed, 2223 insertions(+)
 create mode 100644 drivers/net/sfc/efx/base/efx_mcdi.c
 create mode 100644 drivers/net/sfc/efx/base/efx_mcdi.h
  

Patch

diff --git a/drivers/net/sfc/efx/base/efx.h b/drivers/net/sfc/efx/base/efx.h
index 485ce51..8b9d4d1 100644
--- a/drivers/net/sfc/efx/base/efx.h
+++ b/drivers/net/sfc/efx/base/efx.h
@@ -182,6 +182,62 @@  efx_nic_check_pcie_link_speed(
 	__in		uint32_t pcie_link_gen,
 	__out		efx_pcie_link_performance_t *resultp);
 
+#if EFSYS_OPT_MCDI
+
+typedef struct efx_mcdi_req_s efx_mcdi_req_t;
+
+typedef enum efx_mcdi_exception_e {
+	EFX_MCDI_EXCEPTION_MC_REBOOT,
+	EFX_MCDI_EXCEPTION_MC_BADASSERT,
+} efx_mcdi_exception_t;
+
+typedef struct efx_mcdi_transport_s {
+	void		*emt_context;
+	efsys_mem_t	*emt_dma_mem;
+	void		(*emt_execute)(void *, efx_mcdi_req_t *);
+	void		(*emt_ev_cpl)(void *);
+	void		(*emt_exception)(void *, efx_mcdi_exception_t);
+} efx_mcdi_transport_t;
+
+extern	__checkReturn	efx_rc_t
+efx_mcdi_init(
+	__in		efx_nic_t *enp,
+	__in		const efx_mcdi_transport_t *mtp);
+
+extern	__checkReturn	efx_rc_t
+efx_mcdi_reboot(
+	__in		efx_nic_t *enp);
+
+			void
+efx_mcdi_new_epoch(
+	__in		efx_nic_t *enp);
+
+extern			void
+efx_mcdi_get_timeout(
+	__in		efx_nic_t *enp,
+	__in		efx_mcdi_req_t *emrp,
+	__out		uint32_t *usec_timeoutp);
+
+extern			void
+efx_mcdi_request_start(
+	__in		efx_nic_t *enp,
+	__in		efx_mcdi_req_t *emrp,
+	__in		boolean_t ev_cpl);
+
+extern	__checkReturn	boolean_t
+efx_mcdi_request_poll(
+	__in		efx_nic_t *enp);
+
+extern	__checkReturn	boolean_t
+efx_mcdi_request_abort(
+	__in		efx_nic_t *enp);
+
+extern			void
+efx_mcdi_fini(
+	__in		efx_nic_t *enp);
+
+#endif	/* EFSYS_OPT_MCDI */
+
 /* INTR */
 
 #define	EFX_NINTR_SIENA 1024
@@ -507,6 +563,9 @@  typedef struct efx_nic_cfg_s {
 	uint32_t		enc_rx_prefix_size;
 	uint32_t		enc_rx_buf_align_start;
 	uint32_t		enc_rx_buf_align_end;
+#if EFSYS_OPT_MCDI
+	uint8_t			enc_mcdi_mdio_channel;
+#endif	/* EFSYS_OPT_MCDI */
 	boolean_t		enc_bug26807_workaround;
 	boolean_t		enc_bug35388_workaround;
 	boolean_t		enc_bug41750_workaround;
diff --git a/drivers/net/sfc/efx/base/efx_check.h b/drivers/net/sfc/efx/base/efx_check.h
index 555c184..9d0e988 100644
--- a/drivers/net/sfc/efx/base/efx_check.h
+++ b/drivers/net/sfc/efx/base/efx_check.h
@@ -72,6 +72,11 @@ 
 # error "MAC_FALCON_XMAC is obsolete and is not supported."
 #endif
 
+#if EFSYS_OPT_MCDI
+/* Support management controller messages */
+#  error "MCDI requires SIENA or HUNTINGTON or MEDFORD"
+#endif /* EFSYS_OPT_MCDI */
+
 #ifdef EFSYS_OPT_MON_LM87
 # error "MON_LM87 is obsolete and is not supported."
 #endif
diff --git a/drivers/net/sfc/efx/base/efx_ev.c b/drivers/net/sfc/efx/base/efx_ev.c
index 2bd365f..942dac6 100644
--- a/drivers/net/sfc/efx/base/efx_ev.c
+++ b/drivers/net/sfc/efx/base/efx_ev.c
@@ -270,6 +270,10 @@  efx_ev_qpoll(
 	EFX_STATIC_ASSERT(ESE_DZ_EV_CODE_DRIVER_EV == FSE_AZ_EV_CODE_DRIVER_EV);
 	EFX_STATIC_ASSERT(ESE_DZ_EV_CODE_DRV_GEN_EV ==
 	    FSE_AZ_EV_CODE_DRV_GEN_EV);
+#if EFSYS_OPT_MCDI
+	EFX_STATIC_ASSERT(ESE_DZ_EV_CODE_MCDI_EV ==
+	    FSE_AZ_EV_CODE_MCDI_EVRESPONSE);
+#endif
 
 	EFSYS_ASSERT3U(eep->ee_magic, ==, EFX_EVQ_MAGIC);
 	EFSYS_ASSERT(countp != NULL);
@@ -318,6 +322,12 @@  efx_ev_qpoll(
 				should_abort = eep->ee_drv_gen(eep,
 				    &(ev[index]), eecp, arg);
 				break;
+#if EFSYS_OPT_MCDI
+			case FSE_AZ_EV_CODE_MCDI_EVRESPONSE:
+				should_abort = eep->ee_mcdi(eep,
+				    &(ev[index]), eecp, arg);
+				break;
+#endif
 			case FSE_AZ_EV_CODE_GLOBAL_EV:
 				if (eep->ee_global) {
 					should_abort = eep->ee_global(eep,
diff --git a/drivers/net/sfc/efx/base/efx_impl.h b/drivers/net/sfc/efx/base/efx_impl.h
index 47d4819..c6ec808 100644
--- a/drivers/net/sfc/efx/base/efx_impl.h
+++ b/drivers/net/sfc/efx/base/efx_impl.h
@@ -279,6 +279,30 @@  typedef struct efx_filter_s {
 
 #endif	/* EFSYS_OPT_FILTER */
 
+#if EFSYS_OPT_MCDI
+
+typedef struct efx_mcdi_ops_s {
+	efx_rc_t	(*emco_init)(efx_nic_t *, const efx_mcdi_transport_t *);
+	void		(*emco_send_request)(efx_nic_t *, void *, size_t,
+					void *, size_t);
+	efx_rc_t	(*emco_poll_reboot)(efx_nic_t *);
+	boolean_t	(*emco_poll_response)(efx_nic_t *);
+	void		(*emco_read_response)(efx_nic_t *, void *, size_t, size_t);
+	void		(*emco_fini)(efx_nic_t *);
+	efx_rc_t	(*emco_feature_supported)(efx_nic_t *,
+					    efx_mcdi_feature_id_t, boolean_t *);
+	void		(*emco_get_timeout)(efx_nic_t *, efx_mcdi_req_t *,
+					    uint32_t *);
+} efx_mcdi_ops_t;
+
+typedef struct efx_mcdi_s {
+	const efx_mcdi_ops_t		*em_emcop;
+	const efx_mcdi_transport_t	*em_emtp;
+	efx_mcdi_iface_t		em_emip;
+} efx_mcdi_t;
+
+#endif /* EFSYS_OPT_MCDI */
+
 typedef struct efx_drv_cfg_s {
 	uint32_t		edc_min_vi_count;
 	uint32_t		edc_max_vi_count;
@@ -312,6 +336,9 @@  struct efx_nic_s {
 	efx_filter_t		en_filter;
 	const efx_filter_ops_t	*en_efop;
 #endif	/* EFSYS_OPT_FILTER */
+#if EFSYS_OPT_MCDI
+	efx_mcdi_t		en_mcdi;
+#endif	/* EFSYS_OPT_MCDI */
 	uint32_t		en_vport_id;
 	union {
 		int	enu_unused;
@@ -341,6 +368,9 @@  struct efx_evq_s {
 	efx_ev_handler_t		ee_driver;
 	efx_ev_handler_t		ee_global;
 	efx_ev_handler_t		ee_drv_gen;
+#if EFSYS_OPT_MCDI
+	efx_ev_handler_t		ee_mcdi;
+#endif	/* EFSYS_OPT_MCDI */
 
 	efx_evq_rxq_state_t		ee_rxq_state[EFX_EV_RX_NLABELS];
 
@@ -689,6 +719,23 @@  extern			void
 efx_phy_unprobe(
 	__in		efx_nic_t *enp);
 
+#if EFSYS_OPT_MCDI
+
+extern	__checkReturn		efx_rc_t
+efx_mcdi_set_workaround(
+	__in			efx_nic_t *enp,
+	__in			uint32_t type,
+	__in			boolean_t enabled,
+	__out_opt		uint32_t *flagsp);
+
+extern	__checkReturn		efx_rc_t
+efx_mcdi_get_workarounds(
+	__in			efx_nic_t *enp,
+	__out_opt		uint32_t *implementedp,
+	__out_opt		uint32_t *enabledp);
+
+#endif /* EFSYS_OPT_MCDI */
+
 #ifdef	__cplusplus
 }
 #endif
diff --git a/drivers/net/sfc/efx/base/efx_mcdi.c b/drivers/net/sfc/efx/base/efx_mcdi.c
new file mode 100644
index 0000000..643ba6a
--- /dev/null
+++ b/drivers/net/sfc/efx/base/efx_mcdi.c
@@ -0,0 +1,1725 @@ 
+/*
+ * Copyright (c) 2008-2016 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
+ *
+ * The views and conclusions contained in the software and documentation are
+ * those of the authors and should not be interpreted as representing official
+ * policies, either expressed or implied, of the FreeBSD Project.
+ */
+
+#include "efx.h"
+#include "efx_impl.h"
+
+#if EFSYS_OPT_MCDI
+
+/*
+ * There are three versions of the MCDI interface:
+ *  - MCDIv0: Siena BootROM. Transport uses MCDIv1 headers.
+ *  - MCDIv1: Siena firmware and Huntington BootROM.
+ *  - MCDIv2: EF10 firmware (Huntington/Medford) and Medford BootROM.
+ *            Transport uses MCDIv2 headers.
+ *
+ * MCDIv2 Header NOT_EPOCH flag
+ * ----------------------------
+ * A new epoch begins at initial startup or after an MC reboot, and defines when
+ * the MC should reject stale MCDI requests.
+ *
+ * The first MCDI request sent by the host should contain NOT_EPOCH=0, and all
+ * subsequent requests (until the next MC reboot) should contain NOT_EPOCH=1.
+ *
+ * After rebooting the MC will fail all requests with NOT_EPOCH=1 by writing a
+ * response with ERROR=1 and DATALEN=0 until a request is seen with NOT_EPOCH=0.
+ */
+
+
+
+	__checkReturn	efx_rc_t
+efx_mcdi_init(
+	__in		efx_nic_t *enp,
+	__in		const efx_mcdi_transport_t *emtp)
+{
+	const efx_mcdi_ops_t *emcop;
+	efx_rc_t rc;
+
+	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
+	EFSYS_ASSERT3U(enp->en_mod_flags, ==, 0);
+
+	switch (enp->en_family) {
+
+	default:
+		EFSYS_ASSERT(0);
+		rc = ENOTSUP;
+		goto fail1;
+	}
+
+	if (enp->en_features & EFX_FEATURE_MCDI_DMA) {
+		/* MCDI requires a DMA buffer in host memory */
+		if ((emtp == NULL) || (emtp->emt_dma_mem) == NULL) {
+			rc = EINVAL;
+			goto fail2;
+		}
+	}
+	enp->en_mcdi.em_emtp = emtp;
+
+	if (emcop != NULL && emcop->emco_init != NULL) {
+		if ((rc = emcop->emco_init(enp, emtp)) != 0)
+			goto fail3;
+	}
+
+	enp->en_mcdi.em_emcop = emcop;
+	enp->en_mod_flags |= EFX_MOD_MCDI;
+
+	return (0);
+
+fail3:
+	EFSYS_PROBE(fail3);
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	enp->en_mcdi.em_emcop = NULL;
+	enp->en_mcdi.em_emtp = NULL;
+	enp->en_mod_flags &= ~EFX_MOD_MCDI;
+
+	return (rc);
+}
+
+			void
+efx_mcdi_fini(
+	__in		efx_nic_t *enp)
+{
+	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
+	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
+
+	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
+	EFSYS_ASSERT3U(enp->en_mod_flags, ==, EFX_MOD_MCDI);
+
+	if (emcop != NULL && emcop->emco_fini != NULL)
+		emcop->emco_fini(enp);
+
+	emip->emi_port = 0;
+	emip->emi_aborted = 0;
+
+	enp->en_mcdi.em_emcop = NULL;
+	enp->en_mod_flags &= ~EFX_MOD_MCDI;
+}
+
+			void
+efx_mcdi_new_epoch(
+	__in		efx_nic_t *enp)
+{
+	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
+	efsys_lock_state_t state;
+
+	/* Start a new epoch (allow fresh MCDI requests to succeed) */
+	EFSYS_LOCK(enp->en_eslp, state);
+	emip->emi_new_epoch = B_TRUE;
+	EFSYS_UNLOCK(enp->en_eslp, state);
+}
+
+static			void
+efx_mcdi_send_request(
+	__in		efx_nic_t *enp,
+	__in		void *hdrp,
+	__in		size_t hdr_len,
+	__in		void *sdup,
+	__in		size_t sdu_len)
+{
+	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
+
+	emcop->emco_send_request(enp, hdrp, hdr_len, sdup, sdu_len);
+}
+
+static			efx_rc_t
+efx_mcdi_poll_reboot(
+	__in		efx_nic_t *enp)
+{
+	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
+	efx_rc_t rc;
+
+	rc = emcop->emco_poll_reboot(enp);
+	return (rc);
+}
+
+static			boolean_t
+efx_mcdi_poll_response(
+	__in		efx_nic_t *enp)
+{
+	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
+	boolean_t available;
+
+	available = emcop->emco_poll_response(enp);
+	return (available);
+}
+
+static			void
+efx_mcdi_read_response(
+	__in		efx_nic_t *enp,
+	__out		void *bufferp,
+	__in		size_t offset,
+	__in		size_t length)
+{
+	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
+
+	emcop->emco_read_response(enp, bufferp, offset, length);
+}
+
+			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_mcdi.em_emip);
+	efx_dword_t hdr[2];
+	size_t hdr_len;
+	unsigned int max_version;
+	unsigned int seq;
+	unsigned int xflags;
+	boolean_t new_epoch;
+	efsys_lock_state_t 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);
+
+	/*
+	 * 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 outstanding 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++ & EFX_MASK32(MCDI_HEADER_SEQ);
+	new_epoch = emip->emi_new_epoch;
+	max_version = emip->emi_max_version;
+	EFSYS_UNLOCK(enp->en_eslp, state);
+
+	xflags = 0;
+	if (ev_cpl)
+		xflags |= MCDI_HEADER_XFLAGS_EVREQ;
+
+	/*
+	 * Huntington firmware supports MCDIv2, but the Huntington BootROM only
+	 * supports MCDIv1. Use MCDIv1 headers for MCDIv1 commands where
+	 * possible to support this.
+	 */
+	if ((max_version >= 2) &&
+	    ((emrp->emr_cmd > MC_CMD_CMD_SPACE_ESCAPE_7) ||
+	    (emrp->emr_in_length > MCDI_CTL_SDU_LEN_MAX_V1))) {
+		/* Construct MCDI v2 header */
+		hdr_len = sizeof (hdr);
+		EFX_POPULATE_DWORD_8(hdr[0],
+		    MCDI_HEADER_CODE, MC_CMD_V2_EXTN,
+		    MCDI_HEADER_RESYNC, 1,
+		    MCDI_HEADER_DATALEN, 0,
+		    MCDI_HEADER_SEQ, seq,
+		    MCDI_HEADER_NOT_EPOCH, new_epoch ? 0 : 1,
+		    MCDI_HEADER_ERROR, 0,
+		    MCDI_HEADER_RESPONSE, 0,
+		    MCDI_HEADER_XFLAGS, xflags);
+
+		EFX_POPULATE_DWORD_2(hdr[1],
+		    MC_CMD_V2_EXTN_IN_EXTENDED_CMD, emrp->emr_cmd,
+		    MC_CMD_V2_EXTN_IN_ACTUAL_LEN, emrp->emr_in_length);
+	} else {
+		/* Construct MCDI v1 header */
+		hdr_len = sizeof (hdr[0]);
+		EFX_POPULATE_DWORD_8(hdr[0],
+		    MCDI_HEADER_CODE, emrp->emr_cmd,
+		    MCDI_HEADER_RESYNC, 1,
+		    MCDI_HEADER_DATALEN, emrp->emr_in_length,
+		    MCDI_HEADER_SEQ, seq,
+		    MCDI_HEADER_NOT_EPOCH, new_epoch ? 0 : 1,
+		    MCDI_HEADER_ERROR, 0,
+		    MCDI_HEADER_RESPONSE, 0,
+		    MCDI_HEADER_XFLAGS, xflags);
+	}
+
+	efx_mcdi_send_request(enp, &hdr[0], hdr_len,
+	    emrp->emr_in_buf, emrp->emr_in_length);
+}
+
+
+static			void
+efx_mcdi_read_response_header(
+	__in		efx_nic_t *enp,
+	__inout		efx_mcdi_req_t *emrp)
+{
+	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
+	efx_dword_t hdr[2];
+	unsigned int hdr_len;
+	unsigned int data_len;
+	unsigned int seq;
+	unsigned int cmd;
+	unsigned int error;
+	efx_rc_t rc;
+
+	EFSYS_ASSERT(emrp != NULL);
+
+	efx_mcdi_read_response(enp, &hdr[0], 0, sizeof (hdr[0]));
+	hdr_len = sizeof (hdr[0]);
+
+	cmd = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_CODE);
+	seq = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_SEQ);
+	error = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_ERROR);
+
+	if (cmd != MC_CMD_V2_EXTN) {
+		data_len = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_DATALEN);
+	} else {
+		efx_mcdi_read_response(enp, &hdr[1], hdr_len, sizeof (hdr[1]));
+		hdr_len += sizeof (hdr[1]);
+
+		cmd = EFX_DWORD_FIELD(hdr[1], MC_CMD_V2_EXTN_IN_EXTENDED_CMD);
+		data_len =
+		    EFX_DWORD_FIELD(hdr[1], MC_CMD_V2_EXTN_IN_ACTUAL_LEN);
+	}
+
+	if (error && (data_len == 0)) {
+		/* The MC has rebooted since the request was sent. */
+		EFSYS_SPIN(EFX_MCDI_STATUS_SLEEP_US);
+		efx_mcdi_poll_reboot(enp);
+		rc = EIO;
+		goto fail1;
+	}
+	if ((cmd != emrp->emr_cmd) ||
+	    (seq != ((emip->emi_seq - 1) & EFX_MASK32(MCDI_HEADER_SEQ)))) {
+		/* Response is for a different request */
+		rc = EIO;
+		goto fail2;
+	}
+	if (error) {
+		efx_dword_t err[2];
+		unsigned int err_len = MIN(data_len, sizeof (err));
+		int err_code = MC_CMD_ERR_EPROTO;
+		int err_arg = 0;
+
+		/* Read error code (and arg num for MCDI v2 commands) */
+		efx_mcdi_read_response(enp, &err, hdr_len, err_len);
+
+		if (err_len >= (MC_CMD_ERR_CODE_OFST + sizeof (efx_dword_t)))
+			err_code = EFX_DWORD_FIELD(err[0], EFX_DWORD_0);
+#ifdef WITH_MCDI_V2
+		if (err_len >= (MC_CMD_ERR_ARG_OFST + sizeof (efx_dword_t)))
+			err_arg = EFX_DWORD_FIELD(err[1], EFX_DWORD_0);
+#endif
+		emrp->emr_err_code = err_code;
+		emrp->emr_err_arg = err_arg;
+
+		if (!emrp->emr_quiet) {
+			EFSYS_PROBE3(mcdi_err_arg, int, emrp->emr_cmd,
+			    int, err_code, int, err_arg);
+		}
+
+		rc = efx_mcdi_request_errcode(err_code);
+		goto fail3;
+	}
+
+	emrp->emr_rc = 0;
+	emrp->emr_out_length_used = data_len;
+	return;
+
+fail3:
+fail2:
+fail1:
+	emrp->emr_rc = rc;
+	emrp->emr_out_length_used = 0;
+}
+
+static			void
+efx_mcdi_finish_response(
+	__in		efx_nic_t *enp,
+	__in		efx_mcdi_req_t *emrp)
+{
+	efx_dword_t hdr[2];
+	unsigned int hdr_len;
+	size_t bytes;
+
+	if (emrp->emr_out_buf == NULL)
+		return;
+
+	/* Read the command header to detect MCDI response format */
+	hdr_len = sizeof (hdr[0]);
+	efx_mcdi_read_response(enp, &hdr[0], 0, hdr_len);
+	if (EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_CODE) == MC_CMD_V2_EXTN) {
+		/*
+		 * Read the actual payload length. The length given in the event
+		 * is only correct for responses with the V1 format.
+		 */
+		efx_mcdi_read_response(enp, &hdr[1], hdr_len, sizeof (hdr[1]));
+		hdr_len += sizeof (hdr[1]);
+
+		emrp->emr_out_length_used = EFX_DWORD_FIELD(hdr[1],
+					    MC_CMD_V2_EXTN_IN_ACTUAL_LEN);
+	}
+
+	/* Copy payload out into caller supplied buffer */
+	bytes = MIN(emrp->emr_out_length_used, emrp->emr_out_length);
+	efx_mcdi_read_response(enp, emrp->emr_out_buf, hdr_len, bytes);
+
+}
+
+
+	__checkReturn	boolean_t
+efx_mcdi_request_poll(
+	__in		efx_nic_t *enp)
+{
+	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
+	efx_mcdi_req_t *emrp;
+	efsys_lock_state_t state;
+	efx_rc_t rc;
+
+	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);
+
+	/* 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);
+
+			/* Reboot/Assertion */
+			if (rc == EIO || rc == EINTR)
+				efx_mcdi_raise_exception(enp, emrp, rc);
+
+			goto fail1;
+		}
+	}
+
+	/* Check if a response is available */
+	if (efx_mcdi_poll_response(enp) == B_FALSE) {
+		EFSYS_UNLOCK(enp->en_eslp, state);
+		return (B_FALSE);
+	}
+
+	/* Read the response header */
+	efx_mcdi_read_response_header(enp, emrp);
+
+	/* Request complete */
+	emip->emi_pending_req = NULL;
+
+	/* Ensure stale MCDI requests fail after an MC reboot. */
+	emip->emi_new_epoch = B_FALSE;
+
+	EFSYS_UNLOCK(enp->en_eslp, state);
+
+	if ((rc = emrp->emr_rc) != 0)
+		goto fail2;
+
+	efx_mcdi_finish_response(enp, emrp);
+	return (B_TRUE);
+
+fail2:
+	if (!emrp->emr_quiet)
+		EFSYS_PROBE(fail2);
+fail1:
+	if (!emrp->emr_quiet)
+		EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (B_TRUE);
+}
+
+	__checkReturn	boolean_t
+efx_mcdi_request_abort(
+	__in		efx_nic_t *enp)
+{
+	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
+	efx_mcdi_req_t *emrp;
+	boolean_t aborted;
+	efsys_lock_state_t 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);
+
+	/*
+	 * 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_get_timeout(
+	__in		efx_nic_t *enp,
+	__in		efx_mcdi_req_t *emrp,
+	__out		uint32_t *timeoutp)
+{
+	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
+
+	emcop->emco_get_timeout(enp, emrp, timeoutp);
+}
+
+	__checkReturn	efx_rc_t
+efx_mcdi_request_errcode(
+	__in		unsigned int err)
+{
+
+	switch (err) {
+		/* MCDI v1 */
+	case MC_CMD_ERR_EPERM:
+		return (EACCES);
+	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);
+	case MC_CMD_ERR_ENOTSUP:
+		return (ENOTSUP);
+	case MC_CMD_ERR_EALREADY:
+		return (EALREADY);
+
+		/* MCDI v2 */
+	case MC_CMD_ERR_EEXIST:
+		return (EEXIST);
+#ifdef MC_CMD_ERR_EAGAIN
+	case MC_CMD_ERR_EAGAIN:
+		return (EAGAIN);
+#endif
+#ifdef MC_CMD_ERR_ENOSPC
+	case MC_CMD_ERR_ENOSPC:
+		return (ENOSPC);
+#endif
+	case MC_CMD_ERR_ERANGE:
+		return (ERANGE);
+
+	case MC_CMD_ERR_ALLOC_FAIL:
+		return (ENOMEM);
+	case MC_CMD_ERR_NO_VADAPTOR:
+		return (ENOENT);
+	case MC_CMD_ERR_NO_EVB_PORT:
+		return (ENOENT);
+	case MC_CMD_ERR_NO_VSWITCH:
+		return (ENODEV);
+	case MC_CMD_ERR_VLAN_LIMIT:
+		return (EINVAL);
+	case MC_CMD_ERR_BAD_PCI_FUNC:
+		return (ENODEV);
+	case MC_CMD_ERR_BAD_VLAN_MODE:
+		return (EINVAL);
+	case MC_CMD_ERR_BAD_VSWITCH_TYPE:
+		return (EINVAL);
+	case MC_CMD_ERR_BAD_VPORT_TYPE:
+		return (EINVAL);
+	case MC_CMD_ERR_MAC_EXIST:
+		return (EEXIST);
+
+	case MC_CMD_ERR_PROXY_PENDING:
+		return (EAGAIN);
+
+	default:
+		EFSYS_PROBE1(mc_pcol_error, int, err);
+		return (EIO);
+	}
+}
+
+			void
+efx_mcdi_raise_exception(
+	__in		efx_nic_t *enp,
+	__in_opt	efx_mcdi_req_t *emrp,
+	__in		int rc)
+{
+	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
+	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);
+}
+
+			void
+efx_mcdi_execute(
+	__in		efx_nic_t *enp,
+	__inout		efx_mcdi_req_t *emrp)
+{
+	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
+
+	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
+	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
+
+	emrp->emr_quiet = B_FALSE;
+	emtp->emt_execute(emtp->emt_context, emrp);
+}
+
+			void
+efx_mcdi_execute_quiet(
+	__in		efx_nic_t *enp,
+	__inout		efx_mcdi_req_t *emrp)
+{
+	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
+
+	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
+	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
+
+	emrp->emr_quiet = B_TRUE;
+	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_mcdi.em_emip);
+	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
+	efx_mcdi_req_t *emrp;
+	efsys_lock_state_t state;
+
+	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
+	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) & EFX_MASK32(MCDI_HEADER_SEQ)))) {
+		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);
+
+	if (emip->emi_max_version >= 2) {
+		/* MCDIv2 response details do not fit into an event. */
+		efx_mcdi_read_response_header(enp, emrp);
+	} else {
+		if (errcode != 0) {
+			if (!emrp->emr_quiet) {
+				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;
+		}
+	}
+	if (errcode == 0) {
+		efx_mcdi_finish_response(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_mcdi.em_emip);
+	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
+	efx_mcdi_req_t *emrp = NULL;
+	boolean_t ev_cpl;
+	efsys_lock_state_t state;
+
+	/*
+	 * The MCDI request (if there is one) has been terminated, either
+	 * by a BADASSERT or REBOOT event.
+	 *
+	 * If there is an outstanding 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(EFX_MCDI_STATUS_SLEEP_US);
+		(void) efx_mcdi_poll_reboot(enp);
+		emip->emi_new_epoch = B_TRUE;
+	}
+
+	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		efx_rc_t
+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)
+{
+	efx_mcdi_req_t req;
+	uint8_t payload[MAX(MAX(MC_CMD_GET_VERSION_IN_LEN,
+				MC_CMD_GET_VERSION_OUT_LEN),
+			    MAX(MC_CMD_GET_BOOT_STATUS_IN_LEN,
+				MC_CMD_GET_BOOT_STATUS_OUT_LEN))];
+	efx_word_t *ver_words;
+	uint16_t version[4];
+	uint32_t build;
+	efx_mcdi_boot_t status;
+	efx_rc_t rc;
+
+	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
+
+	(void) memset(payload, 0, sizeof (payload));
+	req.emr_cmd = MC_CMD_GET_VERSION;
+	req.emr_in_buf = payload;
+	req.emr_in_length = MC_CMD_GET_VERSION_IN_LEN;
+	req.emr_out_buf = payload;
+	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 (MC_FW_VERSION_IS_BOOTLOADER(build)) {
+		status = EFX_MCDI_BOOT_ROM;
+		goto out;
+	}
+
+	(void) memset(payload, 0, sizeof (payload));
+	req.emr_cmd = MC_CMD_GET_BOOT_STATUS;
+	req.emr_in_buf = payload;
+	req.emr_in_length = MC_CMD_GET_BOOT_STATUS_IN_LEN;
+	req.emr_out_buf = payload;
+	req.emr_out_length = MC_CMD_GET_BOOT_STATUS_OUT_LEN;
+
+	efx_mcdi_execute_quiet(enp, &req);
+
+	if (req.emr_rc == EACCES) {
+		/* Unprivileged functions cannot access BOOT_STATUS */
+		status = EFX_MCDI_BOOT_PRIMARY;
+		version[0] = version[1] = version[2] = version[3] = 0;
+		build = 0;
+		goto out;
+	}
+
+	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, efx_rc_t, rc);
+
+	return (rc);
+}
+
+static	__checkReturn	efx_rc_t
+efx_mcdi_do_reboot(
+	__in		efx_nic_t *enp,
+	__in		boolean_t after_assertion)
+{
+	uint8_t payload[MAX(MC_CMD_REBOOT_IN_LEN, MC_CMD_REBOOT_OUT_LEN)];
+	efx_mcdi_req_t req;
+	efx_rc_t 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);
+
+	(void) memset(payload, 0, sizeof (payload));
+	req.emr_cmd = MC_CMD_REBOOT;
+	req.emr_in_buf = payload;
+	req.emr_in_length = MC_CMD_REBOOT_IN_LEN;
+	req.emr_out_buf = payload;
+	req.emr_out_length = MC_CMD_REBOOT_OUT_LEN;
+
+	MCDI_IN_SET_DWORD(req, REBOOT_IN_FLAGS,
+	    (after_assertion ? MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION : 0));
+
+	efx_mcdi_execute_quiet(enp, &req);
+
+	if (req.emr_rc == EACCES) {
+		/* Unprivileged functions cannot reboot the MC. */
+		goto out;
+	}
+
+	/* A successful reboot request returns EIO. */
+	if (req.emr_rc != 0 && req.emr_rc != EIO) {
+		rc = req.emr_rc;
+		goto fail1;
+	}
+
+out:
+	return (0);
+
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+	__checkReturn	efx_rc_t
+efx_mcdi_reboot(
+	__in		efx_nic_t *enp)
+{
+	return (efx_mcdi_do_reboot(enp, B_FALSE));
+}
+
+	__checkReturn	efx_rc_t
+efx_mcdi_exit_assertion_handler(
+	__in		efx_nic_t *enp)
+{
+	return (efx_mcdi_do_reboot(enp, B_TRUE));
+}
+
+	__checkReturn	efx_rc_t
+efx_mcdi_read_assertion(
+	__in		efx_nic_t *enp)
+{
+	efx_mcdi_req_t req;
+	uint8_t payload[MAX(MC_CMD_GET_ASSERTS_IN_LEN,
+			    MC_CMD_GET_ASSERTS_OUT_LEN)];
+	const char *reason;
+	unsigned int flags;
+	unsigned int index;
+	unsigned int ofst;
+	int retry;
+	efx_rc_t rc;
+
+	/*
+	 * Before we attempt to chat to the MC, we should verify that the MC
+	 * isn't in it's assertion handler, either due to a previous reboot,
+	 * or because we're reinitializing due to an eec_exception().
+	 *
+	 * Use GET_ASSERTS to read any assertion state that may be present.
+	 * Retry this command twice. Once because a boot-time assertion failure
+	 * might cause the 1st MCDI request to fail. And once again because
+	 * we might race with efx_mcdi_exit_assertion_handler() running on
+	 * partner port(s) on the same NIC.
+	 */
+	retry = 2;
+	do {
+		(void) memset(payload, 0, sizeof (payload));
+		req.emr_cmd = MC_CMD_GET_ASSERTS;
+		req.emr_in_buf = payload;
+		req.emr_in_length = MC_CMD_GET_ASSERTS_IN_LEN;
+		req.emr_out_buf = payload;
+		req.emr_out_length = MC_CMD_GET_ASSERTS_OUT_LEN;
+
+		MCDI_IN_SET_DWORD(req, GET_ASSERTS_IN_CLEAR, 1);
+		efx_mcdi_execute_quiet(enp, &req);
+
+	} while ((req.emr_rc == EINTR || req.emr_rc == EIO) && retry-- > 0);
+
+	if (req.emr_rc != 0) {
+		if (req.emr_rc == EACCES) {
+			/* Unprivileged functions cannot clear assertions. */
+			goto out;
+		}
+		rc = req.emr_rc;
+		goto fail1;
+	}
+
+	if (req.emr_out_length_used < MC_CMD_GET_ASSERTS_OUT_LEN) {
+		rc = EMSGSIZE;
+		goto fail2;
+	}
+
+	/* Print out any assertion state recorded */
+	flags = MCDI_OUT_DWORD(req, GET_ASSERTS_OUT_GLOBAL_FLAGS);
+	if (flags == MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS)
+		return (0);
+
+	reason = (flags == MC_CMD_GET_ASSERTS_FLAGS_SYS_FAIL)
+		? "system-level assertion"
+		: (flags == MC_CMD_GET_ASSERTS_FLAGS_THR_FAIL)
+		? "thread-level assertion"
+		: (flags == MC_CMD_GET_ASSERTS_FLAGS_WDOG_FIRED)
+		? "watchdog reset"
+		: (flags == MC_CMD_GET_ASSERTS_FLAGS_ADDR_TRAP)
+		? "illegal address trap"
+		: "unknown assertion";
+	EFSYS_PROBE3(mcpu_assertion,
+	    const char *, reason, unsigned int,
+	    MCDI_OUT_DWORD(req, GET_ASSERTS_OUT_SAVED_PC_OFFS),
+	    unsigned int,
+	    MCDI_OUT_DWORD(req, GET_ASSERTS_OUT_THREAD_OFFS));
+
+	/* Print out the registers (r1 ... r31) */
+	ofst = MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_OFST;
+	for (index = 1;
+		index < 1 + MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_NUM;
+		index++) {
+		EFSYS_PROBE2(mcpu_register, unsigned int, index, unsigned int,
+			    EFX_DWORD_FIELD(*MCDI_OUT(req, efx_dword_t, ofst),
+					    EFX_DWORD_0));
+		ofst += sizeof (efx_dword_t);
+	}
+	EFSYS_ASSERT(ofst <= MC_CMD_GET_ASSERTS_OUT_LEN);
+
+out:
+	return (0);
+
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+
+/*
+ * Internal routines for for specific MCDI requests.
+ */
+
+	__checkReturn	efx_rc_t
+efx_mcdi_drv_attach(
+	__in		efx_nic_t *enp,
+	__in		boolean_t attach)
+{
+	efx_mcdi_req_t req;
+	uint8_t payload[MAX(MC_CMD_DRV_ATTACH_IN_LEN,
+			    MC_CMD_DRV_ATTACH_EXT_OUT_LEN)];
+	efx_rc_t rc;
+
+	(void) memset(payload, 0, sizeof (payload));
+	req.emr_cmd = MC_CMD_DRV_ATTACH;
+	req.emr_in_buf = payload;
+	req.emr_in_length = MC_CMD_DRV_ATTACH_IN_LEN;
+	req.emr_out_buf = payload;
+	req.emr_out_length = MC_CMD_DRV_ATTACH_EXT_OUT_LEN;
+
+	/*
+	 * Use DONT_CARE for the datapath firmware type to ensure that the
+	 * driver can attach to an unprivileged function. The datapath firmware
+	 * type to use is controlled by the 'sfboot' utility.
+	 */
+	MCDI_IN_SET_DWORD(req, DRV_ATTACH_IN_NEW_STATE, attach ? 1 : 0);
+	MCDI_IN_SET_DWORD(req, DRV_ATTACH_IN_UPDATE, 1);
+	MCDI_IN_SET_DWORD(req, DRV_ATTACH_IN_FIRMWARE_ID, MC_CMD_FW_DONT_CARE);
+
+	efx_mcdi_execute(enp, &req);
+
+	if (req.emr_rc != 0) {
+		rc = req.emr_rc;
+		goto fail1;
+	}
+
+	if (req.emr_out_length_used < MC_CMD_DRV_ATTACH_OUT_LEN) {
+		rc = EMSGSIZE;
+		goto fail2;
+	}
+
+	return (0);
+
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+	__checkReturn		efx_rc_t
+efx_mcdi_get_board_cfg(
+	__in			efx_nic_t *enp,
+	__out_opt		uint32_t *board_typep,
+	__out_opt		efx_dword_t *capabilitiesp,
+	__out_ecount_opt(6)	uint8_t mac_addrp[6])
+{
+	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
+	efx_mcdi_req_t req;
+	uint8_t payload[MAX(MC_CMD_GET_BOARD_CFG_IN_LEN,
+			    MC_CMD_GET_BOARD_CFG_OUT_LENMIN)];
+	efx_rc_t rc;
+
+	(void) memset(payload, 0, sizeof (payload));
+	req.emr_cmd = MC_CMD_GET_BOARD_CFG;
+	req.emr_in_buf = payload;
+	req.emr_in_length = MC_CMD_GET_BOARD_CFG_IN_LEN;
+	req.emr_out_buf = payload;
+	req.emr_out_length = MC_CMD_GET_BOARD_CFG_OUT_LENMIN;
+
+	efx_mcdi_execute(enp, &req);
+
+	if (req.emr_rc != 0) {
+		rc = req.emr_rc;
+		goto fail1;
+	}
+
+	if (req.emr_out_length_used < MC_CMD_GET_BOARD_CFG_OUT_LENMIN) {
+		rc = EMSGSIZE;
+		goto fail2;
+	}
+
+	if (mac_addrp != NULL) {
+		uint8_t *addrp;
+
+		if (emip->emi_port == 1) {
+			addrp = MCDI_OUT2(req, uint8_t,
+			    GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0);
+		} else if (emip->emi_port == 2) {
+			addrp = MCDI_OUT2(req, uint8_t,
+			    GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1);
+		} else {
+			rc = EINVAL;
+			goto fail3;
+		}
+
+		EFX_MAC_ADDR_COPY(mac_addrp, addrp);
+	}
+
+	if (capabilitiesp != NULL) {
+		if (emip->emi_port == 1) {
+			*capabilitiesp = *MCDI_OUT2(req, efx_dword_t,
+			    GET_BOARD_CFG_OUT_CAPABILITIES_PORT0);
+		} else if (emip->emi_port == 2) {
+			*capabilitiesp = *MCDI_OUT2(req, efx_dword_t,
+			    GET_BOARD_CFG_OUT_CAPABILITIES_PORT1);
+		} else {
+			rc = EINVAL;
+			goto fail4;
+		}
+	}
+
+	if (board_typep != NULL) {
+		*board_typep = MCDI_OUT_DWORD(req,
+		    GET_BOARD_CFG_OUT_BOARD_TYPE);
+	}
+
+	return (0);
+
+fail4:
+	EFSYS_PROBE(fail4);
+fail3:
+	EFSYS_PROBE(fail3);
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+	__checkReturn	efx_rc_t
+efx_mcdi_get_resource_limits(
+	__in		efx_nic_t *enp,
+	__out_opt	uint32_t *nevqp,
+	__out_opt	uint32_t *nrxqp,
+	__out_opt	uint32_t *ntxqp)
+{
+	efx_mcdi_req_t req;
+	uint8_t payload[MAX(MC_CMD_GET_RESOURCE_LIMITS_IN_LEN,
+			    MC_CMD_GET_RESOURCE_LIMITS_OUT_LEN)];
+	efx_rc_t rc;
+
+	(void) memset(payload, 0, sizeof (payload));
+	req.emr_cmd = MC_CMD_GET_RESOURCE_LIMITS;
+	req.emr_in_buf = payload;
+	req.emr_in_length = MC_CMD_GET_RESOURCE_LIMITS_IN_LEN;
+	req.emr_out_buf = payload;
+	req.emr_out_length = MC_CMD_GET_RESOURCE_LIMITS_OUT_LEN;
+
+	efx_mcdi_execute(enp, &req);
+
+	if (req.emr_rc != 0) {
+		rc = req.emr_rc;
+		goto fail1;
+	}
+
+	if (req.emr_out_length_used < MC_CMD_GET_RESOURCE_LIMITS_OUT_LEN) {
+		rc = EMSGSIZE;
+		goto fail2;
+	}
+
+	if (nevqp != NULL)
+		*nevqp = MCDI_OUT_DWORD(req, GET_RESOURCE_LIMITS_OUT_EVQ);
+	if (nrxqp != NULL)
+		*nrxqp = MCDI_OUT_DWORD(req, GET_RESOURCE_LIMITS_OUT_RXQ);
+	if (ntxqp != NULL)
+		*ntxqp = MCDI_OUT_DWORD(req, GET_RESOURCE_LIMITS_OUT_TXQ);
+
+	return (0);
+
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+	__checkReturn	efx_rc_t
+efx_mcdi_get_phy_cfg(
+	__in		efx_nic_t *enp)
+{
+	efx_port_t *epp = &(enp->en_port);
+	efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
+	efx_mcdi_req_t req;
+	uint8_t payload[MAX(MC_CMD_GET_PHY_CFG_IN_LEN,
+			    MC_CMD_GET_PHY_CFG_OUT_LEN)];
+	efx_rc_t rc;
+
+	(void) memset(payload, 0, sizeof (payload));
+	req.emr_cmd = MC_CMD_GET_PHY_CFG;
+	req.emr_in_buf = payload;
+	req.emr_in_length = MC_CMD_GET_PHY_CFG_IN_LEN;
+	req.emr_out_buf = payload;
+	req.emr_out_length = MC_CMD_GET_PHY_CFG_OUT_LEN;
+
+	efx_mcdi_execute(enp, &req);
+
+	if (req.emr_rc != 0) {
+		rc = req.emr_rc;
+		goto fail1;
+	}
+
+	if (req.emr_out_length_used < MC_CMD_GET_PHY_CFG_OUT_LEN) {
+		rc = EMSGSIZE;
+		goto fail2;
+	}
+
+	encp->enc_phy_type = MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_TYPE);
+#if EFSYS_OPT_NAMES
+	(void) strncpy(encp->enc_phy_name,
+		MCDI_OUT2(req, char, GET_PHY_CFG_OUT_NAME),
+		MIN(sizeof (encp->enc_phy_name) - 1,
+		    MC_CMD_GET_PHY_CFG_OUT_NAME_LEN));
+#endif	/* EFSYS_OPT_NAMES */
+	(void) memset(encp->enc_phy_revision, 0,
+	    sizeof (encp->enc_phy_revision));
+	memcpy(encp->enc_phy_revision,
+		MCDI_OUT2(req, char, GET_PHY_CFG_OUT_REVISION),
+		MIN(sizeof (encp->enc_phy_revision) - 1,
+		    MC_CMD_GET_PHY_CFG_OUT_REVISION_LEN));
+
+	/* Get the media type of the fixed port, if recognised. */
+	EFX_STATIC_ASSERT(MC_CMD_MEDIA_XAUI == EFX_PHY_MEDIA_XAUI);
+	EFX_STATIC_ASSERT(MC_CMD_MEDIA_CX4 == EFX_PHY_MEDIA_CX4);
+	EFX_STATIC_ASSERT(MC_CMD_MEDIA_KX4 == EFX_PHY_MEDIA_KX4);
+	EFX_STATIC_ASSERT(MC_CMD_MEDIA_XFP == EFX_PHY_MEDIA_XFP);
+	EFX_STATIC_ASSERT(MC_CMD_MEDIA_SFP_PLUS == EFX_PHY_MEDIA_SFP_PLUS);
+	EFX_STATIC_ASSERT(MC_CMD_MEDIA_BASE_T == EFX_PHY_MEDIA_BASE_T);
+	EFX_STATIC_ASSERT(MC_CMD_MEDIA_QSFP_PLUS == EFX_PHY_MEDIA_QSFP_PLUS);
+	epp->ep_fixed_port_type =
+		MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_MEDIA_TYPE);
+	if (epp->ep_fixed_port_type >= EFX_PHY_MEDIA_NTYPES)
+		epp->ep_fixed_port_type = EFX_PHY_MEDIA_INVALID;
+
+	epp->ep_phy_cap_mask =
+		MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_SUPPORTED_CAP);
+
+	encp->enc_port = (uint8_t)MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_PRT);
+
+	/* Populate internal state */
+	encp->enc_mcdi_mdio_channel =
+		(uint8_t)MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_CHANNEL);
+
+	return (0);
+
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+	__checkReturn		efx_rc_t
+efx_mcdi_firmware_update_supported(
+	__in			efx_nic_t *enp,
+	__out			boolean_t *supportedp)
+{
+	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
+	efx_rc_t rc;
+
+	if (emcop != NULL) {
+		if ((rc = emcop->emco_feature_supported(enp,
+			    EFX_MCDI_FEATURE_FW_UPDATE, supportedp)) != 0)
+			goto fail1;
+	} else {
+		/* Earlier devices always supported updates */
+		*supportedp = B_TRUE;
+	}
+
+	return (0);
+
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+	__checkReturn		efx_rc_t
+efx_mcdi_macaddr_change_supported(
+	__in			efx_nic_t *enp,
+	__out			boolean_t *supportedp)
+{
+	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
+	efx_rc_t rc;
+
+	if (emcop != NULL) {
+		if ((rc = emcop->emco_feature_supported(enp,
+			    EFX_MCDI_FEATURE_MACADDR_CHANGE, supportedp)) != 0)
+			goto fail1;
+	} else {
+		/* Earlier devices always supported MAC changes */
+		*supportedp = B_TRUE;
+	}
+
+	return (0);
+
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+	__checkReturn		efx_rc_t
+efx_mcdi_link_control_supported(
+	__in			efx_nic_t *enp,
+	__out			boolean_t *supportedp)
+{
+	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
+	efx_rc_t rc;
+
+	if (emcop != NULL) {
+		if ((rc = emcop->emco_feature_supported(enp,
+			    EFX_MCDI_FEATURE_LINK_CONTROL, supportedp)) != 0)
+			goto fail1;
+	} else {
+		/* Earlier devices always supported link control */
+		*supportedp = B_TRUE;
+	}
+
+	return (0);
+
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+	__checkReturn		efx_rc_t
+efx_mcdi_mac_spoofing_supported(
+	__in			efx_nic_t *enp,
+	__out			boolean_t *supportedp)
+{
+	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
+	efx_rc_t rc;
+
+	if (emcop != NULL) {
+		if ((rc = emcop->emco_feature_supported(enp,
+			    EFX_MCDI_FEATURE_MAC_SPOOFING, supportedp)) != 0)
+			goto fail1;
+	} else {
+		/* Earlier devices always supported MAC spoofing */
+		*supportedp = B_TRUE;
+	}
+
+	return (0);
+
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+
+/* Enable logging of some events (e.g. link state changes) */
+	__checkReturn	efx_rc_t
+efx_mcdi_log_ctrl(
+	__in		efx_nic_t *enp)
+{
+	efx_mcdi_req_t req;
+	uint8_t payload[MAX(MC_CMD_LOG_CTRL_IN_LEN,
+			    MC_CMD_LOG_CTRL_OUT_LEN)];
+	efx_rc_t rc;
+
+	(void) memset(payload, 0, sizeof (payload));
+	req.emr_cmd = MC_CMD_LOG_CTRL;
+	req.emr_in_buf = payload;
+	req.emr_in_length = MC_CMD_LOG_CTRL_IN_LEN;
+	req.emr_out_buf = payload;
+	req.emr_out_length = MC_CMD_LOG_CTRL_OUT_LEN;
+
+	MCDI_IN_SET_DWORD(req, LOG_CTRL_IN_LOG_DEST,
+		    MC_CMD_LOG_CTRL_IN_LOG_DEST_EVQ);
+	MCDI_IN_SET_DWORD(req, LOG_CTRL_IN_LOG_DEST_EVQ, 0);
+
+	efx_mcdi_execute(enp, &req);
+
+	if (req.emr_rc != 0) {
+		rc = req.emr_rc;
+		goto fail1;
+	}
+
+	return (0);
+
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+
+	__checkReturn		efx_rc_t
+efx_mcdi_set_workaround(
+	__in			efx_nic_t *enp,
+	__in			uint32_t type,
+	__in			boolean_t enabled,
+	__out_opt		uint32_t *flagsp)
+{
+	efx_mcdi_req_t req;
+	uint8_t payload[MAX(MC_CMD_WORKAROUND_IN_LEN,
+			    MC_CMD_WORKAROUND_EXT_OUT_LEN)];
+	efx_rc_t rc;
+
+	(void) memset(payload, 0, sizeof (payload));
+	req.emr_cmd = MC_CMD_WORKAROUND;
+	req.emr_in_buf = payload;
+	req.emr_in_length = MC_CMD_WORKAROUND_IN_LEN;
+	req.emr_out_buf = payload;
+	req.emr_out_length = MC_CMD_WORKAROUND_OUT_LEN;
+
+	MCDI_IN_SET_DWORD(req, WORKAROUND_IN_TYPE, type);
+	MCDI_IN_SET_DWORD(req, WORKAROUND_IN_ENABLED, enabled ? 1 : 0);
+
+	efx_mcdi_execute_quiet(enp, &req);
+
+	if (req.emr_rc != 0) {
+		rc = req.emr_rc;
+		goto fail1;
+	}
+
+	if (flagsp != NULL) {
+		if (req.emr_out_length_used >= MC_CMD_WORKAROUND_EXT_OUT_LEN)
+			*flagsp = MCDI_OUT_DWORD(req, WORKAROUND_EXT_OUT_FLAGS);
+		else
+			*flagsp = 0;
+	}
+
+	return (0);
+
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+
+	__checkReturn		efx_rc_t
+efx_mcdi_get_workarounds(
+	__in			efx_nic_t *enp,
+	__out_opt		uint32_t *implementedp,
+	__out_opt		uint32_t *enabledp)
+{
+	efx_mcdi_req_t req;
+	uint8_t payload[MC_CMD_GET_WORKAROUNDS_OUT_LEN];
+	efx_rc_t rc;
+
+	(void) memset(payload, 0, sizeof (payload));
+	req.emr_cmd = MC_CMD_GET_WORKAROUNDS;
+	req.emr_in_buf = NULL;
+	req.emr_in_length = 0;
+	req.emr_out_buf = payload;
+	req.emr_out_length = MC_CMD_GET_WORKAROUNDS_OUT_LEN;
+
+	efx_mcdi_execute(enp, &req);
+
+	if (req.emr_rc != 0) {
+		rc = req.emr_rc;
+		goto fail1;
+	}
+
+	if (implementedp != NULL) {
+		*implementedp =
+		    MCDI_OUT_DWORD(req, GET_WORKAROUNDS_OUT_IMPLEMENTED);
+	}
+
+	if (enabledp != NULL) {
+		*enabledp = MCDI_OUT_DWORD(req, GET_WORKAROUNDS_OUT_ENABLED);
+	}
+
+	return (0);
+
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+/*
+ * Size of media information page in accordance with SFF-8472 and SFF-8436.
+ * It is used in MCDI interface as well.
+ */
+#define	EFX_PHY_MEDIA_INFO_PAGE_SIZE		0x80
+
+static	__checkReturn		efx_rc_t
+efx_mcdi_get_phy_media_info(
+	__in			efx_nic_t *enp,
+	__in			uint32_t mcdi_page,
+	__in			uint8_t offset,
+	__in			uint8_t len,
+	__out_bcount(len)	uint8_t *data)
+{
+	efx_mcdi_req_t req;
+	uint8_t payload[MAX(MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN,
+			    MC_CMD_GET_PHY_MEDIA_INFO_OUT_LEN(
+				EFX_PHY_MEDIA_INFO_PAGE_SIZE))];
+	efx_rc_t rc;
+
+	EFSYS_ASSERT((uint32_t)offset + len <= EFX_PHY_MEDIA_INFO_PAGE_SIZE);
+
+	(void) memset(payload, 0, sizeof (payload));
+	req.emr_cmd = MC_CMD_GET_PHY_MEDIA_INFO;
+	req.emr_in_buf = payload;
+	req.emr_in_length = MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN;
+	req.emr_out_buf = payload;
+	req.emr_out_length =
+	    MC_CMD_GET_PHY_MEDIA_INFO_OUT_LEN(EFX_PHY_MEDIA_INFO_PAGE_SIZE);
+
+	MCDI_IN_SET_DWORD(req, GET_PHY_MEDIA_INFO_IN_PAGE, mcdi_page);
+
+	efx_mcdi_execute(enp, &req);
+
+	if (req.emr_rc != 0) {
+		rc = req.emr_rc;
+		goto fail1;
+	}
+
+	if (req.emr_out_length_used !=
+	    MC_CMD_GET_PHY_MEDIA_INFO_OUT_LEN(EFX_PHY_MEDIA_INFO_PAGE_SIZE)) {
+		rc = EMSGSIZE;
+		goto fail2;
+	}
+
+	if (MCDI_OUT_DWORD(req, GET_PHY_MEDIA_INFO_OUT_DATALEN) !=
+	    EFX_PHY_MEDIA_INFO_PAGE_SIZE) {
+		rc = EIO;
+		goto fail3;
+	}
+
+	memcpy(data,
+	    MCDI_OUT2(req, uint8_t, GET_PHY_MEDIA_INFO_OUT_DATA) + offset,
+	    len);
+
+	return (0);
+
+fail3:
+	EFSYS_PROBE(fail3);
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+/*
+ * 2-wire device address of the base information in accordance with SFF-8472
+ * Diagnostic Monitoring Interface for Optical Transceivers section
+ * 4 Memory Organization.
+ */
+#define	EFX_PHY_MEDIA_INFO_DEV_ADDR_SFP_BASE	0xA0
+
+/*
+ * 2-wire device address of the digital diagnostics monitoring interface
+ * in accordance with SFF-8472 Diagnostic Monitoring Interface for Optical
+ * Transceivers section 4 Memory Organization.
+ */
+#define	EFX_PHY_MEDIA_INFO_DEV_ADDR_SFP_DDM	0xA2
+
+/*
+ * Hard wired 2-wire device address for QSFP+ in accordance with SFF-8436
+ * QSFP+ 10 Gbs 4X PLUGGABLE TRANSCEIVER section 7.4 Device Addressing and
+ * Operation.
+ */
+#define	EFX_PHY_MEDIA_INFO_DEV_ADDR_QSFP	0xA0
+
+	__checkReturn		efx_rc_t
+efx_mcdi_phy_module_get_info(
+	__in			efx_nic_t *enp,
+	__in			uint8_t dev_addr,
+	__in			uint8_t offset,
+	__in			uint8_t len,
+	__out_bcount(len)	uint8_t *data)
+{
+	efx_port_t *epp = &(enp->en_port);
+	efx_rc_t rc;
+	uint32_t mcdi_lower_page;
+	uint32_t mcdi_upper_page;
+
+	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
+
+	/*
+	 * Map device address to MC_CMD_GET_PHY_MEDIA_INFO pages.
+	 * Offset plus length interface allows to access page 0 only.
+	 * I.e. non-zero upper pages are not accessible.
+	 * See SFF-8472 section 4 Memory Organization and SFF-8436 section 7.6
+	 * QSFP+ Memory Map for details on how information is structured
+	 * and accessible.
+	 */
+	switch (epp->ep_fixed_port_type) {
+	case EFX_PHY_MEDIA_SFP_PLUS:
+		/*
+		 * In accordance with SFF-8472 Diagnostic Monitoring
+		 * Interface for Optical Transceivers section 4 Memory
+		 * Organization two 2-wire addresses are defined.
+		 */
+		switch (dev_addr) {
+		/* Base information */
+		case EFX_PHY_MEDIA_INFO_DEV_ADDR_SFP_BASE:
+			/*
+			 * MCDI page 0 should be used to access lower
+			 * page 0 (0x00 - 0x7f) at the device address 0xA0.
+			 */
+			mcdi_lower_page = 0;
+			/*
+			 * MCDI page 1 should be used to access  upper
+			 * page 0 (0x80 - 0xff) at the device address 0xA0.
+			 */
+			mcdi_upper_page = 1;
+			break;
+		/* Diagnostics */
+		case EFX_PHY_MEDIA_INFO_DEV_ADDR_SFP_DDM:
+			/*
+			 * MCDI page 2 should be used to access lower
+			 * page 0 (0x00 - 0x7f) at the device address 0xA2.
+			 */
+			mcdi_lower_page = 2;
+			/*
+			 * MCDI page 3 should be used to access upper
+			 * page 0 (0x80 - 0xff) at the device address 0xA2.
+			 */
+			mcdi_upper_page = 3;
+			break;
+		default:
+			rc = ENOTSUP;
+			goto fail1;
+		}
+		break;
+	case EFX_PHY_MEDIA_QSFP_PLUS:
+		switch (dev_addr) {
+		case EFX_PHY_MEDIA_INFO_DEV_ADDR_QSFP:
+			/*
+			 * MCDI page -1 should be used to access lower page 0
+			 * (0x00 - 0x7f).
+			 */
+			mcdi_lower_page = (uint32_t)-1;
+			/*
+			 * MCDI page 0 should be used to access upper page 0
+			 * (0x80h - 0xff).
+			 */
+			mcdi_upper_page = 0;
+			break;
+		default:
+			rc = ENOTSUP;
+			goto fail1;
+		}
+		break;
+	default:
+		rc = ENOTSUP;
+		goto fail1;
+	}
+
+	if (offset < EFX_PHY_MEDIA_INFO_PAGE_SIZE) {
+		uint8_t read_len =
+		    MIN(len, EFX_PHY_MEDIA_INFO_PAGE_SIZE - offset);
+
+		rc = efx_mcdi_get_phy_media_info(enp,
+		    mcdi_lower_page, offset, read_len, data);
+		if (rc != 0)
+			goto fail2;
+
+		data += read_len;
+		len -= read_len;
+
+		offset = 0;
+	} else {
+		offset -= EFX_PHY_MEDIA_INFO_PAGE_SIZE;
+	}
+
+	if (len > 0) {
+		EFSYS_ASSERT3U(len, <=, EFX_PHY_MEDIA_INFO_PAGE_SIZE);
+		EFSYS_ASSERT3U(offset, <, EFX_PHY_MEDIA_INFO_PAGE_SIZE);
+
+		rc = efx_mcdi_get_phy_media_info(enp,
+		    mcdi_upper_page, offset, len, data);
+		if (rc != 0)
+			goto fail3;
+	}
+
+	return (0);
+
+fail3:
+	EFSYS_PROBE(fail3);
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+	return (rc);
+}
+
+#endif	/* EFSYS_OPT_MCDI */
diff --git a/drivers/net/sfc/efx/base/efx_mcdi.h b/drivers/net/sfc/efx/base/efx_mcdi.h
new file mode 100644
index 0000000..a408b5b
--- /dev/null
+++ b/drivers/net/sfc/efx/base/efx_mcdi.h
@@ -0,0 +1,371 @@ 
+/*
+ * Copyright (c) 2009-2016 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
+ *
+ * The views and conclusions contained in the software and documentation are
+ * those of the authors and should not be interpreted as representing official
+ * policies, either expressed or implied, of the FreeBSD Project.
+ */
+
+#ifndef _SYS_EFX_MCDI_H
+#define	_SYS_EFX_MCDI_H
+
+#include "efx.h"
+#include "efx_regs_mcdi.h"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * 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	EFX_MCDI_STATUS_SLEEP_US	10000
+
+struct efx_mcdi_req_s {
+	boolean_t	emr_quiet;
+	/* Inputs: Command #, input buffer and length */
+	unsigned int	emr_cmd;
+	uint8_t		*emr_in_buf;
+	size_t		emr_in_length;
+	/* Outputs: retcode, buffer, length, and length used*/
+	efx_rc_t	emr_rc;
+	uint8_t		*emr_out_buf;
+	size_t		emr_out_length;
+	size_t		emr_out_length_used;
+	/* Internals: low level transport details */
+	unsigned int	emr_err_code;
+	unsigned int	emr_err_arg;
+};
+
+typedef struct efx_mcdi_iface_s {
+	unsigned int		emi_port;
+	unsigned int		emi_max_version;
+	unsigned int		emi_seq;
+	efx_mcdi_req_t		*emi_pending_req;
+	boolean_t		emi_ev_cpl;
+	boolean_t		emi_new_epoch;
+	int			emi_aborted;
+	uint32_t		emi_poll_cnt;
+	uint32_t		emi_mc_reboot_status;
+} efx_mcdi_iface_t;
+
+extern			void
+efx_mcdi_execute(
+	__in		efx_nic_t *enp,
+	__inout		efx_mcdi_req_t *emrp);
+
+extern			void
+efx_mcdi_execute_quiet(
+	__in		efx_nic_t *enp,
+	__inout		efx_mcdi_req_t *emrp);
+
+extern			void
+efx_mcdi_ev_cpl(
+	__in		efx_nic_t *enp,
+	__in		unsigned int seq,
+	__in		unsigned int outlen,
+	__in		int errcode);
+
+extern			void
+efx_mcdi_ev_death(
+	__in		efx_nic_t *enp,
+	__in		int rc);
+
+extern	__checkReturn	efx_rc_t
+efx_mcdi_request_errcode(
+	__in		unsigned int err);
+
+extern			void
+efx_mcdi_raise_exception(
+	__in		efx_nic_t *enp,
+	__in_opt	efx_mcdi_req_t *emrp,
+	__in		int rc);
+
+typedef enum efx_mcdi_boot_e {
+	EFX_MCDI_BOOT_PRIMARY,
+	EFX_MCDI_BOOT_SECONDARY,
+	EFX_MCDI_BOOT_ROM,
+} efx_mcdi_boot_t;
+
+extern	__checkReturn		efx_rc_t
+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);
+
+extern	__checkReturn		efx_rc_t
+efx_mcdi_read_assertion(
+	__in			efx_nic_t *enp);
+
+extern	__checkReturn		efx_rc_t
+efx_mcdi_exit_assertion_handler(
+	__in			efx_nic_t *enp);
+
+extern	__checkReturn		efx_rc_t
+efx_mcdi_drv_attach(
+	__in			efx_nic_t *enp,
+	__in			boolean_t attach);
+
+extern	__checkReturn		efx_rc_t
+efx_mcdi_get_board_cfg(
+	__in			efx_nic_t *enp,
+	__out_opt		uint32_t *board_typep,
+	__out_opt		efx_dword_t *capabilitiesp,
+	__out_ecount_opt(6)	uint8_t mac_addrp[6]);
+
+extern	__checkReturn		efx_rc_t
+efx_mcdi_get_phy_cfg(
+	__in			efx_nic_t *enp);
+
+extern	__checkReturn		efx_rc_t
+efx_mcdi_firmware_update_supported(
+	__in			efx_nic_t *enp,
+	__out			boolean_t *supportedp);
+
+extern	__checkReturn		efx_rc_t
+efx_mcdi_macaddr_change_supported(
+	__in			efx_nic_t *enp,
+	__out			boolean_t *supportedp);
+
+extern	__checkReturn		efx_rc_t
+efx_mcdi_link_control_supported(
+	__in			efx_nic_t *enp,
+	__out			boolean_t *supportedp);
+
+extern	__checkReturn		efx_rc_t
+efx_mcdi_mac_spoofing_supported(
+	__in			efx_nic_t *enp,
+	__out			boolean_t *supportedp);
+
+
+extern	__checkReturn		efx_rc_t
+efx_mcdi_get_resource_limits(
+	__in			efx_nic_t *enp,
+	__out_opt		uint32_t *nevqp,
+	__out_opt		uint32_t *nrxqp,
+	__out_opt		uint32_t *ntxqp);
+
+extern	__checkReturn	efx_rc_t
+efx_mcdi_log_ctrl(
+	__in		efx_nic_t *enp);
+
+extern	__checkReturn	efx_rc_t
+efx_mcdi_mac_stats_clear(
+	__in		efx_nic_t *enp);
+
+extern	__checkReturn	efx_rc_t
+efx_mcdi_mac_stats_upload(
+	__in		efx_nic_t *enp,
+	__in		efsys_mem_t *esmp);
+
+extern	__checkReturn	efx_rc_t
+efx_mcdi_mac_stats_periodic(
+	__in		efx_nic_t *enp,
+	__in		efsys_mem_t *esmp,
+	__in		uint16_t period,
+	__in		boolean_t events);
+
+
+extern	__checkReturn	efx_rc_t
+efx_mcdi_phy_module_get_info(
+	__in			efx_nic_t *enp,
+	__in			uint8_t dev_addr,
+	__in			uint8_t offset,
+	__in			uint8_t len,
+	__out_bcount(len)	uint8_t *data);
+
+#define	MCDI_IN(_emr, _type, _ofst)					\
+	((_type *)((_emr).emr_in_buf + (_ofst)))
+
+#define	MCDI_IN2(_emr, _type, _ofst)					\
+	MCDI_IN(_emr, _type, MC_CMD_ ## _ofst ## _OFST)
+
+#define	MCDI_IN_SET_BYTE(_emr, _ofst, _value)				\
+	EFX_POPULATE_BYTE_1(*MCDI_IN2(_emr, efx_byte_t, _ofst),		\
+		EFX_BYTE_0, _value)
+
+#define	MCDI_IN_SET_WORD(_emr, _ofst, _value)				\
+	EFX_POPULATE_WORD_1(*MCDI_IN2(_emr, efx_word_t, _ofst),		\
+		EFX_WORD_0, _value)
+
+#define	MCDI_IN_SET_DWORD(_emr, _ofst, _value)				\
+	EFX_POPULATE_DWORD_1(*MCDI_IN2(_emr, efx_dword_t, _ofst),	\
+		EFX_DWORD_0, _value)
+
+#define	MCDI_IN_SET_DWORD_FIELD(_emr, _ofst, _field, _value)		\
+	EFX_SET_DWORD_FIELD(*MCDI_IN2(_emr, efx_dword_t, _ofst),	\
+		MC_CMD_ ## _field, _value)
+
+#define	MCDI_IN_POPULATE_DWORD_1(_emr, _ofst, _field1, _value1)		\
+	EFX_POPULATE_DWORD_1(*MCDI_IN2(_emr, efx_dword_t, _ofst),	\
+		MC_CMD_ ## _field1, _value1)
+
+#define	MCDI_IN_POPULATE_DWORD_2(_emr, _ofst, _field1, _value1,		\
+		_field2, _value2)					\
+	EFX_POPULATE_DWORD_2(*MCDI_IN2(_emr, efx_dword_t, _ofst),	\
+		MC_CMD_ ## _field1, _value1,				\
+		MC_CMD_ ## _field2, _value2)
+
+#define	MCDI_IN_POPULATE_DWORD_3(_emr, _ofst, _field1, _value1,		\
+		_field2, _value2, _field3, _value3)			\
+	EFX_POPULATE_DWORD_3(*MCDI_IN2(_emr, efx_dword_t, _ofst),	\
+		MC_CMD_ ## _field1, _value1,				\
+		MC_CMD_ ## _field2, _value2,				\
+		MC_CMD_ ## _field3, _value3)
+
+#define	MCDI_IN_POPULATE_DWORD_4(_emr, _ofst, _field1, _value1,		\
+		_field2, _value2, _field3, _value3, _field4, _value4)	\
+	EFX_POPULATE_DWORD_4(*MCDI_IN2(_emr, efx_dword_t, _ofst),	\
+		MC_CMD_ ## _field1, _value1,				\
+		MC_CMD_ ## _field2, _value2,				\
+		MC_CMD_ ## _field3, _value3,				\
+		MC_CMD_ ## _field4, _value4)
+
+#define	MCDI_IN_POPULATE_DWORD_5(_emr, _ofst, _field1, _value1,		\
+		_field2, _value2, _field3, _value3, _field4, _value4,	\
+		_field5, _value5)					\
+	EFX_POPULATE_DWORD_5(*MCDI_IN2(_emr, efx_dword_t, _ofst),	\
+		MC_CMD_ ## _field1, _value1,				\
+		MC_CMD_ ## _field2, _value2,				\
+		MC_CMD_ ## _field3, _value3,				\
+		MC_CMD_ ## _field4, _value4,				\
+		MC_CMD_ ## _field5, _value5)
+
+#define	MCDI_IN_POPULATE_DWORD_6(_emr, _ofst, _field1, _value1,		\
+		_field2, _value2, _field3, _value3, _field4, _value4,	\
+		_field5, _value5, _field6, _value6)			\
+	EFX_POPULATE_DWORD_6(*MCDI_IN2(_emr, efx_dword_t, _ofst),	\
+		MC_CMD_ ## _field1, _value1,				\
+		MC_CMD_ ## _field2, _value2,				\
+		MC_CMD_ ## _field3, _value3,				\
+		MC_CMD_ ## _field4, _value4,				\
+		MC_CMD_ ## _field5, _value5,				\
+		MC_CMD_ ## _field6, _value6)
+
+#define	MCDI_IN_POPULATE_DWORD_7(_emr, _ofst, _field1, _value1,		\
+		_field2, _value2, _field3, _value3, _field4, _value4,	\
+		_field5, _value5, _field6, _value6, _field7, _value7)	\
+	EFX_POPULATE_DWORD_7(*MCDI_IN2(_emr, efx_dword_t, _ofst),	\
+		MC_CMD_ ## _field1, _value1,				\
+		MC_CMD_ ## _field2, _value2,				\
+		MC_CMD_ ## _field3, _value3,				\
+		MC_CMD_ ## _field4, _value4,				\
+		MC_CMD_ ## _field5, _value5,				\
+		MC_CMD_ ## _field6, _value6,				\
+		MC_CMD_ ## _field7, _value7)
+
+#define	MCDI_IN_POPULATE_DWORD_8(_emr, _ofst, _field1, _value1,		\
+		_field2, _value2, _field3, _value3, _field4, _value4,	\
+		_field5, _value5, _field6, _value6, _field7, _value7,	\
+		_field8, _value8)					\
+	EFX_POPULATE_DWORD_8(*MCDI_IN2(_emr, efx_dword_t, _ofst),	\
+		MC_CMD_ ## _field1, _value1,				\
+		MC_CMD_ ## _field2, _value2,				\
+		MC_CMD_ ## _field3, _value3,				\
+		MC_CMD_ ## _field4, _value4,				\
+		MC_CMD_ ## _field5, _value5,				\
+		MC_CMD_ ## _field6, _value6,				\
+		MC_CMD_ ## _field7, _value7,				\
+		MC_CMD_ ## _field8, _value8)
+
+#define	MCDI_IN_POPULATE_DWORD_9(_emr, _ofst, _field1, _value1,		\
+		_field2, _value2, _field3, _value3, _field4, _value4,	\
+		_field5, _value5, _field6, _value6, _field7, _value7,	\
+		_field8, _value8, _field9, _value9)			\
+	EFX_POPULATE_DWORD_9(*MCDI_IN2(_emr, efx_dword_t, _ofst),	\
+		MC_CMD_ ## _field1, _value1,				\
+		MC_CMD_ ## _field2, _value2,				\
+		MC_CMD_ ## _field3, _value3,				\
+		MC_CMD_ ## _field4, _value4,				\
+		MC_CMD_ ## _field5, _value5,				\
+		MC_CMD_ ## _field6, _value6,				\
+		MC_CMD_ ## _field7, _value7,				\
+		MC_CMD_ ## _field8, _value8,				\
+		MC_CMD_ ## _field9, _value9)
+
+#define	MCDI_IN_POPULATE_DWORD_10(_emr, _ofst, _field1, _value1,	\
+		_field2, _value2, _field3, _value3, _field4, _value4,	\
+		_field5, _value5, _field6, _value6, _field7, _value7,	\
+		_field8, _value8, _field9, _value9, _field10, _value10)	\
+	EFX_POPULATE_DWORD_10(*MCDI_IN2(_emr, efx_dword_t, _ofst),	\
+		MC_CMD_ ## _field1, _value1,				\
+		MC_CMD_ ## _field2, _value2,				\
+		MC_CMD_ ## _field3, _value3,				\
+		MC_CMD_ ## _field4, _value4,				\
+		MC_CMD_ ## _field5, _value5,				\
+		MC_CMD_ ## _field6, _value6,				\
+		MC_CMD_ ## _field7, _value7,				\
+		MC_CMD_ ## _field8, _value8,				\
+		MC_CMD_ ## _field9, _value9,				\
+		MC_CMD_ ## _field10, _value10)
+
+#define	MCDI_OUT(_emr, _type, _ofst)					\
+	((_type *)((_emr).emr_out_buf + (_ofst)))
+
+#define	MCDI_OUT2(_emr, _type, _ofst)					\
+	MCDI_OUT(_emr, _type, MC_CMD_ ## _ofst ## _OFST)
+
+#define	MCDI_OUT_BYTE(_emr, _ofst)					\
+	EFX_BYTE_FIELD(*MCDI_OUT2(_emr, efx_byte_t, _ofst),		\
+		    EFX_BYTE_0)
+
+#define	MCDI_OUT_WORD(_emr, _ofst)					\
+	EFX_WORD_FIELD(*MCDI_OUT2(_emr, efx_word_t, _ofst),		\
+		    EFX_WORD_0)
+
+#define	MCDI_OUT_DWORD(_emr, _ofst)					\
+	EFX_DWORD_FIELD(*MCDI_OUT2(_emr, efx_dword_t, _ofst),		\
+			EFX_DWORD_0)
+
+#define	MCDI_OUT_DWORD_FIELD(_emr, _ofst, _field)			\
+	EFX_DWORD_FIELD(*MCDI_OUT2(_emr, efx_dword_t, _ofst),		\
+			MC_CMD_ ## _field)
+
+#define	MCDI_EV_FIELD(_eqp, _field)					\
+	EFX_QWORD_FIELD(*_eqp, MCDI_EVENT_ ## _field)
+
+#define	MCDI_CMD_DWORD_FIELD(_edp, _field)				\
+	EFX_DWORD_FIELD(*_edp, MC_CMD_ ## _field)
+
+#define	EFX_MCDI_HAVE_PRIVILEGE(mask, priv)				\
+	(((mask) & (MC_CMD_PRIVILEGE_MASK_IN_GRP_ ## priv)) ==		\
+	(MC_CMD_PRIVILEGE_MASK_IN_GRP_ ## priv))
+
+typedef enum efx_mcdi_feature_id_e {
+	EFX_MCDI_FEATURE_FW_UPDATE = 0,
+	EFX_MCDI_FEATURE_LINK_CONTROL,
+	EFX_MCDI_FEATURE_MACADDR_CHANGE,
+	EFX_MCDI_FEATURE_MAC_SPOOFING,
+	EFX_MCDI_FEATURE_NIDS
+} efx_mcdi_feature_id_t;
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_EFX_MCDI_H */
diff --git a/drivers/net/sfc/efx/base/efx_nic.c b/drivers/net/sfc/efx/base/efx_nic.c
index b5548d7..16c146b 100644
--- a/drivers/net/sfc/efx/base/efx_nic.c
+++ b/drivers/net/sfc/efx/base/efx_nic.c
@@ -185,6 +185,9 @@  efx_nic_probe(
 	efx_rc_t rc;
 
 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
+#if EFSYS_OPT_MCDI
+	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
+#endif	/* EFSYS_OPT_MCDI */
 	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_PROBE));
 
 	enop = enp->en_enop;
@@ -364,6 +367,9 @@  efx_nic_unprobe(
 	const efx_nic_ops_t *enop = enp->en_enop;
 
 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
+#if EFSYS_OPT_MCDI
+	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
+#endif	/* EFSYS_OPT_MCDI */
 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
 	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_NIC));
 	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_INTR));