]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/blob - drivers/misc/tegra-profiler/power_clk.c
7af0753919393fe12e6f269876be046eedfc7ec5
[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) 2013-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/cpufreq.h>
20 #include <linux/clk.h>
21 #include <linux/notifier.h>
22 #include <linux/workqueue.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 *,
38                                 unsigned long, void *);
39
40 struct power_clk_data {
41         unsigned long value;
42         unsigned long prev;
43 };
44
45 struct power_clk_source {
46         int type;
47
48         struct clk *clkp;
49         struct notifier_block nb;
50
51         int nr;
52         struct power_clk_data data[POWER_CLK_MAX_VALUES];
53
54         atomic_t active;
55         struct mutex lock;
56 };
57
58 struct power_clk_context_s {
59         struct power_clk_source cpu;
60         struct power_clk_source gpu;
61         struct power_clk_source emc;
62
63         struct timer_list timer;
64         unsigned int period;
65
66         struct quadd_ctx *quadd_ctx;
67 };
68
69 enum {
70         QUADD_POWER_CLK_CPU = 1,
71         QUADD_POWER_CLK_GPU,
72         QUADD_POWER_CLK_EMC,
73 };
74
75 static struct power_clk_context_s power_ctx;
76
77 static void make_sample(void)
78 {
79         int i;
80         u32 extra_cpus[NR_CPUS];
81         struct power_clk_source *s;
82         struct quadd_iovec vec;
83
84         struct quadd_record_data record;
85         struct quadd_power_rate_data *power_rate = &record.power_rate;
86
87         record.record_type = QUADD_RECORD_TYPE_POWER_RATE;
88
89         power_rate->time = quadd_get_time();
90
91         s = &power_ctx.cpu;
92         mutex_lock(&s->lock);
93         if (atomic_read(&s->active)) {
94                 power_rate->nr_cpus = s->nr;
95                 for (i = 0; i < s->nr; i++)
96                         extra_cpus[i] = s->data[i].value;
97         } else {
98                 power_rate->nr_cpus = 0;
99         }
100         mutex_unlock(&s->lock);
101
102         s = &power_ctx.gpu;
103         mutex_lock(&s->lock);
104         if (atomic_read(&s->active))
105                 power_rate->gpu = s->data[0].value;
106         else
107                 power_rate->gpu = 0;
108
109         mutex_unlock(&s->lock);
110
111         s = &power_ctx.emc;
112         mutex_lock(&s->lock);
113         if (atomic_read(&s->active))
114                 power_rate->emc = s->data[0].value;
115         else
116                 power_rate->emc = 0;
117
118         mutex_unlock(&s->lock);
119 /*
120         pr_debug("make_sample: cpu: %u/%u/%u/%u, gpu: %u, emc: %u\n",
121                  extra_cpus[0], extra_cpus[1], extra_cpus[2], extra_cpus[3],
122                  power_rate->gpu, power_rate->emc);
123 */
124         vec.base = extra_cpus;
125         vec.len = power_rate->nr_cpus * sizeof(extra_cpus[0]);
126
127         quadd_put_sample(&record, &vec, 1);
128 }
129
130 static inline int
131 is_data_changed(struct power_clk_source *s)
132 {
133         int i;
134
135         for (i = 0; i < s->nr; i++) {
136                 if (s->data[i].value != s->data[i].prev)
137                         return 1;
138         }
139
140         return 0;
141 }
142
143 static inline void
144 update_data(struct power_clk_source *s)
145 {
146         int i;
147
148         for (i = 0; i < s->nr; i++)
149                 s->data[i].prev = s->data[i].value;
150 }
151
152 static void check_clks(void)
153 {
154         struct power_clk_source *s;
155         int changed = 0;
156
157         s = &power_ctx.gpu;
158         mutex_lock(&s->lock);
159         if (is_data_changed(s)) {
160                 update_data(s);
161                 changed = 1;
162         }
163         mutex_unlock(&s->lock);
164
165         s = &power_ctx.emc;
166         mutex_lock(&s->lock);
167         if (is_data_changed(s)) {
168                 update_data(s);
169                 changed = 1;
170         }
171         mutex_unlock(&s->lock);
172
173         if (changed) {
174                 pr_debug("gpu: %lu, emc: %lu\n",
175                          power_ctx.gpu.data[0].value,
176                          power_ctx.emc.data[0].value);
177
178                 make_sample();
179         }
180 }
181
182 static void check_source(struct power_clk_source *s)
183 {
184         mutex_lock(&s->lock);
185
186         if (!is_data_changed(s))
187                 goto out_unlock;
188
189         pr_debug("cpu: %lu/%lu/%lu/%lu\n",
190                  power_ctx.cpu.data[0].value,
191                  power_ctx.cpu.data[1].value,
192                  power_ctx.cpu.data[2].value,
193                  power_ctx.cpu.data[3].value);
194
195         update_data(s);
196         mutex_unlock(&s->lock);
197
198         make_sample();
199         return;
200
201 out_unlock:
202         mutex_unlock(&s->lock);
203 }
204
205 static void
206 read_source(struct power_clk_source *s, int cpu)
207 {
208         mutex_lock(&s->lock);
209
210         switch (s->type) {
211         case QUADD_POWER_CLK_CPU:
212                 /* update cpu frequency */
213                 if (cpu < 0 || cpu >= POWER_CLK_MAX_VALUES) {
214                         pr_err_once("error: cpu id: %d\n", cpu);
215                         break;
216                 }
217
218                 s->data[cpu].value = cpufreq_get(cpu);
219                 pr_debug("QUADD_POWER_CLK_CPU, cpu: %d, value: %lu\n",
220                          cpu, s->data[cpu].value);
221                 break;
222
223         case QUADD_POWER_CLK_GPU:
224                 /* update gpu frequency */
225                 s->clkp = clk_get_sys("3d", NULL);
226                 if (!IS_ERR_OR_NULL(s->clkp)) {
227                         s->data[0].value =
228                                 clk_get_rate(s->clkp) / 1000;
229                         clk_put(s->clkp);
230                 }
231                 pr_debug("QUADD_POWER_CLK_GPU, value: %lu\n",
232                          s->data[0].value);
233                 break;
234
235         case QUADD_POWER_CLK_EMC:
236                 /* update emc frequency */
237                 s->clkp = clk_get_sys("cpu", "emc");
238                 if (!IS_ERR_OR_NULL(s->clkp)) {
239                         s->data[0].value =
240                                 clk_get_rate(s->clkp) / 1000;
241                         clk_put(s->clkp);
242                 }
243                 pr_debug("QUADD_POWER_CLK_EMC, value: %lu\n",
244                          s->data[0].value);
245                 break;
246
247         default:
248                 pr_err_once("error: invalid power_clk type\n");
249                 break;
250         }
251
252         mutex_unlock(&s->lock);
253 }
254
255 static int
256 gpu_notifier_call(struct notifier_block *nb,
257                   unsigned long action, void *data)
258 {
259         read_source(&power_ctx.gpu, -1);
260         check_clks();
261
262         return NOTIFY_DONE;
263 }
264
265 static int
266 emc_notifier_call(struct notifier_block *nb,
267                   unsigned long action, void *data)
268 {
269         read_source(&power_ctx.emc, -1);
270         check_clks();
271
272         return NOTIFY_DONE;
273 }
274
275 static void
276 read_cpufreq(struct power_clk_source *s, struct cpufreq_freqs *freq)
277 {
278         int cpu, cpufreq;
279
280         mutex_lock(&s->lock);
281
282         if (!atomic_read(&s->active))
283                 goto out_unlock;
284
285         cpu = freq->cpu;
286         cpufreq = freq->new;
287
288         pr_debug("cpu: %d, cpufreq: %d\n", cpu, cpufreq);
289
290         if (cpu >= s->nr) {
291                 pr_err_once("error: cpu id: %d\n", cpu);
292                 goto out_unlock;
293         }
294
295         s->data[cpu].value = cpufreq;
296
297         pr_debug("[%d] cpufreq: %u --> %u\n",
298                  cpu, freq->old, cpufreq);
299
300         mutex_unlock(&s->lock);
301         check_source(s);
302         return;
303
304 out_unlock:
305         mutex_unlock(&s->lock);
306 }
307
308 static int
309 cpufreq_notifier_call(struct notifier_block *nb,
310                       unsigned long action, void *hcpu)
311 {
312         struct cpufreq_freqs *freq;
313         struct power_clk_source *s = &power_ctx.cpu;
314
315         if (!atomic_read(&s->active))
316                 return 0;
317
318         pr_debug("action: %lu\n", action);
319
320         if (action == CPUFREQ_POSTCHANGE ||
321             action == CPUFREQ_RESUMECHANGE) {
322                 freq = hcpu;
323                 read_cpufreq(s, freq);
324         }
325
326         return 0;
327 }
328
329 static void reset_data(struct power_clk_source *s)
330 {
331         int i;
332
333         mutex_lock(&s->lock);
334         for (i = 0; i < s->nr; i++) {
335                 s->data[i].value = 0;
336                 s->data[i].prev = 0;
337         }
338         mutex_unlock(&s->lock);
339 }
340
341 static void init_source(struct power_clk_source *s,
342                         notifier_call_ft notifier,
343                         int nr_values,
344                         int type)
345 {
346         s->type = type;
347         s->nb.notifier_call = notifier;
348         s->nr = min_t(int, nr_values, POWER_CLK_MAX_VALUES);
349         atomic_set(&s->active, 0);
350         mutex_init(&s->lock);
351
352         reset_data(s);
353 }
354
355 static void
356 power_clk_work_func(struct work_struct *work)
357 {
358         read_source(&power_ctx.gpu, -1);
359         read_source(&power_ctx.emc, -1);
360
361         check_clks();
362 }
363
364 static DECLARE_WORK(power_clk_work, power_clk_work_func);
365
366 static void power_clk_timer(unsigned long data)
367 {
368         struct timer_list *timer = &power_ctx.timer;
369
370         schedule_work(&power_clk_work);
371         timer->expires = jiffies + msecs_to_jiffies(power_ctx.period);
372         add_timer(timer);
373 }
374
375 static void
376 read_all_sources_work_func(struct work_struct *work)
377 {
378         int cpu_id;
379
380         for_each_possible_cpu(cpu_id)
381                 read_source(&power_ctx.cpu, cpu_id);
382
383         read_source(&power_ctx.gpu, -1);
384         read_source(&power_ctx.emc, -1);
385
386         check_clks();
387 }
388
389 static DECLARE_WORK(read_all_sources_work, read_all_sources_work_func);
390
391 int quadd_power_clk_start(void)
392 {
393         struct power_clk_source *s;
394         struct timer_list *timer = &power_ctx.timer;
395         struct quadd_parameters *param = &power_ctx.quadd_ctx->param;
396
397         if (param->power_rate_freq == 0) {
398                 pr_info("power_clk is not started\n");
399                 return 0;
400         }
401
402 #ifdef CONFIG_COMMON_CLK
403         power_ctx.period = 0;
404 #else
405         power_ctx.period = MSEC_PER_SEC / param->power_rate_freq;
406         pr_info("clk: use timer\n");
407 #endif
408
409         pr_info("power_clk: start, freq: %d\n",
410                 param->power_rate_freq);
411
412         /* setup gpu frequency */
413         s = &power_ctx.gpu;
414         s->clkp = clk_get_sys("3d", NULL);
415         if (!IS_ERR_OR_NULL(s->clkp)) {
416 #ifdef CONFIG_COMMON_CLK
417                 status = clk_notifier_register(s->clkp, s->nb);
418                 if (status < 0) {
419                         pr_err("error: could not setup gpu freq\n");
420                         clk_put(s->clkp);
421                         return status;
422                 }
423 #endif
424                 clk_put(s->clkp);
425                 reset_data(s);
426                 atomic_set(&s->active, 1);
427         } else {
428                 pr_warn("warning: could not setup gpu freq\n");
429                 atomic_set(&s->active, 0);
430         }
431
432         /* setup emc frequency */
433         s = &power_ctx.emc;
434         s->clkp = clk_get_sys("cpu", "emc");
435         if (!IS_ERR_OR_NULL(s->clkp)) {
436 #ifdef CONFIG_COMMON_CLK
437                 status = clk_notifier_register(s->clkp, s->nb);
438                 if (status < 0) {
439                         pr_err("error: could not setup emc freq\n");
440                         clk_put(s->clkp);
441                         return status;
442                 }
443 #endif
444                 clk_put(s->clkp);
445                 reset_data(s);
446                 atomic_set(&s->active, 1);
447         } else {
448                 pr_warn("warning: could not setup emc freq\n");
449                 atomic_set(&s->active, 0);
450         }
451
452         /* setup cpu frequency notifier */
453         s = &power_ctx.cpu;
454         reset_data(s);
455         atomic_set(&s->active, 1);
456
457         if (power_ctx.period > 0) {
458                 init_timer(timer);
459                 timer->function = power_clk_timer;
460                 timer->expires = jiffies + msecs_to_jiffies(power_ctx.period);
461                 timer->data = 0;
462                 add_timer(timer);
463         }
464
465         schedule_work(&read_all_sources_work);
466
467         return 0;
468 }
469
470 void quadd_power_clk_stop(void)
471 {
472         struct power_clk_source *s;
473         struct quadd_parameters *param = &power_ctx.quadd_ctx->param;
474
475         if (param->power_rate_freq == 0)
476                 return;
477
478         if (power_ctx.period > 0)
479                 del_timer_sync(&power_ctx.timer);
480
481         s = &power_ctx.gpu;
482         mutex_lock(&s->lock);
483         if (atomic_cmpxchg(&s->active, 1, 0)) {
484 #ifdef CONFIG_COMMON_CLK
485                 if (s->clkp)
486                         clk_notifier_unregister(s->clkp, &s->nb);
487 #endif
488         }
489         mutex_unlock(&s->lock);
490
491         s = &power_ctx.emc;
492         mutex_lock(&s->lock);
493         if (atomic_cmpxchg(&s->active, 1, 0)) {
494 #ifdef CONFIG_COMMON_CLK
495                 if (s->clkp)
496                         clk_notifier_unregister(s->clkp, &s->nb);
497 #endif
498         }
499         mutex_unlock(&s->lock);
500
501         s = &power_ctx.cpu;
502         mutex_lock(&s->lock);
503         atomic_set(&s->active, 0);
504         mutex_unlock(&s->lock);
505
506         pr_info("power_clk: stop\n");
507 }
508
509 int quadd_power_clk_init(struct quadd_ctx *quadd_ctx)
510 {
511         init_source(&power_ctx.cpu, cpufreq_notifier_call, nr_cpu_ids,
512                     QUADD_POWER_CLK_CPU);
513
514         init_source(&power_ctx.gpu, gpu_notifier_call, 1, QUADD_POWER_CLK_GPU);
515         init_source(&power_ctx.emc, emc_notifier_call, 1, QUADD_POWER_CLK_EMC);
516
517         cpufreq_register_notifier(&power_ctx.cpu.nb,
518                                   CPUFREQ_TRANSITION_NOTIFIER);
519
520         power_ctx.quadd_ctx = quadd_ctx;
521
522         return 0;
523 }
524
525 void quadd_power_clk_deinit(void)
526 {
527         quadd_power_clk_stop();
528         cpufreq_unregister_notifier(&power_ctx.cpu.nb,
529                                     CPUFREQ_TRANSITION_NOTIFIER);
530 }