cpufreq_userspace.c 6.0 KB
Newer Older
1

L
Linus Torvalds 已提交
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 *  linux/drivers/cpufreq/cpufreq_userspace.c
 *
 *  Copyright (C)  2001 Russell King
 *            (C)  2002 - 2004 Dominik Brodowski <linux@brodo.de>
 *
 * 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.
 *
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/smp.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/cpufreq.h>
21
#include <linux/cpu.h>
L
Linus Torvalds 已提交
22 23 24
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/sysfs.h>
25
#include <linux/mutex.h>
L
Linus Torvalds 已提交
26 27 28 29

/**
 * A few values needed by the userspace governor
 */
30 31 32 33 34 35
static DEFINE_PER_CPU(unsigned int, cpu_max_freq);
static DEFINE_PER_CPU(unsigned int, cpu_min_freq);
static DEFINE_PER_CPU(unsigned int, cpu_cur_freq); /* current CPU freq */
static DEFINE_PER_CPU(unsigned int, cpu_set_freq); /* CPU freq desired by
							userspace */
static DEFINE_PER_CPU(unsigned int, cpu_is_managed);
L
Linus Torvalds 已提交
36

37
static DEFINE_MUTEX(userspace_mutex);
38
static int cpus_using_userspace_governor;
L
Linus Torvalds 已提交
39

40 41
#define dprintk(msg...) \
	cpufreq_debug_printk(CPUFREQ_DEBUG_GOVERNOR, "userspace", msg)
L
Linus Torvalds 已提交
42 43

/* keep track of frequency transitions */
44
static int
L
Linus Torvalds 已提交
45
userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
46
	void *data)
L
Linus Torvalds 已提交
47
{
48
	struct cpufreq_freqs *freq = data;
L
Linus Torvalds 已提交
49

50
	if (!per_cpu(cpu_is_managed, freq->cpu))
51 52 53 54
		return 0;

	dprintk("saving cpu_cur_freq of cpu %u to be %u kHz\n",
			freq->cpu, freq->new);
55
	per_cpu(cpu_cur_freq, freq->cpu) = freq->new;
L
Linus Torvalds 已提交
56

57
	return 0;
L
Linus Torvalds 已提交
58 59 60
}

static struct notifier_block userspace_cpufreq_notifier_block = {
61
	.notifier_call  = userspace_cpufreq_notifier
L
Linus Torvalds 已提交
62 63 64
};


65
/**
L
Linus Torvalds 已提交
66
 * cpufreq_set - set the CPU frequency
67
 * @policy: pointer to policy struct where freq is being set
L
Linus Torvalds 已提交
68 69 70 71
 * @freq: target frequency in kHz
 *
 * Sets the CPU frequency to freq.
 */
72
static int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq)
L
Linus Torvalds 已提交
73 74 75
{
	int ret = -EINVAL;

76
	dprintk("cpufreq_set for cpu %u, freq %u kHz\n", policy->cpu, freq);
L
Linus Torvalds 已提交
77

78
	mutex_lock(&userspace_mutex);
79
	if (!per_cpu(cpu_is_managed, policy->cpu))
L
Linus Torvalds 已提交
80 81
		goto err;

82
	per_cpu(cpu_set_freq, policy->cpu) = freq;
L
Linus Torvalds 已提交
83

84 85 86 87
	if (freq < per_cpu(cpu_min_freq, policy->cpu))
		freq = per_cpu(cpu_min_freq, policy->cpu);
	if (freq > per_cpu(cpu_max_freq, policy->cpu))
		freq = per_cpu(cpu_max_freq, policy->cpu);
L
Linus Torvalds 已提交
88 89 90

	/*
	 * We're safe from concurrent calls to ->target() here
91
	 * as we hold the userspace_mutex lock. If we were calling
L
Linus Torvalds 已提交
92
	 * cpufreq_driver_target, a deadlock situation might occur:
93 94 95 96 97
	 * A: cpufreq_set (lock userspace_mutex) ->
	 *      cpufreq_driver_target(lock policy->lock)
	 * B: cpufreq_set_policy(lock policy->lock) ->
	 *      __cpufreq_governor ->
	 *         cpufreq_governor_userspace (lock userspace_mutex)
L
Linus Torvalds 已提交
98
	 */
99
	ret = __cpufreq_driver_target(policy, freq, CPUFREQ_RELATION_L);
L
Linus Torvalds 已提交
100 101

 err:
102
	mutex_unlock(&userspace_mutex);
L
Linus Torvalds 已提交
103 104 105 106
	return ret;
}


107
static ssize_t show_speed(struct cpufreq_policy *policy, char *buf)
L
Linus Torvalds 已提交
108
{
109
	return sprintf(buf, "%u\n", per_cpu(cpu_cur_freq, policy->cpu));
L
Linus Torvalds 已提交
110 111 112 113 114 115
}

