freq_table.c 8.7 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0-only
L
Linus Torvalds 已提交
2 3 4 5 6 7
/*
 * linux/drivers/cpufreq/freq_table.c
 *
 * Copyright (C) 2002 - 2003 Dominik Brodowski
 */

V
Viresh Kumar 已提交
8 9
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

L
Linus Torvalds 已提交
10
#include <linux/cpufreq.h>
11
#include <linux/module.h>
L
Linus Torvalds 已提交
12 13 14 15 16

/*********************************************************************
 *                     FREQUENCY TABLE HELPERS                       *
 *********************************************************************/

17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
bool policy_has_boost_freq(struct cpufreq_policy *policy)
{
	struct cpufreq_frequency_table *pos, *table = policy->freq_table;

	if (!table)
		return false;

	cpufreq_for_each_valid_entry(pos, table)
		if (pos->flags & CPUFREQ_BOOST_FREQ)
			return true;

	return false;
}
EXPORT_SYMBOL_GPL(policy_has_boost_freq);

L
Linus Torvalds 已提交
32 33 34
int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
				    struct cpufreq_frequency_table *table)
{
35
	struct cpufreq_frequency_table *pos;
L
Linus Torvalds 已提交
36 37
	unsigned int min_freq = ~0;
	unsigned int max_freq = 0;
38
	unsigned int freq;
L
Linus Torvalds 已提交
39

40 41
	cpufreq_for_each_valid_entry(pos, table) {
		freq = pos->frequency;
L
Linus Torvalds 已提交
42

43
		if (!cpufreq_boost_enabled()
44
		    && (pos->flags & CPUFREQ_BOOST_FREQ))
45 46
			continue;

47
		pr_debug("table entry %u: %u kHz\n", (int)(pos - table), freq);
L
Linus Torvalds 已提交
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
		if (freq < min_freq)
			min_freq = freq;
		if (freq > max_freq)
			max_freq = freq;
	}

	policy->min = policy->cpuinfo.min_freq = min_freq;
	policy->max = policy->cpuinfo.max_freq = max_freq;

	if (policy->min == ~0)
		return -EINVAL;
	else
		return 0;
}

63
int cpufreq_frequency_table_verify(struct cpufreq_policy_data *policy,
L
Linus Torvalds 已提交
64 65
				   struct cpufreq_frequency_table *table)
{
66 67
	struct cpufreq_frequency_table *pos;
	unsigned int freq, next_larger = ~0;
68
	bool found = false;
L
Linus Torvalds 已提交
69

70
	pr_debug("request for verification of policy (%u - %u kHz) for cpu %u\n",
71
					policy->min, policy->max, policy->cpu);
L
Linus Torvalds 已提交
72

73
	cpufreq_verify_within_cpu_limits(policy);
L
Linus Torvalds 已提交
74

75 76 77
	cpufreq_for_each_valid_entry(pos, table) {
		freq = pos->frequency;

78 79 80 81 82 83
		if ((freq >= policy->min) && (freq <= policy->max)) {
			found = true;
			break;
		}

		if ((next_larger > freq) && (freq > policy->max))
L
Linus Torvalds 已提交
84 85 86
			next_larger = freq;
	}

87
	if (!found) {
L
Linus Torvalds 已提交
88
		policy->max = next_larger;
89
		cpufreq_verify_within_cpu_limits(policy);
90
	}
L
Linus Torvalds 已提交
91

92
	pr_debug("verification lead to (%u - %u kHz) for cpu %u\n",
93
				policy->min, policy->max, policy->cpu);
L
Linus Torvalds 已提交
94 95 96 97 98

	return 0;
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_verify);

99
/*
100 101
 * Generic routine to verify policy & frequency table, requires driver to set
 * 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 107
		return -ENODEV;

108
	return cpufreq_frequency_table_verify(policy, policy->freq_table);
109 110
}
EXPORT_SYMBOL_GPL(cpufreq_generic_frequency_table_verify);
L
Linus Torvalds 已提交
111

112 113 114
int cpufreq_table_index_unsorted(struct cpufreq_policy *policy,
				 unsigned int target_freq,
				 unsigned int relation)
L
Linus Torvalds 已提交
115
{
116
	struct cpufreq_frequency_table optimal = {
117
		.driver_data = ~0,
118 119 120
		.frequency = 0,
	};
	struct cpufreq_frequency_table suboptimal = {
121
		.driver_data = ~0,
122 123
		.frequency = 0,
	};
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;
L
Linus Torvalds 已提交
128

129
	pr_debug("request for target %u kHz (relation: %u) for cpu %u\n",
130
					target_freq, relation, policy->cpu);
L
Linus Torvalds 已提交
131 132 133 134 135 136

	switch (relation) {
	case CPUFREQ_RELATION_H:
		suboptimal.frequency = ~0;
		break;
	case CPUFREQ_RELATION_L:
137
	case CPUFREQ_RELATION_C:
L
Linus Torvalds 已提交
138 139 140 141
		optimal.frequency = ~0;
		break;
	}

142
	cpufreq_for_each_valid_entry_idx(pos, table, i) {
143 144
		freq = pos->frequency;

L
Linus Torvalds 已提交
145 146
		if ((freq < policy->min) || (freq > policy->max))
			continue;
147 148 149 150
		if (freq == target_freq) {
			optimal.driver_data = i;
			break;
		}
151
		switch (relation) {
L
Linus Torvalds 已提交
152
		case CPUFREQ_RELATION_H:
153
			if (freq < target_freq) {
L
Linus Torvalds 已提交
154 155
				if (freq >= optimal.frequency) {
					optimal.frequency = freq;
156
					optimal.driver_data = i;
L
Linus Torvalds 已提交
157 158 159 160
				}
			} else {
				if (freq <= suboptimal.frequency) {
					suboptimal.frequency = freq;
161
					suboptimal.driver_data = i;
L
Linus Torvalds 已提交
162 163 164 165
				}
			}
			break;
		case CPUFREQ_RELATION_L:
166
			if (freq > target_freq) {
L
Linus Torvalds 已提交
167 168
				if (freq <= optimal.frequency) {
					optimal.frequency = freq;
169
					optimal.driver_data = i;
L
Linus Torvalds 已提交
170 171 172 173
				}
			} else {
				if (freq >= suboptimal.frequency) {
					suboptimal.frequency = freq;
174
					suboptimal.driver_data = i;
L
Linus Torvalds 已提交
175 176 177
				}
			}
			break;
178 179 180 181 182 183 184 185 186
		case CPUFREQ_RELATION_C:
			diff = abs(freq - target_freq);
			if (diff < optimal.frequency ||
			    (diff == optimal.frequency &&
			     freq > table[optimal.driver_data].frequency)) {
				optimal.frequency = diff;
				optimal.driver_data = i;
			}
			break;
L
Linus Torvalds 已提交
187 188
		}
	}
189
	if (optimal.driver_data > i) {
190 191 192 193
		if (suboptimal.driver_data > i) {
			WARN(1, "Invalid frequency table: %d\n", policy->cpu);
			return 0;
		}
L
Linus Torvalds 已提交
194

195 196 197
		index = suboptimal.driver_data;
	} else
		index = optimal.driver_data;
L
Linus Torvalds 已提交
198

199 200 201
	pr_debug("target index is %u, freq is:%u kHz\n", index,
		 table[index].frequency);
	return index;
L
Linus Torvalds 已提交
202
}
203
EXPORT_SYMBOL_GPL(cpufreq_table_index_unsorted);
L
Linus Torvalds 已提交
204

205 206 207
int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy,
		unsigned int freq)
{
208
	struct cpufreq_frequency_table *pos, *table = policy->freq_table;
209
	int idx;
210 211 212 213 214 215

	if (unlikely(!table)) {
		pr_debug("%s: Unable to find frequency table\n", __func__);
		return -ENOENT;
	}

216
	cpufreq_for_each_valid_entry_idx(pos, table, idx)
217
		if (pos->frequency == freq)
218
			return idx;
219 220 221 222 223

	return -EINVAL;
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_index);

L
Linus Torvalds 已提交
224
/**
225
 * show_available_freqs - show available frequencies for the specified CPU
L
Linus Torvalds 已提交
226
 */
227 228
static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf,
				    bool show_boost)
L
Linus Torvalds 已提交
229 230
{
	ssize_t count = 0;
231
	struct cpufreq_frequency_table *pos, *table = policy->freq_table;
L
Linus Torvalds 已提交
232

233
	if (!table)
L
Linus Torvalds 已提交
234 235
		return -ENODEV;

236
	cpufreq_for_each_valid_entry(pos, table) {
237 238 239 240 241 242 243 244 245 246 247
		/*
		 * show_boost = true and driver_data = BOOST freq
		 * display BOOST freqs
		 *
		 * show_boost = false and driver_data = BOOST freq
		 * show_boost = true and driver_data != BOOST freq
		 * continue - do not display anything
		 *
		 * show_boost = false and driver_data != BOOST freq
		 * display NON BOOST freqs
		 */
248
		if (show_boost ^ (pos->flags & CPUFREQ_BOOST_FREQ))
249 250
			continue;

251
		count += sprintf(&buf[count], "%d ", pos->frequency);
L
Linus Torvalds 已提交
252 253 254 255 256 257 258
	}
	count += sprintf(&buf[count], "\n");

	return count;

}

259 260 261 262 263 264 265 266 267 268 269 270 271 272
#define cpufreq_attr_available_freq(_name)	  \
struct freq_attr cpufreq_freq_attr_##_name##_freqs =     \
__ATTR_RO(_name##_frequencies)

/**
 * show_scaling_available_frequencies - show available normal frequencies for
 * the specified CPU
 */
static ssize_t scaling_available_frequencies_show(struct cpufreq_policy *policy,
						  char *buf)
{
	return show_available_freqs(policy, buf, false);
}
cpufreq_attr_available_freq(scaling_available);
L
Linus Torvalds 已提交
273 274
EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_available_freqs);

275 276 277 278 279 280 281 282 283 284 285 286
/**
 * show_available_boost_freqs - show available boost frequencies for
 * the specified CPU
 */
static ssize_t scaling_boost_frequencies_show(struct cpufreq_policy *policy,
					      char *buf)
{
	return show_available_freqs(policy, buf, true);
}
cpufreq_attr_available_freq(scaling_boost);
EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_boost_freqs);

287 288 289 290 291 292
struct freq_attr *cpufreq_generic_attr[] = {
	&cpufreq_freq_attr_scaling_available_freqs,
	NULL,
};
EXPORT_SYMBOL_GPL(cpufreq_generic_attr);

293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
static int set_freq_table_sorted(struct cpufreq_policy *policy)
{
	struct cpufreq_frequency_table *pos, *table = policy->freq_table;
	struct cpufreq_frequency_table *prev = NULL;
	int ascending = 0;

	policy->freq_table_sorted = CPUFREQ_TABLE_UNSORTED;

	cpufreq_for_each_valid_entry(pos, table) {
		if (!prev) {
			prev = pos;
			continue;
		}

		if (pos->frequency == prev->frequency) {
			pr_warn("Duplicate freq-table entries: %u\n",
				pos->frequency);
			return -EINVAL;
		}

		/* Frequency increased from prev to pos */
		if (pos->frequency > prev->frequency) {
			/* But frequency was decreasing earlier */
			if (ascending < 0) {
				pr_debug("Freq table is unsorted\n");
				return 0;
			}

			ascending++;
		} else {
			/* Frequency decreased from prev to pos */

			/* But frequency was increasing earlier */
			if (ascending > 0) {
				pr_debug("Freq table is unsorted\n");
				return 0;
			}

			ascending--;
		}

		prev = pos;
	}

	if (ascending > 0)
		policy->freq_table_sorted = CPUFREQ_TABLE_SORTED_ASCENDING;
	else
		policy->freq_table_sorted = CPUFREQ_TABLE_SORTED_DESCENDING;

	pr_debug("Freq table is sorted in %s order\n",
		 ascending > 0 ? "ascending" : "descending");

	return 0;
}

348 349 350 351 352 353 354 355 356 357 358 359 360 361
int cpufreq_table_validate_and_sort(struct cpufreq_policy *policy)
{
	int ret;

	if (!policy->freq_table)
		return 0;

	ret = cpufreq_frequency_table_cpuinfo(policy, policy->freq_table);
	if (ret)
		return ret;

	return set_freq_table_sorted(policy);
}

362 363 364
MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
MODULE_DESCRIPTION("CPUfreq frequency table helpers");
MODULE_LICENSE("GPL");