2 * drivers/misc/tegra-profiler/power_clk.c
4 * Copyright (c) 2013-2015, NVIDIA CORPORATION. All rights reserved.
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.
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
17 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
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>
27 #include <linux/tegra_profiler.h>
29 #include "power_clk.h"
35 #define POWER_CLK_MAX_VALUES 32
37 typedef int (*notifier_call_ft)(struct notifier_block *,
38 unsigned long, void *);
40 struct power_clk_data {
45 struct power_clk_source {
49 struct notifier_block nb;
52 struct power_clk_data data[POWER_CLK_MAX_VALUES];
58 struct power_clk_context_s {
59 struct power_clk_source cpu;
60 struct power_clk_source gpu;
61 struct power_clk_source emc;
63 struct timer_list timer;
66 struct quadd_ctx *quadd_ctx;
70 QUADD_POWER_CLK_CPU = 1,
75 static struct power_clk_context_s power_ctx;
77 static void make_sample(void)
80 u32 extra_cpus[NR_CPUS];
81 struct power_clk_source *s;
82 struct quadd_iovec vec;
84 struct quadd_record_data record;
85 struct quadd_power_rate_data *power_rate = &record.power_rate;
87 record.record_type = QUADD_RECORD_TYPE_POWER_RATE;
89 power_rate->time = quadd_get_time();
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;
98 power_rate->nr_cpus = 0;
100 mutex_unlock(&s->lock);
103 mutex_lock(&s->lock);
104 if (atomic_read(&s->active))
105 power_rate->gpu = s->data[0].value;
109 mutex_unlock(&s->lock);
112 mutex_lock(&s->lock);
113 if (atomic_read(&s->active))
114 power_rate->emc = s->data[0].value;
118 mutex_unlock(&s->lock);
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);
124 vec.base = extra_cpus;
125 vec.len = power_rate->nr_cpus * sizeof(extra_cpus[0]);
127 quadd_put_sample(&record, &vec, 1);
131 is_data_changed(struct power_clk_source *s)
135 for (i = 0; i < s->nr; i++) {
136 if (s->data[i].value != s->data[i].prev)
144 update_data(struct power_clk_source *s)
148 for (i = 0; i < s->nr; i++)
149 s->data[i].prev = s->data[i].value;
152 static void check_clks(void)
154 struct power_clk_source *s;
158 mutex_lock(&s->lock);
159 if (is_data_changed(s)) {
163 mutex_unlock(&s->lock);
166 mutex_lock(&s->lock);
167 if (is_data_changed(s)) {
171 mutex_unlock(&s->lock);
174 pr_debug("gpu: %lu, emc: %lu\n",
175 power_ctx.gpu.data[0].value,
176 power_ctx.emc.data[0].value);
182 static void check_source(struct power_clk_source *s)
184 mutex_lock(&s->lock);
186 if (!is_data_changed(s))
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);
196 mutex_unlock(&s->lock);
202 mutex_unlock(&s->lock);
206 read_source(struct power_clk_source *s, int cpu)
208 mutex_lock(&s->lock);
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);
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);
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)) {
228 clk_get_rate(s->clkp) / 1000;
231 pr_debug("QUADD_POWER_CLK_GPU, value: %lu\n",
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)) {
240 clk_get_rate(s->clkp) / 1000;
243 pr_debug("QUADD_POWER_CLK_EMC, value: %lu\n",
248 pr_err_once("error: invalid power_clk type\n");
252 mutex_unlock(&s->lock);
256 gpu_notifier_call(struct notifier_block *nb,
257 unsigned long action, void *data)
259 read_source(&power_ctx.gpu, -1);
266 emc_notifier_call(struct notifier_block *nb,
267 unsigned long action, void *data)
269 read_source(&power_ctx.emc, -1);
276 read_cpufreq(struct power_clk_source *s, struct cpufreq_freqs *freq)
280 mutex_lock(&s->lock);
282 if (!atomic_read(&s->active))
288 pr_debug("cpu: %d, cpufreq: %d\n", cpu, cpufreq);
291 pr_err_once("error: cpu id: %d\n", cpu);
295 s->data[cpu].value = cpufreq;
297 pr_debug("[%d] cpufreq: %u --> %u\n",
298 cpu, freq->old, cpufreq);
300 mutex_unlock(&s->lock);
305 mutex_unlock(&s->lock);
309 cpufreq_notifier_call(struct notifier_block *nb,
310 unsigned long action, void *hcpu)
312 struct cpufreq_freqs *freq;
313 struct power_clk_source *s = &power_ctx.cpu;
315 if (!atomic_read(&s->active))
318 pr_debug("action: %lu\n", action);
320 if (action == CPUFREQ_POSTCHANGE ||
321 action == CPUFREQ_RESUMECHANGE) {
323 read_cpufreq(s, freq);
329 static void reset_data(struct power_clk_source *s)
333 mutex_lock(&s->lock);
334 for (i = 0; i < s->nr; i++) {
335 s->data[i].value = 0;
338 mutex_unlock(&s->lock);
341 static void init_source(struct power_clk_source *s,
342 notifier_call_ft notifier,
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);
356 power_clk_work_func(struct work_struct *work)
358 read_source(&power_ctx.gpu, -1);
359 read_source(&power_ctx.emc, -1);
364 static DECLARE_WORK(power_clk_work, power_clk_work_func);
366 static void power_clk_timer(unsigned long data)
368 struct timer_list *timer = &power_ctx.timer;
370 schedule_work(&power_clk_work);
371 timer->expires = jiffies + msecs_to_jiffies(power_ctx.period);
376 read_all_sources_work_func(struct work_struct *work)
380 for_each_possible_cpu(cpu_id)
381 read_source(&power_ctx.cpu, cpu_id);
383 read_source(&power_ctx.gpu, -1);
384 read_source(&power_ctx.emc, -1);
389 static DECLARE_WORK(read_all_sources_work, read_all_sources_work_func);
391 int quadd_power_clk_start(void)
393 struct power_clk_source *s;
394 struct timer_list *timer = &power_ctx.timer;
395 struct quadd_parameters *param = &power_ctx.quadd_ctx->param;
397 if (param->power_rate_freq == 0) {
398 pr_info("power_clk is not started\n");
402 #ifdef CONFIG_COMMON_CLK
403 power_ctx.period = 0;
405 power_ctx.period = MSEC_PER_SEC / param->power_rate_freq;
406 pr_info("clk: use timer\n");
409 pr_info("power_clk: start, freq: %d\n",
410 param->power_rate_freq);
412 /* setup gpu frequency */
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);
419 pr_err("error: could not setup gpu freq\n");
426 atomic_set(&s->active, 1);
428 pr_warn("warning: could not setup gpu freq\n");
429 atomic_set(&s->active, 0);
432 /* setup emc frequency */
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);
439 pr_err("error: could not setup emc freq\n");
446 atomic_set(&s->active, 1);
448 pr_warn("warning: could not setup emc freq\n");
449 atomic_set(&s->active, 0);
452 /* setup cpu frequency notifier */
455 atomic_set(&s->active, 1);
457 if (power_ctx.period > 0) {
459 timer->function = power_clk_timer;
460 timer->expires = jiffies + msecs_to_jiffies(power_ctx.period);
465 schedule_work(&read_all_sources_work);
470 void quadd_power_clk_stop(void)
472 struct power_clk_source *s;
473 struct quadd_parameters *param = &power_ctx.quadd_ctx->param;
475 if (param->power_rate_freq == 0)
478 if (power_ctx.period > 0)
479 del_timer_sync(&power_ctx.timer);
482 mutex_lock(&s->lock);
483 if (atomic_cmpxchg(&s->active, 1, 0)) {
484 #ifdef CONFIG_COMMON_CLK
486 clk_notifier_unregister(s->clkp, &s->nb);
489 mutex_unlock(&s->lock);
492 mutex_lock(&s->lock);
493 if (atomic_cmpxchg(&s->active, 1, 0)) {
494 #ifdef CONFIG_COMMON_CLK
496 clk_notifier_unregister(s->clkp, &s->nb);
499 mutex_unlock(&s->lock);
502 mutex_lock(&s->lock);
503 atomic_set(&s->active, 0);
504 mutex_unlock(&s->lock);
506 pr_info("power_clk: stop\n");
509 int quadd_power_clk_init(struct quadd_ctx *quadd_ctx)
511 init_source(&power_ctx.cpu, cpufreq_notifier_call, nr_cpu_ids,
512 QUADD_POWER_CLK_CPU);
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);
517 cpufreq_register_notifier(&power_ctx.cpu.nb,
518 CPUFREQ_TRANSITION_NOTIFIER);
520 power_ctx.quadd_ctx = quadd_ctx;
525 void quadd_power_clk_deinit(void)
527 quadd_power_clk_stop();
528 cpufreq_unregister_notifier(&power_ctx.cpu.nb,
529 CPUFREQ_TRANSITION_NOTIFIER);