net/ice/base: add AQC get link topology handle support
[dpdk.git] / drivers / net / ice / base / ice_common.c
index 43c0694..d2f9033 100644 (file)
@@ -187,6 +187,59 @@ ice_aq_get_phy_caps(struct ice_port_info *pi, bool qual_mods, u8 report_mode,
        return status;
 }
 
+/**
+ * ice_aq_get_link_topo_handle - get link topology node return status
+ * @pi: port information structure
+ * @node_type: requested node type
+ * @cd: pointer to command details structure or NULL
+ *
+ * Get link topology node return status for specified node type (0x06E0)
+ *
+ * Node type cage can be used to determine if cage is present. If AQC
+ * returns error (ENOENT), then no cage present. If no cage present, then
+ * connection type is backplane or BASE-T.
+ */
+static enum ice_status
+ice_aq_get_link_topo_handle(struct ice_port_info *pi, u8 node_type,
+                           struct ice_sq_cd *cd)
+{
+       struct ice_aqc_get_link_topo *cmd;
+       struct ice_aq_desc desc;
+
+       cmd = &desc.params.get_link_topo;
+
+       if (!cmd)
+               return ICE_ERR_PARAM;
+
+       ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_link_topo);
+
+       cmd->addr.node_type_ctx = (ICE_AQC_LINK_TOPO_NODE_CTX_PORT <<
+                                  ICE_AQC_LINK_TOPO_NODE_CTX_S);
+
+       /* set node type */
+       cmd->addr.node_type_ctx |= (ICE_AQC_LINK_TOPO_NODE_TYPE_M & node_type);
+
+       return ice_aq_send_cmd(pi->hw, &desc, NULL, 0, cd);
+}
+
+/**
+ * ice_is_media_cage_present
+ * @pi: port information structure
+ *
+ * Returns true if media cage is present, else false. If no cage, then
+ * media type is backplane or BASE-T.
+ */
+static bool ice_is_media_cage_present(struct ice_port_info *pi)
+{
+       /* Node type cage can be used to determine if cage is present. If AQC
+        * returns error (ENOENT), then no cage present. If no cage present then
+        * connection type is backplane or BASE-T.
+        */
+       return !ice_aq_get_link_topo_handle(pi,
+                                           ICE_AQC_LINK_TOPO_NODE_TYPE_CAGE,
+                                           NULL);
+}
+
 /**
  * ice_get_media_type - Gets media type
  * @pi: port information structure
@@ -212,7 +265,6 @@ static enum ice_media_type ice_get_media_type(struct ice_port_info *pi)
                case ICE_PHY_TYPE_LOW_10G_SFI_C2C:
                case ICE_PHY_TYPE_LOW_25GBASE_SR:
                case ICE_PHY_TYPE_LOW_25GBASE_LR:
-               case ICE_PHY_TYPE_LOW_25G_AUI_C2C:
                case ICE_PHY_TYPE_LOW_40GBASE_SR4:
                case ICE_PHY_TYPE_LOW_40GBASE_LR4:
                case ICE_PHY_TYPE_LOW_50GBASE_SR2:
@@ -243,6 +295,16 @@ static enum ice_media_type ice_get_media_type(struct ice_port_info *pi)
                case ICE_PHY_TYPE_LOW_100GBASE_CR_PAM4:
                case ICE_PHY_TYPE_LOW_100GBASE_CP2:
                        return ICE_MEDIA_DA;
+               case ICE_PHY_TYPE_LOW_25G_AUI_C2C:
+               case ICE_PHY_TYPE_LOW_40G_XLAUI:
+               case ICE_PHY_TYPE_LOW_50G_LAUI2:
+               case ICE_PHY_TYPE_LOW_50G_AUI2:
+               case ICE_PHY_TYPE_LOW_50G_AUI1:
+               case ICE_PHY_TYPE_LOW_100G_AUI4:
+               case ICE_PHY_TYPE_LOW_100G_CAUI4:
+                       if (ice_is_media_cage_present(pi))
+                               return ICE_MEDIA_DA;
+                       /* fall-through */
                case ICE_PHY_TYPE_LOW_1000BASE_KX:
                case ICE_PHY_TYPE_LOW_2500BASE_KX:
                case ICE_PHY_TYPE_LOW_2500BASE_X:
