mirror of
https://github.com/torvalds/linux.git
synced 2026-07-29 18:51:21 +02:00
ath11k: reset MHI during power down and power up
For QCA6390, normal power up and power down can't bring MHI to a workable state. This happens especially in warm reboot and rmmod and insmod. Host needs to write a few registers to bring MHI to normal state. Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.1.0.1-01238-QCAHKSWPL_SILICONZ-2 Signed-off-by: Carl Huang <cjhuang@codeauroro.org> Signed-off-by: Kalle Valo <kvalo@codeaurora.org> Link: https://lore.kernel.org/r/1597555891-26112-10-git-send-email-kvalo@codeaurora.org
This commit is contained in:
parent
065f5f683e
commit
f3c603d412
|
|
@ -107,6 +107,51 @@ static struct mhi_controller_config ath11k_mhi_config = {
|
||||||
.event_cfg = ath11k_mhi_events,
|
.event_cfg = ath11k_mhi_events,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void ath11k_mhi_set_mhictrl_reset(struct ath11k_base *ab)
|
||||||
|
{
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
val = ath11k_pci_read32(ab, MHISTATUS);
|
||||||
|
|
||||||
|
ath11k_dbg(ab, ATH11K_DBG_PCI, "MHISTATUS 0x%x\n", val);
|
||||||
|
|
||||||
|
/* Observed on QCA6390 that after SOC_GLOBAL_RESET, MHISTATUS
|
||||||
|
* has SYSERR bit set and thus need to set MHICTRL_RESET
|
||||||
|
* to clear SYSERR.
|
||||||
|
*/
|
||||||
|
ath11k_pci_write32(ab, MHICTRL, MHICTRL_RESET_MASK);
|
||||||
|
|
||||||
|
mdelay(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ath11k_mhi_reset_txvecdb(struct ath11k_base *ab)
|
||||||
|
{
|
||||||
|
ath11k_pci_write32(ab, PCIE_TXVECDB, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ath11k_mhi_reset_txvecstatus(struct ath11k_base *ab)
|
||||||
|
{
|
||||||
|
ath11k_pci_write32(ab, PCIE_TXVECSTATUS, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ath11k_mhi_reset_rxvecdb(struct ath11k_base *ab)
|
||||||
|
{
|
||||||
|
ath11k_pci_write32(ab, PCIE_RXVECDB, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ath11k_mhi_reset_rxvecstatus(struct ath11k_base *ab)
|
||||||
|
{
|
||||||
|
ath11k_pci_write32(ab, PCIE_RXVECSTATUS, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ath11k_mhi_clear_vector(struct ath11k_base *ab)
|
||||||
|
{
|
||||||
|
ath11k_mhi_reset_txvecdb(ab);
|
||||||
|
ath11k_mhi_reset_txvecstatus(ab);
|
||||||
|
ath11k_mhi_reset_rxvecdb(ab);
|
||||||
|
ath11k_mhi_reset_rxvecstatus(ab);
|
||||||
|
}
|
||||||
|
|
||||||
static int ath11k_mhi_get_msi(struct ath11k_pci *ab_pci)
|
static int ath11k_mhi_get_msi(struct ath11k_pci *ab_pci)
|
||||||
{
|
{
|
||||||
struct ath11k_base *ab = ab_pci->ab;
|
struct ath11k_base *ab = ab_pci->ab;
|
||||||
|
|
@ -416,7 +461,6 @@ int ath11k_mhi_start(struct ath11k_pci *ab_pci)
|
||||||
|
|
||||||
void ath11k_mhi_stop(struct ath11k_pci *ab_pci)
|
void ath11k_mhi_stop(struct ath11k_pci *ab_pci)
|
||||||
{
|
{
|
||||||
ath11k_mhi_set_state(ab_pci, ATH11K_MHI_RESUME);
|
|
||||||
ath11k_mhi_set_state(ab_pci, ATH11K_MHI_POWER_OFF);
|
ath11k_mhi_set_state(ab_pci, ATH11K_MHI_POWER_OFF);
|
||||||
ath11k_mhi_set_state(ab_pci, ATH11K_MHI_DEINIT);
|
ath11k_mhi_set_state(ab_pci, ATH11K_MHI_DEINIT);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,15 @@
|
||||||
|
|
||||||
#include "pci.h"
|
#include "pci.h"
|
||||||
|
|
||||||
|
#define PCIE_TXVECDB 0x360
|
||||||
|
#define PCIE_TXVECSTATUS 0x368
|
||||||
|
#define PCIE_RXVECDB 0x394
|
||||||
|
#define PCIE_RXVECSTATUS 0x39C
|
||||||
|
|
||||||
|
#define MHISTATUS 0x48
|
||||||
|
#define MHICTRL 0x38
|
||||||
|
#define MHICTRL_RESET_MASK 0x2
|
||||||
|
|
||||||
enum ath11k_mhi_state {
|
enum ath11k_mhi_state {
|
||||||
ATH11K_MHI_INIT,
|
ATH11K_MHI_INIT,
|
||||||
ATH11K_MHI_DEINIT,
|
ATH11K_MHI_DEINIT,
|
||||||
|
|
@ -24,5 +33,7 @@ int ath11k_mhi_start(struct ath11k_pci *ar_pci);
|
||||||
void ath11k_mhi_stop(struct ath11k_pci *ar_pci);
|
void ath11k_mhi_stop(struct ath11k_pci *ar_pci);
|
||||||
int ath11k_mhi_register(struct ath11k_pci *ar_pci);
|
int ath11k_mhi_register(struct ath11k_pci *ar_pci);
|
||||||
void ath11k_mhi_unregister(struct ath11k_pci *ar_pci);
|
void ath11k_mhi_unregister(struct ath11k_pci *ar_pci);
|
||||||
|
void ath11k_mhi_set_mhictrl_reset(struct ath11k_base *ab);
|
||||||
|
void ath11k_mhi_clear_vector(struct ath11k_base *ab);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -301,7 +301,7 @@ static inline void ath11k_pci_select_window(struct ath11k_pci *ab_pci, u32 offse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ath11k_pci_write32(struct ath11k_base *ab, u32 offset, u32 value)
|
void ath11k_pci_write32(struct ath11k_base *ab, u32 offset, u32 value)
|
||||||
{
|
{
|
||||||
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
|
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
|
||||||
|
|
||||||
|
|
@ -315,7 +315,7 @@ static void ath11k_pci_write32(struct ath11k_base *ab, u32 offset, u32 value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 ath11k_pci_read32(struct ath11k_base *ab, u32 offset)
|
u32 ath11k_pci_read32(struct ath11k_base *ab, u32 offset)
|
||||||
{
|
{
|
||||||
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
|
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
|
||||||
u32 val;
|
u32 val;
|
||||||
|
|
@ -332,6 +332,77 @@ static u32 ath11k_pci_read32(struct ath11k_base *ab, u32 offset)
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ath11k_pci_soc_global_reset(struct ath11k_base *ab)
|
||||||
|
{
|
||||||
|
u32 val, delay;
|
||||||
|
|
||||||
|
val = ath11k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET);
|
||||||
|
|
||||||
|
val |= PCIE_SOC_GLOBAL_RESET_V;
|
||||||
|
|
||||||
|
ath11k_pci_write32(ab, PCIE_SOC_GLOBAL_RESET, val);
|
||||||
|
|
||||||
|
/* TODO: exact time to sleep is uncertain */
|
||||||
|
delay = 10;
|
||||||
|
mdelay(delay);
|
||||||
|
|
||||||
|
/* Need to toggle V bit back otherwise stuck in reset status */
|
||||||
|
val &= ~PCIE_SOC_GLOBAL_RESET_V;
|
||||||
|
|
||||||
|
ath11k_pci_write32(ab, PCIE_SOC_GLOBAL_RESET, val);
|
||||||
|
|
||||||
|
mdelay(delay);
|
||||||
|
|
||||||
|
val = ath11k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET);
|
||||||
|
if (val == 0xffffffff)
|
||||||
|
ath11k_warn(ab, "link down error during global reset\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ath11k_pci_clear_dbg_registers(struct ath11k_base *ab)
|
||||||
|
{
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
/* read cookie */
|
||||||
|
val = ath11k_pci_read32(ab, PCIE_Q6_COOKIE_ADDR);
|
||||||
|
ath11k_dbg(ab, ATH11K_DBG_PCI, "cookie:0x%x\n", val);
|
||||||
|
|
||||||
|
val = ath11k_pci_read32(ab, WLAON_WARM_SW_ENTRY);
|
||||||
|
ath11k_dbg(ab, ATH11K_DBG_PCI, "WLAON_WARM_SW_ENTRY 0x%x\n", val);
|
||||||
|
|
||||||
|
/* TODO: exact time to sleep is uncertain */
|
||||||
|
mdelay(10);
|
||||||
|
|
||||||
|
/* write 0 to WLAON_WARM_SW_ENTRY to prevent Q6 from
|
||||||
|
* continuing warm path and entering dead loop.
|
||||||
|
*/
|
||||||
|
ath11k_pci_write32(ab, WLAON_WARM_SW_ENTRY, 0);
|
||||||
|
mdelay(10);
|
||||||
|
|
||||||
|
val = ath11k_pci_read32(ab, WLAON_WARM_SW_ENTRY);
|
||||||
|
ath11k_dbg(ab, ATH11K_DBG_PCI, "WLAON_WARM_SW_ENTRY 0x%x\n", val);
|
||||||
|
|
||||||
|
/* A read clear register. clear the register to prevent
|
||||||
|
* Q6 from entering wrong code path.
|
||||||
|
*/
|
||||||
|
val = ath11k_pci_read32(ab, WLAON_SOC_RESET_CAUSE_REG);
|
||||||
|
ath11k_dbg(ab, ATH11K_DBG_PCI, "soc reset cause:%d\n", val);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
ath11k_pci_soc_global_reset(ab);
|
||||||
|
ath11k_mhi_clear_vector(ab);
|
||||||
|
ath11k_pci_soc_global_reset(ab);
|
||||||
|
ath11k_mhi_set_mhictrl_reset(ab);
|
||||||
|
ath11k_pci_clear_dbg_registers(ab);
|
||||||
|
}
|
||||||
|
|
||||||
int ath11k_pci_get_msi_irq(struct device *dev, unsigned int vector)
|
int ath11k_pci_get_msi_irq(struct device *dev, unsigned int vector)
|
||||||
{
|
{
|
||||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||||
|
|
@ -834,6 +905,8 @@ static int ath11k_pci_power_up(struct ath11k_base *ab)
|
||||||
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
|
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
ath11k_pci_sw_reset(ab_pci->ab);
|
||||||
|
|
||||||
ret = ath11k_mhi_start(ab_pci);
|
ret = ath11k_mhi_start(ab_pci);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
ath11k_err(ab, "failed to start mhi: %d\n", ret);
|
ath11k_err(ab, "failed to start mhi: %d\n", ret);
|
||||||
|
|
@ -848,6 +921,8 @@ static void ath11k_pci_power_down(struct ath11k_base *ab)
|
||||||
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
|
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
|
||||||
|
|
||||||
ath11k_mhi_stop(ab_pci);
|
ath11k_mhi_stop(ab_pci);
|
||||||
|
ath11k_pci_force_wake(ab_pci->ab);
|
||||||
|
ath11k_pci_sw_reset(ab_pci->ab);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ath11k_pci_kill_tasklets(struct ath11k_base *ab)
|
static void ath11k_pci_kill_tasklets(struct ath11k_base *ab)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,21 @@
|
||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
|
||||||
|
#define PCIE_SOC_GLOBAL_RESET 0x3008
|
||||||
|
#define PCIE_SOC_GLOBAL_RESET_V 1
|
||||||
|
|
||||||
|
#define WLAON_WARM_SW_ENTRY 0x1f80504
|
||||||
|
#define WLAON_SOC_RESET_CAUSE_REG 0x01f8060c
|
||||||
|
|
||||||
|
#define PCIE_Q6_COOKIE_ADDR 0x01f80500
|
||||||
|
#define PCIE_Q6_COOKIE_DATA 0xc0000000
|
||||||
|
|
||||||
|
/* register to wake the UMAC from power collapse */
|
||||||
|
#define PCIE_SCRATCH_0_SOC_PCIE_REG 0x4040
|
||||||
|
|
||||||
|
/* register used for handshake mechanism to validate UMAC is awake */
|
||||||
|
#define PCIE_SOC_WAKE_PCIE_LOCAL_REG 0x3004
|
||||||
|
|
||||||
struct ath11k_msi_user {
|
struct ath11k_msi_user {
|
||||||
char *name;
|
char *name;
|
||||||
int num_vectors;
|
int num_vectors;
|
||||||
|
|
@ -44,5 +59,7 @@ int ath11k_pci_get_user_msi_assignment(struct ath11k_pci *ar_pci, char *user_nam
|
||||||
int *num_vectors, u32 *user_base_data,
|
int *num_vectors, u32 *user_base_data,
|
||||||
u32 *base_vector);
|
u32 *base_vector);
|
||||||
int ath11k_pci_get_msi_irq(struct device *dev, unsigned int vector);
|
int ath11k_pci_get_msi_irq(struct device *dev, unsigned int vector);
|
||||||
|
void ath11k_pci_write32(struct ath11k_base *ab, u32 offset, u32 value);
|
||||||
|
u32 ath11k_pci_read32(struct ath11k_base *ab, u32 offset);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user