]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/blob - drivers/misc/tegra-profiler/power_clk.c
c3b5aa3596c7be6afb56cf78eef39aa2c69929b3
[sojka/nv-tegra/linux-3.10.git] / drivers / misc / tegra-profiler / power_clk.c
1 /*
2  * drivers/misc/tegra-profiler/power_clk.c
3  *
4  * Copyright (c) 2015, NVIDIA CORPORATION.  All rights reserved.
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms and conditions of the GNU General Public License,
8  * version 2, as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  */
16
17 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
18
19 #include <linux/module.h>
20 #include <linux/cpufreq.h>
21 #include <linux/clk.h>
22 #include <linux/notifier.h>
23 #include <linux/cpu.h>
24 #include <linux/timer.h>
25 #include <linux/err.h>
26
27 #include <linux/tegra_profiler.h>
28
29 #include "power_clk.h"
30 #include "quadd.h"
31 #include "hrt.h"
32 #include "comm.h"
33 #include "debug.h"
34
35 #define POWER_CLK_MAX_VALUES    32
36
37 typedef int (*notifier_call_ft)(struct notifier_block *, unsigned long, void *);
38
39 struct power_clk_data {
40         unsigned long value;
41         unsigned long prev;
42 };
43
44 struct power_clk_source {
45         int type;
46
47         struct clk *clkp;
48         struct notifier_block nb;
49
50         int nr;
51         struct power_clk_data data[POWER_CLK_MAX_VALUES];
52
53         unsigned long long counter;
54         atomic_t active;
55
56         struct mutex lock;
57 };
58
59 struct power_clk_context_s {
60         struct power_clk_source cpu;
61         struct power_clk_source gpu;
62         struct power_clk_source emc;
63
64         struct timer_list timer;
65         unsigned int period;
66
67         struct quadd_ctx *quadd_ctx;
68 };
69
70 enum {
71         QUADD_POWER_CLK_CPU = 1,
72         QUADD_POWER_CLK_GPU,
73         QUADD_POWER_CLK_EMC,
74 };
75
76 static struct power_clk_context_s power_ctx;
77
78 static void check_clks(void);
79
80 static void read_source(struct power_clk_source *s)
81 {
82         int i;
83
84         mutex_lock(&s->lock);
85
86         switch (s->type) {
87         case QUADD_POWER_CLK_CPU:
88                 /* update cpu frequency */
89                 for (i = 0; i < nr_cpu_ids; i++)
90                         s->data[i].value = cpufreq_get(i);
91                 break;
92
93         case QUADD_POWER_CLK_GPU:
94                 /* update gpu frequency */
95                 s->clkp = clk_get_sys("3d", NULL);
96                 if (!IS_ERR_OR_NULL(s->clkp)) {
97                         s->data[0].value =
98                                 clk_get_rate(s->clkp) / 1000;
99                         clk_put(s->clkp);
100                 }
101                 break;
102
103         case QUADD_POWER_CLK_EMC:
104                 /* update emc frequency */
105                 s->clkp = clk_get_sys("cpu", "emc");
106                 if (!IS_ERR_OR_NULL(s->clkp)) {
107                         s->data[0].value =
108                                 clk_get_rate(s->clkp) / 1000;
109                         clk_put(s->clkp);
110                 }
111                 break;
112
113         default:
114                 pr_err_once("%s: error: invalid power_clk type\n", __func__);
115                 return;
116         }
117
118         mutex_unlock(&s->lock);
119         s->counter++;
120 }
121
122 static int
123 gpu_notifier_call(struct notifier_block *nb, unsigned long val, void *ptr)
124 {
125         read_source(&power_ctx.gpu);
126         check_clks();
127
128         return 0;
129 }
130
131 static int
132 emc_notifier_call(struct notifier_block *nb, unsigned long val, void *ptr)
133 {
134         read_source(&power_ctx.emc);
135         check_clks();
136
137         return 0;
138 }
139
140 static int
141 cpu_notifier_call(struct notifier_block *nb, unsigned long val, void *ptr)
142 {
143         read_source(&power_ctx.cpu);
144
145 #ifndef CONFIG_COMMON_CLK
146         read_source(&power_ctx.gpu);
147         read_source(&power_ctx.emc);
148 #endif
149
150         check_clks();
151
152         return 0;
153 }
154
155 static void make_sample(void)
156 {
157         int i;
158         u32 extra_cpus[NR_CPUS];
159         struct power_clk_source *s;
160         struct quadd_iovec vec;
161
162         struct quadd_record_data record;
163         struct quadd_power_rate_data *power_rate = &record.power_rate;
164
165         record.record_type = QUADD_RECORD_TYPE_POWER_RATE;
166
167         power_rate->time = quadd_get_time();
168
169         s = &power_ctx.cpu;
170         mutex_lock(&s->lock);
171         if (atomic_read(&s->active)) {
172                 power_rate->nr_cpus = s->nr;
173                 for (i = 0; i < s->nr; i++)
174                         extra_cpus[i] = s->data[i].value;
175         } else {
176                 power_rate->nr_cpus = 0;
177         }
178         mutex_unlock(&s->lock);
179
180         s = &power_ctx.gpu;
181         mutex_lock(&s->lock);
182         if (atomic_read(&s->active))
183                 power_rate->gpu = s->data[0].value;
184         else
185                 power_rate->gpu = 0;
186
187         mutex_unlock(&s->lock);
188
189         s = &power_ctx.emc;
190         mutex_lock(&s->lock);
191         if (atomic_read(&s->active))
192                 power_rate->emc = s->data[0].value;
193         else
194                 power_rate->emc = 0;
195
196         mutex_unlock(&s->lock);
197 /*
198         pr_debug("make_sample: cpu: %u/%u/%u/%u, gpu: %u, emc: %u\n",
199                  extra_cpus[0], extra_cpus[1], extra_cpus[2], extra_cpus[3],
200                  power_rate->gpu, power_rate->emc);
201 */
202         vec.base = extra_cpus;
203         vec.len = power_rate->nr_cpus * sizeof(extra_cpus[0]);
204
205         quadd_put_sample(&record, &vec, 1);
206 }
207
208 static inline int is_data_changed(struct power_clk_source *s)
209 {
210         int i;
211
212         mutex_lock(&s->lock);
213         for (i = 0; i < s->nr; i++) {
214                 if (s->data[i].value != s->data[i].prev) {
215                         mutex_unlock(&s->lock);
216                         return 1;
217                 }
218         }
219         mutex_unlock(&s->lock);
220
221         return 0;
222 }
223
224 static inline void update_data(struct power_clk_source *s)
225 {
226         int i;
227
228         mutex_lock(&s->lock);
229
230         for (i = 0; i < s->nr; i++)
231                 s->data[i].prev = s->data[i].value;
232
233         mutex_unlock(&s->lock);
234 }
235
236 static void check_clks(void)
237 {
238         int changed = 0;
239
240         if (is_data_changed(&power_ctx.cpu)) {
241                 update_data(&power_ctx.cpu);
242                 changed = 1;
243         }
244
245         if (is_data_changed(&power_ctx.gpu)) {
246                 update_data(&power_ctx.gpu);
247                 changed = 1;
248         }
249
250         if (is_data_changed(&power_ctx.emc)) {
251                 update_data(&power_ctx.emc);
252                 changed = 1;
253         }
254 /*
255         pr_debug("cpu: %lu/%lu/%lu/%lu, gpu: %lu, emc: %lu, changed: %s\n",
256                  power_ctx.cpu.data[0].value, power_ctx.cpu.data[1].value,
257                  power_ctx.cpu.data[2].value, power_ctx.cpu.data[3].value,
258                  power_ctx.gpu.data[0].value, power_ctx.emc.data[0].value,
259                  changed ? "yes" : "no");
260 */
261         if (changed)
262                 make_sample();
263 }
264
265 static void reset_data(struct power_clk_source *s)
266 {
267         int i;
268
269         mutex_lock(&s->lock);
270         for (i = 0; i < s->nr; i++) {
271                 s->data[i].value = 0;
272                 s->data[i].prev = 0;
273         }
274         atomic_set(s, 0);
275         mutex_unlock(&s->lock);
276 }
277
278 static void init_source(struct power_clk_source *s,
279                         notifier_call_ft notifier,
280                         int nr_values,
281                         int type)
282 {
283         s->type = type;
284         s->nb.notifier_call = notifier;
285         s->nr = nr_values;
286
287         mutex_init(&s->lock);
288         reset_data(s);
289 }
290
291 static void
292 power_clk_work_func(struct work_struct *dummy)
293 {
294 #ifndef CONFIG_COMMON_CLK
295         read_source(&power_ctx.gpu);
296         read_source(&power_ctx.emc);
297
298         check_clks();
299 #endif
300 }
301
302 static DECLARE_WORK(power_clk_work, power_clk_work_func);
303
304 static void power_clk_timer(unsigned long data)
305 {
306         struct timer_list *timer = &power_ctx.timer;
307
308         schedule_work(&power_clk_work);
309         timer->expires = jiffies + msecs_to_jiffies(power_ctx.period);
310         add_timer(timer);
311 }
312
313 int quadd_power_clk_start(void)
314 {
315         struct power_clk_source *s;
316         int status;
317         struct timer_list *timer = &power_ctx.timer;
318         struct quadd_parameters *param = &power_ctx.quadd_ctx->param;
319
320         if (param->power_rate_freq == 0) {
321                 pr_info("power_clk is not started\n");
322                 return 0;
323         }
324
325 #ifdef CONFIG_COMMON_CLK
326         power_ctx.period = 0;
327 #else
328         power_ctx.period = MSEC_PER_SEC / param->power_rate_freq;
329 #endif
330         pr_info("power_clk: start, freq: %d\n",
331                 param->power_rate_freq);
332
333         /* setup gpu frequency */
334         s = &power_ctx.gpu;
335         s->clkp = clk_get_sys("3d", NULL);
336         if (!IS_ERR_OR_NULL(s->clkp)) {
337 #ifdef CONFIG_COMMON_CLK
338                 status = clk_notifier_register(s->clkp, s->nb);
339                 if (status < 0) {
340                         pr_err("error: could not setup gpu freq\n");
341                         clk_put(s->clkp);
342                         return status;
343                 }
344 #endif
345                 clk_put(s->clkp);
346                 reset_data(s);
347                 atomic_set(&s->active, 1);
348         } else {
349                 pr_warn("warning: could not setup gpu freq\n");
350                 atomic_set(&s->active, 0);
351         }
352
353         /* setup emc frequency */
354         s = &power_ctx.emc;
355         s->clkp = clk_get_sys("cpu", "emc");
356         if (!IS_ERR_OR_NULL(s->clkp)) {
357 #ifdef CONFIG_COMMON_CLK
358                 status = clk_notifier_register(s->clkp, s->nb);
359                 if (status < 0) {
360                         pr_err("error: could not setup emc freq\n");
361                         clk_put(s->clkp);
362                         return status;
363                 }
364 #endif
365                 clk_put(s->clkp);
366                 reset_data(s);
367                 atomic_set(&s->active, 1);
368         } else {
369                 pr_warn("warning: could not setup emc freq\n");
370                 atomic_set(&s->active, 0);
371         }
372
373         /* setup cpu frequency notifier */
374         s = &power_ctx.cpu;
375         status = register_cpu_notifier(&s->nb);
376         if (status < 0) {
377                 pr_err("error: could not setup cpu freq\n");
378                 return status;
379         }
380         reset_data(s);
381
382         if (power_ctx.period > 0) {
383                 init_timer(timer);
384                 timer->function = power_clk_timer;
385                 timer->expires = jiffies + msecs_to_jiffies(power_ctx.period);
386                 timer->data = 0;
387                 add_timer(timer);
388         }
389
390         atomic_set(&s->active, 1);
391
392         return 0;
393 }
394
395 void quadd_power_clk_stop(void)
396 {
397         struct power_clk_source *s;
398
399         if (power_ctx.quadd_ctx->param.power_rate_freq == 0)
400                 return;
401
402         if (power_ctx.period > 0)
403                 del_timer_sync(&power_ctx.timer);
404
405         s = &power_ctx.gpu;
406         if (atomic_cmpxchg(&s->active, 1, 0)) {
407 #ifdef CONFIG_COMMON_CLK
408                 if (s->clkp)
409                         clk_notifier_unregister(s->clkp, &s->nb);
410 #endif
411         }
412
413         s = &power_ctx.emc;
414         if (atomic_cmpxchg(&s->active, 1, 0)) {
415 #ifdef CONFIG_COMMON_CLK
416                 if (s->clkp)
417                         clk_notifier_unregister(s->clkp, &s->nb);
418 #endif
419         }
420
421         s = &power_ctx.cpu;
422         if (atomic_cmpxchg(&s->active, 1, 0)) {
423                 pr_info("power_clk: stop\n");
424                 unregister_cpu_notifier(&s->nb);
425         }
426 }
427
428 int quadd_power_clk_init(struct quadd_ctx *quadd_ctx)
429 {
430         init_source(&power_ctx.cpu, cpu_notifier_call, nr_cpu_ids,
431                     QUADD_POWER_CLK_CPU);
432         init_source(&power_ctx.gpu, gpu_notifier_call, 1, QUADD_POWER_CLK_GPU);
433         init_source(&power_ctx.emc, emc_notifier_call, 1, QUADD_POWER_CLK_EMC);
434
435         power_ctx.quadd_ctx = quadd_ctx;
436
437         return 0;
438 }
439
440 void quadd_power_clk_deinit(void)
441 {
442         quadd_power_clk_stop();
443 }