linux/kernel/cpu_pm.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2011 Google, Inc.
   3 *
   4 * Author:
   5 *      Colin Cross <ccross@android.com>
   6 *
   7 * This software is licensed under the terms of the GNU General Public
   8 * License version 2, as published by the Free Software Foundation, and
   9 * may be copied, distributed, and modified under those terms.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 */
  17
  18#include <linux/kernel.h>
  19#include <linux/cpu_pm.h>
  20#include <linux/module.h>
  21#include <linux/notifier.h>
  22#include <linux/spinlock.h>
  23#include <linux/syscore_ops.h>
  24
  25static ATOMIC_NOTIFIER_HEAD(cpu_pm_notifier_chain);
  26
  27static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls)
  28{
  29        int ret;
  30
  31        /*
  32         * __atomic_notifier_call_chain has a RCU read critical section, which
  33         * could be disfunctional in cpu idle. Copy RCU_NONIDLE code to let
  34         * RCU know this.
  35         */
  36        rcu_irq_enter_irqson();
  37        ret = __atomic_notifier_call_chain(&cpu_pm_notifier_chain, event, NULL,
  38                nr_to_call, nr_calls);
  39        rcu_irq_exit_irqson();
  40
  41        return notifier_to_errno(ret);
  42}
  43
  44/**
  45 * cpu_pm_register_notifier - register a driver with cpu_pm
  46 * @nb: notifier block to register
  47 *
  48 * Add a driver to a list of drivers that are notified about
  49 * CPU and CPU cluster low power entry and exit.
  50 *
  51 * This function may sleep, and has the same return conditions as
  52 * raw_notifier_chain_register.
  53 */
  54int cpu_pm_register_notifier(struct notifier_block *nb)
  55{
  56        return atomic_notifier_chain_register(&cpu_pm_notifier_chain, nb);
  57}
  58EXPORT_SYMBOL_GPL(cpu_pm_register_notifier);
  59
  60/**
  61 * cpu_pm_unregister_notifier - unregister a driver with cpu_pm
  62 * @nb: notifier block to be unregistered
  63 *
  64 * Remove a driver from the CPU PM notifier list.
  65 *
  66 * This function may sleep, and has the same return conditions as
  67 * raw_notifier_chain_unregister.
  68 */
  69int cpu_pm_unregister_notifier(struct notifier_block *nb)
  70{
  71        return atomic_notifier_chain_unregister(&cpu_pm_notifier_chain, nb);
  72}
  73EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier);
  74
  75/**
  76 * cpu_pm_enter - CPU low power entry notifier
  77 *
  78 * Notifies listeners that a single CPU is entering a low power state that may
  79 * cause some blocks in the same power domain as the cpu to reset.
  80 *
  81 * Must be called on the affected CPU with interrupts disabled.  Platform is
  82 * responsible for ensuring that cpu_pm_enter is not called twice on the same
  83 * CPU before cpu_pm_exit is called. Notified drivers can include VFP
  84 * co-processor, interrupt controller and its PM extensions, local CPU
  85 * timers context save/restore which shouldn't be interrupted. Hence it
  86 * must be called with interrupts disabled.
  87 *
  88 * Return conditions are same as __raw_notifier_call_chain.
  89 */
  90int cpu_pm_enter(void)
  91{
  92        int nr_calls;
  93        int ret = 0;
  94
  95        ret = cpu_pm_notify(CPU_PM_ENTER, -1, &nr_calls);
  96        if (ret)
  97                /*
  98                 * Inform listeners (nr_calls - 1) about failure of CPU PM
  99                 * PM entry who are notified earlier to prepare for it.
 100                 */
 101                cpu_pm_notify(CPU_PM_ENTER_FAILED, nr_calls - 1, NULL);
 102
 103        return ret;
 104}
 105EXPORT_SYMBOL_GPL(cpu_pm_enter);
 106
 107/**
 108 * cpu_pm_exit - CPU low power exit notifier
 109 *
 110 * Notifies listeners that a single CPU is exiting a low power state that may
 111 * have caused some blocks in the same power domain as the cpu to reset.
 112 *
 113 * Notified drivers can include VFP co-processor, interrupt controller
 114 * and its PM extensions, local CPU timers context save/restore which
 115 * shouldn't be interrupted. Hence it must be called with interrupts disabled.
 116 *
 117 * Return conditions are same as __raw_notifier_call_chain.
 118 */
 119int cpu_pm_exit(void)
 120{
 121        return cpu_pm_notify(CPU_PM_EXIT, -1, NULL);
 122}
 123EXPORT_SYMBOL_GPL(cpu_pm_exit);
 124
 125/**
 126 * cpu_cluster_pm_enter - CPU cluster low power entry notifier
 127 *
 128 * Notifies listeners that all cpus in a power domain are entering a low power
 129 * state that may cause some blocks in the same power domain to reset.
 130 *
 131 * Must be called after cpu_pm_enter has been called on all cpus in the power
 132 * domain, and before cpu_pm_exit has been called on any cpu in the power
 133 * domain. Notified drivers can include VFP co-processor, interrupt controller
 134 * and its PM extensions, local CPU timers context save/restore which
 135 * shouldn't be interrupted. Hence it must be called with interrupts disabled.
 136 *
 137 * Must be called with interrupts disabled.
 138 *
 139 * Return conditions are same as __raw_notifier_call_chain.
 140 */
 141int cpu_cluster_pm_enter(void)
 142{
 143        int nr_calls;
 144        int ret = 0;
 145
 146        ret = cpu_pm_notify(CPU_CLUSTER_PM_ENTER, -1, &nr_calls);
 147        if (ret)
 148                /*
 149                 * Inform listeners (nr_calls - 1) about failure of CPU cluster
 150                 * PM entry who are notified earlier to prepare for it.
 151                 */
 152                cpu_pm_notify(CPU_CLUSTER_PM_ENTER_FAILED, nr_calls - 1, NULL);
 153
 154        return ret;
 155}
 156EXPORT_SYMBOL_GPL(cpu_cluster_pm_enter);
 157
 158/**
 159 * cpu_cluster_pm_exit - CPU cluster low power exit notifier
 160 *
 161 * Notifies listeners that all cpus in a power domain are exiting form a
 162 * low power state that may have caused some blocks in the same power domain
 163 * to reset.
 164 *
 165 * Must be called after cpu_cluster_pm_enter has been called for the power
 166 * domain, and before cpu_pm_exit has been called on any cpu in the power
 167 * domain. Notified drivers can include VFP co-processor, interrupt controller
 168 * and its PM extensions, local CPU timers context save/restore which
 169 * shouldn't be interrupted. Hence it must be called with interrupts disabled.
 170 *
 171 * Return conditions are same as __raw_notifier_call_chain.
 172 */
 173int cpu_cluster_pm_exit(void)
 174{
 175        return cpu_pm_notify(CPU_CLUSTER_PM_EXIT, -1, NULL);
 176}
 177EXPORT_SYMBOL_GPL(cpu_cluster_pm_exit);
 178
 179#ifdef CONFIG_PM
 180static int cpu_pm_suspend(void)
 181{
 182        int ret;
 183
 184        ret = cpu_pm_enter();
 185        if (ret)
 186                return ret;
 187
 188        ret = cpu_cluster_pm_enter();
 189        return ret;
 190}
 191
 192static void cpu_pm_resume(void)
 193{
 194        cpu_cluster_pm_exit();
 195        cpu_pm_exit();
 196}
 197
 198static struct syscore_ops cpu_pm_syscore_ops = {
 199        .suspend = cpu_pm_suspend,
 200        .resume = cpu_pm_resume,
 201};
 202
 203static int cpu_pm_init(void)
 204{
 205        register_syscore_ops(&cpu_pm_syscore_ops);
 206        return 0;
 207}
 208core_initcall(cpu_pm_init);
 209#endif
 210