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