162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  drivers/cpufreq/cpufreq_conservative.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C)  2001 Russell King
662306a36Sopenharmony_ci *            (C)  2003 Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>.
762306a36Sopenharmony_ci *                      Jun Nakajima <jun.nakajima@intel.com>
862306a36Sopenharmony_ci *            (C)  2009 Alexander Clouter <alex@digriz.org.uk>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include "cpufreq_governor.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistruct cs_policy_dbs_info {
1562306a36Sopenharmony_ci	struct policy_dbs_info policy_dbs;
1662306a36Sopenharmony_ci	unsigned int down_skip;
1762306a36Sopenharmony_ci	unsigned int requested_freq;
1862306a36Sopenharmony_ci};
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic inline struct cs_policy_dbs_info *to_dbs_info(struct policy_dbs_info *policy_dbs)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	return container_of(policy_dbs, struct cs_policy_dbs_info, policy_dbs);
2362306a36Sopenharmony_ci}
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistruct cs_dbs_tuners {
2662306a36Sopenharmony_ci	unsigned int down_threshold;
2762306a36Sopenharmony_ci	unsigned int freq_step;
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/* Conservative governor macros */
3162306a36Sopenharmony_ci#define DEF_FREQUENCY_UP_THRESHOLD		(80)
3262306a36Sopenharmony_ci#define DEF_FREQUENCY_DOWN_THRESHOLD		(20)
3362306a36Sopenharmony_ci#define DEF_FREQUENCY_STEP			(5)
3462306a36Sopenharmony_ci#define DEF_SAMPLING_DOWN_FACTOR		(1)
3562306a36Sopenharmony_ci#define MAX_SAMPLING_DOWN_FACTOR		(10)
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic inline unsigned int get_freq_step(struct cs_dbs_tuners *cs_tuners,
3862306a36Sopenharmony_ci					 struct cpufreq_policy *policy)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	unsigned int freq_step = (cs_tuners->freq_step * policy->max) / 100;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	/* max freq cannot be less than 100. But who knows... */
4362306a36Sopenharmony_ci	if (unlikely(freq_step == 0))
4462306a36Sopenharmony_ci		freq_step = DEF_FREQUENCY_STEP;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	return freq_step;
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/*
5062306a36Sopenharmony_ci * Every sampling_rate, we check, if current idle time is less than 20%
5162306a36Sopenharmony_ci * (default), then we try to increase frequency. Every sampling_rate *
5262306a36Sopenharmony_ci * sampling_down_factor, we check, if current idle time is more than 80%
5362306a36Sopenharmony_ci * (default), then we try to decrease frequency
5462306a36Sopenharmony_ci *
5562306a36Sopenharmony_ci * Frequency updates happen at minimum steps of 5% (default) of maximum
5662306a36Sopenharmony_ci * frequency
5762306a36Sopenharmony_ci */
5862306a36Sopenharmony_cistatic unsigned int cs_dbs_update(struct cpufreq_policy *policy)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct policy_dbs_info *policy_dbs = policy->governor_data;
6162306a36Sopenharmony_ci	struct cs_policy_dbs_info *dbs_info = to_dbs_info(policy_dbs);
6262306a36Sopenharmony_ci	unsigned int requested_freq = dbs_info->requested_freq;
6362306a36Sopenharmony_ci	struct dbs_data *dbs_data = policy_dbs->dbs_data;
6462306a36Sopenharmony_ci	struct cs_dbs_tuners *cs_tuners = dbs_data->tuners;
6562306a36Sopenharmony_ci	unsigned int load = dbs_update(policy);
6662306a36Sopenharmony_ci	unsigned int freq_step;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	/*
6962306a36Sopenharmony_ci	 * break out if we 'cannot' reduce the speed as the user might
7062306a36Sopenharmony_ci	 * want freq_step to be zero
7162306a36Sopenharmony_ci	 */
7262306a36Sopenharmony_ci	if (cs_tuners->freq_step == 0)
7362306a36Sopenharmony_ci		goto out;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	/*
7662306a36Sopenharmony_ci	 * If requested_freq is out of range, it is likely that the limits
7762306a36Sopenharmony_ci	 * changed in the meantime, so fall back to current frequency in that
7862306a36Sopenharmony_ci	 * case.
7962306a36Sopenharmony_ci	 */
8062306a36Sopenharmony_ci	if (requested_freq > policy->max || requested_freq < policy->min) {
8162306a36Sopenharmony_ci		requested_freq = policy->cur;
8262306a36Sopenharmony_ci		dbs_info->requested_freq = requested_freq;
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	freq_step = get_freq_step(cs_tuners, policy);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	/*
8862306a36Sopenharmony_ci	 * Decrease requested_freq one freq_step for each idle period that
8962306a36Sopenharmony_ci	 * we didn't update the frequency.
9062306a36Sopenharmony_ci	 */
9162306a36Sopenharmony_ci	if (policy_dbs->idle_periods < UINT_MAX) {
9262306a36Sopenharmony_ci		unsigned int freq_steps = policy_dbs->idle_periods * freq_step;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci		if (requested_freq > policy->min + freq_steps)
9562306a36Sopenharmony_ci			requested_freq -= freq_steps;
9662306a36Sopenharmony_ci		else
9762306a36Sopenharmony_ci			requested_freq = policy->min;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci		policy_dbs->idle_periods = UINT_MAX;
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	/* Check for frequency increase */
10362306a36Sopenharmony_ci	if (load > dbs_data->up_threshold) {
10462306a36Sopenharmony_ci		dbs_info->down_skip = 0;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci		/* if we are already at full speed then break out early */
10762306a36Sopenharmony_ci		if (requested_freq == policy->max)
10862306a36Sopenharmony_ci			goto out;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci		requested_freq += freq_step;
11162306a36Sopenharmony_ci		if (requested_freq > policy->max)
11262306a36Sopenharmony_ci			requested_freq = policy->max;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		__cpufreq_driver_target(policy, requested_freq,
11562306a36Sopenharmony_ci					CPUFREQ_RELATION_HE);
11662306a36Sopenharmony_ci		dbs_info->requested_freq = requested_freq;
11762306a36Sopenharmony_ci		goto out;
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/* if sampling_down_factor is active break out early */
12162306a36Sopenharmony_ci	if (++dbs_info->down_skip < dbs_data->sampling_down_factor)
12262306a36Sopenharmony_ci		goto out;
12362306a36Sopenharmony_ci	dbs_info->down_skip = 0;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	/* Check for frequency decrease */
12662306a36Sopenharmony_ci	if (load < cs_tuners->down_threshold) {
12762306a36Sopenharmony_ci		/*
12862306a36Sopenharmony_ci		 * if we cannot reduce the frequency anymore, break out early
12962306a36Sopenharmony_ci		 */
13062306a36Sopenharmony_ci		if (requested_freq == policy->min)
13162306a36Sopenharmony_ci			goto out;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci		if (requested_freq > freq_step)
13462306a36Sopenharmony_ci			requested_freq -= freq_step;
13562306a36Sopenharmony_ci		else
13662306a36Sopenharmony_ci			requested_freq = policy->min;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci		__cpufreq_driver_target(policy, requested_freq,
13962306a36Sopenharmony_ci					CPUFREQ_RELATION_LE);
14062306a36Sopenharmony_ci		dbs_info->requested_freq = requested_freq;
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci out:
14462306a36Sopenharmony_ci	return dbs_data->sampling_rate;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/************************** sysfs interface ************************/
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic ssize_t sampling_down_factor_store(struct gov_attr_set *attr_set,
15062306a36Sopenharmony_ci					  const char *buf, size_t count)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	struct dbs_data *dbs_data = to_dbs_data(attr_set);
15362306a36Sopenharmony_ci	unsigned int input;
15462306a36Sopenharmony_ci	int ret;
15562306a36Sopenharmony_ci	ret = sscanf(buf, "%u", &input);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	if (ret != 1 || input > MAX_SAMPLING_DOWN_FACTOR || input < 1)
15862306a36Sopenharmony_ci		return -EINVAL;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	dbs_data->sampling_down_factor = input;
16162306a36Sopenharmony_ci	return count;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic ssize_t up_threshold_store(struct gov_attr_set *attr_set,
16562306a36Sopenharmony_ci				  const char *buf, size_t count)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	struct dbs_data *dbs_data = to_dbs_data(attr_set);
16862306a36Sopenharmony_ci	struct cs_dbs_tuners *cs_tuners = dbs_data->tuners;
16962306a36Sopenharmony_ci	unsigned int input;
17062306a36Sopenharmony_ci	int ret;
17162306a36Sopenharmony_ci	ret = sscanf(buf, "%u", &input);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (ret != 1 || input > 100 || input <= cs_tuners->down_threshold)
17462306a36Sopenharmony_ci		return -EINVAL;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	dbs_data->up_threshold = input;
17762306a36Sopenharmony_ci	return count;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic ssize_t down_threshold_store(struct gov_attr_set *attr_set,
18162306a36Sopenharmony_ci				    const char *buf, size_t count)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct dbs_data *dbs_data = to_dbs_data(attr_set);
18462306a36Sopenharmony_ci	struct cs_dbs_tuners *cs_tuners = dbs_data->tuners;
18562306a36Sopenharmony_ci	unsigned int input;
18662306a36Sopenharmony_ci	int ret;
18762306a36Sopenharmony_ci	ret = sscanf(buf, "%u", &input);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/* cannot be lower than 1 otherwise freq will not fall */
19062306a36Sopenharmony_ci	if (ret != 1 || input < 1 || input > 100 ||
19162306a36Sopenharmony_ci			input >= dbs_data->up_threshold)
19262306a36Sopenharmony_ci		return -EINVAL;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	cs_tuners->down_threshold = input;
19562306a36Sopenharmony_ci	return count;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic ssize_t ignore_nice_load_store(struct gov_attr_set *attr_set,
19962306a36Sopenharmony_ci				      const char *buf, size_t count)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	struct dbs_data *dbs_data = to_dbs_data(attr_set);
20262306a36Sopenharmony_ci	unsigned int input;
20362306a36Sopenharmony_ci	int ret;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	ret = sscanf(buf, "%u", &input);
20662306a36Sopenharmony_ci	if (ret != 1)
20762306a36Sopenharmony_ci		return -EINVAL;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	if (input > 1)
21062306a36Sopenharmony_ci		input = 1;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (input == dbs_data->ignore_nice_load) /* nothing to do */
21362306a36Sopenharmony_ci		return count;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	dbs_data->ignore_nice_load = input;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/* we need to re-evaluate prev_cpu_idle */
21862306a36Sopenharmony_ci	gov_update_cpu_data(dbs_data);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	return count;
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic ssize_t freq_step_store(struct gov_attr_set *attr_set, const char *buf,
22462306a36Sopenharmony_ci			       size_t count)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct dbs_data *dbs_data = to_dbs_data(attr_set);
22762306a36Sopenharmony_ci	struct cs_dbs_tuners *cs_tuners = dbs_data->tuners;
22862306a36Sopenharmony_ci	unsigned int input;
22962306a36Sopenharmony_ci	int ret;
23062306a36Sopenharmony_ci	ret = sscanf(buf, "%u", &input);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	if (ret != 1)
23362306a36Sopenharmony_ci		return -EINVAL;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (input > 100)
23662306a36Sopenharmony_ci		input = 100;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/*
23962306a36Sopenharmony_ci	 * no need to test here if freq_step is zero as the user might actually
24062306a36Sopenharmony_ci	 * want this, they would be crazy though :)
24162306a36Sopenharmony_ci	 */
24262306a36Sopenharmony_ci	cs_tuners->freq_step = input;
24362306a36Sopenharmony_ci	return count;
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cigov_show_one_common(sampling_rate);
24762306a36Sopenharmony_cigov_show_one_common(sampling_down_factor);
24862306a36Sopenharmony_cigov_show_one_common(up_threshold);
24962306a36Sopenharmony_cigov_show_one_common(ignore_nice_load);
25062306a36Sopenharmony_cigov_show_one(cs, down_threshold);
25162306a36Sopenharmony_cigov_show_one(cs, freq_step);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cigov_attr_rw(sampling_rate);
25462306a36Sopenharmony_cigov_attr_rw(sampling_down_factor);
25562306a36Sopenharmony_cigov_attr_rw(up_threshold);
25662306a36Sopenharmony_cigov_attr_rw(ignore_nice_load);
25762306a36Sopenharmony_cigov_attr_rw(down_threshold);
25862306a36Sopenharmony_cigov_attr_rw(freq_step);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic struct attribute *cs_attrs[] = {
26162306a36Sopenharmony_ci	&sampling_rate.attr,
26262306a36Sopenharmony_ci	&sampling_down_factor.attr,
26362306a36Sopenharmony_ci	&up_threshold.attr,
26462306a36Sopenharmony_ci	&down_threshold.attr,
26562306a36Sopenharmony_ci	&ignore_nice_load.attr,
26662306a36Sopenharmony_ci	&freq_step.attr,
26762306a36Sopenharmony_ci	NULL
26862306a36Sopenharmony_ci};
26962306a36Sopenharmony_ciATTRIBUTE_GROUPS(cs);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci/************************** sysfs end ************************/
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic struct policy_dbs_info *cs_alloc(void)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct cs_policy_dbs_info *dbs_info;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	dbs_info = kzalloc(sizeof(*dbs_info), GFP_KERNEL);
27862306a36Sopenharmony_ci	return dbs_info ? &dbs_info->policy_dbs : NULL;
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistatic void cs_free(struct policy_dbs_info *policy_dbs)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	kfree(to_dbs_info(policy_dbs));
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic int cs_init(struct dbs_data *dbs_data)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	struct cs_dbs_tuners *tuners;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	tuners = kzalloc(sizeof(*tuners), GFP_KERNEL);
29162306a36Sopenharmony_ci	if (!tuners)
29262306a36Sopenharmony_ci		return -ENOMEM;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	tuners->down_threshold = DEF_FREQUENCY_DOWN_THRESHOLD;
29562306a36Sopenharmony_ci	tuners->freq_step = DEF_FREQUENCY_STEP;
29662306a36Sopenharmony_ci	dbs_data->up_threshold = DEF_FREQUENCY_UP_THRESHOLD;
29762306a36Sopenharmony_ci	dbs_data->sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR;
29862306a36Sopenharmony_ci	dbs_data->ignore_nice_load = 0;
29962306a36Sopenharmony_ci	dbs_data->tuners = tuners;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	return 0;
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic void cs_exit(struct dbs_data *dbs_data)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	kfree(dbs_data->tuners);
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic void cs_start(struct cpufreq_policy *policy)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	struct cs_policy_dbs_info *dbs_info = to_dbs_info(policy->governor_data);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	dbs_info->down_skip = 0;
31462306a36Sopenharmony_ci	dbs_info->requested_freq = policy->cur;
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic struct dbs_governor cs_governor = {
31862306a36Sopenharmony_ci	.gov = CPUFREQ_DBS_GOVERNOR_INITIALIZER("conservative"),
31962306a36Sopenharmony_ci	.kobj_type = { .default_groups = cs_groups },
32062306a36Sopenharmony_ci	.gov_dbs_update = cs_dbs_update,
32162306a36Sopenharmony_ci	.alloc = cs_alloc,
32262306a36Sopenharmony_ci	.free = cs_free,
32362306a36Sopenharmony_ci	.init = cs_init,
32462306a36Sopenharmony_ci	.exit = cs_exit,
32562306a36Sopenharmony_ci	.start = cs_start,
32662306a36Sopenharmony_ci};
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci#define CPU_FREQ_GOV_CONSERVATIVE	(cs_governor.gov)
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ciMODULE_AUTHOR("Alexander Clouter <alex@digriz.org.uk>");
33162306a36Sopenharmony_ciMODULE_DESCRIPTION("'cpufreq_conservative' - A dynamic cpufreq governor for "
33262306a36Sopenharmony_ci		"Low Latency Frequency Transition capable processors "
33362306a36Sopenharmony_ci		"optimised for use in a battery environment");
33462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE
33762306a36Sopenharmony_cistruct cpufreq_governor *cpufreq_default_governor(void)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	return &CPU_FREQ_GOV_CONSERVATIVE;
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci#endif
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cicpufreq_governor_init(CPU_FREQ_GOV_CONSERVATIVE);
34462306a36Sopenharmony_cicpufreq_governor_exit(CPU_FREQ_GOV_CONSERVATIVE);
345