freq_table.c 6.39 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 15 16 17 18 19 20 21 22 23 24 25 26 27
#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;
28
	unsigned int i;
Linus Torvalds's avatar
Linus Torvalds committed
29

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

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

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

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

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

72
	for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
Linus Torvalds's avatar
Linus Torvalds committed
73 74 75 76 77 78 79 80 81 82 83 84
		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;

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

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

	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)
{
102 103 104 105 106 107 108 109
	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
110 111
	unsigned int i;

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

	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;

127
	for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
Linus Torvalds's avatar
Linus Torvalds committed
128 129 130 131 132
		unsigned int freq = table[i].frequency;
		if (freq == CPUFREQ_ENTRY_INVALID)
			continue;
		if ((freq < policy->min) || (freq > policy->max))
			continue;
133
		switch (relation) {
Linus Torvalds's avatar
Linus Torvalds committed
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 167 168
		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;

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

	return 0;
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_target);

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

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

190
	table = per_cpu(cpufreq_show_table, cpu);
Linus Torvalds's avatar
Linus Torvalds committed
191

192
	for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
Linus Torvalds's avatar
Linus Torvalds committed
193 194 195 196 197 198 199 200 201 202 203
		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 = {
204 205 206
	.attr = { .name = "scaling_available_frequencies",
		  .mode = 0444,
		},
Linus Torvalds's avatar
Linus Torvalds committed
207 208 209 210 211 212 213 214
	.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!
 */
215
void cpufreq_frequency_table_get_attr(struct cpufreq_frequency_table *table,
Linus Torvalds's avatar
Linus Torvalds committed
216 217
				      unsigned int cpu)
{
218
	pr_debug("setting show_table for cpu %u to %p\n", cpu, table);
219
	per_cpu(cpufreq_show_table, cpu) = table;
Linus Torvalds's avatar
Linus Torvalds committed
220 221 222 223 224
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_attr);

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

230 231 232 233 234 235 236 237 238
void cpufreq_frequency_table_update_policy_cpu(struct cpufreq_policy *policy)
{
	pr_debug("Updating show_table for new_cpu %u from last_cpu %u\n",
			policy->cpu, policy->last_cpu);
	per_cpu(cpufreq_show_table, policy->cpu) = per_cpu(cpufreq_show_table,
			policy->last_cpu);
	per_cpu(cpufreq_show_table, policy->last_cpu) = NULL;
}

Linus Torvalds's avatar
Linus Torvalds committed
239 240
struct cpufreq_frequency_table *cpufreq_frequency_get_table(unsigned int cpu)
{
241
	return per_cpu(cpufreq_show_table, cpu);
Linus Torvalds's avatar
Linus Torvalds committed
242 243 244
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_get_table);

245 246 247
MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
MODULE_DESCRIPTION("CPUfreq frequency table helpers");
MODULE_LICENSE("GPL");