static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
				   unsigned int event)
{
	unsigned int cpu = policy->cpu;
J
Jeff Garzik 已提交
116 117
	int rc = 0;

L
Linus Torvalds 已提交
118 119 120 121 122
	switch (event) {
	case CPUFREQ_GOV_START:
		if (!cpu_online(cpu))
			return -EINVAL;
		BUG_ON(!policy->cur);
123
		mutex_lock(&userspace_mutex);
J
Jeff Garzik 已提交
124

125 126 127 128 129 130 131
		if (cpus_using_userspace_governor == 0) {
			cpufreq_register_notifier(
					&userspace_cpufreq_notifier_block,
					CPUFREQ_TRANSITION_NOTIFIER);
		}
		cpus_using_userspace_governor++;

132 133 134 135 136 137 138 139 140 141 142
		per_cpu(cpu_is_managed, cpu) = 1;
		per_cpu(cpu_min_freq, cpu) = policy->min;
		per_cpu(cpu_max_freq, cpu) = policy->max;
		per_cpu(cpu_cur_freq, cpu) = policy->cur;
		per_cpu(cpu_set_freq, cpu) = policy->cur;
		dprintk("managing cpu %u started "
			"(%u - %u kHz, currently %u kHz)\n",
				cpu,
				per_cpu(cpu_min_freq, cpu),
				per_cpu(cpu_max_freq, cpu),
				per_cpu(cpu_cur_freq, cpu));
143

144
		mutex_unlock(&userspace_mutex);
L
Linus Torvalds 已提交
145 146
		break;
	case CPUFREQ_GOV_STOP:
147
		mutex_lock(&userspace_mutex);
148 149 150 151 152 153 154
		cpus_using_userspace_governor--;
		if (cpus_using_userspace_governor == 0) {
			cpufreq_unregister_notifier(
					&userspace_cpufreq_notifier_block,
					CPUFREQ_TRANSITION_NOTIFIER);
		}

155 156 157 158
		per_cpu(cpu_is_managed, cpu) = 0;
		per_cpu(cpu_min_freq, cpu) = 0;
		per_cpu(cpu_max_freq, cpu) = 0;
		per_cpu(cpu_set_freq, cpu) = 0;
L
Linus Torvalds 已提交
159
		dprintk("managing cpu %u stopped\n", cpu);
160
		mutex_unlock(&userspace_mutex);
L
Linus Torvalds 已提交
161 162
		break;
	case CPUFREQ_GOV_LIMITS:
163
		mutex_lock(&userspace_mutex);
164
		dprintk("limit event for cpu %u: %u - %u kHz, "
165 166
			"currently %u kHz, last set to %u kHz\n",
			cpu, policy->min, policy->max,
167 168 169
			per_cpu(cpu_cur_freq, cpu),
			per_cpu(cpu_set_freq, cpu));
		if (policy->max < per_cpu(cpu_set_freq, cpu)) {
170 171
			__cpufreq_driver_target(policy, policy->max,
						CPUFREQ_RELATION_H);
172
		} else if (policy->min > per_cpu(cpu_set_freq, cpu)) {
173 174
			__cpufreq_driver_target(policy, policy->min,
						CPUFREQ_RELATION_L);
175 176 177
		} else {
			__cpufreq_driver_target(policy,
						per_cpu(cpu_set_freq, cpu),
178 179
						CPUFREQ_RELATION_L);
		}
180 181 182
		per_cpu(cpu_min_freq, cpu) = policy->min;
		per_cpu(cpu_max_freq, cpu) = policy->max;
		per_cpu(cpu_cur_freq, cpu) = policy->cur;
183
		mutex_unlock(&userspace_mutex);
L
Linus Torvalds 已提交
184 185
		break;
	}
J
Jeff Garzik 已提交
186
	return rc;
L
Linus Torvalds 已提交
187 188 189
}


190 191 192
#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE
static
#endif
L
Linus Torvalds 已提交
193 194 195
struct cpufreq_governor cpufreq_gov_userspace = {
	.name		= "userspace",
	.governor	= cpufreq_governor_userspace,
196 197
	.store_setspeed	= cpufreq_set,
	.show_setspeed	= show_speed,
L
Linus Torvalds 已提交
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
	.owner		= THIS_MODULE,
};

static int __init cpufreq_gov_userspace_init(void)
{
	return cpufreq_register_governor(&cpufreq_gov_userspace);
}


static void __exit cpufreq_gov_userspace_exit(void)
{
	cpufreq_unregister_governor(&cpufreq_gov_userspace);
}


213 214 215 216
MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>, "
		"Russell King <rmk@arm.linux.org.uk>");
MODULE_DESCRIPTION("CPUfreq policy governor 'userspace'");
MODULE_LICENSE("GPL");
L
Linus Torvalds 已提交
217

218
#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE
L
Linus Torvalds 已提交
219
fs_initcall(cpufreq_gov_userspace_init);
220 221 222
#else
module_init(cpufreq_gov_userspace_init);
#endif
L
Linus Torvalds 已提交
223
module_exit(cpufreq_gov_userspace_exit);