Merge tag 'v3.16-rc5' into timers/core
[sfrench/cifs-2.6.git] / drivers / net / ethernet / intel / i40e / i40e_adminq.c
index ed3902bf249b3e4ceb047ed14762d6ea50b1a4c0..7a027499fc57fc968d9ca33df5c65bb9e6825123 100644 (file)
 
 static void i40e_resume_aq(struct i40e_hw *hw);
 
+/**
+ * i40e_is_nvm_update_op - return true if this is an NVM update operation
+ * @desc: API request descriptor
+ **/
+static inline bool i40e_is_nvm_update_op(struct i40e_aq_desc *desc)
+{
+       return (desc->opcode == i40e_aqc_opc_nvm_erase) ||
+              (desc->opcode == i40e_aqc_opc_nvm_update);
+}
+
 /**
  *  i40e_adminq_init_regs - Initialize AdminQ registers
  *  @hw: pointer to the hardware structure
@@ -281,8 +291,11 @@ static void i40e_free_asq_bufs(struct i40e_hw *hw)
  *
  *  Configure base address and length registers for the transmit queue
  **/
-static void i40e_config_asq_regs(struct i40e_hw *hw)
+static i40e_status i40e_config_asq_regs(struct i40e_hw *hw)
 {
+       i40e_status ret_code = 0;
+       u32 reg = 0;
+
        if (hw->mac.type == I40E_MAC_VF) {
                /* configure the transmit queue */
                wr32(hw, I40E_VF_ATQBAH1,
@@ -291,6 +304,7 @@ static void i40e_config_asq_regs(struct i40e_hw *hw)
                    lower_32_bits(hw->aq.asq.desc_buf.pa));
                wr32(hw, I40E_VF_ATQLEN1, (hw->aq.num_asq_entries |
                                          I40E_VF_ATQLEN1_ATQENABLE_MASK));
+               reg = rd32(hw, I40E_VF_ATQBAL1);
        } else {
                /* configure the transmit queue */
                wr32(hw, I40E_PF_ATQBAH,
@@ -299,7 +313,14 @@ static void i40e_config_asq_regs(struct i40e_hw *hw)
                    lower_32_bits(hw->aq.asq.desc_buf.pa));
                wr32(hw, I40E_PF_ATQLEN, (hw->aq.num_asq_entries |
                                          I40E_PF_ATQLEN_ATQENABLE_MASK));
+               reg = rd32(hw, I40E_PF_ATQBAL);
        }
+
+       /* Check one register to verify that config was applied */
+       if (reg != lower_32_bits(hw->aq.asq.desc_buf.pa))
+               ret_code = I40E_ERR_ADMIN_QUEUE_ERROR;
+
+       return ret_code;
 }
 
 /**
@@ -308,8 +329,11 @@ static void i40e_config_asq_regs(struct i40e_hw *hw)
  *
  * Configure base address and length registers for the receive (event queue)
  **/
-static void i40e_config_arq_regs(struct i40e_hw *hw)
+static i40e_status i40e_config_arq_regs(struct i40e_hw *hw)
 {
+       i40e_status ret_code = 0;
+       u32 reg = 0;
+
        if (hw->mac.type == I40E_MAC_VF) {
                /* configure the receive queue */
                wr32(hw, I40E_VF_ARQBAH1,
@@ -318,6 +342,7 @@ static void i40e_config_arq_regs(struct i40e_hw *hw)
                    lower_32_bits(hw->aq.arq.desc_buf.pa));
                wr32(hw, I40E_VF_ARQLEN1, (hw->aq.num_arq_entries |
                                          I40E_VF_ARQLEN1_ARQENABLE_MASK));
+               reg = rd32(hw, I40E_VF_ARQBAL1);
        } else {
                /* configure the receive queue */
                wr32(hw, I40E_PF_ARQBAH,
@@ -326,10 +351,17 @@ static void i40e_config_arq_regs(struct i40e_hw *hw)
                    lower_32_bits(hw->aq.arq.desc_buf.pa));
                wr32(hw, I40E_PF_ARQLEN, (hw->aq.num_arq_entries |
                                          I40E_PF_ARQLEN_ARQENABLE_MASK));
+               reg = rd32(hw, I40E_PF_ARQBAL);
        }
 
        /* Update tail in the HW to post pre-allocated buffers */
        wr32(hw, hw->aq.arq.tail, hw->aq.num_arq_entries - 1);
+
+       /* Check one register to verify that config was applied */
+       if (reg != lower_32_bits(hw->aq.arq.desc_buf.pa))
+               ret_code = I40E_ERR_ADMIN_QUEUE_ERROR;
+
+       return ret_code;
 }
 
 /**
@@ -377,7 +409,9 @@ static i40e_status i40e_init_asq(struct i40e_hw *hw)
                goto init_adminq_free_rings;
 
        /* initialize base registers */
