Skip to content

Commit 166a490

Browse files
Baochen Qiangkvalo
authored andcommitted
wifi: ath11k: support hibernation
Now that all infrastructure is in place and ath11k is fixed to handle all the corner cases, power down the ath11k firmware during suspend and power it back up during resume. This fixes the problem when using hibernation with ath11k PCI devices. For suspend, two conditions needs to be satisfied: 1. since MHI channel unprepare would be done in late suspend stage, ath11k needs to get all QMI-dependent things done before that stage. 2. and because unprepare MHI channels requires a working MHI stack, ath11k is not allowed to call mhi_power_down() until that finishes. So the original suspend callback is separated into two parts: the first part handles all QMI-dependent things in suspend callback; while the second part powers down MHI in suspend_late callback. This is valid because kernel calls ath11k's suspend callback before all suspend_late callbacks, making the first condition happy. And because MHI devices are children of ath11k device (ab->dev), kernel guarantees that ath11k's suspend_late callback is called after QRTR's suspend_late callback, this satisfies the second condition. Above analysis also applies to resume process. so the original resume callback is separated into two parts: the first part powers up MHI stack in resume_early callback, this guarantees MHI stack is working when QRTR tries to prepare MHI channels (kernel calls QRTR's resume_early callback after ath11k's resume_early callback, due to the child-father relationship); the second part waits for the completion of restart, which won't fail now since MHI channels are ready for use by QMI. Another notable change is in power down path, we tell mhi_power_down() to not to destroy MHI devices, making it possible for QRTR to help unprepare/prepare MHI channels, and finally get us rid of the probe-defer issue when resume. Also change related code due to interface changes. Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30 Tested-by: Takashi Iwai <[email protected]> Signed-off-by: Baochen Qiang <[email protected]> Acked-by: Jeff Johnson <[email protected]> Signed-off-by: Kalle Valo <[email protected]> Link: https://msgid.link/[email protected]
1 parent e0cd118 commit 166a490

File tree

8 files changed

+143
-53
lines changed

8 files changed

+143
-53
lines changed

drivers/net/wireless/ath/ath11k/ahb.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// SPDX-License-Identifier: BSD-3-Clause-Clear
22
/*
33
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
4-
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4+
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
55
*/
66

77
#include <linux/module.h>
@@ -413,7 +413,7 @@ static int ath11k_ahb_power_up(struct ath11k_base *ab)
413413
return ret;
414414
}
415415

416-
static void ath11k_ahb_power_down(struct ath11k_base *ab)
416+
static void ath11k_ahb_power_down(struct ath11k_base *ab, bool is_suspend)
417417
{
418418
struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);
419419

@@ -1256,7 +1256,7 @@ static void ath11k_ahb_remove(struct platform_device *pdev)
12561256
struct ath11k_base *ab = platform_get_drvdata(pdev);
12571257

12581258
if (test_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags)) {
1259-
ath11k_ahb_power_down(ab);
1259+
ath11k_ahb_power_down(ab, false);
12601260
ath11k_debugfs_soc_destroy(ab);
12611261
ath11k_qmi_deinit_service(ab);
12621262
goto qmi_fail;

drivers/net/wireless/ath/ath11k/core.c

Lines changed: 75 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -906,12 +906,6 @@ int ath11k_core_suspend(struct ath11k_base *ab)
906906
return ret;
907907
}
908908

