iwlwifi: fix RF-Kill interrupt while FW load for gen2 devices
authorEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Mon, 20 May 2019 12:18:24 +0000 (15:18 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 26 Jul 2019 07:14:22 +0000 (09:14 +0200)
commit ed3e4c6d3cd8f093a3636cb05492429fe2af228d upstream.

Newest devices have a new firmware load mechanism. This
mechanism is called the context info. It means that the
driver doesn't need to load the sections of the firmware.
The driver rather prepares a place in DRAM, with pointers
to the relevant sections of the firmware, and the firmware
loads itself.
At the end of the process, the firmware sends the ALIVE
interrupt. This is different from the previous scheme in
which the driver expected the FH_TX interrupt after each
section being transferred over the DMA.

In order to support this new flow, we enabled all the
interrupts. This broke the assumption that we have in the
code that the RF-Kill interrupt can't interrupt the firmware
load flow.

Change the context info flow to enable only the ALIVE
interrupt, and re-enable all the other interrupts only
after the firmware is alive. Then, we won't see the RF-Kill
interrupt until then. Getting the RF-Kill interrupt while
loading the firmware made us kill the firmware while it is
loading and we ended up dumping garbage instead of the firmware
state.

Re-enable the ALIVE | RX interrupts from the ISR when we
get the ALIVE interrupt to be able to get the RX interrupt
that comes immediately afterwards for the ALIVE
notification. This is needed for non MSI-X only.

Cc: stable@vger.kernel.org
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c
drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c
drivers/net/wireless/intel/iwlwifi/pcie/internal.h
drivers/net/wireless/intel/iwlwifi/pcie/rx.c
drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c

index 2146fda..64d976d 100644 (file)
@@ -164,7 +164,7 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans,
 
        memcpy(iml_img, trans->iml, trans->iml_len);
 
-       iwl_enable_interrupts(trans);
+       iwl_enable_fw_load_int_ctx_info(trans);
 
        /* kick FW self load */
        iwl_write64(trans, CSR_CTXT_INFO_ADDR,
index b2cd7ef..6f25fd1 100644 (file)
@@ -206,7 +206,7 @@ int iwl_pcie_ctxt_info_init(struct iwl_trans *trans,
 
        trans_pcie->ctxt_info = ctxt_info;
 
-       iwl_enable_interrupts(trans);
+       iwl_enable_fw_load_int_ctx_info(trans);
 
        /* Configure debug, if exists */
        if (trans->dbg_dest_tlv)
index b63d44b..00f9566 100644 (file)
@@ -896,6 +896,33 @@ static inline void iwl_enable_fw_load_int(struct iwl_trans *trans)
        }
 }
 
+static inline void iwl_enable_fw_load_int_ctx_info(struct iwl_trans *trans)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+       IWL_DEBUG_ISR(trans, "Enabling ALIVE interrupt only\n");
+
+       if (!trans_pcie->msix_enabled) {
+               /*
+                * When we'll receive the ALIVE interrupt, the ISR will call
+                * iwl_enable_fw_load_int_ctx_info again to set the ALIVE
+                * interrupt (which is not really needed anymore) but also the
+                * RX interrupt which will allow us to receive the ALIVE
+                * notification (which is Rx) and continue the flow.
+                */
+               trans_pcie->inta_mask =  CSR_INT_BIT_ALIVE | CSR_INT_BIT_FH_RX;
+               iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask);
+       } else {
+               iwl_enable_hw_int_msk_msix(trans,
+                                          MSIX_HW_INT_CAUSES_REG_ALIVE);
+               /*
+                * Leave all the FH causes enabled to get the ALIVE
+                * notification.
+                */
+               iwl_enable_fh_int_msk_msix(trans, trans_pcie->fh_init_mask);
+       }
+}
+
 static inline u16 iwl_pcie_get_cmd_index(const struct iwl_txq *q, u32 index)
 {
        return index & (q->n_window - 1);
index 747ca8b..1d14498 100644 (file)
@@ -1796,6 +1796,8 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
                         */
                        iwl_pcie_rxmq_restock(trans, trans_pcie->rxq);
                }
+
+               handled |= CSR_INT_BIT_ALIVE;
        }
 
        /* Safely ignore these bits for debug checks below */
@@ -1914,6 +1916,9 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
        /* Re-enable RF_KILL if it occurred */
        else if (handled & CSR_INT_BIT_RF_KILL)
                iwl_enable_rfkill_int(trans);
+       /* Re-enable the ALIVE / Rx interrupt if it occurred */
+       else if (handled & (CSR_INT_BIT_ALIVE | CSR_INT_BIT_FH_RX))
+               iwl_enable_fw_load_int_ctx_info(trans);
        spin_unlock(&trans_pcie->irq_lock);
 
 out:
index 2bc6721..31e72e1 100644 (file)
@@ -289,6 +289,15 @@ void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans, u32 scd_addr)
         * paging memory cannot be freed included since FW will still use it
         */
        iwl_pcie_ctxt_info_free(trans);
+
+       /*
+        * Re-enable all the interrupts, including the RF-Kill one, now that
+        * the firmware is alive.
+        */
+       iwl_enable_interrupts(trans);
+       mutex_lock(&trans_pcie->mutex);
+       iwl_pcie_check_hw_rf_kill(trans);
+       mutex_unlock(&trans_pcie->mutex);
 }
 
 int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans,