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 #include <linux/cpumask.h> 15 #include <linux/clockchips.h> 16 17 #include "cpuidle.h" 18 19 DEFINE_SPINLOCK(cpuidle_driver_lock); 20 21 static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu); 22 static struct cpuidle_driver * __cpuidle_get_cpu_driver(int cpu); 23 24 static void cpuidle_setup_broadcast_timer(void *arg) 25 { 26 int cpu = smp_processor_id(); 27 clockevents_notify((long)(arg), &cpu); 28 } 29 30 static void __cpuidle_driver_init(struct cpuidle_driver *drv, int cpu) 31 { 32 int i; 33 34 drv->refcnt = 0; 35 36 for (i = drv->state_count - 1; i >= 0 ; i--) { 37 38 if (!(drv->states[i].flags & CPUIDLE_FLAG_TIMER_STOP)) 39 continue; 40 41 drv->bctimer = 1; 42 on_each_cpu_mask(get_cpu_mask(cpu), cpuidle_setup_broadcast_timer, 43 (void *)CLOCK_EVT_NOTIFY_BROADCAST_ON, 1); 44 break; 45 } 46 } 47 48 static int __cpuidle_register_driver(struct cpuidle_driver *drv, int cpu) 49 { 50 if (!drv || !drv->state_count) 51 return -EINVAL; 52 53 if (cpuidle_disabled()) 54 return -ENODEV; 55 56 if (__cpuidle_get_cpu_driver(cpu)) 57 return -EBUSY; 58 59 __cpuidle_driver_init(drv, cpu); 60 61 __cpuidle_set_cpu_driver(drv, cpu); 62 63 return 0; 64 } 65 66 static void __cpuidle_unregister_driver(struct cpuidle_driver *drv, int cpu) 67 { 68 if (drv != __cpuidle_get_cpu_driver(cpu)) 69 return; 70 71 if (!WARN_ON(drv->refcnt > 0)) 72 __cpuidle_set_cpu_driver(NULL, cpu); 73 74 if (drv->bctimer) { 75 drv->bctimer = 0; 76 on_each_cpu_mask(get_cpu_mask(cpu), cpuidle_setup_broadcast_timer, 77 (void *)CLOCK_EVT_NOTIFY_BROADCAST_OFF, 1); 78 } 79 } 80 81 #ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS 82 83 static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers); 84 85 static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu) 86 { 87 per_cpu(cpuidle_drivers, cpu) = drv; 88 } 89 90 static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) 91 { 92 return per_cpu(cpuidle_drivers, cpu); 93 } 94 95 static void __cpuidle_unregister_all_cpu_driver(struct cpuidle_driver *drv) 96 { 97 int cpu; 98 for_each_present_cpu(cpu) 99 __cpuidle_unregister_driver(drv, cpu); 100 } 101 102 static int __cpuidle_register_all_cpu_driver(struct cpuidle_driver *drv) 103 { 104 int ret = 0; 105 int i, cpu; 106 107 for_each_present_cpu(cpu) { 108 ret = __cpuidle_register_driver(drv, cpu); 109 if (ret) 110 break; 111 } 112 113 if (ret) 114 for_each_present_cpu(i) { 115 if (i == cpu) 116 break; 117 __cpuidle_unregister_driver(drv, i); 118 } 119 120 121 return ret; 122 } 123 124 int cpuidle_register_cpu_driver(struct cpuidle_driver *drv, int cpu) 125 { 126 int ret; 127 128 spin_lock(&cpuidle_driver_lock); 129 ret = __cpuidle_register_driver(drv, cpu); 130 spin_unlock(&cpuidle_driver_lock); 131 132 return ret; 133 } 134 135 void cpuidle_unregister_cpu_driver(struct cpuidle_driver *drv, int cpu) 136 { 137 spin_lock(&cpuidle_driver_lock); 138 __cpuidle_unregister_driver(drv, cpu); 139 spin_unlock(&cpuidle_driver_lock); 140 } 141 142 /** 143 * cpuidle_register_driver - registers a driver 144 * @drv: the driver 145 */ 146 int cpuidle_register_driver(struct cpuidle_driver *drv) 147 { 148 int ret; 149 150 spin_lock(&cpuidle_driver_lock); 151 ret = __cpuidle_register_all_cpu_driver(drv); 152 spin_unlock(&cpuidle_driver_lock); 153 154 return ret; 155 } 156 EXPORT_SYMBOL_GPL(cpuidle_register_driver); 157 158 /** 159 * cpuidle_unregister_driver - unregisters a driver 160 * @drv: the driver 161 */ 162 void cpuidle_unregister_driver(struct cpuidle_driver *drv) 163 { 164 spin_lock(&cpuidle_driver_lock); 165 __cpuidle_unregister_all_cpu_driver(drv); 166 spin_unlock(&cpuidle_driver_lock); 167 } 168 EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); 169 170 #else 171 172 static struct cpuidle_driver *cpuidle_curr_driver; 173 174 static inline void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu) 175 { 176 cpuidle_curr_driver = drv; 177 } 178 179 static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu) 180 { 181 return cpuidle_curr_driver; 182 } 183 184 /** 185 * cpuidle_register_driver - registers a driver 186 * @drv: the driver 187 */ 188 int cpuidle_register_driver(struct cpuidle_driver *drv) 189 { 190 int ret, cpu; 191 192 cpu = get_cpu(); 193 spin_lock(&cpuidle_driver_lock); 194 ret = __cpuidle_register_driver(drv, cpu); 195 spin_unlock(&cpuidle_driver_lock); 196 put_cpu(); 197 198 return ret; 199 } 200 EXPORT_SYMBOL_GPL(cpuidle_register_driver); 201 202 /** 203 * cpuidle_unregister_driver - unregisters a driver 204 * @drv: the driver 205 */ 206 void cpuidle_unregister_driver(struct cpuidle_driver *drv) 207 { 208 int cpu; 209 210 cpu = get_cpu(); 211 spin_lock(&cpuidle_driver_lock); 212 __cpuidle_unregister_driver(drv, cpu); 213 spin_unlock(&cpuidle_driver_lock); 214 put_cpu(); 215 } 216 EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); 217 #endif 218 219 /** 220 * cpuidle_get_driver - return the current driver 221 */ 222 struct cpuidle_driver *cpuidle_get_driver(void) 223 { 224 struct cpuidle_driver *drv; 225 int cpu; 226 227 cpu = get_cpu(); 228 drv = __cpuidle_get_cpu_driver(cpu); 229 put_cpu(); 230 231 return drv; 232 } 233 EXPORT_SYMBOL_GPL(cpuidle_get_driver); 234 235 /** 236 * cpuidle_get_cpu_driver - return the driver tied with a cpu 237 */ 238 struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev) 239 { 240 if (!dev) 241 return NULL; 242 243 return __cpuidle_get_cpu_driver(dev->cpu); 244 } 245 EXPORT_SYMBOL_GPL(cpuidle_get_cpu_driver); 246 247 struct cpuidle_driver *cpuidle_driver_ref(void) 248 { 249 struct cpuidle_driver *drv; 250 251 spin_lock(&cpuidle_driver_lock); 252 253 drv = cpuidle_get_driver(); 254 drv->refcnt++; 255 256 spin_unlock(&cpuidle_driver_lock); 257 return drv; 258 } 259 260 void cpuidle_driver_unref(void) 261 { 262 struct cpuidle_driver *drv = cpuidle_get_driver(); 263 264 spin_lock(&cpuidle_driver_lock); 265 266 if (drv && !WARN_ON(drv->refcnt <= 0)) 267 drv->refcnt--; 268 269 spin_unlock(&cpuidle_driver_lock); 270 } 271