2006-01-27 18:15:26 +03:00
2005-04-17 02:20:36 +04:00
/*
* linux / drivers / cpufreq / cpufreq_userspace . c
*
* Copyright ( C ) 2001 Russell King
* ( C ) 2002 - 2004 Dominik Brodowski < linux @ brodo . de >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
*/
# 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>
2006-07-26 17:40:07 +04:00
# include <linux/cpu.h>
2005-04-17 02:20:36 +04:00
# include <linux/types.h>
# include <linux/fs.h>
# include <linux/sysfs.h>
2006-01-14 02:54:22 +03:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
/**
* A few values needed by the userspace governor
*/
2008-07-19 05:11:32 +04:00
static DEFINE_PER_CPU ( unsigned int , cpu_max_freq ) ;
static DEFINE_PER_CPU ( unsigned int , cpu_min_freq ) ;
static DEFINE_PER_CPU ( unsigned int , cpu_cur_freq ) ; /* current CPU freq */
static DEFINE_PER_CPU ( unsigned int , cpu_set_freq ) ; /* CPU freq desired by
userspace */
static DEFINE_PER_CPU ( unsigned int , cpu_is_managed ) ;
2005-04-17 02:20:36 +04:00
2009-01-18 09:51:46 +03:00
static DEFINE_MUTEX ( userspace_mutex ) ;
2007-06-21 01:24:00 +04:00
static int cpus_using_userspace_governor ;
2005-04-17 02:20:36 +04:00
2008-07-19 05:11:32 +04:00
# define dprintk(msg...) \
cpufreq_debug_printk ( CPUFREQ_DEBUG_GOVERNOR , " userspace " , msg )
2005-04-17 02:20:36 +04:00
/* keep track of frequency transitions */
2006-02-28 08:43:23 +03:00
static int
2005-04-17 02:20:36 +04:00
userspace_cpufreq_notifier ( struct notifier_block * nb , unsigned long val ,
2009-01-18 09:51:46 +03:00
void * data )
2005-04-17 02:20:36 +04:00
{
2009-01-18 09:51:46 +03:00
struct cpufreq_freqs * freq = data ;
2005-04-17 02:20:36 +04:00
2008-07-19 05:11:32 +04:00
if ( ! per_cpu ( cpu_is_managed , freq - > cpu ) )
2007-06-21 01:24:00 +04:00
return 0 ;
dprintk ( " saving cpu_cur_freq of cpu %u to be %u kHz \n " ,
freq - > cpu , freq - > new ) ;
2008-07-19 05:11:32 +04:00
per_cpu ( cpu_cur_freq , freq - > cpu ) = freq - > new ;
2005-04-17 02:20:36 +04:00
2009-01-18 09:51:46 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static struct notifier_block userspace_cpufreq_notifier_block = {
2009-01-18 09:51:46 +03:00
. notifier_call = userspace_cpufreq_notifier
2005-04-17 02:20:36 +04:00
} ;
2006-02-28 08:43:23 +03:00
/**
2005-04-17 02:20:36 +04:00
* cpufreq_set - set the CPU frequency
2007-10-26 21:18:21 +04:00
* @ policy : pointer to policy struct where freq is being set
2005-04-17 02:20:36 +04:00
* @ freq : target frequency in kHz
*
* Sets the CPU frequency to freq .
*/
2007-10-26 21:18:21 +04:00
static int cpufreq_set ( struct cpufreq_policy * policy , unsigned int freq )
2005-04-17 02:20:36 +04:00
{
int ret = - EINVAL ;
2006-01-27 18:15:26 +03:00
dprintk ( " cpufreq_set for cpu %u, freq %u kHz \n " , policy - > cpu , freq ) ;
2005-04-17 02:20:36 +04:00
2006-01-14 02:54:22 +03:00
mutex_lock ( & userspace_mutex ) ;
2008-07-19 05:11:32 +04:00
if ( ! per_cpu ( cpu_is_managed , policy - > cpu ) )
2005-04-17 02:20:36 +04:00
goto err ;
2008-07-19 05:11:32 +04:00
per_cpu ( cpu_set_freq , policy - > cpu ) = freq ;
2005-04-17 02:20:36 +04:00
2008-07-19 05:11:32 +04:00
if ( freq < per_cpu ( cpu_min_freq , policy - > cpu ) )
freq = per_cpu ( cpu_min_freq , policy - > cpu ) ;
if ( freq > per_cpu ( cpu_max_freq , policy - > cpu ) )
freq = per_cpu ( cpu_max_freq , policy - > cpu ) ;
2005-04-17 02:20:36 +04:00
/*
* We ' re safe from concurrent calls to - > target ( ) here
2006-01-14 02:54:22 +03:00
* as we hold the userspace_mutex lock . If we were calling
2005-04-17 02:20:36 +04:00
* cpufreq_driver_target , a deadlock situation might occur :
2009-01-18 09:51:46 +03:00
* A : cpufreq_set ( lock userspace_mutex ) - >
* cpufreq_driver_target ( lock policy - > lock )
* B : cpufreq_set_policy ( lock policy - > lock ) - >
* __cpufreq_governor - >
* cpufreq_governor_userspace ( lock userspace_mutex )
2005-04-17 02:20:36 +04:00
*/
2006-01-27 18:15:26 +03:00
ret = __cpufreq_driver_target ( policy , freq , CPUFREQ_RELATION_L ) ;
2005-04-17 02:20:36 +04:00
err :
2006-01-14 02:54:22 +03:00
mutex_unlock ( & userspace_mutex ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2007-10-26 21:18:21 +04:00
static ssize_t show_speed ( struct cpufreq_policy * policy , char * buf )
2005-04-17 02:20:36 +04:00
{
2008-07-19 05:11:32 +04:00
return sprintf ( buf , " %u \n " , per_cpu ( cpu_cur_freq , policy - > cpu ) ) ;
2005-04-17 02:20:36 +04:00
}
static int cpufreq_governor_userspace ( struct cpufreq_policy * policy ,
unsigned int event )
{
unsigned int cpu = policy - > cpu ;
2006-10-21 01:31:00 +04:00
int rc = 0 ;
2005-04-17 02:20:36 +04:00
switch ( event ) {
case CPUFREQ_GOV_START :
if ( ! cpu_online ( cpu ) )
return - EINVAL ;
BUG_ON ( ! policy - > cur ) ;
2006-01-14 02:54:22 +03:00
mutex_lock ( & userspace_mutex ) ;
2006-10-21 01:31:00 +04:00
2007-06-21 01:24:00 +04:00
if ( cpus_using_userspace_governor = = 0 ) {
cpufreq_register_notifier (
& userspace_cpufreq_notifier_block ,
CPUFREQ_TRANSITION_NOTIFIER ) ;
}
cpus_using_userspace_governor + + ;
2008-07-19 05:11:32 +04:00
per_cpu ( cpu_is_managed , cpu ) = 1 ;
per_cpu ( cpu_min_freq , cpu ) = policy - > min ;
per_cpu ( cpu_max_freq , cpu ) = policy - > max ;
per_cpu ( cpu_cur_freq , cpu ) = policy - > cur ;
per_cpu ( cpu_set_freq , cpu ) = policy - > cur ;
dprintk ( " managing cpu %u started "
" (%u - %u kHz, currently %u kHz) \n " ,
cpu ,
per_cpu ( cpu_min_freq , cpu ) ,
per_cpu ( cpu_max_freq , cpu ) ,
per_cpu ( cpu_cur_freq , cpu ) ) ;
2007-10-26 21:18:21 +04:00
2006-01-14 02:54:22 +03:00
mutex_unlock ( & userspace_mutex ) ;
2005-04-17 02:20:36 +04:00
break ;
case CPUFREQ_GOV_STOP :
2006-01-14 02:54:22 +03:00
mutex_lock ( & userspace_mutex ) ;
2007-06-21 01:24:00 +04:00
cpus_using_userspace_governor - - ;
if ( cpus_using_userspace_governor = = 0 ) {
cpufreq_unregister_notifier (
& userspace_cpufreq_notifier_block ,
CPUFREQ_TRANSITION_NOTIFIER ) ;
}
2008-07-19 05:11:32 +04:00
per_cpu ( cpu_is_managed , cpu ) = 0 ;
per_cpu ( cpu_min_freq , cpu ) = 0 ;
per_cpu ( cpu_max_freq , cpu ) = 0 ;
per_cpu ( cpu_set_freq , cpu ) = 0 ;
2005-04-17 02:20:36 +04:00
dprintk ( " managing cpu %u stopped \n " , cpu ) ;
2006-01-14 02:54:22 +03:00
mutex_unlock ( & userspace_mutex ) ;
2005-04-17 02:20:36 +04:00
break ;
case CPUFREQ_GOV_LIMITS :
2006-01-14 02:54:22 +03:00
mutex_lock ( & userspace_mutex ) ;
2008-07-19 05:11:32 +04:00
dprintk ( " limit event for cpu %u: %u - %u kHz, "
2006-01-27 18:15:26 +03:00
" currently %u kHz, last set to %u kHz \n " ,
cpu , policy - > min , policy - > max ,
2008-07-19 05:11:32 +04:00
per_cpu ( cpu_cur_freq , cpu ) ,
per_cpu ( cpu_set_freq , cpu ) ) ;
if ( policy - > max < per_cpu ( cpu_set_freq , cpu ) ) {
2006-01-27 18:15:26 +03:00
__cpufreq_driver_target ( policy , policy - > max ,
CPUFREQ_RELATION_H ) ;
2008-07-19 05:11:32 +04:00
} else if ( policy - > min > per_cpu ( cpu_set_freq , cpu ) ) {
2006-01-27 18:15:26 +03:00
__cpufreq_driver_target ( policy , policy - > min ,
CPUFREQ_RELATION_L ) ;
2008-07-19 05:11:32 +04:00
} else {
__cpufreq_driver_target ( policy ,
per_cpu ( cpu_set_freq , cpu ) ,
2006-01-27 18:15:26 +03:00
CPUFREQ_RELATION_L ) ;
}
2008-07-19 05:11:32 +04:00
per_cpu ( cpu_min_freq , cpu ) = policy - > min ;
per_cpu ( cpu_max_freq , cpu ) = policy - > max ;
per_cpu ( cpu_cur_freq , cpu ) = policy - > cur ;
2006-01-14 02:54:22 +03:00
mutex_unlock ( & userspace_mutex ) ;
2005-04-17 02:20:36 +04:00
break ;
}
2006-10-21 01:31:00 +04:00
return rc ;
2005-04-17 02:20:36 +04:00
}
2008-09-20 18:50:08 +04:00
# ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE
static
# endif
2005-04-17 02:20:36 +04:00
struct cpufreq_governor cpufreq_gov_userspace = {
. name = " userspace " ,
. governor = cpufreq_governor_userspace ,
2007-10-26 21:18:21 +04:00
. store_setspeed = cpufreq_set ,
. show_setspeed = show_speed ,
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
} ;
static int __init cpufreq_gov_userspace_init ( void )
{
return cpufreq_register_governor ( & cpufreq_gov_userspace ) ;
}
static void __exit cpufreq_gov_userspace_exit ( void )
{
cpufreq_unregister_governor ( & cpufreq_gov_userspace ) ;
}
2009-01-18 09:51:46 +03:00
MODULE_AUTHOR ( " Dominik Brodowski <linux@brodo.de>, "
" Russell King <rmk@arm.linux.org.uk> " ) ;
MODULE_DESCRIPTION ( " CPUfreq policy governor 'userspace' " ) ;
MODULE_LICENSE ( " GPL " ) ;
2005-04-17 02:20:36 +04:00
2008-01-18 02:21:08 +03:00
# ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE
2005-04-17 02:20:36 +04:00
fs_initcall ( cpufreq_gov_userspace_init ) ;
2008-01-18 02:21:08 +03:00
# else
module_init ( cpufreq_gov_userspace_init ) ;
# endif
2005-04-17 02:20:36 +04:00
module_exit ( cpufreq_gov_userspace_exit ) ;