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#include <asm/uaccess.h>
  28
  29
  30/**
  31 * A few values needed by the userspace governor
  32 */
  33static DEFINE_PER_CPU(unsigned int, cpu_max_freq);
  34static DEFINE_PER_CPU(unsigned int, cpu_min_freq);
  35static DEFINE_PER_CPU(unsigned int, cpu_cur_freq); /* current CPU freq */
  36static DEFINE_PER_CPU(unsigned int, cpu_set_freq); /* CPU freq desired by
  37                                                        userspace */
  38static DEFINE_PER_CPU(unsigned int, cpu_is_managed);
  39
  40static DEFINE_MUTEX     (userspace_mutex);
  41static int cpus_using_userspace_governor;
  42
  43#define dprintk(msg...) \
  44        cpufreq_debug_printk(CPUFREQ_DEBUG_GOVERNOR, "userspace", msg)
  45
  46/* keep track of frequency transitions */
  47static int
  48userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
  49                       void *data)
  50{
  51        struct cpufreq_freqs *freq = data;
  52
  53        if (!per_cpu(cpu_is_managed, freq->cpu))
  54                return 0;
  55
  56        dprintk("saving cpu_cur_freq of cpu %u to be %u kHz\n",
  57                        freq->cpu, freq->new);
  58        per_cpu(cpu_cur_freq, freq->cpu) = freq->new;
  59
  60        return 0;
  61}
  62
  63static struct notifier_block userspace_cpufreq_notifier_block = {
  64        .notifier_call  = userspace_cpufreq_notifier
  65};
  66
  67
  68/**
  69 * cpufreq_set - set the CPU frequency
  70 * @policy: pointer to policy struct where freq is being set
  71 * @freq: target frequency in kHz
  72 *
  73 * Sets the CPU frequency to freq.
  74 */
  75static int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq)
  76{
  77        int ret = -EINVAL;
  78
  79        dprintk("cpufreq_set for cpu %u, freq %u kHz\n", policy->cpu, freq);
  80
  81        mutex_lock(&userspace_mutex);
  82        if (!per_cpu(cpu_is_managed, policy->cpu))
  83                goto err;
  84
  85        per_cpu(cpu_set_freq, policy->cpu) = freq;
  86
  87        if (freq < per_cpu(cpu_min_freq, policy->cpu))
  88                freq = per_cpu(cpu_min_freq, policy->cpu);
  89        if (freq > per_cpu(cpu_max_freq, policy->cpu))
  90                freq = per_cpu(cpu_max_freq, policy->cpu);
  91
  92        /*
  93         * We're safe from concurrent calls to ->target() here
  94         * as we hold the userspace_mutex lock. If we were calling
  95         * cpufreq_driver_target, a deadlock situation might occur:
  96         * A: cpufreq_set (lock userspace_mutex) -> cpufreq_driver_target(lock policy->lock)
  97         * B: cpufreq_set_policy(lock policy->lock) -> __cpufreq_governor -> cpufreq_governor_userspace (lock userspace_mutex)
  98         */
  99        ret = __cpufreq_driver_target(policy, freq, CPUFREQ_RELATION_L);
 100
 101 err:
 102        mutex_unlock(&userspace_mutex);
 103        return ret;
 104}
 105
 106
 107static ssize_t show_speed(struct cpufreq_policy *policy, char *buf)
 108{
 109        return sprintf(buf, "%u\n", per_cpu(cpu_cur_freq, policy->cpu));
 110}
 111
 112static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
 113                                   unsigned int event)
 114{
 115        unsigned int cpu = policy->cpu;
 116        int rc = 0;
 117
 118        switch (event) {
 119        case CPUFREQ_GOV_START:
 120                if (!cpu_online(cpu))
 121                        return -EINVAL;
 122                BUG_ON(!policy->cur);
 123                mutex_lock(&userspace_mutex);
 124
 125                if (cpus_using_userspace_governor == 0) {
 126                        cpufreq_register_notifier(
 127                                        &userspace_cpufreq_notifier_block,
 128                                        CPUFREQ_TRANSITION_NOTIFIER);
 129                }
 130                cpus_using_userspace_governor++;
 131
 132                per_cpu(cpu_is_managed, cpu) = 1;
 133                per_cpu(cpu_min_freq, cpu) = policy->min;
 134                per_cpu(cpu_max_freq, cpu) = policy->max;
 135                per_cpu(cpu_cur_freq, cpu) = policy->cur;
 136                per_cpu(cpu_set_freq, cpu) = policy->cur;
 137                dprintk("managing cpu %u started "
 138                        "(%u - %u kHz, currently %u kHz)\n",
 139                                cpu,
 140                                per_cpu(cpu_min_freq, cpu),
 141                                per_cpu(cpu_max_freq, cpu),
 142                                per_cpu(cpu_cur_freq, cpu));
 143
 144                mutex_unlock(&userspace_mutex);
 145                break;
 146        case CPUFREQ_GOV_STOP:
 147                mutex_lock(&userspace_mutex);
 148                cpus_using_userspace_governor--;
 149                if (cpus_using_userspace_governor == 0) {
 150                        cpufreq_unregister_notifier(
 151                                        &userspace_cpufreq_notifier_block,
 152                                        CPUFREQ_TRANSITION_NOTIFIER);
 153                }
 154
 155                per_cpu(cpu_is_managed, cpu) = 0;
 156                per_cpu(cpu_min_freq, cpu) = 0;
 157                per_cpu(cpu_max_freq, cpu) = 0;
 158                per_cpu(cpu_set_freq, cpu) = 0;
 159                dprintk("managing cpu %u stopped\n", cpu);
 160                mutex_unlock(&userspace_mutex);
 161                break;
 162        case CPUFREQ_GOV_LIMITS:
 163                mutex_lock(&userspace_mutex);
 164                dprintk("limit event for cpu %u: %u - %u kHz, "
 165                        "currently %u kHz, last set to %u kHz\n",
 166                        cpu, policy->min, policy->max,
 167                        per_cpu(cpu_cur_freq, cpu),
 168                        per_cpu(cpu_set_freq, cpu));
 169                if (policy->max < per_cpu(cpu_set_freq, cpu)) {
 170                        __cpufreq_driver_target(policy, policy->max,
 171                                                CPUFREQ_RELATION_H);
 172                } else if (policy->min > per_cpu(cpu_set_freq, cpu)) {
 173                        __cpufreq_driver_target(policy, policy->min,
 174                                                CPUFREQ_RELATION_L);
 175                } else {
 176                        __cpufreq_driver_target(policy,
 177                                                per_cpu(cpu_set_freq, cpu),
 178                                                CPUFREQ_RELATION_L);
 179                }
 180                per_cpu(cpu_min_freq, cpu) = policy->min;
 181                per_cpu(cpu_max_freq, cpu) = policy->max;
 182                per_cpu(cpu_cur_freq, cpu) = policy->cur;
 183                mutex_unlock(&userspace_mutex);
 184                break;
 185        }
 186        return rc;
 187}
 188
 189
 190#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE
 191static
 192#endif
 193struct cpufreq_governor cpufreq_gov_userspace = {
 194        .name           = "userspace",
 195        .governor       = cpufreq_governor_userspace,
 196        .store_setspeed = cpufreq_set,
 197        .show_setspeed  = show_speed,
 198        .owner          = THIS_MODULE,
 199};
 200
 201static int __init cpufreq_gov_userspace_init(void)
 202{
 203        return cpufreq_register_governor(&cpufreq_gov_userspace);
 204}
 205
 206
 207static void __exit cpufreq_gov_userspace_exit(void)
 208{
 209        cpufreq_unregister_governor(&cpufreq_gov_userspace);
 210}
 211
 212
 213MODULE_AUTHOR ("Dominik Brodowski <linux@brodo.de>, 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