ath11k: pci: fix L1ss clock unstable problem
authorCarl Huang <cjhuang@codeaurora.org>
Thu, 10 Dec 2020 14:05:22 +0000 (16:05 +0200)
committerKalle Valo <kvalo@codeaurora.org>
Sat, 12 Dec 2020 04:40:17 +0000 (06:40 +0200)
For QCA6390, one PCI related clock drifts sometimes, and
it makes PCI link difficult to quit L1ss. Fix it by writing
some registers which are known to fix the problem.

Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1

Signed-off-by: Carl Huang <cjhuang@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/1607609124-17250-5-git-send-email-kvalo@codeaurora.org
drivers/net/wireless/ath/ath11k/pci.c
drivers/net/wireless/ath/ath11k/pci.h

index 9b6d4bb..064c350 100644 (file)
@@ -239,6 +239,75 @@ static void ath11k_pci_clear_dbg_registers(struct ath11k_base *ab)
        ath11k_dbg(ab, ATH11K_DBG_PCI, "soc reset cause:%d\n", val);
 }
 
+static int ath11k_pci_set_link_reg(struct ath11k_base *ab,
+                                  u32 offset, u32 value, u32 mask)
+{
+       u32 v;
+       int i;
+
+       v = ath11k_pci_read32(ab, offset);
+       if ((v & mask) == value)
+               return 0;
+
+       for (i = 0; i < 10; i++) {
+               ath11k_pci_write32(ab, offset, (v & ~mask) | value);
+
+               v = ath11k_pci_read32(ab, offset);
+               if ((v & mask) == value)
+                       return 0;
+
+               mdelay(2);
+       }
+
+       ath11k_warn(ab, "failed to set pcie link register 0x%08x: 0x%08x != 0x%08x\n",
+                   offset, v & mask, value);
+
+       return -ETIMEDOUT;
+}
+
+static int ath11k_pci_fix_l1ss(struct ath11k_base *ab)
+{
+       int ret;
+
+       ret = ath11k_pci_set_link_reg(ab,
+                                     PCIE_QSERDES_COM_SYSCLK_EN_SEL_REG,
+                                     PCIE_QSERDES_COM_SYSCLK_EN_SEL_VAL,
+                                     PCIE_QSERDES_COM_SYSCLK_EN_SEL_MSK);
+       if (!ret) {
+               ath11k_warn(ab, "failed to set sysclk: %d\n", ret);
+               return ret;
+       }
+
+       ret = ath11k_pci_set_link_reg(ab,
+                                     PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG1_REG,
+                                     PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG1_VAL,
+                                     PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG_MSK);
+       if (!ret) {
+               ath11k_warn(ab, "failed to set dtct config1 error: %d\n", ret);
+               return ret;
+       }
+
+       ret = ath11k_pci_set_link_reg(ab,
+                                     PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG2_REG,
+                                     PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG2_VAL,
+                                     PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG_MSK);
+       if (!ret) {
+               ath11k_warn(ab, "failed to set dtct config2: %d\n", ret);
+               return ret;
+       }
+
+       ret = ath11k_pci_set_link_reg(ab,
+                                     PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG4_REG,
+                                     PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG4_VAL,
+                                     PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG_MSK);
+       if (!ret) {
+               ath11k_warn(ab, "failed to set dtct config4: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
 static void ath11k_pci_enable_ltssm(struct ath11k_base *ab)
 {
        u32 val;
@@ -288,6 +357,7 @@ static void ath11k_pci_sw_reset(struct ath11k_base *ab, bool power_on)
        if (power_on) {
                ath11k_pci_enable_ltssm(ab);
                ath11k_pci_clear_all_intrs(ab);
+               ath11k_pci_fix_l1ss(ab);
        }
 
        ath11k_mhi_clear_vector(ab);
index 296f1ca..6e7cc89 100644 (file)
 #define PCIE_SMLH_REQ_RST_LINK_DOWN            0x2
 #define PCIE_INT_CLEAR_ALL                     0xffffffff
 
+#define PCIE_QSERDES_COM_SYSCLK_EN_SEL_REG     0x01e0c0ac
+#define PCIE_QSERDES_COM_SYSCLK_EN_SEL_VAL     0x10
+#define PCIE_QSERDES_COM_SYSCLK_EN_SEL_MSK     0xffffffff
+#define PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG1_REG        0x01e0c628
+#define PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG1_VAL        0x02
+#define PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG2_REG        0x01e0c62c
+#define PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG2_VAL        0x52
+#define PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG4_REG        0x01e0c634
+#define PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG4_VAL        0xff
+#define PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG_MSK 0x000000ff
+
 struct ath11k_msi_user {
        char *name;
        int num_vectors;