162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * governor.c - governor support
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * (C) 2006-2007 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
562306a36Sopenharmony_ci *               Shaohua Li <shaohua.li@intel.com>
662306a36Sopenharmony_ci *               Adam Belay <abelay@novell.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * This code is licenced under the GPL.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/cpu.h>
1262306a36Sopenharmony_ci#include <linux/cpuidle.h>
1362306a36Sopenharmony_ci#include <linux/mutex.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/pm_qos.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "cpuidle.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cichar param_governor[CPUIDLE_NAME_LEN];
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ciLIST_HEAD(cpuidle_governors);
2262306a36Sopenharmony_cistruct cpuidle_governor *cpuidle_curr_governor;
2362306a36Sopenharmony_cistruct cpuidle_governor *cpuidle_prev_governor;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/**
2662306a36Sopenharmony_ci * cpuidle_find_governor - finds a governor of the specified name
2762306a36Sopenharmony_ci * @str: the name
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * Must be called with cpuidle_lock acquired.
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_cistruct cpuidle_governor *cpuidle_find_governor(const char *str)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct cpuidle_governor *gov;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	list_for_each_entry(gov, &cpuidle_governors, governor_list)
3662306a36Sopenharmony_ci		if (!strncasecmp(str, gov->name, CPUIDLE_NAME_LEN))
3762306a36Sopenharmony_ci			return gov;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	return NULL;
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/**
4362306a36Sopenharmony_ci * cpuidle_switch_governor - changes the governor
4462306a36Sopenharmony_ci * @gov: the new target governor
4562306a36Sopenharmony_ci * Must be called with cpuidle_lock acquired.
4662306a36Sopenharmony_ci */
4762306a36Sopenharmony_ciint cpuidle_switch_governor(struct cpuidle_governor *gov)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	struct cpuidle_device *dev;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	if (!gov)
5262306a36Sopenharmony_ci		return -EINVAL;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (gov == cpuidle_curr_governor)
5562306a36Sopenharmony_ci		return 0;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	cpuidle_uninstall_idle_handler();
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if (cpuidle_curr_governor) {
6062306a36Sopenharmony_ci		list_for_each_entry(dev, &cpuidle_detected_devices, device_list)
6162306a36Sopenharmony_ci			cpuidle_disable_device(dev);
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	cpuidle_curr_governor = gov;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	list_for_each_entry(dev, &cpuidle_detected_devices, device_list)
6762306a36Sopenharmony_ci		cpuidle_enable_device(dev);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	cpuidle_install_idle_handler();
7062306a36Sopenharmony_ci	pr_info("cpuidle: using governor %s\n", gov->name);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return 0;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/**
7662306a36Sopenharmony_ci * cpuidle_register_governor - registers a governor
7762306a36Sopenharmony_ci * @gov: the governor
7862306a36Sopenharmony_ci */
7962306a36Sopenharmony_ciint cpuidle_register_governor(struct cpuidle_governor *gov)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	int ret = -EEXIST;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (!gov || !gov->select)
8462306a36Sopenharmony_ci		return -EINVAL;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	if (cpuidle_disabled())
8762306a36Sopenharmony_ci		return -ENODEV;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	mutex_lock(&cpuidle_lock);
9062306a36Sopenharmony_ci	if (cpuidle_find_governor(gov->name) == NULL) {
9162306a36Sopenharmony_ci		ret = 0;
9262306a36Sopenharmony_ci		list_add_tail(&gov->governor_list, &cpuidle_governors);
9362306a36Sopenharmony_ci		if (!cpuidle_curr_governor ||
9462306a36Sopenharmony_ci		    !strncasecmp(param_governor, gov->name, CPUIDLE_NAME_LEN) ||
9562306a36Sopenharmony_ci		    (cpuidle_curr_governor->rating < gov->rating &&
9662306a36Sopenharmony_ci		     strncasecmp(param_governor, cpuidle_curr_governor->name,
9762306a36Sopenharmony_ci				 CPUIDLE_NAME_LEN)))
9862306a36Sopenharmony_ci			cpuidle_switch_governor(gov);
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci	mutex_unlock(&cpuidle_lock);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	return ret;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/**
10662306a36Sopenharmony_ci * cpuidle_governor_latency_req - Compute a latency constraint for CPU
10762306a36Sopenharmony_ci * @cpu: Target CPU
10862306a36Sopenharmony_ci */
10962306a36Sopenharmony_cis64 cpuidle_governor_latency_req(unsigned int cpu)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	struct device *device = get_cpu_device(cpu);
11262306a36Sopenharmony_ci	int device_req = dev_pm_qos_raw_resume_latency(device);
11362306a36Sopenharmony_ci	int global_req = cpu_latency_qos_limit();
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if (device_req > global_req)
11662306a36Sopenharmony_ci		device_req = global_req;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	return (s64)device_req * NSEC_PER_USEC;
11962306a36Sopenharmony_ci}
120