freq_table.c 8.95 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
int cpufreq_table_index_unsorted(struct cpufreq_policy *policy,
				 unsigned int target_freq,
				 unsigned int relation)
Linus Torvalds's avatar
Linus Torvalds committed
119
{
120
	struct cpufreq_frequency_table optimal = {
121
		.driver_data = ~0,
122 123 124
		.frequency = 0,
	};
	struct cpufreq_frequency_table suboptimal = {
125
		.driver_data = ~0,
126 127
		.frequency = 0,
	};
128
	struct cpufreq_frequency_table *pos;
129
	struct cpufreq_frequency_table *table = policy->freq_table;
130
	unsigned int freq, diff, i = 0;
131
	int index;
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
	if (optimal.driver_data > i) {
195 196 197 198
		if (suboptimal.driver_data > i) {
			WARN(1, "Invalid frequency table: %d\n", policy->cpu);
			return 0;
		}
Linus Torvalds's avatar
Linus Torvalds committed
199

200 201 202
		index = suboptimal.driver_data;
	} else
		index = optimal.driver_data;
Linus Torvalds's avatar
Linus Torvalds committed
203

204 205 206
	pr_debug("target index is %u, freq is:%u kHz\n", index,
		 table[index].frequency);
	return index;
Linus Torvalds's avatar
Linus Torvalds committed
207
}
208
EXPORT_SYMBOL_GPL(cpufreq_table_index_unsorted);
Linus Torvalds's avatar
Linus Torvalds committed
209

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

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

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

	return -EINVAL;
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_index);

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

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

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

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

	return count;

}

263 264 265 266 267 268 269 270 271 272 273 274 275 276
#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
277 278
EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_available_freqs);

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

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

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 348 349 350 351 352 353 354
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;
}

355 356 357
int cpufreq_table_validate_and_show(struct cpufreq_policy *policy,
				      struct cpufreq_frequency_table *table)
{
358
	int ret;
359

360 361 362
	ret = cpufreq_frequency_table_cpuinfo(policy, table);
	if (ret)
		return ret;
363

364 365
	policy->freq_table = table;
	return set_freq_table_sorted(policy);
366 367 368
}
EXPORT_SYMBOL_GPL(cpufreq_table_validate_and_show);

369 370 371
MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
MODULE_DESCRIPTION("CPUfreq frequency table helpers");
MODULE_LICENSE("GPL");