File Index
Files and directories
Sign in
FILE

freq_table.c

File preview with syntax highlighting.

linux-6.19.14/drivers/cpufreq/freq_table.c C
  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
 17static 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
 31int 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
 67int 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}
100EXPORT_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 */
106int 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}
113EXPORT_SYMBOL_GPL(cpufreq_generic_frequency_table_verify);
114
115int 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}
206EXPORT_SYMBOL_GPL(cpufreq_table_index_unsorted);
207
208int 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}
225EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_index);
226
227/*
228 * show_available_freqs - show available frequencies for the specified CPU
229 */
230static 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)	  \
263struct 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 */
270static ssize_t scaling_available_frequencies_show(struct cpufreq_policy *policy,
271						  char *buf)
272{
273	return show_available_freqs(policy, buf, false);
274}
275cpufreq_attr_available_freq(scaling_available);
276
277/*
278 * scaling_boost_frequencies_show - show available boost frequencies for
279 * the specified CPU
280 */
281static ssize_t scaling_boost_frequencies_show(struct cpufreq_policy *policy,
282					      char *buf)
283{
284	return show_available_freqs(policy, buf, true);
285}
286cpufreq_attr_available_freq(scaling_boost);
287
288static 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
343int 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	if (policy->freq_table_sorted == CPUFREQ_TABLE_SORTED_ASCENDING ||
364	    policy->freq_table_sorted == CPUFREQ_TABLE_SORTED_DESCENDING)
365		return 0;
366
367	return set_freq_table_sorted(policy);
368}
369
370MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
371MODULE_DESCRIPTION("CPUfreq frequency table helpers");
~/ Read-only file preview.