Commit d307dbc4 authored by Emmanuel Grumbach's avatar Emmanuel Grumbach Committed by Greg Kroah-Hartman

iwlwifi: fix RF-Kill interrupt while FW load for gen2 devices

commit ed3e4c6d 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: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent cfe69226
...@@ -168,7 +168,7 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans, ...@@ -168,7 +168,7 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans,
memcpy(iml_img, trans->iml, trans->iml_len); memcpy(iml_img, trans->iml, trans->iml_len);
iwl_enable_interrupts(trans); iwl_enable_fw_load_int_ctx_info(trans);
/* kick FW self load */ /* kick FW self load */
iwl_write64(trans, CSR_CTXT_INFO_ADDR, iwl_write64(trans, CSR_CTXT_INFO_ADDR,
......
...@@ -222,7 +222,7 @@ int iwl_pcie_ctxt_info_init(struct iwl_trans *trans, ...@@ -222,7 +222,7 @@ int iwl_pcie_ctxt_info_init(struct iwl_trans *trans,
trans_pcie->ctxt_info = ctxt_info; trans_pcie->ctxt_info = ctxt_info;
iwl_enable_interrupts(trans); iwl_enable_fw_load_int_ctx_info(trans);
/* Configure debug, if exists */ /* Configure debug, if exists */
if (iwl_pcie_dbg_on(trans)) if (iwl_pcie_dbg_on(trans))
......
...@@ -894,6 +894,33 @@ static inline void iwl_enable_fw_load_int(struct iwl_trans *trans) ...@@ -894,6 +894,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) static inline u16 iwl_pcie_get_cmd_index(const struct iwl_txq *q, u32 index)
{ {
return index & (q->n_window - 1); return index & (q->n_window - 1);
......
...@@ -1850,6 +1850,8 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) ...@@ -1850,6 +1850,8 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
*/ */
iwl_pcie_rxmq_restock(trans, trans_pcie->rxq); iwl_pcie_rxmq_restock(trans, trans_pcie->rxq);
} }
handled |= CSR_INT_BIT_ALIVE;
} }
/* Safely ignore these bits for debug checks below */ /* Safely ignore these bits for debug checks below */
...@@ -1968,6 +1970,9 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) ...@@ -1968,6 +1970,9 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
/* Re-enable RF_KILL if it occurred */ /* Re-enable RF_KILL if it occurred */
else if (handled & CSR_INT_BIT_RF_KILL) else if (handled & CSR_INT_BIT_RF_KILL)
iwl_enable_rfkill_int(trans); 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); spin_unlock(&trans_pcie->irq_lock);
out: out:
......
...@@ -272,6 +272,15 @@ void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans, u32 scd_addr) ...@@ -272,6 +272,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 * paging memory cannot be freed included since FW will still use it
*/ */
iwl_pcie_ctxt_info_free(trans); 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, int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment