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