Path: blob/master/drivers/cpufreq/cpufreq_userspace.c
15109 views
1/*2* linux/drivers/cpufreq/cpufreq_userspace.c3*4* Copyright (C) 2001 Russell King5* (C) 2002 - 2004 Dominik Brodowski <[email protected]>6*7* This program is free software; you can redistribute it and/or modify8* it under the terms of the GNU General Public License version 2 as9* published by the Free Software Foundation.10*11*/1213#include <linux/kernel.h>14#include <linux/module.h>15#include <linux/smp.h>16#include <linux/init.h>17#include <linux/spinlock.h>18#include <linux/interrupt.h>19#include <linux/cpufreq.h>20#include <linux/cpu.h>21#include <linux/types.h>22#include <linux/fs.h>23#include <linux/sysfs.h>24#include <linux/mutex.h>2526/**27* A few values needed by the userspace governor28*/29static DEFINE_PER_CPU(unsigned int, cpu_max_freq);30static DEFINE_PER_CPU(unsigned int, cpu_min_freq);31static DEFINE_PER_CPU(unsigned int, cpu_cur_freq); /* current CPU freq */32static DEFINE_PER_CPU(unsigned int, cpu_set_freq); /* CPU freq desired by33userspace */34static DEFINE_PER_CPU(unsigned int, cpu_is_managed);3536static DEFINE_MUTEX(userspace_mutex);37static int cpus_using_userspace_governor;3839/* keep track of frequency transitions */40static int41userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val,42void *data)43{44struct cpufreq_freqs *freq = data;4546if (!per_cpu(cpu_is_managed, freq->cpu))47return 0;4849pr_debug("saving cpu_cur_freq of cpu %u to be %u kHz\n",50freq->cpu, freq->new);51per_cpu(cpu_cur_freq, freq->cpu) = freq->new;5253return 0;54}5556static struct notifier_block userspace_cpufreq_notifier_block = {57.notifier_call = userspace_cpufreq_notifier58};596061/**62* cpufreq_set - set the CPU frequency63* @policy: pointer to policy struct where freq is being set64* @freq: target frequency in kHz65*66* Sets the CPU frequency to freq.67*/68static int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq)69{70int ret = -EINVAL;7172pr_debug("cpufreq_set for cpu %u, freq %u kHz\n", policy->cpu, freq);7374mutex_lock(&userspace_mutex);75if (!per_cpu(cpu_is_managed, policy->cpu))76goto err;7778per_cpu(cpu_set_freq, policy->cpu) = freq;7980if (freq < per_cpu(cpu_min_freq, policy->cpu))81freq = per_cpu(cpu_min_freq, policy->cpu);82if (freq > per_cpu(cpu_max_freq, policy->cpu))83freq = per_cpu(cpu_max_freq, policy->cpu);8485/*86* We're safe from concurrent calls to ->target() here87* as we hold the userspace_mutex lock. If we were calling88* cpufreq_driver_target, a deadlock situation might occur:89* A: cpufreq_set (lock userspace_mutex) ->90* cpufreq_driver_target(lock policy->lock)91* B: cpufreq_set_policy(lock policy->lock) ->92* __cpufreq_governor ->93* cpufreq_governor_userspace (lock userspace_mutex)94*/95ret = __cpufreq_driver_target(policy, freq, CPUFREQ_RELATION_L);9697err:98mutex_unlock(&userspace_mutex);99return ret;100}101102103static ssize_t show_speed(struct cpufreq_policy *policy, char *buf)104{105return sprintf(buf, "%u\n", per_cpu(cpu_cur_freq, policy->cpu));106}107108static int cpufreq_governor_userspace(struct cpufreq_policy *policy,109unsigned int event)110{111unsigned int cpu = policy->cpu;112int rc = 0;113114switch (event) {115case CPUFREQ_GOV_START:116if (!cpu_online(cpu))117return -EINVAL;118BUG_ON(!policy->cur);119mutex_lock(&userspace_mutex);120121if (cpus_using_userspace_governor == 0) {122cpufreq_register_notifier(123&userspace_cpufreq_notifier_block,124CPUFREQ_TRANSITION_NOTIFIER);125}126cpus_using_userspace_governor++;127128per_cpu(cpu_is_managed, cpu) = 1;129per_cpu(cpu_min_freq, cpu) = policy->min;130per_cpu(cpu_max_freq, cpu) = policy->max;131per_cpu(cpu_cur_freq, cpu) = policy->cur;132per_cpu(cpu_set_freq, cpu) = policy->cur;133pr_debug("managing cpu %u started "134"(%u - %u kHz, currently %u kHz)\n",135cpu,136per_cpu(cpu_min_freq, cpu),137per_cpu(cpu_max_freq, cpu),138per_cpu(cpu_cur_freq, cpu));139140mutex_unlock(&userspace_mutex);141break;142case CPUFREQ_GOV_STOP:143mutex_lock(&userspace_mutex);144cpus_using_userspace_governor--;145if (cpus_using_userspace_governor == 0) {146cpufreq_unregister_notifier(147&userspace_cpufreq_notifier_block,148CPUFREQ_TRANSITION_NOTIFIER);149}150151per_cpu(cpu_is_managed, cpu) = 0;152per_cpu(cpu_min_freq, cpu) = 0;153per_cpu(cpu_max_freq, cpu) = 0;154per_cpu(cpu_set_freq, cpu) = 0;155pr_debug("managing cpu %u stopped\n", cpu);156mutex_unlock(&userspace_mutex);157break;158case CPUFREQ_GOV_LIMITS:159mutex_lock(&userspace_mutex);160pr_debug("limit event for cpu %u: %u - %u kHz, "161"currently %u kHz, last set to %u kHz\n",162cpu, policy->min, policy->max,163per_cpu(cpu_cur_freq, cpu),164per_cpu(cpu_set_freq, cpu));165if (policy->max < per_cpu(cpu_set_freq, cpu)) {166__cpufreq_driver_target(policy, policy->max,167CPUFREQ_RELATION_H);168} else if (policy->min > per_cpu(cpu_set_freq, cpu)) {169__cpufreq_driver_target(policy, policy->min,170CPUFREQ_RELATION_L);171} else {172__cpufreq_driver_target(policy,173per_cpu(cpu_set_freq, cpu),174CPUFREQ_RELATION_L);175}176per_cpu(cpu_min_freq, cpu) = policy->min;177per_cpu(cpu_max_freq, cpu) = policy->max;178per_cpu(cpu_cur_freq, cpu) = policy->cur;179mutex_unlock(&userspace_mutex);180break;181}182return rc;183}184185186#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE187static188#endif189struct cpufreq_governor cpufreq_gov_userspace = {190.name = "userspace",191.governor = cpufreq_governor_userspace,192.store_setspeed = cpufreq_set,193.show_setspeed = show_speed,194.owner = THIS_MODULE,195};196197static int __init cpufreq_gov_userspace_init(void)198{199return cpufreq_register_governor(&cpufreq_gov_userspace);200}201202203static void __exit cpufreq_gov_userspace_exit(void)204{205cpufreq_unregister_governor(&cpufreq_gov_userspace);206}207208209MODULE_AUTHOR("Dominik Brodowski <[email protected]>, "210"Russell King <[email protected]>");211MODULE_DESCRIPTION("CPUfreq policy governor 'userspace'");212MODULE_LICENSE("GPL");213214#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE215fs_initcall(cpufreq_gov_userspace_init);216#else217module_init(cpufreq_gov_userspace_init);218#endif219module_exit(cpufreq_gov_userspace_exit);220221222