diff options
author | Johannes Berg <johannes.berg@intel.com> | 2020-12-09 23:16:43 +0200 |
---|---|---|
committer | Luca Coelho <luciano.coelho@intel.com> | 2020-12-10 00:16:03 +0200 |
commit | 906d4eb84408a4bfd63eef0de4f1bd5262f73ac0 (patch) | |
tree | 834747052065be2863116e6814b0019513e5717b /drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c | |
parent | b2ed841ed070ccbe908016537f429a3a8f0221bf (diff) | |
download | linux-906d4eb84408a4bfd63eef0de4f1bd5262f73ac0.tar.gz |
iwlwifi: support firmware reset handshake
There are some races in the hardware that can possibly lead to
a bus lockup later during a restart when we manage to kill the
firmware at a bad time (while it's accessing the bus).
To work around this, add support for a new handshake between
firmware and driver to ensure that the firmware is in a well-
known state before we kill it.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
Link: https://lore.kernel.org/r/iwlwifi.20201209231352.7756fcc9865c.I13de65e0ffcb4186dd4c1a465f66df2e98c9a947@changeid
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c')
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c | 26 |
1 files changed, 26 insertions, 0 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c index c25a2fba3b17..c602b815dcc2 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c @@ -88,6 +88,28 @@ static void iwl_pcie_gen2_apm_stop(struct iwl_trans *trans, bool op_mode_leave) iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); } +static void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int ret; + + trans_pcie->fw_reset_done = false; + + if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) + iwl_write_umac_prph(trans, UREG_NIC_SET_NMI_DRIVER, + UREG_NIC_SET_NMI_DRIVER_RESET_HANDSHAKE); + else + iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6, + UREG_DOORBELL_TO_ISR6_RESET_HANDSHAKE); + + /* wait 200ms */ + ret = wait_event_timeout(trans_pcie->fw_reset_waitq, + trans_pcie->fw_reset_done, HZ / 5); + if (!ret) + IWL_ERR(trans, + "firmware didn't ACK the reset - continue anyway\n"); +} + void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -97,6 +119,10 @@ void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans) if (trans_pcie->is_down) return; + if (trans_pcie->fw_reset_handshake && + trans->state >= IWL_TRANS_FW_STARTED) + iwl_trans_pcie_fw_reset_handshake(trans); + trans_pcie->is_down = true; /* tell the device to stop sending interrupts */ |