aboutsummaryrefslogtreecommitdiff
path: root/drivers/cpufreq/cpufreq_userspace.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/cpufreq/cpufreq_userspace.c')
-rw-r--r--drivers/cpufreq/cpufreq_userspace.c194
1 files changed, 55 insertions, 139 deletions
diff --git a/drivers/cpufreq/cpufreq_userspace.c b/drivers/cpufreq/cpufreq_userspace.c
index d32bf3593cd..4dbf1db16ac 100644
--- a/drivers/cpufreq/cpufreq_userspace.c
+++ b/drivers/cpufreq/cpufreq_userspace.c
@@ -1,3 +1,4 @@
+
/*
* linux/drivers/cpufreq/cpufreq_userspace.c
*
@@ -10,198 +11,113 @@
*
*/
-#include <linux/config.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/smp.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/interrupt.h>
-#include <linux/cpufreq.h>
-#include <linux/types.h>
-#include <linux/fs.h>
-#include <linux/sysfs.h>
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <asm/uaccess.h>
+#include <linux/cpufreq.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+static DEFINE_PER_CPU(unsigned int, cpu_is_managed);
+static DEFINE_MUTEX(userspace_mutex);
/**
- * A few values needed by the userspace governor
- */
-static unsigned int cpu_max_freq[NR_CPUS];
-static unsigned int cpu_min_freq[NR_CPUS];
-static unsigned int cpu_cur_freq[NR_CPUS]; /* current CPU freq */
-static unsigned int cpu_set_freq[NR_CPUS]; /* CPU freq desired by userspace */
-static unsigned int cpu_is_managed[NR_CPUS];
-static struct cpufreq_policy current_policy[NR_CPUS];
-
-static DECLARE_MUTEX (userspace_sem);
-
-#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_GOVERNOR, "userspace", msg)
-
-/* keep track of frequency transitions */
-static int
-userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
- void *data)
-{
- struct cpufreq_freqs *freq = data;
-
- dprintk("saving cpu_cur_freq of cpu %u to be %u kHz\n", freq->cpu, freq->new);
- cpu_cur_freq[freq->cpu] = freq->new;
-
- return 0;
-}
-
-static struct notifier_block userspace_cpufreq_notifier_block = {
- .notifier_call = userspace_cpufreq_notifier
-};
-
-
-/**
* cpufreq_set - set the CPU frequency
+ * @policy: pointer to policy struct where freq is being set
* @freq: target frequency in kHz
- * @cpu: CPU for which the frequency is to be set
*
* Sets the CPU frequency to freq.
*/
-static int cpufreq_set(unsigned int freq, unsigned int cpu)
+static int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq)
{
int ret = -EINVAL;
- dprintk("cpufreq_set for cpu %u, freq %u kHz\n", cpu, freq);
+ pr_debug("cpufreq_set for cpu %u, freq %u kHz\n", policy->cpu, freq);
- down(&userspace_sem);
- if (!cpu_is_managed[cpu])
+ mutex_lock(&userspace_mutex);
+ if (!per_cpu(cpu_is_managed, policy->cpu))
goto err;
- cpu_set_freq[cpu] = freq;
-
- if (freq < cpu_min_freq[cpu])
- freq = cpu_min_freq[cpu];
- if (freq > cpu_max_freq[cpu])
- freq = cpu_max_freq[cpu];
-
- /*
- * We're safe from concurrent calls to ->target() here
- * as we hold the userspace_sem lock. If we were calling
- * cpufreq_driver_target, a deadlock situation might occur:
- * A: cpufreq_set (lock userspace_sem) -> cpufreq_driver_target(lock policy->lock)
- * B: cpufreq_set_policy(lock policy->lock) -> __cpufreq_governor -> cpufreq_governor_userspace (lock userspace_sem)
- */
- ret = __cpufreq_driver_target(&current_policy[cpu], freq,
- CPUFREQ_RELATION_L);
-
+ ret = __cpufreq_driver_target(policy, freq, CPUFREQ_RELATION_L);
err:
- up(&userspace_sem);
+ mutex_unlock(&userspace_mutex);
return ret;
}
-
-/************************** sysfs interface ************************/
-static ssize_t show_speed (struct cpufreq_policy *policy, char *buf)
-{
- return sprintf (buf, "%u\n", cpu_cur_freq[policy->cpu]);
-}
-
-static ssize_t
-store_speed (struct cpufreq_policy *policy, const char *buf, size_t count)
+static ssize_t show_speed(struct cpufreq_policy *policy, char *buf)
{
- unsigned int freq = 0;
- unsigned int ret;
-
- ret = sscanf (buf, "%u", &freq);
- if (ret != 1)
- return -EINVAL;
-
- cpufreq_set(freq, policy->cpu);
-
- return count;
+ return sprintf(buf, "%u\n", policy->cur);
}
-static struct freq_attr freq_attr_scaling_setspeed =
-{
- .attr = { .name = "scaling_setspeed", .mode = 0644, .owner = THIS_MODULE },
- .show = show_speed,
- .store = store_speed,
-};
-
static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
unsigned int event)
{
unsigned int cpu = policy->cpu;
+ int rc = 0;
+
switch (event) {
case CPUFREQ_GOV_START:
- if (!cpu_online(cpu))
- return -EINVAL;
BUG_ON(!policy->cur);
- down(&userspace_sem);
- cpu_is_managed[cpu] = 1;
- cpu_min_freq[cpu] = policy->min;
- cpu_max_freq[cpu] = policy->max;
- cpu_cur_freq[cpu] = policy->cur;
- cpu_set_freq[cpu] = policy->cur;
- sysfs_create_file (&policy->kobj, &freq_attr_scaling_setspeed.attr);
- memcpy (&current_policy[cpu], policy, sizeof(struct cpufreq_policy));
- 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]);
- up(&userspace_sem);
+ pr_debug("started managing cpu %u\n", cpu);
+
+ mutex_lock(&userspace_mutex);
+ per_cpu(cpu_is_managed, cpu) = 1;
+ mutex_unlock(&userspace_mutex);
break;
case CPUFREQ_GOV_STOP:
- down(&userspace_sem);
- cpu_is_managed[cpu] = 0;
- cpu_min_freq[cpu] = 0;
- cpu_max_freq[cpu] = 0;
- cpu_set_freq[cpu] = 0;
- sysfs_remove_file (&policy->kobj, &freq_attr_scaling_setspeed.attr);
- dprintk("managing cpu %u stopped\n", cpu);
- up(&userspace_sem);
+ pr_debug("managing cpu %u stopped\n", cpu);
+
+ mutex_lock(&userspace_mutex);
+ per_cpu(cpu_is_managed, cpu) = 0;
+ mutex_unlock(&userspace_mutex);
break;
case CPUFREQ_GOV_LIMITS:
- down(&userspace_sem);
- cpu_min_freq[cpu] = policy->min;
- cpu_max_freq[cpu] = policy->max;
- 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]);
- if (policy->max < cpu_set_freq[cpu]) {
- __cpufreq_driver_target(&current_policy[cpu], policy->max,
- CPUFREQ_RELATION_H);
- } else if (policy->min > cpu_set_freq[cpu]) {
- __cpufreq_driver_target(&current_policy[cpu], policy->min,
- CPUFREQ_RELATION_L);
- } else {
- __cpufreq_driver_target(&current_policy[cpu], cpu_set_freq[cpu],
- CPUFREQ_RELATION_L);
- }
- memcpy (&current_policy[cpu], policy, sizeof(struct cpufreq_policy));
- up(&userspace_sem);
+ mutex_lock(&userspace_mutex);
+ pr_debug("limit event for cpu %u: %u - %u kHz, currently %u kHz\n",
+ cpu, policy->min, policy->max,
+ policy->cur);
+
+ if (policy->max < policy->cur)
+ __cpufreq_driver_target(policy, policy->max,
+ CPUFREQ_RELATION_H);
+ else if (policy->min > policy->cur)
+ __cpufreq_driver_target(policy, policy->min,
+ CPUFREQ_RELATION_L);
+ mutex_unlock(&userspace_mutex);
break;
}
- return 0;
+ return rc;
}
-
+#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE
+static
+#endif
struct cpufreq_governor cpufreq_gov_userspace = {
.name = "userspace",
.governor = cpufreq_governor_userspace,
+ .store_setspeed = cpufreq_set,
+ .show_setspeed = show_speed,
.owner = THIS_MODULE,
};
-EXPORT_SYMBOL(cpufreq_gov_userspace);
static int __init cpufreq_gov_userspace_init(void)
{
- cpufreq_register_notifier(&userspace_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
return cpufreq_register_governor(&cpufreq_gov_userspace);
}
-
static void __exit cpufreq_gov_userspace_exit(void)
{
cpufreq_unregister_governor(&cpufreq_gov_userspace);
- cpufreq_unregister_notifier(&userspace_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
}
+MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>, "
+ "Russell King <rmk@arm.linux.org.uk>");
+MODULE_DESCRIPTION("CPUfreq policy governor 'userspace'");
+MODULE_LICENSE("GPL");
-MODULE_AUTHOR ("Dominik Brodowski <linux@brodo.de>, Russell King <rmk@arm.linux.org.uk>");
-MODULE_DESCRIPTION ("CPUfreq policy governor 'userspace'");
-MODULE_LICENSE ("GPL");
-
+#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE
fs_initcall(cpufreq_gov_userspace_init);
+#else
+module_init(cpufreq_gov_userspace_init);
+#endif
module_exit(cpufreq_gov_userspace_exit);