+/**
+ * i40e_get_phy_lpi_status - read LPI status from PHY or MAC register
+ * @hw: pointer to the hw struct
+ * @stat: pointer to structure with status of rx and tx lpi
+ *
+ * Read LPI state directly from external PHY register or from MAC
+ * register, depending on device ID and current link speed.
+ */
+enum i40e_status_code i40e_get_phy_lpi_status(struct i40e_hw *hw,
+ struct i40e_hw_port_stats *stat)
+{
+ enum i40e_status_code ret = I40E_SUCCESS;
+ bool eee_mrvl_phy;
+ bool eee_bcm_phy;
+ u32 val;
+
+ stat->rx_lpi_status = 0;
+ stat->tx_lpi_status = 0;
+
+ eee_bcm_phy =
+ (hw->device_id == I40E_DEV_ID_10G_BASE_T_BC ||
+ hw->device_id == I40E_DEV_ID_5G_BASE_T_BC) &&
+ (hw->phy.link_info.link_speed == I40E_LINK_SPEED_2_5GB ||
+ hw->phy.link_info.link_speed == I40E_LINK_SPEED_5GB);
+ eee_mrvl_phy =
+ hw->device_id == I40E_DEV_ID_1G_BASE_T_X722;
+
+ if (eee_bcm_phy || eee_mrvl_phy) {
+ /* read Clause 45 PCS Status 1 register */
+ ret = i40e_aq_get_phy_register(hw,
+ I40E_AQ_PHY_REG_ACCESS_EXTERNAL,
+ I40E_BCM_PHY_PCS_STATUS1_PAGE,
+ true,
+ I40E_BCM_PHY_PCS_STATUS1_REG,
+ &val, NULL);
+
+ if (ret != I40E_SUCCESS)
+ return ret;
+
+ stat->rx_lpi_status = !!(val & I40E_BCM_PHY_PCS_STATUS1_RX_LPI);
+ stat->tx_lpi_status = !!(val & I40E_BCM_PHY_PCS_STATUS1_TX_LPI);
+
+ return ret;
+ }
+
+ val = rd32(hw, I40E_PRTPM_EEE_STAT);
+ stat->rx_lpi_status = (val & I40E_PRTPM_EEE_STAT_RX_LPI_STATUS_MASK) >>
+ I40E_PRTPM_EEE_STAT_RX_LPI_STATUS_SHIFT;
+ stat->tx_lpi_status = (val & I40E_PRTPM_EEE_STAT_TX_LPI_STATUS_MASK) >>
+ I40E_PRTPM_EEE_STAT_TX_LPI_STATUS_SHIFT;
+
+ return ret;
+}
+
+/**
+ * i40e_get_lpi_counters - read LPI counters from EEE statistics
+ * @hw: pointer to the hw struct
+ * @tx_counter: pointer to memory for TX LPI counter
+ * @rx_counter: pointer to memory for RX LPI counter
+ * @is_clear: returns true if counters are clear after read
+ *
+ * Read Low Power Idle (LPI) mode counters from Energy Efficient
+ * Ethernet (EEE) statistics.
+ **/
+enum i40e_status_code i40e_get_lpi_counters(struct i40e_hw *hw,
+ u32 *tx_counter, u32 *rx_counter,
+ bool *is_clear)
+{
+ /* only X710-T*L requires special handling of counters
+ * for other devices we just read the MAC registers
+ */
+ if ((hw->device_id == I40E_DEV_ID_10G_BASE_T_BC ||
+ hw->device_id == I40E_DEV_ID_5G_BASE_T_BC) &&
+ hw->phy.link_info.link_speed != I40E_LINK_SPEED_1GB) {
+ enum i40e_status_code retval;
+ u32 cmd_status = 0;
+
+ *is_clear = false;
+ retval = i40e_aq_run_phy_activity(hw,
+ I40E_AQ_RUN_PHY_ACT_ID_USR_DFND,
+ I40E_AQ_RUN_PHY_ACT_DNL_OPCODE_GET_EEE_STAT,
+ &cmd_status, tx_counter, rx_counter, NULL);
+
+ if (!retval && cmd_status != I40E_AQ_RUN_PHY_ACT_CMD_STAT_SUCC)
+ retval = I40E_ERR_ADMIN_QUEUE_ERROR;
+
+ return retval;
+ }
+
+ *is_clear = true;
+ *tx_counter = rd32(hw, I40E_PRTPM_TLPIC);
+ *rx_counter = rd32(hw, I40E_PRTPM_RLPIC);
+
+ return I40E_SUCCESS;
+}
+
+/**
+ * i40e_get_lpi_duration - read LPI time duration from EEE statistics
+ * @hw: pointer to the hw struct
+ * @stat: pointer to structure with status of rx and tx lpi
+ * @tx_duration: pointer to memory for TX LPI time duration
+ * @rx_duration: pointer to memory for RX LPI time duration
+ *
+ * Read Low Power Idle (LPI) mode time duration from Energy Efficient
+ * Ethernet (EEE) statistics.
+ */
+enum i40e_status_code i40e_get_lpi_duration(struct i40e_hw *hw,
+ struct i40e_hw_port_stats *stat,
+ u64 *tx_duration, u64 *rx_duration)
+{
+ u32 tx_time_dur, rx_time_dur;
+ enum i40e_status_code retval;
+ u32 cmd_status;
+
+ if (hw->device_id != I40E_DEV_ID_10G_BASE_T_BC &&
+ hw->device_id != I40E_DEV_ID_5G_BASE_T_BC)
+ return I40E_ERR_NOT_IMPLEMENTED;
+
+ retval = i40e_aq_run_phy_activity
+ (hw, I40E_AQ_RUN_PHY_ACT_ID_USR_DFND,
+ I40E_AQ_RUN_PHY_ACT_DNL_OPCODE_GET_EEE_DUR,
+ &cmd_status, &tx_time_dur, &rx_time_dur, NULL);
+
+ if (retval)
+ return retval;
+ if ((cmd_status & I40E_AQ_RUN_PHY_ACT_CMD_STAT_MASK) !=
+ I40E_AQ_RUN_PHY_ACT_CMD_STAT_SUCC)
+ return I40E_ERR_ADMIN_QUEUE_ERROR;
+
+ if (hw->phy.link_info.link_speed == I40E_LINK_SPEED_1GB &&
+ !tx_time_dur && !rx_time_dur &&
+ stat->tx_lpi_status && stat->rx_lpi_status) {
+ retval = i40e_aq_run_phy_activity
+ (hw, I40E_AQ_RUN_PHY_ACT_ID_USR_DFND,
+ I40E_AQ_RUN_PHY_ACT_DNL_OPCODE_GET_EEE_STAT_DUR,
+ &cmd_status,
+ &tx_time_dur, &rx_time_dur, NULL);
+
+ if (retval)
+ return retval;
+ if ((cmd_status & I40E_AQ_RUN_PHY_ACT_CMD_STAT_MASK) !=
+ I40E_AQ_RUN_PHY_ACT_CMD_STAT_SUCC)
+ return I40E_ERR_ADMIN_QUEUE_ERROR;
+ tx_time_dur = 0;
+ rx_time_dur = 0;
+ }
+
+ *tx_duration = tx_time_dur;
+ *rx_duration = rx_time_dur;
+
+ return retval;
+}
+
+/**
+ * i40e_lpi_stat_update - update LPI counters with values relative to offset
+ * @hw: pointer to the hw struct
+ * @offset_loaded: flag indicating need of writing current value to offset
+ * @tx_offset: pointer to offset of TX LPI counter
+ * @tx_stat: pointer to value of TX LPI counter
+ * @rx_offset: pointer to offset of RX LPI counter
+ * @rx_stat: pointer to value of RX LPI counter
+ *
+ * Update Low Power Idle (LPI) mode counters while having regard to passed
+ * offsets.
+ **/
+enum i40e_status_code i40e_lpi_stat_update(struct i40e_hw *hw,
+ bool offset_loaded, u64 *tx_offset,
+ u64 *tx_stat, u64 *rx_offset,
+ u64 *rx_stat)
+{
+ enum i40e_status_code retval;
+ u32 tx_counter, rx_counter;
+ bool is_clear;
+
+ retval = i40e_get_lpi_counters(hw, &tx_counter, &rx_counter, &is_clear);
+ if (retval)
+ goto err;
+
+ if (is_clear) {
+ *tx_stat += tx_counter;
+ *rx_stat += rx_counter;
+ } else {
+ if (!offset_loaded) {
+ *tx_offset = tx_counter;
+ *rx_offset = rx_counter;
+ }
+
+ *tx_stat = (tx_counter >= *tx_offset) ?
+ (u32)(tx_counter - *tx_offset) :
+ (u32)((tx_counter + BIT_ULL(32)) - *tx_offset);
+ *rx_stat = (rx_counter >= *rx_offset) ?
+ (u32)(rx_counter - *rx_offset) :
+ (u32)((rx_counter + BIT_ULL(32)) - *rx_offset);
+ }
+err:
+ return retval;
+}
+
+/**
+ * i40e_aq_rx_ctl_read_register - use FW to read from an Rx control register
+ * @hw: pointer to the hw struct
+ * @reg_addr: register address
+ * @reg_val: ptr to register value
+ * @cmd_details: pointer to command details structure or NULL
+ *
+ * Use the firmware to read the Rx control register,
+ * especially useful if the Rx unit is under heavy pressure
+ **/
+enum i40e_status_code i40e_aq_rx_ctl_read_register(struct i40e_hw *hw,
+ u32 reg_addr, u32 *reg_val,
+ struct i40e_asq_cmd_details *cmd_details)
+{
+ struct i40e_aq_desc desc;
+ struct i40e_aqc_rx_ctl_reg_read_write *cmd_resp =
+ (struct i40e_aqc_rx_ctl_reg_read_write *)&desc.params.raw;
+ enum i40e_status_code status;
+
+ if (reg_val == NULL)
+ return I40E_ERR_PARAM;
+
+ i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_rx_ctl_reg_read);
+
+ cmd_resp->address = CPU_TO_LE32(reg_addr);
+
+ status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details);
+
+ if (status == I40E_SUCCESS)
+ *reg_val = LE32_TO_CPU(cmd_resp->value);
+
+ return status;
+}
+
+/**
+ * i40e_read_rx_ctl - read from an Rx control register
+ * @hw: pointer to the hw struct
+ * @reg_addr: register address
+ **/
+u32 i40e_read_rx_ctl(struct i40e_hw *hw, u32 reg_addr)
+{
+ enum i40e_status_code status = I40E_SUCCESS;
+ bool use_register;
+ int retry = 5;
+ u32 val = 0;
+
+ use_register = (((hw->aq.api_maj_ver == 1) &&
+ (hw->aq.api_min_ver < 5)) ||
+ (hw->mac.type == I40E_MAC_X722));
+ if (!use_register) {
+do_retry:
+ status = i40e_aq_rx_ctl_read_register(hw, reg_addr, &val, NULL);
+ if (hw->aq.asq_last_status == I40E_AQ_RC_EAGAIN && retry) {
+ i40e_msec_delay(1);
+ retry--;
+ goto do_retry;
+ }
+ }
+
+ /* if the AQ access failed, try the old-fashioned way */
+ if (status || use_register)
+ val = rd32(hw, reg_addr);
+
+ return val;
+}
+
+/**
+ * i40e_aq_rx_ctl_write_register
+ * @hw: pointer to the hw struct
+ * @reg_addr: register address
+ * @reg_val: register value
+ * @cmd_details: pointer to command details structure or NULL
+ *
+ * Use the firmware to write to an Rx control register,
+ * especially useful if the Rx unit is under heavy pressure
+ **/
+enum i40e_status_code i40e_aq_rx_ctl_write_register(struct i40e_hw *hw,
+ u32 reg_addr, u32 reg_val,
+ struct i40e_asq_cmd_details *cmd_details)
+{
+ struct i40e_aq_desc desc;
+ struct i40e_aqc_rx_ctl_reg_read_write *cmd =
+ (struct i40e_aqc_rx_ctl_reg_read_write *)&desc.params.raw;
+ enum i40e_status_code status;
+
+ i40e_fill_default_direct_cmd_desc(&desc, i40e_aqc_opc_rx_ctl_reg_write);
+
+ cmd->address = CPU_TO_LE32(reg_addr);
+ cmd->value = CPU_TO_LE32(reg_val);
+
+ status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details);
+
+ return status;
+}
+
+/**
+ * i40e_write_rx_ctl - write to an Rx control register
+ * @hw: pointer to the hw struct
+ * @reg_addr: register address
+ * @reg_val: register value
+ **/
+void i40e_write_rx_ctl(struct i40e_hw *hw, u32 reg_addr, u32 reg_val)
+{
+ enum i40e_status_code status = I40E_SUCCESS;
+ bool use_register;
+ int retry = 5;
+
+ use_register = (((hw->aq.api_maj_ver == 1) &&
+ (hw->aq.api_min_ver < 5)) ||
+ (hw->mac.type == I40E_MAC_X722));
+ if (!use_register) {
+do_retry:
+ status = i40e_aq_rx_ctl_write_register(hw, reg_addr,
+ reg_val, NULL);
+ if (hw->aq.asq_last_status == I40E_AQ_RC_EAGAIN && retry) {
+ i40e_msec_delay(1);
+ retry--;
+ goto do_retry;
+ }
+ }
+
+ /* if the AQ access failed, try the old-fashioned way */
+ if (status || use_register)
+ wr32(hw, reg_addr, reg_val);
+}
+
+/**
+ * i40e_mdio_if_number_selection - MDIO I/F number selection
+ * @hw: pointer to the hw struct
+ * @set_mdio: use MDIO I/F number specified by mdio_num
+ * @mdio_num: MDIO I/F number
+ * @cmd: pointer to PHY Register command structure
+ **/
+static void
+i40e_mdio_if_number_selection(struct i40e_hw *hw, bool set_mdio, u8 mdio_num,
+ struct i40e_aqc_phy_register_access *cmd)
+{
+ if (set_mdio && cmd->phy_interface == I40E_AQ_PHY_REG_ACCESS_EXTERNAL) {
+ if (hw->flags & I40E_HW_FLAG_AQ_PHY_ACCESS_EXTENDED)
+ cmd->cmd_flags |=
+ I40E_AQ_PHY_REG_ACCESS_SET_MDIO_IF_NUMBER |
+ ((mdio_num <<
+ I40E_AQ_PHY_REG_ACCESS_MDIO_IF_NUMBER_SHIFT) &
+ I40E_AQ_PHY_REG_ACCESS_MDIO_IF_NUMBER_MASK);
+ else
+ i40e_debug(hw, I40E_DEBUG_PHY,
+ "MDIO I/F number selection not supported by current FW version.\n");
+ }
+}
+
+/**
+ * i40e_aq_set_phy_register_ext
+ * @hw: pointer to the hw struct
+ * @phy_select: select which phy should be accessed
+ * @dev_addr: PHY device address
+ * @page_change: enable auto page change
+ * @set_mdio: use MDIO I/F number specified by mdio_num
+ * @mdio_num: MDIO I/F number
+ * @reg_addr: PHY register address
+ * @reg_val: new register value
+ * @cmd_details: pointer to command details structure or NULL
+ *
+ * Write the external PHY register.
+ * NOTE: In common cases MDIO I/F number should not be changed, thats why you
+ * may use simple wrapper i40e_aq_set_phy_register.
+ **/
+enum i40e_status_code
+i40e_aq_set_phy_register_ext(struct i40e_hw *hw,
+ u8 phy_select, u8 dev_addr, bool page_change,
+ bool set_mdio, u8 mdio_num,
+ u32 reg_addr, u32 reg_val,
+ struct i40e_asq_cmd_details *cmd_details)
+{
+ struct i40e_aq_desc desc;
+ struct i40e_aqc_phy_register_access *cmd =
+ (struct i40e_aqc_phy_register_access *)&desc.params.raw;
+ enum i40e_status_code status;
+
+ i40e_fill_default_direct_cmd_desc(&desc,
+ i40e_aqc_opc_set_phy_register);
+
+ cmd->phy_interface = phy_select;
+ cmd->dev_addres = dev_addr;
+ cmd->reg_address = CPU_TO_LE32(reg_addr);
+ cmd->reg_value = CPU_TO_LE32(reg_val);
+
+ if (!page_change)
+ cmd->cmd_flags = I40E_AQ_PHY_REG_ACCESS_DONT_CHANGE_QSFP_PAGE;
+
+ i40e_mdio_if_number_selection(hw, set_mdio, mdio_num, cmd);
+
+ status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details);
+
+ return status;
+}
+
+/**
+ * i40e_aq_get_phy_register_ext
+ * @hw: pointer to the hw struct
+ * @phy_select: select which phy should be accessed
+ * @dev_addr: PHY device address
+ * @page_change: enable auto page change
+ * @set_mdio: use MDIO I/F number specified by mdio_num
+ * @mdio_num: MDIO I/F number
+ * @reg_addr: PHY register address
+ * @reg_val: read register value
+ * @cmd_details: pointer to command details structure or NULL
+ *
+ * Read the external PHY register.
+ * NOTE: In common cases MDIO I/F number should not be changed, thats why you
+ * may use simple wrapper i40e_aq_get_phy_register.
+ **/
+enum i40e_status_code
+i40e_aq_get_phy_register_ext(struct i40e_hw *hw,
+ u8 phy_select, u8 dev_addr, bool page_change,
+ bool set_mdio, u8 mdio_num,
+ u32 reg_addr, u32 *reg_val,
+ struct i40e_asq_cmd_details *cmd_details)
+{
+ struct i40e_aq_desc desc;
+ struct i40e_aqc_phy_register_access *cmd =
+ (struct i40e_aqc_phy_register_access *)&desc.params.raw;
+ enum i40e_status_code status;
+
+ i40e_fill_default_direct_cmd_desc(&desc,
+ i40e_aqc_opc_get_phy_register);
+
+ cmd->phy_interface = phy_select;
+ cmd->dev_addres = dev_addr;
+ cmd->reg_address = CPU_TO_LE32(reg_addr);
+
+ if (!page_change)
+ cmd->cmd_flags = I40E_AQ_PHY_REG_ACCESS_DONT_CHANGE_QSFP_PAGE;
+
+ i40e_mdio_if_number_selection(hw, set_mdio, mdio_num, cmd);
+
+ status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details);
+ if (!status)
+ *reg_val = LE32_TO_CPU(cmd->reg_value);
+
+ return status;
+}
+
+/**
+ * i40e_aq_run_phy_activity
+ * @hw: pointer to the hw struct
+ * @activity_id: ID of DNL activity to run
+ * @dnl_opcode: opcode passed to DNL script
+ * @cmd_status: pointer to memory to write return value of DNL script
+ * @data0: pointer to memory for first 4 bytes of data returned by DNL script
+ * @data1: pointer to memory for last 4 bytes of data returned by DNL script
+ * @cmd_details: pointer to command details structure or NULL
+ *
+ * Run DNL admin command.
+ **/
+enum i40e_status_code
+i40e_aq_run_phy_activity(struct i40e_hw *hw, u16 activity_id, u32 dnl_opcode,
+ u32 *cmd_status, u32 *data0, u32 *data1,
+ struct i40e_asq_cmd_details *cmd_details)
+{
+ struct i40e_aqc_run_phy_activity *cmd;
+ enum i40e_status_code retval;
+ struct i40e_aq_desc desc;
+
+ cmd = (struct i40e_aqc_run_phy_activity *)&desc.params.raw;
+
+ if (!cmd_status || !data0 || !data1) {
+ retval = I40E_ERR_PARAM;
+ goto err;
+ }
+
+ i40e_fill_default_direct_cmd_desc(&desc,
+ i40e_aqc_opc_run_phy_activity);
+
+ cmd->activity_id = CPU_TO_LE16(activity_id);
+ cmd->params.cmd.dnl_opcode = CPU_TO_LE32(dnl_opcode);
+
+ retval = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details);
+ if (retval)
+ goto err;
+
+ *cmd_status = LE32_TO_CPU(cmd->params.resp.cmd_status);
+ *data0 = LE32_TO_CPU(cmd->params.resp.data0);
+ *data1 = LE32_TO_CPU(cmd->params.resp.data1);
+err:
+ return retval;
+}
+