public inbox for linux-wireless@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH ath-next v2 0/6] wifi: ath11k: bring hibernation support back
@ 2025-03-26  1:33 Baochen Qiang
  2025-03-26  1:33 ` [PATCH ath-next v2 1/6] wifi: ath11k: determine PM policy based on machine model Baochen Qiang
                   ` (6 more replies)
  0 siblings, 7 replies; 9+ messages in thread
From: Baochen Qiang @ 2025-03-26  1:33 UTC (permalink / raw)
  To: Johannes Berg, Jeff Johnson
  Cc: linux-wireless, ath11k, linux-kernel, Baochen Qiang,
	Muhammad Usama Anjum

To handle the Lenovo unexpected wakeup issue [1], previously we revert
commit 166a490f59ac ("wifi: ath11k: support hibernation"). However we
need to bring it back, of course with additional changes such that Lenovo
machines would not break.

For suspend (S3), as those machines work well in WoWLAN mode, the thought
here is that we put WLAN target into WoWLAN mode on those machines while
into non-WoWLAN mode (which is done in the reverted commit) on other
machines. This requires us to identify Lenovo machines from others. For
that purpose, read machine info from DMI interface, match it against all
known affected machines. If there is a match, choose WoWLAN suspend mode,
else choose non-WoWLAN mode. This is done in patches [1 - 4/6]

For hibernation (S4), non-WoWLAN mode is chosen for all machines. The
unexpected wakeup issue should not happen in this mode, since WLAN target
power is cut off. To know if the system is going to suspend or to
hibernate, register a notifier such that kernel can notify us of such
infomation. This is done in patch [5/6]

The last patch bring the reverted "wifi: ath11k: restore country code
during resume" commit back.

[1] https://bugzilla.kernel.org/show_bug.cgi?id=219196

Signed-off-by: Baochen Qiang <quic_bqiang@quicinc.com>
---
Changes in v2:
- fix compile warnings
- support hibernation on Lenovo machines listed in quirk table
- Link to v1: https://lore.kernel.org/linux-wireless/20250320023003.65028-1-quic_bqiang@quicinc.com/

---
Baochen Qiang (6):
      wifi: ath11k: determine PM policy based on machine model
      wifi: ath11k: introduce ath11k_core_continue_suspend_resume()
      wifi: ath11k: refactor ath11k_core_suspend/_resume()
      wifi: ath11k: support non-WoWLAN mode suspend as well
      wifi: ath11l: choose default PM policy for hibernation
      Reapply "wifi: ath11k: restore country code during resume"

 drivers/net/wireless/ath/ath11k/ahb.c  |   4 +-
 drivers/net/wireless/ath/ath11k/core.c | 294 ++++++++++++++++++++++++++++++---
 drivers/net/wireless/ath/ath11k/core.h |  16 ++
 drivers/net/wireless/ath/ath11k/hif.h  |  14 +-
 drivers/net/wireless/ath/ath11k/mhi.c  |  14 +-
 drivers/net/wireless/ath/ath11k/mhi.h  |   5 +-
 drivers/net/wireless/ath/ath11k/pci.c  |  45 ++++-
 drivers/net/wireless/ath/ath11k/qmi.c  |   4 +-
 8 files changed, 350 insertions(+), 46 deletions(-)
---
base-commit: b6f473c96421b8b451a8df8ccb620bcd71d4b3f4
change-id: 20250324-ath11k-bring-hibernation-back-e11ad8e82adf

Best regards,
-- 
Baochen Qiang <quic_bqiang@quicinc.com>


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH ath-next v2 1/6] wifi: ath11k: determine PM policy based on machine model
  2025-03-26  1:33 [PATCH ath-next v2 0/6] wifi: ath11k: bring hibernation support back Baochen Qiang
@ 2025-03-26  1:33 ` Baochen Qiang
  2025-03-26  1:33 ` [PATCH ath-next v2 2/6] wifi: ath11k: introduce ath11k_core_continue_suspend_resume() Baochen Qiang
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Baochen Qiang @ 2025-03-26  1:33 UTC (permalink / raw)
  To: Johannes Berg, Jeff Johnson
  Cc: linux-wireless, ath11k, linux-kernel, Baochen Qiang,
	Muhammad Usama Anjum

To handle the Lenovo unexpected wakeup issue [1], previously we revert
commit 166a490f59ac ("wifi: ath11k: support hibernation"). So currently
WLAN target is put into WoWLAN mode during suspend. This is a temporary
solution as it does not work on machines where WLAN power is cut off.

The thought here is that we do WoWLAN suspend on Lenovo machines while
do non-WoWLAN suspend (which is done in the reverted commit) on other
machines. This requires us to identify Lenovo machines from others.
For that purpose, read board vendor and product name from DMI interface,
match it against all known affected machines. If there is a match, choose
WoWLAN suspend mode, else choose non-WoWLAN mode. Save the mode in ab
for later reference.

[1] https://bugzilla.kernel.org/show_bug.cgi?id=219196

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

Tested-by: Muhammad Usama Anjum <usama.anjum@collabora.com>
Signed-off-by: Baochen Qiang <quic_bqiang@quicinc.com>
---
 drivers/net/wireless/ath/ath11k/core.c | 55 ++++++++++++++++++++++++++++++++++
 drivers/net/wireless/ath/ath11k/core.h |  7 +++++
 2 files changed, 62 insertions(+)

diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c
index 3d39ff85ba94ad7229764045e7e6dcab99dd51d5..f3a29f5d2381b53be895a2687bc7ccc81ec1afbf 100644
--- a/drivers/net/wireless/ath/ath11k/core.c
+++ b/drivers/net/wireless/ath/ath11k/core.c
@@ -907,6 +907,52 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
 	},
 };
 
