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
  21static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu);
  22static struct cpuidle_driver * __cpuidle_get_cpu_driver(int cpu);
  23
  24static void cpuidle_setup_broadcast_timer(void *arg)
  25{
  26        int cpu = smp_processor_id();
  27        clockevents_notify((long)(arg), &cpu);
  28}
  29
  30static 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
  48static 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
  66static 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
  83static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers);
  84
  85static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu)
  86{
  87        per_cpu(cpuidle_drivers, cpu) = drv;
  88}
  89
  90static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu)
  91{
  92        return per_cpu(cpuidle_drivers, cpu);
  93}
  94
  95static 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
 102static 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
 124int 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
 135void 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 */
 146int 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}
 156EXPORT_SYMBOL_GPL(cpuidle_register_driver);
 157
 158/**
 159 * cpuidle_unregister_driver - unregisters a driver
 160 * @drv: the driver
 161 */
 162void 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}
 168EXPORT_SYMBOL_GPL(cpuidle_unregister_driver);
 169
 170#else
 171
 172static struct cpuidle_driver *cpuidle_curr_driver;
 173
 174static inline void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu)
 175{
 176        cpuidle_curr_driver = drv;
 177}
 178
 179static 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 */
 188int 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}
 200EXPORT_SYMBOL_GPL(cpuidle_register_driver);
 201
 202/**
 203 * cpuidle_unregister_driver - unregisters a driver
 204 * @drv: the driver
 205 */
 206void 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}
 216EXPORT_SYMBOL_GPL(cpuidle_unregister_driver);
 217#endif
 218
 219/**
 220 * cpuidle_get_driver - return the current driver
 221 */
 222struct 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}
 233EXPORT_SYMBOL_GPL(cpuidle_get_driver);
 234
 235/**
 236 * cpuidle_get_cpu_driver - return the driver tied with a cpu
 237 */
 238struct 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}
 245EXPORT_SYMBOL_GPL(cpuidle_get_cpu_driver);
 246
 247struct 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
 260void 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