Branch data Line data Source code
1 : : /*
2 : : * linux/drivers/cpufreq/freq_table.c
3 : : *
4 : : * Copyright (C) 2002 - 2003 Dominik Brodowski
5 : : *
6 : : * This program is free software; you can redistribute it and/or modify
7 : : * it under the terms of the GNU General Public License version 2 as
8 : : * published by the Free Software Foundation.
9 : : *
10 : : */
11 : :
12 : : #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13 : :
14 : : #include <linux/cpufreq.h>
15 : : #include <linux/module.h>
16 : :
17 : : /*********************************************************************
18 : : * FREQUENCY TABLE HELPERS *
19 : : *********************************************************************/
20 : :
21 : 0 : int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
22 : : struct cpufreq_frequency_table *table)
23 : : {
24 : : unsigned int min_freq = ~0;
25 : : unsigned int max_freq = 0;
26 : : unsigned int i;
27 : :
28 [ + + ]: 27 : for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
29 : : unsigned int freq = table[i].frequency;
30 [ + + ]: 24 : if (freq == CPUFREQ_ENTRY_INVALID) {
31 : : pr_debug("table entry %u is invalid, skipping\n", i);
32 : :
33 : 3 : continue;
34 : : }
35 [ + - ]: 21 : if (!cpufreq_boost_enabled()
36 [ - + ]: 24 : && table[i].driver_data == CPUFREQ_BOOST_FREQ)
37 : 0 : continue;
38 : :
39 : : pr_debug("table entry %u: %u kHz, %u driver_data\n",
40 : : i, freq, table[i].driver_data);
41 [ + + ]: 24 : if (freq < min_freq)
42 : : min_freq = freq;
43 [ + - ]: 24 : if (freq > max_freq)
44 : : max_freq = freq;
45 : : }
46 : :
47 : 3 : policy->min = policy->cpuinfo.min_freq = min_freq;
48 : 3 : policy->max = policy->cpuinfo.max_freq = max_freq;
49 : :
50 [ + - ]: 3 : if (policy->min == ~0)
51 : : return -EINVAL;
52 : : else
53 : 3 : return 0;
54 : : }
55 : : EXPORT_SYMBOL_GPL(cpufreq_frequency_table_cpuinfo);
56 : :
57 : :
58 : 0 : int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
59 : : struct cpufreq_frequency_table *table)
60 : : {
61 : : unsigned int next_larger = ~0, freq, i = 0;
62 : : bool found = false;
63 : :
64 : : pr_debug("request for verification of policy (%u - %u kHz) for cpu %u\n",
65 : : policy->min, policy->max, policy->cpu);
66 : :
67 : : cpufreq_verify_within_cpu_limits(policy);
68 : :
69 [ + - ]: 162 : for (; freq = table[i].frequency, freq != CPUFREQ_TABLE_END; i++) {
70 [ - + ]: 162 : if (freq == CPUFREQ_ENTRY_INVALID)
71 : 0 : continue;
72 [ + - ][ - + ]: 162 : if ((freq >= policy->min) && (freq <= policy->max)) {
73 : : found = true;
74 : : break;
75 : : }
76 : :
77 [ # # ][ # # ]: 0 : if ((next_larger > freq) && (freq > policy->max))
78 : : next_larger = freq;
79 : : }
80 : :
81 [ - + ]: 162 : if (!found) {
82 : 0 : policy->max = next_larger;
83 : : cpufreq_verify_within_cpu_limits(policy);
84 : : }
85 : :
86 : : pr_debug("verification lead to (%u - %u kHz) for cpu %u\n",
87 : : policy->min, policy->max, policy->cpu);
88 : :
89 : 0 : return 0;
90 : : }
91 : : EXPORT_SYMBOL_GPL(cpufreq_frequency_table_verify);
92 : :
93 : : /*
94 : : * Generic routine to verify policy & frequency table, requires driver to call
95 : : * cpufreq_frequency_table_get_attr() prior to it.
96 : : */
97 : 0 : int cpufreq_generic_frequency_table_verify(struct cpufreq_policy *policy)
98 : : {
99 : : struct cpufreq_frequency_table *table =
100 : 162 : cpufreq_frequency_get_table(policy->cpu);
101 [ + - ]: 162 : if (!table)
102 : : return -ENODEV;
103 : :
104 : 162 : return cpufreq_frequency_table_verify(policy, table);
105 : : }
106 : : EXPORT_SYMBOL_GPL(cpufreq_generic_frequency_table_verify);
107 : :
108 : 0 : int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
109 : : struct cpufreq_frequency_table *table,
110 : : unsigned int target_freq,
111 : : unsigned int relation,
112 : : unsigned int *index)
113 : : {
114 : : struct cpufreq_frequency_table optimal = {
115 : : .driver_data = ~0,
116 : : .frequency = 0,
117 : : };
118 : : struct cpufreq_frequency_table suboptimal = {
119 : : .driver_data = ~0,
120 : : .frequency = 0,
121 : : };
122 : : unsigned int i;
123 : :
124 : : pr_debug("request for target %u kHz (relation: %u) for cpu %u\n",
125 : : target_freq, relation, policy->cpu);
126 : :
127 [ + + - ]: 2768 : switch (relation) {
128 : : case CPUFREQ_RELATION_H:
129 : : suboptimal.frequency = ~0;
130 : 145 : break;
131 : : case CPUFREQ_RELATION_L:
132 : : optimal.frequency = ~0;
133 : 2768 : break;
134 : : }
135 : :
136 [ + + ]: 24936 : for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
137 : : unsigned int freq = table[i].frequency;
138 [ - + ]: 22168 : if (freq == CPUFREQ_ENTRY_INVALID)
139 : 0 : continue;
140 [ + - ][ - + ]: 22168 : if ((freq < policy->min) || (freq > policy->max))
141 : 0 : continue;
142 [ + + - ]: 22168 : switch (relation) {
143 : : case CPUFREQ_RELATION_H:
144 [ + - ]: 1160 : if (freq <= target_freq) {
145 [ + - ]: 1160 : if (freq >= optimal.frequency) {
146 : : optimal.frequency = freq;
147 : : optimal.driver_data = i;
148 : : }
149 : : } else {
150 [ # # ]: 0 : if (freq <= suboptimal.frequency) {
151 : : suboptimal.frequency = freq;
152 : : suboptimal.driver_data = i;
153 : : }
154 : : }
155 : : break;
156 : : case CPUFREQ_RELATION_L:
157 [ + + ]: 21008 : if (freq >= target_freq) {
158 [ + + ]: 14964 : if (freq <= optimal.frequency) {
159 : : optimal.frequency = freq;
160 : : optimal.driver_data = i;
161 : : }
162 : : } else {
163 [ + - ]: 6044 : if (freq >= suboptimal.frequency) {
164 : : suboptimal.frequency = freq;
165 : : suboptimal.driver_data = i;
166 : : }
167 : : }
168 : : break;
169 : : }
170 : : }
171 [ - + ]: 2768 : if (optimal.driver_data > i) {
172 [ # # ]: 0 : if (suboptimal.driver_data > i)
173 : : return -EINVAL;
174 : 0 : *index = suboptimal.driver_data;
175 : : } else
176 : 2768 : *index = optimal.driver_data;
177 : :
178 : : pr_debug("target is %u (%u kHz, %u)\n", *index, table[*index].frequency,
179 : : table[*index].driver_data);
180 : :
181 : : return 0;
182 : : }
183 : : EXPORT_SYMBOL_GPL(cpufreq_frequency_table_target);
184 : :
185 : 0 : int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy,
186 : : unsigned int freq)
187 : : {
188 : : struct cpufreq_frequency_table *table;
189 : : int i;
190 : :
191 : 3 : table = cpufreq_frequency_get_table(policy->cpu);
192 [ + - ]: 3 : if (unlikely(!table)) {
193 : : pr_debug("%s: Unable to find frequency table\n", __func__);
194 : : return -ENOENT;
195 : : }
196 : :
197 [ + - ]: 12 : for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
198 [ + + ]: 12 : if (table[i].frequency == freq)
199 : : return i;
200 : : }
201 : :
202 : : return -EINVAL;
203 : : }
204 : : EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_index);
205 : :
206 : : static DEFINE_PER_CPU(struct cpufreq_frequency_table *, cpufreq_show_table);
207 : :
208 : : /**
209 : : * show_available_freqs - show available frequencies for the specified CPU
210 : : */
211 : 0 : static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf,
212 : : bool show_boost)
213 : : {
214 : : unsigned int i = 0;
215 : 0 : unsigned int cpu = policy->cpu;
216 : : ssize_t count = 0;
217 : : struct cpufreq_frequency_table *table;
218 : :
219 [ # # ]: 0 : if (!per_cpu(cpufreq_show_table, cpu))
220 : : return -ENODEV;
221 : :
222 : 0 : table = per_cpu(cpufreq_show_table, cpu);
223 : :
224 [ # # ]: 0 : for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
225 [ # # ]: 0 : if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
226 : 0 : continue;
227 : : /*
228 : : * show_boost = true and driver_data = BOOST freq
229 : : * display BOOST freqs
230 : : *
231 : : * show_boost = false and driver_data = BOOST freq
232 : : * show_boost = true and driver_data != BOOST freq
233 : : * continue - do not display anything
234 : : *
235 : : * show_boost = false and driver_data != BOOST freq
236 : : * display NON BOOST freqs
237 : : */
238 [ # # ]: 0 : if (show_boost ^ (table[i].driver_data == CPUFREQ_BOOST_FREQ))
239 : 0 : continue;
240 : :
241 : 0 : count += sprintf(&buf[count], "%d ", table[i].frequency);
242 : : }
243 : 0 : count += sprintf(&buf[count], "\n");
244 : :
245 : : return count;
246 : :
247 : : }
248 : :
249 : : #define cpufreq_attr_available_freq(_name) \
250 : : struct freq_attr cpufreq_freq_attr_##_name##_freqs = \
251 : : __ATTR_RO(_name##_frequencies)
252 : :
253 : : /**
254 : : * show_scaling_available_frequencies - show available normal frequencies for
255 : : * the specified CPU
256 : : */
257 : 0 : static ssize_t scaling_available_frequencies_show(struct cpufreq_policy *policy,
258 : : char *buf)
259 : : {
260 : 0 : return show_available_freqs(policy, buf, false);
261 : : }
262 : : cpufreq_attr_available_freq(scaling_available);
263 : : EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_available_freqs);
264 : :
265 : : /**
266 : : * show_available_boost_freqs - show available boost frequencies for
267 : : * the specified CPU
268 : : */
269 : 0 : static ssize_t scaling_boost_frequencies_show(struct cpufreq_policy *policy,
270 : : char *buf)
271 : : {
272 : 0 : return show_available_freqs(policy, buf, true);
273 : : }
274 : : cpufreq_attr_available_freq(scaling_boost);
275 : : EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_boost_freqs);
276 : :
277 : : struct freq_attr *cpufreq_generic_attr[] = {
278 : : &cpufreq_freq_attr_scaling_available_freqs,
279 : : #ifdef CONFIG_CPU_FREQ_BOOST_SW
280 : : &cpufreq_freq_attr_scaling_boost_freqs,
281 : : #endif
282 : : NULL,
283 : : };
284 : : EXPORT_SYMBOL_GPL(cpufreq_generic_attr);
285 : :
286 : : /*
287 : : * if you use these, you must assure that the frequency table is valid
288 : : * all the time between get_attr and put_attr!
289 : : */
290 : 0 : void cpufreq_frequency_table_get_attr(struct cpufreq_frequency_table *table,
291 : : unsigned int cpu)
292 : : {
293 : : pr_debug("setting show_table for cpu %u to %p\n", cpu, table);
294 : 3 : per_cpu(cpufreq_show_table, cpu) = table;
295 : 0 : }
296 : : EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_attr);
297 : :
298 : 0 : void cpufreq_frequency_table_put_attr(unsigned int cpu)
299 : : {
300 : : pr_debug("clearing show_table for cpu %u\n", cpu);
301 : 3 : per_cpu(cpufreq_show_table, cpu) = NULL;
302 : 3 : }
303 : : EXPORT_SYMBOL_GPL(cpufreq_frequency_table_put_attr);
304 : :
305 : 0 : int cpufreq_table_validate_and_show(struct cpufreq_policy *policy,
306 : : struct cpufreq_frequency_table *table)
307 : : {
308 : 3 : int ret = cpufreq_frequency_table_cpuinfo(policy, table);
309 : :
310 [ + - ]: 3 : if (!ret)
311 : 3 : cpufreq_frequency_table_get_attr(table, policy->cpu);
312 : :
313 : 0 : return ret;
314 : : }
315 : : EXPORT_SYMBOL_GPL(cpufreq_table_validate_and_show);
316 : :
317 : 0 : void cpufreq_frequency_table_update_policy_cpu(struct cpufreq_policy *policy)
318 : : {
319 : : pr_debug("Updating show_table for new_cpu %u from last_cpu %u\n",
320 : : policy->cpu, policy->last_cpu);
321 : 29 : per_cpu(cpufreq_show_table, policy->cpu) = per_cpu(cpufreq_show_table,
322 : : policy->last_cpu);
323 : 29 : per_cpu(cpufreq_show_table, policy->last_cpu) = NULL;
324 : 29 : }
325 : :
326 : 0 : struct cpufreq_frequency_table *cpufreq_frequency_get_table(unsigned int cpu)
327 : : {
328 : 3254 : return per_cpu(cpufreq_show_table, cpu);
329 : : }
330 : : EXPORT_SYMBOL_GPL(cpufreq_frequency_get_table);
331 : :
332 : : MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
333 : : MODULE_DESCRIPTION("CPUfreq frequency table helpers");
334 : : MODULE_LICENSE("GPL");
|