1*1da177e4SLinus Torvalds /* 2*1da177e4SLinus Torvalds * linux/drivers/cpufreq/cpufreq_userspace.c 3*1da177e4SLinus Torvalds * 4*1da177e4SLinus Torvalds * Copyright (C) 2001 Russell King 5*1da177e4SLinus Torvalds * (C) 2002 - 2004 Dominik Brodowski <linux@brodo.de> 6*1da177e4SLinus Torvalds * 7*1da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 8*1da177e4SLinus Torvalds * it under the terms of the GNU General Public License version 2 as 9*1da177e4SLinus Torvalds * published by the Free Software Foundation. 10*1da177e4SLinus Torvalds * 11*1da177e4SLinus Torvalds */ 12*1da177e4SLinus Torvalds 13*1da177e4SLinus Torvalds #include <linux/config.h> 14*1da177e4SLinus Torvalds #include <linux/kernel.h> 15*1da177e4SLinus Torvalds #include <linux/module.h> 16*1da177e4SLinus Torvalds #include <linux/smp.h> 17*1da177e4SLinus Torvalds #include <linux/init.h> 18*1da177e4SLinus Torvalds #include <linux/spinlock.h> 19*1da177e4SLinus Torvalds #include <linux/interrupt.h> 20*1da177e4SLinus Torvalds #include <linux/cpufreq.h> 21*1da177e4SLinus Torvalds #include <linux/types.h> 22*1da177e4SLinus Torvalds #include <linux/fs.h> 23*1da177e4SLinus Torvalds #include <linux/sysfs.h> 24*1da177e4SLinus Torvalds 25*1da177e4SLinus Torvalds #include <asm/uaccess.h> 26*1da177e4SLinus Torvalds 27*1da177e4SLinus Torvalds 28*1da177e4SLinus Torvalds /** 29*1da177e4SLinus Torvalds * A few values needed by the userspace governor 30*1da177e4SLinus Torvalds */ 31*1da177e4SLinus Torvalds static unsigned int cpu_max_freq[NR_CPUS]; 32*1da177e4SLinus Torvalds static unsigned int cpu_min_freq[NR_CPUS]; 33*1da177e4SLinus Torvalds static unsigned int cpu_cur_freq[NR_CPUS]; /* current CPU freq */ 34*1da177e4SLinus Torvalds static unsigned int cpu_set_freq[NR_CPUS]; /* CPU freq desired by userspace */ 35*1da177e4SLinus Torvalds static unsigned int cpu_is_managed[NR_CPUS]; 36*1da177e4SLinus Torvalds static struct cpufreq_policy current_policy[NR_CPUS]; 37*1da177e4SLinus Torvalds 38*1da177e4SLinus Torvalds static DECLARE_MUTEX (userspace_sem); 39*1da177e4SLinus Torvalds 40*1da177e4SLinus Torvalds #define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_GOVERNOR, "userspace", msg) 41*1da177e4SLinus Torvalds 42*1da177e4SLinus Torvalds /* keep track of frequency transitions */ 43*1da177e4SLinus Torvalds static int 44*1da177e4SLinus Torvalds userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val, 45*1da177e4SLinus Torvalds void *data) 46*1da177e4SLinus Torvalds { 47*1da177e4SLinus Torvalds struct cpufreq_freqs *freq = data; 48*1da177e4SLinus Torvalds 49*1da177e4SLinus Torvalds dprintk("saving cpu_cur_freq of cpu %u to be %u kHz\n", freq->cpu, freq->new); 50*1da177e4SLinus Torvalds cpu_cur_freq[freq->cpu] = freq->new; 51*1da177e4SLinus Torvalds 52*1da177e4SLinus Torvalds return 0; 53*1da177e4SLinus Torvalds } 54*1da177e4SLinus Torvalds 55*1da177e4SLinus Torvalds static struct notifier_block userspace_cpufreq_notifier_block = { 56*1da177e4SLinus Torvalds .notifier_call = userspace_cpufreq_notifier 57*1da177e4SLinus Torvalds }; 58*1da177e4SLinus Torvalds 59*1da177e4SLinus Torvalds 60*1da177e4SLinus Torvalds /** 61*1da177e4SLinus Torvalds * cpufreq_set - set the CPU frequency 62*1da177e4SLinus Torvalds * @freq: target frequency in kHz 63*1da177e4SLinus Torvalds * @cpu: CPU for which the frequency is to be set 64*1da177e4SLinus Torvalds * 65*1da177e4SLinus Torvalds * Sets the CPU frequency to freq. 66*1da177e4SLinus Torvalds */ 67*1da177e4SLinus Torvalds static int cpufreq_set(unsigned int freq, unsigned int cpu) 68*1da177e4SLinus Torvalds { 69*1da177e4SLinus Torvalds int ret = -EINVAL; 70*1da177e4SLinus Torvalds 71*1da177e4SLinus Torvalds dprintk("cpufreq_set for cpu %u, freq %u kHz\n", cpu, freq); 72*1da177e4SLinus Torvalds 73*1da177e4SLinus Torvalds down(&userspace_sem); 74*1da177e4SLinus Torvalds if (!cpu_is_managed[cpu]) 75*1da177e4SLinus Torvalds goto err; 76*1da177e4SLinus Torvalds 77*1da177e4SLinus Torvalds cpu_set_freq[cpu] = freq; 78*1da177e4SLinus Torvalds 79*1da177e4SLinus Torvalds if (freq < cpu_min_freq[cpu]) 80*1da177e4SLinus Torvalds freq = cpu_min_freq[cpu]; 81*1da177e4SLinus Torvalds if (freq > cpu_max_freq[cpu]) 82*1da177e4SLinus Torvalds freq = cpu_max_freq[cpu]; 83*1da177e4SLinus Torvalds 84*1da177e4SLinus Torvalds /* 85*1da177e4SLinus Torvalds * We're safe from concurrent calls to ->target() here 86*1da177e4SLinus Torvalds * as we hold the userspace_sem lock. If we were calling 87*1da177e4SLinus Torvalds * cpufreq_driver_target, a deadlock situation might occur: 88*1da177e4SLinus Torvalds * A: cpufreq_set (lock userspace_sem) -> cpufreq_driver_target(lock policy->lock) 89*1da177e4SLinus Torvalds * B: cpufreq_set_policy(lock policy->lock) -> __cpufreq_governor -> cpufreq_governor_userspace (lock userspace_sem) 90*1da177e4SLinus Torvalds */ 91*1da177e4SLinus Torvalds ret = __cpufreq_driver_target(¤t_policy[cpu], freq, 92*1da177e4SLinus Torvalds CPUFREQ_RELATION_L); 93*1da177e4SLinus Torvalds 94*1da177e4SLinus Torvalds err: 95*1da177e4SLinus Torvalds up(&userspace_sem); 96*1da177e4SLinus Torvalds return ret; 97*1da177e4SLinus Torvalds } 98*1da177e4SLinus Torvalds 99*1da177e4SLinus Torvalds 100*1da177e4SLinus Torvalds /************************** sysfs interface ************************/ 101*1da177e4SLinus Torvalds static ssize_t show_speed (struct cpufreq_policy *policy, char *buf) 102*1da177e4SLinus Torvalds { 103*1da177e4SLinus Torvalds return sprintf (buf, "%u\n", cpu_cur_freq[policy->cpu]); 104*1da177e4SLinus Torvalds } 105*1da177e4SLinus Torvalds 106*1da177e4SLinus Torvalds static ssize_t 107*1da177e4SLinus Torvalds store_speed (struct cpufreq_policy *policy, const char *buf, size_t count) 108*1da177e4SLinus Torvalds { 109*1da177e4SLinus Torvalds unsigned int freq = 0; 110*1da177e4SLinus Torvalds unsigned int ret; 111*1da177e4SLinus Torvalds 112*1da177e4SLinus Torvalds ret = sscanf (buf, "%u", &freq); 113*1da177e4SLinus Torvalds if (ret != 1) 114*1da177e4SLinus Torvalds return -EINVAL; 115*1da177e4SLinus Torvalds 116*1da177e4SLinus Torvalds cpufreq_set(freq, policy->cpu); 117*1da177e4SLinus Torvalds 118*1da177e4SLinus Torvalds return count; 119*1da177e4SLinus Torvalds } 120*1da177e4SLinus Torvalds 121*1da177e4SLinus Torvalds static struct freq_attr freq_attr_scaling_setspeed = 122*1da177e4SLinus Torvalds { 123*1da177e4SLinus Torvalds .attr = { .name = "scaling_setspeed", .mode = 0644, .owner = THIS_MODULE }, 124*1da177e4SLinus Torvalds .show = show_speed, 125*1da177e4SLinus Torvalds .store = store_speed, 126*1da177e4SLinus Torvalds }; 127*1da177e4SLinus Torvalds 128*1da177e4SLinus Torvalds static int cpufreq_governor_userspace(struct cpufreq_policy *policy, 129*1da177e4SLinus Torvalds unsigned int event) 130*1da177e4SLinus Torvalds { 131*1da177e4SLinus Torvalds unsigned int cpu = policy->cpu; 132*1da177e4SLinus Torvalds switch (event) { 133*1da177e4SLinus Torvalds case CPUFREQ_GOV_START: 134*1da177e4SLinus Torvalds if (!cpu_online(cpu)) 135*1da177e4SLinus Torvalds return -EINVAL; 136*1da177e4SLinus Torvalds BUG_ON(!policy->cur); 137*1da177e4SLinus Torvalds down(&userspace_sem); 138*1da177e4SLinus Torvalds cpu_is_managed[cpu] = 1; 139*1da177e4SLinus Torvalds cpu_min_freq[cpu] = policy->min; 140*1da177e4SLinus Torvalds cpu_max_freq[cpu] = policy->max; 141*1da177e4SLinus Torvalds cpu_cur_freq[cpu] = policy->cur; 142*1da177e4SLinus Torvalds cpu_set_freq[cpu] = policy->cur; 143*1da177e4SLinus Torvalds sysfs_create_file (&policy->kobj, &freq_attr_scaling_setspeed.attr); 144*1da177e4SLinus Torvalds memcpy (¤t_policy[cpu], policy, sizeof(struct cpufreq_policy)); 145*1da177e4SLinus Torvalds dprintk("managing cpu %u started (%u - %u kHz, currently %u kHz)\n", cpu, cpu_min_freq[cpu], cpu_max_freq[cpu], cpu_cur_freq[cpu]); 146*1da177e4SLinus Torvalds up(&userspace_sem); 147*1da177e4SLinus Torvalds break; 148*1da177e4SLinus Torvalds case CPUFREQ_GOV_STOP: 149*1da177e4SLinus Torvalds down(&userspace_sem); 150*1da177e4SLinus Torvalds cpu_is_managed[cpu] = 0; 151*1da177e4SLinus Torvalds cpu_min_freq[cpu] = 0; 152*1da177e4SLinus Torvalds cpu_max_freq[cpu] = 0; 153*1da177e4SLinus Torvalds cpu_set_freq[cpu] = 0; 154*1da177e4SLinus Torvalds sysfs_remove_file (&policy->kobj, &freq_attr_scaling_setspeed.attr); 155*1da177e4SLinus Torvalds dprintk("managing cpu %u stopped\n", cpu); 156*1da177e4SLinus Torvalds up(&userspace_sem); 157*1da177e4SLinus Torvalds break; 158*1da177e4SLinus Torvalds case CPUFREQ_GOV_LIMITS: 159*1da177e4SLinus Torvalds down(&userspace_sem); 160*1da177e4SLinus Torvalds cpu_min_freq[cpu] = policy->min; 161*1da177e4SLinus Torvalds cpu_max_freq[cpu] = policy->max; 162*1da177e4SLinus Torvalds dprintk("limit event for cpu %u: %u - %u kHz, currently %u kHz, last set to %u kHz\n", cpu, cpu_min_freq[cpu], cpu_max_freq[cpu], cpu_cur_freq[cpu], cpu_set_freq[cpu]); 163*1da177e4SLinus Torvalds if (policy->max < cpu_set_freq[cpu]) { 164*1da177e4SLinus Torvalds __cpufreq_driver_target(¤t_policy[cpu], policy->max, 165*1da177e4SLinus Torvalds CPUFREQ_RELATION_H); 166*1da177e4SLinus Torvalds } else if (policy->min > cpu_set_freq[cpu]) { 167*1da177e4SLinus Torvalds __cpufreq_driver_target(¤t_policy[cpu], policy->min, 168*1da177e4SLinus Torvalds CPUFREQ_RELATION_L); 169*1da177e4SLinus Torvalds } else { 170*1da177e4SLinus Torvalds __cpufreq_driver_target(¤t_policy[cpu], cpu_set_freq[cpu], 171*1da177e4SLinus Torvalds CPUFREQ_RELATION_L); 172*1da177e4SLinus Torvalds } 173*1da177e4SLinus Torvalds memcpy (¤t_policy[cpu], policy, sizeof(struct cpufreq_policy)); 174*1da177e4SLinus Torvalds up(&userspace_sem); 175*1da177e4SLinus Torvalds break; 176*1da177e4SLinus Torvalds } 177*1da177e4SLinus Torvalds return 0; 178*1da177e4SLinus Torvalds } 179*1da177e4SLinus Torvalds 180*1da177e4SLinus Torvalds 181*1da177e4SLinus Torvalds struct cpufreq_governor cpufreq_gov_userspace = { 182*1da177e4SLinus Torvalds .name = "userspace", 183*1da177e4SLinus Torvalds .governor = cpufreq_governor_userspace, 184*1da177e4SLinus Torvalds .owner = THIS_MODULE, 185*1da177e4SLinus Torvalds }; 186*1da177e4SLinus Torvalds EXPORT_SYMBOL(cpufreq_gov_userspace); 187*1da177e4SLinus Torvalds 188*1da177e4SLinus Torvalds static int __init cpufreq_gov_userspace_init(void) 189*1da177e4SLinus Torvalds { 190*1da177e4SLinus Torvalds cpufreq_register_notifier(&userspace_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); 191*1da177e4SLinus Torvalds return cpufreq_register_governor(&cpufreq_gov_userspace); 192*1da177e4SLinus Torvalds } 193*1da177e4SLinus Torvalds 194*1da177e4SLinus Torvalds 195*1da177e4SLinus Torvalds static void __exit cpufreq_gov_userspace_exit(void) 196*1da177e4SLinus Torvalds { 197*1da177e4SLinus Torvalds cpufreq_unregister_governor(&cpufreq_gov_userspace); 198*1da177e4SLinus Torvalds cpufreq_unregister_notifier(&userspace_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); 199*1da177e4SLinus Torvalds } 200*1da177e4SLinus Torvalds 201*1da177e4SLinus Torvalds 202*1da177e4SLinus Torvalds MODULE_AUTHOR ("Dominik Brodowski <linux@brodo.de>, Russell King <rmk@arm.linux.org.uk>"); 203*1da177e4SLinus Torvalds MODULE_DESCRIPTION ("CPUfreq policy governor 'userspace'"); 204*1da177e4SLinus Torvalds MODULE_LICENSE ("GPL"); 205*1da177e4SLinus Torvalds 206*1da177e4SLinus Torvalds fs_initcall(cpufreq_gov_userspace_init); 207*1da177e4SLinus Torvalds module_exit(cpufreq_gov_userspace_exit); 208