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