@@ -260,6 +322,10 @@ static enum ice_media_type ice_get_media_type(struct ice_port_info *pi)
                }
        } else {
                switch (hw_link_info->phy_type_high) {
+               case ICE_PHY_TYPE_HIGH_100G_AUI2:
+                       if (ice_is_media_cage_present(pi))
+                               return ICE_MEDIA_DA;
+                       /* fall-through */
                case ICE_PHY_TYPE_HIGH_100GBASE_KR2_PAM4:
                        return ICE_MEDIA_BACKPLANE;
                }
@@ -655,10 +721,10 @@ static void ice_cleanup_fltr_mgmt_struct(struct ice_hw *hw)
 
 
 /**
- * ice_get_itr_intrl_gran - determine int/intrl granularity
+ * ice_get_itr_intrl_gran
  * @hw: pointer to the HW struct
  *
- * Determines the itr/intrl granularities based on the maximum aggregate
+ * Determines the ITR/INTRL granularities based on the maximum aggregate
  * bandwidth according to the device's configuration during power-on.
  */
 static void ice_get_itr_intrl_gran(struct ice_hw *hw)
@@ -759,10 +825,13 @@ enum ice_status ice_init_hw(struct ice_hw *hw)
        if (status)
                goto err_unroll_cqinit;
 
+       status = ice_init_nvm(hw);
+       if (status)
+               goto err_unroll_cqinit;
+
        if (ice_get_fw_mode(hw) == ICE_FW_MODE_ROLLBACK)
                ice_print_rollback_msg(hw);
 
-
        status = ice_clear_pf_cfg(hw);
        if (status)
                goto err_unroll_cqinit;
@@ -773,9 +842,6 @@ enum ice_status ice_init_hw(struct ice_hw *hw)
 
        ice_clear_pxe_mode(hw);
 
-       status = ice_init_nvm(hw);
-       if (status)
-               goto err_unroll_cqinit;
 
        status = ice_get_caps(hw);
        if (status)
@@ -1955,7 +2021,7 @@ ice_parse_caps(struct ice_hw *hw, void *buf, u32 cap_count,
                case ICE_AQC_CAPS_VALID_FUNCTIONS:
                        caps->valid_functions = number;
                        ice_debug(hw, ICE_DBG_INIT,
-                                 "%s: valid functions = %d\n", prefix,
+                                 "%s: valid_functions (bitmap) = %d\n", prefix,
                                  caps->valid_functions);
 
                        /* store func count for resource management purposes */
@@ -1966,17 +2032,17 @@ ice_parse_caps(struct ice_hw *hw, void *buf, u32 cap_count,
                        if (dev_p) {
                                dev_p->num_vsi_allocd_to_host = number;
                                ice_debug(hw, ICE_DBG_INIT,
-                                         "%s: num VSI alloc to host = %d\n",
+                                         "%s: num_vsi_allocd_to_host = %d\n",
                                          prefix,
                                          dev_p->num_vsi_allocd_to_host);
                        } else if (func_p) {
                                func_p->guar_num_vsi =
                                        ice_get_num_per_func(hw, ICE_MAX_VSI);
                                ice_debug(hw, ICE_DBG_INIT,
-                                         "%s: num guaranteed VSI (fw) = %d\n",
+                                         "%s: guar_num_vsi (fw) = %d\n",
                                          prefix, number);
                                ice_debug(hw, ICE_DBG_INIT,
-                                         "%s: num guaranteed VSI = %d\n",
+                                         "%s: guar_num_vsi = %d\n",
                                          prefix, func_p->guar_num_vsi);
                        }
                        break;
@@ -1985,51 +2051,51 @@ ice_parse_caps(struct ice_hw *hw, void *buf, u32 cap_count,
                        caps->active_tc_bitmap = logical_id;
                        caps->maxtc = phys_id;
                        ice_debug(hw, ICE_DBG_INIT,
-                                 "%s: DCB = %d\n", prefix, caps->dcb);
+                                 "%s: dcb = %d\n", prefix, caps->dcb);
                        ice_debug(hw, ICE_DBG_INIT,
-                                 "%s: active TC bitmap = %d\n", prefix,
+                                 "%s: active_tc_bitmap = %d\n", prefix,
                                  caps->active_tc_bitmap);
                        ice_debug(hw, ICE_DBG_INIT,
-                                 "%s: TC max = %d\n", prefix, caps->maxtc);
+                                 "%s: maxtc = %d\n", prefix, caps->maxtc);
                        break;
                case ICE_AQC_CAPS_RSS:
                        caps->rss_table_size = number;
                        caps->rss_table_entry_width = logical_id;
                        ice_debug(hw, ICE_DBG_INIT,
-                                 "%s: RSS table size = %d\n", prefix,
+                                 "%s: rss_table_size = %d\n", prefix,
                                  caps->rss_table_size);
                        ice_debug(hw, ICE_DBG_INIT,
-                                 "%s: RSS table width = %d\n", prefix,
+                                 "%s: rss_table_entry_width = %d\n", prefix,
                                  caps->rss_table_entry_width);
                        break;
                case ICE_AQC_CAPS_RXQS:
                        caps->num_rxq = number;
                        caps->rxq_first_id = phys_id;
                        ice_debug(hw, ICE_DBG_INIT,
-                                 "%s: num Rx queues = %d\n", prefix,
+                                 "%s: num_rxq = %d\n", prefix,
                                  caps->num_rxq);
                        ice_debug(hw, ICE_DBG_INIT,
-                                 "%s: Rx first queue ID = %d\n", prefix,
+                                 "%s: rxq_first_id = %d\n", prefix,
                                  caps->rxq_first_id);
                        break;
                case ICE_AQC_CAPS_TXQS:
                        caps->num_txq = number;
                        caps->txq_first_id = phys_id;
                        ice_debug(hw, ICE_DBG_INIT,
-                                 "%s: num Tx queues = %d\n", prefix,
+                                 "%s: num_txq = %d\n", prefix,
                                  caps->num_txq);
                        ice_debug(hw, ICE_DBG_INIT,
-                                 "%s: Tx first queue ID = %d\n", prefix,
+                                 "%s: txq_first_id = %d\n", prefix,
                                  caps->txq_first_id);
                        break;
                case ICE_AQC_CAPS_MSIX:
                        caps->num_msix_vectors = number;
                        caps->msix_vector_first_id = phys_id;
                        ice_debug(hw, ICE_DBG_INIT,
-                                 "%s: MSIX vector count = %d\n", prefix,
+                                 "%s: num_msix_vectors = %d\n", prefix,
                                  caps->num_msix_vectors);
                        ice_debug(hw, ICE_DBG_INIT,
-                                 "%s: MSIX first vector index = %d\n", prefix,
+                                 "%s: msix_vector_first_id = %d\n", prefix,
                                  caps->msix_vector_first_id);
                        break;
                case ICE_AQC_CAPS_FD:
@@ -2039,7 +2105,8 @@ ice_parse_caps(struct ice_hw *hw, void *buf, u32 cap_count,
                        if (dev_p) {
                                dev_p->num_flow_director_fltr = number;
                                ice_debug(hw, ICE_DBG_INIT,
-                                         "%s: num FD filters = %d\n", prefix,
+                                         "%s: num_flow_director_fltr = %d\n",
+                                         prefix,
                                          dev_p->num_flow_director_fltr);
                        }
                        if (func_p) {
@@ -2052,17 +2119,17 @@ ice_parse_caps(struct ice_hw *hw, void *buf, u32 cap_count,
                                      GLQF_FD_SIZE_FD_BSIZE_S;
                                func_p->fd_fltr_best_effort = val;
                                ice_debug(hw, ICE_DBG_INIT,
-                                         "%s: num guaranteed FD filters = %d\n",
+                                         "%s: fd_fltr_guar = %d\n",
                                          prefix, func_p->fd_fltr_guar);
                                ice_debug(hw, ICE_DBG_INIT,
-                                         "%s: num best effort FD filters = %d\n",
+                                         "%s: fd_fltr_best_effort = %d\n",
                                          prefix, func_p->fd_fltr_best_effort);
                        }
                        break;
                }
                case ICE_AQC_CAPS_MAX_MTU:
                        caps->max_mtu = number;
-                       ice_debug(hw, ICE_DBG_INIT, "%s: max MTU = %d\n",
+                       ice_debug(hw, ICE_DBG_INIT, "%s: max_mtu = %d\n",
                                  prefix, caps->max_mtu);
                        break;
                default:
@@ -2081,7 +2148,7 @@ ice_parse_caps(struct ice_hw *hw, void *buf, u32 cap_count,
                /* Max 4 TCs per port */
                caps->maxtc = 4;
                ice_debug(hw, ICE_DBG_INIT,
-                         "%s: TC max = %d (based on #ports)\n", prefix,
+                         "%s: maxtc = %d (based on #ports)\n", prefix,
                          caps->maxtc);
        }
 }
@@ -2170,6 +2237,70 @@ ice_discover_caps(struct ice_hw *hw, enum ice_adminq_opc opc)
        return status;
 }
 
+/**
+ * ice_set_safe_mode_caps - Override dev/func capabilities when in safe mode
+ * @hw: pointer to the hardware structure
+ */
+void ice_set_safe_mode_caps(struct ice_hw *hw)
+{
+       struct ice_hw_func_caps *func_caps = &hw->func_caps;
+       struct ice_hw_dev_caps *dev_caps = &hw->dev_caps;
+       u32 valid_func, rxq_first_id, txq_first_id;
+       u32 msix_vector_first_id, max_mtu;
+       u32 num_funcs;
+
+       /* cache some func_caps values that should be restored after memset */
+       valid_func = func_caps->common_cap.valid_functions;
+       txq_first_id = func_caps->common_cap.txq_first_id;
+       rxq_first_id = func_caps->common_cap.rxq_first_id;
+       msix_vector_first_id = func_caps->common_cap.msix_vector_first_id;
+       max_mtu = func_caps->common_cap.max_mtu;
+
+       /* unset func capabilities */
+       memset(func_caps, 0, sizeof(*func_caps));
+
+       /* restore cached values */
+       func_caps->common_cap.valid_functions = valid_func;
+       func_caps->common_cap.txq_first_id = txq_first_id;
+       func_caps->common_cap.rxq_first_id = rxq_first_id;
+       func_caps->common_cap.msix_vector_first_id = msix_vector_first_id;
+       func_caps->common_cap.max_mtu = max_mtu;
+
+       /* one Tx and one Rx queue in safe mode */
+       func_caps->common_cap.num_rxq = 1;
+       func_caps->common_cap.num_txq = 1;
+
+       /* two MSIX vectors, one for traffic and one for misc causes */
+       func_caps->common_cap.num_msix_vectors = 2;
+       func_caps->guar_num_vsi = 1;
+
+       /* cache some dev_caps values that should be restored after memset */
+       valid_func = dev_caps->common_cap.valid_functions;
+       txq_first_id = dev_caps->common_cap.txq_first_id;
+       rxq_first_id = dev_caps->common_cap.rxq_first_id;
+       msix_vector_first_id = dev_caps->common_cap.msix_vector_first_id;
+       max_mtu = dev_caps->common_cap.max_mtu;
+       num_funcs = dev_caps->num_funcs;
+
+       /* unset dev capabilities */
+       memset(dev_caps, 0, sizeof(*dev_caps));
+
+       /* restore cached values */
+       dev_caps->common_cap.valid_functions = valid_func;
+       dev_caps->common_cap.txq_first_id = txq_first_id;
+       dev_caps->common_cap.rxq_first_id = rxq_first_id;
+       dev_caps->common_cap.msix_vector_first_id = msix_vector_first_id;
+       dev_caps->common_cap.max_mtu = max_mtu;
+       dev_caps->num_funcs = num_funcs;
+
+       /* one Tx and one Rx queue per function in safe mode */
+       dev_caps->common_cap.num_rxq = num_funcs;
+       dev_caps->common_cap.num_txq = num_funcs;
+
+       /* two MSIX vectors per function */
+       dev_caps->common_cap.num_msix_vectors = 2 * num_funcs;
+}
+
 /**
  * ice_get_caps - get info about the HW
  * @hw: pointer to the hardware structure
@@ -2547,6 +2678,53 @@ ice_cache_phy_user_req(struct ice_port_info *pi,
        }
 }
 
+/**
+ * ice_caps_to_fc_mode
+ * @caps: PHY capabilities
+ *
+ * Convert PHY FC capabilities to ice FC mode
+ */
+enum ice_fc_mode ice_caps_to_fc_mode(u8 caps)
+{
+       if (caps & ICE_AQC_PHY_EN_TX_LINK_PAUSE &&
+           caps & ICE_AQC_PHY_EN_RX_LINK_PAUSE)
+               return ICE_FC_FULL;
+
+       if (caps & ICE_AQC_PHY_EN_TX_LINK_PAUSE)
+               return ICE_FC_TX_PAUSE;
+
+       if (caps & ICE_AQC_PHY_EN_RX_LINK_PAUSE)
+               return ICE_FC_RX_PAUSE;
+
+       return ICE_FC_NONE;
+}
+
+/**
+ * ice_caps_to_fec_mode
+ * @caps: PHY capabilities
+ * @fec_options: Link FEC options
+ *
+ * Convert PHY FEC capabilities to ice FEC mode
+ */
+enum ice_fec_mode ice_caps_to_fec_mode(u8 caps, u8 fec_options)
+{
+       if (caps & ICE_AQC_PHY_EN_AUTO_FEC)
+               return ICE_FEC_AUTO;
+
+       if (fec_options & (ICE_AQC_PHY_FEC_10G_KR_40G_KR4_EN |
+                          ICE_AQC_PHY_FEC_10G_KR_40G_KR4_REQ |
+                          ICE_AQC_PHY_FEC_25G_KR_CLAUSE74_EN |
+                          ICE_AQC_PHY_FEC_25G_KR_REQ))
+               return ICE_FEC_BASER;
+
+       if (fec_options & (ICE_AQC_PHY_FEC_25G_RS_528_REQ |
+                          ICE_AQC_PHY_FEC_25G_RS_544_REQ |
+                          ICE_AQC_PHY_FEC_25G_RS_CLAUSE91_EN))
+               return ICE_FEC_RS;
+
+       return ICE_FEC_NONE;
+}
+
 /**
  * ice_set_fc
  * @pi: port information structure
@@ -2653,6 +2831,42 @@ out:
        return status;
 }
 
+/**
+ * ice_phy_caps_equals_cfg
+ * @phy_caps: PHY capabilities
+ * @phy_cfg: PHY configuration
+ *
+ * Helper function to determine if PHY capabilities matches PHY
+ * configuration
+ */
+bool
+ice_phy_caps_equals_cfg(struct ice_aqc_get_phy_caps_data *phy_caps,
+                       struct ice_aqc_set_phy_cfg_data *phy_cfg)
+{
+       u8 caps_mask, cfg_mask;
+
+       if (!phy_caps || !phy_cfg)
+               return false;
+
+       /* These bits are not common between capabilities and configuration.
+        * Do not use them to determine equality.
+        */
+       caps_mask = ICE_AQC_PHY_CAPS_MASK & ~(ICE_AQC_PHY_AN_MODE |
+                                             ICE_AQC_PHY_EN_MOD_QUAL);
+       cfg_mask = ICE_AQ_PHY_ENA_VALID_MASK & ~ICE_AQ_PHY_ENA_AUTO_LINK_UPDT;
+
+       if (phy_caps->phy_type_low != phy_cfg->phy_type_low ||
+           phy_caps->phy_type_high != phy_cfg->phy_type_high ||
+           ((phy_caps->caps & caps_mask) != (phy_cfg->caps & cfg_mask)) ||
+           phy_caps->low_power_ctrl != phy_cfg->low_power_ctrl ||
+           phy_caps->eee_cap != phy_cfg->eee_cap ||
+           phy_caps->eeer_value != phy_cfg->eeer_value ||
+           phy_caps->link_fec_options != phy_cfg->link_fec_opt)
+               return false;
+
+       return true;
+}
+
 /**
  * ice_copy_phy_caps_to_cfg - Copy PHY ability data to configuration data
  * @caps: PHY ability structure to copy date from
@@ -2859,6 +3073,52 @@ ice_aq_set_port_id_led(struct ice_port_info *pi, bool is_orig_mode,
        return ice_aq_send_cmd(hw, &desc, NULL, 0, cd);
 }
 
+/**
+ * ice_aq_sff_eeprom
+ * @hw: pointer to the HW struct
+ * @lport: bits [7:0] = logical port, bit [8] = logical port valid
+ * @bus_addr: I2C bus address of the eeprom (typically 0xA0, 0=topo default)
+ * @mem_addr: I2C offset. lower 8 bits for address, 8 upper bits zero padding.
+ * @page: QSFP page
+ * @set_page: set or ignore the page
+ * @data: pointer to data buffer to be read/written to the I2C device.
+ * @length: 1-16 for read, 1 for write.
+ * @write: 0 read, 1 for write.
+ * @cd: pointer to command details structure or NULL
+ *
+ * Read/Write SFF EEPROM (0x06EE)
+ */
+enum ice_status
+ice_aq_sff_eeprom(struct ice_hw *hw, u16 lport, u8 bus_addr,
+                 u16 mem_addr, u8 page, u8 set_page, u8 *data, u8 length,
+                 bool write, struct ice_sq_cd *cd)
+{
+       struct ice_aqc_sff_eeprom *cmd;
+       struct ice_aq_desc desc;
+       enum ice_status status;
+
+       if (!data || (mem_addr & 0xff00))
+               return ICE_ERR_PARAM;
+
+       ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_sff_eeprom);
+       cmd = &desc.params.read_write_sff_param;
+       desc.flags = CPU_TO_LE16(ICE_AQ_FLAG_RD | ICE_AQ_FLAG_BUF);
+       cmd->lport_num = (u8)(lport & 0xff);
+       cmd->lport_num_valid = (u8)((lport >> 8) & 0x01);
+       cmd->i2c_bus_addr = CPU_TO_LE16(((bus_addr >> 1) &
+                                        ICE_AQC_SFF_I2CBUS_7BIT_M) |
+                                       ((set_page <<
+                                         ICE_AQC_SFF_SET_EEPROM_PAGE_S) &
+                                        ICE_AQC_SFF_SET_EEPROM_PAGE_M));
+       cmd->i2c_mem_addr = CPU_TO_LE16(mem_addr & 0xff);
+       cmd->eeprom_page = CPU_TO_LE16((u16)page << ICE_AQC_SFF_EEPROM_PAGE_S);
+       if (write)
+               cmd->i2c_bus_addr |= CPU_TO_LE16(ICE_AQC_SFF_IS_WRITE);
+
+       status = ice_aq_send_cmd(hw, &desc, data, length, cd);
+       return status;
+}
+
 /**
  * __ice_aq_get_set_rss_lut
  * @hw: pointer to the hardware structure
@@ -3221,6 +3481,77 @@ do_aq:
        return status;
 }
 
+/**
+ * ice_aq_move_recfg_lan_txq
+ * @hw: pointer to the hardware structure
+ * @num_qs: number of queues to move/reconfigure
+ * @is_move: true if this operation involves node movement
+ * @is_tc_change: true if this operation involves a TC change
+ * @subseq_call: true if this operation is a subsequent call
+ * @flush_pipe: on timeout, true to flush pipe, false to return EAGAIN
+ * @timeout: timeout in units of 100 usec (valid values 0-50)
+ * @blocked_cgds: out param, bitmap of CGDs that timed out if returning EAGAIN
+ * @buf: struct containing src/dest TEID and per-queue info
+ * @buf_size: size of buffer for indirect command
+ * @txqs_moved: out param, number of queues successfully moved
+ * @cd: pointer to command details structure or NULL
+ *
+ * Move / Reconfigure Tx LAN queues (0x0C32)
+ */
+enum ice_status
+ice_aq_move_recfg_lan_txq(struct ice_hw *hw, u8 num_qs, bool is_move,
+                         bool is_tc_change, bool subseq_call, bool flush_pipe,
+                         u8 timeout, u32 *blocked_cgds,
+                         struct ice_aqc_move_txqs_data *buf, u16 buf_size,
+                         u8 *txqs_moved, struct ice_sq_cd *cd)
+{
+       struct ice_aqc_move_txqs *cmd;
+       struct ice_aq_desc desc;
+       enum ice_status status;
+
+       cmd = &desc.params.move_txqs;
+       ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_move_recfg_txqs);
+
+#define ICE_LAN_TXQ_MOVE_TIMEOUT_MAX 50
+       if (timeout > ICE_LAN_TXQ_MOVE_TIMEOUT_MAX)
+               return ICE_ERR_PARAM;
+
+       if (is_tc_change && !flush_pipe && !blocked_cgds)
+               return ICE_ERR_PARAM;
+
+       if (!is_move && !is_tc_change)
+               return ICE_ERR_PARAM;
+
+       desc.flags |= CPU_TO_LE16(ICE_AQ_FLAG_RD);
+
+       if (is_move)
+               cmd->cmd_type |= ICE_AQC_Q_CMD_TYPE_MOVE;
+
+       if (is_tc_change)
+               cmd->cmd_type |= ICE_AQC_Q_CMD_TYPE_TC_CHANGE;
+
+       if (subseq_call)
+               cmd->cmd_type |= ICE_AQC_Q_CMD_SUBSEQ_CALL;
+
+       if (flush_pipe)
+               cmd->cmd_type |= ICE_AQC_Q_CMD_FLUSH_PIPE;
+
+       cmd->num_qs = num_qs;
+       cmd->timeout = ((timeout << ICE_AQC_Q_CMD_TIMEOUT_S) &
+                       ICE_AQC_Q_CMD_TIMEOUT_M);
+
+       status = ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
+
+       if (!status && txqs_moved)
+               *txqs_moved = cmd->num_qs;
+
+       if (hw->adminq.sq_last_status == ICE_AQ_RC_EAGAIN &&
+           is_tc_change && !flush_pipe)
+               *blocked_cgds = LE32_TO_CPU(cmd->blocked_cgds);
+
+       return status;
+}
+
 
 /* End of FW Admin Queue command wrappers */
 
