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 #include <trace/hooks/cpufreq.h>
13
14 /*********************************************************************
15 * FREQUENCY TABLE HELPERS *
16 *********************************************************************/
17
policy_has_boost_freq(struct cpufreq_policy * policy)18 bool policy_has_boost_freq(struct cpufreq_policy *policy)
19 {
20 struct cpufreq_frequency_table *pos, *table = policy->freq_table;
21
22 if (!table)
23 return false;
24
25 cpufreq_for_each_valid_entry(pos, table)
26 if (pos->flags & CPUFREQ_BOOST_FREQ)
27 return true;
28
29 return false;
30 }
31 EXPORT_SYMBOL_GPL(policy_has_boost_freq);
32
cpufreq_frequency_table_cpuinfo(struct cpufreq_policy * policy,struct cpufreq_frequency_table * table)33 int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
34 struct cpufreq_frequency_table *table)
35 {
36 struct cpufreq_frequency_table *pos;
37 unsigned int min_freq = ~0;
38 unsigned int max_freq = 0;
39 unsigned int freq;
40
41 cpufreq_for_each_valid_entry(pos, table) {
42 freq = pos->frequency;
43
44 if ((!cpufreq_boost_enabled() || !policy->boost_enabled)
45 && (pos->flags & CPUFREQ_BOOST_FREQ))
46 continue;
47
48 pr_debug("table entry %u: %u kHz\n", (int)(pos - table), freq);
49 if (freq < min_freq)
50 min_freq = freq;
51 if (freq > max_freq)
52 max_freq = freq;
53 }
54
55 policy->min = policy->cpuinfo.min_freq = min_freq;
56 policy->max = max_freq;
57 /*
58 * If the driver has set its own cpuinfo.max_freq above max_freq, leave
59 * it as is.
60 */
61 if (policy->cpuinfo.max_freq < max_freq)
62 policy->max = policy->cpuinfo.max_freq = max_freq;
63
64 if (policy->min == ~0)
65 return -EINVAL;
66 else
67 return 0;
68 }
69
cpufreq_frequency_table_verify(struct cpufreq_policy_data * policy,struct cpufreq_frequency_table * table)70 int cpufreq_frequency_table_verify(struct cpufreq_policy_data *policy,
71 struct cpufreq_frequency_table *table)
72 {
73 struct cpufreq_frequency_table *pos;
74 unsigned int freq, prev_smaller = 0;
75 bool found = false;
76
77 pr_debug("request for verification of policy (%u - %u kHz) for cpu %u\n",
78 policy->min, policy->max, policy->cpu);
79
80 cpufreq_verify_within_cpu_limits(policy);
81
82 cpufreq_for_each_valid_entry(pos, table) {
83 freq = pos->frequency;
84
85 if ((freq >= policy->min) && (freq <= policy->max)) {
86 found = true;
87 break;
88 }
89
90 if ((prev_smaller < freq) && (freq <= policy->max))
91 prev_smaller = freq;
92 }
93
94 if (!found) {
95 policy->max = prev_smaller;
96 cpufreq_verify_within_cpu_limits(policy);
97 }
98
99 pr_debug("verification lead to (%u - %u kHz) for cpu %u\n",
100 policy->min, policy->max, policy->cpu);
101
102 return 0;
103 }
104 EXPORT_SYMBOL_GPL(cpufreq_frequency_table_verify);
105
106 /*
107 * Generic routine to verify policy & frequency table, requires driver to set
108 * policy->freq_table prior to it.
109 */
cpufreq_generic_frequency_table_verify(struct cpufreq_policy_data * policy)110 int cpufreq_generic_frequency_table_verify(struct cpufreq_policy_data *policy)
111 {
112 if (!policy->freq_table)
113 return -ENODEV;
114
115 return cpufreq_frequency_table_verify(policy, policy->freq_table);
116 }
117 EXPORT_SYMBOL_GPL(cpufreq_generic_frequency_table_verify);
118
cpufreq_table_index_unsorted(struct cpufreq_policy * policy,unsigned int target_freq,unsigned int relation)119 int cpufreq_table_index_unsorted(struct cpufreq_policy *policy,
120 unsigned int target_freq,
121 unsigned int relation)
122 {
123 struct cpufreq_frequency_table optimal = {
124 .driver_data = ~0,
125 .frequency = 0,
126 };
127 struct cpufreq_frequency_table suboptimal = {
128 .driver_data = ~0,
129 .frequency = 0,
130 };
131 struct cpufreq_frequency_table *pos;
132 struct cpufreq_frequency_table *table = policy->freq_table;
133 unsigned int freq, diff, i = 0;
134 int index;
135
136 pr_debug("request for target %u kHz (relation: %u) for cpu %u\n",
137 target_freq, relation, policy->cpu);
138
139 switch (relation) {
140 case CPUFREQ_RELATION_H:
141 suboptimal.frequency = ~0;
142 break;
143 case CPUFREQ_RELATION_L:
144 case CPUFREQ_RELATION_C:
145 optimal.frequency = ~0;
146 break;
147 }
148
149 cpufreq_for_each_valid_entry_idx(pos, table, i) {
150 freq = pos->frequency;
151
152 if ((freq < policy->min) || (freq > policy->max))
153 continue;
154 if (freq == target_freq) {
155 optimal.driver_data = i;
156 break;
157 }
158 switch (relation) {
159 case CPUFREQ_RELATION_H:
160 if (freq < target_freq) {
161 if (freq >= optimal.frequency) {
162 optimal.frequency = freq;
163 optimal.driver_data = i;
164 }
165 } else {
166 if (freq <= suboptimal.frequency) {
167 suboptimal.frequency = freq;
168 suboptimal.driver_data = i;
169 }
170 }
171 break;
172 case CPUFREQ_RELATION_L:
173 if (freq > target_freq) {
174 if (freq <= optimal.frequency) {
175 optimal.frequency = freq;
176 optimal.driver_data = i;
177 }
178 } else {
179 if (freq >= suboptimal.frequency) {
180 suboptimal.frequency = freq;
181 suboptimal.driver_data = i;
182 }
183 }
184 break;
185 case CPUFREQ_RELATION_C:
186 diff = abs(freq - target_freq);
187 if (diff < optimal.frequency ||
188 (diff == optimal.frequency &&
189 freq > table[optimal.driver_data].frequency)) {
190 optimal.frequency = diff;
191 optimal.driver_data = i;
192 }
193 break;
194 }
195 }
196 if (optimal.driver_data > i) {
197 if (suboptimal.driver_data > i) {
198 WARN(1, "Invalid frequency table: %u\n", policy->cpu);
199 return 0;
200 }
201
202 index = suboptimal.driver_data;
203 } else
204 index = optimal.driver_data;
205
206 pr_debug("target index is %u, freq is:%u kHz\n", index,
207 table[index].frequency);
208 return index;
209 }
210 EXPORT_SYMBOL_GPL(cpufreq_table_index_unsorted);
211
cpufreq_frequency_table_get_index(struct cpufreq_policy * policy,unsigned int freq)212 int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy,
213 unsigned int freq)
214 {
215 struct cpufreq_frequency_table *pos, *table = policy->freq_table;
216 int idx;
217
218 if (unlikely(!table)) {
219 pr_debug("%s: Unable to find frequency table\n", __func__);
220 return -ENOENT;
221 }
222
223 cpufreq_for_each_valid_entry_idx(pos, table, idx)
224 if (pos->frequency == freq)
225 return idx;
226
227 return -EINVAL;
228 }
229 EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_index);
230
231 /*
232 * show_available_freqs - show available frequencies for the specified CPU
233 */
show_available_freqs(struct cpufreq_policy * policy,char * buf,bool show_boost)234 static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf,
235 bool show_boost)
236 {
237 ssize_t count = 0;
238 struct cpufreq_frequency_table *pos, *table = policy->freq_table;
239
240 if (!table)
241 return -ENODEV;
242
243 cpufreq_for_each_valid_entry(pos, table) {
244 /*
245 * show_boost = true and driver_data = BOOST freq
246 * display BOOST freqs
247 *
248 * show_boost = false and driver_data = BOOST freq
249 * show_boost = true and driver_data != BOOST freq
250 * continue - do not display anything
251 *
252 * show_boost = false and driver_data != BOOST freq
253 * display NON BOOST freqs
254 */
255 if (show_boost ^ (pos->flags & CPUFREQ_BOOST_FREQ))
256 continue;
257
258 count += sprintf(&buf[count], "%u ", pos->frequency);
259 }
260 count += sprintf(&buf[count], "\n");
261
262 return count;
263
264 }
265
266 #define cpufreq_attr_available_freq(_name) \
267 struct freq_attr cpufreq_freq_attr_##_name##_freqs = \
268 __ATTR_RO(_name##_frequencies)
269
270 /*
271 * scaling_available_frequencies_show - show available normal frequencies for
272 * the specified CPU
273 */
scaling_available_frequencies_show(struct cpufreq_policy * policy,char * buf)274 static ssize_t scaling_available_frequencies_show(struct cpufreq_policy *policy,
275 char *buf)
276 {
277 return show_available_freqs(policy, buf, false);
278 }
279 cpufreq_attr_available_freq(scaling_available);
280 EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_available_freqs);
281
282 /*
283 * scaling_boost_frequencies_show - show available boost frequencies for
284 * the specified CPU
285 */
scaling_boost_frequencies_show(struct cpufreq_policy * policy,char * buf)286 static ssize_t scaling_boost_frequencies_show(struct cpufreq_policy *policy,
287 char *buf)
288 {
289 return show_available_freqs(policy, buf, true);
290 }
291 cpufreq_attr_available_freq(scaling_boost);
292 EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_boost_freqs);
293
294 struct freq_attr *cpufreq_generic_attr[] = {
295 &cpufreq_freq_attr_scaling_available_freqs,
296 NULL,
297 };
298 EXPORT_SYMBOL_GPL(cpufreq_generic_attr);
299
set_freq_table_sorted(struct cpufreq_policy * policy)300 static 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
cpufreq_table_validate_and_sort(struct cpufreq_policy * policy)355 int cpufreq_table_validate_and_sort(struct cpufreq_policy *policy)
356 {
357 int ret;
358
359 if (!policy->freq_table) {
360 /* Freq table must be passed by drivers with target_index() */
361 if (has_target_index())
362 return -EINVAL;
363
364 return 0;
365 }
366
367 ret = cpufreq_frequency_table_cpuinfo(policy, policy->freq_table);
368 if (ret)
369 return ret;
370
371 return set_freq_table_sorted(policy);
372 }
373
374 MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
375 MODULE_DESCRIPTION("CPUfreq frequency table helpers");
376