Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * linux/drivers/cpufreq/freq_table.c
0004  *
0005  * Copyright (C) 2002 - 2003 Dominik Brodowski
0006  */
0007 
0008 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0009 
0010 #include <linux/cpufreq.h>
0011 #include <linux/module.h>
0012 
0013 /*********************************************************************
0014  *                     FREQUENCY TABLE HELPERS                       *
0015  *********************************************************************/
0016 
0017 bool policy_has_boost_freq(struct cpufreq_policy *policy)
0018 {
0019     struct cpufreq_frequency_table *pos, *table = policy->freq_table;
0020 
0021     if (!table)
0022         return false;
0023 
0024     cpufreq_for_each_valid_entry(pos, table)
0025         if (pos->flags & CPUFREQ_BOOST_FREQ)
0026             return true;
0027 
0028     return false;
0029 }
0030 EXPORT_SYMBOL_GPL(policy_has_boost_freq);
0031 
0032 int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
0033                     struct cpufreq_frequency_table *table)
0034 {
0035     struct cpufreq_frequency_table *pos;
0036     unsigned int min_freq = ~0;
0037     unsigned int max_freq = 0;
0038     unsigned int freq;
0039 
0040     cpufreq_for_each_valid_entry(pos, table) {
0041         freq = pos->frequency;
0042 
0043         if (!cpufreq_boost_enabled()
0044             && (pos->flags & CPUFREQ_BOOST_FREQ))
0045             continue;
0046 
0047         pr_debug("table entry %u: %u kHz\n", (int)(pos - table), freq);
0048         if (freq < min_freq)
0049             min_freq = freq;
0050         if (freq > max_freq)
0051             max_freq = freq;
0052     }
0053 
0054     policy->min = policy->cpuinfo.min_freq = min_freq;
0055     policy->max = max_freq;
0056     /*
0057      * If the driver has set its own cpuinfo.max_freq above max_freq, leave
0058      * it as is.
0059      */
0060     if (policy->cpuinfo.max_freq < max_freq)
0061         policy->max = policy->cpuinfo.max_freq = max_freq;
0062 
0063     if (policy->min == ~0)
0064         return -EINVAL;
0065     else
0066         return 0;
0067 }
0068 
0069 int cpufreq_frequency_table_verify(struct cpufreq_policy_data *policy,
0070                    struct cpufreq_frequency_table *table)
0071 {
0072     struct cpufreq_frequency_table *pos;
0073     unsigned int freq, next_larger = ~0;
0074     bool found = false;
0075 
0076     pr_debug("request for verification of policy (%u - %u kHz) for cpu %u\n",
0077                     policy->min, policy->max, policy->cpu);
0078 
0079     cpufreq_verify_within_cpu_limits(policy);
0080 
0081     cpufreq_for_each_valid_entry(pos, table) {
0082         freq = pos->frequency;
0083 
0084         if ((freq >= policy->min) && (freq <= policy->max)) {
0085             found = true;
0086             break;
0087         }
0088 
0089         if ((next_larger > freq) && (freq > policy->max))
0090             next_larger = freq;
0091     }
0092 
0093     if (!found) {
0094         policy->max = next_larger;
0095         cpufreq_verify_within_cpu_limits(policy);
0096     }
0097 
0098     pr_debug("verification lead to (%u - %u kHz) for cpu %u\n",
0099                 policy->min, policy->max, policy->cpu);
0100 
0101     return 0;
0102 }
0103 EXPORT_SYMBOL_GPL(cpufreq_frequency_table_verify);
0104 
0105 /*
0106  * Generic routine to verify policy & frequency table, requires driver to set
0107  * policy->freq_table prior to it.
0108  */
0109 int cpufreq_generic_frequency_table_verify(struct cpufreq_policy_data *policy)
0110 {
0111     if (!policy->freq_table)
0112         return -ENODEV;
0113 
0114     return cpufreq_frequency_table_verify(policy, policy->freq_table);
0115 }
0116 EXPORT_SYMBOL_GPL(cpufreq_generic_frequency_table_verify);
0117 
0118 int cpufreq_table_index_unsorted(struct cpufreq_policy *policy,
0119                  unsigned int target_freq,
0120                  unsigned int relation)
0121 {
0122     struct cpufreq_frequency_table optimal = {
0123         .driver_data = ~0,
0124         .frequency = 0,
0125     };
0126     struct cpufreq_frequency_table suboptimal = {
0127         .driver_data = ~0,
0128         .frequency = 0,
0129     };
0130     struct cpufreq_frequency_table *pos;
0131     struct cpufreq_frequency_table *table = policy->freq_table;
0132     unsigned int freq, diff, i = 0;
0133     int index;
0134 
0135     pr_debug("request for target %u kHz (relation: %u) for cpu %u\n",
0136                     target_freq, relation, policy->cpu);
0137 
0138     switch (relation) {
0139     case CPUFREQ_RELATION_H:
0140         suboptimal.frequency = ~0;
0141         break;
0142     case CPUFREQ_RELATION_L:
0143     case CPUFREQ_RELATION_C:
0144         optimal.frequency = ~0;
0145         break;
0146     }
0147 
0148     cpufreq_for_each_valid_entry_idx(pos, table, i) {
0149         freq = pos->frequency;
0150 
0151         if ((freq < policy->min) || (freq > policy->max))
0152             continue;
0153         if (freq == target_freq) {
0154             optimal.driver_data = i;
0155             break;
0156         }
0157         switch (relation) {
0158         case CPUFREQ_RELATION_H:
0159             if (freq < target_freq) {
0160                 if (freq >= optimal.frequency) {
0161                     optimal.frequency = freq;
0162                     optimal.driver_data = i;
0163                 }
0164             } else {
0165                 if (freq <= suboptimal.frequency) {
0166                     suboptimal.frequency = freq;
0167                     suboptimal.driver_data = i;
0168                 }
0169             }
0170             break;
0171         case CPUFREQ_RELATION_L:
0172             if (freq > target_freq) {
0173                 if (freq <= optimal.frequency) {
0174                     optimal.frequency = freq;
0175                     optimal.driver_data = i;
0176                 }
0177             } else {
0178                 if (freq >= suboptimal.frequency) {
0179                     suboptimal.frequency = freq;
0180                     suboptimal.driver_data = i;
0181                 }
0182             }
0183             break;
0184         case CPUFREQ_RELATION_C:
0185             diff = abs(freq - target_freq);
0186             if (diff < optimal.frequency ||
0187                 (diff == optimal.frequency &&
0188                  freq > table[optimal.driver_data].frequency)) {
0189                 optimal.frequency = diff;
0190                 optimal.driver_data = i;
0191             }
0192             break;
0193         }
0194     }
0195     if (optimal.driver_data > i) {
0196         if (suboptimal.driver_data > i) {
0197             WARN(1, "Invalid frequency table: %d\n", policy->cpu);
0198             return 0;
0199         }
0200 
0201         index = suboptimal.driver_data;
0202     } else
0203         index = optimal.driver_data;
0204 
0205     pr_debug("target index is %u, freq is:%u kHz\n", index,
0206          table[index].frequency);
0207     return index;
0208 }
0209 EXPORT_SYMBOL_GPL(cpufreq_table_index_unsorted);
0210 
0211 int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy,
0212         unsigned int freq)
0213 {
0214     struct cpufreq_frequency_table *pos, *table = policy->freq_table;
0215     int idx;
0216 
0217     if (unlikely(!table)) {
0218         pr_debug("%s: Unable to find frequency table\n", __func__);
0219         return -ENOENT;
0220     }
0221 
0222     cpufreq_for_each_valid_entry_idx(pos, table, idx)
0223         if (pos->frequency == freq)
0224             return idx;
0225 
0226     return -EINVAL;
0227 }
0228 EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_index);
0229 
0230 /*
0231  * show_available_freqs - show available frequencies for the specified CPU
0232  */
0233 static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf,
0234                     bool show_boost)
0235 {
0236     ssize_t count = 0;
0237     struct cpufreq_frequency_table *pos, *table = policy->freq_table;
0238 
0239     if (!table)
0240         return -ENODEV;
0241 
0242     cpufreq_for_each_valid_entry(pos, table) {
0243         /*
0244          * show_boost = true and driver_data = BOOST freq
0245          * display BOOST freqs
0246          *
0247          * show_boost = false and driver_data = BOOST freq
0248          * show_boost = true and driver_data != BOOST freq
0249          * continue - do not display anything
0250          *
0251          * show_boost = false and driver_data != BOOST freq
0252          * display NON BOOST freqs
0253          */
0254         if (show_boost ^ (pos->flags & CPUFREQ_BOOST_FREQ))
0255             continue;
0256 
0257         count += sprintf(&buf[count], "%d ", pos->frequency);
0258     }
0259     count += sprintf(&buf[count], "\n");
0260 
0261     return count;
0262 
0263 }
0264 
0265 #define cpufreq_attr_available_freq(_name)    \
0266 struct freq_attr cpufreq_freq_attr_##_name##_freqs =     \
0267 __ATTR_RO(_name##_frequencies)
0268 
0269 /*
0270  * scaling_available_frequencies_show - show available normal frequencies for
0271  * the specified CPU
0272  */
0273 static ssize_t scaling_available_frequencies_show(struct cpufreq_policy *policy,
0274                           char *buf)
0275 {
0276     return show_available_freqs(policy, buf, false);
0277 }
0278 cpufreq_attr_available_freq(scaling_available);
0279 EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_available_freqs);
0280 
0281 /*
0282  * scaling_boost_frequencies_show - show available boost frequencies for
0283  * the specified CPU
0284  */
0285 static ssize_t scaling_boost_frequencies_show(struct cpufreq_policy *policy,
0286                           char *buf)
0287 {
0288     return show_available_freqs(policy, buf, true);
0289 }
0290 cpufreq_attr_available_freq(scaling_boost);
0291 EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_boost_freqs);
0292 
0293 struct freq_attr *cpufreq_generic_attr[] = {
0294     &cpufreq_freq_attr_scaling_available_freqs,
0295     NULL,
0296 };
0297 EXPORT_SYMBOL_GPL(cpufreq_generic_attr);
0298 
0299 static int set_freq_table_sorted(struct cpufreq_policy *policy)
0300 {
0301     struct cpufreq_frequency_table *pos, *table = policy->freq_table;
0302     struct cpufreq_frequency_table *prev = NULL;
0303     int ascending = 0;
0304 
0305     policy->freq_table_sorted = CPUFREQ_TABLE_UNSORTED;
0306 
0307     cpufreq_for_each_valid_entry(pos, table) {
0308         if (!prev) {
0309             prev = pos;
0310             continue;
0311         }
0312 
0313         if (pos->frequency == prev->frequency) {
0314             pr_warn("Duplicate freq-table entries: %u\n",
0315                 pos->frequency);
0316             return -EINVAL;
0317         }
0318 
0319         /* Frequency increased from prev to pos */
0320         if (pos->frequency > prev->frequency) {
0321             /* But frequency was decreasing earlier */
0322             if (ascending < 0) {
0323                 pr_debug("Freq table is unsorted\n");
0324                 return 0;
0325             }
0326 
0327             ascending++;
0328         } else {
0329             /* Frequency decreased from prev to pos */
0330 
0331             /* But frequency was increasing earlier */
0332             if (ascending > 0) {
0333                 pr_debug("Freq table is unsorted\n");
0334                 return 0;
0335             }
0336 
0337             ascending--;
0338         }
0339 
0340         prev = pos;
0341     }
0342 
0343     if (ascending > 0)
0344         policy->freq_table_sorted = CPUFREQ_TABLE_SORTED_ASCENDING;
0345     else
0346         policy->freq_table_sorted = CPUFREQ_TABLE_SORTED_DESCENDING;
0347 
0348     pr_debug("Freq table is sorted in %s order\n",
0349          ascending > 0 ? "ascending" : "descending");
0350 
0351     return 0;
0352 }
0353 
0354 int cpufreq_table_validate_and_sort(struct cpufreq_policy *policy)
0355 {
0356     int ret;
0357 
0358     if (!policy->freq_table)
0359         return 0;
0360 
0361     ret = cpufreq_frequency_table_cpuinfo(policy, policy->freq_table);
0362     if (ret)
0363         return ret;
0364 
0365     return set_freq_table_sorted(policy);
0366 }
0367 
0368 MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
0369 MODULE_DESCRIPTION("CPUfreq frequency table helpers");
0370 MODULE_LICENSE("GPL");