projects/linux·blob master · refs

370 lines · 8.8 KiB

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