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