18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * spu aware cpufreq governor for the cell processor 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * © Copyright IBM Corporation 2006-2008 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Christian Krafft <krafft@de.ibm.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/cpufreq.h> 118c2ecf20Sopenharmony_ci#include <linux/sched.h> 128c2ecf20Sopenharmony_ci#include <linux/sched/loadavg.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/timer.h> 158c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 168c2ecf20Sopenharmony_ci#include <linux/atomic.h> 178c2ecf20Sopenharmony_ci#include <asm/machdep.h> 188c2ecf20Sopenharmony_ci#include <asm/spu.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define POLL_TIME 100000 /* in µs */ 218c2ecf20Sopenharmony_ci#define EXP 753 /* exp(-1) in fixed-point */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistruct spu_gov_info_struct { 248c2ecf20Sopenharmony_ci unsigned long busy_spus; /* fixed-point */ 258c2ecf20Sopenharmony_ci struct cpufreq_policy *policy; 268c2ecf20Sopenharmony_ci struct delayed_work work; 278c2ecf20Sopenharmony_ci unsigned int poll_int; /* µs */ 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(struct spu_gov_info_struct, spu_gov_info); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic int calc_freq(struct spu_gov_info_struct *info) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci int cpu; 348c2ecf20Sopenharmony_ci int busy_spus; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci cpu = info->policy->cpu; 378c2ecf20Sopenharmony_ci busy_spus = atomic_read(&cbe_spu_info[cpu_to_node(cpu)].busy_spus); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci info->busy_spus = calc_load(info->busy_spus, EXP, busy_spus * FIXED_1); 408c2ecf20Sopenharmony_ci pr_debug("cpu %d: busy_spus=%d, info->busy_spus=%ld\n", 418c2ecf20Sopenharmony_ci cpu, busy_spus, info->busy_spus); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci return info->policy->max * info->busy_spus / FIXED_1; 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic void spu_gov_work(struct work_struct *work) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct spu_gov_info_struct *info; 498c2ecf20Sopenharmony_ci int delay; 508c2ecf20Sopenharmony_ci unsigned long target_freq; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci info = container_of(work, struct spu_gov_info_struct, work.work); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* after cancel_delayed_work_sync we unset info->policy */ 558c2ecf20Sopenharmony_ci BUG_ON(info->policy == NULL); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci target_freq = calc_freq(info); 588c2ecf20Sopenharmony_ci __cpufreq_driver_target(info->policy, target_freq, CPUFREQ_RELATION_H); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci delay = usecs_to_jiffies(info->poll_int); 618c2ecf20Sopenharmony_ci schedule_delayed_work_on(info->policy->cpu, &info->work, delay); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic void spu_gov_init_work(struct spu_gov_info_struct *info) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci int delay = usecs_to_jiffies(info->poll_int); 678c2ecf20Sopenharmony_ci INIT_DEFERRABLE_WORK(&info->work, spu_gov_work); 688c2ecf20Sopenharmony_ci schedule_delayed_work_on(info->policy->cpu, &info->work, delay); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic void spu_gov_cancel_work(struct spu_gov_info_struct *info) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&info->work); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic int spu_gov_start(struct cpufreq_policy *policy) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci unsigned int cpu = policy->cpu; 798c2ecf20Sopenharmony_ci struct spu_gov_info_struct *info = &per_cpu(spu_gov_info, cpu); 808c2ecf20Sopenharmony_ci struct spu_gov_info_struct *affected_info; 818c2ecf20Sopenharmony_ci int i; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (!cpu_online(cpu)) { 848c2ecf20Sopenharmony_ci printk(KERN_ERR "cpu %d is not online\n", cpu); 858c2ecf20Sopenharmony_ci return -EINVAL; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (!policy->cur) { 898c2ecf20Sopenharmony_ci printk(KERN_ERR "no cpu specified in policy\n"); 908c2ecf20Sopenharmony_ci return -EINVAL; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* initialize spu_gov_info for all affected cpus */ 948c2ecf20Sopenharmony_ci for_each_cpu(i, policy->cpus) { 958c2ecf20Sopenharmony_ci affected_info = &per_cpu(spu_gov_info, i); 968c2ecf20Sopenharmony_ci affected_info->policy = policy; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci info->poll_int = POLL_TIME; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* setup timer */ 1028c2ecf20Sopenharmony_ci spu_gov_init_work(info); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return 0; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic void spu_gov_stop(struct cpufreq_policy *policy) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci unsigned int cpu = policy->cpu; 1108c2ecf20Sopenharmony_ci struct spu_gov_info_struct *info = &per_cpu(spu_gov_info, cpu); 1118c2ecf20Sopenharmony_ci int i; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* cancel timer */ 1148c2ecf20Sopenharmony_ci spu_gov_cancel_work(info); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* clean spu_gov_info for all affected cpus */ 1178c2ecf20Sopenharmony_ci for_each_cpu (i, policy->cpus) { 1188c2ecf20Sopenharmony_ci info = &per_cpu(spu_gov_info, i); 1198c2ecf20Sopenharmony_ci info->policy = NULL; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic struct cpufreq_governor spu_governor = { 1248c2ecf20Sopenharmony_ci .name = "spudemand", 1258c2ecf20Sopenharmony_ci .start = spu_gov_start, 1268c2ecf20Sopenharmony_ci .stop = spu_gov_stop, 1278c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_cicpufreq_governor_init(spu_governor); 1308c2ecf20Sopenharmony_cicpufreq_governor_exit(spu_governor); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1338c2ecf20Sopenharmony_ciMODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>"); 134