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