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