OSDN Git Service

iwlwifi: pcie: support Bz suspend/resume trigger
authorHaim Dreyfuss <haim.dreyfuss@intel.com>
Sat, 4 Dec 2021 06:35:49 +0000 (08:35 +0200)
committerLuca Coelho <luciano.coelho@intel.com>
Tue, 7 Dec 2021 18:06:34 +0000 (20:06 +0200)
Instead of using two bits in the doorbell interrupt, the new Bz
devices have a new CSR_IPC_SLEEP_CONTROL register to let drivers
indicate the desired transition before triggering the doorbell
interrupt.

Signed-off-by: Haim Dreyfuss <haim.dreyfuss@intel.com>
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.20211204083238.63f3d150689a.Iaeb6f9b007e81b1a5a02144b0281935e4613cb78@changeid
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
drivers/net/wireless/intel/iwlwifi/iwl-csr.h
drivers/net/wireless/intel/iwlwifi/iwl-prph.h
drivers/net/wireless/intel/iwlwifi/pcie/trans.c

index 046a01f..21d8249 100644 (file)
 /* GIO Chicken Bits (PCI Express bus link power management) */
 #define CSR_GIO_CHICKEN_BITS    (CSR_BASE+0x100)
 
+#define CSR_IPC_SLEEP_CONTROL  (CSR_BASE + 0x114)
+#define CSR_IPC_SLEEP_CONTROL_SUSPEND  0x3
+#define CSR_IPC_SLEEP_CONTROL_RESUME   0
+
 /* Doorbell NMI (since Bz) */
 #define CSR_DOORBELL_VECTOR    (CSR_BASE + 0x130)
 #define CSR_DOORBELL_VECTOR_NMI        BIT(1)
index a84ab02..2ca22e1 100644 (file)
@@ -455,6 +455,13 @@ enum {
 #define UREG_DOORBELL_TO_ISR6_RESUME   BIT(19)
 #define UREG_DOORBELL_TO_ISR6_PNVM     BIT(20)
 
+/*
+ * From BZ family driver triggers this bit for suspend and resume
+ * The driver should update CSR_IPC_SLEEP_CONTROL before triggering
+ * this interrupt with suspend/resume value
+ */
+#define UREG_DOORBELL_TO_ISR6_SLEEP_CTRL       BIT(31)
+
 #define CNVI_MBOX_C                    0xA3400C
 
 #define FSEQ_ERROR_CODE                        0xA340C8
index f607edd..0aeab8a 100644 (file)
@@ -1499,33 +1499,54 @@ void iwl_pcie_d3_complete_suspend(struct iwl_trans *trans,
        iwl_pcie_set_pwr(trans, true);
 }
 
+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->trans_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->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) {
+               iwl_write32(trans, CSR_IPC_SLEEP_CONTROL,
+                           suspend ? CSR_IPC_SLEEP_CONTROL_SUSPEND :
+                                     CSR_IPC_SLEEP_CONTROL_RESUME);
+               iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6,
+                                   UREG_DOORBELL_TO_ISR6_SLEEP_CTRL);
+       } 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;
+
+       if (!ret) {
+               IWL_ERR(trans, "Timeout %s D3\n",
+                       suspend ? "entering" : "exiting");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
 static int iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test,
                                     bool reset)
 {
        int ret;
-       struct iwl_trans_pcie *trans_pcie =  IWL_TRANS_GET_PCIE_TRANS(trans);
 
        if (!reset)
                /* Enable persistence mode to avoid reset */
                iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
                            CSR_HW_IF_CONFIG_REG_PERSIST_MODE);
 
-       if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
-               iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6,
-                                   UREG_DOORBELL_TO_ISR6_SUSPEND);
-
-               ret = wait_event_timeout(trans_pcie->sx_waitq,
-                                        trans_pcie->sx_complete, 2 * HZ);
-               /*
-                * Invalidate it toward resume.
-                */
-               trans_pcie->sx_complete = false;
+       ret = iwl_pcie_d3_handshake(trans, true);
+       if (ret)
+               return ret;
 
-               if (!ret) {
-                       IWL_ERR(trans, "Timeout entering D3\n");
-                       return -ETIMEDOUT;
-               }
-       }
        iwl_pcie_d3_complete_suspend(trans, test, reset);
 
        return 0;
@@ -1542,6 +1563,7 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
        if (test) {
                iwl_enable_interrupts(trans);
                *status = IWL_D3_STATUS_ALIVE;
+               ret = 0;
                goto out;
        }
 
@@ -1590,25 +1612,10 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
                *status = IWL_D3_STATUS_ALIVE;
 
 out:
-       if (*status == IWL_D3_STATUS_ALIVE &&
-           trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
-               trans_pcie->sx_complete = false;
-               iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6,
-                                   UREG_DOORBELL_TO_ISR6_RESUME);
+       if (*status == IWL_D3_STATUS_ALIVE)
+               ret = iwl_pcie_d3_handshake(trans, false);
 
-               ret = wait_event_timeout(trans_pcie->sx_waitq,
-                                        trans_pcie->sx_complete, 2 * HZ);
-               /*
-                * Invalidate it toward next suspend.
-                */
-               trans_pcie->sx_complete = false;
-
-               if (!ret) {
-                       IWL_ERR(trans, "Timeout exiting D3\n");
-                       return -ETIMEDOUT;
-               }
-       }
-       return 0;
+       return ret;
 }
 
 static void