freq_table.c 7.69 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
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.
 *
Linus Torvalds's avatar
Linus Torvalds committed
10 11
 */

Viresh Kumar's avatar
Viresh Kumar committed
12 13
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

Linus Torvalds's avatar
Linus Torvalds committed
14
#include <linux/cpufreq.h>
15
#include <linux/module.h>
Linus Torvalds's avatar
Linus Torvalds committed
16 17 18 19 20

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

21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
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);

Linus Torvalds's avatar
Linus Torvalds committed
36 37 38
int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
				    struct cpufreq_frequency_table *table)
{
39
	struct cpufreq_frequency_table *pos;
Linus Torvalds's avatar
Linus Torvalds committed
40 41
	unsigned int min_freq = ~0;
	unsigned int max_freq = 0;
42
	unsigned int freq;
Linus Torvalds's avatar
Linus Torvalds committed
43

44 45
	cpufreq_for_each_valid_entry(pos, table) {
		freq = pos->frequency;
Linus Torvalds's avatar
Linus Torvalds committed
46

47
		if (!cpufreq_boost_enabled()
48
		    && (pos->flags & CPUFREQ_BOOST_FREQ))
49 50
			continue;

51
		pr_debug("table entry %u: %u kHz\n", (int)(pos - table), freq);
Linus Torvalds's avatar
Linus Torvalds committed
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
		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;
}

int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
				   struct cpufreq_frequency_table *table)
{
70 71
	struct cpufreq_frequency_table *pos;
	unsigned int freq, next_larger = ~0;
72
	bool found = false;
Linus Torvalds's avatar
Linus Torvalds committed
73

74
	pr_debug("request for verification of policy (%u - %u kHz) for cpu %u\n",
75
					policy->min, policy->max, policy->cpu);
Linus Torvalds's avatar
Linus Torvalds committed
76

77
	cpufreq_verify_within_cpu_limits(policy);
Linus Torvalds's avatar
Linus Torvalds committed
78

79 80 81
	cpufreq_for_each_valid_entry(pos, table) {
		freq = pos->frequency;

82 83 84 85 86 87
		if ((freq >= policy->min) && (freq <= policy->max)) {
			found = true;
			break;
		}

		if ((next_larger > freq) && (freq > policy->max))
Linus Torvalds's avatar
Linus Torvalds committed
88 89 90
			next_larger = freq;
	}

91
	if (!found) {
Linus Torvalds's avatar
Linus Torvalds committed
92
		policy->max = next_larger;
93
		cpufreq_verify_within_cpu_limits(policy);
94
	}
Linus Torvalds's avatar
Linus Torvalds committed
95

96
	pr_debug("verification lead to (%u - %u kHz) for cpu %u\n",
97
				policy->min, policy->max, policy->cpu);
Linus Torvalds's avatar
Linus Torvalds committed
98 99 100 101 102

	return 0;
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_verify);

103
/*
104 105
 * Generic routine to verify policy & frequency table, requires driver to set
 * policy->freq_table prior to it.
106 107 108
 */
int cpufreq_generic_frequency_table_verify(struct cpufreq_policy *policy)
{
109
	if (!policy->freq_table)
110 111
		return -ENODEV;

112
	return cpufreq_frequency_table_verify(policy, policy->freq_table);
113 114
}
EXPORT_SYMBOL_GPL(cpufreq_generic_frequency_table_verify);
Linus Torvalds's avatar
Linus Torvalds committed
115 116 117 118 119 120

