summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/nvgpu/os/linux/debug_pmu.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/nvgpu/os/linux/debug_pmu.c')
-rw-r--r--drivers/gpu/nvgpu/os/linux/debug_pmu.c481
1 files changed, 481 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/os/linux/debug_pmu.c b/drivers/gpu/nvgpu/os/linux/debug_pmu.c
new file mode 100644
index 00000000..f4ed992d
--- /dev/null
+++ b/drivers/gpu/nvgpu/os/linux/debug_pmu.c
@@ -0,0 +1,481 @@
1/*
2 * Copyright (C) 2017 NVIDIA Corporation. All rights reserved.
3 *
4 * This software is licensed under the terms of the GNU General Public
5 * License version 2, as published by the Free Software Foundation, and
6 * may be copied, distributed, and modified under those terms.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 */
14
15#include <nvgpu/enabled.h>
16#include "debug_pmu.h"
17#include "os_linux.h"
18
19#include <linux/debugfs.h>
20#include <linux/seq_file.h>
21#include <linux/uaccess.h>
22
23static int lpwr_debug_show(struct seq_file *s, void *data)
24{
25 struct gk20a *g = s->private;
26
27 if (g->ops.pmu.pmu_pg_engines_feature_list &&
28 g->ops.pmu.pmu_pg_engines_feature_list(g,
29 PMU_PG_ELPG_ENGINE_ID_GRAPHICS) !=
30 NVGPU_PMU_GR_FEATURE_MASK_POWER_GATING) {
31 seq_printf(s, "PSTATE: %u\n"
32 "RPPG Enabled: %u\n"
33 "RPPG ref count: %u\n"
34 "RPPG state: %u\n"
35 "MSCG Enabled: %u\n"
36 "MSCG pstate state: %u\n"
37 "MSCG transition state: %u\n",
38 g->ops.clk_arb.get_current_pstate(g),
39 g->elpg_enabled, g->pmu.elpg_refcnt,
40 g->pmu.elpg_stat, g->mscg_enabled,
41 g->pmu.mscg_stat, g->pmu.mscg_transition_state);
42
43 } else
44 seq_printf(s, "ELPG Enabled: %u\n"
45 "ELPG ref count: %u\n"
46 "ELPG state: %u\n",
47 g->elpg_enabled, g->pmu.elpg_refcnt,
48 g->pmu.elpg_stat);
49
50 return 0;
51
52}
53
54static int lpwr_debug_open(struct inode *inode, struct file *file)
55{
56 return single_open(file, lpwr_debug_show, inode->i_private);
57}
58
59static const struct file_operations lpwr_debug_fops = {
60 .open = lpwr_debug_open,
61 .read = seq_read,
62 .llseek = seq_lseek,
63 .release = single_release,
64};
65
66static int mscg_stat_show(struct seq_file *s, void *data)
67{
68 struct gk20a *g = s->private;
69 u64 total_ingating, total_ungating, residency, divisor, dividend;
70 struct pmu_pg_stats_data pg_stat_data = { 0 };
71 int err;
72
73 /* Don't unnecessarily power on the device */
74 if (g->power_on) {
75 err = gk20a_busy(g);
76 if (err)
77 return err;
78
79 nvgpu_pmu_get_pg_stats(g,
80 PMU_PG_ELPG_ENGINE_ID_MS, &pg_stat_data);
81 gk20a_idle(g);
82 }
83 total_ingating = g->pg_ingating_time_us +
84 (u64)pg_stat_data.ingating_time;
85 total_ungating = g->pg_ungating_time_us +
86 (u64)pg_stat_data.ungating_time;
87
88 divisor = total_ingating + total_ungating;
89
90 /* We compute the residency on a scale of 1000 */
91 dividend = total_ingating * 1000;
92
93 if (divisor)
94 residency = div64_u64(dividend, divisor);
95 else
96 residency = 0;
97
98 seq_printf(s,
99 "Time in MSCG: %llu us\n"
100 "Time out of MSCG: %llu us\n"
101 "MSCG residency ratio: %llu\n"
102 "MSCG Entry Count: %u\n"
103 "MSCG Avg Entry latency %u\n"
104 "MSCG Avg Exit latency %u\n",
105 total_ingating, total_ungating,
106 residency, pg_stat_data.gating_cnt,
107 pg_stat_data.avg_entry_latency_us,
108 pg_stat_data.avg_exit_latency_us);
109 return 0;
110
111}
112
113static int mscg_stat_open(struct inode *inode, struct file *file)
114{
115 return single_open(file, mscg_stat_show, inode->i_private);
116}
117
118static const struct file_operations mscg_stat_fops = {
119 .open = mscg_stat_open,
120 .read = seq_read,
121 .llseek = seq_lseek,
122 .release = single_release,
123};
124
125static int mscg_transitions_show(struct seq_file *s, void *data)
126{
127 struct gk20a *g = s->private;
128 struct pmu_pg_stats_data pg_stat_data = { 0 };
129 u32 total_gating_cnt;
130 int err;
131
132 if (g->power_on) {
133 err = gk20a_busy(g);
134 if (err)
135 return err;
136
137 nvgpu_pmu_get_pg_stats(g,
138 PMU_PG_ELPG_ENGINE_ID_MS, &pg_stat_data);
139 gk20a_idle(g);
140 }
141 total_gating_cnt = g->pg_gating_cnt + pg_stat_data.gating_cnt;
142
143 seq_printf(s, "%u\n", total_gating_cnt);
144 return 0;
145
146}
147
148static int mscg_transitions_open(struct inode *inode, struct file *file)
149{
150 return single_open(file, mscg_transitions_show, inode->i_private);
151}
152
153static const struct file_operations mscg_transitions_fops = {
154 .open = mscg_transitions_open,
155 .read = seq_read,
156 .llseek = seq_lseek,
157 .release = single_release,
158};
159
160static int elpg_stat_show(struct seq_file *s, void *data)
161{
162 struct gk20a *g = s->private;
163 struct pmu_pg_stats_data pg_stat_data = { 0 };
164 u64 total_ingating, total_ungating, residency, divisor, dividend;
165 int err;
166
167 /* Don't unnecessarily power on the device */
168 if (g->power_on) {
169 err = gk20a_busy(g);
170 if (err)
171 return err;
172
173 nvgpu_pmu_get_pg_stats(g,
174 PMU_PG_ELPG_ENGINE_ID_GRAPHICS, &pg_stat_data);
175 gk20a_idle(g);
176 }
177 total_ingating = g->pg_ingating_time_us +
178 (u64)pg_stat_data.ingating_time;
179 total_ungating = g->pg_ungating_time_us +
180 (u64)pg_stat_data.ungating_time;
181 divisor = total_ingating + total_ungating;
182
183 /* We compute the residency on a scale of 1000 */
184 dividend = total_ingating * 1000;
185
186 if (divisor)
187 residency = div64_u64(dividend, divisor);
188 else
189 residency = 0;
190
191 seq_printf(s,
192 "Time in ELPG: %llu us\n"
193 "Time out of ELPG: %llu us\n"
194 "ELPG residency ratio: %llu\n"
195 "ELPG Entry Count: %u\n"
196 "ELPG Avg Entry latency %u us\n"
197 "ELPG Avg Exit latency %u us\n",
198 total_ingating, total_ungating,
199 residency, pg_stat_data.gating_cnt,
200 pg_stat_data.avg_entry_latency_us,
201 pg_stat_data.avg_exit_latency_us);
202 return 0;
203
204}
205
206static int elpg_stat_open(struct inode *inode, struct file *file)
207{
208 return single_open(file, elpg_stat_show, inode->i_private);
209}
210
211static const struct file_operations elpg_stat_fops = {
212 .open = elpg_stat_open,
213 .read = seq_read,
214 .llseek = seq_lseek,
215 .release = single_release,
216};
217
218static int elpg_transitions_show(struct seq_file *s, void *data)
219{
220 struct gk20a *g = s->private;
221 struct pmu_pg_stats_data pg_stat_data = { 0 };
222 u32 total_gating_cnt;
223 int err;
224
225 if (g->power_on) {
226 err = gk20a_busy(g);
227 if (err)
228 return err;
229
230 nvgpu_pmu_get_pg_stats(g,
231 PMU_PG_ELPG_ENGINE_ID_GRAPHICS, &pg_stat_data);
232 gk20a_idle(g);
233 }
234 total_gating_cnt = g->pg_gating_cnt + pg_stat_data.gating_cnt;
235
236 seq_printf(s, "%u\n", total_gating_cnt);
237 return 0;
238
239}
240
241static int elpg_transitions_open(struct inode *inode, struct file *file)
242{
243 return single_open(file, elpg_transitions_show, inode->i_private);
244}
245
246static const struct file_operations elpg_transitions_fops = {
247 .open = elpg_transitions_open,
248 .read = seq_read,
249 .llseek = seq_lseek,
250 .release = single_release,
251};
252
253static int falc_trace_show(struct seq_file *s, void *data)
254{
255 struct gk20a *g = s->private;
256 struct nvgpu_pmu *pmu = &g->pmu;
257 u32 i = 0, j = 0, k, l, m;
258 char part_str[40];
259 void *tracebuffer;
260 char *trace;
261 u32 *trace1;
262
263 /* allocate system memory to copy pmu trace buffer */
264 tracebuffer = nvgpu_kzalloc(g, GK20A_PMU_TRACE_BUFSIZE);
265 if (tracebuffer == NULL)
266 return -ENOMEM;
267
268 /* read pmu traces into system memory buffer */
269 nvgpu_mem_rd_n(g, &pmu->trace_buf,
270 0, tracebuffer, GK20A_PMU_TRACE_BUFSIZE);
271
272 trace = (char *)tracebuffer;
273 trace1 = (u32 *)tracebuffer;
274
275 for (i = 0; i < GK20A_PMU_TRACE_BUFSIZE; i += 0x40) {
276 for (j = 0; j < 0x40; j++)
277 if (trace1[(i / 4) + j])
278 break;
279 if (j == 0x40)
280 break;
281 seq_printf(s, "Index %x: ", trace1[(i / 4)]);
282 l = 0;
283 m = 0;
284 while (nvgpu_find_hex_in_string((trace+i+20+m), g, &k)) {
285 if (k >= 40)
286 break;
287 strncpy(part_str, (trace+i+20+m), k);
288 part_str[k] = 0;
289 seq_printf(s, "%s0x%x", part_str,
290 trace1[(i / 4) + 1 + l]);
291 l++;
292 m += k + 2;
293 }
294 seq_printf(s, "%s", (trace+i+20+m));
295 }
296
297 nvgpu_kfree(g, tracebuffer);
298 return 0;
299}
300
301static int falc_trace_open(struct inode *inode, struct file *file)
302{
303 return single_open(file, falc_trace_show, inode->i_private);
304}
305
306static const struct file_operations falc_trace_fops = {
307 .open = falc_trace_open,
308 .read = seq_read,
309 .llseek = seq_lseek,
310 .release = single_release,
311};
312
313static int perfmon_events_enable_show(struct seq_file *s, void *data)
314{
315 struct gk20a *g = s->private;
316
317 seq_printf(s, "%u\n", g->pmu.perfmon_sampling_enabled ? 1 : 0);
318 return 0;
319
320}
321
322static int perfmon_events_enable_open(struct inode *inode, struct file *file)
323{
324 return single_open(file, perfmon_events_enable_show, inode->i_private);
325}
326
327static ssize_t perfmon_events_enable_write(struct file *file,
328 const char __user *userbuf, size_t count, loff_t *ppos)
329{
330 struct seq_file *s = file->private_data;
331 struct gk20a *g = s->private;
332 unsigned long val = 0;
333 char buf[40];
334 int buf_size;
335 int err;
336
337 memset(buf, 0, sizeof(buf));
338 buf_size = min(count, (sizeof(buf)-1));
339
340 if (copy_from_user(buf, userbuf, buf_size))
341 return -EFAULT;
342
343 if (kstrtoul(buf, 10, &val) < 0)
344 return -EINVAL;
345
346 /* Don't turn on gk20a unnecessarily */
347 if (g->power_on) {
348 err = gk20a_busy(g);
349 if (err)
350 return err;
351
352 if (val && !g->pmu.perfmon_sampling_enabled &&
353 nvgpu_is_enabled(g, NVGPU_PMU_PERFMON)) {
354 g->pmu.perfmon_sampling_enabled = true;
355 g->ops.pmu.pmu_perfmon_start_sampling(&(g->pmu));
356 } else if (!val && g->pmu.perfmon_sampling_enabled &&
357 nvgpu_is_enabled(g, NVGPU_PMU_PERFMON)) {
358 g->pmu.perfmon_sampling_enabled = false;
359 g->ops.pmu.pmu_perfmon_stop_sampling(&(g->pmu));
360 }
361 gk20a_idle(g);
362 } else {
363 g->pmu.perfmon_sampling_enabled = val ? true : false;
364 }
365
366 return count;
367}
368
369static const struct file_operations perfmon_events_enable_fops = {
370 .open = perfmon_events_enable_open,
371 .read = seq_read,
372 .write = perfmon_events_enable_write,
373 .llseek = seq_lseek,
374 .release = single_release,
375};
376
377static int perfmon_events_count_show(struct seq_file *s, void *data)
378{
379 struct gk20a *g = s->private;
380
381 seq_printf(s, "%lu\n", g->pmu.perfmon_events_cnt);
382 return 0;
383
384}
385
386static int perfmon_events_count_open(struct inode *inode, struct file *file)
387{
388 return single_open(file, perfmon_events_count_show, inode->i_private);
389}
390
391static const struct file_operations perfmon_events_count_fops = {
392 .open = perfmon_events_count_open,
393 .read = seq_read,
394 .llseek = seq_lseek,
395 .release = single_release,
396};
397
398static int security_show(struct seq_file *s, void *data)
399{
400 struct gk20a *g = s->private;
401
402 seq_printf(s, "%d\n", g->pmu.pmu_mode);
403 return 0;
404
405}
406
407static int security_open(struct inode *inode, struct file *file)
408{
409 return single_open(file, security_show, inode->i_private);
410}
411
412static const struct file_operations security_fops = {
413 .open = security_open,
414 .read = seq_read,
415 .llseek = seq_lseek,
416 .release = single_release,
417};
418
419int gk20a_pmu_debugfs_init(struct gk20a *g)
420{
421 struct dentry *d;
422 struct nvgpu_os_linux *l = nvgpu_os_linux_from_gk20a(g);
423
424 d = debugfs_create_file(
425 "lpwr_debug", S_IRUGO|S_IWUSR, l->debugfs, g,
426 &lpwr_debug_fops);
427 if (!d)
428 goto err_out;
429
430 d = debugfs_create_file(
431 "mscg_residency", S_IRUGO|S_IWUSR, l->debugfs, g,
432 &mscg_stat_fops);
433 if (!d)
434 goto err_out;
435
436 d = debugfs_create_file(
437 "mscg_transitions", S_IRUGO, l->debugfs, g,
438 &mscg_transitions_fops);
439 if (!d)
440 goto err_out;
441
442 d = debugfs_create_file(
443 "elpg_residency", S_IRUGO|S_IWUSR, l->debugfs, g,
444 &elpg_stat_fops);
445 if (!d)
446 goto err_out;
447
448 d = debugfs_create_file(
449 "elpg_transitions", S_IRUGO, l->debugfs, g,
450 &elpg_transitions_fops);
451 if (!d)
452 goto err_out;
453
454 d = debugfs_create_file(
455 "falc_trace", S_IRUGO, l->debugfs, g,
456 &falc_trace_fops);
457 if (!d)
458 goto err_out;
459
460 d = debugfs_create_file(
461 "perfmon_events_enable", S_IRUGO, l->debugfs, g,
462 &perfmon_events_enable_fops);
463 if (!d)
464 goto err_out;
465
466 d = debugfs_create_file(
467 "perfmon_events_count", S_IRUGO, l->debugfs, g,
468 &perfmon_events_count_fops);
469 if (!d)
470 goto err_out;
471
472 d = debugfs_create_file(
473 "pmu_security", S_IRUGO, l->debugfs, g,
474 &security_fops);
475 if (!d)
476 goto err_out;
477 return 0;
478err_out:
479 pr_err("%s: Failed to make debugfs node\n", __func__);
480 return -ENOMEM;
481}