375 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * linux/drivers/cpufreq/freq_table.c4 *5 * Copyright (C) 2002 - 2003 Dominik Brodowski6 */7 8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt9 10#include <linux/cpufreq.h>11#include <linux/module.h>12 13/*********************************************************************14 * FREQUENCY TABLE HELPERS *15 *********************************************************************/16 17bool policy_has_boost_freq(struct cpufreq_policy *policy)18{19 struct cpufreq_frequency_table *pos, *table = policy->freq_table;20 21 if (!table)22 return false;23 24 cpufreq_for_each_valid_entry(pos, table)25 if (pos->flags & CPUFREQ_BOOST_FREQ)26 return true;27 28 return false;29}30EXPORT_SYMBOL_GPL(policy_has_boost_freq);31 32int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,33 struct cpufreq_frequency_table *table)34{35 struct cpufreq_frequency_table *pos;36 unsigned int min_freq = ~0;37 unsigned int max_freq = 0;38 unsigned int freq;39 40 cpufreq_for_each_valid_entry(pos, table) {41 freq = pos->frequency;42 43 if ((!cpufreq_boost_enabled() || !policy->boost_enabled)44 && (pos->flags & CPUFREQ_BOOST_FREQ))45 continue;46 47 pr_debug("table entry %u: %u kHz\n", (int)(pos - table), freq);48 if (freq < min_freq)49 min_freq = freq;50 if (freq > max_freq)51 max_freq = freq;52 }53 54 policy->min = policy->cpuinfo.min_freq = min_freq;55 policy->max = max_freq;56 /*57 * If the driver has set its own cpuinfo.max_freq above max_freq, leave58 * it as is.59 */60 if (policy->cpuinfo.max_freq < max_freq)61 policy->max = policy->cpuinfo.max_freq = max_freq;62 63 if (policy->min == ~0)64 return -EINVAL;65 else66 return 0;67}68 69int cpufreq_frequency_table_verify(struct cpufreq_policy_data *policy,70 struct cpufreq_frequency_table *table)71{72 struct cpufreq_frequency_table *pos;73 unsigned int freq, prev_smaller = 0;74 bool found = false;75 76 pr_debug("request for verification of policy (%u - %u kHz) for cpu %u\n",77 policy->min, policy->max, policy->cpu);78 79 cpufreq_verify_within_cpu_limits(policy);80 81 cpufreq_for_each_valid_entry(pos, table) {82 freq = pos->frequency;83 84 if ((freq >= policy->min) && (freq <= policy->max)) {85 found = true;86 break;87 }88 89 if ((prev_smaller < freq) && (freq <= policy->max))90 prev_smaller = freq;91 }92 93 if (!found) {94 policy->max = prev_smaller;95 cpufreq_verify_within_cpu_limits(policy);96 }97 98 pr_debug("verification lead to (%u - %u kHz) for cpu %u\n",99 policy->min, policy->max, policy->cpu);100 101 return 0;102}103EXPORT_SYMBOL_GPL(cpufreq_frequency_table_verify);104 105/*106 * Generic routine to verify policy & frequency table, requires driver to set107 * policy->freq_table prior to it.108 */109int cpufreq_generic_frequency_table_verify(struct cpufreq_policy_data *policy)110{111 if (!policy->freq_table)112 return -ENODEV;113 114 return cpufreq_frequency_table_verify(policy, policy->freq_table);115}116EXPORT_SYMBOL_GPL(cpufreq_generic_frequency_table_verify);117 118int cpufreq_table_index_unsorted(struct cpufreq_policy *policy,119 unsigned int target_freq,120 unsigned int relation)121{122 struct cpufreq_frequency_table optimal = {123 .driver_data = ~0,124 .frequency = 0,125 };126 struct cpufreq_frequency_table suboptimal = {127 .driver_data = ~0,128 .frequency = 0,129 };130 struct cpufreq_frequency_table *pos;131 struct cpufreq_frequency_table *table = policy->freq_table;132 unsigned int freq, diff, i = 0;133 int index;134 135 pr_debug("request for target %u kHz (relation: %u) for cpu %u\n",136 target_freq, relation, policy->cpu);137 138 switch (relation) {139 case CPUFREQ_RELATION_H:140 suboptimal.frequency = ~0;141 break;142 case CPUFREQ_RELATION_L:143 case CPUFREQ_RELATION_C:144 optimal.frequency = ~0;145 break;146 }147 148 cpufreq_for_each_valid_entry_idx(pos, table, i) {149 freq = pos->frequency;150 151 if ((freq < policy->min) || (freq > policy->max))152 continue;153 if (freq == target_freq) {154 optimal.driver_data = i;155 break;156 }157 switch (relation) {158 case CPUFREQ_RELATION_H:159 if (freq < target_freq) {160 if (freq >= optimal.frequency) {161 optimal.frequency = freq;162 optimal.driver_data = i;163 }164 } else {165 if (freq <= suboptimal.frequency) {166 suboptimal.frequency = freq;167 suboptimal.driver_data = i;168 }169 }170 break;171 case CPUFREQ_RELATION_L:172 if (freq > target_freq) {173 if (freq <= optimal.frequency) {174 optimal.frequency = freq;175 optimal.driver_data = i;176 }177 } else {178 if (freq >= suboptimal.frequency) {179 suboptimal.frequency = freq;180 suboptimal.driver_data = i;181 }182 }183 break;184 case CPUFREQ_RELATION_C:185 diff = abs(freq - target_freq);186 if (diff < optimal.frequency ||187 (diff == optimal.frequency &&188 freq > table[optimal.driver_data].frequency)) {189 optimal.frequency = diff;190 optimal.driver_data = i;191 }192 break;193 }194 }195 if (optimal.driver_data > i) {196 if (suboptimal.driver_data > i) {197 WARN(1, "Invalid frequency table: %u\n", policy->cpu);198 return 0;199 }200 201 index = suboptimal.driver_data;202 } else203 index = optimal.driver_data;204 205 pr_debug("target index is %u, freq is:%u kHz\n", index,206 table[index].frequency);207 return index;208}209EXPORT_SYMBOL_GPL(cpufreq_table_index_unsorted);210 211int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy,212 unsigned int freq)213{214 struct cpufreq_frequency_table *pos, *table = policy->freq_table;215 int idx;216 217 if (unlikely(!table)) {218 pr_debug("%s: Unable to find frequency table\n", __func__);219 return -ENOENT;220 }221 222 cpufreq_for_each_valid_entry_idx(pos, table, idx)223 if (pos->frequency == freq)224 return idx;225 226 return -EINVAL;227}228EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_index);229 230/*231 * show_available_freqs - show available frequencies for the specified CPU232 */233static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf,234 bool show_boost)235{236 ssize_t count = 0;237 struct cpufreq_frequency_table *pos, *table = policy->freq_table;238 239 if (!table)240 return -ENODEV;241 242 cpufreq_for_each_valid_entry(pos, table) {243 /*244 * show_boost = true and driver_data = BOOST freq245 * display BOOST freqs246 *247 * show_boost = false and driver_data = BOOST freq248 * show_boost = true and driver_data != BOOST freq249 * continue - do not display anything250 *251 * show_boost = false and driver_data != BOOST freq252 * display NON BOOST freqs253 */254 if (show_boost ^ (pos->flags & CPUFREQ_BOOST_FREQ))255 continue;256 257 count += sprintf(&buf[count], "%u ", pos->frequency);258 }259 count += sprintf(&buf[count], "\n");260 261 return count;262 263}264 265#define cpufreq_attr_available_freq(_name) \266struct freq_attr cpufreq_freq_attr_##_name##_freqs = \267__ATTR_RO(_name##_frequencies)268 269/*270 * scaling_available_frequencies_show - show available normal frequencies for271 * the specified CPU272 */273static ssize_t scaling_available_frequencies_show(struct cpufreq_policy *policy,274 char *buf)275{276 return show_available_freqs(policy, buf, false);277}278cpufreq_attr_available_freq(scaling_available);279EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_available_freqs);280 281/*282 * scaling_boost_frequencies_show - show available boost frequencies for283 * the specified CPU284 */285static ssize_t scaling_boost_frequencies_show(struct cpufreq_policy *policy,286 char *buf)287{288 return show_available_freqs(policy, buf, true);289}290cpufreq_attr_available_freq(scaling_boost);291EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_boost_freqs);292 293struct freq_attr *cpufreq_generic_attr[] = {294 &cpufreq_freq_attr_scaling_available_freqs,295 NULL,296};297EXPORT_SYMBOL_GPL(cpufreq_generic_attr);298 299static int set_freq_table_sorted(struct cpufreq_policy *policy)300{301 struct cpufreq_frequency_table *pos, *table = policy->freq_table;302 struct cpufreq_frequency_table *prev = NULL;303 int ascending = 0;304 305 policy->freq_table_sorted = CPUFREQ_TABLE_UNSORTED;306 307 cpufreq_for_each_valid_entry(pos, table) {308 if (!prev) {309 prev = pos;310 continue;311 }312 313 if (pos->frequency == prev->frequency) {314 pr_warn("Duplicate freq-table entries: %u\n",315 pos->frequency);316 return -EINVAL;317 }318 319 /* Frequency increased from prev to pos */320 if (pos->frequency > prev->frequency) {321 /* But frequency was decreasing earlier */322 if (ascending < 0) {323 pr_debug("Freq table is unsorted\n");324 return 0;325 }326 327 ascending++;328 } else {329 /* Frequency decreased from prev to pos */330 331 /* But frequency was increasing earlier */332 if (ascending > 0) {333 pr_debug("Freq table is unsorted\n");334 return 0;335 }336 337 ascending--;338 }339 340 prev = pos;341 }342 343 if (ascending > 0)344 policy->freq_table_sorted = CPUFREQ_TABLE_SORTED_ASCENDING;345 else346 policy->freq_table_sorted = CPUFREQ_TABLE_SORTED_DESCENDING;347 348 pr_debug("Freq table is sorted in %s order\n",349 ascending > 0 ? "ascending" : "descending");350 351 return 0;352}353 354int cpufreq_table_validate_and_sort(struct cpufreq_policy *policy)355{356 int ret;357 358 if (!policy->freq_table) {359 /* Freq table must be passed by drivers with target_index() */360 if (has_target_index())361 return -EINVAL;362 363 return 0;364 }365 366 ret = cpufreq_frequency_table_cpuinfo(policy, policy->freq_table);367 if (ret)368 return ret;369 370 return set_freq_table_sorted(policy);371}372 373MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");374MODULE_DESCRIPTION("CPUfreq frequency table helpers");375