+static const struct dmi_system_id ath11k_pm_quirk_table[] = {
+	{
+		.driver_data = (void *)ATH11K_PM_WOW,
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "21J4"),
+		},
+	},
+	{
+		.driver_data = (void *)ATH11K_PM_WOW,
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "21K4"),
+		},
+	},
+	{
+		.driver_data = (void *)ATH11K_PM_WOW,
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "21K6"),
+		},
+	},
+	{
+		.driver_data = (void *)ATH11K_PM_WOW,
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "21K8"),
+		},
+	},
+	{
+		.driver_data = (void *)ATH11K_PM_WOW,
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "21KA"),
+		},
+	},
+	{
+		.driver_data = (void *)ATH11K_PM_WOW,
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "21F9"),
+		},
+	},
+	{}
+};
+
 static inline struct ath11k_pdev *ath11k_core_get_single_pdev(struct ath11k_base *ab)
 {
 	WARN_ON(!ab->hw_params.single_pdev_only);
@@ -2327,8 +2373,17 @@ EXPORT_SYMBOL(ath11k_core_pre_init);
 
 int ath11k_core_init(struct ath11k_base *ab)
 {
+	const struct dmi_system_id *dmi_id;
 	int ret;
 
+	dmi_id = dmi_first_match(ath11k_pm_quirk_table);
+	if (dmi_id)
+		ab->pm_policy = (kernel_ulong_t)dmi_id->driver_data;
+	else
+		ab->pm_policy = ATH11K_PM_DEFAULT;
+
+	ath11k_dbg(ab, ATH11K_DBG_BOOT, "pm policy %u\n", ab->pm_policy);
+
 	ret = ath11k_core_soc_create(ab);
 	if (ret) {
 		ath11k_err(ab, "failed to create soc core: %d\n", ret);
diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h
index 1a3d0de4afde833ec1ce4e3bcef3b7c8955a9c3d..df2b0cb2f0b5a700806cd261ffeb31f5da0741f5 100644
--- a/drivers/net/wireless/ath/ath11k/core.h
+++ b/drivers/net/wireless/ath/ath11k/core.h
@@ -892,6 +892,11 @@ struct ath11k_msi_config {
 	u16 hw_rev;
 };
 
+enum ath11k_pm_policy {
+	ATH11K_PM_DEFAULT,
+	ATH11K_PM_WOW,
+};
+
 /* Master structure to hold the hw data which may be used in core module */
 struct ath11k_base {
 	enum ath11k_hw_rev hw_rev;
@@ -1058,6 +1063,8 @@ struct ath11k_base {
 	} testmode;
 #endif
 
+	enum ath11k_pm_policy pm_policy;
+
 	/* must be last */
 	u8 drv_priv[] __aligned(sizeof(void *));
 };

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH ath-next v2 2/6] wifi: ath11k: introduce ath11k_core_continue_suspend_resume()
  2025-03-26  1:33 [PATCH ath-next v2 0/6] wifi: ath11k: bring hibernation support back Baochen Qiang
  2025-03-26  1:33 ` [PATCH ath-next v2 1/6] wifi: ath11k: determine PM policy based on machine model Baochen Qiang
@ 2025-03-26  1:33 ` Baochen Qiang
  2025-03-26  1:33 ` [PATCH ath-next v2 3/6] wifi: ath11k: refactor ath11k_core_suspend/_resume() Baochen Qiang
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Baochen Qiang @ 2025-03-26  1:33 UTC (permalink / raw)
  To: Johannes Berg, Jeff Johnson
  Cc: linux-wireless, ath11k, linux-kernel, Baochen Qiang,
	Muhammad Usama Anjum

Currently ath11k_core_suspend() and ath11k_core_resume() have the same
check before going on. In upcoming patches the check is needed by some
newly added functions as well.

To avoid duplicate code, introduce a new helper
ath11k_core_continue_suspend_resume() which does such check internally.
Callers can decide whether to go on based on its return value.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

Tested-by: Muhammad Usama Anjum <usama.anjum@collabora.com>
Signed-off-by: Baochen Qiang <quic_bqiang@quicinc.com>
---
 drivers/net/wireless/ath/ath11k/core.c | 53 +++++++++++++++++-----------------
 1 file changed, 27 insertions(+), 26 deletions(-)

diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c
index f3a29f5d2381b53be895a2687bc7ccc81ec1afbf..39922383f28ce0aa3e23281da050a8cffd3397ea 100644
--- a/drivers/net/wireless/ath/ath11k/core.c
+++ b/drivers/net/wireless/ath/ath11k/core.c
@@ -953,13 +953,6 @@ static const struct dmi_system_id ath11k_pm_quirk_table[] = {
 	{}
 };
 
-static inline struct ath11k_pdev *ath11k_core_get_single_pdev(struct ath11k_base *ab)
-{
-	WARN_ON(!ab->hw_params.single_pdev_only);
-
-	return &ab->pdevs[0];
-}
-
 void ath11k_fw_stats_pdevs_free(struct list_head *head)
 {
 	struct ath11k_fw_stats_pdev *i, *tmp;
@@ -1018,23 +1011,37 @@ bool ath11k_core_coldboot_cal_support(struct ath11k_base *ab)
 		return ab->hw_params.coldboot_cal_mm;
 }
 
-int ath11k_core_suspend(struct ath11k_base *ab)
+/* Check if we need to continue with suspend/resume operation.
+ * Return:
+ *	a negative value: error happens and don't continue.
+ *	0:  no error but don't continue.
+ *	positive value: no error and do continue.
+ */
+static int ath11k_core_continue_suspend_resume(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.
+	 * so pass 0 as a dummy pdev_id here.
 	 */
-	pdev = ath11k_core_get_single_pdev(ab);
-	ar = pdev->ar;
+	ar = ab->pdevs[0].ar;
 	if (!ar || ar->state != ATH11K_STATE_OFF)
 		return 0;
 
+	return 1;
+}
+
+int ath11k_core_suspend(struct ath11k_base *ab)
+{
+	int ret;
+
+	ret = ath11k_core_continue_suspend_resume(ab);
+	if (ret <= 0)
+		return ret;
+
 	ret = ath11k_dp_rx_pktlog_stop(ab, true);
 	if (ret) {
 		ath11k_warn(ab, "failed to stop dp rx (and timer) pktlog during suspend: %d\n",
@@ -1042,7 +1049,10 @@ int ath11k_core_suspend(struct ath11k_base *ab)
 		return ret;
 	}
 
-	ret = ath11k_mac_wait_tx_complete(ar);
+	/* So far only single_pdev_only devices can reach here,
+	 * so it is valid to handle the first, and the only, pdev.
+	 */
+	ret = ath11k_mac_wait_tx_complete(ab->pdevs[0].ar);
 	if (ret) {
 		ath11k_warn(ab, "failed to wait tx complete: %d\n", ret);
 		return ret;
@@ -1080,19 +1090,10 @@ EXPORT_SYMBOL(ath11k_core_suspend);
 int ath11k_core_resume(struct ath11k_base *ab)
 {
 	int ret;
-	struct ath11k_pdev *pdev;
-	struct ath11k *ar;
 
-	if (!ab->hw_params.supports_suspend)
-		return -EOPNOTSUPP;
-
-	/* so far signle_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;
+	ret = ath11k_core_continue_suspend_resume(ab);
+	if (ret <= 0)
+		return ret;
 
 	ret = ath11k_hif_resume(ab);
 	if (ret) {

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH ath-next v2 3/6] wifi: ath11k: refactor ath11k_core_suspend/_resume()
  2025-03-26  1:33 [PATCH ath-next v2 0/6] wifi: ath11k: bring hibernation support back Baochen Qiang
  2025-03-26  1:33 ` [PATCH ath-next v2 1/6] wifi: ath11k: determine PM policy based on machine model Baochen Qiang
  2025-03-26  1:33 ` [PATCH ath-next v2 2/6] wifi: ath11k: introduce ath11k_core_continue_suspend_resume() Baochen Qiang
@ 2025-03-26  1:33 ` Baochen Qiang
  2025-03-26  1:33 ` [PATCH ath-next v2 4/6] wifi: ath11k: support non-WoWLAN mode suspend as well Baochen Qiang
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Baochen Qiang @ 2025-03-26  1:33 UTC (permalink / raw)
  To: Johannes Berg, Jeff Johnson
  Cc: linux-wireless, ath11k, linux-kernel, Baochen Qiang,
	Muhammad Usama Anjum

Due to [1] we currently put WLAN target into WoWLAN mode regardless of
machine models. In upcoming patches we will support another mode, and
finally which mode is chosen depends on the exact machine model.

To prepare for such change, refactor the actual WoWLAN stuff in
ath11k_core_suspend() into a new helper ath11k_core_suspend_wow(), this
increase the code readability when the new suspend mode is added in
upcoming patches.

Same change applies to ath11k_core_resume();

[1] https://bugzilla.kernel.org/show_bug.cgi?id=219196

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

Tested-by: Muhammad Usama Anjum <usama.anjum@collabora.com>
Signed-off-by: Baochen Qiang <quic_bqiang@quicinc.com>
---
 drivers/net/wireless/ath/ath11k/core.c | 28 +++++++++++++++++++++-------
 1 file changed, 21 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c
index 39922383f28ce0aa3e23281da050a8cffd3397ea..e9077b1c4c522291c0b87f23d6f1fcfffebe7509 100644
--- a/drivers/net/wireless/ath/ath11k/core.c
+++ b/drivers/net/wireless/ath/ath11k/core.c
@@ -1034,14 +1034,10 @@ static int ath11k_core_continue_suspend_resume(struct ath11k_base *ab)
 	return 1;
 }
 
-int ath11k_core_suspend(struct ath11k_base *ab)
+static int ath11k_core_suspend_wow(struct ath11k_base *ab)
 {
 	int ret;
 
-	ret = ath11k_core_continue_suspend_resume(ab);
-	if (ret <= 0)
-		return ret;
-
 	ret = ath11k_dp_rx_pktlog_stop(ab, true);
 	if (ret) {
 		ath11k_warn(ab, "failed to stop dp rx (and timer) pktlog during suspend: %d\n",
@@ -1085,9 +1081,8 @@ int ath11k_core_suspend(struct ath11k_base *ab)
 
 	return 0;
 }
-EXPORT_SYMBOL(ath11k_core_suspend);
 
-int ath11k_core_resume(struct ath11k_base *ab)
+int ath11k_core_suspend(struct ath11k_base *ab)
 {
 	int ret;
 
@@ -1095,6 +1090,14 @@ int ath11k_core_resume(struct ath11k_base *ab)
 	if (ret <= 0)
 		return ret;
 
+	return ath11k_core_suspend_wow(ab);
+}
+EXPORT_SYMBOL(ath11k_core_suspend);
+
+static int ath11k_core_resume_wow(struct ath11k_base *ab)
+{
+	int ret;
+
 	ret = ath11k_hif_resume(ab);
 	if (ret) {
 		ath11k_warn(ab, "failed to resume hif during resume: %d\n", ret);
@@ -1119,6 +1122,17 @@ int ath11k_core_resume(struct ath11k_base *ab)
 
 	return 0;
 }
+
+int ath11k_core_resume(struct ath11k_base *ab)
+{
+	int ret;
+
+	ret = ath11k_core_continue_suspend_resume(ab);
+	if (ret <= 0)
+		return ret;
+
+	return ath11k_core_resume_wow(ab);
+}
 EXPORT_SYMBOL(ath11k_core_resume);
 
 static void ath11k_core_check_cc_code_bdfext(const struct dmi_header *hdr, void *data)

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH ath-next v2 4/6] wifi: ath11k: support non-WoWLAN mode suspend as well
  2025-03-26  1:33 [PATCH ath-next v2 0/6] wifi: ath11k: bring hibernation support back Baochen Qiang
                   ` (2 preceding siblings ...)
  2025-03-26  1:33 ` [PATCH ath-next v2 3/6] wifi: ath11k: refactor ath11k_core_suspend/_resume() Baochen Qiang
@ 2025-03-26  1:33 ` Baochen Qiang
  2025-03-26  1:33 ` [PATCH ath-next v2 5/6] wifi: ath11l: choose default PM policy for hibernation Baochen Qiang
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Baochen Qiang @ 2025-03-26  1:33 UTC (permalink / raw)
  To: Johannes Berg, Jeff Johnson
  Cc: linux-wireless, ath11k, linux-kernel, Baochen Qiang,
	Muhammad Usama Anjum

Previously commit 166a490f59ac ("wifi: ath11k: support hibernation") was
reverted due to [1], so currently we only support WoWLAN mode suspend.
This works well in scenarios where WLAN power is sustained during suspend,
however breaks in those where power is cut off.

This change basically brings the reverted commit back, but differs in that
we decide based on the PM policy to choose WoWLAN mode suspend or the
non-WoWLAN mode. As stated in the previous patch for now the PM policy is
determined based on machine models. That said we will choose WoWLAN mode
suspend if we are running on machines listed in ath11k_pm_quirk_table,
otherwise we choose the other one.

[1] https://bugzilla.kernel.org/show_bug.cgi?id=219196

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

Tested-by: Muhammad Usama Anjum <usama.anjum@collabora.com>
Signed-off-by: Baochen Qiang <quic_bqiang@quicinc.com>
---
 drivers/net/wireless/ath/ath11k/ahb.c  |   4 +-
 drivers/net/wireless/ath/ath11k/core.c | 123 +++++++++++++++++++++++++++++++--
 drivers/net/wireless/ath/ath11k/core.h |   4 ++
 drivers/net/wireless/ath/ath11k/hif.h  |  14 ++--
 drivers/net/wireless/ath/ath11k/mhi.c  |  14 +++-
 drivers/net/wireless/ath/ath11k/mhi.h  |   5 +-
 drivers/net/wireless/ath/ath11k/pci.c  |  44 ++++++++++--
 drivers/net/wireless/ath/ath11k/qmi.c  |   4 +-
 8 files changed, 187 insertions(+), 25 deletions(-)

diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c
index eedba3766ba24430e7034fcbae39bd38b0ac5165..511f5f91da0f36f6dfe81a7824bb8a84b5cee3c2 100644
--- a/drivers/net/wireless/ath/ath11k/ahb.c
+++ b/drivers/net/wireless/ath/ath11k/ahb.c
@@ -413,7 +413,7 @@ static int ath11k_ahb_power_up(struct ath11k_base *ab)
 	return ret;
 }
 
-static void ath11k_ahb_power_down(struct ath11k_base *ab)
+static void ath11k_ahb_power_down(struct ath11k_base *ab, bool is_suspend)
 {
 	struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab);
 
@@ -1280,7 +1280,7 @@ static void ath11k_ahb_remove(struct platform_device *pdev)
 	struct ath11k_base *ab = platform_get_drvdata(pdev);
 
 	if (test_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags)) {
-		ath11k_ahb_power_down(ab);
+		ath11k_ahb_power_down(ab, false);
 		ath11k_debugfs_soc_destroy(ab);
 		ath11k_qmi_deinit_service(ab);
 		goto qmi_fail;
diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c
index e9077b1c4c522291c0b87f23d6f1fcfffebe7509..e1c0eca773fc0243aa4d159dd8671f6a4084b29a 100644
--- a/drivers/net/wireless/ath/ath11k/core.c
+++ b/drivers/net/wireless/ath/ath11k/core.c
@@ -1082,6 +1082,52 @@ static int ath11k_core_suspend_wow(struct ath11k_base *ab)
 	return 0;
 }
 
+static int ath11k_core_suspend_default(struct ath11k_base *ab)
+{
+	int ret;
+
+	ret = ath11k_dp_rx_pktlog_stop(ab, true);
+	if (ret) {
+		ath11k_warn(ab, "failed to stop dp rx (and timer) pktlog during suspend: %d\n",
+			    ret);
+		return ret;
+	}
+
+	/* So far only single_pdev_only devices can reach here,
+	 * so it is valid to handle the first, and the only, pdev.
+	 */
+	ret = ath11k_mac_wait_tx_complete(ab->pdevs[0].ar);
+	if (ret) {
+		ath11k_warn(ab, "failed to wait tx complete: %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",
+			    ret);
+		return ret;
+	}
+
+	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;
+}
+
 int ath11k_core_suspend(struct ath11k_base *ab)
 {
 	int ret;
@@ -1090,10 +1136,73 @@ int ath11k_core_suspend(struct ath11k_base *ab)
 	if (ret <= 0)
 		return ret;
 
-	return ath11k_core_suspend_wow(ab);
+	if (ab->pm_policy == ATH11K_PM_WOW)
+		return ath11k_core_suspend_wow(ab);
+
+	return ath11k_core_suspend_default(ab);
 }
 EXPORT_SYMBOL(ath11k_core_suspend);
 
+int ath11k_core_suspend_late(struct ath11k_base *ab)
+{
+	int ret;
+
+	ret = ath11k_core_continue_suspend_resume(ab);
+	if (ret <= 0)
+		return ret;
+
+	if (ab->pm_policy == ATH11K_PM_WOW)
+		return 0;
+
+	ath11k_hif_irq_disable(ab);
+	ath11k_hif_ce_irq_disable(ab);
+
+	ath11k_hif_power_down(ab, true);
+
+	return 0;
+}
+EXPORT_SYMBOL(ath11k_core_suspend_late);
+
+int ath11k_core_resume_early(struct ath11k_base *ab)
+{
+	int ret;
+
+	ret = ath11k_core_continue_suspend_resume(ab);
+	if (ret <= 0)
+		return ret;
+
+	if (ab->pm_policy == ATH11K_PM_WOW)
+		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);
+
+static int ath11k_core_resume_default(struct ath11k_base *ab)
+{
+	long time_left;
+	int 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;
+	}
+
+	ret = ath11k_dp_rx_pktlog_start(ab);
+	if (ret)
+		ath11k_warn(ab, "failed to start rx pktlog during resume: %d\n",
+			    ret);
+
+	return ret;
+}
+
 static int ath11k_core_resume_wow(struct ath11k_base *ab)
 {
 	int ret;
@@ -1131,7 +1240,10 @@ int ath11k_core_resume(struct ath11k_base *ab)
 	if (ret <= 0)
 		return ret;
 
-	return ath11k_core_resume_wow(ab);
+	if (ab->pm_policy == ATH11K_PM_WOW)
+		return ath11k_core_resume_wow(ab);
+
+	return ath11k_core_resume_default(ab);
 }
 EXPORT_SYMBOL(ath11k_core_resume);
 
@@ -2266,6 +2378,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)
@@ -2336,7 +2450,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");
@@ -2418,7 +2532,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);
 }
@@ -2471,6 +2585,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;
diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h
index df2b0cb2f0b5a700806cd261ffeb31f5da0741f5..7a9354e0eadb58730028217960e5bbfe0056c2fb 100644
--- a/drivers/net/wireless/ath/ath11k/core.h
+++ b/drivers/net/wireless/ath/ath11k/core.h
@@ -1055,6 +1055,8 @@ struct ath11k_base {
 		DECLARE_BITMAP(fw_features, ATH11K_FW_FEATURE_COUNT);
 	} fw;
 
+	struct completion restart_completed;
+
 #ifdef CONFIG_NL80211_TESTMODE
 	struct {
 		u32 data_pos;
@@ -1256,8 +1258,10 @@ void ath11k_core_free_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd);
 int ath11k_core_check_dt(struct ath11k_base *ath11k);
 int ath11k_core_check_smbios(struct ath11k_base *ab);
 void ath11k_core_halt(struct ath11k *ar);
+int ath11k_core_resume_early(struct ath11k_base *ab);
 int ath11k_core_resume(struct ath11k_base *ab);
 int ath11k_core_suspend(struct ath11k_base *ab);
+int ath11k_core_suspend_late(struct ath11k_base *ab);
 void ath11k_core_pre_reconfigure_recovery(struct ath11k_base *ab);
 bool ath11k_core_coldboot_cal_support(struct ath11k_base *ab);
 
diff --git a/drivers/net/wireless/ath/ath11k/hif.h b/drivers/net/wireless/ath/ath11k/hif.h
index 770c39ff99b45e93de65c88c13bdf30e75fa8e64..cd9c4b838246815ecb2a10784732c009a5423b6e 100644
--- a/drivers/net/wireless/ath/ath11k/hif.h
+++ b/drivers/net/wireless/ath/ath11k/hif.h
@@ -1,7 +1,7 @@
 /* SPDX-License-Identifier: BSD-3-Clause-Clear */
 /*
  * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
- * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #ifndef _HIF_H_
@@ -18,7 +18,7 @@ struct ath11k_hif_ops {
 	int (*start)(struct ath11k_base *ab);
 	void (*stop)(struct ath11k_base *ab);
 	int (*power_up)(struct ath11k_base *ab);
-	void (*power_down)(struct ath11k_base *ab);
+	void (*power_down)(struct ath11k_base *ab, bool is_suspend);
 	int (*suspend)(struct ath11k_base *ab);
 	int (*resume)(struct ath11k_base *ab);
 	int (*map_service_to_pipe)(struct ath11k_base *ab, u16 service_id,
@@ -68,12 +68,18 @@ static inline void ath11k_hif_irq_disable(struct ath11k_base *ab)
 
 static inline int ath11k_hif_power_up(struct ath11k_base *ab)
 {
+	if (!ab->hif.ops->power_up)
+		return -EOPNOTSUPP;
+
 	return ab->hif.ops->power_up(ab);
 }
 
-static inline void ath11k_hif_power_down(struct ath11k_base *ab)
+static inline void ath11k_hif_power_down(struct ath11k_base *ab, bool is_suspend)
 {
-	ab->hif.ops->power_down(ab);
+	if (!ab->hif.ops->power_down)
+		return;
+
+	ab->hif.ops->power_down(ab, is_suspend);
 }
 
 static inline int ath11k_hif_suspend(struct ath11k_base *ab)
diff --git a/drivers/net/wireless/ath/ath11k/mhi.c b/drivers/net/wireless/ath/ath11k/mhi.c
index fc77eac83e953148b96cad096d26b32222157b24..acd76e9392d31192aca6776319ef0829a1c69628 100644
--- a/drivers/net/wireless/ath/ath11k/mhi.c
+++ b/drivers/net/wireless/ath/ath11k/mhi.c
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: BSD-3-Clause-Clear
 /*
  * Copyright (c) 2020 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #include <linux/msi.h>
@@ -454,9 +454,17 @@ int ath11k_mhi_start(struct ath11k_pci *ab_pci)
 	return 0;
 }
 
-void ath11k_mhi_stop(struct ath11k_pci *ab_pci)
+void ath11k_mhi_stop(struct ath11k_pci *ab_pci, bool is_suspend)
 {
-	mhi_power_down(ab_pci->mhi_ctrl, true);
+	/* During suspend we need to use mhi_power_down_keep_dev()
+	 * workaround, otherwise ath11k_core_resume() will timeout
+	 * during resume.
+	 */
+	if (is_suspend)
+		mhi_power_down_keep_dev(ab_pci->mhi_ctrl, true);
+	else
+		mhi_power_down(ab_pci->mhi_ctrl, true);
+
 	mhi_unprepare_after_power_down(ab_pci->mhi_ctrl);
 }
 
diff --git a/drivers/net/wireless/ath/ath11k/mhi.h b/drivers/net/wireless/ath/ath11k/mhi.h
index 651470091bd5ab60b3fbfb37c1be14b048e877c5..be997652b2cd0c9b16df9ea4e9a746abb81f4382 100644
--- a/drivers/net/wireless/ath/ath11k/mhi.h
+++ b/drivers/net/wireless/ath/ath11k/mhi.h
@@ -1,7 +1,7 @@
 /* SPDX-License-Identifier: BSD-3-Clause-Clear */
 /*
  * Copyright (c) 2020 The Linux Foundation. All rights reserved.
- * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 #ifndef _ATH11K_MHI_H
 #define _ATH11K_MHI_H
@@ -18,7 +18,7 @@
 #define MHICTRL_RESET_MASK			0x2
 
 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, bool is_suspend);
 int ath11k_mhi_register(struct ath11k_pci *ar_pci);
 void ath11k_mhi_unregister(struct ath11k_pci *ar_pci);
 void ath11k_mhi_set_mhictrl_reset(struct ath11k_base *ab);
@@ -27,5 +27,4 @@ void ath11k_mhi_clear_vector(struct ath11k_base *ab);
 int ath11k_mhi_suspend(struct ath11k_pci *ar_pci);
 int ath11k_mhi_resume(struct ath11k_pci *ar_pci);
 void ath11k_mhi_coredump(struct mhi_controller *mhi_ctrl, bool in_panic);
-
 #endif
diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c
index 412f4a134e4a8bf5f8b7ecfe26197acec48858cc..37f5ed7f74f2f6adb3a6b5e176ed8c0105d235c2 100644
--- a/drivers/net/wireless/ath/ath11k/pci.c
+++ b/drivers/net/wireless/ath/ath11k/pci.c
@@ -821,7 +821,7 @@ static int ath11k_pci_power_up(struct ath11k_base *ab)
 	return 0;
 }
 
-static void ath11k_pci_power_down(struct ath11k_base *ab)
+static void ath11k_pci_power_down(struct ath11k_base *ab, bool is_suspend)
 {
 	struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
 
@@ -832,7 +832,7 @@ static void ath11k_pci_power_down(struct ath11k_base *ab)
 
 	ath11k_pci_msi_disable(ab_pci);
 
-	ath11k_mhi_stop(ab_pci);
+	ath11k_mhi_stop(ab_pci, is_suspend);
 	clear_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags);
 	ath11k_pci_sw_reset(ab_pci->ab, false);
 }
@@ -1161,7 +1161,7 @@ static void ath11k_pci_remove(struct pci_dev *pdev)
 	ath11k_pci_set_irq_affinity_hint(ab_pci, NULL);
 
 	if (test_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags)) {
-		ath11k_pci_power_down(ab);
+		ath11k_pci_power_down(ab, false);
 		ath11k_debugfs_soc_destroy(ab);
 		ath11k_qmi_deinit_service(ab);
 		goto qmi_fail;
@@ -1192,7 +1192,7 @@ static void ath11k_pci_shutdown(struct pci_dev *pdev)
 	struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
 
 	ath11k_pci_set_irq_affinity_hint(ab_pci, NULL);
-	ath11k_pci_power_down(ab);
+	ath11k_pci_power_down(ab, false);
 }
 
 static __maybe_unused int ath11k_pci_pm_suspend(struct device *dev)
@@ -1229,9 +1229,39 @@ static __maybe_unused int ath11k_pci_pm_resume(struct device *dev)
 	return ret;
 }
 
-static SIMPLE_DEV_PM_OPS(ath11k_pci_pm_ops,
-			 ath11k_pci_pm_suspend,
-			 ath11k_pci_pm_resume);
+static __maybe_unused int ath11k_pci_pm_suspend_late(struct device *dev)
+{
+	struct ath11k_base *ab = dev_get_drvdata(dev);
+	int ret;
+
+	ret = ath11k_core_suspend_late(ab);
+	if (ret)
+		ath11k_warn(ab, "failed to late suspend core: %d\n", ret);
+
+	/* Similar to ath11k_pci_pm_suspend(), we return success here
+	 * even error happens, to allow system suspend/hibernation survive.
+	 */
+	return 0;
+}
+
+static __maybe_unused int ath11k_pci_pm_resume_early(struct device *dev)
+{
+	struct ath11k_base *ab = dev_get_drvdata(dev);
+	int ret;
+
+	ret = ath11k_core_resume_early(ab);
+	if (ret)
+		ath11k_warn(ab, "failed to early resume core: %d\n", ret);
+
+	return ret;
+}
+
+static const struct dev_pm_ops __maybe_unused ath11k_pci_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(ath11k_pci_pm_suspend,
+				ath11k_pci_pm_resume)
+	SET_LATE_SYSTEM_SLEEP_PM_OPS(ath11k_pci_pm_suspend_late,
+				     ath11k_pci_pm_resume_early)
+};
 
 static struct pci_driver ath11k_pci_driver = {
 	.name = "ath11k_pci",
diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c
index 4f8b08ed1bbc6ec426da83bd7ce49a73b3a93c0c..47b9d4126d3a9b85b424fe50aaca2bce65045994 100644
--- a/drivers/net/wireless/ath/ath11k/qmi.c
+++ b/drivers/net/wireless/ath/ath11k/qmi.c
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: BSD-3-Clause-Clear
 /*
  * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #include <linux/elf.h>
@@ -2887,7 +2887,7 @@ int ath11k_qmi_fwreset_from_cold_boot(struct ath11k_base *ab)
 	}
 
 	/* reset the firmware */
-	ath11k_hif_power_down(ab);
+	ath11k_hif_power_down(ab, false);
 	ath11k_hif_power_up(ab);
 	ath11k_dbg(ab, ATH11K_DBG_QMI, "exit wait for cold boot done\n");
 	return 0;

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH ath-next v2 5/6] wifi: ath11l: choose default PM policy for hibernation
  2025-03-26  1:33 [PATCH ath-next v2 0/6] wifi: ath11k: bring hibernation support back Baochen Qiang
                   ` (3 preceding siblings ...)
  2025-03-26  1:33 ` [PATCH ath-next v2 4/6] wifi: ath11k: support non-WoWLAN mode suspend as well Baochen Qiang
@ 2025-03-26  1:33 ` Baochen Qiang
  2025-03-26  1:33 ` [PATCH ath-next v2 6/6] Reapply "wifi: ath11k: restore country code during resume" Baochen Qiang
  2025-03-26 17:48 ` [PATCH ath-next v2 0/6] wifi: ath11k: bring hibernation support back Julian Wollrath
  6 siblings, 0 replies; 9+ messages in thread
From: Baochen Qiang @ 2025-03-26  1:33 UTC (permalink / raw)
  To: Johannes Berg, Jeff Johnson
  Cc: linux-wireless, ath11k, linux-kernel, Baochen Qiang

Now WoWLAN mode is chosen for those machines listed in the quirk table.
This works for suspend (S3) but breaks for hibernation (S4), because
WoWLAN mode requires WLAN power to be sustained, which is not the case
during hibernation. For hibernation, the default mode should be used.

Register a PM notifier with which kernel can notify us of the actual PM
operation: if system is going to suspend, the original PM policy is
honored; while if it is hibernation, overwrite it with default policy.

To summarize: for suspend (S3), WoWLAN mode is chosen for machines listed
in the quirk table, non-WoWLAN mode for others; for hibernation (S4),
non-WoWLAN mode is chosen for all.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

Signed-off-by: Baochen Qiang <quic_bqiang@quicinc.com>
---
 drivers/net/wireless/ath/ath11k/core.c | 52 +++++++++++++++++++++++++++++++---
 drivers/net/wireless/ath/ath11k/core.h |  5 ++++
 drivers/net/wireless/ath/ath11k/pci.c  |  1 +
 3 files changed, 54 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c
index e1c0eca773fc0243aa4d159dd8671f6a4084b29a..cde009c3bdaf1e7a4f64a10df61029ef83408cd0 100644
--- a/drivers/net/wireless/ath/ath11k/core.c
+++ b/drivers/net/wireless/ath/ath11k/core.c
@@ -1136,7 +1136,7 @@ int ath11k_core_suspend(struct ath11k_base *ab)
 	if (ret <= 0)
 		return ret;
 
-	if (ab->pm_policy == ATH11K_PM_WOW)
+	if (ab->actual_pm_policy == ATH11K_PM_WOW)
 		return ath11k_core_suspend_wow(ab);
 
 	return ath11k_core_suspend_default(ab);
@@ -1151,7 +1151,7 @@ int ath11k_core_suspend_late(struct ath11k_base *ab)
 	if (ret <= 0)
 		return ret;
 
-	if (ab->pm_policy == ATH11K_PM_WOW)
+	if (ab->actual_pm_policy == ATH11K_PM_WOW)
 		return 0;
 
 	ath11k_hif_irq_disable(ab);
@@ -1171,7 +1171,7 @@ int ath11k_core_resume_early(struct ath11k_base *ab)
 	if (ret <= 0)
 		return ret;
 
-	if (ab->pm_policy == ATH11K_PM_WOW)
+	if (ab->actual_pm_policy == ATH11K_PM_WOW)
 		return 0;
 
 	reinit_completion(&ab->restart_completed);
@@ -1240,7 +1240,7 @@ int ath11k_core_resume(struct ath11k_base *ab)
 	if (ret <= 0)
 		return ret;
 
-	if (ab->pm_policy == ATH11K_PM_WOW)
+	if (ab->actual_pm_policy == ATH11K_PM_WOW)
 		return ath11k_core_resume_wow(ab);
 
 	return ath11k_core_resume_default(ab);
@@ -2500,6 +2500,43 @@ int ath11k_core_pre_init(struct ath11k_base *ab)
 }
 EXPORT_SYMBOL(ath11k_core_pre_init);
 
+static int ath11k_core_pm_notify(struct notifier_block *nb,
+				 unsigned long action, void *nouse)
+{
+	struct ath11k_base *ab = container_of(nb, struct ath11k_base,
+					      pm_nb);
+
+	switch (action) {
+	case PM_SUSPEND_PREPARE:
+		ab->actual_pm_policy = ab->pm_policy;
+		break;
+	case PM_HIBERNATION_PREPARE:
+		ab->actual_pm_policy = ATH11K_PM_DEFAULT;
+		break;
+	default:
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static int ath11k_core_pm_notifier_register(struct ath11k_base *ab)
+{
+	ab->pm_nb.notifier_call = ath11k_core_pm_notify;
+	return register_pm_notifier(&ab->pm_nb);
+}
+
+void ath11k_core_pm_notifier_unregister(struct ath11k_base *ab)
+{
+	int ret;
+
+	ret = unregister_pm_notifier(&ab->pm_nb);
+	if (ret)
+		/* just warn here, there is nothing can be done in fail case */
+		ath11k_warn(ab, "failed to unregister PM notifier %d\n", ret);
+}
+EXPORT_SYMBOL(ath11k_core_pm_notifier_unregister);
+
 int ath11k_core_init(struct ath11k_base *ab)
 {
 	const struct dmi_system_id *dmi_id;
@@ -2513,6 +2550,12 @@ int ath11k_core_init(struct ath11k_base *ab)
 
 	ath11k_dbg(ab, ATH11K_DBG_BOOT, "pm policy %u\n", ab->pm_policy);
 
+	ret = ath11k_core_pm_notifier_register(ab);
+	if (ret) {
+		ath11k_err(ab, "failed to register PM notifier: %d\n", ret);
+		return ret;
+	}
+
 	ret = ath11k_core_soc_create(ab);
 	if (ret) {
 		ath11k_err(ab, "failed to create soc core: %d\n", ret);
@@ -2535,6 +2578,7 @@ void ath11k_core_deinit(struct ath11k_base *ab)
 	ath11k_hif_power_down(ab, false);
 	ath11k_mac_destroy(ab);
 	ath11k_core_soc_destroy(ab);
+	ath11k_core_pm_notifier_unregister(ab);
 }
 EXPORT_SYMBOL(ath11k_core_deinit);
 
diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h
index 7a9354e0eadb58730028217960e5bbfe0056c2fb..339d4fca1ed5fbddd8f386589529f0febba98ecb 100644
--- a/drivers/net/wireless/ath/ath11k/core.h
+++ b/drivers/net/wireless/ath/ath11k/core.h
@@ -16,6 +16,7 @@
 #include <linux/rhashtable.h>
 #include <linux/average.h>
 #include <linux/firmware.h>
+#include <linux/suspend.h>
 
 #include "qmi.h"
 #include "htc.h"
@@ -1066,6 +1067,8 @@ struct ath11k_base {
 #endif
 
 	enum ath11k_pm_policy pm_policy;
+	enum ath11k_pm_policy actual_pm_policy;
+	struct notifier_block pm_nb;
 
 	/* must be last */
 	u8 drv_priv[] __aligned(sizeof(void *));
@@ -1333,4 +1336,6 @@ static inline const char *ath11k_bus_str(enum ath11k_bus bus)
 	return "unknown";
 }
 
+void ath11k_core_pm_notifier_unregister(struct ath11k_base *ab);
+
 #endif /* _CORE_H_ */
diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c
index 37f5ed7f74f2f6adb3a6b5e176ed8c0105d235c2..630df6b1d82dff357c8259475cc9b03e4580a1b9 100644
--- a/drivers/net/wireless/ath/ath11k/pci.c
+++ b/drivers/net/wireless/ath/ath11k/pci.c
@@ -1164,6 +1164,7 @@ static void ath11k_pci_remove(struct pci_dev *pdev)
 		ath11k_pci_power_down(ab, false);
 		ath11k_debugfs_soc_destroy(ab);
 		ath11k_qmi_deinit_service(ab);
+		ath11k_core_pm_notifier_unregister(ab);
 		goto qmi_fail;
 	}
 

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH ath-next v2 6/6] Reapply "wifi: ath11k: restore country code during resume"
  2025-03-26  1:33 [PATCH ath-next v2 0/6] wifi: ath11k: bring hibernation support back Baochen Qiang
                   ` (4 preceding siblings ...)
  2025-03-26  1:33 ` [PATCH ath-next v2 5/6] wifi: ath11l: choose default PM policy for hibernation Baochen Qiang
@ 2025-03-26  1:33 ` Baochen Qiang
  2025-03-26 17:48 ` [PATCH ath-next v2 0/6] wifi: ath11k: bring hibernation support back Julian Wollrath
  6 siblings, 0 replies; 9+ messages in thread
From: Baochen Qiang @ 2025-03-26  1:33 UTC (permalink / raw)
  To: Johannes Berg, Jeff Johnson
  Cc: linux-wireless, ath11k, linux-kernel, Baochen Qiang,
	Muhammad Usama Anjum

This reverts commit d3e154d7776ba57ab679fb816fb87b627fba21c9.

With non-WoWLAN suspend support brought back, commit 7f0343b7b871 ("wifi:
ath11k: restore country code during resume") needs to be brought back
as well.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30

Tested-by: Muhammad Usama Anjum <usama.anjum@collabora.com>
Signed-off-by: Baochen Qiang <quic_bqiang@quicinc.com>
---
 drivers/net/wireless/ath/ath11k/core.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c
index cde009c3bdaf1e7a4f64a10df61029ef83408cd0..3f0e8e65e8e907d70705634b62909886a09b3b70 100644
--- a/drivers/net/wireless/ath/ath11k/core.c
+++ b/drivers/net/wireless/ath/ath11k/core.c
@@ -1185,6 +1185,7 @@ EXPORT_SYMBOL(ath11k_core_resume_early);
 
 static int ath11k_core_resume_default(struct ath11k_base *ab)
 {
+	struct ath11k *ar;
 	long time_left;
 	int ret;
 
@@ -1195,6 +1196,20 @@ static int ath11k_core_resume_default(struct ath11k_base *ab)
 		return -ETIMEDOUT;
 	}
 
+	/* So far only single_pdev_only devices can reach here,
+	 * so it is valid to handle the first, and the only, pdev.
+	 */
+	ar = ab->pdevs[0].ar;
+	if (ab->hw_params.current_cc_support &&
+	    ar->alpha2[0] != 0 && ar->alpha2[1] != 0) {
+		ret = ath11k_reg_set_cc(ar);
+		if (ret) {
+			ath11k_warn(ab, "failed to set country code during resume: %d\n",
+				    ret);
+			return ret;
+		}
+	}
+
 	ret = ath11k_dp_rx_pktlog_start(ab);
 	if (ret)
 		ath11k_warn(ab, "failed to start rx pktlog during resume: %d\n",

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH ath-next v2 0/6] wifi: ath11k: bring hibernation support back
  2025-03-26  1:33 [PATCH ath-next v2 0/6] wifi: ath11k: bring hibernation support back Baochen Qiang
                   ` (5 preceding siblings ...)
  2025-03-26  1:33 ` [PATCH ath-next v2 6/6] Reapply "wifi: ath11k: restore country code during resume" Baochen Qiang
@ 2025-03-26 17:48 ` Julian Wollrath
  2025-03-27  1:52   ` Baochen Qiang
  6 siblings, 1 reply; 9+ messages in thread
From: Julian Wollrath @ 2025-03-26 17:48 UTC (permalink / raw)
  To: Baochen Qiang
  Cc: Johannes Berg, Jeff Johnson, linux-wireless, ath11k, linux-kernel,
	Muhammad Usama Anjum

Hi,

Am Mi, 26 Mär 2025 09:33:04 +0800
schrieb Baochen Qiang <quic_bqiang@quicinc.com>:

> To handle the Lenovo unexpected wakeup issue [1], previously we revert
> commit 166a490f59ac ("wifi: ath11k: support hibernation"). However we
> need to bring it back, of course with additional changes such that
> Lenovo machines would not break.
> 
> For suspend (S3), as those machines work well in WoWLAN mode, the
> thought here is that we put WLAN target into WoWLAN mode on those
> machines while into non-WoWLAN mode (which is done in the reverted
> commit) on other machines. This requires us to identify Lenovo
> machines from others. For that purpose, read machine info from DMI
> interface, match it against all known affected machines. If there is
> a match, choose WoWLAN suspend mode, else choose non-WoWLAN mode.
> This is done in patches [1 - 4/6]
> 
> For hibernation (S4), non-WoWLAN mode is chosen for all machines. The
> unexpected wakeup issue should not happen in this mode, since WLAN
> target power is cut off. To know if the system is going to suspend or
> to hibernate, register a notifier such that kernel can notify us of
> such infomation. This is done in patch [5/6]

with this, I just want to confirm, everything now works as expected
after hibernation.


Best regards,
Julian

> 
> The last patch bring the reverted "wifi: ath11k: restore country code
> during resume" commit back.
> 
> [1] https://bugzilla.kernel.org/show_bug.cgi?id=219196
> 
> Signed-off-by: Baochen Qiang <quic_bqiang@quicinc.com>
> ---
> Changes in v2:
> - fix compile warnings
> - support hibernation on Lenovo machines listed in quirk table
> - Link to v1:
> https://lore.kernel.org/linux-wireless/20250320023003.65028-1-quic_bqiang@quicinc.com/
> 
> ---
> Baochen Qiang (6):
>       wifi: ath11k: determine PM policy based on machine model
>       wifi: ath11k: introduce ath11k_core_continue_suspend_resume()
>       wifi: ath11k: refactor ath11k_core_suspend/_resume()
>       wifi: ath11k: support non-WoWLAN mode suspend as well
>       wifi: ath11l: choose default PM policy for hibernation
>       Reapply "wifi: ath11k: restore country code during resume"
> 
>  drivers/net/wireless/ath/ath11k/ahb.c  |   4 +-
>  drivers/net/wireless/ath/ath11k/core.c | 294
> ++++++++++++++++++++++++++++++---
> drivers/net/wireless/ath/ath11k/core.h |  16 ++
> drivers/net/wireless/ath/ath11k/hif.h  |  14 +-
> drivers/net/wireless/ath/ath11k/mhi.c  |  14 +-
> drivers/net/wireless/ath/ath11k/mhi.h  |   5 +-
> drivers/net/wireless/ath/ath11k/pci.c  |  45 ++++-
> drivers/net/wireless/ath/ath11k/qmi.c  |   4 +- 8 files changed, 350
> insertions(+), 46 deletions(-) ---
> base-commit: b6f473c96421b8b451a8df8ccb620bcd71d4b3f4
> change-id: 20250324-ath11k-bring-hibernation-back-e11ad8e82adf
> 
> Best regards,



-- 
 ()  ascii ribbon campaign - against html e-mail 
 /\                        - against proprietary attachments

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH ath-next v2 0/6] wifi: ath11k: bring hibernation support back
  2025-03-26 17:48 ` [PATCH ath-next v2 0/6] wifi: ath11k: bring hibernation support back Julian Wollrath
@ 2025-03-27  1:52   ` Baochen Qiang
  0 siblings, 0 replies; 9+ messages in thread
From: Baochen Qiang @ 2025-03-27  1:52 UTC (permalink / raw)
  To: Julian Wollrath
  Cc: Johannes Berg, Jeff Johnson, linux-wireless, ath11k, linux-kernel,
	Muhammad Usama Anjum



On 3/27/2025 1:48 AM, Julian Wollrath wrote:
> Hi,
> 
> Am Mi, 26 Mär 2025 09:33:04 +0800
> schrieb Baochen Qiang <quic_bqiang@quicinc.com>:
> 
>> To handle the Lenovo unexpected wakeup issue [1], previously we revert
>> commit 166a490f59ac ("wifi: ath11k: support hibernation"). However we
>> need to bring it back, of course with additional changes such that
>> Lenovo machines would not break.
>>
>> For suspend (S3), as those machines work well in WoWLAN mode, the
>> thought here is that we put WLAN target into WoWLAN mode on those
>> machines while into non-WoWLAN mode (which is done in the reverted
>> commit) on other machines. This requires us to identify Lenovo
>> machines from others. For that purpose, read machine info from DMI
>> interface, match it against all known affected machines. If there is
>> a match, choose WoWLAN suspend mode, else choose non-WoWLAN mode.
>> This is done in patches [1 - 4/6]
>>
>> For hibernation (S4), non-WoWLAN mode is chosen for all machines. The
>> unexpected wakeup issue should not happen in this mode, since WLAN
>> target power is cut off. To know if the system is going to suspend or
>> to hibernate, register a notifier such that kernel can notify us of
>> such infomation. This is done in patch [5/6]
> 
> with this, I just want to confirm, everything now works as expected
> after hibernation.

Thank you for testing.

> 
> 
> Best regards,
> Julian
> 
>>
>> The last patch bring the reverted "wifi: ath11k: restore country code
>> during resume" commit back.
>>
>> [1] https://bugzilla.kernel.org/show_bug.cgi?id=219196
>>
>> Signed-off-by: Baochen Qiang <quic_bqiang@quicinc.com>
>> ---
>> Changes in v2:
>> - fix compile warnings
>> - support hibernation on Lenovo machines listed in quirk table
>> - Link to v1:
>> https://lore.kernel.org/linux-wireless/20250320023003.65028-1-quic_bqiang@quicinc.com/
>>
>> ---
>> Baochen Qiang (6):
>>       wifi: ath11k: determine PM policy based on machine model
>>       wifi: ath11k: introduce ath11k_core_continue_suspend_resume()
>>       wifi: ath11k: refactor ath11k_core_suspend/_resume()
>>       wifi: ath11k: support non-WoWLAN mode suspend as well
>>       wifi: ath11l: choose default PM policy for hibernation
>>       Reapply "wifi: ath11k: restore country code during resume"
>>
>>  drivers/net/wireless/ath/ath11k/ahb.c  |   4 +-
>>  drivers/net/wireless/ath/ath11k/core.c | 294
>> ++++++++++++++++++++++++++++++---
>> drivers/net/wireless/ath/ath11k/core.h |  16 ++
>> drivers/net/wireless/ath/ath11k/hif.h  |  14 +-
>> drivers/net/wireless/ath/ath11k/mhi.c  |  14 +-
>> drivers/net/wireless/ath/ath11k/mhi.h  |   5 +-
>> drivers/net/wireless/ath/ath11k/pci.c  |  45 ++++-
>> drivers/net/wireless/ath/ath11k/qmi.c  |   4 +- 8 files changed, 350
>> insertions(+), 46 deletions(-) ---
>> base-commit: b6f473c96421b8b451a8df8ccb620bcd71d4b3f4
>> change-id: 20250324-ath11k-bring-hibernation-back-e11ad8e82adf
>>
>> Best regards,
> 
> 
> 


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2025-03-27  1:52 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-03-26  1:33 [PATCH ath-next v2 0/6] wifi: ath11k: bring hibernation support back Baochen Qiang
2025-03-26  1:33 ` [PATCH ath-next v2 1/6] wifi: ath11k: determine PM policy based on machine model Baochen Qiang
2025-03-26  1:33 ` [PATCH ath-next v2 2/6] wifi: ath11k: introduce ath11k_core_continue_suspend_resume() Baochen Qiang
2025-03-26  1:33 ` [PATCH ath-next v2 3/6] wifi: ath11k: refactor ath11k_core_suspend/_resume() Baochen Qiang
2025-03-26  1:33 ` [PATCH ath-next v2 4/6] wifi: ath11k: support non-WoWLAN mode suspend as well Baochen Qiang
2025-03-26  1:33 ` [PATCH ath-next v2 5/6] wifi: ath11l: choose default PM policy for hibernation Baochen Qiang
2025-03-26  1:33 ` [PATCH ath-next v2 6/6] Reapply "wifi: ath11k: restore country code during resume" Baochen Qiang
2025-03-26 17:48 ` [PATCH ath-next v2 0/6] wifi: ath11k: bring hibernation support back Julian Wollrath
2025-03-27  1:52   ` Baochen Qiang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox