freq_table.c 7.8 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4
/*
 * linux/drivers/cpufreq/freq_table.c
 *
 * Copyright (C) 2002 - 2003 Dominik Brodowski
5 6 7 8 9
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
L
Linus Torvalds 已提交
10 11
 */

V
Viresh Kumar 已提交
12 13
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

L
Linus Torvalds 已提交
14
#include <linux/cpufreq.h>
15
#include <linux/module.h>
L
Linus Torvalds 已提交
16 17 18 19 20 21 22 23 24 25

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

int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
				    struct cpufreq_frequency_table *table)
{
	unsigned int min_freq = ~0;
	unsigned int max_freq = 0;
26
	unsigned int i;
L
Linus Torvalds 已提交
27

28
	for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
L
Linus Torvalds 已提交
29 30
		unsigned int freq = table[i].frequency;
		if (freq == CPUFREQ_ENTRY_INVALID) {
31
			pr_debug("table entry %u is invalid, skipping\n", i);
L
Linus Torvalds 已提交
32 33 34

			continue;
		}
35 36 37 38
		if (!cpufreq_boost_enabled()
		    && table[i].driver_data == CPUFREQ_BOOST_FREQ)
			continue;

39 40
		pr_debug("table entry %u: %u kHz, %u driver_data\n",
					i, freq, table[i].driver_data);
L
Linus Torvalds 已提交
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
		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;
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_cpuinfo);


int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
				   struct cpufreq_frequency_table *table)
{
61 62
	unsigned int next_larger = ~0, freq, i = 0;
	bool found = false;
L
Linus Torvalds 已提交
63

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

67
	cpufreq_verify_within_cpu_limits(policy);
L
Linus Torvalds 已提交
68

69
	for (; freq = table[i].frequency, freq != CPUFREQ_TABLE_END; i++) {
L
Linus Torvalds 已提交
70 71
		if (freq == CPUFREQ_ENTRY_INVALID)
			continue;
72 73 74 75 76 77
		if ((freq >= policy->min) && (freq <= policy->max)) {
			found = true;
			break;
		}

		if ((next_larger > freq) && (freq > policy->max))
L
Linus Torvalds 已提交
78 79 80
			next_larger = freq;
	}

81
	if (!found) {
L
Linus Torvalds 已提交
82
		policy->max = next_larger;
83
		cpufreq_verify_within_cpu_limits(policy);
84
	}
L
Linus Torvalds 已提交
85

86
	pr_debug("verification lead to (%u - %u kHz) for cpu %u\n",
87
				policy->min, policy->max, policy->cpu);
L
Linus Torvalds 已提交
88 89 90 91 92

	return 0;
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_verify);

93
/*
94 95
 * Generic routine to verify policy & frequency table, requires driver to set
 * policy->freq_table prior to it.
96 97 98 99 100 101 102 103 104 105 106
 */
int cpufreq_generic_frequency_table_verify(struct cpufreq_policy *policy)
{
	struct cpufreq_frequency_table *table =
		cpufreq_frequency_get_table(policy->cpu);
	if (!table)
		return -ENODEV;

	return cpufreq_frequency_table_verify(policy, table);
}
EXPORT_SYMBOL_GPL(cpufreq_generic_frequency_table_verify);
L
Linus Torvalds 已提交
107 108 109 110 111 112 113

int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
				   struct cpufreq_frequency_table *table,
				   unsigned int target_freq,
				   unsigned int relation,
				   unsigned int *index)
{
114
	struct cpufreq_frequency_table optimal = {
115
		.driver_data = ~0,
116 117 118
		.frequency = 0,
	};
	struct cpufreq_frequency_table suboptimal = {
119
		.driver_data = ~0,
120 121
		.frequency = 0,
	};
L
Linus Torvalds 已提交
122 123
	unsigned int i;

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

	switch (relation) {
	case CPUFREQ_RELATION_H:
		suboptimal.frequency = ~0;
		break;
	case CPUFREQ_RELATION_L:
		optimal.frequency = ~0;
		break;
	}

136
	for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
L
Linus Torvalds 已提交
137 138 139 140 141
		unsigned int freq = table[i].frequency;
		if (freq == CPUFREQ_ENTRY_INVALID)
			continue;
		if ((freq < policy->min) || (freq > policy->max))
			continue;
142
		switch (relation) {
L
Linus Torvalds 已提交
143 144 145 146
		case CPUFREQ_RELATION_H:
			if (freq <= target_freq) {
				if (freq >= optimal.frequency) {
					optimal.frequency = freq;
147
					optimal.driver_data = i;
L
Linus Torvalds 已提交
148 149 150 151
				}
			} else {
				if (freq <= suboptimal.frequency) {
					suboptimal.frequency = freq;
152
					suboptimal.driver_data = i;
L
Linus Torvalds 已提交
153 154 155 156 157 158 159
				}
			}
			break;
		case CPUFREQ_RELATION_L:
			if (freq >= target_freq) {
				if (freq <= optimal.frequency) {
					optimal.frequency = freq;
160
					optimal.driver_data = i;
L
Linus Torvalds 已提交
161 162 163 164
				}
			} else {
				if (freq >= suboptimal.frequency) {
					suboptimal.frequency = freq;
165
					suboptimal.driver_data = i;
L
Linus Torvalds 已提交
166 167 168 169 170
				}
			}
			break;
		}
	}
171 172
	if (optimal.driver_data > i) {
		if (suboptimal.driver_data > i)
L
Linus Torvalds 已提交
173
			return -EINVAL;
174
		*index = suboptimal.driver_data;
L
Linus Torvalds 已提交
175
	} else
176
		*index = optimal.driver_data;
L
Linus Torvalds 已提交
177

178
	pr_debug("target is %u (%u kHz, %u)\n", *index, table[*index].frequency,
179
		table[*index].driver_data);
L
Linus Torvalds 已提交
180 181 182 183 184

	return 0;
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_target);

185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy,
		unsigned int freq)
{
	struct cpufreq_frequency_table *table;
	int i;

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

	for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
		if (table[i].frequency == freq)
			return i;
	}

	return -EINVAL;
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_index);

L
Linus Torvalds 已提交
206
/**
207
 * show_available_freqs - show available frequencies for the specified CPU
L
Linus Torvalds 已提交
208
 */
209 210
static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf,
				    bool show_boost)
L
Linus Torvalds 已提交
211 212 213
{
	unsigned int i = 0;
	ssize_t count = 0;
214
	struct cpufreq_frequency_table *table = policy->freq_table;
L
Linus Torvalds 已提交
215

216
	if (!table)
L
Linus Torvalds 已提交
217 218
		return -ENODEV;

219
	for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
L
Linus Torvalds 已提交
220 221
		if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
			continue;
222 223 224 225 226 227 228 229 230 231 232 233 234 235
		/*
		 * 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
		 */
		if (show_boost ^ (table[i].driver_data == CPUFREQ_BOOST_FREQ))
			continue;

L
Linus Torvalds 已提交
236 237 238 239 240 241 242 243
		count += sprintf(&buf[count], "%d ", table[i].frequency);
	}
	count += sprintf(&buf[count], "\n");

	return count;

}

244 245 246 247 248 249 250 251 252 253 254 255 256 257
#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 已提交
258 259
EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_available_freqs);

260 261 262 263 264 265 266 267 268 269 270 271
/**
 * 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);

272 273
struct freq_attr *cpufreq_generic_attr[] = {
	&cpufreq_freq_attr_scaling_available_freqs,
274 275 276
#ifdef CONFIG_CPU_FREQ_BOOST_SW
	&cpufreq_freq_attr_scaling_boost_freqs,
#endif
277 278 279 280
	NULL,
};
EXPORT_SYMBOL_GPL(cpufreq_generic_attr);

281 282 283 284 285 286
int cpufreq_table_validate_and_show(struct cpufreq_policy *policy,
				      struct cpufreq_frequency_table *table)
{
	int ret = cpufreq_frequency_table_cpuinfo(policy, table);

	if (!ret)
287
		policy->freq_table = table;
288 289 290 291 292

	return ret;
}
EXPORT_SYMBOL_GPL(cpufreq_table_validate_and_show);

293
struct cpufreq_policy *cpufreq_cpu_get_raw(unsigned int cpu);
294

L
Linus Torvalds 已提交
295 296
struct cpufreq_frequency_table *cpufreq_frequency_get_table(unsigned int cpu)
{
297 298
	struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu);
	return policy ? policy->freq_table : NULL;
L
Linus Torvalds 已提交
299 300 301
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_get_table);

302 303 304
MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
MODULE_DESCRIPTION("CPUfreq frequency table helpers");
MODULE_LICENSE("GPL");