diff --git a/Kbuild b/Kbuild
index c512116bbd..3410ecae6d 100644
--- a/Kbuild
+++ b/Kbuild
@@ -4573,6 +4573,8 @@ endif
cppflags-$(CONFIG_WLAN_FEATURE_MARK_FIRST_WAKEUP_PACKET) += -DWLAN_FEATURE_MARK_FIRST_WAKEUP_PACKET
+ccflags-$(CONFIG_SHUTDOWN_WLAN_IN_SYSTEM_SUSPEND) += -DSHUTDOWN_WLAN_IN_SYSTEM_SUSPEND
+
ifeq ($(CONFIG_WLAN_FEATURE_MCC_QUOTA), y)
cppflags-y += -DWLAN_FEATURE_MCC_QUOTA
ifdef CONFIG_WLAN_MCC_MIN_CHANNEL_QUOTA
diff --git a/components/pmo/dispatcher/inc/wlan_pmo_common_cfg.h b/components/pmo/dispatcher/inc/wlan_pmo_common_cfg.h
index d820335e84..e4e8f5f514 100644
--- a/components/pmo/dispatcher/inc/wlan_pmo_common_cfg.h
+++ b/components/pmo/dispatcher/inc/wlan_pmo_common_cfg.h
@@ -340,13 +340,14 @@
*
* gSuspendMode - Suspend mode configuration
* @Min: 0
- * @Max: 2
+ * @Max: 3
* @Default: 2
*
* This ini is used to set suspend mode. Configurations are as follows:
* 0 - Does not support suspend.
* 1 - Legency suspend mode, PDEV suspend.
* 2 - WOW suspend mode.
+ * 3 - Shutdown wlan while suspend.
*
* Related: None
*
@@ -357,7 +358,7 @@
*
*/
#define CFG_PMO_SUSPEND_MODE CFG_INI_UINT("gSuspendMode", \
- 0, 2, 2, \
+ 0, 3, 2, \
CFG_VALUE_OR_DEFAULT, \
"Suspend mode")
diff --git a/components/pmo/dispatcher/inc/wlan_pmo_common_public_struct.h b/components/pmo/dispatcher/inc/wlan_pmo_common_public_struct.h
index 45e6d2c26f..5bca2e2379 100644
--- a/components/pmo/dispatcher/inc/wlan_pmo_common_public_struct.h
+++ b/components/pmo/dispatcher/inc/wlan_pmo_common_public_struct.h
@@ -175,7 +175,7 @@ enum powersave_mode {
* @PMO_SUSPEND_NONE: Does not support suspend
* @PMO_SUSPEND_LEGENCY: Legency PDEV suspend mode
* @PMO_SUSPEND_WOW: WoW suspend mode
- * @PMO_SUSPEND_SHUTDOWN: shutdown while suspend mode
+ * @PMO_SUSPEND_SHUTDOWN: Shutdown suspend mode. Shutdown while suspend
*/
enum pmo_suspend_mode {
PMO_SUSPEND_NONE = 0,
diff --git a/core/hdd/inc/wlan_hdd_main.h b/core/hdd/inc/wlan_hdd_main.h
index 87e45b93e1..250593f026 100644
--- a/core/hdd/inc/wlan_hdd_main.h
+++ b/core/hdd/inc/wlan_hdd_main.h
@@ -1710,6 +1710,7 @@ enum wlan_state_ctrl_str_id {
* @dump_in_progress: Stores value of dump in progress
* @hdd_dual_sta_policy: Concurrent STA policy configuration
* @is_wlan_disabled: if wlan is disabled by userspace
+ * @pm_notifier: PM notifier of hdd modules
*/
struct hdd_context {
struct wlan_objmgr_psoc *psoc;
@@ -1889,6 +1890,7 @@ struct hdd_context {
/* Present state of driver cds modules */
enum driver_modules_status driver_status;
struct qdf_delayed_work psoc_idle_timeout_work;
+ struct notifier_block pm_notifier;
struct acs_dfs_policy acs_policy;
uint16_t wmi_max_len;
struct suspend_resume_stats suspend_resume_stats;
diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c
index 4e715657c9..0ce10065bd 100644
--- a/core/hdd/src/wlan_hdd_main.c
+++ b/core/hdd/src/wlan_hdd_main.c
@@ -83,6 +83,7 @@
#include
#include
#include
+#include
#ifdef WLAN_FEATURE_DP_BUS_BANDWIDTH
#include "qdf_periodic_work.h"
@@ -9395,6 +9396,79 @@ out:
return ret;
}
+#ifdef SHUTDOWN_WLAN_IN_SYSTEM_SUSPEND
+static QDF_STATUS
+hdd_shutdown_wlan_in_suspend_prepare(struct hdd_context *hdd_ctx)
+{
+#define SHUTDOWN_IN_SUSPEND_RETRY 10
+
+ int count = 0;
+
+ if (ucfg_pmo_get_suspend_mode(hdd_ctx->psoc) != PMO_SUSPEND_SHUTDOWN) {
+ hdd_debug("shutdown in suspend not supported");
+ return 0;
+ }
+
+ while (hdd_is_any_interface_open(hdd_ctx) &&
+ count < SHUTDOWN_IN_SUSPEND_RETRY) {
+ count++;
+ hdd_debug_rl("sleep 50ms to wait adapters stopped, #%d", count);
+ msleep(50);
+ }
+ if (count >= SHUTDOWN_IN_SUSPEND_RETRY) {
+ hdd_err("some adapters not stopped");
+ return -EBUSY;
+ }
+
+ hdd_debug("call pld idle shutdown directly");
+ return pld_idle_shutdown(hdd_ctx->parent_dev, hdd_psoc_idle_shutdown);
+}
+
+static int hdd_pm_notify(struct notifier_block *b,
+ unsigned long event, void *p)
+{
+ struct hdd_context *hdd_ctx = container_of(b, struct hdd_context,
+ pm_notifier);
+
+ if (wlan_hdd_validate_context(hdd_ctx) != 0)
+ return NOTIFY_STOP;
+
+ hdd_debug("got PM event: %lu", event);
+
+ switch (event) {
+ case PM_SUSPEND_PREPARE:
+ case PM_HIBERNATION_PREPARE:
+ if (0 != hdd_shutdown_wlan_in_suspend_prepare(hdd_ctx))
+ return NOTIFY_STOP;
+ break;
+ case PM_POST_SUSPEND:
+ case PM_POST_HIBERNATION:
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static void hdd_pm_notifier_init(struct hdd_context *hdd_ctx)
+{
+ hdd_ctx->pm_notifier.notifier_call = hdd_pm_notify;
+ register_pm_notifier(&hdd_ctx->pm_notifier);
+}
+
+static void hdd_pm_notifier_deinit(struct hdd_context *hdd_ctx)
+{
+ unregister_pm_notifier(&hdd_ctx->pm_notifier);
+}
+#else
+static inline void hdd_pm_notifier_init(struct hdd_context *hdd_ctx)
+{
+}
+
+static inline void hdd_pm_notifier_deinit(struct hdd_context *hdd_ctx)
+{
+}
+#endif
+
/**
* hdd_context_deinit() - Deinitialize HDD context
* @hdd_ctx: HDD context.
@@ -9438,6 +9512,7 @@ void hdd_context_destroy(struct hdd_context *hdd_ctx)
hdd_ctx->config = NULL;
cfg_release();
+ hdd_pm_notifier_deinit(hdd_ctx);
qdf_delayed_work_destroy(&hdd_ctx->psoc_idle_timeout_work);
wiphy_free(hdd_ctx->wiphy);
}
@@ -12631,6 +12706,8 @@ struct hdd_context *hdd_context_create(struct device *dev)
goto wiphy_dealloc;
}
+ hdd_pm_notifier_init(hdd_ctx);
+
hdd_ctx->parent_dev = dev;
hdd_ctx->last_scan_reject_vdev_id = WLAN_UMAC_VDEV_ID_MAX;