driver.c 4.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * driver.c - driver support
 *
 * (C) 2006-2007 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
 *               Shaohua Li <shaohua.li@intel.com>
 *               Adam Belay <abelay@novell.com>
 *
 * This code is licenced under the GPL.
 */

#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/cpuidle.h>

#include "cpuidle.h"

DEFINE_SPINLOCK(cpuidle_driver_lock);

D
Daniel Lezcano 已提交
19 20 21
static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu);
static struct cpuidle_driver * __cpuidle_get_cpu_driver(int cpu);

22 23 24 25 26
static void __cpuidle_driver_init(struct cpuidle_driver *drv)
{
	drv->refcnt = 0;
}

D
Daniel Lezcano 已提交
27
static int __cpuidle_register_driver(struct cpuidle_driver *drv, int cpu)
28
{
29
	if (!drv || !drv->state_count)
30 31
		return -EINVAL;

32 33 34
	if (cpuidle_disabled())
		return -ENODEV;

D
Daniel Lezcano 已提交
35
	if (__cpuidle_get_cpu_driver(cpu))
36
		return -EBUSY;
37

38
	__cpuidle_driver_init(drv);
39

D
Daniel Lezcano 已提交
40
	__cpuidle_set_cpu_driver(drv, cpu);
41

42 43 44
	return 0;
}

D
Daniel Lezcano 已提交
45
static void __cpuidle_unregister_driver(struct cpuidle_driver *drv, int cpu)
46
{
D
Daniel Lezcano 已提交
47
	if (drv != __cpuidle_get_cpu_driver(cpu))
48 49 50
		return;

	if (!WARN_ON(drv->refcnt > 0))
D
Daniel Lezcano 已提交
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
		__cpuidle_set_cpu_driver(NULL, cpu);
}

#ifdef CONFIG_CPU_IDLE_MULTIPLE_DRIVERS

static DEFINE_PER_CPU(struct cpuidle_driver *, cpuidle_drivers);

static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu)
{
	per_cpu(cpuidle_drivers, cpu) = drv;
}

static struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu)
{
	return per_cpu(cpuidle_drivers, cpu);
}

static void __cpuidle_unregister_all_cpu_driver(struct cpuidle_driver *drv)
{
	int cpu;
	for_each_present_cpu(cpu)
		__cpuidle_unregister_driver(drv, cpu);
}

static int __cpuidle_register_all_cpu_driver(struct cpuidle_driver *drv)
{
	int ret = 0;
	int i, cpu;

	for_each_present_cpu(cpu) {
		ret = __cpuidle_register_driver(drv, cpu);
		if (ret)
			break;
	}

	if (ret)
		for_each_present_cpu(i) {
			if (i == cpu)
				break;
			__cpuidle_unregister_driver(drv, i);
		}


	return ret;
}

int cpuidle_register_cpu_driver(struct cpuidle_driver *drv, int cpu)
{
	int ret;

	spin_lock(&cpuidle_driver_lock);
	ret = __cpuidle_register_driver(drv, cpu);
	spin_unlock(&cpuidle_driver_lock);

	return ret;
}

void cpuidle_unregister_cpu_driver(struct cpuidle_driver *drv, int cpu)
{
	spin_lock(&cpuidle_driver_lock);
	__cpuidle_unregister_driver(drv, cpu);
	spin_unlock(&cpuidle_driver_lock);
113
}
114

115 116 117 118 119 120 121 122 123
/**
 * cpuidle_register_driver - registers a driver
 * @drv: the driver
 */
int cpuidle_register_driver(struct cpuidle_driver *drv)
{
	int ret;

	spin_lock(&cpuidle_driver_lock);
D
Daniel Lezcano 已提交
124
	ret = __cpuidle_register_all_cpu_driver(drv);
125 126
	spin_unlock(&cpuidle_driver_lock);

127
	return ret;
128 129 130
}
EXPORT_SYMBOL_GPL(cpuidle_register_driver);

131
/**
D
Daniel Lezcano 已提交
132 133
 * cpuidle_unregister_driver - unregisters a driver
 * @drv: the driver
134
 */
D
Daniel Lezcano 已提交
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
void cpuidle_unregister_driver(struct cpuidle_driver *drv)
{
	spin_lock(&cpuidle_driver_lock);
	__cpuidle_unregister_all_cpu_driver(drv);
	spin_unlock(&cpuidle_driver_lock);
}
EXPORT_SYMBOL_GPL(cpuidle_unregister_driver);

#else

static struct cpuidle_driver *cpuidle_curr_driver;

static inline void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu)
{
	cpuidle_curr_driver = drv;
}

static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu)
153 154 155
{
	return cpuidle_curr_driver;
}
D
Daniel Lezcano 已提交
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173

/**
 * cpuidle_register_driver - registers a driver
 * @drv: the driver
 */
int cpuidle_register_driver(struct cpuidle_driver *drv)
{
	int ret, cpu;

	cpu = get_cpu();
	spin_lock(&cpuidle_driver_lock);
	ret = __cpuidle_register_driver(drv, cpu);
	spin_unlock(&cpuidle_driver_lock);
	put_cpu();

	return ret;
}
EXPORT_SYMBOL_GPL(cpuidle_register_driver);
174

175 176 177 178 179 180
/**
 * cpuidle_unregister_driver - unregisters a driver
 * @drv: the driver
 */
void cpuidle_unregister_driver(struct cpuidle_driver *drv)
{
D
Daniel Lezcano 已提交
181 182 183
	int cpu;

	cpu = get_cpu();
184
	spin_lock(&cpuidle_driver_lock);
D
Daniel Lezcano 已提交
185
	__cpuidle_unregister_driver(drv, cpu);
186
	spin_unlock(&cpuidle_driver_lock);
D
Daniel Lezcano 已提交
187
	put_cpu();
188 189
}
EXPORT_SYMBOL_GPL(cpuidle_unregister_driver);
D
Daniel Lezcano 已提交
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
#endif

/**
 * cpuidle_get_driver - return the current driver
 */
struct cpuidle_driver *cpuidle_get_driver(void)
{
	struct cpuidle_driver *drv;
	int cpu;

	cpu = get_cpu();
	drv = __cpuidle_get_cpu_driver(cpu);
	put_cpu();

	return drv;
}
EXPORT_SYMBOL_GPL(cpuidle_get_driver);

/**
 * cpuidle_get_cpu_driver - return the driver tied with a cpu
 */
struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev)
{
	if (!dev)
		return NULL;

216
	return __cpuidle_get_cpu_driver(dev->cpu);
D
Daniel Lezcano 已提交
217 218
}
EXPORT_SYMBOL_GPL(cpuidle_get_cpu_driver);
219 220 221 222 223 224 225

struct cpuidle_driver *cpuidle_driver_ref(void)
{
	struct cpuidle_driver *drv;

	spin_lock(&cpuidle_driver_lock);

226
	drv = cpuidle_get_driver();
227
	drv->refcnt++;
228 229 230 231 232 233 234

	spin_unlock(&cpuidle_driver_lock);
	return drv;
}

void cpuidle_driver_unref(void)
{
235
	struct cpuidle_driver *drv = cpuidle_get_driver();
236

237 238
	spin_lock(&cpuidle_driver_lock);

239 240
	if (drv && !WARN_ON(drv->refcnt <= 0))
		drv->refcnt--;
241 242 243

	spin_unlock(&cpuidle_driver_lock);
}