i40e/i40evf: ARQ copy desc data even for failed commands
[cascardo/linux.git] / drivers / net / ethernet / intel / i40e / i40e_adminq.c
index ed3902b..c6d767c 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 == cpu_to_le16(i40e_aqc_opc_nvm_erase)) ||
+               (desc->opcode == cpu_to_le16(i40e_aqc_opc_nvm_update));
+}
+
 /**
  *  i40e_adminq_init_regs - Initialize AdminQ registers
  *  @hw: pointer to the hardware structure
@@ -45,16 +55,24 @@ static void i40e_adminq_init_regs(struct i40e_hw *hw)
                hw->aq.asq.tail = I40E_VF_ATQT1;
                hw->aq.asq.head = I40E_VF_ATQH1;
                hw->aq.asq.len  = I40E_VF_ATQLEN1;
+               hw->aq.asq.bal  = I40E_VF_ATQBAL1;
+               hw->aq.asq.bah  = I40E_VF_ATQBAH1;
                hw->aq.arq.tail = I40E_VF_ARQT1;
                hw->aq.arq.head = I40E_VF_ARQH1;
                hw->aq.arq.len  = I40E_VF_ARQLEN1;
+               hw->aq.arq.bal  = I40E_VF_ARQBAL1;
+               hw->aq.arq.bah  = I40E_VF_ARQBAH1;
        } else {
                hw->aq.asq.tail = I40E_PF_ATQT;
                hw->aq.asq.head = I40E_PF_ATQH;
                hw->aq.asq.len  = I40E_PF_ATQLEN;
+               hw->aq.asq.bal  = I40E_PF_ATQBAL;
+               hw->aq.asq.bah  = I40E_PF_ATQBAH;
                hw->aq.arq.tail = I40E_PF_ARQT;
                hw->aq.arq.head = I40E_PF_ARQH;
                hw->aq.arq.len  = I40E_PF_ARQLEN;
+               hw->aq.arq.bal  = I40E_PF_ARQBAL;
+               hw->aq.arq.bah  = I40E_PF_ARQBAH;
        }
 }
 
@@ -281,25 +299,27 @@ 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)
 {
-       if (hw->mac.type == I40E_MAC_VF) {
-               /* configure the transmit queue */
-               wr32(hw, I40E_VF_ATQBAH1,
-                   upper_32_bits(hw->aq.asq.desc_buf.pa));
-               wr32(hw, I40E_VF_ATQBAL1,
-                   lower_32_bits(hw->aq.asq.desc_buf.pa));
-               wr32(hw, I40E_VF_ATQLEN1, (hw->aq.num_asq_entries |
-                                         I40E_VF_ATQLEN1_ATQENABLE_MASK));
-       } else {
-               /* configure the transmit queue */
-               wr32(hw, I40E_PF_ATQBAH,
-                   upper_32_bits(hw->aq.asq.desc_buf.pa));
-               wr32(hw, I40E_PF_ATQBAL,
-                   lower_32_bits(hw->aq.asq.desc_buf.pa));
-               wr32(hw, I40E_PF_ATQLEN, (hw->aq.num_asq_entries |
-                                         I40E_PF_ATQLEN_ATQENABLE_MASK));
-       }
+       i40e_status ret_code = 0;
+       u32 reg = 0;
+
+       /* Clear Head and Tail */
+       wr32(hw, hw->aq.asq.head, 0);
+       wr32(hw, hw->aq.asq.tail, 0);
+
+       /* set starting point */
+       wr32(hw, hw->aq.asq.len, (hw->aq.num_asq_entries |
+                                 I40E_PF_ATQLEN_ATQENABLE_MASK));
+       wr32(hw, hw->aq.asq.bal, lower_32_bits(hw->aq.asq.desc_buf.pa));
+       wr32(hw, hw->aq.asq.bah, upper_32_bits(hw->aq.asq.desc_buf.pa));
+
+       /* Check one register to verify that config was applied */
+       reg = rd32(hw, hw->aq.asq.bal);
+       if (reg != lower_32_bits(hw->aq.asq.desc_buf.pa))
+               ret_code = I40E_ERR_ADMIN_QUEUE_ERROR;
+
+       return ret_code;
 }
 
 /**
@@ -308,28 +328,30 @@ 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)
 {
-       if (hw->mac.type == I40E_MAC_VF) {
-               /* configure the receive queue */
-               wr32(hw, I40E_VF_ARQBAH1,
-                   upper_32_bits(hw->aq.arq.desc_buf.pa));
-               wr32(hw, I40E_VF_ARQBAL1,
-                   lower_32_bits(hw->aq.arq.desc_buf.pa));
-               wr32(hw, I40E_VF_ARQLEN1, (hw->aq.num_arq_entries |
-                                         I40E_VF_ARQLEN1_ARQENABLE_MASK));
-       } else {
-               /* configure the receive queue */
-               wr32(hw, I40E_PF_ARQBAH,
-                   upper_32_bits(hw->aq.arq.desc_buf.pa));
-               wr32(hw, I40E_PF_ARQBAL,
-                   lower_32_bits(hw->aq.arq.desc_buf.pa));
-               wr32(hw, I40E_PF_ARQLEN, (hw->aq.num_arq_entries |
-                                         I40E_PF_ARQLEN_ARQENABLE_MASK));
-       }
+       i40e_status ret_code = 0;
+       u32 reg = 0;
+
+       /* Clear Head and Tail */
+       wr32(hw, hw->aq.arq.head, 0);
+       wr32(hw, hw->aq.arq.tail, 0);
+
+       /* set starting point */
+       wr32(hw, hw->aq.arq.len, (hw->aq.num_arq_entries |
+                                 I40E_PF_ARQLEN_ARQENABLE_MASK));
+       wr32(hw, hw->aq.arq.bal, lower_32_bits(hw->aq.arq.desc_buf.pa));
+       wr32(hw, hw->aq.arq.bah, upper_32_bits(hw->aq.arq.desc_buf.pa));
 
        /* 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 */
