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 DEFINE_RWLOCK(cpu_pm_notifier_lock);
  26static RAW_NOTIFIER_HEAD(cpu_pm_notifier_chain);
  27
  28static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls)
  29{
  30        int ret;
  31
  32        ret = __raw_notifier_call_chain(&cpu_pm_notifier_chain, event, NULL,
  33                nr_to_call, nr_calls);
  34
  35        return notifier_to_errno(ret);
  36}
  37
  38/**
  39 * cpu_pm_register_notifier - register a driver with cpu_pm
  40 * @nb: notifier block to register
  41 *
  42 * Add a driver to a list of drivers that are notified about
  43 * CPU and CPU cluster low power entry and exit.
  44 *
  45 * This function may sleep, and has the same return conditions as
  46 * raw_notifier_chain_register.
  47 */
  48int cpu_pm_register_notifier(struct notifier_block *nb)
  49{
  50        unsigned long flags;
  51        int ret;
  52
  53        write_lock_irqsave(&cpu_pm_notifier_lock, flags);
  54        ret = raw_notifier_chain_register(&cpu_pm_notifier_chain, nb);
  55        write_unlock_irqrestore(&cpu_pm_notifier_lock, flags);
  56
  57        return ret;
  58}
  59EXPORT_SYMBOL_GPL(cpu_pm_register_notifier);
  60
  61/**
  62 * cpu_pm_unregister_notifier - unregister a driver with cpu_pm
  63 * @nb: notifier block to be unregistered
  64 *
  65 * Remove a driver from the CPU PM notifier list.
  66 *
  67 * This function may sleep, and has the same return conditions as
  68 * raw_notifier_chain_unregister.
  69 */
  70int cpu_pm_unregister_notifier(struct notifier_block *nb)
  71{
  72        unsigned long flags;
  73        int ret;
  74
  75        write_lock_irqsave(&cpu_pm_notifier_lock, flags);
  76        ret = raw_notifier_chain_unregister(&cpu_pm_notifier_chain, nb);
  77        write_unlock_irqrestore(&cpu_pm_notifier_lock, flags);
  78
  79        return ret;
  80}
  81EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier);
  82
  83/**
  84 * cpu_pm_enter - CPU low power entry notifier
  85 *
  86 * Notifies listeners that a single CPU is entering a low power state that may
  87 * cause some blocks in the same power domain as the cpu to reset.
  88 *
  89 * Must be called on the affected CPU with interrupts disabled.  Platform is
  90 * responsible for ensuring that cpu_pm_enter is not called twice on the same
  91 * CPU before cpu_pm_exit is called. Notified drivers can include VFP
  92 * co-processor, interrupt controller and its PM extensions, local CPU
  93 * timers context save/restore which shouldn't be interrupted. Hence it
  94 * must be called with interrupts disabled.
  95 *
  96 * Return conditions are same as __raw_notifier_call_chain.
  97 */
  98int cpu_pm_enter(void)
  99{
 100        int nr_calls;
 101        int ret = 0;
 102
 103        read_lock(&cpu_pm_notifier_lock);
 104        ret = cpu_pm_notify(CPU_PM_ENTER, -1, &nr_calls);
 105        if (ret)
 106                /*
 107                 * Inform listeners (nr_calls - 1) about failure of CPU PM
 108                 * PM entry who are notified earlier to prepare for it.
 109                 */
 110                cpu_pm_notify(CPU_PM_ENTER_FAILED, nr_calls - 1, NULL);
 111        read_unlock(&cpu_pm_notifier_lock);
 112
 113        return ret;
 114}
 115EXPORT_SYMBOL_GPL(cpu_pm_enter);
 116
 117/**
 118 * cpu_pm_exit - CPU low power exit notifier
 119 *
 120 * Notifies listeners that a single CPU is exiting a low power state that may
 121 * have caused some blocks in the same power domain as the cpu to reset.
 122 *
 123 * Notified drivers can include VFP co-processor, interrupt controller
 124 * and its PM extensions, local CPU timers context save/restore which
 125 * shouldn't be interrupted. Hence it must be called with interrupts disabled.
 126 *
 127 * Return conditions are same as __raw_notifier_call_chain.
 128 */
 129int cpu_pm_exit(void)
 130{
 131        int ret;
 132
 133        read_lock(&cpu_pm_notifier_lock);
 134        ret = cpu_pm_notify(CPU_PM_EXIT, -1, NULL);
 135        read_unlock(&cpu_pm_notifier_lock);
 136
 137        return ret;
 138}
 139EXPORT_SYMBOL_GPL(cpu_pm_exit);
 140
 141/**
 142 * cpu_cluster_pm_enter - CPU cluster low power entry notifier
 143 *
 144 * Notifies listeners that all cpus in a power domain are entering a low power
 145 * state that may cause some blocks in the same power domain to reset.
 146 *
 147 * Must be called after cpu_pm_enter has been called on all cpus in the power
 148 * domain, and before cpu_pm_exit has been called on any cpu in the power
 149 * domain. Notified drivers can include VFP co-processor, interrupt controller
 150 * and its PM extensions, local CPU timers context save/restore which
 151 * shouldn't be interrupted. Hence it must be called with interrupts disabled.
 152 *
 153 * Must be called with interrupts disabled.
 154 *
 155 * Return conditions are same as __raw_notifier_call_chain.
 156 */
 157int cpu_cluster_pm_enter(void)
 158{
 159        int nr_calls;
 160        int ret = 0;
 161
 162        read_lock(&cpu_pm_notifier_lock);
 163        ret = cpu_pm_notify(CPU_CLUSTER_PM_ENTER, -1, &nr_calls);
 164        if (ret)
 165                /*
 166                 * Inform listeners (nr_calls - 1) about failure of CPU cluster
 167                 * PM entry who are notified earlier to prepare for it.
 168                 */
 169                cpu_pm_notify(CPU_CLUSTER_PM_ENTER_FAILED, nr_calls - 1, NULL);
 170        read_unlock(&cpu_pm_notifier_lock);
 171
 172        return ret;
 173}
 174EXPORT_SYMBOL_GPL(cpu_cluster_pm_enter);
 175
 176/**
 177 * cpu_cluster_pm_exit - CPU cluster low power exit notifier
 178 *
 179 * Notifies listeners that all cpus in a power domain are exiting form a
 180 * low power state that may have caused some blocks in the same power domain
 181 * to reset.
 182 *
 183 * Must be called after cpu_cluster_pm_enter has been called for the power
 184 * domain, and before cpu_pm_exit has been called on any cpu in the power
 185 * domain. Notified drivers can include VFP co-processor, interrupt controller
 186 * and its PM extensions, local CPU timers context save/restore which
 187 * shouldn't be interrupted. Hence it must be called with interrupts disabled.
 188 *
 189 * Return conditions are same as __raw_notifier_call_chain.
 190 */
 191int cpu_cluster_pm_exit(void)
 192{
 193        int ret;
 194
 195        read_lock(&cpu_pm_notifier_lock);
 196        ret = cpu_pm_notify(CPU_CLUSTER_PM_EXIT, -1, NULL);
 197        read_unlock(&cpu_pm_notifier_lock);
 198
 199        return ret;
 200}
 201EXPORT_SYMBOL_GPL(cpu_cluster_pm_exit);
 202
 203#ifdef CONFIG_PM
 204static int cpu_pm_suspend(void)
 205{
 206        int ret;
 207
 208        ret = cpu_pm_enter();
 209        if (ret)
 210                return ret;
 211
 212        ret = cpu_cluster_pm_enter();
 213        return ret;
 214}
 215
 216static void cpu_pm_resume(void)
 217{
 218        cpu_cluster_pm_exit();
 219        cpu_pm_exit();
 220}
 221
 222static struct syscore_ops cpu_pm_syscore_ops = {
 223        .suspend = cpu_pm_suspend,
 224        .resume = cpu_pm_resume,
 225};
 226
 227static int cpu_pm_init(void)
 228{
 229        register_syscore_ops(&cpu_pm_syscore_ops);
 230        return 0;
 231}
 232core_initcall(cpu_pm_init);
 233#endif
 234