at v3.6-rc2 222 lines 6.1 kB view raw
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 */ 30static DEFINE_PER_CPU(unsigned int, cpu_max_freq); 31static DEFINE_PER_CPU(unsigned int, cpu_min_freq); 32static DEFINE_PER_CPU(unsigned int, cpu_cur_freq); /* current CPU freq */ 33static DEFINE_PER_CPU(unsigned int, cpu_set_freq); /* CPU freq desired by 34 userspace */ 35static DEFINE_PER_CPU(unsigned int, cpu_is_managed); 36 37static DEFINE_MUTEX(userspace_mutex); 38static int cpus_using_userspace_governor; 39 40/* keep track of frequency transitions */ 41static int 42userspace_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 59static 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 */ 71static 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 106static 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 111static 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 190static 191#endif 192struct 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 200static int __init cpufreq_gov_userspace_init(void) 201{ 202 return cpufreq_register_governor(&cpufreq_gov_userspace); 203} 204 205 206static void __exit cpufreq_gov_userspace_exit(void) 207{ 208 cpufreq_unregister_governor(&cpufreq_gov_userspace); 209} 210 211 212MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>, " 213 "Russell King <rmk@arm.linux.org.uk>"); 214MODULE_DESCRIPTION("CPUfreq policy governor 'userspace'"); 215MODULE_LICENSE("GPL"); 216 217#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE 218fs_initcall(cpufreq_gov_userspace_init); 219#else 220module_init(cpufreq_gov_userspace_init); 221#endif 222module_exit(cpufreq_gov_userspace_exit);