+       reg = rd32(hw, hw->aq.arq.bal);
+       if (reg != lower_32_bits(hw->aq.arq.desc_buf.pa))
+               ret_code = I40E_ERR_ADMIN_QUEUE_ERROR;
+
+       return ret_code;
 }
 
 /**
@@ -377,7 +399,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 +458,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;
@@ -463,6 +489,8 @@ static i40e_status i40e_shutdown_asq(struct i40e_hw *hw)
        wr32(hw, hw->aq.asq.head, 0);
        wr32(hw, hw->aq.asq.tail, 0);
        wr32(hw, hw->aq.asq.len, 0);
+       wr32(hw, hw->aq.asq.bal, 0);
+       wr32(hw, hw->aq.asq.bah, 0);
 
        /* make sure lock is available */
        mutex_lock(&hw->aq.asq_mutex);
@@ -494,6 +522,8 @@ static i40e_status i40e_shutdown_arq(struct i40e_hw *hw)
        wr32(hw, hw->aq.arq.head, 0);
        wr32(hw, hw->aq.arq.tail, 0);
        wr32(hw, hw->aq.arq.len, 0);
+       wr32(hw, hw->aq.arq.bal, 0);
+       wr32(hw, hw->aq.arq.bah, 0);
 
        /* make sure lock is available */
        mutex_lock(&hw->aq.arq_mutex);
@@ -541,6 +571,9 @@ i40e_status i40e_init_adminq(struct i40e_hw *hw)
        /* Set up register offsets */
        i40e_adminq_init_regs(hw);
 
+       /* setup ASQ command write back timeout */
+       hw->aq.asq_cmd_timeout = I40E_ASQ_CMD_TIMEOUT;
+
        /* allocate the ASQ */
        ret_code = i40e_init_asq(hw);
        if (ret_code)
@@ -577,14 +610,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,
@@ -641,6 +674,10 @@ static u16 i40e_clean_asq(struct i40e_hw *hw)
        desc = I40E_ADMINQ_DESC(*asq, ntc);
        details = I40E_ADMINQ_DETAILS(*asq, ntc);
        while (rd32(hw, hw->aq.asq.head) != ntc) {
+               i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE,
+                          "%s: ntc %d head %d.\n", __func__, ntc,
+                          rd32(hw, hw->aq.asq.head));
+
                if (details->callback) {
                        I40E_ADMINQ_CALLBACK cb_func =
                                        (I40E_ADMINQ_CALLBACK)details->callback;
@@ -700,6 +737,15 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw,
        struct i40e_aq_desc *desc_on_ring;
        bool cmd_completed = false;
        u16  retval = 0;
+       u32  val = 0;
+
+       val = rd32(hw, hw->aq.asq.head);
+       if (val >= hw->aq.num_asq_entries) {
+               i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE,
+                          "AQTX: head overrun at %d\n", val);
+               status = I40E_ERR_QUEUE_EMPTY;
+               goto asq_send_command_exit;
+       }
 
        if (hw->aq.asq.count == 0) {
                i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE,
@@ -708,6 +754,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;
@@ -787,6 +839,7 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw,
        }
 
        /* bump the tail */
