New upstream version 18.08
[deb_dpdk.git] / drivers / net / sfc / base / ef10_nic.c
index eb9ec2b..7dbf843 100644 (file)
@@ -10,7 +10,7 @@
 #include "mcdi_mon.h"
 #endif
 
-#if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
+#if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
 
 #include "ef10_tlv_layout.h"
 
@@ -25,7 +25,8 @@ efx_mcdi_get_port_assignment(
        efx_rc_t rc;
 
        EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
-                   enp->en_family == EFX_FAMILY_MEDFORD);
+           enp->en_family == EFX_FAMILY_MEDFORD ||
+           enp->en_family == EFX_FAMILY_MEDFORD2);
 
        (void) memset(payload, 0, sizeof (payload));
        req.emr_cmd = MC_CMD_GET_PORT_ASSIGNMENT;
@@ -70,7 +71,8 @@ efx_mcdi_get_port_modes(
        efx_rc_t rc;
 
        EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
-                   enp->en_family == EFX_FAMILY_MEDFORD);
+           enp->en_family == EFX_FAMILY_MEDFORD ||
+           enp->en_family == EFX_FAMILY_MEDFORD2);
 
        (void) memset(payload, 0, sizeof (payload));
        req.emr_cmd = MC_CMD_GET_PORT_MODES;
@@ -250,7 +252,8 @@ efx_mcdi_get_mac_address_pf(
        efx_rc_t rc;
 
        EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
-                   enp->en_family == EFX_FAMILY_MEDFORD);
+           enp->en_family == EFX_FAMILY_MEDFORD ||
+           enp->en_family == EFX_FAMILY_MEDFORD2);
 
        (void) memset(payload, 0, sizeof (payload));
        req.emr_cmd = MC_CMD_GET_MAC_ADDRESSES;
@@ -308,7 +311,8 @@ efx_mcdi_get_mac_address_vf(
        efx_rc_t rc;
 
        EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
-                   enp->en_family == EFX_FAMILY_MEDFORD);
+           enp->en_family == EFX_FAMILY_MEDFORD ||
+           enp->en_family == EFX_FAMILY_MEDFORD2);
 
        (void) memset(payload, 0, sizeof (payload));
        req.emr_cmd = MC_CMD_VPORT_GET_MAC_ADDRESSES;
@@ -372,7 +376,8 @@ efx_mcdi_get_clock(
        efx_rc_t rc;
 
        EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
-                   enp->en_family == EFX_FAMILY_MEDFORD);
+           enp->en_family == EFX_FAMILY_MEDFORD ||
+           enp->en_family == EFX_FAMILY_MEDFORD2);
 
        (void) memset(payload, 0, sizeof (payload));
        req.emr_cmd = MC_CMD_GET_CLOCK;
@@ -415,6 +420,64 @@ fail2:
 fail1:
        EFSYS_PROBE1(fail1, efx_rc_t, rc);
 
+       return (rc);
+}
+
+       __checkReturn   efx_rc_t
+efx_mcdi_get_rxdp_config(
+       __in            efx_nic_t *enp,
+       __out           uint32_t *end_paddingp)
+{
+       efx_mcdi_req_t req;
+       uint8_t payload[MAX(MC_CMD_GET_RXDP_CONFIG_IN_LEN,
+                           MC_CMD_GET_RXDP_CONFIG_OUT_LEN)];
+       uint32_t end_padding;
+       efx_rc_t rc;
+
+       memset(payload, 0, sizeof (payload));
+       req.emr_cmd = MC_CMD_GET_RXDP_CONFIG;
+       req.emr_in_buf = payload;
+       req.emr_in_length = MC_CMD_GET_RXDP_CONFIG_IN_LEN;
+       req.emr_out_buf = payload;
+       req.emr_out_length = MC_CMD_GET_RXDP_CONFIG_OUT_LEN;
+
+       efx_mcdi_execute(enp, &req);
+       if (req.emr_rc != 0) {
+               rc = req.emr_rc;
+               goto fail1;
+       }
+
+       if (MCDI_OUT_DWORD_FIELD(req, GET_RXDP_CONFIG_OUT_DATA,
+                                   GET_RXDP_CONFIG_OUT_PAD_HOST_DMA) == 0) {
+               /* RX DMA end padding is disabled */
+               end_padding = 0;
+       } else {
+               switch (MCDI_OUT_DWORD_FIELD(req, GET_RXDP_CONFIG_OUT_DATA,
+                                           GET_RXDP_CONFIG_OUT_PAD_HOST_LEN)) {
+               case MC_CMD_SET_RXDP_CONFIG_IN_PAD_HOST_64:
+                       end_padding = 64;
+                       break;
+               case MC_CMD_SET_RXDP_CONFIG_IN_PAD_HOST_128:
+                       end_padding = 128;
+                       break;
+               case MC_CMD_SET_RXDP_CONFIG_IN_PAD_HOST_256:
+                       end_padding = 256;
+                       break;
+               default:
+                       rc = ENOTSUP;
+                       goto fail2;
+               }
+       }
+
+       *end_paddingp = end_padding;
+
+       return (0);
+
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
        return (rc);
 }
 
@@ -783,7 +846,8 @@ ef10_nic_pio_alloc(
        efx_rc_t rc;
 
        EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
-                   enp->en_family == EFX_FAMILY_MEDFORD);
+           enp->en_family == EFX_FAMILY_MEDFORD ||
+           enp->en_family == EFX_FAMILY_MEDFORD2);
        EFSYS_ASSERT(bufnump);
        EFSYS_ASSERT(handlep);
        EFSYS_ASSERT(blknump);
@@ -925,62 +989,103 @@ fail1:
        return (rc);
 }
 
