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 = max_freq;
  56        /*
  57         * If the driver has set its own cpuinfo.max_freq above max_freq, leave
  58         * 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        else
  66                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, next_larger = ~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 ((next_larger > freq) && (freq > policy->max))
  90                        next_larger = freq;
  91        }
  92
  93        if (!found) {
  94                policy->max = next_larger;
  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 set
 107 * 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: %d\n", policy->cpu);
 198                        return 0;
 199                }
 200
 201                index = suboptimal.driver_data;
 202        } else
 203                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 CPU
 232 */
 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 freq
 245                 * display BOOST freqs
 246                 *
 247                 * show_boost = false and driver_data = BOOST freq
 248                 * show_boost = true and driver_data != BOOST freq
 249                 * continue - do not display anything
 250                 *
 251                 * show_boost = false and driver_data != BOOST freq
 252                 * display NON BOOST freqs
 253                 */
 254                if (show_boost ^ (pos->flags & CPUFREQ_BOOST_FREQ))
 255                        continue;
 256
 257                count += sprintf(&buf[count], "%d ", 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 for
 271 * the specified CPU
 272 */
 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 for
 283 * the specified CPU
 284 */
 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        else
 346                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                return 0;
 360
 361        ret = cpufreq_frequency_table_cpuinfo(policy, policy->freq_table);
 362        if (ret)
 363                return ret;
 364
 365        return set_freq_table_sorted(policy);
 366}
 367
 368MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
 369MODULE_DESCRIPTION("CPUfreq frequency table helpers");
 370MODULE_LICENSE("GPL");
 371