linux/drivers/cpufreq/freq_table.c
<<
>>
Prefs
   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
  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()
  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 = policy->cpuinfo.max_freq = max_freq;
  56
  57        if (policy->min == ~0)
  58                return -EINVAL;
  59        else
  60                return 0;
  61}
  62
  63int 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        cpufreq_for_each_valid_entry(pos, table) {
  76                freq = pos->frequency;
  77
  78                if ((freq >= policy->min) && (freq <= policy->max)) {
  79                        found = true;
  80                        break;
  81                }
  82
  83                if ((next_larger > freq) && (freq > policy->max))
  84                        next_larger = freq;
  85        }
  86
  87        if (!found) {
  88                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        return 0;
  96}
  97EXPORT_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 */
 103int cpufreq_generic_frequency_table_verify(struct cpufreq_policy_data *policy)
 104{
 105        if (!policy->freq_table)
 106                return -ENODEV;
 107
 108        return cpufreq_frequency_table_verify(policy, policy->freq_table);
 109}
 110EXPORT_SYMBOL_GPL(cpufreq_generic_frequency_table_verify);
 111
 112int 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        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        switch (relation) {
 133        case CPUFREQ_RELATION_H:
 134                suboptimal.frequency = ~0;
 135                break;
 136        case CPUFREQ_RELATION_L:
 137        case CPUFREQ_RELATION_C:
 138                optimal.frequency = ~0;
 139                break;
 140        }
 141
 142        cpufreq_for_each_valid_entry_idx(pos, table, i) {
 143                freq = pos->frequency;
 144
 145                if ((freq < policy->min) || (freq > policy->max))
 146                        continue;
 147                if (freq == target_freq) {
 148                        optimal.driver_data = i;
 149                        break;
 150                }
 151                switch (relation) {
 152                case CPUFREQ_RELATION_H:
 153                        if (freq < target_freq) {
 154                                if (freq >= optimal.frequency) {
 155                                        optimal.frequency = freq;
 156                                        optimal.driver_data = i;
 157                                }
 158                        } else {
 159                                if (freq <= suboptimal.frequency) {
 160                                        suboptimal.frequency = freq;
 161                                        suboptimal.driver_data = i;
 162                                }
 163                        }
 164                        break;
 165                case CPUFREQ_RELATION_L:
 166                        if (freq > target_freq) {
 167                                if (freq <= optimal.frequency) {
 168                                        optimal.frequency = freq;
 169                                        optimal.driver_data = i;
 170                                }
 171                        } else {
 172                                if (freq >= suboptimal.frequency) {
 173                                        suboptimal.frequency = freq;
 174                                        suboptimal.driver_data = i;
 175                                }
 176                        }
 177                        break;
 178                case CPUFREQ_RELATION_C:
 179                        diff = abs(freq - target_freq);
 180                        if (diff < optimal.frequency ||
 181                            (diff == optimal.frequency &&
 182                             freq > table[optimal.driver_data].frequency)) {
 183                                optimal.frequency = diff;
 184                                optimal.driver_data = i;
 185                        }
 186                        break;
 187                }
 188        }
 189        if (optimal.driver_data > i) {
 190                if (suboptimal.driver_data > i) {
 191                        WARN(1, "Invalid frequency table: %d\n", policy->cpu);
 192                        return 0;
 193                }
 194
 195                index = suboptimal.driver_data;
 196        } else
 197                index = optimal.driver_data;
 198
 199        pr_debug("target index is %u, freq is:%u kHz\n", index,
 200                 table[index].frequency);
 201        return index;
 202}
 203EXPORT_SYMBOL_GPL(cpufreq_table_index_unsorted);
 204
 205int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy,
 206                unsigned int freq)
 207{
 208        struct cpufreq_frequency_table *pos, *table = policy->freq_table;
 209        int idx;
 210
 211        if (unlikely(!table)) {
 212                pr_debug("%s: Unable to find frequency table\n", __func__);
 213                return -ENOENT;
 214        }
 215
 216        cpufreq_for_each_valid_entry_idx(pos, table, idx)
 217                if (pos->frequency == freq)
 218                        return idx;
 219
 220        return -EINVAL;
 221}
 222EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_index);
 223
 224/*
 225 * show_available_freqs - show available frequencies for the specified CPU
 226 */
 227static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf,
 228                                    bool show_boost)
 229{
 230        ssize_t count = 0;
 231        struct cpufreq_frequency_table *pos, *table = policy->freq_table;
 232
 233        if (!table)
 234                return -ENODEV;
 235
 236        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                if (show_boost ^ (pos->flags & CPUFREQ_BOOST_FREQ))
 249                        continue;
 250
 251                count += sprintf(&buf[count], "%d ", pos->frequency);
 252        }
 253        count += sprintf(&buf[count], "\n");
 254
 255        return count;
 256
 257}
 258
 259#define cpufreq_attr_available_freq(_name)        \
 260struct 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 */
 267static ssize_t scaling_available_frequencies_show(struct cpufreq_policy *policy,
 268                                                  char *buf)
 269{
 270        return show_available_freqs(policy, buf, false);
 271}
 272cpufreq_attr_available_freq(scaling_available);
 273EXPORT_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 */
 279static ssize_t scaling_boost_frequencies_show(struct cpufreq_policy *policy,
 280                                              char *buf)
 281{
 282        return show_available_freqs(policy, buf, true);
 283}
 284cpufreq_attr_available_freq(scaling_boost);
 285EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_boost_freqs);
 286
 287struct freq_attr *cpufreq_generic_attr[] = {
 288        &cpufreq_freq_attr_scaling_available_freqs,
 289        NULL,
 290};
 291EXPORT_SYMBOL_GPL(cpufreq_generic_attr);
 292
 293static int set_freq_table_sorted(struct cpufreq_policy *policy)
 294{
 295        struct cpufreq_frequency_table *pos, *table = policy->freq_table;
 296        struct cpufreq_frequency_table *prev = NULL;
 297        int ascending = 0;
 298
 299        policy->freq_table_sorted = CPUFREQ_TABLE_UNSORTED;
 300
 301        cpufreq_for_each_valid_entry(pos, table) {
 302                if (!prev) {
 303                        prev = pos;
 304                        continue;
 305                }
 306
 307                if (pos->frequency == prev->frequency) {
 308                        pr_warn("Duplicate freq-table entries: %u\n",
 309                                pos->frequency);
 310                        return -EINVAL;
 311                }
 312
 313                /* Frequency increased from prev to pos */
 314                if (pos->frequency > prev->frequency) {
 315                        /* But frequency was decreasing earlier */
 316                        if (ascending < 0) {
 317                                pr_debug("Freq table is unsorted\n");
 318                                return 0;
 319                        }
 320
 321                        ascending++;
 322                } else {
 323                        /* Frequency decreased from prev to pos */
 324
 325                        /* But frequency was increasing earlier */
 326                        if (ascending > 0) {
 327                                pr_debug("Freq table is unsorted\n");
 328                                return 0;
 329                        }
 330
 331                        ascending--;
 332                }
 333
 334                prev = pos;
 335        }
 336
 337        if (ascending > 0)
 338                policy->freq_table_sorted = CPUFREQ_TABLE_SORTED_ASCENDING;
 339        else
 340                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
 348int cpufreq_table_validate_and_sort(struct cpufreq_policy *policy)
 349{
 350        int ret;
 351
 352        if (!policy->freq_table)
 353                return 0;
 354
 355        ret = cpufreq_frequency_table_cpuinfo(policy, policy->freq_table);
 356        if (ret)
 357                return ret;
 358
 359        return set_freq_table_sorted(policy);
 360}
 361
 362MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
 363MODULE_DESCRIPTION("CPUfreq frequency table helpers");
 364MODULE_LICENSE("GPL");
 365