net/sfc/base: import MCDI implementation
authorAndrew Rybchenko <arybchenko@solarflare.com>
Tue, 29 Nov 2016 16:18:38 +0000 (16:18 +0000)
committerFerruh Yigit <ferruh.yigit@intel.com>
Tue, 17 Jan 2017 18:39:25 +0000 (19:39 +0100)
Implement interface to talk to NIC management CPU. Provide
helpers to fill in MCDI requests, execute it and process
received 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>
Reviewed-by: Ferruh Yigit <ferruh.yigit@intel.com>
drivers/net/sfc/base/efx.h
drivers/net/sfc/base/efx_check.h
drivers/net/sfc/base/efx_ev.c
drivers/net/sfc/base/efx_impl.h
drivers/net/sfc/base/efx_mcdi.c [new file with mode: 0644]
drivers/net/sfc/base/efx_mcdi.h [new file with mode: 0644]
drivers/net/sfc/base/efx_nic.c

index 1814aef..8d0e691 100644 (file)
@@ -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;
index 555c184..9d0e988 100644 (file)
 # 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
index 2bd365f..942dac6 100644 (file)
@@ -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,
index 47d4819..c6ec808 100644 (file)
@@ -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/base/efx_mcdi.c b/drivers/net/sfc/base/efx_mcdi.c
new file mode 100644 (file)
index 0000000..c11ece9
--- /dev/null
@@ -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 =
+               (efx_phy_media_type_t) 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/base/efx_mcdi.h b/drivers/net/sfc/base/efx_mcdi.h
new file mode 100644 (file)
index 0000000..a408b5b
--- /dev/null
@@ -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 */
index 805136c..a551f0d 100644 (file)
@@ -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));