2 * spu aware cpufreq governor for the cell processor
4 * © Copyright IBM Corporation 2006-2008
6 * Author: Christian Krafft <krafft@de.ibm.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include <linux/cpufreq.h>
24 #include <linux/sched.h>
25 #include <linux/module.h>
26 #include <linux/timer.h>
27 #include <linux/workqueue.h>
28 #include <linux/atomic.h>
29 #include <asm/machdep.h>
32 #define POLL_TIME 100000 /* in µs */
33 #define EXP 753 /* exp(-1) in fixed-point */
35 struct spu_gov_info_struct
{
36 unsigned long busy_spus
; /* fixed-point */
37 struct cpufreq_policy
*policy
;
38 struct delayed_work work
;
39 unsigned int poll_int
; /* µs */
41 static DEFINE_PER_CPU(struct spu_gov_info_struct
, spu_gov_info
);
43 static int calc_freq(struct spu_gov_info_struct
*info
)
48 cpu
= info
->policy
->cpu
;
49 busy_spus
= atomic_read(&cbe_spu_info
[cpu_to_node(cpu
)].busy_spus
);
51 CALC_LOAD(info
->busy_spus
, EXP
, busy_spus
* FIXED_1
);
52 pr_debug("cpu %d: busy_spus=%d, info->busy_spus=%ld\n",
53 cpu
, busy_spus
, info
->busy_spus
);
55 return info
->policy
->max
* info
->busy_spus
/ FIXED_1
;
58 static void spu_gov_work(struct work_struct
*work
)
60 struct spu_gov_info_struct
*info
;
62 unsigned long target_freq
;
64 info
= container_of(work
, struct spu_gov_info_struct
, work
.work
);
66 /* after cancel_delayed_work_sync we unset info->policy */
67 BUG_ON(info
->policy
== NULL
);
69 target_freq
= calc_freq(info
);
70 __cpufreq_driver_target(info
->policy
, target_freq
, CPUFREQ_RELATION_H
);
72 delay
= usecs_to_jiffies(info
->poll_int
);
73 schedule_delayed_work_on(info
->policy
->cpu
, &info
->work
, delay
);
76 static void spu_gov_init_work(struct spu_gov_info_struct
*info
)
78 int delay
= usecs_to_jiffies(info
->poll_int
);
79 INIT_DEFERRABLE_WORK(&info
->work
, spu_gov_work
);
80 schedule_delayed_work_on(info
->policy
->cpu
, &info
->work
, delay
);
83 static void spu_gov_cancel_work(struct spu_gov_info_struct
*info
)
85 cancel_delayed_work_sync(&info
->work
);
88 static int spu_gov_govern(struct cpufreq_policy
*policy
, unsigned int event
)
90 unsigned int cpu
= policy
->cpu
;
91 struct spu_gov_info_struct
*info
, *affected_info
;
95 info
= &per_cpu(spu_gov_info
, cpu
);
98 case CPUFREQ_GOV_START
:
99 if (!cpu_online(cpu
)) {
100 printk(KERN_ERR
"cpu %d is not online\n", cpu
);
106 printk(KERN_ERR
"no cpu specified in policy\n");
111 /* initialize spu_gov_info for all affected cpus */
112 for_each_cpu(i
, policy
->cpus
) {
113 affected_info
= &per_cpu(spu_gov_info
, i
);
114 affected_info
->policy
= policy
;
117 info
->poll_int
= POLL_TIME
;
120 spu_gov_init_work(info
);
124 case CPUFREQ_GOV_STOP
:
126 spu_gov_cancel_work(info
);
128 /* clean spu_gov_info for all affected cpus */
129 for_each_cpu (i
, policy
->cpus
) {
130 info
= &per_cpu(spu_gov_info
, i
);
140 static struct cpufreq_governor spu_governor
= {
142 .governor
= spu_gov_govern
,
143 .owner
= THIS_MODULE
,
147 * module init and destoy
150 static int __init
spu_gov_init(void)
154 ret
= cpufreq_register_governor(&spu_governor
);
156 printk(KERN_ERR
"registration of governor failed\n");
160 static void __exit
spu_gov_exit(void)
162 cpufreq_unregister_governor(&spu_governor
);
166 module_init(spu_gov_init
);
167 module_exit(spu_gov_exit
);
169 MODULE_LICENSE("GPL");
170 MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>");