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 DEFINE_PER_CPU(unsigned int, cpu_max_freq); 34 static DEFINE_PER_CPU(unsigned int, cpu_min_freq); 35 static DEFINE_PER_CPU(unsigned int, cpu_cur_freq); /* current CPU freq */ 36 static DEFINE_PER_CPU(unsigned int, cpu_set_freq); /* CPU freq desired by 37 userspace */ 38 static DEFINE_PER_CPU(unsigned int, cpu_is_managed); 39 40 static DEFINE_MUTEX (userspace_mutex); 41 static int cpus_using_userspace_governor; 42 43 #define dprintk(msg...) \ 44 cpufreq_debug_printk(CPUFREQ_DEBUG_GOVERNOR, "userspace", msg) 45 46 /* keep track of frequency transitions */ 47 static int 48 userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val, 49 void *data) 50 { 51 struct cpufreq_freqs *freq = data; 52 53 if (!per_cpu(cpu_is_managed, freq->cpu)) 54 return 0; 55 56 dprintk("saving cpu_cur_freq of cpu %u to be %u kHz\n", 57 freq->cpu, freq->new); 58 per_cpu(cpu_cur_freq, freq->cpu) = freq->new; 59 60 return 0; 61 } 62 63 static struct notifier_block userspace_cpufreq_notifier_block = { 64 .notifier_call = userspace_cpufreq_notifier 65 }; 66 67 68 /** 69 * cpufreq_set - set the CPU frequency 70 * @policy: pointer to policy struct where freq is being set 71 * @freq: target frequency in kHz 72 * 73 * Sets the CPU frequency to freq. 74 */ 75 static int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq) 76 { 77 int ret = -EINVAL; 78 79 dprintk("cpufreq_set for cpu %u, freq %u kHz\n", policy->cpu, freq); 80 81 mutex_lock(&userspace_mutex); 82 if (!per_cpu(cpu_is_managed, policy->cpu)) 83 goto err; 84 85 per_cpu(cpu_set_freq, policy->cpu) = freq; 86 87 if (freq < per_cpu(cpu_min_freq, policy->cpu)) 88 freq = per_cpu(cpu_min_freq, policy->cpu); 89 if (freq > per_cpu(cpu_max_freq, policy->cpu)) 90 freq = per_cpu(cpu_max_freq, policy->cpu); 91 92 /* 93 * We're safe from concurrent calls to ->target() here 94 * as we hold the userspace_mutex lock. If we were calling 95 * cpufreq_driver_target, a deadlock situation might occur: 96 * A: cpufreq_set (lock userspace_mutex) -> cpufreq_driver_target(lock policy->lock) 97 * B: cpufreq_set_policy(lock policy->lock) -> __cpufreq_governor -> cpufreq_governor_userspace (lock userspace_mutex) 98 */ 99 ret = __cpufreq_driver_target(policy, freq, CPUFREQ_RELATION_L); 100 101 err: 102 mutex_unlock(&userspace_mutex); 103 return ret; 104 } 105 106 107 static ssize_t show_speed(struct cpufreq_policy *policy, char *buf) 108 { 109 return sprintf(buf, "%u\n", per_cpu(cpu_cur_freq, policy->cpu)); 110 } 111 112 static int cpufreq_governor_userspace(struct cpufreq_policy *policy, 113 unsigned int event) 114 { 115 unsigned int cpu = policy->cpu; 116 int rc = 0; 117 118 switch (event) { 119 case CPUFREQ_GOV_START: 120 if (!cpu_online(cpu)) 121 return -EINVAL; 122 BUG_ON(!policy->cur); 123 mutex_lock(&userspace_mutex); 124 125 if (cpus_using_userspace_governor == 0) { 126 cpufreq_register_notifier( 127 &userspace_cpufreq_notifier_block, 128 CPUFREQ_TRANSITION_NOTIFIER); 129 } 130 cpus_using_userspace_governor++; 131 132 per_cpu(cpu_is_managed, cpu) = 1; 133 per_cpu(cpu_min_freq, cpu) = policy->min; 134 per_cpu(cpu_max_freq, cpu) = policy->max; 135 per_cpu(cpu_cur_freq, cpu) = policy->cur; 136 per_cpu(cpu_set_freq, cpu) = policy->cur; 137 dprintk("managing cpu %u started " 138 "(%u - %u kHz, currently %u kHz)\n", 139 cpu, 140 per_cpu(cpu_min_freq, cpu), 141 per_cpu(cpu_max_freq, cpu), 142 per_cpu(cpu_cur_freq, cpu)); 143 144 mutex_unlock(&userspace_mutex); 145 break; 146 case CPUFREQ_GOV_STOP: 147 mutex_lock(&userspace_mutex); 148 cpus_using_userspace_governor--; 149 if (cpus_using_userspace_governor == 0) { 150 cpufreq_unregister_notifier( 151 &userspace_cpufreq_notifier_block, 152 CPUFREQ_TRANSITION_NOTIFIER); 153 } 154 155 per_cpu(cpu_is_managed, cpu) = 0; 156 per_cpu(cpu_min_freq, cpu) = 0; 157 per_cpu(cpu_max_freq, cpu) = 0; 158 per_cpu(cpu_set_freq, cpu) = 0; 159 dprintk("managing cpu %u stopped\n", cpu); 160 mutex_unlock(&userspace_mutex); 161 break; 162 case CPUFREQ_GOV_LIMITS: 163 mutex_lock(&userspace_mutex); 164 dprintk("limit event for cpu %u: %u - %u kHz, " 165 "currently %u kHz, last set to %u kHz\n", 166 cpu, policy->min, policy->max, 167 per_cpu(cpu_cur_freq, cpu), 168 per_cpu(cpu_set_freq, cpu)); 169 if (policy->max < per_cpu(cpu_set_freq, cpu)) { 170 __cpufreq_driver_target(policy, policy->max, 171 CPUFREQ_RELATION_H); 172 } else if (policy->min > per_cpu(cpu_set_freq, cpu)) { 173 __cpufreq_driver_target(policy, policy->min, 174 CPUFREQ_RELATION_L); 175 } else { 176 __cpufreq_driver_target(policy, 177 per_cpu(cpu_set_freq, cpu), 178 CPUFREQ_RELATION_L); 179 } 180 per_cpu(cpu_min_freq, cpu) = policy->min; 181 per_cpu(cpu_max_freq, cpu) = policy->max; 182 per_cpu(cpu_cur_freq, cpu) = policy->cur; 183 mutex_unlock(&userspace_mutex); 184 break; 185 } 186 return rc; 187 } 188 189 190 #ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE 191 static 192 #endif 193 struct cpufreq_governor cpufreq_gov_userspace = { 194 .name = "userspace", 195 .governor = cpufreq_governor_userspace, 196 .store_setspeed = cpufreq_set, 197 .show_setspeed = show_speed, 198 .owner = THIS_MODULE, 199 }; 200 201 static int __init cpufreq_gov_userspace_init(void) 202 { 203 return cpufreq_register_governor(&cpufreq_gov_userspace); 204 } 205 206 207 static void __exit cpufreq_gov_userspace_exit(void) 208 { 209 cpufreq_unregister_governor(&cpufreq_gov_userspace); 210 } 211 212 213 MODULE_AUTHOR ("Dominik Brodowski <linux@brodo.de>, Russell King <rmk@arm.linux.org.uk>"); 214 MODULE_DESCRIPTION ("CPUfreq policy governor 'userspace'"); 215 MODULE_LICENSE ("GPL"); 216 217 #ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE 218 fs_initcall(cpufreq_gov_userspace_init); 219 #else 220 module_init(cpufreq_gov_userspace_init); 221 #endif 222 module_exit(cpufreq_gov_userspace_exit); 223