common/sfc_efx/base: support NIC DMA memory regions API
[dpdk.git] / drivers / common / sfc_efx / base / efx_mcdi.c
index ade7f7f..9189a7a 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: BSD-3-Clause
  *
- * Copyright(c) 2019-2020 Xilinx, Inc.
+ * Copyright(c) 2019-2021 Xilinx, Inc.
  * Copyright(c) 2008-2019 Solarflare Communications Inc.
  */
 
@@ -516,6 +516,9 @@ efx_mcdi_finish_response(
        bytes = MIN(emrp->emr_out_length_used, emrp->emr_out_length);
        efx_mcdi_read_response(enp, emrp->emr_out_buf, resp_off, bytes);
 
+       /* Report bytes copied to caller (response message may be larger) */
+       emrp->emr_out_length_used = bytes;
+
 #if EFSYS_OPT_MCDI_LOGGING
        if (emtp->emt_logger != NULL) {
                emtp->emt_logger(emtp->emt_context,
@@ -644,6 +647,79 @@ efx_mcdi_request_abort(
        return (aborted);
 }
 
+       __checkReturn   efx_rc_t
+efx_mcdi_get_client_handle(
+       __in            efx_nic_t *enp,
+       __in            efx_pcie_interface_t intf,
+       __in            uint16_t pf,
+       __in            uint16_t vf,
+       __out           uint32_t *handle)
+{
+       efx_mcdi_req_t req;
+       EFX_MCDI_DECLARE_BUF(payload,
+           MC_CMD_GET_CLIENT_HANDLE_IN_LEN,
+           MC_CMD_GET_CLIENT_HANDLE_OUT_LEN);
+       efx_rc_t rc;
+
+       if (handle == NULL) {
+               rc = EINVAL;
+               goto fail1;
+       }
+
+       req.emr_cmd = MC_CMD_GET_CLIENT_HANDLE;
+       req.emr_in_buf = payload;
+       req.emr_in_length = MC_CMD_GET_CLIENT_HANDLE_IN_LEN;
+       req.emr_out_buf = payload;
+       req.emr_out_length = MC_CMD_GET_CLIENT_HANDLE_OUT_LEN;
+
+       MCDI_IN_SET_DWORD(req, GET_CLIENT_HANDLE_IN_TYPE,
+           MC_CMD_GET_CLIENT_HANDLE_IN_TYPE_FUNC);
+       MCDI_IN_SET_WORD(req, GET_CLIENT_HANDLE_IN_FUNC_PF, pf);
+       MCDI_IN_SET_WORD(req, GET_CLIENT_HANDLE_IN_FUNC_VF, vf);
+       MCDI_IN_SET_DWORD(req, GET_CLIENT_HANDLE_IN_FUNC_INTF, intf);
+
+       efx_mcdi_execute(enp, &req);
+
+       if (req.emr_rc != 0) {
+               rc = req.emr_rc;
+               goto fail2;
+       }
+
+       if (req.emr_out_length_used < MC_CMD_GET_CLIENT_HANDLE_OUT_LEN) {
+               rc = EMSGSIZE;
+               goto fail3;
+       }
+
+       *handle = MCDI_OUT_DWORD(req, GET_CLIENT_HANDLE_OUT_HANDLE);
+
+       return 0;
+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_own_client_handle(
+       __in            efx_nic_t *enp,
+       __out           uint32_t *handle)
+{
+       efx_rc_t rc;
+
+       rc = efx_mcdi_get_client_handle(enp, PCIE_INTERFACE_CALLER,
+           PCIE_FUNCTION_PF_NULL, PCIE_FUNCTION_VF_NULL, handle);
+       if (rc != 0)
+               goto fail1;
+
+       return (0);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+       return (rc);
+}
+
                        void
 efx_mcdi_get_timeout(
        __in            efx_nic_t *enp,
@@ -962,30 +1038,51 @@ efx_mcdi_ev_death(
 }
 
        __checkReturn           efx_rc_t
-efx_mcdi_version(
+efx_mcdi_get_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)
+       __in                    uint32_t flags,
+       __out                   efx_mcdi_version_t *verp)
 {
-       efx_mcdi_req_t req;
+       efx_nic_board_info_t *board_infop = &verp->emv_board_info;
        EFX_MCDI_DECLARE_BUF(payload,
-               MAX(MC_CMD_GET_VERSION_IN_LEN, MC_CMD_GET_BOOT_STATUS_IN_LEN),
-               MAX(MC_CMD_GET_VERSION_OUT_LEN,
-                       MC_CMD_GET_BOOT_STATUS_OUT_LEN));
+           MC_CMD_GET_VERSION_EXT_IN_LEN,
+           MC_CMD_GET_VERSION_V2_OUT_LEN);
        efx_word_t *ver_words;
        uint16_t version[4];
-       uint32_t build;
-       efx_mcdi_boot_t status;
+       efx_mcdi_req_t req;
+       uint32_t firmware;
        efx_rc_t rc;
 
+       EFX_STATIC_ASSERT(sizeof (verp->emv_version) ==
+           MC_CMD_GET_VERSION_OUT_VERSION_LEN);
+       EFX_STATIC_ASSERT(sizeof (verp->emv_firmware) ==
+           MC_CMD_GET_VERSION_OUT_FIRMWARE_LEN);
+
+       EFX_STATIC_ASSERT(EFX_MCDI_VERSION_BOARD_INFO ==
+           (1U << MC_CMD_GET_VERSION_V2_OUT_BOARD_EXT_INFO_PRESENT_LBN));
+
+       EFX_STATIC_ASSERT(sizeof (board_infop->enbi_serial) ==
+           MC_CMD_GET_VERSION_V2_OUT_BOARD_SERIAL_LEN);
+       EFX_STATIC_ASSERT(sizeof (board_infop->enbi_name) ==
+           MC_CMD_GET_VERSION_V2_OUT_BOARD_NAME_LEN);
+       EFX_STATIC_ASSERT(sizeof (board_infop->enbi_revision) ==
+           MC_CMD_GET_VERSION_V2_OUT_BOARD_REVISION_LEN);
+
        EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
 
        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;
+
+       if ((flags & EFX_MCDI_VERSION_BOARD_INFO) != 0) {
+               /* Request basic + extended version information. */
+               req.emr_in_length = MC_CMD_GET_VERSION_EXT_IN_LEN;
+               req.emr_out_length = MC_CMD_GET_VERSION_V2_OUT_LEN;
+       } else {
+               /* Request only basic version information. */
+               req.emr_in_length = MC_CMD_GET_VERSION_IN_LEN;
+               req.emr_out_length = MC_CMD_GET_VERSION_OUT_LEN;
+       }
 
        efx_mcdi_execute(enp, &req);
 
@@ -997,12 +1094,11 @@ efx_mcdi_version(
        /* 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;
+               firmware = MCDI_OUT_DWORD(req, GET_VERSION_OUT_FIRMWARE);
+               goto out;
        }
 
-       if (req.emr_out_length_used < MC_CMD_GET_VERSION_OUT_LEN) {
+       if (req.emr_out_length_used < req.emr_out_length) {
                rc = EMSGSIZE;
                goto fail2;
        }
@@ -1012,16 +1108,54 @@ efx_mcdi_version(
        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);
+       firmware = 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;
+out:
+       memset(verp, 0, sizeof (*verp));
+
+       verp->emv_version[0] = version[0];
+       verp->emv_version[1] = version[1];
+       verp->emv_version[2] = version[2];
+       verp->emv_version[3] = version[3];
+       verp->emv_firmware = firmware;
+
+       verp->emv_flags = MCDI_OUT_DWORD(req, GET_VERSION_V2_OUT_FLAGS);
+       verp->emv_flags &= flags;
+
+       if ((verp->emv_flags & EFX_MCDI_VERSION_BOARD_INFO) != 0) {
+               memcpy(board_infop->enbi_serial,
+                   MCDI_OUT2(req, char, GET_VERSION_V2_OUT_BOARD_SERIAL),
+                   sizeof (board_infop->enbi_serial));
+               memcpy(board_infop->enbi_name,
+                   MCDI_OUT2(req, char, GET_VERSION_V2_OUT_BOARD_NAME),
+                   sizeof (board_infop->enbi_name));
+               board_infop->enbi_revision =
+                   MCDI_OUT_DWORD(req, GET_VERSION_V2_OUT_BOARD_REVISION);
        }
 
-       (void) memset(payload, 0, sizeof (payload));
+       return (0);
+
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+static __checkReturn           efx_rc_t
+efx_mcdi_get_boot_status(
+       __in                    efx_nic_t *enp,
+       __out                   efx_mcdi_boot_t *statusp)
+{
+       EFX_MCDI_DECLARE_BUF(payload,
+           MC_CMD_GET_BOOT_STATUS_IN_LEN,
+           MC_CMD_GET_BOOT_STATUS_OUT_LEN);
+       efx_mcdi_req_t req;
+       efx_rc_t rc;
+
+       EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
+
        req.emr_cmd = MC_CMD_GET_BOOT_STATUS;
        req.emr_in_buf = payload;
        req.emr_in_length = MC_CMD_GET_BOOT_STATUS_IN_LEN;
@@ -1030,44 +1164,79 @@ version:
 
        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;
-       }
+       /*
+        * NOTE: Unprivileged functions cannot access boot status,
+        *       so the MCDI request will return EACCES. This is
+        *       also checked in efx_mcdi_version.
+        */
 
        if (req.emr_rc != 0) {
                rc = req.emr_rc;
-               goto fail3;
+               goto fail1;
        }
 
        if (req.emr_out_length_used < MC_CMD_GET_BOOT_STATUS_OUT_LEN) {
                rc = EMSGSIZE;
-               goto fail4;
+               goto fail2;
        }
 
        if (MCDI_OUT_DWORD_FIELD(req, GET_BOOT_STATUS_OUT_FLAGS,
            GET_BOOT_STATUS_OUT_FLAGS_PRIMARY))
-               status = EFX_MCDI_BOOT_PRIMARY;
+               *statusp = EFX_MCDI_BOOT_PRIMARY;
        else
-               status = EFX_MCDI_BOOT_SECONDARY;
+               *statusp = EFX_MCDI_BOOT_SECONDARY;
+
+       return (0);
+
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+       __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_version_t ver;
+       efx_mcdi_boot_t status;
+       efx_rc_t rc;
+
+       rc = efx_mcdi_get_version(enp, 0, &ver);
+       if (rc != 0)
+               goto fail1;
+
+       /* The bootrom doesn't understand BOOT_STATUS */
+       if (MC_FW_VERSION_IS_BOOTLOADER(ver.emv_firmware)) {
+               status = EFX_MCDI_BOOT_ROM;
+               goto out;
+       }
+
+       rc = efx_mcdi_get_boot_status(enp, &status);
+       if (rc == EACCES) {
+               /* Unprivileged functions cannot access BOOT_STATUS */
+               status = EFX_MCDI_BOOT_PRIMARY;
+               memset(ver.emv_version, 0, sizeof (ver.emv_version));
+               ver.emv_firmware = 0;
+       } else if (rc != 0) {
+               goto fail2;
+       }
 
 out:
        if (versionp != NULL)
-               memcpy(versionp, version, sizeof (version));
+               memcpy(versionp, ver.emv_version, sizeof (ver.emv_version));
        if (buildp != NULL)
-               *buildp = build;
+               *buildp = ver.emv_firmware;
        if (statusp != NULL)
                *statusp = status;
 
        return (0);
 
-fail4:
-       EFSYS_PROBE(fail4);
-fail3:
-       EFSYS_PROBE(fail3);
 fail2:
        EFSYS_PROBE(fail2);
 fail1:
@@ -2034,6 +2203,36 @@ fail1:
 
 #if EFSYS_OPT_RIVERHEAD || EFX_OPTS_EF10()
 
+       __checkReturn           efx_rc_t
+efx_mcdi_intf_from_pcie(
+       __in                    uint32_t pcie_intf,
+       __out                   efx_pcie_interface_t *efx_intf)
+{
+       efx_rc_t rc;
+
+       switch (pcie_intf) {
+       case PCIE_INTERFACE_CALLER:
+               *efx_intf = EFX_PCIE_INTERFACE_CALLER;
+               break;
+       case PCIE_INTERFACE_HOST_PRIMARY:
+               *efx_intf = EFX_PCIE_INTERFACE_HOST_PRIMARY;
+               break;
+       case PCIE_INTERFACE_NIC_EMBEDDED:
+               *efx_intf = EFX_PCIE_INTERFACE_NIC_EMBEDDED;
+               break;
+       default:
+               rc = EINVAL;
+               goto fail1;
+       }
+
+       return (0);
+
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
 /*
  * This function returns the pf and vf number of a function.  If it is a pf the
  * vf number is 0xffff.  The vf number is the index of the vf on that
@@ -2044,18 +2243,21 @@ fail1:
 efx_mcdi_get_function_info(
        __in                    efx_nic_t *enp,
        __out                   uint32_t *pfp,
-       __out_opt               uint32_t *vfp)
+       __out_opt               uint32_t *vfp,
+       __out_opt               efx_pcie_interface_t *intfp)
 {
+       efx_pcie_interface_t intf;
        efx_mcdi_req_t req;
        EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_FUNCTION_INFO_IN_LEN,
-               MC_CMD_GET_FUNCTION_INFO_OUT_LEN);
+               MC_CMD_GET_FUNCTION_INFO_OUT_V2_LEN);
+       uint32_t pcie_intf;
        efx_rc_t rc;
 
        req.emr_cmd = MC_CMD_GET_FUNCTION_INFO;
        req.emr_in_buf = payload;
        req.emr_in_length = MC_CMD_GET_FUNCTION_INFO_IN_LEN;
        req.emr_out_buf = payload;
-       req.emr_out_length = MC_CMD_GET_FUNCTION_INFO_OUT_LEN;
+       req.emr_out_length = MC_CMD_GET_FUNCTION_INFO_OUT_V2_LEN;
 
        efx_mcdi_execute(enp, &req);
 
@@ -2073,8 +2275,24 @@ efx_mcdi_get_function_info(
        if (vfp != NULL)
                *vfp = MCDI_OUT_DWORD(req, GET_FUNCTION_INFO_OUT_VF);
 
+       if (req.emr_out_length < MC_CMD_GET_FUNCTION_INFO_OUT_V2_LEN) {
+               intf = EFX_PCIE_INTERFACE_HOST_PRIMARY;
+       } else {
+               pcie_intf = MCDI_OUT_DWORD(req,
+                   GET_FUNCTION_INFO_OUT_V2_INTF);
+
+               rc = efx_mcdi_intf_from_pcie(pcie_intf, &intf);
+               if (rc != 0)
+                       goto fail3;
+       }
+
+       if (intfp != NULL)
+               *intfp = intf;
+
        return (0);
 
+fail3:
+       EFSYS_PROBE(fail3);
 fail2:
        EFSYS_PROBE(fail2);
 fail1:
@@ -2198,6 +2416,11 @@ efx_mcdi_get_workarounds(
                goto fail1;
        }
 
+       if (req.emr_out_length_used < MC_CMD_GET_WORKAROUNDS_OUT_LEN) {
+               rc = EMSGSIZE;
+               goto fail2;
+       }
+
        if (implementedp != NULL) {
                *implementedp =
                    MCDI_OUT_DWORD(req, GET_WORKAROUNDS_OUT_IMPLEMENTED);
@@ -2209,6 +2432,8 @@ efx_mcdi_get_workarounds(
 
        return (0);
 
+fail2:
+       EFSYS_PROBE(fail2);
 fail1:
        EFSYS_PROBE1(fail1, efx_rc_t, rc);
 
@@ -2443,4 +2668,776 @@ fail1:
        return (rc);
 }
 
+#if EFSYS_OPT_RIVERHEAD || EFX_OPTS_EF10()
+
+#define        INIT_EVQ_MAXNBUFS       MC_CMD_INIT_EVQ_V2_IN_DMA_ADDR_MAXNUM
+
+#if EFX_OPTS_EF10()
+# if (INIT_EVQ_MAXNBUFS < EF10_EVQ_MAXNBUFS)
+#  error "INIT_EVQ_MAXNBUFS too small"
+# endif
+#endif /* EFX_OPTS_EF10 */
+#if EFSYS_OPT_RIVERHEAD
+# if (INIT_EVQ_MAXNBUFS < RHEAD_EVQ_MAXNBUFS)
+#  error "INIT_EVQ_MAXNBUFS too small"
+# endif
+#endif /* EFSYS_OPT_RIVERHEAD */
+
+       __checkReturn   efx_rc_t
+efx_mcdi_init_evq(
+       __in            efx_nic_t *enp,
+       __in            unsigned int instance,
+       __in            efsys_mem_t *esmp,
+       __in            size_t nevs,
+       __in            uint32_t irq,
+       __in            uint32_t target_evq,
+       __in            uint32_t us,
+       __in            uint32_t flags,
+       __in            boolean_t low_latency)
+{
+       const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
+       efx_mcdi_req_t req;
+       EFX_MCDI_DECLARE_BUF(payload,
+               MC_CMD_INIT_EVQ_V2_IN_LEN(INIT_EVQ_MAXNBUFS),
+               MC_CMD_INIT_EVQ_V2_OUT_LEN);
+       boolean_t interrupting;
+       int ev_extended_width;
+       int ev_cut_through;
+       int ev_merge;
+       unsigned int evq_type;
+       efx_qword_t *dma_addr;
+       uint64_t addr;
+       int npages;
+       int i;
+       efx_rc_t rc;
+
+       npages = efx_evq_nbufs(enp, nevs, flags);
+       if (npages > INIT_EVQ_MAXNBUFS) {
+               rc = EINVAL;
+               goto fail1;
+       }
+
+       req.emr_cmd = MC_CMD_INIT_EVQ;
+       req.emr_in_buf = payload;
+       req.emr_in_length = MC_CMD_INIT_EVQ_V2_IN_LEN(npages);
+       req.emr_out_buf = payload;
+       req.emr_out_length = MC_CMD_INIT_EVQ_V2_OUT_LEN;
+
+       MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_SIZE, nevs);
+       MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_INSTANCE, instance);
+
+       interrupting = ((flags & EFX_EVQ_FLAGS_NOTIFY_MASK) ==
+           EFX_EVQ_FLAGS_NOTIFY_INTERRUPT);
+
+       if (interrupting)
+               MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_IRQ_NUM, irq);
+       else
+               MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_TARGET_EVQ, target_evq);
+
+       if (encp->enc_init_evq_v2_supported) {
+               /*
+                * On Medford the low latency license is required to enable RX
+                * and event cut through and to disable RX batching.  If event
+                * queue type in flags is auto, we let the firmware decide the
+                * settings to use. If the adapter has a low latency license,
+                * it will choose the best settings for low latency, otherwise
+                * it will choose the best settings for throughput.
+                */
+               switch (flags & EFX_EVQ_FLAGS_TYPE_MASK) {
+               case EFX_EVQ_FLAGS_TYPE_AUTO:
+                       evq_type = MC_CMD_INIT_EVQ_V2_IN_FLAG_TYPE_AUTO;
+                       break;
+               case EFX_EVQ_FLAGS_TYPE_THROUGHPUT:
+                       evq_type = MC_CMD_INIT_EVQ_V2_IN_FLAG_TYPE_THROUGHPUT;
+                       break;
+               case EFX_EVQ_FLAGS_TYPE_LOW_LATENCY:
+                       evq_type = MC_CMD_INIT_EVQ_V2_IN_FLAG_TYPE_LOW_LATENCY;
+                       break;
+               default:
+                       rc = EINVAL;
+                       goto fail2;
+               }
+               /* EvQ type controls merging, no manual settings */
+               ev_merge = 0;
+               ev_cut_through = 0;
+       } else {
+               /* EvQ types other than manual are not supported */
+               evq_type = MC_CMD_INIT_EVQ_V2_IN_FLAG_TYPE_MANUAL;
+               /*
+                * On Huntington RX and TX event batching can only be requested
+                * together (even if the datapath firmware doesn't actually
+                * support RX batching). If event cut through is enabled no RX
+                * batching will occur.
+                *
+                * So always enable RX and TX event batching, and enable event
+                * cut through if we want low latency operation.
+                */
+               ev_merge = 1;
+               switch (flags & EFX_EVQ_FLAGS_TYPE_MASK) {
+               case EFX_EVQ_FLAGS_TYPE_AUTO:
+                       ev_cut_through = low_latency ? 1 : 0;
+                       break;
+               case EFX_EVQ_FLAGS_TYPE_THROUGHPUT:
+                       ev_cut_through = 0;
+                       break;
+               case EFX_EVQ_FLAGS_TYPE_LOW_LATENCY:
+                       ev_cut_through = 1;
+                       break;
+               default:
+                       rc = EINVAL;
+                       goto fail2;
+               }
+       }
+
+       /*
+        * On EF100, extended width event queues have a different event
+        * descriptor layout and are used to support descriptor proxy queues.
+        */
+       ev_extended_width = 0;
+#if EFSYS_OPT_EV_EXTENDED_WIDTH
+       if (encp->enc_init_evq_extended_width_supported) {
+               if (flags & EFX_EVQ_FLAGS_EXTENDED_WIDTH)
+                       ev_extended_width = 1;
+       }
+#endif
+
+       MCDI_IN_POPULATE_DWORD_8(req, INIT_EVQ_V2_IN_FLAGS,
+           INIT_EVQ_V2_IN_FLAG_INTERRUPTING, interrupting,
+           INIT_EVQ_V2_IN_FLAG_RPTR_DOS, 0,
+           INIT_EVQ_V2_IN_FLAG_INT_ARMD, 0,
+           INIT_EVQ_V2_IN_FLAG_CUT_THRU, ev_cut_through,
+           INIT_EVQ_V2_IN_FLAG_RX_MERGE, ev_merge,
+           INIT_EVQ_V2_IN_FLAG_TX_MERGE, ev_merge,
+           INIT_EVQ_V2_IN_FLAG_TYPE, evq_type,
+           INIT_EVQ_V2_IN_FLAG_EXT_WIDTH, ev_extended_width);
+
+       /* If the value is zero then disable the timer */
+       if (us == 0) {
+               MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_TMR_MODE,
+                   MC_CMD_INIT_EVQ_V2_IN_TMR_MODE_DIS);
+               MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_TMR_LOAD, 0);
+               MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_TMR_RELOAD, 0);
+       } else {
+               unsigned int ticks;
+
+               if ((rc = efx_ev_usecs_to_ticks(enp, us, &ticks)) != 0)
+                       goto fail3;
+
+               MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_TMR_MODE,
+                   MC_CMD_INIT_EVQ_V2_IN_TMR_INT_HLDOFF);
+               MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_TMR_LOAD, ticks);
+               MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_TMR_RELOAD, ticks);
+       }
+
+       MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_COUNT_MODE,
+           MC_CMD_INIT_EVQ_V2_IN_COUNT_MODE_DIS);
+       MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_COUNT_THRSHLD, 0);
+
+       dma_addr = MCDI_IN2(req, efx_qword_t, INIT_EVQ_V2_IN_DMA_ADDR);
+       addr = EFSYS_MEM_ADDR(esmp);
+
+       for (i = 0; i < npages; i++) {
+               EFX_POPULATE_QWORD_2(*dma_addr,
+                   EFX_DWORD_1, (uint32_t)(addr >> 32),
+                   EFX_DWORD_0, (uint32_t)(addr & 0xffffffff));
+
+               dma_addr++;
+               addr += EFX_BUF_SIZE;
+       }
+
+       efx_mcdi_execute(enp, &req);
+
+       if (req.emr_rc != 0) {
+               rc = req.emr_rc;
+               goto fail4;
+       }
+
+       if (encp->enc_init_evq_v2_supported) {
+               if (req.emr_out_length_used < MC_CMD_INIT_EVQ_V2_OUT_LEN) {
+                       rc = EMSGSIZE;
+                       goto fail5;
+               }
+               EFSYS_PROBE1(mcdi_evq_flags, uint32_t,
+                           MCDI_OUT_DWORD(req, INIT_EVQ_V2_OUT_FLAGS));
+       } else {
+               if (req.emr_out_length_used < MC_CMD_INIT_EVQ_OUT_LEN) {
+                       rc = EMSGSIZE;
+                       goto fail6;
+               }
+       }
+
+       /* NOTE: ignore the returned IRQ param as firmware does not set it. */
+
+       return (0);
+
+fail6:
+       EFSYS_PROBE(fail6);
+fail5:
+       EFSYS_PROBE(fail5);
+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_fini_evq(
+       __in            efx_nic_t *enp,
+       __in            uint32_t instance)
+{
+       efx_mcdi_req_t req;
+       EFX_MCDI_DECLARE_BUF(payload, MC_CMD_FINI_EVQ_IN_LEN,
+               MC_CMD_FINI_EVQ_OUT_LEN);
+       efx_rc_t rc;
+
+       req.emr_cmd = MC_CMD_FINI_EVQ;
+       req.emr_in_buf = payload;
+       req.emr_in_length = MC_CMD_FINI_EVQ_IN_LEN;
+       req.emr_out_buf = payload;
+       req.emr_out_length = MC_CMD_FINI_EVQ_OUT_LEN;
+
+       MCDI_IN_SET_DWORD(req, FINI_EVQ_IN_INSTANCE, instance);
+
+       efx_mcdi_execute_quiet(enp, &req);
+
+       if (req.emr_rc != 0) {
+               rc = req.emr_rc;
+               goto fail1;
+       }
+
+       return (0);
+
+fail1:
+       /*
+        * EALREADY is not an error, but indicates that the MC has rebooted and
+        * that the EVQ has already been destroyed.
+        */
+       if (rc != EALREADY)
+               EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+       __checkReturn   efx_rc_t
+efx_mcdi_init_rxq(
+       __in            efx_nic_t *enp,
+       __in            uint32_t ndescs,
+       __in            efx_evq_t *eep,
+       __in            uint32_t label,
+       __in            uint32_t instance,
+       __in            efsys_mem_t *esmp,
+       __in            const efx_mcdi_init_rxq_params_t *params)
+{
+       efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
+       efx_mcdi_req_t req;
+       EFX_MCDI_DECLARE_BUF(payload, MC_CMD_INIT_RXQ_V5_IN_LEN,
+               MC_CMD_INIT_RXQ_V5_OUT_LEN);
+       int npages = efx_rxq_nbufs(enp, ndescs);
+       int i;
+       efx_qword_t *dma_addr;
+       uint64_t addr;
+       efx_rc_t rc;
+       uint32_t dma_mode;
+       boolean_t want_outer_classes;
+       boolean_t no_cont_ev;
+
+       EFSYS_ASSERT3U(ndescs, <=, encp->enc_rxq_max_ndescs);
+
+       if ((esmp == NULL) ||
+           (EFSYS_MEM_SIZE(esmp) < efx_rxq_size(enp, ndescs))) {
+               rc = EINVAL;
+               goto fail1;
+       }
+
+       no_cont_ev = (eep->ee_flags & EFX_EVQ_FLAGS_NO_CONT_EV);
+       if ((no_cont_ev == B_TRUE) && (params->disable_scatter == B_FALSE)) {
+               /* TODO: Support scatter in NO_CONT_EV mode */
+               rc = EINVAL;
+               goto fail2;
+       }
+
+       if (params->ps_buf_size > 0)
+               dma_mode = MC_CMD_INIT_RXQ_EXT_IN_PACKED_STREAM;
+       else if (params->es_bufs_per_desc > 0)
+               dma_mode = MC_CMD_INIT_RXQ_V3_IN_EQUAL_STRIDE_SUPER_BUFFER;
+       else
+               dma_mode = MC_CMD_INIT_RXQ_EXT_IN_SINGLE_PACKET;
+
+       if (encp->enc_tunnel_encapsulations_supported != 0 &&
+           !params->want_inner_classes) {
+               /*
+                * WANT_OUTER_CLASSES can only be specified on hardware which
+                * supports tunnel encapsulation offloads, even though it is
+                * effectively the behaviour the hardware gives.
+                *
+                * Also, on hardware which does support such offloads, older
+                * firmware rejects the flag if the offloads are not supported
+                * by the current firmware variant, which means this may fail if
+                * the capabilities are not updated when the firmware variant
+                * changes. This is not an issue on newer firmware, as it was
+                * changed in bug 69842 (v6.4.2.1007) to permit this flag to be
+                * specified on all firmware variants.
+                */
+               want_outer_classes = B_TRUE;
+       } else {
+               want_outer_classes = B_FALSE;
+       }
+
+       req.emr_cmd = MC_CMD_INIT_RXQ;
+       req.emr_in_buf = payload;
+       req.emr_in_length = MC_CMD_INIT_RXQ_V5_IN_LEN;
+       req.emr_out_buf = payload;
+       req.emr_out_length = MC_CMD_INIT_RXQ_V5_OUT_LEN;
+
+       MCDI_IN_SET_DWORD(req, INIT_RXQ_EXT_IN_SIZE, ndescs);
+       MCDI_IN_SET_DWORD(req, INIT_RXQ_EXT_IN_TARGET_EVQ, eep->ee_index);
+       MCDI_IN_SET_DWORD(req, INIT_RXQ_EXT_IN_LABEL, label);
+       MCDI_IN_SET_DWORD(req, INIT_RXQ_EXT_IN_INSTANCE, instance);
+       MCDI_IN_POPULATE_DWORD_10(req, INIT_RXQ_EXT_IN_FLAGS,
+           INIT_RXQ_EXT_IN_FLAG_BUFF_MODE, 0,
+           INIT_RXQ_EXT_IN_FLAG_HDR_SPLIT, 0,
+           INIT_RXQ_EXT_IN_FLAG_TIMESTAMP, 0,
+           INIT_RXQ_EXT_IN_CRC_MODE, 0,
+           INIT_RXQ_EXT_IN_FLAG_PREFIX, 1,
+           INIT_RXQ_EXT_IN_FLAG_DISABLE_SCATTER, params->disable_scatter,
+           INIT_RXQ_EXT_IN_DMA_MODE,
+           dma_mode,
+           INIT_RXQ_EXT_IN_PACKED_STREAM_BUFF_SIZE, params->ps_buf_size,
+           INIT_RXQ_EXT_IN_FLAG_WANT_OUTER_CLASSES, want_outer_classes,
+           INIT_RXQ_EXT_IN_FLAG_NO_CONT_EV, no_cont_ev);
+       MCDI_IN_SET_DWORD(req, INIT_RXQ_EXT_IN_OWNER_ID, 0);
+       MCDI_IN_SET_DWORD(req, INIT_RXQ_EXT_IN_PORT_ID, enp->en_vport_id);
+
+       if (params->es_bufs_per_desc > 0) {
+               MCDI_IN_SET_DWORD(req,
+                   INIT_RXQ_V3_IN_ES_PACKET_BUFFERS_PER_BUCKET,
+                   params->es_bufs_per_desc);
+               MCDI_IN_SET_DWORD(req,
+                   INIT_RXQ_V3_IN_ES_MAX_DMA_LEN, params->es_max_dma_len);
+               MCDI_IN_SET_DWORD(req,
+                   INIT_RXQ_V3_IN_ES_PACKET_STRIDE, params->es_buf_stride);
+               MCDI_IN_SET_DWORD(req,
+                   INIT_RXQ_V3_IN_ES_HEAD_OF_LINE_BLOCK_TIMEOUT,
+                   params->hol_block_timeout);
+       }
+
+       if (encp->enc_init_rxq_with_buffer_size)
+               MCDI_IN_SET_DWORD(req, INIT_RXQ_V4_IN_BUFFER_SIZE_BYTES,
+                   params->buf_size);
+
+       MCDI_IN_SET_DWORD(req, INIT_RXQ_V5_IN_RX_PREFIX_ID, params->prefix_id);
+
+       dma_addr = MCDI_IN2(req, efx_qword_t, INIT_RXQ_IN_DMA_ADDR);
+       addr = EFSYS_MEM_ADDR(esmp);
+
+       for (i = 0; i < npages; i++) {
+               EFX_POPULATE_QWORD_2(*dma_addr,
+                   EFX_DWORD_1, (uint32_t)(addr >> 32),
+                   EFX_DWORD_0, (uint32_t)(addr & 0xffffffff));
+
+               dma_addr++;
+               addr += EFX_BUF_SIZE;
+       }
+
+       efx_mcdi_execute(enp, &req);
+
+       if (req.emr_rc != 0) {
+               rc = req.emr_rc;
+               goto fail3;
+       }
+
+       return (0);
+
+fail3:
+       EFSYS_PROBE(fail3);
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+       __checkReturn   efx_rc_t
+efx_mcdi_fini_rxq(
+       __in            efx_nic_t *enp,
+       __in            uint32_t instance)
+{
+       efx_mcdi_req_t req;
+       EFX_MCDI_DECLARE_BUF(payload, MC_CMD_FINI_RXQ_IN_LEN,
+               MC_CMD_FINI_RXQ_OUT_LEN);
+       efx_rc_t rc;
+
+       req.emr_cmd = MC_CMD_FINI_RXQ;
+       req.emr_in_buf = payload;
+       req.emr_in_length = MC_CMD_FINI_RXQ_IN_LEN;
+       req.emr_out_buf = payload;
+       req.emr_out_length = MC_CMD_FINI_RXQ_OUT_LEN;
+
+       MCDI_IN_SET_DWORD(req, FINI_RXQ_IN_INSTANCE, instance);
+
+       efx_mcdi_execute_quiet(enp, &req);
+
+       if (req.emr_rc != 0) {
+               rc = req.emr_rc;
+               goto fail1;
+       }
+
+       return (0);
+
+fail1:
+       /*
+        * EALREADY is not an error, but indicates that the MC has rebooted and
+        * that the RXQ has already been destroyed.
+        */
+       if (rc != EALREADY)
+               EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+       __checkReturn   efx_rc_t
+efx_mcdi_init_txq(
+       __in            efx_nic_t *enp,
+       __in            uint32_t ndescs,
+       __in            uint32_t target_evq,
+       __in            uint32_t label,
+       __in            uint32_t instance,
+       __in            uint16_t flags,
+       __in            efsys_mem_t *esmp)
+{
+       efx_mcdi_req_t req;
+       EFX_MCDI_DECLARE_BUF(payload, MC_CMD_INIT_TXQ_EXT_IN_LEN,
+               MC_CMD_INIT_TXQ_OUT_LEN);
+       efx_qword_t *dma_addr;
+       uint64_t addr;
+       int npages;
+       int i;
+       efx_rc_t rc;
+
+       EFSYS_ASSERT(MC_CMD_INIT_TXQ_EXT_IN_DMA_ADDR_MAXNUM >=
+           efx_txq_nbufs(enp, enp->en_nic_cfg.enc_txq_max_ndescs));
+
+       if ((esmp == NULL) ||
+           (EFSYS_MEM_SIZE(esmp) < efx_txq_size(enp, ndescs))) {
+               rc = EINVAL;
+               goto fail1;
+       }
+
+       npages = efx_txq_nbufs(enp, ndescs);
+       if (MC_CMD_INIT_TXQ_IN_LEN(npages) > sizeof (payload)) {
+               rc = EINVAL;
+               goto fail2;
+       }
+
+       req.emr_cmd = MC_CMD_INIT_TXQ;
+       req.emr_in_buf = payload;
+       req.emr_in_length = MC_CMD_INIT_TXQ_IN_LEN(npages);
+       req.emr_out_buf = payload;
+       req.emr_out_length = MC_CMD_INIT_TXQ_OUT_LEN;
+
+       MCDI_IN_SET_DWORD(req, INIT_TXQ_IN_SIZE, ndescs);
+       MCDI_IN_SET_DWORD(req, INIT_TXQ_IN_TARGET_EVQ, target_evq);
+       MCDI_IN_SET_DWORD(req, INIT_TXQ_IN_LABEL, label);
+       MCDI_IN_SET_DWORD(req, INIT_TXQ_IN_INSTANCE, instance);
+
+       MCDI_IN_POPULATE_DWORD_9(req, INIT_TXQ_IN_FLAGS,
+           INIT_TXQ_IN_FLAG_BUFF_MODE, 0,
+           INIT_TXQ_IN_FLAG_IP_CSUM_DIS,
+           (flags & EFX_TXQ_CKSUM_IPV4) ? 0 : 1,
+           INIT_TXQ_IN_FLAG_TCP_CSUM_DIS,
+           (flags & EFX_TXQ_CKSUM_TCPUDP) ? 0 : 1,
+           INIT_TXQ_EXT_IN_FLAG_INNER_IP_CSUM_EN,
+           (flags & EFX_TXQ_CKSUM_INNER_IPV4) ? 1 : 0,
+           INIT_TXQ_EXT_IN_FLAG_INNER_TCP_CSUM_EN,
+           (flags & EFX_TXQ_CKSUM_INNER_TCPUDP) ? 1 : 0,
+           INIT_TXQ_EXT_IN_FLAG_TSOV2_EN, (flags & EFX_TXQ_FATSOV2) ? 1 : 0,
+           INIT_TXQ_IN_FLAG_TCP_UDP_ONLY, 0,
+           INIT_TXQ_IN_CRC_MODE, 0,
+           INIT_TXQ_IN_FLAG_TIMESTAMP, 0);
+
+       MCDI_IN_SET_DWORD(req, INIT_TXQ_IN_OWNER_ID, 0);
+       MCDI_IN_SET_DWORD(req, INIT_TXQ_IN_PORT_ID, enp->en_vport_id);
+
+       dma_addr = MCDI_IN2(req, efx_qword_t, INIT_TXQ_IN_DMA_ADDR);
+       addr = EFSYS_MEM_ADDR(esmp);
+
+       for (i = 0; i < npages; i++) {
+               EFX_POPULATE_QWORD_2(*dma_addr,
+                   EFX_DWORD_1, (uint32_t)(addr >> 32),
+                   EFX_DWORD_0, (uint32_t)(addr & 0xffffffff));
+
+               dma_addr++;
+               addr += EFX_BUF_SIZE;
+       }
+
+       efx_mcdi_execute(enp, &req);
+
+       if (req.emr_rc != 0) {
+               rc = req.emr_rc;
+               goto fail3;
+       }
+
+       return (0);
+
+fail3:
+       EFSYS_PROBE(fail3);
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+       __checkReturn   efx_rc_t
+efx_mcdi_fini_txq(
+       __in            efx_nic_t *enp,
+       __in            uint32_t instance)
+{
+       efx_mcdi_req_t req;
+       EFX_MCDI_DECLARE_BUF(payload, MC_CMD_FINI_TXQ_IN_LEN,
+               MC_CMD_FINI_TXQ_OUT_LEN);
+       efx_rc_t rc;
+
+       req.emr_cmd = MC_CMD_FINI_TXQ;
+       req.emr_in_buf = payload;
+       req.emr_in_length = MC_CMD_FINI_TXQ_IN_LEN;
+       req.emr_out_buf = payload;
+       req.emr_out_length = MC_CMD_FINI_TXQ_OUT_LEN;
+
+       MCDI_IN_SET_DWORD(req, FINI_TXQ_IN_INSTANCE, instance);
+
+       efx_mcdi_execute_quiet(enp, &req);
+
+       if (req.emr_rc != 0) {
+               rc = req.emr_rc;
+               goto fail1;
+       }
+
+       return (0);
+
+fail1:
+       /*
+        * EALREADY is not an error, but indicates that the MC has rebooted and
+        * that the TXQ has already been destroyed.
+        */
+       if (rc != EALREADY)
+               EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+#endif /* EFSYS_OPT_RIVERHEAD || EFX_OPTS_EF10() */
+
+       __checkReturn   efx_rc_t
+efx_mcdi_get_nic_addr_info(
+       __in            efx_nic_t *enp,
+       __out           uint32_t *mapping_typep)
+{
+       EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_DESC_ADDR_INFO_IN_LEN,
+               MC_CMD_GET_DESC_ADDR_INFO_OUT_LEN);
+       efx_mcdi_req_t req;
+       efx_rc_t rc;
+
+       req.emr_cmd = MC_CMD_GET_DESC_ADDR_INFO;
+       req.emr_in_buf = payload;
+       req.emr_in_length = MC_CMD_GET_DESC_ADDR_INFO_IN_LEN;
+       req.emr_out_buf = payload;
+       req.emr_out_length = MC_CMD_GET_DESC_ADDR_INFO_OUT_LEN;
+
+       efx_mcdi_execute_quiet(enp, &req);
+
+       if (req.emr_rc != 0) {
+               rc = req.emr_rc;
+               goto fail1;
+       }
+
+       if (req.emr_out_length_used < MC_CMD_GET_DESC_ADDR_INFO_OUT_LEN) {
+               rc = EMSGSIZE;
+               goto fail2;
+       }
+
+       *mapping_typep =
+           MCDI_OUT_DWORD(req, GET_DESC_ADDR_INFO_OUT_MAPPING_TYPE);
+
+       return (0);
+
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+       __checkReturn   efx_rc_t
+efx_mcdi_get_nic_addr_regions(
+       __in            efx_nic_t *enp,
+       __out           efx_nic_dma_region_info_t *endrip)
+{
+       EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_DESC_ADDR_REGIONS_IN_LEN,
+               MC_CMD_GET_DESC_ADDR_REGIONS_OUT_LENMAX_MCDI2);
+       efx_xword_t *regions;
+       efx_mcdi_req_t req;
+       efx_rc_t rc;
+       size_t alloc_size;
+       unsigned int nregions;
+       unsigned int i;
+
+       req.emr_cmd = MC_CMD_GET_DESC_ADDR_REGIONS;
+       req.emr_in_buf = payload;
+       req.emr_in_length = MC_CMD_GET_DESC_ADDR_REGIONS_IN_LEN;
+       req.emr_out_buf = payload;
+       req.emr_out_length = MC_CMD_GET_DESC_ADDR_REGIONS_OUT_LENMAX_MCDI2;
+
+       efx_mcdi_execute_quiet(enp, &req);
+
+       if (req.emr_rc != 0) {
+               rc = req.emr_rc;
+               goto fail1;
+       }
+
+       if (req.emr_out_length_used <
+           MC_CMD_GET_DESC_ADDR_REGIONS_OUT_LENMIN) {
+               rc = EMSGSIZE;
+               goto fail2;
+       }
+
+       nregions = MC_CMD_GET_DESC_ADDR_REGIONS_OUT_REGIONS_NUM(
+           req.emr_out_length_used);
+
+       EFX_STATIC_ASSERT(sizeof (*regions) == DESC_ADDR_REGION_LEN);
+       regions = MCDI_OUT2(req, efx_xword_t,
+           GET_DESC_ADDR_REGIONS_OUT_REGIONS);
+
+       alloc_size = nregions * sizeof(endrip->endri_regions[0]);
+       if (alloc_size / sizeof (endrip->endri_regions[0]) != nregions) {
+               rc = ENOMEM;
+               goto fail3;
+       }
+
+       EFSYS_KMEM_ALLOC(enp->en_esip,
+           alloc_size,
+           endrip->endri_regions);
+       if (endrip->endri_regions == NULL) {
+               rc = ENOMEM;
+               goto fail4;
+       }
+
+       endrip->endri_count = nregions;
+       for (i = 0; i < nregions; ++i) {
+               efx_nic_dma_region_t *region_info;
+
+               region_info = &endrip->endri_regions[i];
+
+               region_info->endr_inuse = B_FALSE;
+
+               region_info->endr_nic_base =
+                   MCDI_OUT_INDEXED_MEMBER_QWORD(req,
+                       GET_DESC_ADDR_REGIONS_OUT_REGIONS, i,
+                       DESC_ADDR_REGION_DESC_ADDR_BASE);
+
+               region_info->endr_trgt_base =
+                   MCDI_OUT_INDEXED_MEMBER_QWORD(req,
+                       GET_DESC_ADDR_REGIONS_OUT_REGIONS, i,
+                       DESC_ADDR_REGION_TRGT_ADDR_BASE);
+
+               region_info->endr_window_log2 =
+                   MCDI_OUT_INDEXED_MEMBER_DWORD(req,
+                       GET_DESC_ADDR_REGIONS_OUT_REGIONS, i,
+                       DESC_ADDR_REGION_WINDOW_SIZE_LOG2);
+
+               region_info->endr_align_log2 =
+                   MCDI_OUT_INDEXED_MEMBER_DWORD(req,
+                       GET_DESC_ADDR_REGIONS_OUT_REGIONS, i,
+                       DESC_ADDR_REGION_TRGT_ADDR_ALIGN_LOG2);
+       }
+
+       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_set_nic_addr_regions(
+       __in            efx_nic_t *enp,
+       __in            const efx_nic_dma_region_info_t *endrip)
+{
+       EFX_MCDI_DECLARE_BUF(payload,
+               MC_CMD_SET_DESC_ADDR_REGIONS_IN_LENMAX_MCDI2,
+               MC_CMD_SET_DESC_ADDR_REGIONS_OUT_LEN);
+       efx_qword_t *trgt_addr_base;
+       efx_mcdi_req_t req;
+       unsigned int i;
+       efx_rc_t rc;
+
+       if (endrip->endri_count >
+           MC_CMD_SET_DESC_ADDR_REGIONS_IN_TRGT_ADDR_BASE_MAXNUM) {
+               rc = EINVAL;
+               goto fail1;
+       }
+
+       req.emr_cmd = MC_CMD_SET_DESC_ADDR_REGIONS;
+       req.emr_in_buf = payload;
+       req.emr_in_length =
+           MC_CMD_SET_DESC_ADDR_REGIONS_IN_LEN(endrip->endri_count);
+       req.emr_out_buf = payload;
+       req.emr_out_length = MC_CMD_SET_DESC_ADDR_REGIONS_OUT_LEN;
+
+       EFX_STATIC_ASSERT(sizeof (*trgt_addr_base) ==
+           MC_CMD_SET_DESC_ADDR_REGIONS_IN_TRGT_ADDR_BASE_LEN);
+       trgt_addr_base = MCDI_OUT2(req, efx_qword_t,
+           SET_DESC_ADDR_REGIONS_IN_TRGT_ADDR_BASE);
+
+       for (i = 0; i < endrip->endri_count; ++i) {
+               const efx_nic_dma_region_t *region_info;
+
+               region_info = &endrip->endri_regions[i];
+
+               if (region_info->endr_inuse != B_TRUE)
+                       continue;
+
+               EFX_STATIC_ASSERT(sizeof (1U) * 8 >=
+                   MC_CMD_SET_DESC_ADDR_REGIONS_IN_TRGT_ADDR_BASE_MAXNUM);
+               MCDI_IN_SET_DWORD(req,
+                   SET_DESC_ADDR_REGIONS_IN_SET_REGION_MASK, 1U << i);
+
+               MCDI_IN_SET_INDEXED_QWORD(req,
+                   SET_DESC_ADDR_REGIONS_IN_TRGT_ADDR_BASE, i,
+                   region_info->endr_trgt_base);
+       }
+
+       efx_mcdi_execute_quiet(enp, &req);
+
+       if (req.emr_rc != 0) {
+               rc = req.emr_rc;
+               goto fail2;
+       }
+
+       return (0);
+
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
 #endif /* EFSYS_OPT_MCDI */