]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: iwlwifi: pcie: abort D3 handshake on error
authorJohannes Berg <johannes.berg@intel.com>
Wed, 11 Jun 2025 19:26:20 +0000 (22:26 +0300)
committerMiri Korenblit <miriam.rachel.korenblit@intel.com>
Wed, 25 Jun 2025 07:57:32 +0000 (10:57 +0300)
The D3 handshake can be interrupted by an error, especially
on resume where we no longer want to check explicitly for
errors. Expand the sx_complete to sx_state and handle any
errors occurring during the handshake.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20250611222325.157dca92c573.I6dd3b9d2f435c2c363224aa84e373931e56a545f@changeid
drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h
drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c
drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c

index 796410f2fa4852bbee2f07a4eed237b11b75f2e4..ebcc174f6c623d4491c6e4bde88e02f1c40eed6d 100644 (file)
@@ -382,7 +382,7 @@ struct iwl_pcie_txqs {
  * @irq_lock: lock to synchronize IRQ handling
  * @txq_memory: TXQ allocation array
  * @sx_waitq: waitqueue for Sx transitions
- * @sx_complete: completion for Sx transitions
+ * @sx_state: state tracking Sx transitions
  * @pcie_dbg_dumped_once: indicates PCIe regs were dumped already
  * @opmode_down: indicates opmode went away
  * @num_rx_bufs: number of RX buffers to allocate/use
@@ -448,7 +448,12 @@ struct iwl_trans_pcie {
        u8 __iomem *hw_base;
 
        bool ucode_write_complete;
-       bool sx_complete;
+       enum {
+               IWL_SX_INVALID = 0,
+               IWL_SX_WAITING,
+               IWL_SX_ERROR,
+               IWL_SX_COMPLETE,
+       } sx_state;
        wait_queue_head_t ucode_write_waitq;
        wait_queue_head_t sx_waitq;
 
index 0c73b1fe3645d22c1341701877b6ef1e552d4df0..619a9505e6d9d36f1c5594444d2778a283623892 100644 (file)
@@ -2394,6 +2394,11 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id)
                } else {
                        iwl_pcie_irq_handle_error(trans);
                }
+
+               if (trans_pcie->sx_state == IWL_SX_WAITING) {
+                       trans_pcie->sx_state = IWL_SX_ERROR;
+                       wake_up(&trans_pcie->sx_waitq);
+               }
        }
 
        /* After checking FH register check HW register */
@@ -2428,13 +2433,20 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id)
        if (inta_hw & MSIX_HW_INT_CAUSES_REG_WAKEUP && trans_pcie->prph_info) {
                u32 sleep_notif =
                        le32_to_cpu(trans_pcie->prph_info->sleep_notif);
+
                if (sleep_notif == IWL_D3_SLEEP_STATUS_SUSPEND ||
                    sleep_notif == IWL_D3_SLEEP_STATUS_RESUME) {
                        IWL_DEBUG_ISR(trans,
                                      "Sx interrupt: sleep notification = 0x%x\n",
                                      sleep_notif);
-                       trans_pcie->sx_complete = true;
-                       wake_up(&trans_pcie->sx_waitq);
+                       if (trans_pcie->sx_state == IWL_SX_WAITING) {
+                               trans_pcie->sx_state = IWL_SX_COMPLETE;
+                               wake_up(&trans_pcie->sx_waitq);
+                       } else {
+                               IWL_ERR(trans,
+                                       "unexpected Sx interrupt (0x%x)\n",
+                                       sleep_notif);
+                       }
                } else {
                        /* uCode wakes up after power-down sleep */
                        IWL_DEBUG_ISR(trans, "Wakeup interrupt\n");
index bace11a949c86d25efd13a134f8ff17aa0abc015..6054ebebd8c8fce2cf7c4040fcac1f51788801c8 100644 (file)
@@ -1536,30 +1536,41 @@ static int iwl_pcie_d3_handshake(struct iwl_trans *trans, bool suspend)
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        int ret;
 
+       if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
+               return 0;
+
+       trans_pcie->sx_state = IWL_SX_WAITING;
+
        if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_AX210)
                iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6,
                                    suspend ? UREG_DOORBELL_TO_ISR6_SUSPEND :
                                              UREG_DOORBELL_TO_ISR6_RESUME);
-       else if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
+       else
                iwl_write32(trans, CSR_IPC_SLEEP_CONTROL,
                            suspend ? CSR_IPC_SLEEP_CONTROL_SUSPEND :
                                      CSR_IPC_SLEEP_CONTROL_RESUME);
-       else
-               return 0;
 
        ret = wait_event_timeout(trans_pcie->sx_waitq,
-                                trans_pcie->sx_complete, 2 * HZ);
-
-       /* Invalidate it toward next suspend or resume */
-       trans_pcie->sx_complete = false;
-
+                                trans_pcie->sx_state != IWL_SX_WAITING,
+                                2 * HZ);
        if (!ret) {
                IWL_ERR(trans, "Timeout %s D3\n",
                        suspend ? "entering" : "exiting");
-               return -ETIMEDOUT;
+               ret = -ETIMEDOUT;
+       } else {
+               ret = 0;
        }
 
-       return 0;
+       if (trans_pcie->sx_state == IWL_SX_ERROR) {
+               IWL_ERR(trans, "FW error while %s D3\n",
+                       suspend ? "entering" : "exiting");
+               ret = -EIO;
+       }
+
+       /* Invalidate it toward next suspend or resume */
+       trans_pcie->sx_state = IWL_SX_INVALID;
+
+       return ret;
 }
 
 int iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test, bool reset)