diff options
Diffstat (limited to 'drivers/cpuidle/driver.c')
| -rw-r--r-- | drivers/cpuidle/driver.c | 209 | 
1 files changed, 180 insertions, 29 deletions
| diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index 87db3877fead..3af841fb397a 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);  } |