909-
ret = ath11k_wow_enable(ab);
910-
if (ret) {
911-
ath11k_warn(ab, "failed to enable wow during suspend: %d\n", ret);
912-
return ret;
913-
}
914-
915909
ret = ath11k_dp_rx_pktlog_stop(ab, false);
916910
if (ret) {
917911
ath11k_warn(ab, "failed to stop dp rx pktlog during suspend: %d\n",
@@ -922,59 +916,105 @@ int ath11k_core_suspend(struct ath11k_base *ab)
922916
ath11k_ce_stop_shadow_timers(ab);
923917
ath11k_dp_stop_shadow_timers(ab);
924918

919+
/* PM framework skips suspend_late/resume_early callbacks
920+
* if other devices report errors in their suspend callbacks.
921+
* However ath11k_core_resume() would still be called because
922+
* here we return success thus kernel put us on dpm_suspended_list.
923+
* Since we won't go through a power down/up cycle, there is
924+
* no chance to call complete(&ab->restart_completed) in
925+
* ath11k_core_restart(), making ath11k_core_resume() timeout.
926+
* So call it here to avoid this issue. This also works in case
927+
* no error happens thus suspend_late/resume_early get called,
928+
* because it will be reinitialized in ath11k_core_resume_early().
929+
*/
930+
complete(&ab->restart_completed);
931+
932+
return 0;
933+
}
934+
EXPORT_SYMBOL(ath11k_core_suspend);
935+
936+
int ath11k_core_suspend_late(struct ath11k_base *ab)
937+
{
938+
struct ath11k_pdev *pdev;
939+
struct ath11k *ar;
940+
941+
if (!ab->hw_params.supports_suspend)
942+
return -EOPNOTSUPP;
943+
944+
/* so far single_pdev_only chips have supports_suspend as true
945+
* and only the first pdev is valid.
946+
*/
947+
pdev = ath11k_core_get_single_pdev(ab);
948+
ar = pdev->ar;
949+
if (!ar || ar->state != ATH11K_STATE_OFF)
950+
return 0;
951+
925952
ath11k_hif_irq_disable(ab);
926953
ath11k_hif_ce_irq_disable(ab);
927954

928-
ret = ath11k_hif_suspend(ab);
929-
if (ret) {
930-
ath11k_warn(ab, "failed to suspend hif: %d\n", ret);
931-
return ret;
932-
}
955+
ath11k_hif_power_down(ab, true);
933956

934957
return 0;
935958
}
936-
EXPORT_SYMBOL(ath11k_core_suspend);
959+
EXPORT_SYMBOL(ath11k_core_suspend_late);
960+
961+
int ath11k_core_resume_early(struct ath11k_base *ab)
962+
{
963+
int ret;
964+
struct ath11k_pdev *pdev;
965+
struct ath11k *ar;
966+
967+
if (!ab->hw_params.supports_suspend)
968+
return -EOPNOTSUPP;
969+
970+
/* so far single_pdev_only chips have supports_suspend as true
971+
* and only the first pdev is valid.
972+
*/
973+
pdev = ath11k_core_get_single_pdev(ab);
974+
ar = pdev->ar;
975+
if (!ar || ar->state != ATH11K_STATE_OFF)
976+
return 0;
977+
978+
reinit_completion(&ab->restart_completed);
979+
ret = ath11k_hif_power_up(ab);
980+
if (ret)
981+
ath11k_warn(ab, "failed to power up hif during resume: %d\n", ret);
982+
983+
return ret;
984+
}
985+
EXPORT_SYMBOL(ath11k_core_resume_early);
937986

938987
int ath11k_core_resume(struct ath11k_base *ab)
939988
{
940989
int ret;
941990
struct ath11k_pdev *pdev;
942991
struct ath11k *ar;
992+
long time_left;
943993

944994
if (!ab->hw_params.supports_suspend)
945995
return -EOPNOTSUPP;
946996

947-
/* so far signle_pdev_only chips have supports_suspend as true
997+
/* so far single_pdev_only chips have supports_suspend as true
948998
* and only the first pdev is valid.
949999
*/
9501000
pdev = ath11k_core_get_single_pdev(ab);
9511001
ar = pdev->ar;
9521002
if (!ar || ar->state != ATH11K_STATE_OFF)
9531003
return 0;
9541004

955-
ret = ath11k_hif_resume(ab);
956-
if (ret) {
957-
ath11k_warn(ab, "failed to resume hif during resume: %d\n", ret);
958-
return ret;
1005+
time_left = wait_for_completion_timeout(&ab->restart_completed,
1006+
ATH11K_RESET_TIMEOUT_HZ);
1007+
if (time_left == 0) {
1008+
ath11k_warn(ab, "timeout while waiting for restart complete");
1009+
return -ETIMEDOUT;
9591010
}
9601011

961-
ath11k_hif_ce_irq_enable(ab);
962-
ath11k_hif_irq_enable(ab);
963-
9641012
ret = ath11k_dp_rx_pktlog_start(ab);
965-
if (ret) {
1013+
if (ret)
9661014
ath11k_warn(ab, "failed to start rx pktlog during resume: %d\n",
9671015
ret);
968-
return ret;
969-
}
970-
971-
ret = ath11k_wow_wakeup(ab);
972-
if (ret) {
973-
ath11k_warn(ab, "failed to wakeup wow during resume: %d\n", ret);
974-
return ret;
975-
}
9761016

977-
return 0;
1017+
return ret;
9781018
}
9791019
EXPORT_SYMBOL(ath11k_core_resume);
9801020

@@ -2072,6 +2112,8 @@ static void ath11k_core_restart(struct work_struct *work)
20722112

20732113
if (!ab->is_reset)
20742114
ath11k_core_post_reconfigure_recovery(ab);
2115+
2116+
complete(&ab->restart_completed);
20752117
}
20762118

20772119
static void ath11k_core_reset(struct work_struct *work)
@@ -2141,7 +2183,7 @@ static void ath11k_core_reset(struct work_struct *work)
21412183
ath11k_hif_irq_disable(ab);
21422184
ath11k_hif_ce_irq_disable(ab);
21432185

2144-
ath11k_hif_power_down(ab);
2186+
ath11k_hif_power_down(ab, false);
21452187
ath11k_hif_power_up(ab);
21462188

21472189
ath11k_dbg(ab, ATH11K_DBG_BOOT, "reset started\n");
@@ -2214,7 +2256,7 @@ void ath11k_core_deinit(struct ath11k_base *ab)
22142256

22152257
mutex_unlock(&ab->core_lock);
22162258

2217-
ath11k_hif_power_down(ab);
2259+
ath11k_hif_power_down(ab, false);
22182260
ath11k_mac_destroy(ab);
22192261
ath11k_core_soc_destroy(ab);
22202262
ath11k_fw_destroy(ab);
@@ -2267,6 +2309,7 @@ struct ath11k_base *ath11k_core_alloc(struct device *dev, size_t priv_size,
22672309
timer_setup(&ab->rx_replenish_retry, ath11k_ce_rx_replenish_retry, 0);
22682310
init_completion(&ab->htc_suspend);
22692311
init_completion(&ab->wow.wakeup_completed);
2312+
init_completion(&ab->restart_completed);
22702313

22712314
ab->dev = dev;
22722315
ab->hif.bus = bus;

drivers/net/wireless/ath/ath11k/core.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
22
/*
33
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
4-
* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4+
* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
55
*/
66

77
#ifndef ATH11K_CORE_H
@@ -1033,6 +1033,8 @@ struct ath11k_base {
10331033
DECLARE_BITMAP(fw_features, ATH11K_FW_FEATURE_COUNT);
10341034
} fw;
10351035

1036+
struct completion restart_completed;
1037+
10361038
#ifdef CONFIG_NL80211_TESTMODE
10371039
struct {
10381040
u32 data_pos;
@@ -1232,8 +1234,10 @@ void ath11k_core_free_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd);
12321234
int ath11k_core_check_dt(struct ath11k_base *ath11k);
12331235
int ath11k_core_check_smbios(struct ath11k_base *ab);
12341236
void ath11k_core_halt(struct ath11k *ar);
1237+
int ath11k_core_resume_early(struct ath11k_base *ab);
12351238
int ath11k_core_resume(struct ath11k_base *ab);
12361239
int ath11k_core_suspend(struct ath11k_base *ab);
1240+
int ath11k_core_suspend_late(struct ath11k_base *ab);
12371241
void ath11k_core_pre_reconfigure_recovery(struct ath11k_base *ab);
12381242
bool ath11k_core_coldboot_cal_support(struct ath11k_base *ab);
12391243

drivers/net/wireless/ath/ath11k/hif.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
22
/*
33
* Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
4-
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4+
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
55
*/
66

77
#ifndef _HIF_H_
@@ -18,7 +18,7 @@ struct ath11k_hif_ops {
1818
int (*start)(struct ath11k_base *ab);
1919
void (*stop)(struct ath11k_base *ab);
2020
int (*power_up)(struct ath11k_base *ab);
21-
void (*power_down)(struct ath11k_base *ab);
21+
void (*power_down)(struct ath11k_base *ab, bool is_suspend);
2222
int (*suspend)(struct ath11k_base *ab);
2323
int (*resume)(struct ath11k_base *ab);
2424
int (*map_service_to_pipe)(struct ath11k_base *ab, u16 service_id,
@@ -67,12 +67,18 @@ static inline void ath11k_hif_irq_disable(struct ath11k_base *ab)
6767

6868
static inline int ath11k_hif_power_up(struct ath11k_base *ab)
6969
{
70+
if (!ab->hif.ops->power_up)
71+
return -EOPNOTSUPP;
72+
7073
return ab->hif.ops->power_up(ab);
7174
}
7275

73-
static inline void ath11k_hif_power_down(struct ath11k_base *ab)
76+
static inline void ath11k_hif_power_down(struct ath11k_base *ab, bool is_suspend)
7477
{
75-
ab->hif.ops->power_down(ab);
78+
if (!ab->hif.ops->power_down)
79+
return;
80+
81+
ab->hif.ops->power_down(ab, is_suspend);
7682
}
7783

7884
static inline int ath11k_hif_suspend(struct ath11k_base *ab)

drivers/net/wireless/ath/ath11k/mhi.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -453,9 +453,17 @@ int ath11k_mhi_start(struct ath11k_pci *ab_pci)
453453
return 0;
454454
}
455455

456-
void ath11k_mhi_stop(struct ath11k_pci *ab_pci)
456+
void ath11k_mhi_stop(struct ath11k_pci *ab_pci, bool is_suspend)
457457
{
458-
mhi_power_down(ab_pci->mhi_ctrl, true);
458+
/* During suspend we need to use mhi_power_down_keep_dev()
459+
* workaround, otherwise ath11k_core_resume() will timeout
460+
* during resume.
461+
*/
462+
if (is_suspend)
463+
mhi_power_down_keep_dev(ab_pci->mhi_ctrl, true);
464+
else
465+
mhi_power_down(ab_pci->mhi_ctrl, true);
466+
459467
mhi_unprepare_after_power_down(ab_pci->mhi_ctrl);
460468
}
461469

drivers/net/wireless/ath/ath11k/mhi.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
22
/*
33
* Copyright (c) 2020 The Linux Foundation. All rights reserved.
4-
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
4+
* Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
55
*/
66
#ifndef _ATH11K_MHI_H
77
#define _ATH11K_MHI_H
@@ -18,13 +18,12 @@
1818
#define MHICTRL_RESET_MASK 0x2
1919

2020
int ath11k_mhi_start(struct ath11k_pci *ar_pci);
21-
void ath11k_mhi_stop(struct ath11k_pci *ar_pci);
21+
void ath11k_mhi_stop(struct ath11k_pci *ar_pci, bool is_suspend);
2222
int ath11k_mhi_register(struct ath11k_pci *ar_pci);
2323
void ath11k_mhi_unregister(struct ath11k_pci *ar_pci);
2424
void ath11k_mhi_set_mhictrl_reset(struct ath11k_base *ab);
2525
void ath11k_mhi_clear_vector(struct ath11k_base *ab);
2626

2727
int ath11k_mhi_suspend(struct ath11k_pci *ar_pci);
2828
int ath11k_mhi_resume(struct ath11k_pci *ar_pci);
29-
3029
#endif

drivers/net/wireless/ath/ath11k/pci.c

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -638,7 +638,7 @@ static int ath11k_pci_power_up(struct ath11k_base *ab)
638638
return 0;
639639
}
640640

641-
static void ath11k_pci_power_down(struct ath11k_base *ab)
641+
static void ath11k_pci_power_down(struct ath11k_base *ab, bool is_suspend)
642642
{
643643
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
644644

@@ -649,7 +649,7 @@ static void ath11k_pci_power_down(struct ath11k_base *ab)
649649

650650
ath11k_pci_msi_disable(ab_pci);
651651

652-
ath11k_mhi_stop(ab_pci);
652+
ath11k_mhi_stop(ab_pci, is_suspend);
653653
clear_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags);
654654
ath11k_pci_sw_reset(ab_pci->ab, false);
655655
}
@@ -970,7 +970,7 @@ static void ath11k_pci_remove(struct pci_dev *pdev)
970970
ath11k_pci_set_irq_affinity_hint(ab_pci, NULL);
971971

972972
if (test_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags)) {
973-
ath11k_pci_power_down(ab);
973+
ath11k_pci_power_down(ab, false);
974974
ath11k_debugfs_soc_destroy(ab);
975975
ath11k_qmi_deinit_service(ab);
976976
goto qmi_fail;
@@ -998,7 +998,7 @@ static void ath11k_pci_shutdown(struct pci_dev *pdev)
998998
struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
999999

10001000
ath11k_pci_set_irq_affinity_hint(ab_pci, NULL);
1001-
ath11k_pci_power_down(ab);
1001+
ath11k_pci_power_down(ab, false);
10021002
}
10031003

