diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath11k/pci.c')
-rw-r--r-- | drivers/net/wireless/ath/ath11k/pci.c | 52 |
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) |