]>
Commit | Line | Data |
---|---|---|
880e7105 CK |
1 | /* |
2 | * spu aware cpufreq governor for the cell processor | |
3 | * | |
4 | * © Copyright IBM Corporation 2006-2008 | |
5 | * | |
6 | * Author: Christian Krafft <krafft@de.ibm.com> | |
7 | * | |
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) | |
11 | * any later version. | |
12 | * | |
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. | |
17 | * | |
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. | |
21 | */ | |
22 | ||
23 | #include <linux/cpufreq.h> | |
24 | #include <linux/sched.h> | |
7dfe293c | 25 | #include <linux/module.h> |
880e7105 CK |
26 | #include <linux/timer.h> |
27 | #include <linux/workqueue.h> | |
60063497 | 28 | #include <linux/atomic.h> |
880e7105 CK |
29 | #include <asm/machdep.h> |
30 | #include <asm/spu.h> | |
31 | ||
32 | #define POLL_TIME 100000 /* in µs */ | |
33 | #define EXP 753 /* exp(-1) in fixed-point */ | |
34 | ||
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 */ | |
40 | }; | |
41 | static DEFINE_PER_CPU(struct spu_gov_info_struct, spu_gov_info); | |
42 | ||
880e7105 CK |
43 | static int calc_freq(struct spu_gov_info_struct *info) |
44 | { | |
45 | int cpu; | |
46 | int busy_spus; | |
47 | ||
48 | cpu = info->policy->cpu; | |
49 | busy_spus = atomic_read(&cbe_spu_info[cpu_to_node(cpu)].busy_spus); | |
50 | ||
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); | |
54 | ||
55 | return info->policy->max * info->busy_spus / FIXED_1; | |
56 | } | |
57 | ||
58 | static void spu_gov_work(struct work_struct *work) | |
59 | { | |
60 | struct spu_gov_info_struct *info; | |
61 | int delay; | |
62 | unsigned long target_freq; | |
63 | ||
64 | info = container_of(work, struct spu_gov_info_struct, work.work); | |
65 | ||
66 | /* after cancel_delayed_work_sync we unset info->policy */ | |
67 | BUG_ON(info->policy == NULL); | |
68 | ||
69 | target_freq = calc_freq(info); | |
70 | __cpufreq_driver_target(info->policy, target_freq, CPUFREQ_RELATION_H); | |
71 | ||
72 | delay = usecs_to_jiffies(info->poll_int); | |
b18ae08d | 73 | schedule_delayed_work_on(info->policy->cpu, &info->work, delay); |
880e7105 CK |
74 | } |
75 | ||
76 | static void spu_gov_init_work(struct spu_gov_info_struct *info) | |
77 | { | |
78 | int delay = usecs_to_jiffies(info->poll_int); | |
203b42f7 | 79 | INIT_DEFERRABLE_WORK(&info->work, spu_gov_work); |
b18ae08d | 80 | schedule_delayed_work_on(info->policy->cpu, &info->work, delay); |
880e7105 CK |
81 | } |
82 | ||
83 | static void spu_gov_cancel_work(struct spu_gov_info_struct *info) | |
84 | { | |
85 | cancel_delayed_work_sync(&info->work); | |
86 | } | |
87 | ||
88 | static int spu_gov_govern(struct cpufreq_policy *policy, unsigned int event) | |
89 | { | |
90 | unsigned int cpu = policy->cpu; | |
91 | struct spu_gov_info_struct *info, *affected_info; | |
92 | int i; | |
93 | int ret = 0; | |
94 | ||
95 | info = &per_cpu(spu_gov_info, cpu); | |
96 | ||
97 | switch (event) { | |
98 | case CPUFREQ_GOV_START: | |
99 | if (!cpu_online(cpu)) { | |
100 | printk(KERN_ERR "cpu %d is not online\n", cpu); | |
101 | ret = -EINVAL; | |
102 | break; | |
103 | } | |
104 | ||
105 | if (!policy->cur) { | |
106 | printk(KERN_ERR "no cpu specified in policy\n"); | |
107 | ret = -EINVAL; | |
108 | break; | |
109 | } | |
110 | ||
111 | /* initialize spu_gov_info for all affected cpus */ | |
ae04d140 | 112 | for_each_cpu(i, policy->cpus) { |
880e7105 CK |
113 | affected_info = &per_cpu(spu_gov_info, i); |
114 | affected_info->policy = policy; | |
115 | } | |
116 | ||
117 | info->poll_int = POLL_TIME; | |
118 | ||
119 | /* setup timer */ | |
120 | spu_gov_init_work(info); | |
121 | ||
122 | break; | |
123 | ||
124 | case CPUFREQ_GOV_STOP: | |
125 | /* cancel timer */ | |
126 | spu_gov_cancel_work(info); | |
127 | ||
128 | /* clean spu_gov_info for all affected cpus */ | |
ae04d140 | 129 | for_each_cpu (i, policy->cpus) { |
880e7105 CK |
130 | info = &per_cpu(spu_gov_info, i); |
131 | info->policy = NULL; | |
132 | } | |
133 | ||
134 | break; | |
135 | } | |
136 | ||
137 | return ret; | |
138 | } | |
139 | ||
140 | static struct cpufreq_governor spu_governor = { | |
141 | .name = "spudemand", | |
142 | .governor = spu_gov_govern, | |
143 | .owner = THIS_MODULE, | |
144 | }; | |
145 | ||
146 | /* | |
147 | * module init and destoy | |
148 | */ | |
149 | ||
150 | static int __init spu_gov_init(void) | |
151 | { | |
152 | int ret; | |
153 | ||
880e7105 | 154 | ret = cpufreq_register_governor(&spu_governor); |
b18ae08d | 155 | if (ret) |
880e7105 | 156 | printk(KERN_ERR "registration of governor failed\n"); |
880e7105 CK |
157 | return ret; |
158 | } | |
159 | ||
160 | static void __exit spu_gov_exit(void) | |
161 | { | |
162 | cpufreq_unregister_governor(&spu_governor); | |
880e7105 CK |
163 | } |
164 | ||
165 | ||
166 | module_init(spu_gov_init); | |
167 | module_exit(spu_gov_exit); | |
168 | ||
169 | MODULE_LICENSE("GPL"); | |
170 | MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>"); | |
171 |