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