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