summaryrefslogtreecommitdiffstats
path: root/drivers/cpuidle/driver.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/cpuidle/driver.c')
-rw-r--r--drivers/cpuidle/driver.c209
1 files changed, 180 insertions, 29 deletions
diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c
index 87db3877fea..3af841fb397 100644
--- a/drivers/cpuidle/driver.c
+++ b/drivers/cpuidle/driver.c
@@ -14,9 +14,10 @@
#include "cpuidle.h"
-static struct cpuidle_driver *cpuidle_curr_driver;
DEFINE_SPINLOCK(cpuidle_driver_lock);
-int cpuidle_driver_refcount;
+
+static void __cpuidle_set_cpu_driver(struct cpuidle_driver *drv, int cpu);
+static struct cpuidle_driver * __cpuidle_get_cpu_driver(int cpu);
static void set_power_states(struct cpuidle_driver *drv)
{
@@ -40,11 +41,15 @@ static void set_power_states(struct cpuidle_driver *drv)
drv->states[i].power_usage = -1 - i;
}
-/**
- * cpuidle_register_driver - registers a driver
- * @drv: the driver
- */
-int cpuidle_register_driver(struct cpuidle_driver *drv)
+static void __cpuidle_driver_init(struct cpuidle_driver *drv)
+{
+ drv->refcnt = 0;
+
+ if (!drv->power_specified)
+ set_power_states(drv);
+}
+
+static int __cpuidle_register_driver(struct cpuidle_driver *drv, int cpu)
{
if (!drv || !drv->state_count)
return -EINVAL;
@@ -52,31 +57,145 @@ int cpuidle_register_driver(struct cpuidle_driver *drv)
if (cpuidle_disabled())
return -ENODEV;
- spin_lock(&cpuidle_driver_lock);
- if (cpuidle_curr_driver) {
- spin_unlock(&cpuidle_driver_lock);
+ if (__cpuidle_get_cpu_driver(cpu))
return -EBUSY;
+
+ __cpuidle_driver_init(drv);
+
+ __cpuidle_set_cpu_driver(drv, cpu);
+
+ return 0;
+}
+
+static void __cpuidle_unregister_driver(struct cpuidle_driver *drv, int cpu)
+{
+ if (drv != __cpuidle_get_cpu_driver(cpu))
+ return;
+
+ if (!WARN_ON(drv->refcnt > 0))
+ __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 (!drv->power_specified)
- set_power_states(drv);
+ if (ret)
+ for_each_present_cpu(i) {
+ if (i == cpu)
+ break;
+ __cpuidle_unregister_driver(drv, i);
+ }
- cpuidle_curr_driver = drv;
+ 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 0;
+ 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);
+}
+
+/**
+ * cpuidle_register_driver - registers a driver
+ * @drv: the driver
+ */
+int cpuidle_register_driver(struct cpuidle_driver *drv)
+{
+ int ret;
+
+ spin_lock(&cpuidle_driver_lock);
+ ret = __cpuidle_register_all_cpu_driver(drv);
+ spin_unlock(&cpuidle_driver_lock);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(cpuidle_register_driver);
/**
- * cpuidle_get_driver - return the current driver
+ * cpuidle_unregister_driver - unregisters a driver
+ * @drv: the driver
*/
-struct cpuidle_driver *cpuidle_get_driver(void)
+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)
{
return cpuidle_curr_driver;
}
-EXPORT_SYMBOL_GPL(cpuidle_get_driver);
+
+/**
+ * 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);
/**
* cpuidle_unregister_driver - unregisters a driver
@@ -84,20 +203,50 @@ EXPORT_SYMBOL_GPL(cpuidle_get_driver);
*/
void cpuidle_unregister_driver(struct cpuidle_driver *drv)
{
- if (drv != cpuidle_curr_driver) {
- WARN(1, "invalid cpuidle_unregister_driver(%s)\n",
- drv->name);
- return;
- }
+ int cpu;
+ cpu = get_cpu();
spin_lock(&cpuidle_driver_lock);
+ __cpuidle_unregister_driver(drv, cpu);
+ spin_unlock(&cpuidle_driver_lock);
+ put_cpu();
+}
+EXPORT_SYMBOL_GPL(cpuidle_unregister_driver);
+#endif
+
+/**
+ * cpuidle_get_driver - return the current driver
+ */
+struct cpuidle_driver *cpuidle_get_driver(void)
+{
+ struct cpuidle_driver *drv;
+ int cpu;
- if (!WARN_ON(cpuidle_driver_refcount > 0))
- cpuidle_curr_driver = NULL;
+ 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)
+{
+ struct cpuidle_driver *drv;
+
+ if (!dev)
+ return NULL;
+
+ spin_lock(&cpuidle_driver_lock);
+ drv = __cpuidle_get_cpu_driver(dev->cpu);
spin_unlock(&cpuidle_driver_lock);
+
+ return drv;
}
-EXPORT_SYMBOL_GPL(cpuidle_unregister_driver);
+EXPORT_SYMBOL_GPL(cpuidle_get_cpu_driver);
struct cpuidle_driver *cpuidle_driver_ref(void)
{
@@ -105,8 +254,8 @@ struct cpuidle_driver *cpuidle_driver_ref(void)
spin_lock(&cpuidle_driver_lock);
- drv = cpuidle_curr_driver;
- cpuidle_driver_refcount++;
+ drv = cpuidle_get_driver();
+ drv->refcnt++;
spin_unlock(&cpuidle_driver_lock);
return drv;
@@ -114,10 +263,12 @@ struct cpuidle_driver *cpuidle_driver_ref(void)
void cpuidle_driver_unref(void)
{
+ struct cpuidle_driver *drv = cpuidle_get_driver();
+
spin_lock(&cpuidle_driver_lock);
- if (!WARN_ON(cpuidle_driver_refcount <= 0))
- cpuidle_driver_refcount--;
+ if (drv && !WARN_ON(drv->refcnt <= 0))
+ drv->refcnt--;
spin_unlock(&cpuidle_driver_lock);
}