diff options
Diffstat (limited to 'drivers/cpuidle/cpuidle.c')
| -rw-r--r-- | drivers/cpuidle/cpuidle.c | 502 | 
1 files changed, 326 insertions, 176 deletions
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index a5071084337..cb7019977c5 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -8,105 +8,198 @@   * This code is licenced under the GPL.   */ +#include <linux/clockchips.h>  #include <linux/kernel.h>  #include <linux/mutex.h>  #include <linux/sched.h>  #include <linux/notifier.h> -#include <linux/pm_qos_params.h> +#include <linux/pm_qos.h>  #include <linux/cpu.h>  #include <linux/cpuidle.h>  #include <linux/ktime.h>  #include <linux/hrtimer.h> +#include <linux/module.h>  #include <trace/events/power.h>  #include "cpuidle.h"  DEFINE_PER_CPU(struct cpuidle_device *, cpuidle_devices); +DEFINE_PER_CPU(struct cpuidle_device, cpuidle_dev);  DEFINE_MUTEX(cpuidle_lock);  LIST_HEAD(cpuidle_detected_devices); -static void (*pm_idle_old)(void);  static int enabled_devices; +static int off __read_mostly; +static int initialized __read_mostly; +static bool use_deepest_state __read_mostly; -#if defined(CONFIG_ARCH_HAS_CPU_IDLE_WAIT) -static void cpuidle_kick_cpus(void) +int cpuidle_disabled(void)  { -	cpu_idle_wait(); +	return off;  } -#elif defined(CONFIG_SMP) -# error "Arch needs cpu_idle_wait() equivalent here" -#else /* !CONFIG_ARCH_HAS_CPU_IDLE_WAIT && !CONFIG_SMP */ -static void cpuidle_kick_cpus(void) {} -#endif +void disable_cpuidle(void) +{ +	off = 1; +} + +/** + * cpuidle_play_dead - cpu off-lining + * + * Returns in case of an error or no driver + */ +int cpuidle_play_dead(void) +{ +	struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices); +	struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev); +	int i; + +	if (!drv) +		return -ENODEV; + +	/* Find lowest-power state that supports long-term idle */ +	for (i = drv->state_count - 1; i >= CPUIDLE_DRIVER_STATE_START; i--) +		if (drv->states[i].enter_dead) +			return drv->states[i].enter_dead(dev, i); -static int __cpuidle_register_device(struct cpuidle_device *dev); +	return -ENODEV; +}  /** - * cpuidle_idle_call - the main idle loop + * cpuidle_use_deepest_state - Enable/disable the "deepest idle" mode. + * @enable: Whether enable or disable the feature. + * + * If the "deepest idle" mode is enabled, cpuidle will ignore the governor and + * always use the state with the greatest exit latency (out of the states that + * are not disabled).   * - * NOTE: no locks or semaphores should be used here + * This function can only be called after cpuidle_pause() to avoid races.   */ -static void cpuidle_idle_call(void) +void cpuidle_use_deepest_state(bool enable)  { -	struct cpuidle_device *dev = __get_cpu_var(cpuidle_devices); -	struct cpuidle_state *target_state; -	int next_state; - -	/* check if the device is ready */ -	if (!dev || !dev->enabled) { -		if (pm_idle_old) -			pm_idle_old(); -		else -#if defined(CONFIG_ARCH_HAS_DEFAULT_IDLE) -			default_idle(); -#else -			local_irq_enable(); -#endif -		return; +	use_deepest_state = enable; +} + +/** + * cpuidle_find_deepest_state - Find the state of the greatest exit latency. + * @drv: cpuidle driver for a given CPU. + * @dev: cpuidle device for a given CPU. + */ +static int cpuidle_find_deepest_state(struct cpuidle_driver *drv, +				      struct cpuidle_device *dev) +{ +	unsigned int latency_req = 0; +	int i, ret = CPUIDLE_DRIVER_STATE_START - 1; + +	for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) { +		struct cpuidle_state *s = &drv->states[i]; +		struct cpuidle_state_usage *su = &dev->states_usage[i]; + +		if (s->disabled || su->disable || s->exit_latency <= latency_req) +			continue; + +		latency_req = s->exit_latency; +		ret = i;  	} +	return ret; +} -#if 0 -	/* shows regressions, re-enable for 2.6.29 */ -	/* -	 * run any timers that can be run now, at this point -	 * before calculating the idle duration etc. -	 */ -	hrtimer_peek_ahead_timers(); -#endif +/** + * cpuidle_enter_state - enter the state and update stats + * @dev: cpuidle device for this cpu + * @drv: cpuidle driver for this cpu + * @next_state: index into drv->states of the state to enter + */ +int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, +			int index) +{ +	int entered_state; + +	struct cpuidle_state *target_state = &drv->states[index]; +	ktime_t time_start, time_end; +	s64 diff; -	/* -	 * Call the device's prepare function before calling the -	 * governor's select function.  ->prepare gives the device's -	 * cpuidle driver a chance to update any dynamic information -	 * of its cpuidle states for the current idle period, e.g. -	 * state availability, latencies, residencies, etc. -	 */ -	if (dev->prepare) -		dev->prepare(dev); - -	/* ask the governor for the next state */ -	next_state = cpuidle_curr_governor->select(dev); -	if (need_resched()) { +	time_start = ktime_get(); + +	entered_state = target_state->enter(dev, drv, index); + +	time_end = ktime_get(); + +	if (!cpuidle_state_is_coupled(dev, drv, entered_state))  		local_irq_enable(); -		return; + +	diff = ktime_to_us(ktime_sub(time_end, time_start)); +	if (diff > INT_MAX) +		diff = INT_MAX; + +	dev->last_residency = (int) diff; + +	if (entered_state >= 0) { +		/* Update cpuidle counters */ +		/* This can be moved to within driver enter routine +		 * but that results in multiple copies of same code. +		 */ +		dev->states_usage[entered_state].time += dev->last_residency; +		dev->states_usage[entered_state].usage++; +	} else { +		dev->last_residency = 0;  	} -	target_state = &dev->states[next_state]; +	return entered_state; +} -	/* enter the state and update stats */ -	dev->last_state = target_state; -	dev->last_residency = target_state->enter(dev, target_state); -	if (dev->last_state) -		target_state = dev->last_state; +/** + * cpuidle_select - ask the cpuidle framework to choose an idle state + * + * @drv: the cpuidle driver + * @dev: the cpuidle device + * + * Returns the index of the idle state. + */ +int cpuidle_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) +{ +	if (off || !initialized) +		return -ENODEV; -	target_state->time += (unsigned long long)dev->last_residency; -	target_state->usage++; +	if (!drv || !dev || !dev->enabled) +		return -EBUSY; -	/* give the governor an opportunity to reflect on the outcome */ -	if (cpuidle_curr_governor->reflect) -		cpuidle_curr_governor->reflect(dev); -	trace_power_end(smp_processor_id()); +	if (unlikely(use_deepest_state)) +		return cpuidle_find_deepest_state(drv, dev); + +	return cpuidle_curr_governor->select(drv, dev); +} + +/** + * cpuidle_enter - enter into the specified idle state + * + * @drv:   the cpuidle driver tied with the cpu + * @dev:   the cpuidle device + * @index: the index in the idle state table + * + * Returns the index in the idle state, < 0 in case of error. + * The error code depends on the backend driver + */ +int cpuidle_enter(struct cpuidle_driver *drv, struct cpuidle_device *dev, +		  int index) +{ +	if (cpuidle_state_is_coupled(dev, drv, index)) +		return cpuidle_enter_state_coupled(dev, drv, index); +	return cpuidle_enter_state(dev, drv, index); +} + +/** + * cpuidle_reflect - tell the underlying governor what was the state + * we were in + * + * @dev  : the cpuidle device + * @index: the index in the idle state table + * + */ +void cpuidle_reflect(struct cpuidle_device *dev, int index) +{ +	if (cpuidle_curr_governor->reflect && !unlikely(use_deepest_state)) +		cpuidle_curr_governor->reflect(dev, index);  }  /** @@ -114,10 +207,10 @@ static void cpuidle_idle_call(void)   */  void cpuidle_install_idle_handler(void)  { -	if (enabled_devices && (pm_idle != cpuidle_idle_call)) { +	if (enabled_devices) {  		/* Make sure all changes finished before we switch to new idle */  		smp_wmb(); -		pm_idle = cpuidle_idle_call; +		initialized = 1;  	}  } @@ -126,9 +219,9 @@ void cpuidle_install_idle_handler(void)   */  void cpuidle_uninstall_idle_handler(void)  { -	if (enabled_devices && pm_idle_old && (pm_idle != pm_idle_old)) { -		pm_idle = pm_idle_old; -		cpuidle_kick_cpus(); +	if (enabled_devices) { +		initialized = 0; +		kick_all_cpus_sync();  	}  } @@ -154,6 +247,22 @@ void cpuidle_resume_and_unlock(void)  EXPORT_SYMBOL_GPL(cpuidle_resume_and_unlock); +/* Currently used in suspend/resume path to suspend cpuidle */ +void cpuidle_pause(void) +{ +	mutex_lock(&cpuidle_lock); +	cpuidle_uninstall_idle_handler(); +	mutex_unlock(&cpuidle_lock); +} + +/* Currently used in suspend/resume path to resume cpuidle */ +void cpuidle_resume(void) +{ +	mutex_lock(&cpuidle_lock); +	cpuidle_install_idle_handler(); +	mutex_unlock(&cpuidle_lock); +} +  /**   * cpuidle_enable_device - enables idle PM for a CPU   * @dev: the CPU @@ -163,35 +272,34 @@ EXPORT_SYMBOL_GPL(cpuidle_resume_and_unlock);   */  int cpuidle_enable_device(struct cpuidle_device *dev)  { -	int ret, i; +	int ret; +	struct cpuidle_driver *drv; + +	if (!dev) +		return -EINVAL;  	if (dev->enabled)  		return 0; -	if (!cpuidle_get_driver() || !cpuidle_curr_governor) + +	drv = cpuidle_get_cpu_driver(dev); + +	if (!drv || !cpuidle_curr_governor)  		return -EIO; -	if (!dev->state_count) + +	if (!dev->registered)  		return -EINVAL; -	if (dev->registered == 0) { -		ret = __cpuidle_register_device(dev); -		if (ret) -			return ret; -	} +	if (!dev->state_count) +		dev->state_count = drv->state_count; -	if ((ret = cpuidle_add_state_sysfs(dev))) +	ret = cpuidle_add_device_sysfs(dev); +	if (ret)  		return ret;  	if (cpuidle_curr_governor->enable && -	    (ret = cpuidle_curr_governor->enable(dev))) +	    (ret = cpuidle_curr_governor->enable(drv, dev)))  		goto fail_sysfs; -	for (i = 0; i < dev->state_count; i++) { -		dev->states[i].usage = 0; -		dev->states[i].time = 0; -	} -	dev->last_residency = 0; -	dev->last_state = NULL; -  	smp_wmb();  	dev->enabled = 1; @@ -200,7 +308,7 @@ int cpuidle_enable_device(struct cpuidle_device *dev)  	return 0;  fail_sysfs: -	cpuidle_remove_state_sysfs(dev); +	cpuidle_remove_device_sysfs(dev);  	return ret;  } @@ -216,60 +324,39 @@ EXPORT_SYMBOL_GPL(cpuidle_enable_device);   */  void cpuidle_disable_device(struct cpuidle_device *dev)  { -	if (!dev->enabled) +	struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev); + +	if (!dev || !dev->enabled)  		return; -	if (!cpuidle_get_driver() || !cpuidle_curr_governor) + +	if (!drv || !cpuidle_curr_governor)  		return;  	dev->enabled = 0;  	if (cpuidle_curr_governor->disable) -		cpuidle_curr_governor->disable(dev); +		cpuidle_curr_governor->disable(drv, dev); -	cpuidle_remove_state_sysfs(dev); +	cpuidle_remove_device_sysfs(dev);  	enabled_devices--;  }  EXPORT_SYMBOL_GPL(cpuidle_disable_device); -#ifdef CONFIG_ARCH_HAS_CPU_RELAX -static int poll_idle(struct cpuidle_device *dev, struct cpuidle_state *st) +static void __cpuidle_unregister_device(struct cpuidle_device *dev)  { -	ktime_t	t1, t2; -	s64 diff; -	int ret; - -	t1 = ktime_get(); -	local_irq_enable(); -	while (!need_resched()) -		cpu_relax(); - -	t2 = ktime_get(); -	diff = ktime_to_us(ktime_sub(t2, t1)); -	if (diff > INT_MAX) -		diff = INT_MAX; +	struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev); -	ret = (int) diff; -	return ret; +	list_del(&dev->device_list); +	per_cpu(cpuidle_devices, dev->cpu) = NULL; +	module_put(drv->owner);  } -static void poll_idle_init(struct cpuidle_device *dev) +static void __cpuidle_device_init(struct cpuidle_device *dev)  { -	struct cpuidle_state *state = &dev->states[0]; - -	cpuidle_set_statedata(state, NULL); - -	snprintf(state->name, CPUIDLE_NAME_LEN, "C0"); -	snprintf(state->desc, CPUIDLE_DESC_LEN, "CPUIDLE CORE POLL IDLE"); -	state->exit_latency = 0; -	state->target_residency = 0; -	state->power_usage = -1; -	state->flags = CPUIDLE_FLAG_POLL; -	state->enter = poll_idle; +	memset(dev->states_usage, 0, sizeof(dev->states_usage)); +	dev->last_residency = 0;  } -#else -static void poll_idle_init(struct cpuidle_device *dev) {} -#endif /* CONFIG_ARCH_HAS_CPU_RELAX */  /**   * __cpuidle_register_device - internal register function called before register @@ -281,47 +368,21 @@ static void poll_idle_init(struct cpuidle_device *dev) {}  static int __cpuidle_register_device(struct cpuidle_device *dev)  {  	int ret; -	struct sys_device *sys_dev = get_cpu_sysdev((unsigned long)dev->cpu); -	struct cpuidle_driver *cpuidle_driver = cpuidle_get_driver(); +	struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev); -	if (!sys_dev) -		return -EINVAL; -	if (!try_module_get(cpuidle_driver->owner)) +	if (!try_module_get(drv->owner))  		return -EINVAL; -	init_completion(&dev->kobj_unregister); - -	poll_idle_init(dev); - -	/* -	 * cpuidle driver should set the dev->power_specified bit -	 * before registering the device if the driver provides -	 * power_usage numbers. -	 * -	 * For those devices whose ->power_specified is not set, -	 * we fill in power_usage with decreasing values as the -	 * cpuidle code has an implicit assumption that state Cn -	 * uses less power than C(n-1). -	 * -	 * With CONFIG_ARCH_HAS_CPU_RELAX, C0 is already assigned -	 * an power value of -1.  So we use -2, -3, etc, for other -	 * c-states. -	 */ -	if (!dev->power_specified) { -		int i; -		for (i = CPUIDLE_DRIVER_STATE_START; i < dev->state_count; i++) -			dev->states[i].power_usage = -1 - i; -	} -  	per_cpu(cpuidle_devices, dev->cpu) = dev;  	list_add(&dev->device_list, &cpuidle_detected_devices); -	if ((ret = cpuidle_add_sysfs(sys_dev))) { -		module_put(cpuidle_driver->owner); -		return ret; -	} -	dev->registered = 1; -	return 0; +	ret = cpuidle_coupled_register_device(dev); +	if (ret) +		__cpuidle_unregister_device(dev); +	else +		dev->registered = 1; + +	return ret;  }  /** @@ -330,22 +391,42 @@ static int __cpuidle_register_device(struct cpuidle_device *dev)   */  int cpuidle_register_device(struct cpuidle_device *dev)  { -	int ret; +	int ret = -EBUSY; + +	if (!dev) +		return -EINVAL;  	mutex_lock(&cpuidle_lock); -	if ((ret = __cpuidle_register_device(dev))) { -		mutex_unlock(&cpuidle_lock); -		return ret; -	} +	if (dev->registered) +		goto out_unlock; + +	__cpuidle_device_init(dev); + +	ret = __cpuidle_register_device(dev); +	if (ret) +		goto out_unlock; + +	ret = cpuidle_add_sysfs(dev); +	if (ret) +		goto out_unregister; + +	ret = cpuidle_enable_device(dev); +	if (ret) +		goto out_sysfs; -	cpuidle_enable_device(dev);  	cpuidle_install_idle_handler(); +out_unlock:  	mutex_unlock(&cpuidle_lock); -	return 0; +	return ret; +out_sysfs: +	cpuidle_remove_sysfs(dev); +out_unregister: +	__cpuidle_unregister_device(dev); +	goto out_unlock;  }  EXPORT_SYMBOL_GPL(cpuidle_register_device); @@ -356,28 +437,95 @@ EXPORT_SYMBOL_GPL(cpuidle_register_device);   */  void cpuidle_unregister_device(struct cpuidle_device *dev)  { -	struct sys_device *sys_dev = get_cpu_sysdev((unsigned long)dev->cpu); -	struct cpuidle_driver *cpuidle_driver = cpuidle_get_driver(); - -	if (dev->registered == 0) +	if (!dev || dev->registered == 0)  		return;  	cpuidle_pause_and_lock();  	cpuidle_disable_device(dev); -	cpuidle_remove_sysfs(sys_dev); -	list_del(&dev->device_list); -	wait_for_completion(&dev->kobj_unregister); -	per_cpu(cpuidle_devices, dev->cpu) = NULL; +	cpuidle_remove_sysfs(dev); -	cpuidle_resume_and_unlock(); +	__cpuidle_unregister_device(dev); -	module_put(cpuidle_driver->owner); +	cpuidle_coupled_unregister_device(dev); + +	cpuidle_resume_and_unlock();  }  EXPORT_SYMBOL_GPL(cpuidle_unregister_device); +/** + * cpuidle_unregister: unregister a driver and the devices. This function + * can be used only if the driver has been previously registered through + * the cpuidle_register function. + * + * @drv: a valid pointer to a struct cpuidle_driver + */ +void cpuidle_unregister(struct cpuidle_driver *drv) +{ +	int cpu; +	struct cpuidle_device *device; + +	for_each_cpu(cpu, drv->cpumask) { +		device = &per_cpu(cpuidle_dev, cpu); +		cpuidle_unregister_device(device); +	} + +	cpuidle_unregister_driver(drv); +} +EXPORT_SYMBOL_GPL(cpuidle_unregister); + +/** + * cpuidle_register: registers the driver and the cpu devices with the + * coupled_cpus passed as parameter. This function is used for all common + * initialization pattern there are in the arch specific drivers. The + * devices is globally defined in this file. + * + * @drv         : a valid pointer to a struct cpuidle_driver + * @coupled_cpus: a cpumask for the coupled states + * + * Returns 0 on success, < 0 otherwise + */ +int cpuidle_register(struct cpuidle_driver *drv, +		     const struct cpumask *const coupled_cpus) +{ +	int ret, cpu; +	struct cpuidle_device *device; + +	ret = cpuidle_register_driver(drv); +	if (ret) { +		pr_err("failed to register cpuidle driver\n"); +		return ret; +	} + +	for_each_cpu(cpu, drv->cpumask) { +		device = &per_cpu(cpuidle_dev, cpu); +		device->cpu = cpu; + +#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED +		/* +		 * On multiplatform for ARM, the coupled idle states could be +		 * enabled in the kernel even if the cpuidle driver does not +		 * use it. Note, coupled_cpus is a struct copy. +		 */ +		if (coupled_cpus) +			device->coupled_cpus = *coupled_cpus; +#endif +		ret = cpuidle_register_device(device); +		if (!ret) +			continue; + +		pr_err("Failed to register cpuidle device for cpu%d\n", cpu); + +		cpuidle_unregister(drv); +		break; +	} + +	return ret; +} +EXPORT_SYMBOL_GPL(cpuidle_register); +  #ifdef CONFIG_SMP  static void smp_callback(void *v) @@ -420,9 +568,10 @@ static int __init cpuidle_init(void)  {  	int ret; -	pm_idle_old = pm_idle; +	if (cpuidle_disabled()) +		return -ENODEV; -	ret = cpuidle_add_class_sysfs(&cpu_sysdev_class); +	ret = cpuidle_add_interface(cpu_subsys.dev_root);  	if (ret)  		return ret; @@ -431,4 +580,5 @@ static int __init cpuidle_init(void)  	return 0;  } +module_param(off, int, 0444);  core_initcall(cpuidle_init);  | 
