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/timer.h>
26 #include <linux/workqueue.h>
27 #include <linux/atomic.h>
28 #include <asm/machdep.h>
31 #define POLL_TIME 100000 /* in µs */
32 #define EXP 753 /* exp(-1) in fixed-point */
34 struct spu_gov_info_struct
{
35 unsigned long busy_spus
; /* fixed-point */
36 struct cpufreq_policy
*policy
;
37 struct delayed_work work
;
38 unsigned int poll_int
; /* µs */
40 static DEFINE_PER_CPU(struct spu_gov_info_struct
, spu_gov_info
);
42 static int calc_freq(struct spu_gov_info_struct
*info
)
47 cpu
= info
->policy
->cpu
;
48 busy_spus
= atomic_read(&cbe_spu_info
[cpu_to_node(cpu
)].busy_spus
);
50 CALC_LOAD(info
->busy_spus
, EXP
, busy_spus
* FIXED_1
);
51 pr_debug("cpu %d: busy_spus=%d, info->busy_spus=%ld\n",
52 cpu
, busy_spus
, info
->busy_spus
);
54 return info
->policy
->max
* info
->busy_spus
/ FIXED_1
;
57 static void spu_gov_work(struct work_struct
*work
)
59 struct spu_gov_info_struct
*info
;
61 unsigned long target_freq
;
63 info
= container_of(work
, struct spu_gov_info_struct
, work
.work
);
65 /* after cancel_delayed_work_sync we unset info->policy */
66 BUG_ON(info
->policy
== NULL
);
68 target_freq
= calc_freq(info
);
69 __cpufreq_driver_target(info
->policy
, target_freq
, CPUFREQ_RELATION_H
);
71 delay
= usecs_to_jiffies(info
->poll_int
);
72 schedule_delayed_work_on(info
->policy
->cpu
, &info
->work
, delay
);
75 static void spu_gov_init_work(struct spu_gov_info_struct
*info
)
77 int delay
= usecs_to_jiffies(info
->poll_int
);
78 INIT_DELAYED_WORK_DEFERRABLE(&info
->work
, spu_gov_work
);
79 schedule_delayed_work_on(info
->policy
->cpu
, &info
->work
, delay
);
82 static void spu_gov_cancel_work(struct spu_gov_info_struct
*info
)
84 cancel_delayed_work_sync(&info
->work
);
87 static int spu_gov_govern(struct cpufreq_policy
*policy
, unsigned int event
)
89 unsigned int cpu
= policy
->cpu
;
90 struct spu_gov_info_struct
*info
, *affected_info
;
94 info
= &per_cpu(spu_gov_info
, cpu
);
97 case CPUFREQ_GOV_START
:
98 if (!cpu_online(cpu
)) {
99 printk(KERN_ERR
"cpu %d is not online\n", cpu
);
105 printk(KERN_ERR
"no cpu specified in policy\n");
110 /* initialize spu_gov_info for all affected cpus */
111 for_each_cpu(i
, policy
->cpus
) {
112 affected_info
= &per_cpu(spu_gov_info
, i
);
113 affected_info
->policy
= policy
;
116 info
->poll_int
= POLL_TIME
;
119 spu_gov_init_work(info
);
123 case CPUFREQ_GOV_STOP
:
125 spu_gov_cancel_work(info
);
127 /* clean spu_gov_info for all affected cpus */
128 for_each_cpu (i
, policy
->cpus
) {
129 info
= &per_cpu(spu_gov_info
, i
);
139 static struct cpufreq_governor spu_governor
= {
141 .governor
= spu_gov_govern
,
142 .owner
= THIS_MODULE
,
146 * module init and destoy
149 static int __init
spu_gov_init(void)
153 ret
= cpufreq_register_governor(&spu_governor
);
155 printk(KERN_ERR
"registration of governor failed\n");
159 static void __exit
spu_gov_exit(void)
161 cpufreq_unregister_governor(&spu_governor
);
165 module_init(spu_gov_init
);
166 module_exit(spu_gov_exit
);
168 MODULE_LICENSE("GPL");
169 MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>");