Back to home page

LXR

 
 

    


0001 /*
0002  * Copyright (C) 2011 Google, Inc.
0003  *
0004  * Author:
0005  *  Colin Cross <ccross@android.com>
0006  *
0007  * This software is licensed under the terms of the GNU General Public
0008  * License version 2, as published by the Free Software Foundation, and
0009  * may be copied, distributed, and modified under those terms.
0010  *
0011  * This program is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014  * GNU General Public License for more details.
0015  *
0016  */
0017 
0018 #include <linux/kernel.h>
0019 #include <linux/cpu_pm.h>
0020 #include <linux/module.h>
0021 #include <linux/notifier.h>
0022 #include <linux/spinlock.h>
0023 #include <linux/syscore_ops.h>
0024 
0025 static DEFINE_RWLOCK(cpu_pm_notifier_lock);
0026 static RAW_NOTIFIER_HEAD(cpu_pm_notifier_chain);
0027 
0028 static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls)
0029 {
0030     int ret;
0031 
0032     ret = __raw_notifier_call_chain(&cpu_pm_notifier_chain, event, NULL,
0033         nr_to_call, nr_calls);
0034 
0035     return notifier_to_errno(ret);
0036 }
0037 
0038 /**
0039  * cpu_pm_register_notifier - register a driver with cpu_pm
0040  * @nb: notifier block to register
0041  *
0042  * Add a driver to a list of drivers that are notified about
0043  * CPU and CPU cluster low power entry and exit.
0044  *
0045  * This function may sleep, and has the same return conditions as
0046  * raw_notifier_chain_register.
0047  */
0048 int cpu_pm_register_notifier(struct notifier_block *nb)
0049 {
0050     unsigned long flags;
0051     int ret;
0052 
0053     write_lock_irqsave(&cpu_pm_notifier_lock, flags);
0054     ret = raw_notifier_chain_register(&cpu_pm_notifier_chain, nb);
0055     write_unlock_irqrestore(&cpu_pm_notifier_lock, flags);
0056 
0057     return ret;
0058 }
0059 EXPORT_SYMBOL_GPL(cpu_pm_register_notifier);
0060 
0061 /**
0062  * cpu_pm_unregister_notifier - unregister a driver with cpu_pm
0063  * @nb: notifier block to be unregistered
0064  *
0065  * Remove a driver from the CPU PM notifier list.
0066  *
0067  * This function may sleep, and has the same return conditions as
0068  * raw_notifier_chain_unregister.
0069  */
0070 int cpu_pm_unregister_notifier(struct notifier_block *nb)
0071 {
0072     unsigned long flags;
0073     int ret;
0074 
0075     write_lock_irqsave(&cpu_pm_notifier_lock, flags);
0076     ret = raw_notifier_chain_unregister(&cpu_pm_notifier_chain, nb);
0077     write_unlock_irqrestore(&cpu_pm_notifier_lock, flags);
0078 
0079     return ret;
0080 }
0081 EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier);
0082 
0083 /**
0084  * cpu_pm_enter - CPU low power entry notifier
0085  *
0086  * Notifies listeners that a single CPU is entering a low power state that may
0087  * cause some blocks in the same power domain as the cpu to reset.
0088  *
0089  * Must be called on the affected CPU with interrupts disabled.  Platform is
0090  * responsible for ensuring that cpu_pm_enter is not called twice on the same
0091  * CPU before cpu_pm_exit is called. Notified drivers can include VFP
0092  * co-processor, interrupt controller and its PM extensions, local CPU
0093  * timers context save/restore which shouldn't be interrupted. Hence it
0094  * must be called with interrupts disabled.
0095  *
0096  * Return conditions are same as __raw_notifier_call_chain.
0097  */
0098 int cpu_pm_enter(void)
0099 {
0100     int nr_calls;
0101     int ret = 0;
0102 
0103     read_lock(&cpu_pm_notifier_lock);
0104     ret = cpu_pm_notify(CPU_PM_ENTER, -1, &nr_calls);
0105     if (ret)
0106         /*
0107          * Inform listeners (nr_calls - 1) about failure of CPU PM
0108          * PM entry who are notified earlier to prepare for it.
0109          */
0110         cpu_pm_notify(CPU_PM_ENTER_FAILED, nr_calls - 1, NULL);
0111     read_unlock(&cpu_pm_notifier_lock);
0112 
0113     return ret;
0114 }
0115 EXPORT_SYMBOL_GPL(cpu_pm_enter);
0116 
0117 /**
0118  * cpu_pm_exit - CPU low power exit notifier
0119  *
0120  * Notifies listeners that a single CPU is exiting a low power state that may
0121  * have caused some blocks in the same power domain as the cpu to reset.
0122  *
0123  * Notified drivers can include VFP co-processor, interrupt controller
0124  * and its PM extensions, local CPU timers context save/restore which
0125  * shouldn't be interrupted. Hence it must be called with interrupts disabled.
0126  *
0127  * Return conditions are same as __raw_notifier_call_chain.
0128  */
0129 int cpu_pm_exit(void)
0130 {
0131     int ret;
0132 
0133     read_lock(&cpu_pm_notifier_lock);
0134     ret = cpu_pm_notify(CPU_PM_EXIT, -1, NULL);
0135     read_unlock(&cpu_pm_notifier_lock);
0136 
0137     return ret;
0138 }
0139 EXPORT_SYMBOL_GPL(cpu_pm_exit);
0140 
0141 /**
0142  * cpu_cluster_pm_enter - CPU cluster low power entry notifier
0143  *
0144  * Notifies listeners that all cpus in a power domain are entering a low power
0145  * state that may cause some blocks in the same power domain to reset.
0146  *
0147  * Must be called after cpu_pm_enter has been called on all cpus in the power
0148  * domain, and before cpu_pm_exit has been called on any cpu in the power
0149  * domain. Notified drivers can include VFP co-processor, interrupt controller
0150  * and its PM extensions, local CPU timers context save/restore which
0151  * shouldn't be interrupted. Hence it must be called with interrupts disabled.
0152  *
0153  * Must be called with interrupts disabled.
0154  *
0155  * Return conditions are same as __raw_notifier_call_chain.
0156  */
0157 int cpu_cluster_pm_enter(void)
0158 {
0159     int nr_calls;
0160     int ret = 0;
0161 
0162     read_lock(&cpu_pm_notifier_lock);
0163     ret = cpu_pm_notify(CPU_CLUSTER_PM_ENTER, -1, &nr_calls);
0164     if (ret)
0165         /*
0166          * Inform listeners (nr_calls - 1) about failure of CPU cluster
0167          * PM entry who are notified earlier to prepare for it.
0168          */
0169         cpu_pm_notify(CPU_CLUSTER_PM_ENTER_FAILED, nr_calls - 1, NULL);
0170     read_unlock(&cpu_pm_notifier_lock);
0171 
0172     return ret;
0173 }
0174 EXPORT_SYMBOL_GPL(cpu_cluster_pm_enter);
0175 
0176 /**
0177  * cpu_cluster_pm_exit - CPU cluster low power exit notifier
0178  *
0179  * Notifies listeners that all cpus in a power domain are exiting form a
0180  * low power state that may have caused some blocks in the same power domain
0181  * to reset.
0182  *
0183  * Must be called after cpu_cluster_pm_enter has been called for the power
0184  * domain, and before cpu_pm_exit has been called on any cpu in the power
0185  * domain. Notified drivers can include VFP co-processor, interrupt controller
0186  * and its PM extensions, local CPU timers context save/restore which
0187  * shouldn't be interrupted. Hence it must be called with interrupts disabled.
0188  *
0189  * Return conditions are same as __raw_notifier_call_chain.
0190  */
0191 int cpu_cluster_pm_exit(void)
0192 {
0193     int ret;
0194 
0195     read_lock(&cpu_pm_notifier_lock);
0196     ret = cpu_pm_notify(CPU_CLUSTER_PM_EXIT, -1, NULL);
0197     read_unlock(&cpu_pm_notifier_lock);
0198 
0199     return ret;
0200 }
0201 EXPORT_SYMBOL_GPL(cpu_cluster_pm_exit);
0202 
0203 #ifdef CONFIG_PM
0204 static int cpu_pm_suspend(void)
0205 {
0206     int ret;
0207 
0208     ret = cpu_pm_enter();
0209     if (ret)
0210         return ret;
0211 
0212     ret = cpu_cluster_pm_enter();
0213     return ret;
0214 }
0215 
0216 static void cpu_pm_resume(void)
0217 {
0218     cpu_cluster_pm_exit();
0219     cpu_pm_exit();
0220 }
0221 
0222 static struct syscore_ops cpu_pm_syscore_ops = {
0223     .suspend = cpu_pm_suspend,
0224     .resume = cpu_pm_resume,
0225 };
0226 
0227 static int cpu_pm_init(void)
0228 {
0229     register_syscore_ops(&cpu_pm_syscore_ops);
0230     return 0;
0231 }
0232 core_initcall(cpu_pm_init);
0233 #endif