root/kernel/power/autosleep.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. try_to_suspend
  2. queue_up_suspend_work
  3. pm_autosleep_state
  4. pm_autosleep_lock
  5. pm_autosleep_unlock
  6. pm_autosleep_set_state
  7. pm_autosleep_init

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * kernel/power/autosleep.c
   4  *
   5  * Opportunistic sleep support.
   6  *
   7  * Copyright (C) 2012 Rafael J. Wysocki <rjw@sisk.pl>
   8  */
   9 
  10 #include <linux/device.h>
  11 #include <linux/mutex.h>
  12 #include <linux/pm_wakeup.h>
  13 
  14 #include "power.h"
  15 
  16 static suspend_state_t autosleep_state;
  17 static struct workqueue_struct *autosleep_wq;
  18 /*
  19  * Note: it is only safe to mutex_lock(&autosleep_lock) if a wakeup_source
  20  * is active, otherwise a deadlock with try_to_suspend() is possible.
  21  * Alternatively mutex_lock_interruptible() can be used.  This will then fail
  22  * if an auto_sleep cycle tries to freeze processes.
  23  */
  24 static DEFINE_MUTEX(autosleep_lock);
  25 static struct wakeup_source *autosleep_ws;
  26 
  27 static void try_to_suspend(struct work_struct *work)
  28 {
  29         unsigned int initial_count, final_count;
  30 
  31         if (!pm_get_wakeup_count(&initial_count, true))
  32                 goto out;
  33 
  34         mutex_lock(&autosleep_lock);
  35 
  36         if (!pm_save_wakeup_count(initial_count) ||
  37                 system_state != SYSTEM_RUNNING) {
  38                 mutex_unlock(&autosleep_lock);
  39                 goto out;
  40         }
  41 
  42         if (autosleep_state == PM_SUSPEND_ON) {
  43                 mutex_unlock(&autosleep_lock);
  44                 return;
  45         }
  46         if (autosleep_state >= PM_SUSPEND_MAX)
  47                 hibernate();
  48         else
  49                 pm_suspend(autosleep_state);
  50 
  51         mutex_unlock(&autosleep_lock);
  52 
  53         if (!pm_get_wakeup_count(&final_count, false))
  54                 goto out;
  55 
  56         /*
  57          * If the wakeup occured for an unknown reason, wait to prevent the
  58          * system from trying to suspend and waking up in a tight loop.
  59          */
  60         if (final_count == initial_count)
  61                 schedule_timeout_uninterruptible(HZ / 2);
  62 
  63  out:
  64         queue_up_suspend_work();
  65 }
  66 
  67 static DECLARE_WORK(suspend_work, try_to_suspend);
  68 
  69 void queue_up_suspend_work(void)
  70 {
  71         if (autosleep_state > PM_SUSPEND_ON)
  72                 queue_work(autosleep_wq, &suspend_work);
  73 }
  74 
  75 suspend_state_t pm_autosleep_state(void)
  76 {
  77         return autosleep_state;
  78 }
  79 
  80 int pm_autosleep_lock(void)
  81 {
  82         return mutex_lock_interruptible(&autosleep_lock);
  83 }
  84 
  85 void pm_autosleep_unlock(void)
  86 {
  87         mutex_unlock(&autosleep_lock);
  88 }
  89 
  90 int pm_autosleep_set_state(suspend_state_t state)
  91 {
  92 
  93 #ifndef CONFIG_HIBERNATION
  94         if (state >= PM_SUSPEND_MAX)
  95                 return -EINVAL;
  96 #endif
  97 
  98         __pm_stay_awake(autosleep_ws);
  99 
 100         mutex_lock(&autosleep_lock);
 101 
 102         autosleep_state = state;
 103 
 104         __pm_relax(autosleep_ws);
 105 
 106         if (state > PM_SUSPEND_ON) {
 107                 pm_wakep_autosleep_enabled(true);
 108                 queue_up_suspend_work();
 109         } else {
 110                 pm_wakep_autosleep_enabled(false);
 111         }
 112 
 113         mutex_unlock(&autosleep_lock);
 114         return 0;
 115 }
 116 
 117 int __init pm_autosleep_init(void)
 118 {
 119         autosleep_ws = wakeup_source_register(NULL, "autosleep");
 120         if (!autosleep_ws)
 121                 return -ENOMEM;
 122 
 123         autosleep_wq = alloc_ordered_workqueue("autosleep", 0);
 124         if (autosleep_wq)
 125                 return 0;
 126 
 127         wakeup_source_unregister(autosleep_ws);
 128         return -ENOMEM;
 129 }

/* [<][>][^][v][top][bottom][index][help] */