@@ -4059,6 +4390,57 @@ ice_stat_update32(struct ice_hw *hw, u32 reg, bool prev_stat_loaded,
        *prev_stat = new_data;
 }
 
+/**
+ * ice_stat_update_repc - read GLV_REPC stats from chip and update stat values
+ * @hw: ptr to the hardware info
+ * @vsi_handle: VSI handle
+ * @prev_stat_loaded: bool to specify if the previous stat values are loaded
+ * @cur_stats: ptr to current stats structure
+ *
+ * The GLV_REPC statistic register actually tracks two 16bit statistics, and
+ * thus cannot be read using the normal ice_stat_update32 function.
+ *
+ * Read the GLV_REPC register associated with the given VSI, and update the
+ * rx_no_desc and rx_error values in the ice_eth_stats structure.
+ *
+ * Because the statistics in GLV_REPC stick at 0xFFFF, the register must be
+ * cleared each time it's read.
+ *
+ * Note that the GLV_RDPC register also counts the causes that would trigger
+ * GLV_REPC. However, it does not give the finer grained detail about why the
+ * packets are being dropped. The GLV_REPC values can be used to distinguish
+ * whether Rx packets are dropped due to errors or due to no available
+ * descriptors.
+ */
+void
+ice_stat_update_repc(struct ice_hw *hw, u16 vsi_handle, bool prev_stat_loaded,
+                    struct ice_eth_stats *cur_stats)
+{
+       u16 vsi_num, no_desc, error_cnt;
+       u32 repc;
+
+       if (!ice_is_vsi_valid(hw, vsi_handle))
+               return;
+
+       vsi_num = ice_get_hw_vsi_num(hw, vsi_handle);
+
+       /* If we haven't loaded stats yet, just clear the current value */
+       if (!prev_stat_loaded) {
+               wr32(hw, GLV_REPC(vsi_num), 0);
+               return;
+       }
+
+       repc = rd32(hw, GLV_REPC(vsi_num));
+       no_desc = (repc & GLV_REPC_NO_DESC_CNT_M) >> GLV_REPC_NO_DESC_CNT_S;
+       error_cnt = (repc & GLV_REPC_ERROR_CNT_M) >> GLV_REPC_ERROR_CNT_S;
+
+       /* Clear the count by writing to the stats register */
+       wr32(hw, GLV_REPC(vsi_num), 0);
+
+       cur_stats->rx_no_desc += no_desc;
+       cur_stats->rx_errors += error_cnt;
+}
+
 
 /**
  * ice_sched_query_elem - query element information from HW