10041004
static __maybe_unused int ath11k_pci_pm_suspend(struct device *dev)
@@ -1035,9 +1035,39 @@ static __maybe_unused int ath11k_pci_pm_resume(struct device *dev)
10351035
return ret;
10361036
}
10371037

1038-
static SIMPLE_DEV_PM_OPS(ath11k_pci_pm_ops,
1039-
ath11k_pci_pm_suspend,
1040-
ath11k_pci_pm_resume);
1038+
static __maybe_unused int ath11k_pci_pm_suspend_late(struct device *dev)
1039+
{
1040+
struct ath11k_base *ab = dev_get_drvdata(dev);
1041+
int ret;
1042+
1043+
ret = ath11k_core_suspend_late(ab);
1044+
if (ret)
1045+
ath11k_warn(ab, "failed to late suspend core: %d\n", ret);
1046+
1047+
/* Similar to ath11k_pci_pm_suspend(), we return success here
1048+
* even error happens, to allow system suspend/hibernation survive.
1049+
*/
1050+
return 0;
1051+
}
1052+
1053+
static __maybe_unused int ath11k_pci_pm_resume_early(struct device *dev)
1054+
{
1055+
struct ath11k_base *ab = dev_get_drvdata(dev);
1056+
int ret;
1057+
1058+
ret = ath11k_core_resume_early(ab);
1059+
if (ret)
1060+
ath11k_warn(ab, "failed to early resume core: %d\n", ret);
1061+
1062+
return ret;
1063+
}
1064+
1065+
static const struct dev_pm_ops __maybe_unused ath11k_pci_pm_ops = {
1066+
SET_SYSTEM_SLEEP_PM_OPS(ath11k_pci_pm_suspend,
1067+
ath11k_pci_pm_resume)
1068+
SET_LATE_SYSTEM_SLEEP_PM_OPS(ath11k_pci_pm_suspend_late,
1069+
ath11k_pci_pm_resume_early)
1070+
};
10411071

10421072
static struct pci_driver ath11k_pci_driver = {
10431073
.name = "ath11k_pci",

0 commit comments

Comments
 (0)