2019-05-27 08:55:02 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
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 >
*/
# include <linux/cpufreq.h>
# include <linux/sched.h>
2017-02-08 08:45:17 +01:00
# include <linux/sched/loadavg.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 ) ;
2018-10-26 15:06:11 -07:00
info - > busy_spus = calc_load ( info - > busy_spus , EXP , busy_spus * FIXED_1 ) ;
2008-07-16 05:51:43 +10:00
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 ) ;
2012-08-21 13:18:23 -07:00
INIT_DEFERRABLE_WORK ( & 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 ) ;
}
2016-06-02 23:24:15 +02:00
static int spu_gov_start ( struct cpufreq_policy * policy )
2008-07-16 05:51:43 +10:00
{
unsigned int cpu = policy - > cpu ;
2016-06-02 23:24:15 +02:00
struct spu_gov_info_struct * info = & per_cpu ( spu_gov_info , cpu ) ;
struct spu_gov_info_struct * affected_info ;
2008-07-16 05:51:43 +10:00
int i ;
2016-06-02 23:24:15 +02:00
if ( ! cpu_online ( cpu ) ) {
printk ( KERN_ERR " cpu %d is not online \n " , cpu ) ;
return - EINVAL ;
}
2008-07-16 05:51:43 +10:00
2016-06-02 23:24:15 +02:00
if ( ! policy - > cur ) {
printk ( KERN_ERR " no cpu specified in policy \n " ) ;
return - EINVAL ;
}
2008-07-16 05:51:43 +10:00
2016-06-02 23:24:15 +02:00
/* initialize spu_gov_info for all affected cpus */
for_each_cpu ( i , policy - > cpus ) {
affected_info = & per_cpu ( spu_gov_info , i ) ;
affected_info - > policy = policy ;
}
2008-07-16 05:51:43 +10:00
2016-06-02 23:24:15 +02:00
info - > poll_int = POLL_TIME ;
2008-07-16 05:51:43 +10:00
2016-06-02 23:24:15 +02:00
/* setup timer */
spu_gov_init_work ( info ) ;
2008-07-16 05:51:43 +10:00
2016-06-02 23:24:15 +02:00
return 0 ;
}
2008-07-16 05:51:43 +10:00
2016-06-02 23:24:15 +02:00
static void spu_gov_stop ( struct cpufreq_policy * policy )
{
unsigned int cpu = policy - > cpu ;
struct spu_gov_info_struct * info = & per_cpu ( spu_gov_info , cpu ) ;
int i ;
2008-07-16 05:51:43 +10:00
2016-06-02 23:24:15 +02:00
/* cancel timer */
spu_gov_cancel_work ( info ) ;
2008-07-16 05:51:43 +10:00
2016-06-02 23:24:15 +02:00
/* clean spu_gov_info for all affected cpus */
for_each_cpu ( i , policy - > cpus ) {
info = & per_cpu ( spu_gov_info , i ) ;
info - > policy = NULL ;
2008-07-16 05:51:43 +10:00
}
}
static struct cpufreq_governor spu_governor = {
. name = " spudemand " ,
2016-06-02 23:24:15 +02:00
. start = spu_gov_start ,
. stop = spu_gov_stop ,
2008-07-16 05:51:43 +10:00
. owner = THIS_MODULE ,
} ;
2020-06-29 13:54:59 +05:30
cpufreq_governor_init ( spu_governor ) ;
cpufreq_governor_exit ( spu_governor ) ;
2008-07-16 05:51:43 +10:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Christian Krafft <krafft@de.ibm.com> " ) ;