int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
				   unsigned int target_freq,
				   unsigned int relation,
				   unsigned int *index)
{
121
	struct cpufreq_frequency_table optimal = {
122
		.driver_data = ~0,
123 124 125
		.frequency = 0,
	};
	struct cpufreq_frequency_table suboptimal = {
126
		.driver_data = ~0,
127 128
		.frequency = 0,
	};
129
	struct cpufreq_frequency_table *pos;
130
	struct cpufreq_frequency_table *table = policy->freq_table;
131
	unsigned int freq, diff, i = 0;
Linus Torvalds's avatar
Linus Torvalds committed
132

133
	pr_debug("request for target %u kHz (relation: %u) for cpu %u\n",
134
					target_freq, relation, policy->cpu);
Linus Torvalds's avatar
Linus Torvalds committed
135 136 137 138 139 140

	switch (relation) {
	case CPUFREQ_RELATION_H:
		suboptimal.frequency = ~0;
		break;
	case CPUFREQ_RELATION_L:
141
	case CPUFREQ_RELATION_C:
Linus Torvalds's avatar
Linus Torvalds committed
142 143 144 145
		optimal.frequency = ~0;
		break;
	}

146 147 148 149
	cpufreq_for_each_valid_entry(pos, table) {
		freq = pos->frequency;

		i = pos - table;
Linus Torvalds's avatar
Linus Torvalds committed
150 151
		if ((freq < policy->min) || (freq > policy->max))
			continue;
152 153 154 155
		if (freq == target_freq) {
			optimal.driver_data = i;
			break;
		}
156
		switch (relation) {
Linus Torvalds's avatar
Linus Torvalds committed
157
		case CPUFREQ_RELATION_H:
158
			if (freq < target_freq) {
Linus Torvalds's avatar
Linus Torvalds committed
159 160
				if (freq >= optimal.frequency) {
					optimal.frequency = freq;
161
					optimal.driver_data = i;
Linus Torvalds's avatar
Linus Torvalds committed
162 163 164 165
				}
			} else {
				if (freq <= suboptimal.frequency) {
					suboptimal.frequency = freq;
166
					suboptimal.driver_data = i;
Linus Torvalds's avatar
Linus Torvalds committed
167 168 169 170
				}
			}
			break;
		case CPUFREQ_RELATION_L:
171
			if (freq > target_freq) {
Linus Torvalds's avatar
Linus Torvalds committed
172 173
				if (freq <= optimal.frequency) {
					optimal.frequency = freq;
174
					optimal.driver_data = i;
Linus Torvalds's avatar
Linus Torvalds committed
175 176 177 178
				}
			} else {
				if (freq >= suboptimal.frequency) {
					suboptimal.frequency = freq;
179
					suboptimal.driver_data = i;
Linus Torvalds's avatar
Linus Torvalds committed
180 181 182
				}
			}
			break;
183 184 185 186 187 188 189 190 191
		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;
Linus Torvalds's avatar
Linus Torvalds committed
192 193
		}
	}
194 195
	if (optimal.driver_data > i) {
		if (suboptimal.driver_data > i)
Linus Torvalds's avatar
Linus Torvalds committed
196
			return -EINVAL;
197
		*index = suboptimal.driver_data;
Linus Torvalds's avatar
Linus Torvalds committed
198
	} else
199
		*index = optimal.driver_data;
Linus Torvalds's avatar
Linus Torvalds committed
200

201 202
	pr_debug("target index is %u, freq is:%u kHz\n", *index,
		 table[*index].frequency);
Linus Torvalds's avatar
Linus Torvalds committed
203 204 205 206 207

	return 0;
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_target);

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

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

218 219 220
	cpufreq_for_each_valid_entry(pos, table)
		if (pos->frequency == freq)
			return pos - table;
221 222 223 224 225

	return -EINVAL;
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_index);

Linus Torvalds's avatar
Linus Torvalds committed
226
/**
227
 * show_available_freqs - show available frequencies for the specified CPU
Linus Torvalds's avatar
Linus Torvalds committed
228
 */
229 230
static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf,
				    bool show_boost)
Linus Torvalds's avatar
Linus Torvalds committed
231 232
{
	ssize_t count = 0;
233
	struct cpufreq_frequency_table *pos, *table = policy->freq_table;
Linus Torvalds's avatar
Linus Torvalds committed
234

235
	if (!table)
Linus Torvalds's avatar
Linus Torvalds committed
236 237
		return -ENODEV;

238
	cpufreq_for_each_valid_entry(pos, table) {
239 240 241 242 243 244 245 246 247 248 249
		/*
		 * 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
		 */
250
		if (show_boost ^ (pos->flags & CPUFREQ_BOOST_FREQ))
251 252
			continue;

253
		count += sprintf(&buf[count], "%d ", pos->frequency);
Linus Torvalds's avatar
Linus Torvalds committed
254 255 256 257 258 259 260
	}
	count += sprintf(&buf[count], "\n");

	return count;

}

261 262 263 264 265 266 267 268 269 270 271 272 273 274
#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);
Linus Torvalds's avatar
Linus Torvalds committed
275 276
EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_available_freqs);

277 278 279 280 281 282 283 284 285 286 287 288
/**
 * 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);

289 290
struct freq_attr *cpufreq_generic_attr[] = {
	&cpufreq_freq_attr_scaling_available_freqs,
291 292 293
#ifdef CONFIG_CPU_FREQ_BOOST_SW
	&cpufreq_freq_attr_scaling_boost_freqs,
#endif
294 295 296 297
	NULL,
};
EXPORT_SYMBOL_GPL(cpufreq_generic_attr);

298 299 300 301 302 303
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)
304
		policy->freq_table = table;
305 306 307 308 309

	return ret;
}
EXPORT_SYMBOL_GPL(cpufreq_table_validate_and_show);

310 311 312
MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
MODULE_DESCRIPTION("CPUfreq frequency table helpers");
MODULE_LICENSE("GPL");