diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath11k/core.c')
-rw-r--r-- | drivers/net/wireless/ath/ath11k/core.c | 107 |
1 files changed, 75 insertions, 32 deletions
diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 6e9f16f01ebd..3cc817a3b4a4 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -906,12 +906,6 @@ int ath11k_core_suspend(struct ath11k_base *ab) return ret; } - ret = ath11k_wow_enable(ab); - if (ret) { - ath11k_warn(ab, "failed to enable wow during suspend: %d\n", ret); - return ret; - } - ret = ath11k_dp_rx_pktlog_stop(ab, false); if (ret) { ath11k_warn(ab, "failed to stop dp rx pktlog during suspend: %d\n", @@ -922,29 +916,85 @@ int ath11k_core_suspend(struct ath11k_base *ab) ath11k_ce_stop_shadow_timers(ab); ath11k_dp_stop_shadow_timers(ab); + /* PM framework skips suspend_late/resume_early callbacks + * if other devices report errors in their suspend callbacks. + * However ath11k_core_resume() would still be called because + * here we return success thus kernel put us on dpm_suspended_list. + * Since we won't go through a power down/up cycle, there is + * no chance to call complete(&ab->restart_completed) in + * ath11k_core_restart(), making ath11k_core_resume() timeout. + * So call it here to avoid this issue. This also works in case + * no error happens thus suspend_late/resume_early get called, + * because it will be reinitialized in ath11k_core_resume_early(). + */ + complete(&ab->restart_completed); + + return 0; +} +EXPORT_SYMBOL(ath11k_core_suspend); + +int ath11k_core_suspend_late(struct ath11k_base *ab) +{ + struct ath11k_pdev *pdev; + struct ath11k *ar; + + if (!ab->hw_params.supports_suspend) + return -EOPNOTSUPP; + + /* so far single_pdev_only chips have supports_suspend as true + * and only the first pdev is valid. + */ + pdev = ath11k_core_get_single_pdev(ab); + ar = pdev->ar; + if (!ar || ar->state != ATH11K_STATE_OFF) + return 0; + ath11k_hif_irq_disable(ab); ath11k_hif_ce_irq_disable(ab); - ret = ath11k_hif_suspend(ab); - if (ret) { - ath11k_warn(ab, "failed to suspend hif: %d\n", ret); - return ret; - } + ath11k_hif_power_down(ab, true); return 0; } -EXPORT_SYMBOL(ath11k_core_suspend); +EXPORT_SYMBOL(ath11k_core_suspend_late); + +int ath11k_core_resume_early(struct ath11k_base *ab) +{ + int ret; + struct ath11k_pdev *pdev; + struct ath11k *ar; + + if (!ab->hw_params.supports_suspend) + return -EOPNOTSUPP; + + /* so far single_pdev_only chips have supports_suspend as true + * and only the first pdev is valid. + */ + pdev = ath11k_core_get_single_pdev(ab); + ar = pdev->ar; + if (!ar || ar->state != ATH11K_STATE_OFF) + return 0; + + reinit_completion(&ab->restart_completed); + ret = ath11k_hif_power_up(ab); + if (ret) + ath11k_warn(ab, "failed to power up hif during resume: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL(ath11k_core_resume_early); int ath11k_core_resume(struct ath11k_base *ab) { int ret; struct ath11k_pdev *pdev; struct ath11k *ar; + long time_left; if (!ab->hw_params.supports_suspend) return -EOPNOTSUPP; - /* so far signle_pdev_only chips have supports_suspend as true + /* so far single_pdev_only chips have supports_suspend as true * and only the first pdev is valid. */ pdev = ath11k_core_get_single_pdev(ab); @@ -952,29 +1002,19 @@ int ath11k_core_resume(struct ath11k_base *ab) if (!ar || ar->state != ATH11K_STATE_OFF) return 0; - ret = ath11k_hif_resume(ab); - if (ret) { - ath11k_warn(ab, "failed to resume hif during resume: %d\n", ret); - return ret; + time_left = wait_for_completion_timeout(&ab->restart_completed, + ATH11K_RESET_TIMEOUT_HZ); + if (time_left == 0) { + ath11k_warn(ab, "timeout while waiting for restart complete"); + return -ETIMEDOUT; } - ath11k_hif_ce_irq_enable(ab); - ath11k_hif_irq_enable(ab); - ret = ath11k_dp_rx_pktlog_start(ab); - if (ret) { + if (ret) ath11k_warn(ab, "failed to start rx pktlog during resume: %d\n", ret); - return ret; - } - - ret = ath11k_wow_wakeup(ab); - if (ret) { - ath11k_warn(ab, "failed to wakeup wow during resume: %d\n", ret); - return ret; - } - return 0; + return ret; } EXPORT_SYMBOL(ath11k_core_resume); @@ -2072,6 +2112,8 @@ static void ath11k_core_restart(struct work_struct *work) if (!ab->is_reset) ath11k_core_post_reconfigure_recovery(ab); + + complete(&ab->restart_completed); } static void ath11k_core_reset(struct work_struct *work) @@ -2141,7 +2183,7 @@ static void ath11k_core_reset(struct work_struct *work) ath11k_hif_irq_disable(ab); ath11k_hif_ce_irq_disable(ab); - ath11k_hif_power_down(ab); + ath11k_hif_power_down(ab, false); ath11k_hif_power_up(ab); ath11k_dbg(ab, ATH11K_DBG_BOOT, "reset started\n"); @@ -2214,7 +2256,7 @@ void ath11k_core_deinit(struct ath11k_base *ab) mutex_unlock(&ab->core_lock); - ath11k_hif_power_down(ab); + ath11k_hif_power_down(ab, false); ath11k_mac_destroy(ab); ath11k_core_soc_destroy(ab); ath11k_fw_destroy(ab); @@ -2267,6 +2309,7 @@ struct ath11k_base *ath11k_core_alloc(struct device *dev, size_t priv_size, timer_setup(&ab->rx_replenish_retry, ath11k_ce_rx_replenish_retry, 0); init_completion(&ab->htc_suspend); init_completion(&ab->wow.wakeup_completed); + init_completion(&ab->restart_completed); ab->dev = dev; ab->hif.bus = bus; |