xref: /openbmc/linux/drivers/cpuidle/governor.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
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