summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/ath/ath11k/pci.c
diff options
context:
space:
mode:
authorCarl Huang <cjhuang@codeaurora.org>2020-12-10 16:05:21 +0200
committerKalle Valo <kvalo@codeaurora.org>2020-12-12 06:40:14 +0200
commitbabb0ced6acdbaa0b5e0721ec7b347fdbdfa0f6f (patch)
tree86afb2d78479577f3f0675a74ed86a60a734f837 /drivers/net/wireless/ath/ath11k/pci.c
parent43ed15e1ee01631687ed8b2e8595859802bc9f10 (diff)
ath11k: pci: fix hot reset stability issues
For QCA6390, host needs to reset some registers before MHI power up to fix PCI link unstable issue if hot reset happened. Also clear all pending interrupts during power up. 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-4-git-send-email-kvalo@codeaurora.org
Diffstat (limited to 'drivers/net/wireless/ath/ath11k/pci.c')
-rw-r--r--drivers/net/wireless/ath/ath11k/pci.c52
1 files changed, 47 insertions, 5 deletions
diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c
index c9ab1e9fc0fa..9b6d4bb40e6c 100644
--- a/drivers/net/wireless/ath/ath11k/pci.c
+++ b/drivers/net/wireless/ath/ath11k/pci.c
@@ -239,15 +239,57 @@ static void ath11k_pci_clear_dbg_registers(struct ath11k_base *ab)
ath11k_dbg(ab, ATH11K_DBG_PCI, "soc reset cause:%d\n", val);
}
+static void ath11k_pci_enable_ltssm(struct ath11k_base *ab)
+{
+ u32 val;
+ int i;
+
+ val = ath11k_pci_read32(ab, PCIE_PCIE_PARF_LTSSM);
+
+ /* PCIE link seems very unstable after the Hot Reset*/
+ for (i = 0; val != PARM_LTSSM_VALUE && i < 5; i++) {
+ if (val == 0xffffffff)
+ mdelay(5);
+
+ ath11k_pci_write32(ab, PCIE_PCIE_PARF_LTSSM, PARM_LTSSM_VALUE);
+ val = ath11k_pci_read32(ab, PCIE_PCIE_PARF_LTSSM);
+ }
+
+ ath11k_dbg(ab, ATH11K_DBG_PCI, "pci ltssm 0x%x\n", val);
+
+ val = ath11k_pci_read32(ab, GCC_GCC_PCIE_HOT_RST);
+ val |= GCC_GCC_PCIE_HOT_RST_VAL | 0x10;
+ ath11k_pci_write32(ab, GCC_GCC_PCIE_HOT_RST, val);
+ val = ath11k_pci_read32(ab, GCC_GCC_PCIE_HOT_RST);
+
+ ath11k_dbg(ab, ATH11K_DBG_PCI, "pci pcie_hot_rst 0x%x\n", val);
+
+ mdelay(5);
+}
+
+static void ath11k_pci_clear_all_intrs(struct ath11k_base *ab)
+{
+ /* This is a WAR for PCIE Hotreset.
+ * When target receive Hotreset, but will set the interrupt.
+ * So when download SBL again, SBL will open Interrupt and
+ * receive it, and crash immediately.
+ */
+ ath11k_pci_write32(ab, PCIE_PCIE_INT_ALL_CLEAR, PCIE_INT_CLEAR_ALL);
+}
+
static void ath11k_pci_force_wake(struct ath11k_base *ab)
{
ath11k_pci_write32(ab, PCIE_SOC_WAKE_PCIE_LOCAL_REG, 1);
mdelay(5);
}
-static void ath11k_pci_sw_reset(struct ath11k_base *ab)
+static void ath11k_pci_sw_reset(struct ath11k_base *ab, bool power_on)
{
- ath11k_pci_soc_global_reset(ab);
+ if (power_on) {
+ ath11k_pci_enable_ltssm(ab);
+ ath11k_pci_clear_all_intrs(ab);
+ }
+
ath11k_mhi_clear_vector(ab);
ath11k_pci_soc_global_reset(ab);
ath11k_mhi_set_mhictrl_reset(ab);
@@ -770,7 +812,7 @@ static int ath11k_pci_power_up(struct ath11k_base *ab)
ab_pci->register_window = 0;
clear_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags);
- ath11k_pci_sw_reset(ab_pci->ab);
+ ath11k_pci_sw_reset(ab_pci->ab, true);
ret = ath11k_mhi_start(ab_pci);
if (ret) {
@@ -785,10 +827,10 @@ static void ath11k_pci_power_down(struct ath11k_base *ab)
{
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
+ ath11k_pci_force_wake(ab_pci->ab);
ath11k_mhi_stop(ab_pci);
clear_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags);
- ath11k_pci_force_wake(ab_pci->ab);
- ath11k_pci_sw_reset(ab_pci->ab);
+ ath11k_pci_sw_reset(ab_pci->ab, false);
}
static void ath11k_pci_kill_tasklets(struct ath11k_base *ab)