2008-07-16 05:51:43 +10:00
/*
* spu aware cpufreq governor for the cell processor
*
* © Copyright IBM Corporation 2006 - 2008
*
* Author : Christian Krafft < krafft @ de . ibm . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 , or ( at your option )
* any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/cpufreq.h>
# include <linux/sched.h>
2011-05-27 13:23:32 -04:00
# include <linux/module.h>
2008-07-16 05:51:43 +10:00
# include <linux/timer.h>
# include <linux/workqueue.h>
2011-07-26 16:09:06 -07:00
# include <linux/atomic.h>
2008-07-16 05:51:43 +10:00
# include <asm/machdep.h>
# include <asm/spu.h>
# define POLL_TIME 100000 /* in µs */
# define EXP 753 /* exp(-1) in fixed-point */
struct spu_gov_info_struct {
unsigned long busy_spus ; /* fixed-point */
struct cpufreq_policy * policy ;
struct delayed_work work ;
unsigned int poll_int ; /* µs */
} ;
static DEFINE_PER_CPU ( struct spu_gov_info_struct , spu_gov_info ) ;
static int calc_freq ( struct spu_gov_info_struct * info )
{
int cpu ;
int busy_spus ;
cpu = info - > policy - > cpu ;
busy_spus = atomic_read ( & cbe_spu_info [ cpu_to_node ( cpu ) ] . busy_spus ) ;
CALC_LOAD ( info - > busy_spus , EXP , busy_spus * FIXED_1 ) ;
pr_debug ( " cpu %d: busy_spus=%d, info->busy_spus=%ld \n " ,
cpu , busy_spus , info - > busy_spus ) ;
return info - > policy - > max * info - > busy_spus / FIXED_1 ;
}
static void spu_gov_work ( struct work_struct * work )
{
struct spu_gov_info_struct * info ;
int delay ;
unsigned long target_freq ;
info = container_of ( work , struct spu_gov_info_struct , work . work ) ;
/* after cancel_delayed_work_sync we unset info->policy */
BUG_ON ( info - > policy = = NULL ) ;
target_freq = calc_freq ( info ) ;
__cpufreq_driver_target ( info - > policy , target_freq , CPUFREQ_RELATION_H ) ;
delay = usecs_to_jiffies ( info - > poll_int ) ;
2011-01-03 03:49:25 +00:00
schedule_delayed_work_on ( info - > policy - > cpu , & info - > work , delay ) ;
2008-07-16 05:51:43 +10:00
}
static void spu_gov_init_work ( struct spu_gov_info_struct * info )
{
int delay = usecs_to_jiffies ( info - > poll_int ) ;
INIT_DELAYED_WORK_DEFERRABLE ( & info - > work , spu_gov_work ) ;
2011-01-03 03:49:25 +00:00
schedule_delayed_work_on ( info - > policy - > cpu , & info - > work , delay ) ;
2008-07-16 05:51:43 +10:00
}
static void spu_gov_cancel_work ( struct spu_gov_info_struct * info )
{
cancel_delayed_work_sync ( & info - > work ) ;
}
static int spu_gov_govern ( struct cpufreq_policy * policy , unsigned int event )
{
unsigned int cpu = policy - > cpu ;
struct spu_gov_info_struct * info , * affected_info ;
int i ;
int ret = 0 ;
info = & per_cpu ( spu_gov_info , cpu ) ;
switch ( event ) {
case CPUFREQ_GOV_START :
if ( ! cpu_online ( cpu ) ) {
printk ( KERN_ERR " cpu %d is not online \n " , cpu ) ;
ret = - EINVAL ;
break ;
}
if ( ! policy - > cur ) {
printk ( KERN_ERR " no cpu specified in policy \n " ) ;
ret = - EINVAL ;
break ;
}
/* initialize spu_gov_info for all affected cpus */
2009-01-12 11:22:01 +11:00
for_each_cpu ( i , policy - > cpus ) {
2008-07-16 05:51:43 +10:00
affected_info = & per_cpu ( spu_gov_info , i ) ;
affected_info - > policy = policy ;
}
info - > poll_int = POLL_TIME ;
/* setup timer */
spu_gov_init_work ( info ) ;
break ;
case CPUFREQ_GOV_STOP :
/* cancel timer */
spu_gov_cancel_work ( info ) ;
/* clean spu_gov_info for all affected cpus */
2009-01-12 11:22:01 +11:00
for_each_cpu ( i , policy - > cpus ) {
2008-07-16 05:51:43 +10:00
info = & per_cpu ( spu_gov_info , i ) ;
info - > policy = NULL ;
}
break ;
}
return ret ;
}
static struct cpufreq_governor spu_governor = {
. name = " spudemand " ,
. governor = spu_gov_govern ,
. owner = THIS_MODULE ,
} ;
/*
* module init and destoy
*/
static int __init spu_gov_init ( void )
{
int ret ;
ret = cpufreq_register_governor ( & spu_governor ) ;
2011-01-03 03:49:25 +00:00
if ( ret )
2008-07-16 05:51:43 +10:00
printk ( KERN_ERR " registration of governor failed \n " ) ;
return ret ;
}
static void __exit spu_gov_exit ( void )
{
cpufreq_unregister_governor ( & spu_governor ) ;
}
module_init ( spu_gov_init ) ;
module_exit ( spu_gov_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Christian Krafft <krafft@de.ibm.com> " ) ;