-       i40e_config_asq_regs(hw);
+       ret_code = i40e_config_asq_regs(hw);
+       if (ret_code)
+               goto init_adminq_free_rings;
 
        /* success! */
        goto init_adminq_exit;
@@ -434,7 +468,9 @@ static i40e_status i40e_init_arq(struct i40e_hw *hw)
                goto init_adminq_free_rings;
 
        /* initialize base registers */
-       i40e_config_arq_regs(hw);
+       ret_code = i40e_config_arq_regs(hw);
+       if (ret_code)
+               goto init_adminq_free_rings;
 
        /* success! */
        goto init_adminq_exit;
@@ -577,14 +613,14 @@ i40e_status i40e_init_adminq(struct i40e_hw *hw)
        i40e_read_nvm_word(hw, I40E_SR_NVM_EETRACK_HI, &eetrack_hi);
        hw->nvm.eetrack = (eetrack_hi << 16) | eetrack_lo;
 
-       if (hw->aq.api_maj_ver != I40E_FW_API_VERSION_MAJOR ||
-           hw->aq.api_min_ver > I40E_FW_API_VERSION_MINOR) {
+       if (hw->aq.api_maj_ver > I40E_FW_API_VERSION_MAJOR) {
                ret_code = I40E_ERR_FIRMWARE_API_VERSION;
                goto init_adminq_free_arq;
        }
 
        /* pre-emptive resource lock release */
        i40e_aq_release_resource(hw, I40E_NVM_RESOURCE_ID, 0, NULL);
+       hw->aq.nvm_busy = false;
 
        ret_code = i40e_aq_set_hmc_resource_profile(hw,
                                                    I40E_HMC_PROFILE_DEFAULT,
@@ -708,6 +744,12 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw,
                goto asq_send_command_exit;
        }
 
+       if (i40e_is_nvm_update_op(desc) && hw->aq.nvm_busy) {
+               i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, "AQTX: NVM busy.\n");
+               status = I40E_ERR_NVM;
+               goto asq_send_command_exit;
+       }
+
        details = I40E_ADMINQ_DETAILS(hw->aq.asq, hw->aq.asq.next_to_use);
        if (cmd_details) {
                *details = *cmd_details;
@@ -835,6 +877,9 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw,
                hw->aq.asq_last_status = (enum i40e_admin_queue_err)retval;
        }
 
+       if (i40e_is_nvm_update_op(desc))
+               hw->aq.nvm_busy = true;
+
        /* update the error if time out occurred */
        if ((!cmd_completed) &&
            (!details->async && !details->postpone)) {
@@ -929,6 +974,9 @@ i40e_status i40e_clean_arq_element(struct i40e_hw *hw,
                               e->msg_size);
        }
 
+       if (i40e_is_nvm_update_op(&e->desc))
+               hw->aq.nvm_busy = false;
+
        /* Restore the original datalen and buffer address in the desc,
         * FW updates datalen to indicate the event message
         * size