blob: cf99d97cc68f5b3b1c3d036972c9afd0c2dde0c1 [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001// 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
13static DEFINE_PER_CPU(struct cpufreq_frequency_table *, cpufreq_show_table);
14
15/*********************************************************************
16 * FREQUENCY TABLE HELPERS *
17 *********************************************************************/
18
19bool policy_has_boost_freq(struct cpufreq_policy *policy)
20{
21 struct cpufreq_frequency_table *pos, *table = policy->freq_table;
22
23 if (!table)
24 return false;
25
26 cpufreq_for_each_valid_entry(pos, table)
27 if (pos->flags & CPUFREQ_BOOST_FREQ)
28 return true;
29
30 return false;
31}
32EXPORT_SYMBOL_GPL(policy_has_boost_freq);
33
34int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
35 struct cpufreq_frequency_table *table)
36{
37 struct cpufreq_frequency_table *pos;
38 unsigned int min_freq = ~0;
39 unsigned int max_freq = 0;
40 unsigned int freq;
41
42 cpufreq_for_each_valid_entry(pos, table) {
43 freq = pos->frequency;
44
45 if (!cpufreq_boost_enabled()
46 && (pos->flags & CPUFREQ_BOOST_FREQ))
47 continue;
48
49 pr_debug("table entry %u: %u kHz\n", (int)(pos - table), freq);
50 if (freq < min_freq)
51 min_freq = freq;
52 if (freq > max_freq)
53 max_freq = freq;
54 }
55
56 policy->min = policy->cpuinfo.min_freq = min_freq;
57 policy->max = policy->cpuinfo.max_freq = max_freq;
58
59 if (policy->min == ~0)
60 return -EINVAL;
61 else
62 return 0;
63}
64
65int cpufreq_frequency_table_verify(struct cpufreq_policy_data *policy,
66 struct cpufreq_frequency_table *table)
67{
68 struct cpufreq_frequency_table *pos;
69 unsigned int freq, next_larger = ~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 ((next_larger > freq) && (freq > policy->max))
86 next_larger = freq;
87 }
88
89 if (!found) {
90 policy->max = next_larger;
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, policy->freq_table);
111}
112EXPORT_SYMBOL_GPL(cpufreq_generic_frequency_table_verify);
113
114int cpufreq_table_index_unsorted(struct cpufreq_policy *policy,
115 unsigned int target_freq,
116 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 = 0;
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 < policy->min) || (freq > policy->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: %d\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], "%d ", 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 * show_scaling_available_frequencies - 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);
275EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_available_freqs);
276
277/**
278 * show_available_boost_freqs - show available boost frequencies for
279 * the specified CPU
280 */
281static ssize_t scaling_boost_frequencies_show(struct cpufreq_policy *policy,
282 char *buf)
283{
284 return show_available_freqs(policy, buf, true);
285}
286cpufreq_attr_available_freq(scaling_boost);
287EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_boost_freqs);
288
289struct freq_attr *cpufreq_generic_attr[] = {
290 &cpufreq_freq_attr_scaling_available_freqs,
291 NULL,
292};
293EXPORT_SYMBOL_GPL(cpufreq_generic_attr);
294
295static int set_freq_table_sorted(struct cpufreq_policy *policy)
296{
297 struct cpufreq_frequency_table *pos, *table = policy->freq_table;
298 struct cpufreq_frequency_table *prev = NULL;
299 int ascending = 0;
300
301 policy->freq_table_sorted = CPUFREQ_TABLE_UNSORTED;
302
303 cpufreq_for_each_valid_entry(pos, table) {
304 if (!prev) {
305 prev = pos;
306 continue;
307 }
308
309 if (pos->frequency == prev->frequency) {
310 pr_warn("Duplicate freq-table entries: %u\n",
311 pos->frequency);
312 return -EINVAL;
313 }
314
315 /* Frequency increased from prev to pos */
316 if (pos->frequency > prev->frequency) {
317 /* But frequency was decreasing earlier */
318 if (ascending < 0) {
319 pr_debug("Freq table is unsorted\n");
320 return 0;
321 }
322
323 ascending++;
324 } else {
325 /* Frequency decreased from prev to pos */
326
327 /* But frequency was increasing earlier */
328 if (ascending > 0) {
329 pr_debug("Freq table is unsorted\n");
330 return 0;
331 }
332
333 ascending--;
334 }
335
336 prev = pos;
337 }
338
339 if (ascending > 0)
340 policy->freq_table_sorted = CPUFREQ_TABLE_SORTED_ASCENDING;
341 else
342 policy->freq_table_sorted = CPUFREQ_TABLE_SORTED_DESCENDING;
343
344 pr_debug("Freq table is sorted in %s order\n",
345 ascending > 0 ? "ascending" : "descending");
346
347 return 0;
348}
349
350int cpufreq_table_validate_and_sort(struct cpufreq_policy *policy)
351{
352 int ret;
353
354 if (!policy->freq_table)
355 return 0;
356
357 ret = cpufreq_frequency_table_cpuinfo(policy, policy->freq_table);
358 if (ret)
359 return ret;
360
361 return set_freq_table_sorted(policy);
362}
363
364/*
365 * if you use these, you must assure that the frequency table is valid
366 * all the time between get_attr and put_attr!
367 */
368void cpufreq_frequency_table_get_attr(struct cpufreq_frequency_table *table,
369 unsigned int cpu)
370{
371 pr_debug("setting show_table for cpu %u to %p\n", cpu, table);
372 per_cpu(cpufreq_show_table, cpu) = table;
373}
374EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_attr);
375
376struct cpufreq_frequency_table *cpufreq_frequency_get_table(unsigned int cpu)
377{
378 return per_cpu(cpufreq_show_table, cpu);
379}
380EXPORT_SYMBOL_GPL(cpufreq_frequency_get_table);
381
382MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
383MODULE_DESCRIPTION("CPUfreq frequency table helpers");
384MODULE_LICENSE("GPL");