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