-       __checkReturn   efx_rc_t
+static __checkReturn   efx_rc_t
 ef10_get_datapath_caps(
        __in            efx_nic_t *enp)
 {
        efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
-       uint32_t flags;
-       uint32_t flags2;
-       uint32_t tso2nc;
+       efx_mcdi_req_t req;
+       uint8_t payload[MAX(MC_CMD_GET_CAPABILITIES_IN_LEN,
+                           MC_CMD_GET_CAPABILITIES_V5_OUT_LEN)];
        efx_rc_t rc;
 
-       if ((rc = efx_mcdi_get_capabilities(enp, &flags, NULL, NULL,
-                                           &flags2, &tso2nc)) != 0)
-               goto fail1;
-
        if ((rc = ef10_mcdi_get_pf_count(enp, &encp->enc_hw_pf_count)) != 0)
                goto fail1;
 
-#define        CAP_FLAG(flags1, field)         \
-       ((flags1) & (1 << (MC_CMD_GET_CAPABILITIES_V2_OUT_ ## field ## _LBN)))
 
-#define        CAP_FLAG2(flags2, field)        \
-       ((flags2) & (1 << (MC_CMD_GET_CAPABILITIES_V2_OUT_ ## field ## _LBN)))
+       (void) memset(payload, 0, sizeof (payload));
+       req.emr_cmd = MC_CMD_GET_CAPABILITIES;
+       req.emr_in_buf = payload;
+       req.emr_in_length = MC_CMD_GET_CAPABILITIES_IN_LEN;
+       req.emr_out_buf = payload;
+       req.emr_out_length = MC_CMD_GET_CAPABILITIES_V5_OUT_LEN;
+
+       efx_mcdi_execute_quiet(enp, &req);
+
+       if (req.emr_rc != 0) {
+               rc = req.emr_rc;
+               goto fail2;
+       }
+
+       if (req.emr_out_length_used < MC_CMD_GET_CAPABILITIES_OUT_LEN) {
+               rc = EMSGSIZE;
+               goto fail3;
+       }
+
+#define        CAP_FLAGS1(_req, _flag)                                         \
+       (MCDI_OUT_DWORD((_req), GET_CAPABILITIES_OUT_FLAGS1) &          \
+       (1u << (MC_CMD_GET_CAPABILITIES_V2_OUT_ ## _flag ## _LBN)))
+
+#define        CAP_FLAGS2(_req, _flag)                                         \
+       (((_req).emr_out_length_used >= MC_CMD_GET_CAPABILITIES_V2_OUT_LEN) && \
+           (MCDI_OUT_DWORD((_req), GET_CAPABILITIES_V2_OUT_FLAGS2) &   \
+           (1u << (MC_CMD_GET_CAPABILITIES_V2_OUT_ ## _flag ## _LBN))))
 
        /*
         * Huntington RXDP firmware inserts a 0 or 14 byte prefix.
         * We only support the 14 byte prefix here.
         */
-       if (CAP_FLAG(flags, RX_PREFIX_LEN_14) == 0) {
+       if (CAP_FLAGS1(req, RX_PREFIX_LEN_14) == 0) {
                rc = ENOTSUP;
-               goto fail2;
+               goto fail4;
        }
        encp->enc_rx_prefix_size = 14;
 
+       /* Check if the firmware supports additional RSS modes */
+       if (CAP_FLAGS1(req, ADDITIONAL_RSS_MODES))
+               encp->enc_rx_scale_additional_modes_supported = B_TRUE;
+       else
+               encp->enc_rx_scale_additional_modes_supported = B_FALSE;
+
        /* Check if the firmware supports TSO */
-       encp->enc_fw_assisted_tso_enabled =
-           CAP_FLAG(flags, TX_TSO) ? B_TRUE : B_FALSE;
+       if (CAP_FLAGS1(req, TX_TSO))
+               encp->enc_fw_assisted_tso_enabled = B_TRUE;
+       else
+               encp->enc_fw_assisted_tso_enabled = B_FALSE;
 
        /* Check if the firmware supports FATSOv2 */
-       encp->enc_fw_assisted_tso_v2_enabled =
-           CAP_FLAG2(flags2, TX_TSO_V2) ? B_TRUE : B_FALSE;
+       if (CAP_FLAGS2(req, TX_TSO_V2)) {
+               encp->enc_fw_assisted_tso_v2_enabled = B_TRUE;
+               encp->enc_fw_assisted_tso_v2_n_contexts = MCDI_OUT_WORD(req,
+                   GET_CAPABILITIES_V2_OUT_TX_TSO_V2_N_CONTEXTS);
+       } else {
+               encp->enc_fw_assisted_tso_v2_enabled = B_FALSE;
+               encp->enc_fw_assisted_tso_v2_n_contexts = 0;
+       }
 
-       /* Get the number of TSO contexts (FATSOv2) */
-       encp->enc_fw_assisted_tso_v2_n_contexts =
-               CAP_FLAG2(flags2, TX_TSO_V2) ? tso2nc : 0;
+       /* Check if the firmware supports FATSOv2 encap */
+       if (CAP_FLAGS2(req, TX_TSO_V2_ENCAP))
+               encp->enc_fw_assisted_tso_v2_encap_enabled = B_TRUE;
+       else
+               encp->enc_fw_assisted_tso_v2_encap_enabled = B_FALSE;
 
        /* Check if the firmware has vadapter/vport/vswitch support */
-       encp->enc_datapath_cap_evb =
-           CAP_FLAG(flags, EVB) ? B_TRUE : B_FALSE;
+       if (CAP_FLAGS1(req, EVB))
+               encp->enc_datapath_cap_evb = B_TRUE;
+       else
+               encp->enc_datapath_cap_evb = B_FALSE;
 
        /* Check if the firmware supports VLAN insertion */
-       encp->enc_hw_tx_insert_vlan_enabled =
-           CAP_FLAG(flags, TX_VLAN_INSERTION) ? B_TRUE : B_FALSE;
+       if (CAP_FLAGS1(req, TX_VLAN_INSERTION))
+               encp->enc_hw_tx_insert_vlan_enabled = B_TRUE;
+       else
+               encp->enc_hw_tx_insert_vlan_enabled = B_FALSE;
 
        /* Check if the firmware supports RX event batching */
-       encp->enc_rx_batching_enabled =
-           CAP_FLAG(flags, RX_BATCHING) ? B_TRUE : B_FALSE;
+       if (CAP_FLAGS1(req, RX_BATCHING))
+               encp->enc_rx_batching_enabled = B_TRUE;
+       else
+               encp->enc_rx_batching_enabled = B_FALSE;
 
        /*
         * Even if batching isn't reported as supported, we may still get
@@ -989,38 +1094,61 @@ ef10_get_datapath_caps(
        encp->enc_rx_batch_max = 16;
 
        /* Check if the firmware supports disabling scatter on RXQs */
-       encp->enc_rx_disable_scatter_supported =
-           CAP_FLAG(flags, RX_DISABLE_SCATTER) ? B_TRUE : B_FALSE;
+       if (CAP_FLAGS1(req, RX_DISABLE_SCATTER))
+               encp->enc_rx_disable_scatter_supported = B_TRUE;
+       else
+               encp->enc_rx_disable_scatter_supported = B_FALSE;
 
        /* Check if the firmware supports packed stream mode */
-       encp->enc_rx_packed_stream_supported =
-           CAP_FLAG(flags, RX_PACKED_STREAM) ? B_TRUE : B_FALSE;
+       if (CAP_FLAGS1(req, RX_PACKED_STREAM))
+               encp->enc_rx_packed_stream_supported = B_TRUE;
+       else
+               encp->enc_rx_packed_stream_supported = B_FALSE;
 
        /*
         * Check if the firmware supports configurable buffer sizes
         * for packed stream mode (otherwise buffer size is 1Mbyte)
         */
-       encp->enc_rx_var_packed_stream_supported =
-           CAP_FLAG(flags, RX_PACKED_STREAM_VAR_BUFFERS) ? B_TRUE : B_FALSE;
+       if (CAP_FLAGS1(req, RX_PACKED_STREAM_VAR_BUFFERS))
+               encp->enc_rx_var_packed_stream_supported = B_TRUE;
+       else
+               encp->enc_rx_var_packed_stream_supported = B_FALSE;
+
+       /* Check if the firmware supports equal stride super-buffer mode */
+       if (CAP_FLAGS2(req, EQUAL_STRIDE_SUPER_BUFFER))
+               encp->enc_rx_es_super_buffer_supported = B_TRUE;
+       else
+               encp->enc_rx_es_super_buffer_supported = B_FALSE;
+
+       /* Check if the firmware supports FW subvariant w/o Tx checksumming */
+       if (CAP_FLAGS2(req, FW_SUBVARIANT_NO_TX_CSUM))
+               encp->enc_fw_subvariant_no_tx_csum_supported = B_TRUE;
+       else
+               encp->enc_fw_subvariant_no_tx_csum_supported = B_FALSE;
 
        /* Check if the firmware supports set mac with running filters */
-       encp->enc_allow_set_mac_with_installed_filters =
-           CAP_FLAG(flags, VADAPTOR_PERMIT_SET_MAC_WHEN_FILTERS_INSTALLED) ?
-           B_TRUE : B_FALSE;
+       if (CAP_FLAGS1(req, VADAPTOR_PERMIT_SET_MAC_WHEN_FILTERS_INSTALLED))
+               encp->enc_allow_set_mac_with_installed_filters = B_TRUE;
+       else
+               encp->enc_allow_set_mac_with_installed_filters = B_FALSE;
 
        /*
         * Check if firmware supports the extended MC_CMD_SET_MAC, which allows
         * specifying which parameters to configure.
         */
-       encp->enc_enhanced_set_mac_supported =
-               CAP_FLAG(flags, SET_MAC_ENHANCED) ? B_TRUE : B_FALSE;
+       if (CAP_FLAGS1(req, SET_MAC_ENHANCED))
+               encp->enc_enhanced_set_mac_supported = B_TRUE;
+       else
+               encp->enc_enhanced_set_mac_supported = B_FALSE;
 
        /*
         * Check if firmware supports version 2 of MC_CMD_INIT_EVQ, which allows
         * us to let the firmware choose the settings to use on an EVQ.
         */
-       encp->enc_init_evq_v2_supported =
-               CAP_FLAG2(flags2, INIT_EVQ_V2) ? B_TRUE : B_FALSE;
+       if (CAP_FLAGS2(req, INIT_EVQ_V2))
+               encp->enc_init_evq_v2_supported = B_TRUE;
+       else
+               encp->enc_init_evq_v2_supported = B_FALSE;
 
        /*
         * Check if firmware-verified NVRAM updates must be used.
@@ -1030,29 +1158,34 @@ ef10_get_datapath_caps(
         * and version 2 of MC_CMD_NVRAM_UPDATE_FINISH (to verify the updated
         * partition and report the result).
         */
-       encp->enc_nvram_update_verify_result_supported =
-           CAP_FLAG2(flags2, NVRAM_UPDATE_REPORT_VERIFY_RESULT) ?
-           B_TRUE : B_FALSE;
+       if (CAP_FLAGS2(req, NVRAM_UPDATE_REPORT_VERIFY_RESULT))
+               encp->enc_nvram_update_verify_result_supported = B_TRUE;
+       else
+               encp->enc_nvram_update_verify_result_supported = B_FALSE;
 
        /*
         * Check if firmware provides packet memory and Rx datapath
         * counters.
         */
-       encp->enc_pm_and_rxdp_counters =
-           CAP_FLAG(flags, PM_AND_RXDP_COUNTERS) ? B_TRUE : B_FALSE;
+       if (CAP_FLAGS1(req, PM_AND_RXDP_COUNTERS))
+               encp->enc_pm_and_rxdp_counters = B_TRUE;
+       else
+               encp->enc_pm_and_rxdp_counters = B_FALSE;
 
        /*
         * Check if the 40G MAC hardware is capable of reporting
         * statistics for Tx size bins.
         */
-       encp->enc_mac_stats_40g_tx_size_bins =
-           CAP_FLAG2(flags2, MAC_STATS_40G_TX_SIZE_BINS) ? B_TRUE : B_FALSE;
+       if (CAP_FLAGS2(req, MAC_STATS_40G_TX_SIZE_BINS))
+               encp->enc_mac_stats_40g_tx_size_bins = B_TRUE;
+       else
+               encp->enc_mac_stats_40g_tx_size_bins = B_FALSE;
 
        /*
         * Check if firmware supports VXLAN and NVGRE tunnels.
         * The capability indicates Geneve protocol support as well.
         */
-       if (CAP_FLAG(flags, VXLAN_NVGRE)) {
+       if (CAP_FLAGS1(req, VXLAN_NVGRE)) {
                encp->enc_tunnel_encapsulations_supported =
                    (1u << EFX_TUNNEL_PROTOCOL_VXLAN) |
                    (1u << EFX_TUNNEL_PROTOCOL_GENEVE) |
@@ -1066,11 +1199,136 @@ ef10_get_datapath_caps(
                encp->enc_tunnel_config_udp_entries_max = 0;
        }
 
-#undef CAP_FLAG
-#undef CAP_FLAG2
+       /*
+        * Check if firmware reports the VI window mode.
+        * Medford2 has a variable VI window size (8K, 16K or 64K).
+        * Medford and Huntington have a fixed 8K VI window size.
+        */
+       if (req.emr_out_length_used >= MC_CMD_GET_CAPABILITIES_V3_OUT_LEN) {
+               uint8_t mode =
+                   MCDI_OUT_BYTE(req, GET_CAPABILITIES_V3_OUT_VI_WINDOW_MODE);
+
+               switch (mode) {
+               case MC_CMD_GET_CAPABILITIES_V3_OUT_VI_WINDOW_MODE_8K:
+                       encp->enc_vi_window_shift = EFX_VI_WINDOW_SHIFT_8K;
+                       break;
+               case MC_CMD_GET_CAPABILITIES_V3_OUT_VI_WINDOW_MODE_16K:
+                       encp->enc_vi_window_shift = EFX_VI_WINDOW_SHIFT_16K;
+                       break;
+               case MC_CMD_GET_CAPABILITIES_V3_OUT_VI_WINDOW_MODE_64K:
+                       encp->enc_vi_window_shift = EFX_VI_WINDOW_SHIFT_64K;
+                       break;
+               default:
+                       encp->enc_vi_window_shift = EFX_VI_WINDOW_SHIFT_INVALID;
+                       break;
+               }
+       } else if ((enp->en_family == EFX_FAMILY_HUNTINGTON) ||
+                   (enp->en_family == EFX_FAMILY_MEDFORD)) {
+               /* Huntington and Medford have fixed 8K window size */
+               encp->enc_vi_window_shift = EFX_VI_WINDOW_SHIFT_8K;
+       } else {
+               encp->enc_vi_window_shift = EFX_VI_WINDOW_SHIFT_INVALID;
+       }
+
+       /* Check if firmware supports extended MAC stats. */
+       if (req.emr_out_length_used >= MC_CMD_GET_CAPABILITIES_V4_OUT_LEN) {
+               /* Extended stats buffer supported */
+               encp->enc_mac_stats_nstats = MCDI_OUT_WORD(req,
+                   GET_CAPABILITIES_V4_OUT_MAC_STATS_NUM_STATS);
+       } else {
+               /* Use Siena-compatible legacy MAC stats */
+               encp->enc_mac_stats_nstats = MC_CMD_MAC_NSTATS;
+       }
+
+       if (encp->enc_mac_stats_nstats >= MC_CMD_MAC_NSTATS_V2)
+               encp->enc_fec_counters = B_TRUE;
+       else
+               encp->enc_fec_counters = B_FALSE;
+
+       /* Check if the firmware provides head-of-line blocking counters */
+       if (CAP_FLAGS2(req, RXDP_HLB_IDLE))
+               encp->enc_hlb_counters = B_TRUE;
+       else
+               encp->enc_hlb_counters = B_FALSE;
+
+       if (CAP_FLAGS1(req, RX_RSS_LIMITED)) {
+               /* Only one exclusive RSS context is available per port. */
+               encp->enc_rx_scale_max_exclusive_contexts = 1;
+
+               switch (enp->en_family) {
+               case EFX_FAMILY_MEDFORD2:
+                       encp->enc_rx_scale_hash_alg_mask =
+                           (1U << EFX_RX_HASHALG_TOEPLITZ);
+                       break;
+
+               case EFX_FAMILY_MEDFORD:
+               case EFX_FAMILY_HUNTINGTON:
+                       /*
+                        * Packed stream firmware variant maintains a
+                        * non-standard algorithm for hash computation.
+                        * It implies explicit XORing together
+                        * source + destination IP addresses (or last
+                        * four bytes in the case of IPv6) and using the
+                        * resulting value as the input to a Toeplitz hash.
+                        */
+                       encp->enc_rx_scale_hash_alg_mask =
+                           (1U << EFX_RX_HASHALG_PACKED_STREAM);
+                       break;
+
+               default:
+                       rc = EINVAL;
+                       goto fail5;
+               }
+
+               /* Port numbers cannot contribute to the hash value */
+               encp->enc_rx_scale_l4_hash_supported = B_FALSE;
+       } else {
+               /*
+                * Maximum number of exclusive RSS contexts.
+                * EF10 hardware supports 64 in total, but 6 are reserved
+                * for shared contexts. They are a global resource so
+                * not all may be available.
+                */
+               encp->enc_rx_scale_max_exclusive_contexts = 64 - 6;
+
+               encp->enc_rx_scale_hash_alg_mask =
+                   (1U << EFX_RX_HASHALG_TOEPLITZ);
+
+               /*
+                * It is possible to use port numbers as
+                * the input data for hash computation.
+                */
+               encp->enc_rx_scale_l4_hash_supported = B_TRUE;
+       }
+       /* Check if the firmware supports "FLAG" and "MARK" filter actions */
+       if (CAP_FLAGS2(req, FILTER_ACTION_FLAG))
+               encp->enc_filter_action_flag_supported = B_TRUE;
+       else
+               encp->enc_filter_action_flag_supported = B_FALSE;
+
+       if (CAP_FLAGS2(req, FILTER_ACTION_MARK))
+               encp->enc_filter_action_mark_supported = B_TRUE;
+       else
+               encp->enc_filter_action_mark_supported = B_FALSE;
+
+       /* Get maximum supported value for "MARK" filter action */
+       if (req.emr_out_length_used >= MC_CMD_GET_CAPABILITIES_V5_OUT_LEN)
+               encp->enc_filter_action_mark_max = MCDI_OUT_DWORD(req,
+                   GET_CAPABILITIES_V5_OUT_FILTER_ACTION_MARK_MAX);
+       else
+               encp->enc_filter_action_mark_max = 0;
+
+#undef CAP_FLAGS1
+#undef CAP_FLAGS2
 
        return (0);
 
+fail5:
+       EFSYS_PROBE(fail5);
+fail4:
+       EFSYS_PROBE(fail4);
+fail3:
+       EFSYS_PROBE(fail3);
 fail2:
        EFSYS_PROBE(fail2);
 fail1:
@@ -1132,78 +1390,238 @@ fail1:
 
 
 /*
- * Table of mapping schemes from port number to the number of the external
- * connector on the board. The external numbering does not distinguish
- * off-board separated outputs such as from multi-headed cables.
+ * Table of mapping schemes from port number to external number.
+ *
+ * Each port number ultimately corresponds to a connector: either as part of
+ * a cable assembly attached to a module inserted in an SFP+/QSFP+ cage on
+ * the board, or fixed to the board (e.g. 10GBASE-T magjack on SFN5121T
+ * "Salina"). In general:
  *
- * The count of adjacent port numbers that map to each external port
+ * Port number (0-based)
+ *     |
+ *   port mapping (n:1)
+ *     |
+ *     v
+ * External port number (normally 1-based)
+ *     |
+ *   fixed (1:1) or cable assembly (1:m)
+ *     |
+ *     v
+ * Connector
+ *
+ * The external numbering refers to the cages or magjacks on the board,
+ * as visibly annotated on the board or back panel. This table describes
+ * how to determine which external cage/magjack corresponds to the port
+ * numbers used by the driver.
+ *
+ * The count of adjacent port numbers that map to each external number,
  * and the offset in the numbering, is determined by the chip family and
  * current port mode.
  *
  * For the Huntington family, the current port mode cannot be discovered,
+ * but a single mapping is used by all modes for a given chip variant,
  * so the mapping used is instead the last match in the table to the full
  * set of port modes to which the NIC can be configured. Therefore the
  * ordering of entries in the mapping table is significant.
  */
-static struct {
+static struct ef10_external_port_map_s {
        efx_family_t    family;
        uint32_t        modes_mask;
        int32_t         count;
        int32_t         offset;
 }      __ef10_external_port_mappings[] = {
-       /* Supported modes with 1 output per external port */
+       /*
+        * Modes used by Huntington family controllers where each port
+        * number maps to a separate cage.
+        * SFN7x22F (Torino):
+        *      port 0 -> cage 1
+        *      port 1 -> cage 2
+        * SFN7xx4F (Pavia):
+        *      port 0 -> cage 1
+        *      port 1 -> cage 2
+        *      port 2 -> cage 3
+        *      port 3 -> cage 4
+        */
        {
                EFX_FAMILY_HUNTINGTON,
-               (1 << TLV_PORT_MODE_10G) |
-               (1 << TLV_PORT_MODE_10G_10G) |
-               (1 << TLV_PORT_MODE_10G_10G_10G_10G),
-               1,
-               1
+               (1U << TLV_PORT_MODE_10G) |                     /* mode 0 */
+               (1U << TLV_PORT_MODE_10G_10G) |                 /* mode 2 */
+               (1U << TLV_PORT_MODE_10G_10G_10G_10G),          /* mode 4 */
+               1,      /* ports per cage */
+               1       /* first cage */
        },
+       /*
+        * Modes which for Huntington identify a chip variant where 2
+        * adjacent port numbers map to each cage.
+        * SFN7x42Q (Monza):
+        *      port 0 -> cage 1
+        *      port 1 -> cage 1
+        *      port 2 -> cage 2
+        *      port 3 -> cage 2
+        */
        {
-               EFX_FAMILY_MEDFORD,
-               (1 << TLV_PORT_MODE_10G) |
-               (1 << TLV_PORT_MODE_10G_10G),
-               1,
-               1
+               EFX_FAMILY_HUNTINGTON,
+               (1U << TLV_PORT_MODE_40G) |                     /* mode 1 */
+               (1U << TLV_PORT_MODE_40G_40G) |                 /* mode 3 */
+               (1U << TLV_PORT_MODE_40G_10G_10G) |             /* mode 6 */
+               (1U << TLV_PORT_MODE_10G_10G_40G),              /* mode 7 */
+               2,      /* ports per cage */
+               1       /* first cage */
        },
-       /* Supported modes with 2 outputs per external port */
+       /*
+        * Modes that on Medford allocate each port number to a separate
+        * cage.
+        *      port 0 -> cage 1
+        *      port 1 -> cage 2
+        *      port 2 -> cage 3
+        *      port 3 -> cage 4
+        */
        {
-               EFX_FAMILY_HUNTINGTON,
-               (1 << TLV_PORT_MODE_40G) |
-               (1 << TLV_PORT_MODE_40G_40G) |
-               (1 << TLV_PORT_MODE_40G_10G_10G) |
-               (1 << TLV_PORT_MODE_10G_10G_40G),
-               2,
-               1
+               EFX_FAMILY_MEDFORD,
+               (1U << TLV_PORT_MODE_10G) |                     /* mode 0 */
+               (1U << TLV_PORT_MODE_10G_10G),                  /* mode 2 */
+               1,      /* ports per cage */
+               1       /* first cage */
        },
+       /*
+        * Modes that on Medford allocate 2 adjacent port numbers to each
+        * cage.
+        *      port 0 -> cage 1
+        *      port 1 -> cage 1
+        *      port 2 -> cage 2
+        *      port 3 -> cage 2
+        */
        {
                EFX_FAMILY_MEDFORD,
-               (1 << TLV_PORT_MODE_40G) |
-               (1 << TLV_PORT_MODE_40G_40G) |
-               (1 << TLV_PORT_MODE_40G_10G_10G) |
-               (1 << TLV_PORT_MODE_10G_10G_40G) |
-               (1 << TLV_PORT_MODE_10G_10G_10G_10G_Q1_Q2),
-               2,
-               1
+               (1U << TLV_PORT_MODE_40G) |                     /* mode 1 */
+               (1U << TLV_PORT_MODE_40G_40G) |                 /* mode 3 */
+               (1U << TLV_PORT_MODE_40G_10G_10G) |             /* mode 6 */
+               (1U << TLV_PORT_MODE_10G_10G_40G) |             /* mode 7 */
+               /* Do not use 10G_10G_10G_10G_Q1_Q2 (see bug63270) */
+               (1U << TLV_PORT_MODE_10G_10G_10G_10G_Q1_Q2),    /* mode 9 */
+               2,      /* ports per cage */
+               1       /* first cage */
        },
-       /* Supported modes with 4 outputs per external port */
+       /*
+        * Modes that on Medford allocate 4 adjacent port numbers to each
+        * connector, starting on cage 1.
+        *      port 0 -> cage 1
+        *      port 1 -> cage 1
+        *      port 2 -> cage 1
+        *      port 3 -> cage 1
+        */
        {
                EFX_FAMILY_MEDFORD,
-               (1 << TLV_PORT_MODE_10G_10G_10G_10G_Q) |
-               (1 << TLV_PORT_MODE_10G_10G_10G_10G_Q1),
-               4,
-               1,
+               (1U << TLV_PORT_MODE_10G_10G_10G_10G_Q) |       /* mode 5 */
+               /* Do not use 10G_10G_10G_10G_Q1 (see bug63270) */
+               (1U << TLV_PORT_MODE_10G_10G_10G_10G_Q1),       /* mode 4 */
+               4,      /* ports per cage */
+               1       /* first cage */
        },
+       /*
+        * Modes that on Medford allocate 4 adjacent port numbers to each
+        * connector, starting on cage 2.
+        *      port 0 -> cage 2
+        *      port 1 -> cage 2
+        *      port 2 -> cage 2
+        *      port 3 -> cage 2
+        */
        {
                EFX_FAMILY_MEDFORD,
-               (1 << TLV_PORT_MODE_10G_10G_10G_10G_Q2),
-               4,
-               2
+               (1U << TLV_PORT_MODE_10G_10G_10G_10G_Q2),       /* mode 8 */
+               4,      /* ports per cage */
+               2       /* first cage */
+       },
+       /*
+        * Modes that on Medford2 allocate each port number to a separate
+        * cage.
+        *      port 0 -> cage 1
+        *      port 1 -> cage 2
+        *      port 2 -> cage 3
+        *      port 3 -> cage 4
+        */
+       {
+               EFX_FAMILY_MEDFORD2,
+               (1U << TLV_PORT_MODE_1x1_NA) |                  /* mode 0 */
+               (1U << TLV_PORT_MODE_1x4_NA) |                  /* mode 1 */
+               (1U << TLV_PORT_MODE_1x1_1x1) |                 /* mode 2 */
+               (1U << TLV_PORT_MODE_1x2_NA) |                  /* mode 10 */
+               (1U << TLV_PORT_MODE_1x2_1x2) |                 /* mode 12 */
+               (1U << TLV_PORT_MODE_1x4_1x2) |                 /* mode 15 */
+               (1U << TLV_PORT_MODE_1x2_1x4),                  /* mode 16 */
+               1,      /* ports per cage */
+               1       /* first cage */
+       },
+       /*
+        * FIXME: Some port modes are not representable in this mapping:
+        *  - TLV_PORT_MODE_1x2_2x1 (mode 17):
+        *      port 0 -> cage 1
+        *      port 1 -> cage 2
+        *      port 2 -> cage 2
+        */
+       /*
+        * Modes that on Medford2 allocate 2 adjacent port numbers to each
+        * cage, starting on cage 1.
+        *      port 0 -> cage 1
+        *      port 1 -> cage 1
+        *      port 2 -> cage 2
+        *      port 3 -> cage 2
+        */
+       {
+               EFX_FAMILY_MEDFORD2,
+               (1U << TLV_PORT_MODE_1x4_1x4) |                 /* mode 3 */
+               (1U << TLV_PORT_MODE_2x1_2x1) |                 /* mode 4 */
+               (1U << TLV_PORT_MODE_1x4_2x1) |                 /* mode 6 */
+               (1U << TLV_PORT_MODE_2x1_1x4) |                 /* mode 7 */
+               (1U << TLV_PORT_MODE_2x2_NA) |                  /* mode 13 */
+               (1U << TLV_PORT_MODE_2x1_1x2),                  /* mode 18 */
+               2,      /* ports per cage */
+               1       /* first cage */
+       },
+       /*
+        * Modes that on Medford2 allocate 2 adjacent port numbers to each
+        * cage, starting on cage 2.
+        *      port 0 -> cage 2
+        *      port 1 -> cage 2
+        */
+       {
+               EFX_FAMILY_MEDFORD2,
+               (1U << TLV_PORT_MODE_NA_2x2),                   /* mode 14 */
+               2,      /* ports per cage */
+               2       /* first cage */
+       },
+       /*
+        * Modes that on Medford2 allocate 4 adjacent port numbers to each
+        * connector, starting on cage 1.
+        *      port 0 -> cage 1
+        *      port 1 -> cage 1
+        *      port 2 -> cage 1
+        *      port 3 -> cage 1
+        */
+       {
+               EFX_FAMILY_MEDFORD2,
+               (1U << TLV_PORT_MODE_4x1_NA),                   /* mode 5 */
+               4,      /* ports per cage */
+               1       /* first cage */
+       },
+       /*
+        * Modes that on Medford2 allocate 4 adjacent port numbers to each
+        * connector, starting on cage 2.
+        *      port 0 -> cage 2
+        *      port 1 -> cage 2
+        *      port 2 -> cage 2
+        *      port 3 -> cage 2
+        */
+       {
+               EFX_FAMILY_MEDFORD2,
+               (1U << TLV_PORT_MODE_NA_4x1) |                  /* mode 8 */
+               (1U << TLV_PORT_MODE_NA_1x2),                   /* mode 11 */
+               4,      /* ports per cage */
+               2       /* first cage */
        },
 };
 
-       __checkReturn   efx_rc_t
+static __checkReturn   efx_rc_t
 ef10_external_port_mapping(
        __in            efx_nic_t *enp,
        __in            uint32_t port,
@@ -1219,7 +1637,7 @@ ef10_external_port_mapping(
 
        if ((rc = efx_mcdi_get_port_modes(enp, &port_modes, &current)) != 0) {
                /*
-                * No current port mode information
+                * No current port mode information (i.e. Huntington)
                 * - infer mapping from available modes
                 */
                if ((rc = efx_mcdi_get_port_modes(enp,
@@ -1236,18 +1654,23 @@ ef10_external_port_mapping(
        }
 
        /*
-        * Infer the internal port -> external port mapping from
+        * Infer the internal port -> external number mapping from
         * the possible port modes for this NIC.
         */
        for (i = 0; i < EFX_ARRAY_SIZE(__ef10_external_port_mappings); ++i) {
-               if (__ef10_external_port_mappings[i].family !=
-                   enp->en_family)
+               struct ef10_external_port_map_s *eepmp =
+                   &__ef10_external_port_mappings[i];
+               if (eepmp->family != enp->en_family)
                        continue;
-               matches = (__ef10_external_port_mappings[i].modes_mask &
-                   port_modes);
+               matches = (eepmp->modes_mask & port_modes);
                if (matches != 0) {
-                       count = __ef10_external_port_mappings[i].count;
-                       offset = __ef10_external_port_mappings[i].offset;
+                       /*
+                        * Some modes match. For some Huntington boards
+                        * there will be multiple matches. The mapping on the
+                        * last match is used.
+                        */
+                       count = eepmp->count;
+                       offset = eepmp->offset;
                        port_modes &= ~matches;
                }
        }
@@ -1272,18 +1695,193 @@ fail1:
        return (rc);
 }
 
+static __checkReturn   efx_rc_t
+ef10_nic_board_cfg(
+       __in            efx_nic_t *enp)
+{
+       const efx_nic_ops_t *enop = enp->en_enop;
+       efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
+       efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
+       ef10_link_state_t els;
+       efx_port_t *epp = &(enp->en_port);
+       uint32_t board_type = 0;
+       uint32_t base, nvec;
+       uint32_t port;
+       uint32_t mask;
+       uint32_t pf;
+       uint32_t vf;
+       uint8_t mac_addr[6] = { 0 };
+       efx_rc_t rc;
+
+       /* Get the (zero-based) MCDI port number */
+       if ((rc = efx_mcdi_get_port_assignment(enp, &port)) != 0)
+               goto fail1;
+
+       /* EFX MCDI interface uses one-based port numbers */
+       emip->emi_port = port + 1;
+
+       if ((rc = ef10_external_port_mapping(enp, port,
+                   &encp->enc_external_port)) != 0)
+               goto fail2;
+
+       /*
+        * Get PCIe function number from firmware (used for
+        * per-function privilege and dynamic config info).
+        *  - PCIe PF: pf = PF number, vf = 0xffff.
+        *  - PCIe VF: pf = parent PF, vf = VF number.
+        */
+       if ((rc = efx_mcdi_get_function_info(enp, &pf, &vf)) != 0)
+               goto fail3;
+
+       encp->enc_pf = pf;
+       encp->enc_vf = vf;
+
+       /* MAC address for this function */
+       if (EFX_PCI_FUNCTION_IS_PF(encp)) {
+               rc = efx_mcdi_get_mac_address_pf(enp, mac_addr);
+#if EFSYS_OPT_ALLOW_UNCONFIGURED_NIC
+               /*
+                * Disable static config checking, ONLY for manufacturing test
+                * and setup at the factory, to allow the static config to be
+                * installed.
+                */
+#else /* EFSYS_OPT_ALLOW_UNCONFIGURED_NIC */
+               if ((rc == 0) && (mac_addr[0] & 0x02)) {
+                       /*
+                        * If the static config does not include a global MAC
+                        * address pool then the board may return a locally
+                        * administered MAC address (this should only happen on
+                        * incorrectly programmed boards).
+                        */
+                       rc = EINVAL;
+               }
+#endif /* EFSYS_OPT_ALLOW_UNCONFIGURED_NIC */
+       } else {
+               rc = efx_mcdi_get_mac_address_vf(enp, mac_addr);
+       }
+       if (rc != 0)
+               goto fail4;
+
+       EFX_MAC_ADDR_COPY(encp->enc_mac_addr, mac_addr);
+
+       /* Board configuration (legacy) */
+       rc = efx_mcdi_get_board_cfg(enp, &board_type, NULL, NULL);
+       if (rc != 0) {
+               /* Unprivileged functions may not be able to read board cfg */
+               if (rc == EACCES)
+                       board_type = 0;
+               else
+                       goto fail5;
+       }
+
+       encp->enc_board_type = board_type;
+       encp->enc_clk_mult = 1; /* not used for EF10 */
+
+       /* Fill out fields in enp->en_port and enp->en_nic_cfg from MCDI */
+       if ((rc = efx_mcdi_get_phy_cfg(enp)) != 0)
+               goto fail6;
+
+       /* Obtain the default PHY advertised capabilities */
+       if ((rc = ef10_phy_get_link(enp, &els)) != 0)
+               goto fail7;
+       epp->ep_default_adv_cap_mask = els.els_adv_cap_mask;
+       epp->ep_adv_cap_mask = els.els_adv_cap_mask;
+
+       /* Check capabilities of running datapath firmware */
+       if ((rc = ef10_get_datapath_caps(enp)) != 0)
+               goto fail8;
+
+       /* Alignment for WPTR updates */
+       encp->enc_rx_push_align = EF10_RX_WPTR_ALIGN;
+
+       encp->enc_tx_dma_desc_size_max = EFX_MASK32(ESF_DZ_RX_KER_BYTE_CNT);
+       /* No boundary crossing limits */
+       encp->enc_tx_dma_desc_boundary = 0;
+
+       /*
+        * Maximum number of bytes into the frame the TCP header can start for
+        * firmware assisted TSO to work.
+        */
+       encp->enc_tx_tso_tcp_header_offset_limit = EF10_TCP_HEADER_OFFSET_LIMIT;
+
+       /*
+        * Set resource limits for MC_CMD_ALLOC_VIS. Note that we cannot use
+        * MC_CMD_GET_RESOURCE_LIMITS here as that reports the available
+        * resources (allocated to this PCIe function), which is zero until
+        * after we have allocated VIs.
+        */
+       encp->enc_evq_limit = 1024;
+       encp->enc_rxq_limit = EFX_RXQ_LIMIT_TARGET;
+       encp->enc_txq_limit = EFX_TXQ_LIMIT_TARGET;
+
+       encp->enc_buftbl_limit = 0xFFFFFFFF;
+
+       /* Get interrupt vector limits */
+       if ((rc = efx_mcdi_get_vector_cfg(enp, &base, &nvec, NULL)) != 0) {
+               if (EFX_PCI_FUNCTION_IS_PF(encp))
+                       goto fail9;
+
+               /* Ignore error (cannot query vector limits from a VF). */
+               base = 0;
+               nvec = 1024;
+       }
+       encp->enc_intr_vec_base = base;
+       encp->enc_intr_limit = nvec;
+
+       /*
+        * Get the current privilege mask. Note that this may be modified
+        * dynamically, so this value is informational only. DO NOT use
+        * the privilege mask to check for sufficient privileges, as that
+        * can result in time-of-check/time-of-use bugs.
+        */
+       if ((rc = ef10_get_privilege_mask(enp, &mask)) != 0)
+               goto fail10;
+       encp->enc_privilege_mask = mask;
+
+       /* Get remaining controller-specific board config */
+       if ((rc = enop->eno_board_cfg(enp)) != 0)
+               if (rc != EACCES)
+                       goto fail11;
+
+       return (0);
+
+fail11:
+       EFSYS_PROBE(fail11);
+fail10:
+       EFSYS_PROBE(fail10);
+fail9:
+       EFSYS_PROBE(fail9);
+fail8:
+       EFSYS_PROBE(fail8);
+fail7:
+       EFSYS_PROBE(fail7);
+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
 ef10_nic_probe(
        __in            efx_nic_t *enp)
 {
-       const efx_nic_ops_t *enop = enp->en_enop;
        efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
        efx_drv_cfg_t *edcp = &(enp->en_drv_cfg);
        efx_rc_t rc;
 
        EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
-                   enp->en_family == EFX_FAMILY_MEDFORD);
+           enp->en_family == EFX_FAMILY_MEDFORD ||
+           enp->en_family == EFX_FAMILY_MEDFORD2);
 
        /* Read and clear any assertion state */
        if ((rc = efx_mcdi_read_assertion(enp)) != 0)
@@ -1297,9 +1895,8 @@ ef10_nic_probe(
        if ((rc = efx_mcdi_drv_attach(enp, B_TRUE)) != 0)
                goto fail3;
 
-       if ((rc = enop->eno_board_cfg(enp)) != 0)
-               if (rc != EACCES)
-                       goto fail4;
+       if ((rc = ef10_nic_board_cfg(enp)) != 0)
+               goto fail4;
 
        /*
         * Set default driver config limits (based on board config).
@@ -1494,10 +2091,12 @@ ef10_nic_init(
        uint32_t i;
        uint32_t retry;
        uint32_t delay_us;
+       uint32_t vi_window_size;
        efx_rc_t rc;
 
        EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
-                   enp->en_family == EFX_FAMILY_MEDFORD);
+           enp->en_family == EFX_FAMILY_MEDFORD ||
+           enp->en_family == EFX_FAMILY_MEDFORD2);
 
        /* Enable reporting of some events (e.g. link change) */
        if ((rc = efx_mcdi_log_ctrl(enp)) != 0)
@@ -1555,15 +2154,21 @@ ef10_nic_init(
        enp->en_arch.ef10.ena_pio_write_vi_base =
            vi_count - enp->en_arch.ef10.ena_piobuf_count;
 
+       EFSYS_ASSERT3U(enp->en_nic_cfg.enc_vi_window_shift, !=,
+           EFX_VI_WINDOW_SHIFT_INVALID);
+       EFSYS_ASSERT3U(enp->en_nic_cfg.enc_vi_window_shift, <=,
+           EFX_VI_WINDOW_SHIFT_64K);
+       vi_window_size = 1U << enp->en_nic_cfg.enc_vi_window_shift;
+
        /* Save UC memory mapping details */
        enp->en_arch.ef10.ena_uc_mem_map_offset = 0;
        if (enp->en_arch.ef10.ena_piobuf_count > 0) {
                enp->en_arch.ef10.ena_uc_mem_map_size =
-                   (ER_DZ_TX_PIOBUF_STEP *
+                   (vi_window_size *
                    enp->en_arch.ef10.ena_pio_write_vi_base);
        } else {
                enp->en_arch.ef10.ena_uc_mem_map_size =
-                   (ER_DZ_TX_PIOBUF_STEP *
+                   (vi_window_size *
                    enp->en_arch.ef10.ena_vi_count);
        }
 
@@ -1573,7 +2178,7 @@ ef10_nic_init(
            enp->en_arch.ef10.ena_uc_mem_map_size;
 
        enp->en_arch.ef10.ena_wc_mem_map_size =
-           (ER_DZ_TX_PIOBUF_STEP *
+           (vi_window_size *
            enp->en_arch.ef10.ena_piobuf_count);
 
        /* Link piobufs to extra VIs in WC mapping */
@@ -1653,7 +2258,8 @@ ef10_nic_get_vi_pool(
        __out           uint32_t *vi_countp)
 {
        EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
-                   enp->en_family == EFX_FAMILY_MEDFORD);
+           enp->en_family == EFX_FAMILY_MEDFORD ||
+           enp->en_family == EFX_FAMILY_MEDFORD2);
 
        /*
         * Report VIs that the client driver can use.
@@ -1674,7 +2280,8 @@ ef10_nic_get_bar_region(
        efx_rc_t rc;
 
        EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
-                   enp->en_family == EFX_FAMILY_MEDFORD);
+           enp->en_family == EFX_FAMILY_MEDFORD ||
+           enp->en_family == EFX_FAMILY_MEDFORD2);
 
        /*
         * TODO: Specify host memory mapping alignment and granularity
@@ -1770,5 +2377,87 @@ fail1:
 
 #endif /* EFSYS_OPT_DIAG */
 
+#if EFSYS_OPT_FW_SUBVARIANT_AWARE
+
+       __checkReturn   efx_rc_t
+efx_mcdi_get_nic_global(
+       __in            efx_nic_t *enp,
+       __in            uint32_t key,
+       __out           uint32_t *valuep)
+{
+       efx_mcdi_req_t req;
+       uint8_t payload[MAX(MC_CMD_GET_NIC_GLOBAL_IN_LEN,
+                           MC_CMD_GET_NIC_GLOBAL_OUT_LEN)];
+       efx_rc_t rc;
+
+       (void) memset(payload, 0, sizeof (payload));
+       req.emr_cmd = MC_CMD_GET_NIC_GLOBAL;
+       req.emr_in_buf = payload;
+       req.emr_in_length = MC_CMD_GET_NIC_GLOBAL_IN_LEN;
+       req.emr_out_buf = payload;
+       req.emr_out_length = MC_CMD_GET_NIC_GLOBAL_OUT_LEN;
+
+       MCDI_IN_SET_DWORD(req, GET_NIC_GLOBAL_IN_KEY, key);
+
+       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_NIC_GLOBAL_OUT_LEN) {
+               rc = EMSGSIZE;
+               goto fail2;
+       }
+
+       *valuep = MCDI_OUT_DWORD(req, GET_NIC_GLOBAL_OUT_VALUE);
+
+       return (0);
+
+fail2:
+       EFSYS_PROBE(fail2);
+fail1:
+       EFSYS_PROBE1(fail1, efx_rc_t, rc);
+
+       return (rc);
+}
+
+       __checkReturn   efx_rc_t
+efx_mcdi_set_nic_global(
+       __in            efx_nic_t *enp,
+       __in            uint32_t key,
+       __in            uint32_t value)
+{
+       efx_mcdi_req_t req;
+       uint8_t payload[MC_CMD_SET_NIC_GLOBAL_IN_LEN];
+       efx_rc_t rc;
+
+       (void) memset(payload, 0, sizeof (payload));
+       req.emr_cmd = MC_CMD_SET_NIC_GLOBAL;
+       req.emr_in_buf = payload;
+       req.emr_in_length = MC_CMD_SET_NIC_GLOBAL_IN_LEN;
+       req.emr_out_buf = NULL;
+       req.emr_out_length = 0;
+
+       MCDI_IN_SET_DWORD(req, SET_NIC_GLOBAL_IN_KEY, key);
+       MCDI_IN_SET_DWORD(req, SET_NIC_GLOBAL_IN_VALUE, value);
+
+       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);
+}
+
+#endif /* EFSYS_OPT_FW_SUBVARIANT_AWARE */
 
-#endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */
+#endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */