cpufreq_spudemand.c (3336B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * spu aware cpufreq governor for the cell processor 4 * 5 * © Copyright IBM Corporation 2006-2008 6 * 7 * Author: Christian Krafft <krafft@de.ibm.com> 8 */ 9 10#include <linux/cpufreq.h> 11#include <linux/sched.h> 12#include <linux/sched/loadavg.h> 13#include <linux/module.h> 14#include <linux/timer.h> 15#include <linux/workqueue.h> 16#include <linux/atomic.h> 17#include <asm/machdep.h> 18#include <asm/spu.h> 19 20#define POLL_TIME 100000 /* in µs */ 21#define EXP 753 /* exp(-1) in fixed-point */ 22 23struct spu_gov_info_struct { 24 unsigned long busy_spus; /* fixed-point */ 25 struct cpufreq_policy *policy; 26 struct delayed_work work; 27 unsigned int poll_int; /* µs */ 28}; 29static DEFINE_PER_CPU(struct spu_gov_info_struct, spu_gov_info); 30 31static int calc_freq(struct spu_gov_info_struct *info) 32{ 33 int cpu; 34 int busy_spus; 35 36 cpu = info->policy->cpu; 37 busy_spus = atomic_read(&cbe_spu_info[cpu_to_node(cpu)].busy_spus); 38 39 info->busy_spus = calc_load(info->busy_spus, EXP, busy_spus * FIXED_1); 40 pr_debug("cpu %d: busy_spus=%d, info->busy_spus=%ld\n", 41 cpu, busy_spus, info->busy_spus); 42 43 return info->policy->max * info->busy_spus / FIXED_1; 44} 45 46static void spu_gov_work(struct work_struct *work) 47{ 48 struct spu_gov_info_struct *info; 49 int delay; 50 unsigned long target_freq; 51 52 info = container_of(work, struct spu_gov_info_struct, work.work); 53 54 /* after cancel_delayed_work_sync we unset info->policy */ 55 BUG_ON(info->policy == NULL); 56 57 target_freq = calc_freq(info); 58 __cpufreq_driver_target(info->policy, target_freq, CPUFREQ_RELATION_H); 59 60 delay = usecs_to_jiffies(info->poll_int); 61 schedule_delayed_work_on(info->policy->cpu, &info->work, delay); 62} 63 64static void spu_gov_init_work(struct spu_gov_info_struct *info) 65{ 66 int delay = usecs_to_jiffies(info->poll_int); 67 INIT_DEFERRABLE_WORK(&info->work, spu_gov_work); 68 schedule_delayed_work_on(info->policy->cpu, &info->work, delay); 69} 70 71static void spu_gov_cancel_work(struct spu_gov_info_struct *info) 72{ 73 cancel_delayed_work_sync(&info->work); 74} 75 76static int spu_gov_start(struct cpufreq_policy *policy) 77{ 78 unsigned int cpu = policy->cpu; 79 struct spu_gov_info_struct *info = &per_cpu(spu_gov_info, cpu); 80 struct spu_gov_info_struct *affected_info; 81 int i; 82 83 if (!cpu_online(cpu)) { 84 printk(KERN_ERR "cpu %d is not online\n", cpu); 85 return -EINVAL; 86 } 87 88 if (!policy->cur) { 89 printk(KERN_ERR "no cpu specified in policy\n"); 90 return -EINVAL; 91 } 92 93 /* initialize spu_gov_info for all affected cpus */ 94 for_each_cpu(i, policy->cpus) { 95 affected_info = &per_cpu(spu_gov_info, i); 96 affected_info->policy = policy; 97 } 98 99 info->poll_int = POLL_TIME; 100 101 /* setup timer */ 102 spu_gov_init_work(info); 103 104 return 0; 105} 106 107static void spu_gov_stop(struct cpufreq_policy *policy) 108{ 109 unsigned int cpu = policy->cpu; 110 struct spu_gov_info_struct *info = &per_cpu(spu_gov_info, cpu); 111 int i; 112 113 /* cancel timer */ 114 spu_gov_cancel_work(info); 115 116 /* clean spu_gov_info for all affected cpus */ 117 for_each_cpu (i, policy->cpus) { 118 info = &per_cpu(spu_gov_info, i); 119 info->policy = NULL; 120 } 121} 122 123static struct cpufreq_governor spu_governor = { 124 .name = "spudemand", 125 .start = spu_gov_start, 126 .stop = spu_gov_stop, 127 .owner = THIS_MODULE, 128}; 129cpufreq_governor_init(spu_governor); 130cpufreq_governor_exit(spu_governor); 131 132MODULE_LICENSE("GPL"); 133MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>");