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