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