Commit 31b8b343 authored by Emmanuel Grumbach's avatar Emmanuel Grumbach

iwlwifi: fix RFkill while calibrating

If the RFkill interrupt fires while we calibrate, it would
make the firmware fail and the driver wasn't able to recover.
Change the flow so that the driver will kill the firmware
in that case.

Since we have now two flows that are calling
trans_stop_device (the RFkill interrupt and the
op_mode_mvm_start function) - we need to better sync this.
Use the STATUS_DEVICE_ENABLED in the pcie transport in an
atomic way to achieve this.

This fixes: https://bugzilla.kernel.org/show_bug.cgi?id=86231

CC: <stable@vger.kernel.org> [3.10+]
Reviewed-by: default avatarJohannes Berg <johannes.berg@intel.com>
Reviewed-by: default avatarLuciano Coelho <luciano.coelho@intel.com>
Signed-off-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
parent 7b358f06
...@@ -284,7 +284,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) ...@@ -284,7 +284,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
lockdep_assert_held(&mvm->mutex); lockdep_assert_held(&mvm->mutex);
if (WARN_ON_ONCE(mvm->init_ucode_complete)) if (WARN_ON_ONCE(mvm->init_ucode_complete || mvm->calibrating))
return 0; return 0;
iwl_init_notification_wait(&mvm->notif_wait, iwl_init_notification_wait(&mvm->notif_wait,
...@@ -334,6 +334,8 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) ...@@ -334,6 +334,8 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
goto out; goto out;
} }
mvm->calibrating = true;
/* Send TX valid antennas before triggering calibrations */ /* Send TX valid antennas before triggering calibrations */
ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant); ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant);
if (ret) if (ret)
...@@ -358,11 +360,17 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) ...@@ -358,11 +360,17 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
MVM_UCODE_CALIB_TIMEOUT); MVM_UCODE_CALIB_TIMEOUT);
if (!ret) if (!ret)
mvm->init_ucode_complete = true; mvm->init_ucode_complete = true;
if (ret && iwl_mvm_is_radio_killed(mvm)) {
IWL_DEBUG_RF_KILL(mvm, "RFKILL while calibrating.\n");
ret = 1;
}
goto out; goto out;
error: error:
iwl_remove_notification(&mvm->notif_wait, &calib_wait); iwl_remove_notification(&mvm->notif_wait, &calib_wait);
out: out:
mvm->calibrating = false;
if (iwlmvm_mod_params.init_dbg && !mvm->nvm_data) { if (iwlmvm_mod_params.init_dbg && !mvm->nvm_data) {
/* we want to debug INIT and we have no NVM - fake */ /* we want to debug INIT and we have no NVM - fake */
mvm->nvm_data = kzalloc(sizeof(struct iwl_nvm_data) + mvm->nvm_data = kzalloc(sizeof(struct iwl_nvm_data) +
......
...@@ -788,6 +788,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) ...@@ -788,6 +788,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
mvm->scan_status = IWL_MVM_SCAN_NONE; mvm->scan_status = IWL_MVM_SCAN_NONE;
mvm->ps_disabled = false; mvm->ps_disabled = false;
mvm->calibrating = false;
/* just in case one was running */ /* just in case one was running */
ieee80211_remain_on_channel_expired(mvm->hw); ieee80211_remain_on_channel_expired(mvm->hw);
......
...@@ -548,6 +548,7 @@ struct iwl_mvm { ...@@ -548,6 +548,7 @@ struct iwl_mvm {
enum iwl_ucode_type cur_ucode; enum iwl_ucode_type cur_ucode;
bool ucode_loaded; bool ucode_loaded;
bool init_ucode_complete; bool init_ucode_complete;
bool calibrating;
u32 error_event_table; u32 error_event_table;
u32 log_event_table; u32 log_event_table;
u32 umac_error_event_table; u32 umac_error_event_table;
......
...@@ -753,6 +753,7 @@ void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state) ...@@ -753,6 +753,7 @@ void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state)
static bool iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) static bool iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
{ {
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
bool calibrating = ACCESS_ONCE(mvm->calibrating);
if (state) if (state)
set_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status); set_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status);
...@@ -761,7 +762,15 @@ static bool iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) ...@@ -761,7 +762,15 @@ static bool iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm)); wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm));
return state && mvm->cur_ucode != IWL_UCODE_INIT; /* iwl_run_init_mvm_ucode is waiting for results, abort it */
if (calibrating)
iwl_abort_notification_waits(&mvm->notif_wait);
/*
* Stop the device if we run OPERATIONAL firmware or if we are in the
* middle of the calibrations.
*/
return state && (mvm->cur_ucode != IWL_UCODE_INIT || calibrating);
} }
static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)
......
...@@ -915,7 +915,8 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) ...@@ -915,7 +915,8 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
* restart. So don't process again if the device is * restart. So don't process again if the device is
* already dead. * already dead.
*/ */
if (test_bit(STATUS_DEVICE_ENABLED, &trans->status)) { if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
IWL_DEBUG_INFO(trans, "DEVICE_ENABLED bit was set and is now cleared\n");
iwl_pcie_tx_stop(trans); iwl_pcie_tx_stop(trans);
iwl_pcie_rx_stop(trans); iwl_pcie_rx_stop(trans);
...@@ -945,7 +946,6 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) ...@@ -945,7 +946,6 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
/* clear all status bits */ /* clear all status bits */
clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
clear_bit(STATUS_INT_ENABLED, &trans->status); clear_bit(STATUS_INT_ENABLED, &trans->status);
clear_bit(STATUS_DEVICE_ENABLED, &trans->status);
clear_bit(STATUS_TPOWER_PMI, &trans->status); clear_bit(STATUS_TPOWER_PMI, &trans->status);
clear_bit(STATUS_RFKILL, &trans->status); clear_bit(STATUS_RFKILL, &trans->status);
......
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