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