linux/drivers/cpufreq/cpufreq_userspace.c
<<
>>
Prefs
   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);
 223
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.