+       i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, "AQTX: desc and buffer:\n");
        i40e_debug_aq(hw, I40E_DEBUG_AQ_COMMAND, (void *)desc_on_ring, buff);
        (hw->aq.asq.next_to_use)++;
        if (hw->aq.asq.next_to_use == hw->aq.asq.count)
@@ -810,7 +863,7 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw,
                        /* ugh! delay while spin_lock */
                        udelay(delay_len);
                        total_delay += delay_len;
-               } while (total_delay <  I40E_ASQ_CMD_TIMEOUT);
+               } while (total_delay < hw->aq.asq_cmd_timeout);
        }
 
        /* if ready, copy the desc back to temp */
@@ -824,6 +877,7 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw,
                                   I40E_DEBUG_AQ_MESSAGE,
                                   "AQTX: Command completed with error 0x%X.\n",
                                   retval);
+
                        /* strip off FW internal code */
                        retval &= 0xff;
                }
@@ -835,6 +889,12 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw,
                hw->aq.asq_last_status = (enum i40e_admin_queue_err)retval;
        }
 
+       if (le16_to_cpu(desc->datalen) == buff_size) {
+               i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE,
+                          "AQTX: desc and buffer writeback:\n");
+               i40e_debug_aq(hw, I40E_DEBUG_AQ_COMMAND, (void *)desc, buff);
+       }
+
        /* update the error if time out occurred */
        if ((!cmd_completed) &&
            (!details->async && !details->postpone)) {
@@ -844,6 +904,9 @@ i40e_status i40e_asq_send_command(struct i40e_hw *hw,
                status = I40E_ERR_ADMIN_QUEUE_TIMEOUT;
        }
 
+       if (!status && i40e_is_nvm_update_op(desc))
+               hw->aq.nvm_busy = true;
+
 asq_send_command_error:
        mutex_unlock(&hw->aq.asq_mutex);
 asq_send_command_exit:
@@ -906,10 +969,6 @@ i40e_status i40e_clean_arq_element(struct i40e_hw *hw,
        /* now clean the next descriptor */
        desc = I40E_ADMINQ_DESC(hw->aq.arq, ntc);
        desc_idx = ntc;
-       i40e_debug_aq(hw,
-                     I40E_DEBUG_AQ_COMMAND,
-                     (void *)desc,
-                     hw->aq.arq.r.arq_bi[desc_idx].va);
 
        flags = le16_to_cpu(desc->flags);
        if (flags & I40E_AQ_FLAG_ERR) {
@@ -920,15 +979,18 @@ i40e_status i40e_clean_arq_element(struct i40e_hw *hw,
                           I40E_DEBUG_AQ_MESSAGE,
                           "AQRX: Event received with error 0x%X.\n",
                           hw->aq.arq_last_status);
-       } else {
-               e->desc = *desc;
-               datalen = le16_to_cpu(desc->datalen);
-               e->msg_size = min(datalen, e->msg_size);
-               if (e->msg_buf != NULL && (e->msg_size != 0))
-                       memcpy(e->msg_buf, hw->aq.arq.r.arq_bi[desc_idx].va,
-                              e->msg_size);
        }
 
+       e->desc = *desc;
+       datalen = le16_to_cpu(desc->datalen);
+       e->msg_size = min(datalen, e->msg_size);
+       if (e->msg_buf != NULL && (e->msg_size != 0))
+               memcpy(e->msg_buf, hw->aq.arq.r.arq_bi[desc_idx].va,
+                      e->msg_size);
+
+       i40e_debug(hw, I40E_DEBUG_AQ_MESSAGE, "AQRX: desc and buffer:\n");
+       i40e_debug_aq(hw, I40E_DEBUG_AQ_COMMAND, (void *)desc, e->msg_buf);
+
        /* Restore the original datalen and buffer address in the desc,
         * FW updates datalen to indicate the event message
         * size
@@ -958,6 +1020,14 @@ clean_arq_element_out:
                *pending = (ntc > ntu ? hw->aq.arq.count : 0) + (ntu - ntc);
        mutex_unlock(&hw->aq.arq_mutex);
 
+       if (i40e_is_nvm_update_op(&e->desc)) {
+               hw->aq.nvm_busy = false;
+               if (hw->aq.nvm_release_on_done) {
+                       i40e_release_nvm(hw);
+                       hw->aq.nvm_release_on_done = false;
+               }
+       }
+
        return ret_code;
 }