1 /* 2 * Copyright (C) 2011 Google, Inc. 3 * 4 * Author: 5 * Colin Cross <[email protected]> 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 24 static DEFINE_RWLOCK(cpu_pm_notifier_lock); 25 static RAW_NOTIFIER_HEAD(cpu_pm_notifier_chain); 26 27 static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls) 28 { 29 int ret; 30 31 ret = __raw_notifier_call_chain(&cpu_pm_notifier_chain, event, NULL, 32 nr_to_call, nr_calls); 33 34 return notifier_to_errno(ret); 35 } 36 37 /** 38 * cpu_pm_register_notifier - register a driver with cpu_pm 39 * @nb: notifier block to register 40 * 41 * Add a driver to a list of drivers that are notified about 42 * CPU and CPU cluster low power entry and exit. 43 * 44 * This function may sleep, and has the same return conditions as 45 * raw_notifier_chain_register. 46 */ 47 int cpu_pm_register_notifier(struct notifier_block *nb) 48 { 49 unsigned long flags; 50 int ret; 51 52 write_lock_irqsave(&cpu_pm_notifier_lock, flags); 53 ret = raw_notifier_chain_register(&cpu_pm_notifier_chain, nb); 54 write_unlock_irqrestore(&cpu_pm_notifier_lock, flags); 55 56 return ret; 57 } 58 EXPORT_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 */ 69 int cpu_pm_unregister_notifier(struct notifier_block *nb) 70 { 71 unsigned long flags; 72 int ret; 73 74 write_lock_irqsave(&cpu_pm_notifier_lock, flags); 75 ret = raw_notifier_chain_unregister(&cpu_pm_notifier_chain, nb); 76 write_unlock_irqrestore(&cpu_pm_notifier_lock, flags); 77 78 return ret; 79 } 80 EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier); 81 82 /** 83 * cpm_pm_enter - CPU low power entry notifier 84 * 85 * Notifies listeners that a single CPU is entering a low power state that may 86 * cause some blocks in the same power domain as the cpu to reset. 87 * 88 * Must be called on the affected CPU with interrupts disabled. Platform is 89 * responsible for ensuring that cpu_pm_enter is not called twice on the same 90 * CPU before cpu_pm_exit is called. Notified drivers can include VFP 91 * co-processor, interrupt controller and it's PM extensions, local CPU 92 * timers context save/restore which shouldn't be interrupted. Hence it 93 * must be called with interrupts disabled. 94 * 95 * Return conditions are same as __raw_notifier_call_chain. 96 */ 97 int cpu_pm_enter(void) 98 { 99 int nr_calls; 100 int ret = 0; 101 102 read_lock(&cpu_pm_notifier_lock); 103 ret = cpu_pm_notify(CPU_PM_ENTER, -1, &nr_calls); 104 if (ret) 105 /* 106 * Inform listeners (nr_calls - 1) about failure of CPU PM 107 * PM entry who are notified earlier to prepare for it. 108 */ 109 cpu_pm_notify(CPU_PM_ENTER_FAILED, nr_calls - 1, NULL); 110 read_unlock(&cpu_pm_notifier_lock); 111 112 return ret; 113 } 114 EXPORT_SYMBOL_GPL(cpu_pm_enter); 115 116 /** 117 * cpm_pm_exit - CPU low power exit notifier 118 * 119 * Notifies listeners that a single CPU is exiting a low power state that may 120 * have caused some blocks in the same power domain as the cpu to reset. 121 * 122 * Notified drivers can include VFP co-processor, interrupt controller 123 * and it's PM extensions, local CPU timers context save/restore which 124 * shouldn't be interrupted. Hence it must be called with interrupts disabled. 125 * 126 * Return conditions are same as __raw_notifier_call_chain. 127 */ 128 int cpu_pm_exit(void) 129 { 130 int ret; 131 132 read_lock(&cpu_pm_notifier_lock); 133 ret = cpu_pm_notify(CPU_PM_EXIT, -1, NULL); 134 read_unlock(&cpu_pm_notifier_lock); 135 136 return ret; 137 } 138 EXPORT_SYMBOL_GPL(cpu_pm_exit); 139 140 /** 141 * cpm_cluster_pm_enter - CPU cluster low power entry notifier 142 * 143 * Notifies listeners that all cpus in a power domain are entering a low power 144 * state that may cause some blocks in the same power domain to reset. 145 * 146 * Must be called after cpu_pm_enter has been called on all cpus in the power 147 * domain, and before cpu_pm_exit has been called on any cpu in the power 148 * domain. Notified drivers can include VFP co-processor, interrupt controller 149 * and it's PM extensions, local CPU timers context save/restore which 150 * shouldn't be interrupted. Hence it must be called with interrupts disabled. 151 * 152 * Must be called with interrupts disabled. 153 * 154 * Return conditions are same as __raw_notifier_call_chain. 155 */ 156 int cpu_cluster_pm_enter(void) 157 { 158 int nr_calls; 159 int ret = 0; 160 161 read_lock(&cpu_pm_notifier_lock); 162 ret = cpu_pm_notify(CPU_CLUSTER_PM_ENTER, -1, &nr_calls); 163 if (ret) 164 /* 165 * Inform listeners (nr_calls - 1) about failure of CPU cluster 166 * PM entry who are notified earlier to prepare for it. 167 */ 168 cpu_pm_notify(CPU_CLUSTER_PM_ENTER_FAILED, nr_calls - 1, NULL); 169 read_unlock(&cpu_pm_notifier_lock); 170 171 return ret; 172 } 173 EXPORT_SYMBOL_GPL(cpu_cluster_pm_enter); 174 175 /** 176 * cpm_cluster_pm_exit - CPU cluster low power exit notifier 177 * 178 * Notifies listeners that all cpus in a power domain are exiting form a 179 * low power state that may have caused some blocks in the same power domain 180 * to reset. 181 * 182 * Must be called after cpu_pm_exit has been called on all cpus in the power 183 * domain, and before cpu_pm_exit has been called on any cpu in the power 184 * domain. Notified drivers can include VFP co-processor, interrupt controller 185 * and it's PM extensions, local CPU timers context save/restore which 186 * shouldn't be interrupted. Hence it must be called with interrupts disabled. 187 * 188 * Return conditions are same as __raw_notifier_call_chain. 189 */ 190 int cpu_cluster_pm_exit(void) 191 { 192 int ret; 193 194 read_lock(&cpu_pm_notifier_lock); 195 ret = cpu_pm_notify(CPU_CLUSTER_PM_EXIT, -1, NULL); 196 read_unlock(&cpu_pm_notifier_lock); 197 198 return ret; 199 } 200 EXPORT_SYMBOL_GPL(cpu_cluster_pm_exit); 201