1 /* 2 * driver.c - driver support 3 * 4 * (C) 2006-2007 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> 5 * Shaohua Li <shaohua.li@intel.com> 6 * Adam Belay <abelay@novell.com> 7 * 8 * This code is licenced under the GPL. 9 */ 10 11 #include <linux/mutex.h> 12 #include <linux/module.h> 13 #include <linux/cpuidle.h> 14 15 #include "cpuidle.h" 16 17 DEFINE_SPINLOCK(cpuidle_driver_lock); 18 19 static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu); 20 static struct cpuidle_driver * __cpuidle_get_cpu_driver(int cpu); 21 22 static void set_power_states(struct cpuidle_driver *drv) 23 { 24 int i; 25 26 /* 27 * cpuidle driver should set the drv->power_specified bit 28 * before registering if the driver provides 29 * power_usage numbers. 30 * 31 * If power_specified is not set, 32 * we fill in power_usage with decreasing values as the 33 * cpuidle code has an implicit assumption that state Cn 34 * uses less power than C(n-1). 35 * 36 * With CONFIG_ARCH_HAS_CPU_RELAX, C0 is already assigned 37 * an power value of -1. So we use -2, -3, etc, for other 38 * c-states. 39 */ 40 for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) 41 drv->states[i].power_usage = -1 - i; 42 } 43 44 static void __cpuidle_driver_init(struct cpuidle_driver *drv) 45 { 46 drv->refcnt = 0; 47 48 if (!drv->power_specified) 49 set_power_states(drv); 50 } 51 52 static int __cpuidle_register_driver(struct cpuidle_driver *drv, int cpu) 53 { 54 if (!drv || !drv->state_count) 55 return -EINVAL; 56 57 if (cpuidle_disabled()) 58 return -ENODEV; 59 60 if (__cpuidle_get_cpu_driver(cpu)) 61 return -EBUSY; 62 63 __cpuidle_driver_init(drv); 64 65 __cpuidle_set_cpu_driver(drv, cpu); 66 67 return 0; 68 } 69 70 static void __cpuidle_unregister_driver(struct cpuidle_driver *drv, int cpu) 71 { 72 if (drv != __cpuidle_get_cpu_driver(cpu)) 73 return; 74 75 if (!WARN_ON(drv->refcnt > 0)) 76 __cpuidle_set_cpu_driver(NULL, cpu); 77 } 78 79 #ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS 80 81 static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers); 82 83 static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu) 84 { 85 per_cpu(cpuidle_drivers, cpu) = drv; 86 } 87 88 static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) 89 { 90 return per_cpu(cpuidle_drivers, cpu); 91 } 92 93 static void __cpuidle_unregister_all_cpu_driver(struct cpuidle_driver *drv) 94 { 95 int cpu; 96 for_each_present_cpu(cpu) 97 __cpuidle_unregister_driver(drv, cpu); 98 } 99 100 static int __cpuidle_register_all_cpu_driver(struct cpuidle_driver *drv) 101 { 102 int ret = 0; 103 int i, cpu; 104 105 for_each_present_cpu(cpu) { 106 ret = __cpuidle_register_driver(drv, cpu); 107 if (ret) 108 break; 109 } 110 111 if (ret) 112 for_each_present_cpu(i) { 113 if (i == cpu) 114 break; 115 __cpuidle_unregister_driver(drv, i); 116 } 117 118 119 return ret; 120 } 121 122 int cpuidle_register_cpu_driver(struct cpuidle_driver *drv, int cpu) 123 { 124 int ret; 125 126 spin_lock(&cpuidle_driver_lock); 127 ret = __cpuidle_register_driver(drv, cpu); 128 spin_unlock(&cpuidle_driver_lock); 129 130 return ret; 131 } 132 133 void cpuidle_unregister_cpu_driver(struct cpuidle_driver *drv, int cpu) 134 { 135 spin_lock(&cpuidle_driver_lock); 136 __cpuidle_unregister_driver(drv, cpu); 137 spin_unlock(&cpuidle_driver_lock); 138 } 139 140 /** 141 * cpuidle_register_driver - registers a driver 142 * @drv: the driver 143 */ 144 int cpuidle_register_driver(struct cpuidle_driver *drv) 145 { 146 int ret; 147 148 spin_lock(&cpuidle_driver_lock); 149 ret = __cpuidle_register_all_cpu_driver(drv); 150 spin_unlock(&cpuidle_driver_lock); 151 152 return ret; 153 } 154 EXPORT_SYMBOL_GPL(cpuidle_register_driver); 155 156 /** 157 * cpuidle_unregister_driver - unregisters a driver 158 * @drv: the driver 159 */ 160 void cpuidle_unregister_driver(struct cpuidle_driver *drv) 161 { 162 spin_lock(&cpuidle_driver_lock); 163 __cpuidle_unregister_all_cpu_driver(drv); 164 spin_unlock(&cpuidle_driver_lock); 165 } 166 EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); 167 168 #else 169 170 static struct cpuidle_driver *cpuidle_curr_driver; 171 172 static inline void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu) 173 { 174 cpuidle_curr_driver = drv; 175 } 176 177 static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) 178 { 179 return cpuidle_curr_driver; 180 } 181 182 /** 183 * cpuidle_register_driver - registers a driver 184 * @drv: the driver 185 */ 186 int cpuidle_register_driver(struct cpuidle_driver *drv) 187 { 188 int ret, cpu; 189 190 cpu = get_cpu(); 191 spin_lock(&cpuidle_driver_lock); 192 ret = __cpuidle_register_driver(drv, cpu); 193 spin_unlock(&cpuidle_driver_lock); 194 put_cpu(); 195 196 return ret; 197 } 198 EXPORT_SYMBOL_GPL(cpuidle_register_driver); 199 200 /** 201 * cpuidle_unregister_driver - unregisters a driver 202 * @drv: the driver 203 */ 204 void cpuidle_unregister_driver(struct cpuidle_driver *drv) 205 { 206 int cpu; 207 208 cpu = get_cpu(); 209 spin_lock(&cpuidle_driver_lock); 210 __cpuidle_unregister_driver(drv, cpu); 211 spin_unlock(&cpuidle_driver_lock); 212 put_cpu(); 213 } 214 EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); 215 #endif 216 217 /** 218 * cpuidle_get_driver - return the current driver 219 */ 220 struct cpuidle_driver *cpuidle_get_driver(void) 221 { 222 struct cpuidle_driver *drv; 223 int cpu; 224 225 cpu = get_cpu(); 226 drv = __cpuidle_get_cpu_driver(cpu); 227 put_cpu(); 228 229 return drv; 230 } 231 EXPORT_SYMBOL_GPL(cpuidle_get_driver); 232 233 /** 234 * cpuidle_get_cpu_driver - return the driver tied with a cpu 235 */ 236 struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev) 237 { 238 struct cpuidle_driver *drv; 239 240 if (!dev) 241 return NULL; 242 243 spin_lock(&cpuidle_driver_lock); 244 drv = __cpuidle_get_cpu_driver(dev->cpu); 245 spin_unlock(&cpuidle_driver_lock); 246 247 return drv; 248 } 249 EXPORT_SYMBOL_GPL(cpuidle_get_cpu_driver); 250 251 struct cpuidle_driver *cpuidle_driver_ref(void) 252 { 253 struct cpuidle_driver *drv; 254 255 spin_lock(&cpuidle_driver_lock); 256 257 drv = cpuidle_get_driver(); 258 drv->refcnt++; 259 260 spin_unlock(&cpuidle_driver_lock); 261 return drv; 262 } 263 264 void cpuidle_driver_unref(void) 265 { 266 struct cpuidle_driver *drv = cpuidle_get_driver(); 267 268 spin_lock(&cpuidle_driver_lock); 269 270 if (drv && !WARN_ON(drv->refcnt <= 0)) 271 drv->refcnt--; 272 273 spin_unlock(&cpuidle_driver_lock); 274 } 275