freq_table.c 6.02 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 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/cpufreq.h>

/*********************************************************************
 *                     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;
Linus Torvalds's avatar
Linus Torvalds committed
27

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

			continue;
		}
35
		pr_debug("table entry %u: %u kHz, %u index\n",
36
					i, freq, table[i].index);
Linus Torvalds's avatar
Linus Torvalds committed
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
		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)
{
	unsigned int next_larger = ~0;
58
	unsigned int i;
Linus Torvalds's avatar
Linus Torvalds committed
59 60
	unsigned int count = 0;

61
	pr_debug("request for verification of policy (%u - %u kHz) for cpu %u\n",
62
					policy->min, policy->max, policy->cpu);
Linus Torvalds's avatar
Linus Torvalds committed
63 64 65 66

	if (!cpu_online(policy->cpu))
		return -EINVAL;

67 68
	cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
				     policy->cpuinfo.max_freq);
Linus Torvalds's avatar
Linus Torvalds committed
69

70
	for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
Linus Torvalds's avatar
Linus Torvalds committed
71 72 73 74 75 76 77 78 79 80 81 82
		unsigned int freq = table[i].frequency;
		if (freq == CPUFREQ_ENTRY_INVALID)
			continue;
		if ((freq >= policy->min) && (freq <= policy->max))
			count++;
		else if ((next_larger > freq) && (freq > policy->max))
			next_larger = freq;
	}

	if (!count)
		policy->max = next_larger;

83 84
	cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
				     policy->cpuinfo.max_freq);
Linus Torvalds's avatar
Linus Torvalds committed
85

86
	pr_debug("verification lead to (%u - %u kHz) for cpu %u\n",
87
				policy->min, policy->max, policy->cpu);
Linus Torvalds's avatar
Linus Torvalds committed
88 89 90 91 92 93 94 95 96 97 98 99

	return 0;
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_verify);


int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
				   struct cpufreq_frequency_table *table,
				   unsigned int target_freq,
				   unsigned int relation,
				   unsigned int *index)
{
100 101 102 103 104 105 106 107
	struct cpufreq_frequency_table optimal = {
		.index = ~0,
		.frequency = 0,
	};
	struct cpufreq_frequency_table suboptimal = {
		.index = ~0,
		.frequency = 0,
	};
Linus Torvalds's avatar
Linus Torvalds committed
108 109
	unsigned int i;

110
	pr_debug("request for target %u kHz (relation: %u) for cpu %u\n",
111
					target_freq, relation, policy->cpu);
Linus Torvalds's avatar
Linus Torvalds committed
112 113 114 115 116 117 118 119 120 121 122 123 124

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

	if (!cpu_online(policy->cpu))
		return -EINVAL;

125
	for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
Linus Torvalds's avatar
Linus Torvalds committed
126 127 128 129 130
		unsigned int freq = table[i].frequency;
		if (freq == CPUFREQ_ENTRY_INVALID)
			continue;
		if ((freq < policy->min) || (freq > policy->max))
			continue;
131
		switch (relation) {
Linus Torvalds's avatar
Linus Torvalds committed
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
		case CPUFREQ_RELATION_H:
			if (freq <= target_freq) {
				if (freq >= optimal.frequency) {
					optimal.frequency = freq;
					optimal.index = i;
				}
			} else {
				if (freq <= suboptimal.frequency) {
					suboptimal.frequency = freq;
					suboptimal.index = i;
				}
			}
			break;
		case CPUFREQ_RELATION_L:
			if (freq >= target_freq) {
				if (freq <= optimal.frequency) {
					optimal.frequency = freq;
					optimal.index = i;
				}
			} else {
				if (freq >= suboptimal.frequency) {
					suboptimal.frequency = freq;
					suboptimal.index = i;
				}
			}
			break;
		}
	}
	if (optimal.index > i) {
		if (suboptimal.index > i)
			return -EINVAL;
		*index = suboptimal.index;
	} else
		*index = optimal.index;

167
	pr_debug("target is %u (%u kHz, %u)\n", *index, table[*index].frequency,
Linus Torvalds's avatar
Linus Torvalds committed
168 169 170 171 172 173
		table[*index].index);

	return 0;
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_target);

174
static DEFINE_PER_CPU(struct cpufreq_frequency_table *, cpufreq_show_table);
Linus Torvalds's avatar
Linus Torvalds committed
175
/**
176
 * show_available_freqs - show available frequencies for the specified CPU
Linus Torvalds's avatar
Linus Torvalds committed
177
 */
178
static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf)
Linus Torvalds's avatar
Linus Torvalds committed
179 180 181 182 183 184
{
	unsigned int i = 0;
	unsigned int cpu = policy->cpu;
	ssize_t count = 0;
	struct cpufreq_frequency_table *table;

185
	if (!per_cpu(cpufreq_show_table, cpu))
Linus Torvalds's avatar
Linus Torvalds committed
186 187
		return -ENODEV;

188
	table = per_cpu(cpufreq_show_table, cpu);
Linus Torvalds's avatar
Linus Torvalds committed
189

190
	for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
Linus Torvalds's avatar
Linus Torvalds committed
191 192 193 194 195 196 197 198 199 200 201
		if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
			continue;
		count += sprintf(&buf[count], "%d ", table[i].frequency);
	}
	count += sprintf(&buf[count], "\n");

	return count;

}

struct freq_attr cpufreq_freq_attr_scaling_available_freqs = {
202 203 204
	.attr = { .name = "scaling_available_frequencies",
		  .mode = 0444,
		},
Linus Torvalds's avatar
Linus Torvalds committed
205 206 207 208 209 210 211 212
	.show = show_available_freqs,
};
EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_available_freqs);

/*
 * if you use these, you must assure that the frequency table is valid
 * all the time between get_attr and put_attr!
 */
213
void cpufreq_frequency_table_get_attr(struct cpufreq_frequency_table *table,
Linus Torvalds's avatar
Linus Torvalds committed
214 215
				      unsigned int cpu)
{
216
	pr_debug("setting show_table for cpu %u to %p\n", cpu, table);
217
	per_cpu(cpufreq_show_table, cpu) = table;
Linus Torvalds's avatar
Linus Torvalds committed
218 219 220 221 222
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_attr);

void cpufreq_frequency_table_put_attr(unsigned int cpu)
{
223
	pr_debug("clearing show_table for cpu %u\n", cpu);
224
	per_cpu(cpufreq_show_table, cpu) = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
225 226 227 228 229
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_put_attr);

struct cpufreq_frequency_table *cpufreq_frequency_get_table(unsigned int cpu)
{
230
	return per_cpu(cpufreq_show_table, cpu);
Linus Torvalds's avatar
Linus Torvalds committed
231 232 233
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_get_table);

234 235 236
MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
MODULE_DESCRIPTION("CPUfreq frequency table helpers");
MODULE_LICENSE("GPL");