@@ -91,6 +91,17 @@ config THERMAL_WRITABLE_TRIPS
Say 'Y' here if you would like to allow userspace tools to
change trip temperatures.
+config THERMAL_ASYNC_RESUME
+ bool "Thermal async init zones on system resume"
+ default n
+ help
+ Re-initialize thermal zones asynchronously on system resume.
+ Thermal zone sensors may be attached on slow buses, impacting
+ the duration of PM_POST_SUSPEND. If that is a concern enable
+ this switch.
+
+ If in doubt, say N.
+
choice
prompt "Default Thermal governor"
default THERMAL_DEFAULT_GOV_STEP_WISE
@@ -21,6 +21,10 @@
#include <linux/of.h>
#include <linux/suspend.h>
+#ifdef CONFIG_THERMAL_ASYNC_RESUME
+#include <linux/workqueue.h>
+#endif /* CONFIG_THERMAL_ASYNC_RESUME */
+
#define CREATE_TRACE_POINTS
#include "thermal_trace.h"
@@ -41,6 +45,10 @@ static atomic_t in_suspend;
static struct thermal_governor *def_governor;
+#ifdef CONFIG_THERMAL_ASYNC_RESUME
+struct work_struct *resume_thermal_zones_wk;
+#endif /* CONFIG_THERMAL_ASYNC_RESUME */
+
/*
* Governor section: set of functions to handle thermal governors
*
@@ -1495,26 +1503,53 @@ struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name)
}
EXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name);
-static int thermal_pm_notify(struct notifier_block *nb,
- unsigned long mode, void *_unused)
+static inline void resume_thermal_zones(void)
{
struct thermal_zone_device *tz;
+ list_for_each_entry(tz, &thermal_tz_list, node) {
+ if (!thermal_zone_device_is_enabled(tz))
+ continue;
+
+ thermal_zone_device_init(tz);
+ thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
+ }
+}
+
+#ifdef CONFIG_THERMAL_ASYNC_RESUME
+static void thermal_resume_work_fn(struct work_struct *work)
+{
+ resume_thermal_zones();
+}
+
+static inline void thermal_resume_enqueue(void)
+{
+ INIT_WORK(resume_thermal_zones_wk, thermal_resume_work_fn);
+ queue_work(system_unbound_wq, resume_thermal_zones_wk);
+}
+#endif /* CONFIG_THERMAL_ASYNC_RESUME */
+
+static int thermal_pm_notify(struct notifier_block *nb,
+ unsigned long mode, void *_unused)
+{
switch (mode) {
case PM_HIBERNATION_PREPARE:
case PM_RESTORE_PREPARE:
case PM_SUSPEND_PREPARE:
+#ifdef CONFIG_THERMAL_ASYNC_RESUME
+ flush_work(resume_thermal_zones_wk);
+#endif /* CONFIG_THERMAL_ASYNC_RESUME */
atomic_set(&in_suspend, 1);
break;
case PM_POST_HIBERNATION:
case PM_POST_RESTORE:
case PM_POST_SUSPEND:
atomic_set(&in_suspend, 0);
- list_for_each_entry(tz, &thermal_tz_list, node) {
- thermal_zone_device_init(tz);
- thermal_zone_device_update(tz,
- THERMAL_EVENT_UNSPECIFIED);
- }
+#ifdef CONFIG_THERMAL_ASYNC_RESUME
+ thermal_resume_enqueue();
+#else /* CONFIG_THERMAL_ASYNC_RESUME */
+ resume_thermal_zones();
+#endif /* CONFIG_THERMAL_ASYNC_RESUME */
break;
default:
break;
@@ -1530,6 +1565,15 @@ static int __init thermal_init(void)
{
int result;
+#ifdef CONFIG_THERMAL_ASYNC_RESUME
+ resume_thermal_zones_wk = kmalloc(sizeof(*resume_thermal_zones_wk),
+ GFP_KERNEL);
+ if (!resume_thermal_zones_wk) {
+ result = -ENOMEM;
+ goto error;
+ }
+#endif /* CONFIG_THERMAL_ASYNC_RESUME */
+
result = thermal_netlink_init();
if (result)
goto error;
Some thermal zones are bus connected and slow to resume, thus delaying actions which depend on completion of PM_POST_SUSPEND. Add optional execution path to resume thermal zones on the system unbounded workqueue. Signed-off-by: Radu Solea <radusolea@google.com> --- drivers/thermal/Kconfig | 11 +++++++ drivers/thermal/thermal_core.c | 58 ++++++++++++++++++++++++++++++---- 2 files changed, 62 insertions(+), 7 deletions(-)