14f86d3a8e297205780cca027e974fd5f81064780Len Brown/*
24f86d3a8e297205780cca027e974fd5f81064780Len Brown * governor.c - governor support
34f86d3a8e297205780cca027e974fd5f81064780Len Brown *
44f86d3a8e297205780cca027e974fd5f81064780Len Brown * (C) 2006-2007 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
54f86d3a8e297205780cca027e974fd5f81064780Len Brown *               Shaohua Li <shaohua.li@intel.com>
64f86d3a8e297205780cca027e974fd5f81064780Len Brown *               Adam Belay <abelay@novell.com>
74f86d3a8e297205780cca027e974fd5f81064780Len Brown *
84f86d3a8e297205780cca027e974fd5f81064780Len Brown * This code is licenced under the GPL.
94f86d3a8e297205780cca027e974fd5f81064780Len Brown */
104f86d3a8e297205780cca027e974fd5f81064780Len Brown
114f86d3a8e297205780cca027e974fd5f81064780Len Brown#include <linux/mutex.h>
124f86d3a8e297205780cca027e974fd5f81064780Len Brown#include <linux/module.h>
134f86d3a8e297205780cca027e974fd5f81064780Len Brown#include <linux/cpuidle.h>
144f86d3a8e297205780cca027e974fd5f81064780Len Brown
154f86d3a8e297205780cca027e974fd5f81064780Len Brown#include "cpuidle.h"
164f86d3a8e297205780cca027e974fd5f81064780Len Brown
174f86d3a8e297205780cca027e974fd5f81064780Len BrownLIST_HEAD(cpuidle_governors);
184f86d3a8e297205780cca027e974fd5f81064780Len Brownstruct cpuidle_governor *cpuidle_curr_governor;
194f86d3a8e297205780cca027e974fd5f81064780Len Brown
204f86d3a8e297205780cca027e974fd5f81064780Len Brown/**
214f86d3a8e297205780cca027e974fd5f81064780Len Brown * __cpuidle_find_governor - finds a governor of the specified name
224f86d3a8e297205780cca027e974fd5f81064780Len Brown * @str: the name
234f86d3a8e297205780cca027e974fd5f81064780Len Brown *
2421ae2956ce289f61f11863cc67080f9a28101ae0Uwe Kleine-König * Must be called with cpuidle_lock acquired.
254f86d3a8e297205780cca027e974fd5f81064780Len Brown */
264f86d3a8e297205780cca027e974fd5f81064780Len Brownstatic struct cpuidle_governor * __cpuidle_find_governor(const char *str)
274f86d3a8e297205780cca027e974fd5f81064780Len Brown{
284f86d3a8e297205780cca027e974fd5f81064780Len Brown	struct cpuidle_governor *gov;
294f86d3a8e297205780cca027e974fd5f81064780Len Brown
304f86d3a8e297205780cca027e974fd5f81064780Len Brown	list_for_each_entry(gov, &cpuidle_governors, governor_list)
314f86d3a8e297205780cca027e974fd5f81064780Len Brown		if (!strnicmp(str, gov->name, CPUIDLE_NAME_LEN))
324f86d3a8e297205780cca027e974fd5f81064780Len Brown			return gov;
334f86d3a8e297205780cca027e974fd5f81064780Len Brown
344f86d3a8e297205780cca027e974fd5f81064780Len Brown	return NULL;
354f86d3a8e297205780cca027e974fd5f81064780Len Brown}
364f86d3a8e297205780cca027e974fd5f81064780Len Brown
374f86d3a8e297205780cca027e974fd5f81064780Len Brown/**
384f86d3a8e297205780cca027e974fd5f81064780Len Brown * cpuidle_switch_governor - changes the governor
394f86d3a8e297205780cca027e974fd5f81064780Len Brown * @gov: the new target governor
404f86d3a8e297205780cca027e974fd5f81064780Len Brown *
414f86d3a8e297205780cca027e974fd5f81064780Len Brown * NOTE: "gov" can be NULL to specify disabled
4221ae2956ce289f61f11863cc67080f9a28101ae0Uwe Kleine-König * Must be called with cpuidle_lock acquired.
434f86d3a8e297205780cca027e974fd5f81064780Len Brown */
444f86d3a8e297205780cca027e974fd5f81064780Len Brownint cpuidle_switch_governor(struct cpuidle_governor *gov)
454f86d3a8e297205780cca027e974fd5f81064780Len Brown{
464f86d3a8e297205780cca027e974fd5f81064780Len Brown	struct cpuidle_device *dev;
474f86d3a8e297205780cca027e974fd5f81064780Len Brown
484f86d3a8e297205780cca027e974fd5f81064780Len Brown	if (gov == cpuidle_curr_governor)
494f86d3a8e297205780cca027e974fd5f81064780Len Brown		return 0;
504f86d3a8e297205780cca027e974fd5f81064780Len Brown
514f86d3a8e297205780cca027e974fd5f81064780Len Brown	cpuidle_uninstall_idle_handler();
524f86d3a8e297205780cca027e974fd5f81064780Len Brown
534f86d3a8e297205780cca027e974fd5f81064780Len Brown	if (cpuidle_curr_governor) {
544f86d3a8e297205780cca027e974fd5f81064780Len Brown		list_for_each_entry(dev, &cpuidle_detected_devices, device_list)
554f86d3a8e297205780cca027e974fd5f81064780Len Brown			cpuidle_disable_device(dev);
564f86d3a8e297205780cca027e974fd5f81064780Len Brown		module_put(cpuidle_curr_governor->owner);
574f86d3a8e297205780cca027e974fd5f81064780Len Brown	}
584f86d3a8e297205780cca027e974fd5f81064780Len Brown
594f86d3a8e297205780cca027e974fd5f81064780Len Brown	cpuidle_curr_governor = gov;
604f86d3a8e297205780cca027e974fd5f81064780Len Brown
614f86d3a8e297205780cca027e974fd5f81064780Len Brown	if (gov) {
624f86d3a8e297205780cca027e974fd5f81064780Len Brown		if (!try_module_get(cpuidle_curr_governor->owner))
634f86d3a8e297205780cca027e974fd5f81064780Len Brown			return -EINVAL;
644f86d3a8e297205780cca027e974fd5f81064780Len Brown		list_for_each_entry(dev, &cpuidle_detected_devices, device_list)
654f86d3a8e297205780cca027e974fd5f81064780Len Brown			cpuidle_enable_device(dev);
664f86d3a8e297205780cca027e974fd5f81064780Len Brown		cpuidle_install_idle_handler();
674f86d3a8e297205780cca027e974fd5f81064780Len Brown		printk(KERN_INFO "cpuidle: using governor %s\n", gov->name);
684f86d3a8e297205780cca027e974fd5f81064780Len Brown	}
694f86d3a8e297205780cca027e974fd5f81064780Len Brown
704f86d3a8e297205780cca027e974fd5f81064780Len Brown	return 0;
714f86d3a8e297205780cca027e974fd5f81064780Len Brown}
724f86d3a8e297205780cca027e974fd5f81064780Len Brown
734f86d3a8e297205780cca027e974fd5f81064780Len Brown/**
744f86d3a8e297205780cca027e974fd5f81064780Len Brown * cpuidle_register_governor - registers a governor
754f86d3a8e297205780cca027e974fd5f81064780Len Brown * @gov: the governor
764f86d3a8e297205780cca027e974fd5f81064780Len Brown */
774f86d3a8e297205780cca027e974fd5f81064780Len Brownint cpuidle_register_governor(struct cpuidle_governor *gov)
784f86d3a8e297205780cca027e974fd5f81064780Len Brown{
794f86d3a8e297205780cca027e974fd5f81064780Len Brown	int ret = -EEXIST;
804f86d3a8e297205780cca027e974fd5f81064780Len Brown
814f86d3a8e297205780cca027e974fd5f81064780Len Brown	if (!gov || !gov->select)
824f86d3a8e297205780cca027e974fd5f81064780Len Brown		return -EINVAL;
834f86d3a8e297205780cca027e974fd5f81064780Len Brown
8462027aea23fcd14478abdddd3b74a4e0f5fb2984Len Brown	if (cpuidle_disabled())
8562027aea23fcd14478abdddd3b74a4e0f5fb2984Len Brown		return -ENODEV;
8662027aea23fcd14478abdddd3b74a4e0f5fb2984Len Brown
874f86d3a8e297205780cca027e974fd5f81064780Len Brown	mutex_lock(&cpuidle_lock);
884f86d3a8e297205780cca027e974fd5f81064780Len Brown	if (__cpuidle_find_governor(gov->name) == NULL) {
894f86d3a8e297205780cca027e974fd5f81064780Len Brown		ret = 0;
904f86d3a8e297205780cca027e974fd5f81064780Len Brown		list_add_tail(&gov->governor_list, &cpuidle_governors);
914f86d3a8e297205780cca027e974fd5f81064780Len Brown		if (!cpuidle_curr_governor ||
924f86d3a8e297205780cca027e974fd5f81064780Len Brown		    cpuidle_curr_governor->rating < gov->rating)
934f86d3a8e297205780cca027e974fd5f81064780Len Brown			cpuidle_switch_governor(gov);
944f86d3a8e297205780cca027e974fd5f81064780Len Brown	}
954f86d3a8e297205780cca027e974fd5f81064780Len Brown	mutex_unlock(&cpuidle_lock);
964f86d3a8e297205780cca027e974fd5f81064780Len Brown
974f86d3a8e297205780cca027e974fd5f81064780Len Brown	return ret;
984f86d3a8e297205780cca027e974fd5f81064780Len Brown}
994f86d3a8e297205780cca027e974fd5f81064780Len Brown
1004f86d3a8e297205780cca027e974fd5f81064780Len Brown/**
1014f86d3a8e297205780cca027e974fd5f81064780Len Brown * cpuidle_replace_governor - find a replacement governor
1024f86d3a8e297205780cca027e974fd5f81064780Len Brown * @exclude_rating: the rating that will be skipped while looking for
1034f86d3a8e297205780cca027e974fd5f81064780Len Brown * new governor.
1044f86d3a8e297205780cca027e974fd5f81064780Len Brown */
1054f86d3a8e297205780cca027e974fd5f81064780Len Brownstatic struct cpuidle_governor *cpuidle_replace_governor(int exclude_rating)
1064f86d3a8e297205780cca027e974fd5f81064780Len Brown{
1074f86d3a8e297205780cca027e974fd5f81064780Len Brown	struct cpuidle_governor *gov;
1084f86d3a8e297205780cca027e974fd5f81064780Len Brown	struct cpuidle_governor *ret_gov = NULL;
1094f86d3a8e297205780cca027e974fd5f81064780Len Brown	unsigned int max_rating = 0;
1104f86d3a8e297205780cca027e974fd5f81064780Len Brown
1114f86d3a8e297205780cca027e974fd5f81064780Len Brown	list_for_each_entry(gov, &cpuidle_governors, governor_list) {
1124f86d3a8e297205780cca027e974fd5f81064780Len Brown		if (gov->rating == exclude_rating)
1134f86d3a8e297205780cca027e974fd5f81064780Len Brown			continue;
1144f86d3a8e297205780cca027e974fd5f81064780Len Brown		if (gov->rating > max_rating) {
1154f86d3a8e297205780cca027e974fd5f81064780Len Brown			max_rating = gov->rating;
1164f86d3a8e297205780cca027e974fd5f81064780Len Brown			ret_gov = gov;
1174f86d3a8e297205780cca027e974fd5f81064780Len Brown		}
1184f86d3a8e297205780cca027e974fd5f81064780Len Brown	}
1194f86d3a8e297205780cca027e974fd5f81064780Len Brown
1204f86d3a8e297205780cca027e974fd5f81064780Len Brown	return ret_gov;
1214f86d3a8e297205780cca027e974fd5f81064780Len Brown}
1224f86d3a8e297205780cca027e974fd5f81064780Len Brown
1234f86d3a8e297205780cca027e974fd5f81064780Len Brown/**
1244f86d3a8e297205780cca027e974fd5f81064780Len Brown * cpuidle_unregister_governor - unregisters a governor
1254f86d3a8e297205780cca027e974fd5f81064780Len Brown * @gov: the governor
1264f86d3a8e297205780cca027e974fd5f81064780Len Brown */
1274f86d3a8e297205780cca027e974fd5f81064780Len Brownvoid cpuidle_unregister_governor(struct cpuidle_governor *gov)
1284f86d3a8e297205780cca027e974fd5f81064780Len Brown{
1294f86d3a8e297205780cca027e974fd5f81064780Len Brown	if (!gov)
1304f86d3a8e297205780cca027e974fd5f81064780Len Brown		return;
1314f86d3a8e297205780cca027e974fd5f81064780Len Brown
1324f86d3a8e297205780cca027e974fd5f81064780Len Brown	mutex_lock(&cpuidle_lock);
1334f86d3a8e297205780cca027e974fd5f81064780Len Brown	if (gov == cpuidle_curr_governor) {
1344f86d3a8e297205780cca027e974fd5f81064780Len Brown		struct cpuidle_governor *new_gov;
1354f86d3a8e297205780cca027e974fd5f81064780Len Brown		new_gov = cpuidle_replace_governor(gov->rating);
1364f86d3a8e297205780cca027e974fd5f81064780Len Brown		cpuidle_switch_governor(new_gov);
1374f86d3a8e297205780cca027e974fd5f81064780Len Brown	}
1384f86d3a8e297205780cca027e974fd5f81064780Len Brown	list_del(&gov->governor_list);
1394f86d3a8e297205780cca027e974fd5f81064780Len Brown	mutex_unlock(&cpuidle_lock);
1404f86d3a8e297205780cca027e974fd5f81064780Len Brown}
1414f86d3a8e297205780cca027e974fd5f81064780Len Brown
142