2 * drivers/misc/tegra-profiler/power_clk.c
4 * Copyright (c) 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/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>
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 *, unsigned long, void *);
39 struct power_clk_data {
44 struct power_clk_source {
48 struct notifier_block nb;
51 struct power_clk_data data[POWER_CLK_MAX_VALUES];
53 unsigned long long counter;
59 struct power_clk_context_s {
60 struct power_clk_source cpu;
61 struct power_clk_source gpu;
62 struct power_clk_source emc;
64 struct timer_list timer;
67 struct quadd_ctx *quadd_ctx;
71 QUADD_POWER_CLK_CPU = 1,
76 static struct power_clk_context_s power_ctx;
78 static void check_clks(void);
80 static void read_source(struct power_clk_source *s)
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);
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)) {
98 clk_get_rate(s->clkp) / 1000;
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)) {
108 clk_get_rate(s->clkp) / 1000;
114 pr_err_once("%s: error: invalid power_clk type\n", __func__);
118 mutex_unlock(&s->lock);
123 gpu_notifier_call(struct notifier_block *nb, unsigned long val, void *ptr)
125 read_source(&power_ctx.gpu);
132 emc_notifier_call(struct notifier_block *nb, unsigned long val, void *ptr)
134 read_source(&power_ctx.emc);
141 cpu_notifier_call(struct notifier_block *nb, unsigned long val, void *ptr)
143 read_source(&power_ctx.cpu);
145 #ifndef CONFIG_COMMON_CLK
146 read_source(&power_ctx.gpu);
147 read_source(&power_ctx.emc);
155 static void make_sample(void)
158 u32 extra_cpus[NR_CPUS];
159 struct power_clk_source *s;
160 struct quadd_iovec vec;
162 struct quadd_record_data record;
163 struct quadd_power_rate_data *power_rate = &record.power_rate;
165 record.record_type = QUADD_RECORD_TYPE_POWER_RATE;
167 power_rate->time = quadd_get_time();
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;
176 power_rate->nr_cpus = 0;
178 mutex_unlock(&s->lock);
181 mutex_lock(&s->lock);
182 if (atomic_read(&s->active))
183 power_rate->gpu = s->data[0].value;
187 mutex_unlock(&s->lock);
190 mutex_lock(&s->lock);
191 if (atomic_read(&s->active))
192 power_rate->emc = s->data[0].value;
196 mutex_unlock(&s->lock);
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);
202 vec.base = extra_cpus;
203 vec.len = power_rate->nr_cpus * sizeof(extra_cpus[0]);
205 quadd_put_sample(&record, &vec, 1);
208 static inline int is_data_changed(struct power_clk_source *s)
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);
219 mutex_unlock(&s->lock);
224 static inline void update_data(struct power_clk_source *s)
228 mutex_lock(&s->lock);
230 for (i = 0; i < s->nr; i++)
231 s->data[i].prev = s->data[i].value;
233 mutex_unlock(&s->lock);
236 static void check_clks(void)
240 if (is_data_changed(&power_ctx.cpu)) {
241 update_data(&power_ctx.cpu);
245 if (is_data_changed(&power_ctx.gpu)) {
246 update_data(&power_ctx.gpu);
250 if (is_data_changed(&power_ctx.emc)) {
251 update_data(&power_ctx.emc);
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");
265 static void reset_data(struct power_clk_source *s)
269 mutex_lock(&s->lock);
270 for (i = 0; i < s->nr; i++) {
271 s->data[i].value = 0;
275 mutex_unlock(&s->lock);
278 static void init_source(struct power_clk_source *s,
279 notifier_call_ft notifier,
284 s->nb.notifier_call = notifier;
287 mutex_init(&s->lock);
292 power_clk_work_func(struct work_struct *dummy)
294 #ifndef CONFIG_COMMON_CLK
295 read_source(&power_ctx.gpu);
296 read_source(&power_ctx.emc);
302 static DECLARE_WORK(power_clk_work, power_clk_work_func);
304 static void power_clk_timer(unsigned long data)
306 struct timer_list *timer = &power_ctx.timer;
308 schedule_work(&power_clk_work);
309 timer->expires = jiffies + msecs_to_jiffies(power_ctx.period);
313 int quadd_power_clk_start(void)
315 struct power_clk_source *s;
317 struct timer_list *timer = &power_ctx.timer;
318 struct quadd_parameters *param = &power_ctx.quadd_ctx->param;
320 if (param->power_rate_freq == 0) {
321 pr_info("power_clk is not started\n");
325 #ifdef CONFIG_COMMON_CLK
326 power_ctx.period = 0;
328 power_ctx.period = MSEC_PER_SEC / param->power_rate_freq;
330 pr_info("power_clk: start, freq: %d\n",
331 param->power_rate_freq);
333 /* setup gpu frequency */
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);
340 pr_err("error: could not setup gpu freq\n");
347 atomic_set(&s->active, 1);
349 pr_warn("warning: could not setup gpu freq\n");
350 atomic_set(&s->active, 0);
353 /* setup emc frequency */
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);
360 pr_err("error: could not setup emc freq\n");
367 atomic_set(&s->active, 1);
369 pr_warn("warning: could not setup emc freq\n");
370 atomic_set(&s->active, 0);
373 /* setup cpu frequency notifier */
375 status = register_cpu_notifier(&s->nb);
377 pr_err("error: could not setup cpu freq\n");
382 if (power_ctx.period > 0) {
384 timer->function = power_clk_timer;
385 timer->expires = jiffies + msecs_to_jiffies(power_ctx.period);
390 atomic_set(&s->active, 1);
395 void quadd_power_clk_stop(void)
397 struct power_clk_source *s;
399 if (power_ctx.quadd_ctx->param.power_rate_freq == 0)
402 if (power_ctx.period > 0)
403 del_timer_sync(&power_ctx.timer);
406 if (atomic_cmpxchg(&s->active, 1, 0)) {
407 #ifdef CONFIG_COMMON_CLK
409 clk_notifier_unregister(s->clkp, &s->nb);
414 if (atomic_cmpxchg(&s->active, 1, 0)) {
415 #ifdef CONFIG_COMMON_CLK
417 clk_notifier_unregister(s->clkp, &s->nb);
422 if (atomic_cmpxchg(&s->active, 1, 0)) {
423 pr_info("power_clk: stop\n");
424 unregister_cpu_notifier(&s->nb);
428 int quadd_power_clk_init(struct quadd_ctx *quadd_ctx)
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);
435 power_ctx.quadd_ctx = quadd_ctx;
440 void quadd_power_clk_deinit(void)
442 quadd_power_clk_stop();