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 */ 33 static unsigned int cpu_max_freq[NR_CPUS]; 34 static unsigned int cpu_min_freq[NR_CPUS]; 35 static unsigned int cpu_cur_freq[NR_CPUS]; /* current CPU freq */ 36 static unsigned int cpu_set_freq[NR_CPUS]; /* CPU freq desired by userspace */ 37 static unsigned int cpu_is_managed[NR_CPUS]; 38 39 static DEFINE_MUTEX (userspace_mutex); 40 static 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 */ 45 static int 46 userspace_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 61 static 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 * @freq: target frequency in kHz 69 * @cpu: CPU for which the frequency is to be set 70 * 71 * Sets the CPU frequency to freq. 72 */ 73 static int cpufreq_set(unsigned int freq, struct cpufreq_policy *policy) 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 105 /************************** sysfs interface ************************/ 106 static ssize_t show_speed (struct cpufreq_policy *policy, char *buf) 107 { 108 return sprintf (buf, "%u\n", cpu_cur_freq[policy->cpu]); 109 } 110 111 static ssize_t 112 store_speed (struct cpufreq_policy *policy, const char *buf, size_t count) 113 { 114 unsigned int freq = 0; 115 unsigned int ret; 116 117 ret = sscanf (buf, "%u", &freq); 118 if (ret != 1) 119 return -EINVAL; 120 121 cpufreq_set(freq, policy); 122 123 return count; 124 } 125 126 static struct freq_attr freq_attr_scaling_setspeed = 127 { 128 .attr = { .name = "scaling_setspeed", .mode = 0644 }, 129 .show = show_speed, 130 .store = store_speed, 131 }; 132 133 static int cpufreq_governor_userspace(struct cpufreq_policy *policy, 134 unsigned int event) 135 { 136 unsigned int cpu = policy->cpu; 137 int rc = 0; 138 139 switch (event) { 140 case CPUFREQ_GOV_START: 141 if (!cpu_online(cpu)) 142 return -EINVAL; 143 BUG_ON(!policy->cur); 144 mutex_lock(&userspace_mutex); 145 rc = sysfs_create_file (&policy->kobj, 146 &freq_attr_scaling_setspeed.attr); 147 if (rc) 148 goto start_out; 149 150 if (cpus_using_userspace_governor == 0) { 151 cpufreq_register_notifier( 152 &userspace_cpufreq_notifier_block, 153 CPUFREQ_TRANSITION_NOTIFIER); 154 } 155 cpus_using_userspace_governor++; 156 157 cpu_is_managed[cpu] = 1; 158 cpu_min_freq[cpu] = policy->min; 159 cpu_max_freq[cpu] = policy->max; 160 cpu_cur_freq[cpu] = policy->cur; 161 cpu_set_freq[cpu] = policy->cur; 162 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]); 163 start_out: 164 mutex_unlock(&userspace_mutex); 165 break; 166 case CPUFREQ_GOV_STOP: 167 mutex_lock(&userspace_mutex); 168 cpus_using_userspace_governor--; 169 if (cpus_using_userspace_governor == 0) { 170 cpufreq_unregister_notifier( 171 &userspace_cpufreq_notifier_block, 172 CPUFREQ_TRANSITION_NOTIFIER); 173 } 174 175 cpu_is_managed[cpu] = 0; 176 cpu_min_freq[cpu] = 0; 177 cpu_max_freq[cpu] = 0; 178 cpu_set_freq[cpu] = 0; 179 sysfs_remove_file (&policy->kobj, &freq_attr_scaling_setspeed.attr); 180 dprintk("managing cpu %u stopped\n", cpu); 181 mutex_unlock(&userspace_mutex); 182 break; 183 case CPUFREQ_GOV_LIMITS: 184 mutex_lock(&userspace_mutex); 185 dprintk("limit event for cpu %u: %u - %u kHz," 186 "currently %u kHz, last set to %u kHz\n", 187 cpu, policy->min, policy->max, 188 cpu_cur_freq[cpu], cpu_set_freq[cpu]); 189 if (policy->max < cpu_set_freq[cpu]) { 190 __cpufreq_driver_target(policy, policy->max, 191 CPUFREQ_RELATION_H); 192 } 193 else if (policy->min > cpu_set_freq[cpu]) { 194 __cpufreq_driver_target(policy, policy->min, 195 CPUFREQ_RELATION_L); 196 } 197 else { 198 __cpufreq_driver_target(policy, cpu_set_freq[cpu], 199 CPUFREQ_RELATION_L); 200 } 201 cpu_min_freq[cpu] = policy->min; 202 cpu_max_freq[cpu] = policy->max; 203 cpu_cur_freq[cpu] = policy->cur; 204 mutex_unlock(&userspace_mutex); 205 break; 206 } 207 return rc; 208 } 209 210 211 struct cpufreq_governor cpufreq_gov_userspace = { 212 .name = "userspace", 213 .governor = cpufreq_governor_userspace, 214 .owner = THIS_MODULE, 215 }; 216 EXPORT_SYMBOL(cpufreq_gov_userspace); 217 218 static int __init cpufreq_gov_userspace_init(void) 219 { 220 return cpufreq_register_governor(&cpufreq_gov_userspace); 221 } 222 223 224 static void __exit cpufreq_gov_userspace_exit(void) 225 { 226 cpufreq_unregister_governor(&cpufreq_gov_userspace); 227 } 228 229 230 MODULE_AUTHOR ("Dominik Brodowski <linux@brodo.de>, Russell King <rmk@arm.linux.org.uk>"); 231 MODULE_DESCRIPTION ("CPUfreq policy governor 'userspace'"); 232 MODULE_LICENSE ("GPL"); 233 234 fs_initcall(cpufreq_gov_userspace_init); 235 module_exit(cpufreq_gov_userspace_exit); 236