misc: tegra-profiler: reduce size of sample
[linux-2.6.git] / drivers / misc / tegra-profiler / power_clk.c
1 /*
2  * drivers/misc/tegra-profiler/power_clk.c
3  *
4  * Copyright (c) 2014, 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
26 #include <linux/tegra_profiler.h>
27
28 #include "power_clk.h"
29 #include "quadd.h"
30 #include "hrt.h"
31 #include "comm.h"
32 #include "debug.h"
33
34 #define POWER_CLK_MAX_VALUES    32
35
36 typedef int (*notifier_call_ft)(struct notifier_block *, unsigned long, void *);
37
38 struct power_clk_data {
39         unsigned long value;
40         unsigned long prev;
41 };
42
43 struct power_clk_source {
44         int type;
45
46         struct clk *clkp;
47         struct notifier_block nb;
48
49         int nr;
50         struct power_clk_data data[POWER_CLK_MAX_VALUES];
51
52         unsigned long long counter;
53         atomic_t active;
54
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 check_clks(void);
78
79 static void read_source(struct power_clk_source *s)
80 {
81         int i;
82
83         mutex_lock(&s->lock);
84
85         switch (s->type) {
86         case QUADD_POWER_CLK_CPU:
87                 /* update cpu frequency */
88                 for (i = 0; i < nr_cpu_ids; i++)
89                         s->data[i].value = cpufreq_get(i);
90                 break;
91
92         case QUADD_POWER_CLK_GPU:
93                 /* update gpu frequency */
94                 s->clkp = clk_get_sys("3d", NULL);
95                 if (s->clkp) {
96                         s->data[0].value =
97                                 clk_get_rate(s->clkp) / 1000;
98                         clk_put(s->clkp);
99                 }
100                 break;
101
102         case QUADD_POWER_CLK_EMC:
103                 /* update emc frequency */
104                 s->clkp = clk_get_sys("cpu", "emc");
105                 if (s->clkp) {
106                         s->data[0].value =
107                                 clk_get_rate(s->clkp) / 1000;
108                         clk_put(s->clkp);
109                 }
110                 break;
111
112         default:
113                 pr_err_once("%s: error: invalid power_clk type\n", __func__);
114                 return;
115         }
116
117         mutex_unlock(&s->lock);
118         s->counter++;
119 }
120
121 static int
122 gpu_notifier_call(struct notifier_block *nb, unsigned long val, void *ptr)
123 {
124         read_source(&power_ctx.gpu);
125         check_clks();
126
127         return 0;
128 }
129
130 static int
131 emc_notifier_call(struct notifier_block *nb, unsigned long val, void *ptr)
132 {
133         read_source(&power_ctx.emc);
134         check_clks();
135
136         return 0;
137 }
138
139 static int
140 cpu_notifier_call(struct notifier_block *nb, unsigned long val, void *ptr)
141 {
142         read_source(&power_ctx.cpu);
143
144 #ifndef CONFIG_COMMON_CLK
145         read_source(&power_ctx.gpu);
146         read_source(&power_ctx.emc);
147 #endif
148
149         check_clks();
150
151         return 0;
152 }
153
154 static void make_sample(void)
155 {
156         int i;
157         u32 extra_cpus[NR_CPUS];
158         struct power_clk_source *s;
159         struct quadd_iovec vec;
160
161         struct quadd_record_data record;
162         struct quadd_power_rate_data *power_rate = &record.power_rate;
163         struct quadd_comm_data_interface *comm = power_ctx.quadd_ctx->comm;
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         comm->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 (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                         return status;
342                 }
343                 clk_put(s->clkp);
344 #endif
345                 reset_data(s);
346                 atomic_set(&s->active, 1);
347         } else {
348                 pr_err("error: could not setup gpu freq\n");
349                 atomic_set(&s->active, 0);
350         }
351
352         /* setup emc frequency */
353         s = &power_ctx.emc;
354         s->clkp = clk_get_sys("cpu", "emc");
355         if (s->clkp) {
356 #ifdef CONFIG_COMMON_CLK
357                 status = clk_notifier_register(s->clkp, s->nb);
358                 if (status < 0) {
359                         pr_err("error: could not setup emc freq\n");
360                         return status;
361                 }
362                 clk_put(s->clkp);
363 #endif
364                 reset_data(s);
365                 atomic_set(&s->active, 1);
366         } else {
367                 pr_err("error: could not setup emc freq\n");
368                 atomic_set(&s->active, 0);
369         }
370
371         /* setup cpu frequency notifier */
372         s = &power_ctx.cpu;
373         status = register_cpu_notifier(&s->nb);
374         if (status < 0) {
375                 pr_err("error: could not setup cpu freq\n");
376                 return status;
377         }
378         reset_data(s);
379
380         if (power_ctx.period > 0) {
381                 init_timer(timer);
382                 timer->function = power_clk_timer;
383                 timer->expires = jiffies + msecs_to_jiffies(power_ctx.period);
384                 timer->data = 0;
385                 add_timer(timer);
386         }
387
388         atomic_set(&s->active, 1);
389
390         return 0;
391 }
392
393 void quadd_power_clk_stop(void)
394 {
395         struct power_clk_source *s;
396
397         if (power_ctx.quadd_ctx->param.power_rate_freq == 0)
398                 return;
399
400         if (power_ctx.period > 0)
401                 del_timer_sync(&power_ctx.timer);
402
403         s = &power_ctx.gpu;
404         if (atomic_cmpxchg(&s->active, 1, 0)) {
405 #ifdef CONFIG_COMMON_CLK
406                 if (s->clkp)
407                         clk_notifier_unregister(s->clkp, &s->nb);
408 #endif
409         }
410
411         s = &power_ctx.emc;
412         if (atomic_cmpxchg(&s->active, 1, 0)) {
413 #ifdef CONFIG_COMMON_CLK
414                 if (s->clkp)
415                         clk_notifier_unregister(s->clkp, &s->nb);
416 #endif
417         }
418
419         s = &power_ctx.cpu;
420         if (atomic_cmpxchg(&s->active, 1, 0)) {
421                 pr_info("power_clk: stop\n");
422                 unregister_cpu_notifier(&s->nb);
423         }
424 }
425
426 int quadd_power_clk_init(struct quadd_ctx *quadd_ctx)
427 {
428         pr_info("power_clk: init\n");
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         pr_info("power_clk: deinit\n");
443         quadd_power_clk_stop();
444 }