xref: /openbmc/linux/drivers/devfreq/governor_simpleondemand.c (revision cdd38c5f1ce4398ec58fec95904b75824daab7b5)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2ce26c5bbSMyungJoo Ham /*
3ce26c5bbSMyungJoo Ham  *  linux/drivers/devfreq/governor_simpleondemand.c
4ce26c5bbSMyungJoo Ham  *
5ce26c5bbSMyungJoo Ham  *  Copyright (C) 2011 Samsung Electronics
6ce26c5bbSMyungJoo Ham  *	MyungJoo Ham <myungjoo.ham@samsung.com>
7ce26c5bbSMyungJoo Ham  */
8ce26c5bbSMyungJoo Ham 
9ce26c5bbSMyungJoo Ham #include <linux/errno.h>
10eff607fdSNishanth Menon #include <linux/module.h>
11ce26c5bbSMyungJoo Ham #include <linux/devfreq.h>
12ce26c5bbSMyungJoo Ham #include <linux/math64.h>
137e6fdd4bSRajagopal Venkat #include "governor.h"
14ce26c5bbSMyungJoo Ham 
15ce26c5bbSMyungJoo Ham /* Default constants for DevFreq-Simple-Ondemand (DFSO) */
16ce26c5bbSMyungJoo Ham #define DFSO_UPTHRESHOLD	(90)
17ce26c5bbSMyungJoo Ham #define DFSO_DOWNDIFFERENCTIAL	(5)
devfreq_simple_ondemand_func(struct devfreq * df,unsigned long * freq)18ce26c5bbSMyungJoo Ham static int devfreq_simple_ondemand_func(struct devfreq *df,
19ce26c5bbSMyungJoo Ham 					unsigned long *freq)
20ce26c5bbSMyungJoo Ham {
2108e75e75SJavi Merino 	int err;
2208e75e75SJavi Merino 	struct devfreq_dev_status *stat;
23ce26c5bbSMyungJoo Ham 	unsigned long long a, b;
24ce26c5bbSMyungJoo Ham 	unsigned int dfso_upthreshold = DFSO_UPTHRESHOLD;
25ce26c5bbSMyungJoo Ham 	unsigned int dfso_downdifferential = DFSO_DOWNDIFFERENCTIAL;
26ce26c5bbSMyungJoo Ham 	struct devfreq_simple_ondemand_data *data = df->data;
27ce26c5bbSMyungJoo Ham 
2808e75e75SJavi Merino 	err = devfreq_update_stats(df);
29ce26c5bbSMyungJoo Ham 	if (err)
30ce26c5bbSMyungJoo Ham 		return err;
31ce26c5bbSMyungJoo Ham 
3208e75e75SJavi Merino 	stat = &df->last_status;
3308e75e75SJavi Merino 
34ce26c5bbSMyungJoo Ham 	if (data) {
35ce26c5bbSMyungJoo Ham 		if (data->upthreshold)
36ce26c5bbSMyungJoo Ham 			dfso_upthreshold = data->upthreshold;
37ce26c5bbSMyungJoo Ham 		if (data->downdifferential)
38ce26c5bbSMyungJoo Ham 			dfso_downdifferential = data->downdifferential;
39ce26c5bbSMyungJoo Ham 	}
40ce26c5bbSMyungJoo Ham 	if (dfso_upthreshold > 100 ||
41ce26c5bbSMyungJoo Ham 	    dfso_upthreshold < dfso_downdifferential)
42ce26c5bbSMyungJoo Ham 		return -EINVAL;
43ce26c5bbSMyungJoo Ham 
44ce26c5bbSMyungJoo Ham 	/* Assume MAX if it is going to be divided by zero */
4508e75e75SJavi Merino 	if (stat->total_time == 0) {
466ff66e2aSMatthias Kaehlcke 		*freq = DEVFREQ_MAX_FREQ;
47ce26c5bbSMyungJoo Ham 		return 0;
48ce26c5bbSMyungJoo Ham 	}
49ce26c5bbSMyungJoo Ham 
50ce26c5bbSMyungJoo Ham 	/* Prevent overflow */
5108e75e75SJavi Merino 	if (stat->busy_time >= (1 << 24) || stat->total_time >= (1 << 24)) {
5208e75e75SJavi Merino 		stat->busy_time >>= 7;
5308e75e75SJavi Merino 		stat->total_time >>= 7;
54ce26c5bbSMyungJoo Ham 	}
55ce26c5bbSMyungJoo Ham 
56ce26c5bbSMyungJoo Ham 	/* Set MAX if it's busy enough */
5708e75e75SJavi Merino 	if (stat->busy_time * 100 >
5808e75e75SJavi Merino 	    stat->total_time * dfso_upthreshold) {
596ff66e2aSMatthias Kaehlcke 		*freq = DEVFREQ_MAX_FREQ;
60ce26c5bbSMyungJoo Ham 		return 0;
61ce26c5bbSMyungJoo Ham 	}
62ce26c5bbSMyungJoo Ham 
63ce26c5bbSMyungJoo Ham 	/* Set MAX if we do not know the initial frequency */
6408e75e75SJavi Merino 	if (stat->current_frequency == 0) {
656ff66e2aSMatthias Kaehlcke 		*freq = DEVFREQ_MAX_FREQ;
66ce26c5bbSMyungJoo Ham 		return 0;
67ce26c5bbSMyungJoo Ham 	}
68ce26c5bbSMyungJoo Ham 
69ce26c5bbSMyungJoo Ham 	/* Keep the current frequency */
7008e75e75SJavi Merino 	if (stat->busy_time * 100 >
7108e75e75SJavi Merino 	    stat->total_time * (dfso_upthreshold - dfso_downdifferential)) {
7208e75e75SJavi Merino 		*freq = stat->current_frequency;
73ce26c5bbSMyungJoo Ham 		return 0;
74ce26c5bbSMyungJoo Ham 	}
75ce26c5bbSMyungJoo Ham 
76ce26c5bbSMyungJoo Ham 	/* Set the desired frequency based on the load */
7708e75e75SJavi Merino 	a = stat->busy_time;
7808e75e75SJavi Merino 	a *= stat->current_frequency;
7908e75e75SJavi Merino 	b = div_u64(a, stat->total_time);
80ce26c5bbSMyungJoo Ham 	b *= 100;
81ce26c5bbSMyungJoo Ham 	b = div_u64(b, (dfso_upthreshold - dfso_downdifferential / 2));
82ce26c5bbSMyungJoo Ham 	*freq = (unsigned long) b;
83ce26c5bbSMyungJoo Ham 
84ce26c5bbSMyungJoo Ham 	return 0;
85ce26c5bbSMyungJoo Ham }
86ce26c5bbSMyungJoo Ham 
devfreq_simple_ondemand_handler(struct devfreq * devfreq,unsigned int event,void * data)877e6fdd4bSRajagopal Venkat static int devfreq_simple_ondemand_handler(struct devfreq *devfreq,
887e6fdd4bSRajagopal Venkat 				unsigned int event, void *data)
897e6fdd4bSRajagopal Venkat {
907e6fdd4bSRajagopal Venkat 	switch (event) {
917e6fdd4bSRajagopal Venkat 	case DEVFREQ_GOV_START:
927e6fdd4bSRajagopal Venkat 		devfreq_monitor_start(devfreq);
937e6fdd4bSRajagopal Venkat 		break;
947e6fdd4bSRajagopal Venkat 
957e6fdd4bSRajagopal Venkat 	case DEVFREQ_GOV_STOP:
967e6fdd4bSRajagopal Venkat 		devfreq_monitor_stop(devfreq);
977e6fdd4bSRajagopal Venkat 		break;
987e6fdd4bSRajagopal Venkat 
993a1ec2e8SChanwoo Choi 	case DEVFREQ_GOV_UPDATE_INTERVAL:
1003a1ec2e8SChanwoo Choi 		devfreq_update_interval(devfreq, (unsigned int *)data);
1017e6fdd4bSRajagopal Venkat 		break;
102206c30cfSRajagopal Venkat 
103206c30cfSRajagopal Venkat 	case DEVFREQ_GOV_SUSPEND:
104206c30cfSRajagopal Venkat 		devfreq_monitor_suspend(devfreq);
105206c30cfSRajagopal Venkat 		break;
106206c30cfSRajagopal Venkat 
107206c30cfSRajagopal Venkat 	case DEVFREQ_GOV_RESUME:
108206c30cfSRajagopal Venkat 		devfreq_monitor_resume(devfreq);
109206c30cfSRajagopal Venkat 		break;
110206c30cfSRajagopal Venkat 
1117e6fdd4bSRajagopal Venkat 	default:
1127e6fdd4bSRajagopal Venkat 		break;
1137e6fdd4bSRajagopal Venkat 	}
1147e6fdd4bSRajagopal Venkat 
1157e6fdd4bSRajagopal Venkat 	return 0;
1167e6fdd4bSRajagopal Venkat }
1177e6fdd4bSRajagopal Venkat 
1181b5c1be2SNishanth Menon static struct devfreq_governor devfreq_simple_ondemand = {
119aa7c352fSChanwoo Choi 	.name = DEVFREQ_GOV_SIMPLE_ONDEMAND,
120*5f1a9066SChanwoo Choi 	.attrs = DEVFREQ_GOV_ATTR_POLLING_INTERVAL
121*5f1a9066SChanwoo Choi 		| DEVFREQ_GOV_ATTR_TIMER,
122ce26c5bbSMyungJoo Ham 	.get_target_freq = devfreq_simple_ondemand_func,
1237e6fdd4bSRajagopal Venkat 	.event_handler = devfreq_simple_ondemand_handler,
124ce26c5bbSMyungJoo Ham };
12583116e66SNishanth Menon 
devfreq_simple_ondemand_init(void)12683116e66SNishanth Menon static int __init devfreq_simple_ondemand_init(void)
12783116e66SNishanth Menon {
12883116e66SNishanth Menon 	return devfreq_add_governor(&devfreq_simple_ondemand);
12983116e66SNishanth Menon }
13083116e66SNishanth Menon subsys_initcall(devfreq_simple_ondemand_init);
13183116e66SNishanth Menon 
devfreq_simple_ondemand_exit(void)13283116e66SNishanth Menon static void __exit devfreq_simple_ondemand_exit(void)
13383116e66SNishanth Menon {
13483116e66SNishanth Menon 	int ret;
13583116e66SNishanth Menon 
13683116e66SNishanth Menon 	ret = devfreq_remove_governor(&devfreq_simple_ondemand);
13783116e66SNishanth Menon 	if (ret)
13883116e66SNishanth Menon 		pr_err("%s: failed remove governor %d\n", __func__, ret);
13983116e66SNishanth Menon 
14083116e66SNishanth Menon 	return;
14183116e66SNishanth Menon }
14283116e66SNishanth Menon module_exit(devfreq_simple_ondemand_exit);
143eff607fdSNishanth Menon MODULE_LICENSE("GPL");
144