Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * kernel/power/autosleep.c
0004  *
0005  * Opportunistic sleep support.
0006  *
0007  * Copyright (C) 2012 Rafael J. Wysocki <rjw@sisk.pl>
0008  */
0009 
0010 #include <linux/device.h>
0011 #include <linux/mutex.h>
0012 #include <linux/pm_wakeup.h>
0013 
0014 #include "power.h"
0015 
0016 static suspend_state_t autosleep_state;
0017 static struct workqueue_struct *autosleep_wq;
0018 /*
0019  * Note: it is only safe to mutex_lock(&autosleep_lock) if a wakeup_source
0020  * is active, otherwise a deadlock with try_to_suspend() is possible.
0021  * Alternatively mutex_lock_interruptible() can be used.  This will then fail
0022  * if an auto_sleep cycle tries to freeze processes.
0023  */
0024 static DEFINE_MUTEX(autosleep_lock);
0025 static struct wakeup_source *autosleep_ws;
0026 
0027 static void try_to_suspend(struct work_struct *work)
0028 {
0029     unsigned int initial_count, final_count;
0030 
0031     if (!pm_get_wakeup_count(&initial_count, true))
0032         goto out;
0033 
0034     mutex_lock(&autosleep_lock);
0035 
0036     if (!pm_save_wakeup_count(initial_count) ||
0037         system_state != SYSTEM_RUNNING) {
0038         mutex_unlock(&autosleep_lock);
0039         goto out;
0040     }
0041 
0042     if (autosleep_state == PM_SUSPEND_ON) {
0043         mutex_unlock(&autosleep_lock);
0044         return;
0045     }
0046     if (autosleep_state >= PM_SUSPEND_MAX)
0047         hibernate();
0048     else
0049         pm_suspend(autosleep_state);
0050 
0051     mutex_unlock(&autosleep_lock);
0052 
0053     if (!pm_get_wakeup_count(&final_count, false))
0054         goto out;
0055 
0056     /*
0057      * If the wakeup occurred for an unknown reason, wait to prevent the
0058      * system from trying to suspend and waking up in a tight loop.
0059      */
0060     if (final_count == initial_count)
0061         schedule_timeout_uninterruptible(HZ / 2);
0062 
0063  out:
0064     queue_up_suspend_work();
0065 }
0066 
0067 static DECLARE_WORK(suspend_work, try_to_suspend);
0068 
0069 void queue_up_suspend_work(void)
0070 {
0071     if (autosleep_state > PM_SUSPEND_ON)
0072         queue_work(autosleep_wq, &suspend_work);
0073 }
0074 
0075 suspend_state_t pm_autosleep_state(void)
0076 {
0077     return autosleep_state;
0078 }
0079 
0080 int pm_autosleep_lock(void)
0081 {
0082     return mutex_lock_interruptible(&autosleep_lock);
0083 }
0084 
0085 void pm_autosleep_unlock(void)
0086 {
0087     mutex_unlock(&autosleep_lock);
0088 }
0089 
0090 int pm_autosleep_set_state(suspend_state_t state)
0091 {
0092 
0093 #ifndef CONFIG_HIBERNATION
0094     if (state >= PM_SUSPEND_MAX)
0095         return -EINVAL;
0096 #endif
0097 
0098     __pm_stay_awake(autosleep_ws);
0099 
0100     mutex_lock(&autosleep_lock);
0101 
0102     autosleep_state = state;
0103 
0104     __pm_relax(autosleep_ws);
0105 
0106     if (state > PM_SUSPEND_ON) {
0107         pm_wakep_autosleep_enabled(true);
0108         queue_up_suspend_work();
0109     } else {
0110         pm_wakep_autosleep_enabled(false);
0111     }
0112 
0113     mutex_unlock(&autosleep_lock);
0114     return 0;
0115 }
0116 
0117 int __init pm_autosleep_init(void)
0118 {
0119     autosleep_ws = wakeup_source_register(NULL, "autosleep");
0120     if (!autosleep_ws)
0121         return -ENOMEM;
0122 
0123     autosleep_wq = alloc_ordered_workqueue("autosleep", 0);
0124     if (autosleep_wq)
0125         return 0;
0126 
0127     wakeup_source_unregister(autosleep_ws);
0128     return -ENOMEM;
0129 }