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#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 */
  32static DEFINE_PER_CPU(unsigned int, cpu_max_freq);
  33static DEFINE_PER_CPU(unsigned int, cpu_min_freq);
  34static DEFINE_PER_CPU(unsigned int, cpu_cur_freq); /* current CPU freq */
  35static DEFINE_PER_CPU(unsigned int, cpu_set_freq); /* CPU freq desired by
  36                                                        userspace */
  37static DEFINE_PER_CPU(unsigned int, cpu_is_managed);
  38
  39static DEFINE_MUTEX(userspace_mutex);
  40static int cpus_using_userspace_governor;
  41
  42/* keep track of frequency transitions */
  43static int
  44userspace_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
  61static 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 */
  73static 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
 108static 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
 113static 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
 192static
 193#endif
 194struct 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
 202static int __init cpufreq_gov_userspace_init(void)
 203{
 204        return cpufreq_register_governor(&cpufreq_gov_userspace);
 205}
 206
 207
 208static void __exit cpufreq_gov_userspace_exit(void)
 209{
 210        cpufreq_unregister_governor(&cpufreq_gov_userspace);
 211}
 212
 213
 214MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>, "
 215                "Russell King <rmk@arm.linux.org.uk>");
 216MODULE_DESCRIPTION("CPUfreq policy governor 'userspace'");
 217MODULE_LICENSE("GPL");
 218
 219#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE
 220fs_initcall(cpufreq_gov_userspace_init);
 221#else
 222module_init(cpufreq_gov_userspace_init);
 223#endif
 224module_exit(cpufreq_gov_userspace_exit);
 225
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.