14f86d3a8SLen Brown /*
24f86d3a8SLen Brown * governor.c - governor support
34f86d3a8SLen Brown *
44f86d3a8SLen Brown * (C) 2006-2007 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
54f86d3a8SLen Brown * Shaohua Li <shaohua.li@intel.com>
64f86d3a8SLen Brown * Adam Belay <abelay@novell.com>
74f86d3a8SLen Brown *
84f86d3a8SLen Brown * This code is licenced under the GPL.
94f86d3a8SLen Brown */
104f86d3a8SLen Brown
110fc784fbSRafael J. Wysocki #include <linux/cpu.h>
124f86d3a8SLen Brown #include <linux/cpuidle.h>
130fc784fbSRafael J. Wysocki #include <linux/mutex.h>
1461cb5758SRafael J. Wysocki #include <linux/module.h>
150fc784fbSRafael J. Wysocki #include <linux/pm_qos.h>
164f86d3a8SLen Brown
174f86d3a8SLen Brown #include "cpuidle.h"
184f86d3a8SLen Brown
1961cb5758SRafael J. Wysocki char param_governor[CPUIDLE_NAME_LEN];
2061cb5758SRafael J. Wysocki
214f86d3a8SLen Brown LIST_HEAD(cpuidle_governors);
224f86d3a8SLen Brown struct cpuidle_governor *cpuidle_curr_governor;
23cb5d8c45SJoao Martins struct cpuidle_governor *cpuidle_prev_governor;
244f86d3a8SLen Brown
254f86d3a8SLen Brown /**
26cb5d8c45SJoao Martins * cpuidle_find_governor - finds a governor of the specified name
274f86d3a8SLen Brown * @str: the name
284f86d3a8SLen Brown *
2921ae2956SUwe Kleine-König * Must be called with cpuidle_lock acquired.
304f86d3a8SLen Brown */
cpuidle_find_governor(const char * str)31cb5d8c45SJoao Martins struct cpuidle_governor *cpuidle_find_governor(const char *str)
324f86d3a8SLen Brown {
334f86d3a8SLen Brown struct cpuidle_governor *gov;
344f86d3a8SLen Brown
354f86d3a8SLen Brown list_for_each_entry(gov, &cpuidle_governors, governor_list)
3691336640SRasmus Villemoes if (!strncasecmp(str, gov->name, CPUIDLE_NAME_LEN))
374f86d3a8SLen Brown return gov;
384f86d3a8SLen Brown
394f86d3a8SLen Brown return NULL;
404f86d3a8SLen Brown }
414f86d3a8SLen Brown
424f86d3a8SLen Brown /**
434f86d3a8SLen Brown * cpuidle_switch_governor - changes the governor
444f86d3a8SLen Brown * @gov: the new target governor
4521ae2956SUwe Kleine-König * Must be called with cpuidle_lock acquired.
464f86d3a8SLen Brown */
cpuidle_switch_governor(struct cpuidle_governor * gov)474f86d3a8SLen Brown int cpuidle_switch_governor(struct cpuidle_governor *gov)
484f86d3a8SLen Brown {
494f86d3a8SLen Brown struct cpuidle_device *dev;
504f86d3a8SLen Brown
513cfd68b5Sgaurav jindal if (!gov)
523cfd68b5Sgaurav jindal return -EINVAL;
533cfd68b5Sgaurav jindal
544f86d3a8SLen Brown if (gov == cpuidle_curr_governor)
554f86d3a8SLen Brown return 0;
564f86d3a8SLen Brown
574f86d3a8SLen Brown cpuidle_uninstall_idle_handler();
584f86d3a8SLen Brown
594f86d3a8SLen Brown if (cpuidle_curr_governor) {
604f86d3a8SLen Brown list_for_each_entry(dev, &cpuidle_detected_devices, device_list)
614f86d3a8SLen Brown cpuidle_disable_device(dev);
624f86d3a8SLen Brown }
634f86d3a8SLen Brown
644f86d3a8SLen Brown cpuidle_curr_governor = gov;
654f86d3a8SLen Brown
664f86d3a8SLen Brown list_for_each_entry(dev, &cpuidle_detected_devices, device_list)
674f86d3a8SLen Brown cpuidle_enable_device(dev);
68*06f7c087SYu Liao
694f86d3a8SLen Brown cpuidle_install_idle_handler();
70*06f7c087SYu Liao pr_info("cpuidle: using governor %s\n", gov->name);
714f86d3a8SLen Brown
724f86d3a8SLen Brown return 0;
734f86d3a8SLen Brown }
744f86d3a8SLen Brown
754f86d3a8SLen Brown /**
764f86d3a8SLen Brown * cpuidle_register_governor - registers a governor
774f86d3a8SLen Brown * @gov: the governor
784f86d3a8SLen Brown */
cpuidle_register_governor(struct cpuidle_governor * gov)794f86d3a8SLen Brown int cpuidle_register_governor(struct cpuidle_governor *gov)
804f86d3a8SLen Brown {
814f86d3a8SLen Brown int ret = -EEXIST;
824f86d3a8SLen Brown
834f86d3a8SLen Brown if (!gov || !gov->select)
844f86d3a8SLen Brown return -EINVAL;
854f86d3a8SLen Brown
8662027aeaSLen Brown if (cpuidle_disabled())
8762027aeaSLen Brown return -ENODEV;
8862027aeaSLen Brown
894f86d3a8SLen Brown mutex_lock(&cpuidle_lock);
90cb5d8c45SJoao Martins if (cpuidle_find_governor(gov->name) == NULL) {
914f86d3a8SLen Brown ret = 0;
9222782b3fSRafael J. Wysocki list_add_tail(&gov->governor_list, &cpuidle_governors);
934f86d3a8SLen Brown if (!cpuidle_curr_governor ||
9461cb5758SRafael J. Wysocki !strncasecmp(param_governor, gov->name, CPUIDLE_NAME_LEN) ||
9561cb5758SRafael J. Wysocki (cpuidle_curr_governor->rating < gov->rating &&
9661cb5758SRafael J. Wysocki strncasecmp(param_governor, cpuidle_curr_governor->name,
9761cb5758SRafael J. Wysocki CPUIDLE_NAME_LEN)))
984f86d3a8SLen Brown cpuidle_switch_governor(gov);
994f86d3a8SLen Brown }
1004f86d3a8SLen Brown mutex_unlock(&cpuidle_lock);
1014f86d3a8SLen Brown
1024f86d3a8SLen Brown return ret;
1034f86d3a8SLen Brown }
1040fc784fbSRafael J. Wysocki
1050fc784fbSRafael J. Wysocki /**
1060fc784fbSRafael J. Wysocki * cpuidle_governor_latency_req - Compute a latency constraint for CPU
1070fc784fbSRafael J. Wysocki * @cpu: Target CPU
1080fc784fbSRafael J. Wysocki */
cpuidle_governor_latency_req(unsigned int cpu)109c1d51f68SRafael J. Wysocki s64 cpuidle_governor_latency_req(unsigned int cpu)
1100fc784fbSRafael J. Wysocki {
1110fc784fbSRafael J. Wysocki struct device *device = get_cpu_device(cpu);
1128262331eSViresh Kumar int device_req = dev_pm_qos_raw_resume_latency(device);
113f60ccc35SRafael J. Wysocki int global_req = cpu_latency_qos_limit();
1140fc784fbSRafael J. Wysocki
115c1d51f68SRafael J. Wysocki if (device_req > global_req)
116c1d51f68SRafael J. Wysocki device_req = global_req;
117c1d51f68SRafael J. Wysocki
118c1d51f68SRafael J. Wysocki return (s64)device_req * NSEC_PER_USEC;
1190fc784fbSRafael J. Wysocki }
120