linux/drivers/cpuidle/driver.c
<<
>>
Prefs
   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
  19DEFINE_SPINLOCK(cpuidle_driver_lock);
  20
  21#ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS
  22
  23static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers);
  24
  25/**
  26 * __cpuidle_get_cpu_driver - return the cpuidle driver tied to a CPU.
  27 * @cpu: the CPU handled by the driver
  28 *
  29 * Returns a pointer to struct cpuidle_driver or NULL if no driver has been
  30 * registered for @cpu.
  31 */
  32static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu)
  33{
  34        return per_cpu(cpuidle_drivers, cpu);
  35}
  36
  37/**
  38 * __cpuidle_unset_driver - unset per CPU driver variables.
  39 * @drv: a valid pointer to a struct cpuidle_driver
  40 *
  41 * For each CPU in the driver's CPU mask, unset the registered driver per CPU
  42 * variable. If @drv is different from the registered driver, the corresponding
  43 * variable is not cleared.
  44 */
  45static inline void __cpuidle_unset_driver(struct cpuidle_driver *drv)
  46{
  47        int cpu;
  48
  49        for_each_cpu(cpu, drv->cpumask) {
  50
  51                if (drv != __cpuidle_get_cpu_driver(cpu))
  52                        continue;
  53
  54                per_cpu(cpuidle_drivers, cpu) = NULL;
  55        }
  56}
  57
  58/**
  59 * __cpuidle_set_driver - set per CPU driver variables the the given driver.
  60 * @drv: a valid pointer to a struct cpuidle_driver
  61 *
  62 * For each CPU in the driver's cpumask, unset the registered driver per CPU
  63 * to @drv.
  64 *
  65 * Returns 0 on success, -EBUSY if the CPUs have driver(s) already.
  66 */
  67static inline int __cpuidle_set_driver(struct cpuidle_driver *drv)
  68{
  69        int cpu;
  70
  71        for_each_cpu(cpu, drv->cpumask) {
  72
  73                if (__cpuidle_get_cpu_driver(cpu)) {
  74                        __cpuidle_unset_driver(drv);
  75                        return -EBUSY;
  76                }
  77
  78                per_cpu(cpuidle_drivers, cpu) = drv;
  79        }
  80
  81        return 0;
  82}
  83
  84#else
  85
  86static struct cpuidle_driver *cpuidle_curr_driver;
  87
  88/**
  89 * __cpuidle_get_cpu_driver - return the global cpuidle driver pointer.
  90 * @cpu: ignored without the multiple driver support
  91 *
  92 * Return a pointer to a struct cpuidle_driver object or NULL if no driver was
  93 * previously registered.
  94 */
  95static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu)
  96{
  97        return cpuidle_curr_driver;
  98}
  99
 100/**
 101 * __cpuidle_set_driver - assign the global cpuidle driver variable.
 102 * @drv: pointer to a struct cpuidle_driver object
 103 *
 104 * Returns 0 on success, -EBUSY if the driver is already registered.
 105 */
 106static inline int __cpuidle_set_driver(struct cpuidle_driver *drv)
 107{
 108        if (cpuidle_curr_driver)
 109                return -EBUSY;
 110
 111        cpuidle_curr_driver = drv;
 112
 113        return 0;
 114}
 115
 116/**
 117 * __cpuidle_unset_driver - unset the global cpuidle driver variable.
 118 * @drv: a pointer to a struct cpuidle_driver
 119 *
 120 * Reset the global cpuidle variable to NULL.  If @drv does not match the
 121 * registered driver, do nothing.
 122 */
 123static inline void __cpuidle_unset_driver(struct cpuidle_driver *drv)
 124{
 125        if (drv == cpuidle_curr_driver)
 126                cpuidle_curr_driver = NULL;
 127}
 128
 129#endif
 130
 131/**
 132 * cpuidle_setup_broadcast_timer - enable/disable the broadcast timer
 133 * @arg: a void pointer used to match the SMP cross call API
 134 *
 135 * @arg is used as a value of type 'long' with on of the two values:
 136 * - CLOCK_EVT_NOTIFY_BROADCAST_ON
 137 * - CLOCK_EVT_NOTIFY_BROADCAST_OFF
 138 *
 139 * Set the broadcast timer notification for the current CPU.  This function
 140 * is executed per CPU by an SMP cross call.  It not supposed to be called
 141 * directly.
 142 */
 143static void cpuidle_setup_broadcast_timer(void *arg)
 144{
 145        int cpu = smp_processor_id();
 146        clockevents_notify((long)(arg), &cpu);
 147}
 148
 149/**
 150 * __cpuidle_driver_init - initialize the driver's internal data
 151 * @drv: a valid pointer to a struct cpuidle_driver
 152 *
 153 * Returns 0 on success, a negative error code otherwise.
 154 */
 155static int __cpuidle_driver_init(struct cpuidle_driver *drv)
 156{
 157        int i;
 158
 159        drv->refcnt = 0;
 160
 161        /*
 162         * Use all possible CPUs as the default, because if the kernel boots
 163         * with some CPUs offline and then we online one of them, the CPU
 164         * notifier has to know which driver to assign.
 165         */
 166        if (!drv->cpumask)
 167                drv->cpumask = (struct cpumask *)cpu_possible_mask;
 168
 169        /*
 170         * Look for the timer stop flag in the different states, so that we know
 171         * if the broadcast timer has to be set up.  The loop is in the reverse
 172         * order, because usually on of the the deeper states has this flag set.
 173         */
 174        for (i = drv->state_count - 1; i >= 0 ; i--) {
 175
 176                if (!(drv->states[i].flags & CPUIDLE_FLAG_TIMER_STOP))
 177                        continue;
 178
 179                drv->bctimer = 1;
 180                break;
 181        }
 182
 183        return 0;
 184}
 185
 186/**
 187 * __cpuidle_register_driver: register the driver
 188 * @drv: a valid pointer to a struct cpuidle_driver
 189 *
 190 * Do some sanity checks, initialize the driver, assign the driver to the
 191 * global cpuidle driver variable(s) and set up the broadcast timer if the
 192 * cpuidle driver has some states that shut down the local timer.
 193 *
 194 * Returns 0 on success, a negative error code otherwise:
 195 *  * -EINVAL if the driver pointer is NULL or no idle states are available
 196 *  * -ENODEV if the cpuidle framework is disabled
 197 *  * -EBUSY if the driver is already assigned to the global variable(s)
 198 */
 199static int __cpuidle_register_driver(struct cpuidle_driver *drv)
 200{
 201        int ret;
 202
 203        if (!drv || !drv->state_count)
 204                return -EINVAL;
 205
 206        if (cpuidle_disabled())
 207                return -ENODEV;
 208
 209        ret = __cpuidle_driver_init(drv);
 210        if (ret)
 211                return ret;
 212
 213        ret = __cpuidle_set_driver(drv);
 214        if (ret)
 215                return ret;
 216
 217        if (drv->bctimer)
 218                on_each_cpu_mask(drv->cpumask, cpuidle_setup_broadcast_timer,
 219                                 (void *)CLOCK_EVT_NOTIFY_BROADCAST_ON, 1);
 220
 221        return 0;
 222}
 223
 224/**
 225 * __cpuidle_unregister_driver - unregister the driver
 226 * @drv: a valid pointer to a struct cpuidle_driver
 227 *
 228 * Check if the driver is no longer in use, reset the global cpuidle driver
 229 * variable(s) and disable the timer broadcast notification mechanism if it was
 230 * in use.
 231 *
 232 */
 233static void __cpuidle_unregister_driver(struct cpuidle_driver *drv)
 234{
 235        if (WARN_ON(drv->refcnt > 0))
 236                return;
 237
 238        if (drv->bctimer) {
 239                drv->bctimer = 0;
 240                on_each_cpu_mask(drv->cpumask, cpuidle_setup_broadcast_timer,
 241                                 (void *)CLOCK_EVT_NOTIFY_BROADCAST_OFF, 1);
 242        }
 243
 244        __cpuidle_unset_driver(drv);
 245}
 246
 247/**
 248 * cpuidle_register_driver - registers a driver
 249 * @drv: a pointer to a valid struct cpuidle_driver
 250 *
 251 * Register the driver under a lock to prevent concurrent attempts to
 252 * [un]register the driver from occuring at the same time.
 253 *
 254 * Returns 0 on success, a negative error code (returned by
 255 * __cpuidle_register_driver()) otherwise.
 256 */
 257int cpuidle_register_driver(struct cpuidle_driver *drv)
 258{
 259        int ret;
 260
 261        spin_lock(&cpuidle_driver_lock);
 262        ret = __cpuidle_register_driver(drv);
 263        spin_unlock(&cpuidle_driver_lock);
 264
 265        return ret;
 266}
 267EXPORT_SYMBOL_GPL(cpuidle_register_driver);
 268
 269/**
 270 * cpuidle_unregister_driver - unregisters a driver
 271 * @drv: a pointer to a valid struct cpuidle_driver
 272 *
 273 * Unregisters the cpuidle driver under a lock to prevent concurrent attempts
 274 * to [un]register the driver from occuring at the same time.  @drv has to
 275 * match the currently registered driver.
 276 */
 277void cpuidle_unregister_driver(struct cpuidle_driver *drv)
 278{
 279        spin_lock(&cpuidle_driver_lock);
 280        __cpuidle_unregister_driver(drv);
 281        spin_unlock(&cpuidle_driver_lock);
 282}
 283EXPORT_SYMBOL_GPL(cpuidle_unregister_driver);
 284
 285/**
 286 * cpuidle_get_driver - return the driver tied to the current CPU.
 287 *
 288 * Returns a struct cpuidle_driver pointer, or NULL if no driver is registered.
 289 */
 290struct cpuidle_driver *cpuidle_get_driver(void)
 291{
 292        struct cpuidle_driver *drv;
 293        int cpu;
 294
 295        cpu = get_cpu();
 296        drv = __cpuidle_get_cpu_driver(cpu);
 297        put_cpu();
 298
 299        return drv;
 300}
 301EXPORT_SYMBOL_GPL(cpuidle_get_driver);
 302
 303/**
 304 * cpuidle_get_cpu_driver - return the driver registered for a CPU.
 305 * @dev: a valid pointer to a struct cpuidle_device
 306 *
 307 * Returns a struct cpuidle_driver pointer, or NULL if no driver is registered
 308 * for the CPU associated with @dev.
 309 */
 310struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev)
 311{
 312        if (!dev)
 313                return NULL;
 314
 315        return __cpuidle_get_cpu_driver(dev->cpu);
 316}
 317EXPORT_SYMBOL_GPL(cpuidle_get_cpu_driver);
 318
 319/**
 320 * cpuidle_driver_ref - get a reference to the driver.
 321 *
 322 * Increment the reference counter of the cpuidle driver associated with
 323 * the current CPU.
 324 *
 325 * Returns a pointer to the driver, or NULL if the current CPU has no driver.
 326 */
 327struct cpuidle_driver *cpuidle_driver_ref(void)
 328{
 329        struct cpuidle_driver *drv;
 330
 331        spin_lock(&cpuidle_driver_lock);
 332
 333        drv = cpuidle_get_driver();
 334        if (drv)
 335                drv->refcnt++;
 336
 337        spin_unlock(&cpuidle_driver_lock);
 338        return drv;
 339}
 340
 341/**
 342 * cpuidle_driver_unref - puts down the refcount for the driver
 343 *
 344 * Decrement the reference counter of the cpuidle driver associated with
 345 * the current CPU.
 346 */
 347void cpuidle_driver_unref(void)
 348{
 349        struct cpuidle_driver *drv = cpuidle_get_driver();
 350
 351        spin_lock(&cpuidle_driver_lock);
 352
 353        if (drv && !WARN_ON(drv->refcnt <= 0))
 354                drv->refcnt--;
 355
 356        spin_unlock(&cpuidle_driver_lock);
 357}
 358
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.