cpufreq_userspace.c revision 9e76988e9390a4ff4d171f690586d0c58186b47e
1 2/* 3 * linux/drivers/cpufreq/cpufreq_userspace.c 4 * 5 * Copyright (C) 2001 Russell King 6 * (C) 2002 - 2004 Dominik Brodowski <linux@brodo.de> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 * 12 */ 13 14#include <linux/kernel.h> 15#include <linux/module.h> 16#include <linux/smp.h> 17#include <linux/init.h> 18#include <linux/spinlock.h> 19#include <linux/interrupt.h> 20#include <linux/cpufreq.h> 21#include <linux/cpu.h> 22#include <linux/types.h> 23#include <linux/fs.h> 24#include <linux/sysfs.h> 25#include <linux/mutex.h> 26 27#include <asm/uaccess.h> 28 29 30/** 31 * A few values needed by the userspace governor 32 */ 33static unsigned int cpu_max_freq[NR_CPUS]; 34static unsigned int cpu_min_freq[NR_CPUS]; 35static unsigned int cpu_cur_freq[NR_CPUS]; /* current CPU freq */ 36static unsigned int cpu_set_freq[NR_CPUS]; /* CPU freq desired by userspace */ 37static unsigned int cpu_is_managed[NR_CPUS]; 38 39static DEFINE_MUTEX (userspace_mutex); 40static int cpus_using_userspace_governor; 41 42#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_GOVERNOR, "userspace", msg) 43 44/* keep track of frequency transitions */ 45static int 46userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val, 47 void *data) 48{ 49 struct cpufreq_freqs *freq = data; 50 51 if (!cpu_is_managed[freq->cpu]) 52 return 0; 53 54 dprintk("saving cpu_cur_freq of cpu %u to be %u kHz\n", 55 freq->cpu, freq->new); 56 cpu_cur_freq[freq->cpu] = freq->new; 57 58 return 0; 59} 60 61static struct notifier_block userspace_cpufreq_notifier_block = { 62 .notifier_call = userspace_cpufreq_notifier 63}; 64 65 66/** 67 * cpufreq_set - set the CPU frequency 68 * @policy: pointer to policy struct where freq is being set 69 * @freq: target frequency in kHz 70 * 71 * Sets the CPU frequency to freq. 72 */ 73static int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq) 74{ 75 int ret = -EINVAL; 76 77 dprintk("cpufreq_set for cpu %u, freq %u kHz\n", policy->cpu, freq); 78 79 mutex_lock(&userspace_mutex); 80 if (!cpu_is_managed[policy->cpu]) 81 goto err; 82 83 cpu_set_freq[policy->cpu] = freq; 84 85 if (freq < cpu_min_freq[policy->cpu]) 86 freq = cpu_min_freq[policy->cpu]; 87 if (freq > cpu_max_freq[policy->cpu]) 88 freq = cpu_max_freq[policy->cpu]; 89 90 /* 91 * We're safe from concurrent calls to ->target() here 92 * as we hold the userspace_mutex lock. If we were calling 93 * cpufreq_driver_target, a deadlock situation might occur: 94 * A: cpufreq_set (lock userspace_mutex) -> cpufreq_driver_target(lock policy->lock) 95 * B: cpufreq_set_policy(lock policy->lock) -> __cpufreq_governor -> cpufreq_governor_userspace (lock userspace_mutex) 96 */ 97 ret = __cpufreq_driver_target(policy, freq, CPUFREQ_RELATION_L); 98 99 err: 100 mutex_unlock(&userspace_mutex); 101 return ret; 102} 103 104 105static ssize_t show_speed(struct cpufreq_policy *policy, char *buf) 106{ 107 return sprintf(buf, "%u\n", cpu_cur_freq[policy->cpu]); 108} 109 110static int cpufreq_governor_userspace(struct cpufreq_policy *policy, 111 unsigned int event) 112{ 113 unsigned int cpu = policy->cpu; 114 int rc = 0; 115 116 switch (event) { 117 case CPUFREQ_GOV_START: 118 if (!cpu_online(cpu)) 119 return -EINVAL; 120 BUG_ON(!policy->cur); 121 mutex_lock(&userspace_mutex); 122 123 if (cpus_using_userspace_governor == 0) { 124 cpufreq_register_notifier( 125 &userspace_cpufreq_notifier_block, 126 CPUFREQ_TRANSITION_NOTIFIER); 127 } 128 cpus_using_userspace_governor++; 129 130 cpu_is_managed[cpu] = 1; 131 cpu_min_freq[cpu] = policy->min; 132 cpu_max_freq[cpu] = policy->max; 133 cpu_cur_freq[cpu] = policy->cur; 134 cpu_set_freq[cpu] = policy->cur; 135 dprintk("managing cpu %u started (%u - %u kHz, currently %u kHz)\n", cpu, cpu_min_freq[cpu], cpu_max_freq[cpu], cpu_cur_freq[cpu]); 136 137 mutex_unlock(&userspace_mutex); 138 break; 139 case CPUFREQ_GOV_STOP: 140 mutex_lock(&userspace_mutex); 141 cpus_using_userspace_governor--; 142 if (cpus_using_userspace_governor == 0) { 143 cpufreq_unregister_notifier( 144 &userspace_cpufreq_notifier_block, 145 CPUFREQ_TRANSITION_NOTIFIER); 146 } 147 148 cpu_is_managed[cpu] = 0; 149 cpu_min_freq[cpu] = 0; 150 cpu_max_freq[cpu] = 0; 151 cpu_set_freq[cpu] = 0; 152 dprintk("managing cpu %u stopped\n", cpu); 153 mutex_unlock(&userspace_mutex); 154 break; 155 case CPUFREQ_GOV_LIMITS: 156 mutex_lock(&userspace_mutex); 157 dprintk("limit event for cpu %u: %u - %u kHz," 158 "currently %u kHz, last set to %u kHz\n", 159 cpu, policy->min, policy->max, 160 cpu_cur_freq[cpu], cpu_set_freq[cpu]); 161 if (policy->max < cpu_set_freq[cpu]) { 162 __cpufreq_driver_target(policy, policy->max, 163 CPUFREQ_RELATION_H); 164 } 165 else if (policy->min > cpu_set_freq[cpu]) { 166 __cpufreq_driver_target(policy, policy->min, 167 CPUFREQ_RELATION_L); 168 } 169 else { 170 __cpufreq_driver_target(policy, cpu_set_freq[cpu], 171 CPUFREQ_RELATION_L); 172 } 173 cpu_min_freq[cpu] = policy->min; 174 cpu_max_freq[cpu] = policy->max; 175 cpu_cur_freq[cpu] = policy->cur; 176 mutex_unlock(&userspace_mutex); 177 break; 178 } 179 return rc; 180} 181 182 183struct cpufreq_governor cpufreq_gov_userspace = { 184 .name = "userspace", 185 .governor = cpufreq_governor_userspace, 186 .store_setspeed = cpufreq_set, 187 .show_setspeed = show_speed, 188 .owner = THIS_MODULE, 189}; 190EXPORT_SYMBOL(cpufreq_gov_userspace); 191 192static int __init cpufreq_gov_userspace_init(void) 193{ 194 return cpufreq_register_governor(&cpufreq_gov_userspace); 195} 196 197 198static void __exit cpufreq_gov_userspace_exit(void) 199{ 200 cpufreq_unregister_governor(&cpufreq_gov_userspace); 201} 202 203 204MODULE_AUTHOR ("Dominik Brodowski <linux@brodo.de>, Russell King <rmk@arm.linux.org.uk>"); 205MODULE_DESCRIPTION ("CPUfreq policy governor 'userspace'"); 206MODULE_LICENSE ("GPL"); 207 208#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE 209fs_initcall(cpufreq_gov_userspace_init); 210#else 211module_init(cpufreq_gov_userspace_init); 212#endif 213module_exit(cpufreq_gov_userspace_exit); 214