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