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