18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  linux/drivers/devfreq/governor_simpleondemand.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2011 Samsung Electronics
68c2ecf20Sopenharmony_ci *	MyungJoo Ham <myungjoo.ham@samsung.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/errno.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/devfreq.h>
128c2ecf20Sopenharmony_ci#include <linux/math64.h>
138c2ecf20Sopenharmony_ci#include "governor.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci/* Default constants for DevFreq-Simple-Ondemand (DFSO) */
168c2ecf20Sopenharmony_ci#define DFSO_UPTHRESHOLD	(90)
178c2ecf20Sopenharmony_ci#define DFSO_DOWNDIFFERENCTIAL	(5)
188c2ecf20Sopenharmony_cistatic int devfreq_simple_ondemand_func(struct devfreq *df,
198c2ecf20Sopenharmony_ci					unsigned long *freq)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	int err;
228c2ecf20Sopenharmony_ci	struct devfreq_dev_status *stat;
238c2ecf20Sopenharmony_ci	unsigned long long a, b;
248c2ecf20Sopenharmony_ci	unsigned int dfso_upthreshold = DFSO_UPTHRESHOLD;
258c2ecf20Sopenharmony_ci	unsigned int dfso_downdifferential = DFSO_DOWNDIFFERENCTIAL;
268c2ecf20Sopenharmony_ci	struct devfreq_simple_ondemand_data *data = df->data;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	err = devfreq_update_stats(df);
298c2ecf20Sopenharmony_ci	if (err)
308c2ecf20Sopenharmony_ci		return err;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	stat = &df->last_status;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	if (data) {
358c2ecf20Sopenharmony_ci		if (data->upthreshold)
368c2ecf20Sopenharmony_ci			dfso_upthreshold = data->upthreshold;
378c2ecf20Sopenharmony_ci		if (data->downdifferential)
388c2ecf20Sopenharmony_ci			dfso_downdifferential = data->downdifferential;
398c2ecf20Sopenharmony_ci	}
408c2ecf20Sopenharmony_ci	if (dfso_upthreshold > 100 ||
418c2ecf20Sopenharmony_ci	    dfso_upthreshold < dfso_downdifferential)
428c2ecf20Sopenharmony_ci		return -EINVAL;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	/* Assume MAX if it is going to be divided by zero */
458c2ecf20Sopenharmony_ci	if (stat->total_time == 0) {
468c2ecf20Sopenharmony_ci		*freq = DEVFREQ_MAX_FREQ;
478c2ecf20Sopenharmony_ci		return 0;
488c2ecf20Sopenharmony_ci	}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	/* Prevent overflow */
518c2ecf20Sopenharmony_ci	if (stat->busy_time >= (1 << 24) || stat->total_time >= (1 << 24)) {
528c2ecf20Sopenharmony_ci		stat->busy_time >>= 7;
538c2ecf20Sopenharmony_ci		stat->total_time >>= 7;
548c2ecf20Sopenharmony_ci	}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	/* Set MAX if it's busy enough */
578c2ecf20Sopenharmony_ci	if (stat->busy_time * 100 >
588c2ecf20Sopenharmony_ci	    stat->total_time * dfso_upthreshold) {
598c2ecf20Sopenharmony_ci		*freq = DEVFREQ_MAX_FREQ;
608c2ecf20Sopenharmony_ci		return 0;
618c2ecf20Sopenharmony_ci	}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	/* Set MAX if we do not know the initial frequency */
648c2ecf20Sopenharmony_ci	if (stat->current_frequency == 0) {
658c2ecf20Sopenharmony_ci		*freq = DEVFREQ_MAX_FREQ;
668c2ecf20Sopenharmony_ci		return 0;
678c2ecf20Sopenharmony_ci	}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	/* Keep the current frequency */
708c2ecf20Sopenharmony_ci	if (stat->busy_time * 100 >
718c2ecf20Sopenharmony_ci	    stat->total_time * (dfso_upthreshold - dfso_downdifferential)) {
728c2ecf20Sopenharmony_ci		*freq = stat->current_frequency;
738c2ecf20Sopenharmony_ci		return 0;
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	/* Set the desired frequency based on the load */
778c2ecf20Sopenharmony_ci	a = stat->busy_time;
788c2ecf20Sopenharmony_ci	a *= stat->current_frequency;
798c2ecf20Sopenharmony_ci	b = div_u64(a, stat->total_time);
808c2ecf20Sopenharmony_ci	b *= 100;
818c2ecf20Sopenharmony_ci	b = div_u64(b, (dfso_upthreshold - dfso_downdifferential / 2));
828c2ecf20Sopenharmony_ci	*freq = (unsigned long) b;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	return 0;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int devfreq_simple_ondemand_handler(struct devfreq *devfreq,
888c2ecf20Sopenharmony_ci				unsigned int event, void *data)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	switch (event) {
918c2ecf20Sopenharmony_ci	case DEVFREQ_GOV_START:
928c2ecf20Sopenharmony_ci		devfreq_monitor_start(devfreq);
938c2ecf20Sopenharmony_ci		break;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	case DEVFREQ_GOV_STOP:
968c2ecf20Sopenharmony_ci		devfreq_monitor_stop(devfreq);
978c2ecf20Sopenharmony_ci		break;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	case DEVFREQ_GOV_UPDATE_INTERVAL:
1008c2ecf20Sopenharmony_ci		devfreq_update_interval(devfreq, (unsigned int *)data);
1018c2ecf20Sopenharmony_ci		break;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	case DEVFREQ_GOV_SUSPEND:
1048c2ecf20Sopenharmony_ci		devfreq_monitor_suspend(devfreq);
1058c2ecf20Sopenharmony_ci		break;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	case DEVFREQ_GOV_RESUME:
1088c2ecf20Sopenharmony_ci		devfreq_monitor_resume(devfreq);
1098c2ecf20Sopenharmony_ci		break;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	default:
1128c2ecf20Sopenharmony_ci		break;
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	return 0;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic struct devfreq_governor devfreq_simple_ondemand = {
1198c2ecf20Sopenharmony_ci	.name = DEVFREQ_GOV_SIMPLE_ONDEMAND,
1208c2ecf20Sopenharmony_ci	.get_target_freq = devfreq_simple_ondemand_func,
1218c2ecf20Sopenharmony_ci	.event_handler = devfreq_simple_ondemand_handler,
1228c2ecf20Sopenharmony_ci};
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic int __init devfreq_simple_ondemand_init(void)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	return devfreq_add_governor(&devfreq_simple_ondemand);
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_cisubsys_initcall(devfreq_simple_ondemand_init);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic void __exit devfreq_simple_ondemand_exit(void)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	int ret;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	ret = devfreq_remove_governor(&devfreq_simple_ondemand);
1358c2ecf20Sopenharmony_ci	if (ret)
1368c2ecf20Sopenharmony_ci		pr_err("%s: failed remove governor %d\n", __func__, ret);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	return;
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_cimodule_exit(devfreq_simple_ondemand_exit);
1418c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
142