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