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
	cpufreq_for_each_valid_entry_idx(pos, table, i) {
147 148
		freq = pos->frequency;

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

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

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

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

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

220
	cpufreq_for_each_valid_entry_idx(pos, table, idx)
221
		if (pos->frequency == freq)
222
			return idx;
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");