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 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 15 16 #include <linux/kernel.h> 17 #include <linux/module.h> 18 #include <linux/smp.h> 19 #include <linux/init.h> 20 #include <linux/spinlock.h> 21 #include <linux/interrupt.h> 22 #include <linux/cpufreq.h> 23 #include <linux/cpu.h> 24 #include <linux/types.h> 25 #include <linux/fs.h> 26 #include <linux/sysfs.h> 27 #include <linux/mutex.h> 28 29 /** 30 * A few values needed by the userspace governor 31 */ 32 static DEFINE_PER_CPU(unsigned int, cpu_max_freq); 33 static DEFINE_PER_CPU(unsigned int, cpu_min_freq); 34 static DEFINE_PER_CPU(unsigned int, cpu_cur_freq); /* current CPU freq */ 35 static DEFINE_PER_CPU(unsigned int, cpu_set_freq); /* CPU freq desired by 36 userspace */ 37 static DEFINE_PER_CPU(unsigned int, cpu_is_managed); 38 39 static DEFINE_MUTEX(userspace_mutex); 40 static int cpus_using_userspace_governor; 41 42 /* keep track of frequency transitions */ 43 static int 44 userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val, 45 void *data) 46 { 47 struct cpufreq_freqs *freq = data; 48 49 if (!per_cpu(cpu_is_managed, freq->cpu)) 50 return 0; 51 52 if (val == CPUFREQ_POSTCHANGE) { 53 pr_debug("saving cpu_cur_freq of cpu %u to be %u kHz\n", 54 freq->cpu, freq->new); 55 per_cpu(cpu_cur_freq, freq->cpu) = freq->new; 56 } 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 * @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 */ 73 static int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq) 74 { 75 int ret = -EINVAL; 76 77 pr_debug("cpufreq_set for cpu %u, freq %u kHz\n", policy->cpu, freq); 78 79 mutex_lock(&userspace_mutex); 80 if (!per_cpu(cpu_is_managed, policy->cpu)) 81 goto err; 82 83 per_cpu(cpu_set_freq, policy->cpu) = freq; 84 85 if (freq < per_cpu(cpu_min_freq, policy->cpu)) 86 freq = per_cpu(cpu_min_freq, policy->cpu); 87 if (freq > per_cpu(cpu_max_freq, policy->cpu)) 88 freq = per_cpu(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) -> 95 * cpufreq_driver_target(lock policy->lock) 96 * B: cpufreq_set_policy(lock policy->lock) -> 97 * __cpufreq_governor -> 98 * cpufreq_governor_userspace (lock userspace_mutex) 99 */ 100 ret = __cpufreq_driver_target(policy, freq, CPUFREQ_RELATION_L); 101 102 err: 103 mutex_unlock(&userspace_mutex); 104 return ret; 105 } 106 107 108 static ssize_t show_speed(struct cpufreq_policy *policy, char *buf) 109 { 110 return sprintf(buf, "%u\n", per_cpu(cpu_cur_freq, policy->cpu)); 111 } 112 113 static int cpufreq_governor_userspace(struct cpufreq_policy *policy, 114 unsigned int event) 115 { 116 unsigned int cpu = policy->cpu; 117 int rc = 0; 118 119 switch (event) { 120 case CPUFREQ_GOV_START: 121 if (!cpu_online(cpu)) 122 return -EINVAL; 123 BUG_ON(!policy->cur); 124 mutex_lock(&userspace_mutex); 125 126 if (cpus_using_userspace_governor == 0) { 127 cpufreq_register_notifier( 128 &userspace_cpufreq_notifier_block, 129 CPUFREQ_TRANSITION_NOTIFIER); 130 } 131 cpus_using_userspace_governor++; 132 133 per_cpu(cpu_is_managed, cpu) = 1; 134 per_cpu(cpu_min_freq, cpu) = policy->min; 135 per_cpu(cpu_max_freq, cpu) = policy->max; 136 per_cpu(cpu_cur_freq, cpu) = policy->cur; 137 per_cpu(cpu_set_freq, cpu) = policy->cur; 138 pr_debug("managing cpu %u started " 139 "(%u - %u kHz, currently %u kHz)\n", 140 cpu, 141 per_cpu(cpu_min_freq, cpu), 142 per_cpu(cpu_max_freq, cpu), 143 per_cpu(cpu_cur_freq, cpu)); 144 145 mutex_unlock(&userspace_mutex); 146 break; 147 case CPUFREQ_GOV_STOP: 148 mutex_lock(&userspace_mutex); 149 cpus_using_userspace_governor--; 150 if (cpus_using_userspace_governor == 0) { 151 cpufreq_unregister_notifier( 152 &userspace_cpufreq_notifier_block, 153 CPUFREQ_TRANSITION_NOTIFIER); 154 } 155 156 per_cpu(cpu_is_managed, cpu) = 0; 157 per_cpu(cpu_min_freq, cpu) = 0; 158 per_cpu(cpu_max_freq, cpu) = 0; 159 per_cpu(cpu_set_freq, cpu) = 0; 160 pr_debug("managing cpu %u stopped\n", cpu); 161 mutex_unlock(&userspace_mutex); 162 break; 163 case CPUFREQ_GOV_LIMITS: 164 mutex_lock(&userspace_mutex); 165 pr_debug("limit event for cpu %u: %u - %u kHz, " 166 "currently %u kHz, last set to %u kHz\n", 167 cpu, policy->min, policy->max, 168 per_cpu(cpu_cur_freq, cpu), 169 per_cpu(cpu_set_freq, cpu)); 170 if (policy->max < per_cpu(cpu_set_freq, cpu)) { 171 __cpufreq_driver_target(policy, policy->max, 172 CPUFREQ_RELATION_H); 173 } else if (policy->min > per_cpu(cpu_set_freq, cpu)) { 174 __cpufreq_driver_target(policy, policy->min, 175 CPUFREQ_RELATION_L); 176 } else { 177 __cpufreq_driver_target(policy, 178 per_cpu(cpu_set_freq, cpu), 179 CPUFREQ_RELATION_L); 180 } 181 per_cpu(cpu_min_freq, cpu) = policy->min; 182 per_cpu(cpu_max_freq, cpu) = policy->max; 183 per_cpu(cpu_cur_freq, cpu) = policy->cur; 184 mutex_unlock(&userspace_mutex); 185 break; 186 } 187 return rc; 188 } 189 190 191 #ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE 192 static 193 #endif 194 struct cpufreq_governor cpufreq_gov_userspace = { 195 .name = "userspace", 196 .governor = cpufreq_governor_userspace, 197 .store_setspeed = cpufreq_set, 198 .show_setspeed = show_speed, 199 .owner = THIS_MODULE, 200 }; 201 202 static int __init cpufreq_gov_userspace_init(void) 203 { 204 return cpufreq_register_governor(&cpufreq_gov_userspace); 205 } 206 207 208 static void __exit cpufreq_gov_userspace_exit(void) 209 { 210 cpufreq_unregister_governor(&cpufreq_gov_userspace); 211 } 212 213 214 MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>, " 215 "Russell King <rmk@arm.linux.org.uk>"); 216 MODULE_DESCRIPTION("CPUfreq policy governor 'userspace'"); 217 MODULE_LICENSE("GPL"); 218 219 #ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE 220 fs_initcall(cpufreq_gov_userspace_init); 221 #else 222 module_init(cpufreq_gov_userspace_init); 223 #endif 224 module_exit(cpufreq_gov_userspace_exit); 225