Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (C) 2020-2026 Intel Corporation
4 */
5
6#include <linux/debugfs.h>
7#include <linux/fault-inject.h>
8
9#include <drm/drm_debugfs.h>
10#include <drm/drm_file.h>
11#include <drm/drm_print.h>
12
13#include <uapi/drm/ivpu_accel.h>
14
15#include "ivpu_debugfs.h"
16#include "ivpu_drv.h"
17#include "ivpu_fw.h"
18#include "ivpu_fw_log.h"
19#include "ivpu_gem.h"
20#include "ivpu_hw.h"
21#include "ivpu_jsm_msg.h"
22#include "ivpu_pm.h"
23#include "vpu_boot_api.h"
24
25static inline struct ivpu_device *seq_to_ivpu(struct seq_file *s)
26{
27 struct drm_debugfs_entry *entry = s->private;
28
29 return to_ivpu_device(entry->dev);
30}
31
32static int bo_list_show(struct seq_file *s, void *v)
33{
34 struct drm_printer p = drm_seq_file_printer(s);
35 struct ivpu_device *vdev = seq_to_ivpu(s);
36
37 ivpu_bo_list(&vdev->drm, &p);
38
39 return 0;
40}
41
42static int fw_name_show(struct seq_file *s, void *v)
43{
44 struct ivpu_device *vdev = seq_to_ivpu(s);
45
46 seq_printf(s, "%s\n", vdev->fw->name);
47 return 0;
48}
49
50static int fw_version_show(struct seq_file *s, void *v)
51{
52 struct ivpu_device *vdev = seq_to_ivpu(s);
53
54 seq_printf(s, "%s\n", vdev->fw->version);
55 return 0;
56}
57
58static int fw_trace_capability_show(struct seq_file *s, void *v)
59{
60 struct ivpu_device *vdev = seq_to_ivpu(s);
61 u64 trace_hw_component_mask;
62 u32 trace_destination_mask;
63 int ret;
64
65 ret = ivpu_jsm_trace_get_capability(vdev, &trace_destination_mask,
66 &trace_hw_component_mask);
67 if (!ret) {
68 seq_printf(s,
69 "trace_destination_mask: %#18x\n"
70 "trace_hw_component_mask: %#18llx\n",
71 trace_destination_mask, trace_hw_component_mask);
72 }
73 return 0;
74}
75
76static int fw_trace_config_show(struct seq_file *s, void *v)
77{
78 struct ivpu_device *vdev = seq_to_ivpu(s);
79 /**
80 * WA: VPU_JSM_MSG_TRACE_GET_CONFIG command is not working yet,
81 * so we use values from vdev->fw instead of calling ivpu_jsm_trace_get_config()
82 */
83 u32 trace_level = vdev->fw->trace_level;
84 u32 trace_destination_mask = vdev->fw->trace_destination_mask;
85 u64 trace_hw_component_mask = vdev->fw->trace_hw_component_mask;
86
87 seq_printf(s,
88 "trace_level: %#18x\n"
89 "trace_destination_mask: %#18x\n"
90 "trace_hw_component_mask: %#18llx\n",
91 trace_level, trace_destination_mask, trace_hw_component_mask);
92
93 return 0;
94}
95
96static int last_bootmode_show(struct seq_file *s, void *v)
97{
98 struct ivpu_device *vdev = seq_to_ivpu(s);
99
100 seq_printf(s, "%s\n", (vdev->fw->last_boot_mode == VPU_BOOT_TYPE_WARMBOOT) ?
101 "warm boot" : "cold boot");
102
103 return 0;
104}
105
106static int reset_counter_show(struct seq_file *s, void *v)
107{
108 struct ivpu_device *vdev = seq_to_ivpu(s);
109
110 seq_printf(s, "%d\n", atomic_read(&vdev->pm->reset_counter));
111 return 0;
112}
113
114static int reset_pending_show(struct seq_file *s, void *v)
115{
116 struct ivpu_device *vdev = seq_to_ivpu(s);
117
118 seq_printf(s, "%d\n", atomic_read(&vdev->pm->reset_pending));
119 return 0;
120}
121
122static int firewall_irq_counter_show(struct seq_file *s, void *v)
123{
124 struct ivpu_device *vdev = seq_to_ivpu(s);
125
126 seq_printf(s, "%d\n", atomic_read(&vdev->hw->firewall_irq_counter));
127 return 0;
128}
129
130static int engine_reset_counter_show(struct seq_file *s, void *v)
131{
132 struct ivpu_device *vdev = seq_to_ivpu(s);
133
134 seq_printf(s, "%d\n", atomic_read(&vdev->pm->engine_reset_counter));
135 return 0;
136}
137
138static const struct drm_debugfs_info vdev_debugfs_list[] = {
139 {"bo_list", bo_list_show, 0},
140 {"fw_name", fw_name_show, 0},
141 {"fw_version", fw_version_show, 0},
142 {"fw_trace_capability", fw_trace_capability_show, 0},
143 {"fw_trace_config", fw_trace_config_show, 0},
144 {"last_bootmode", last_bootmode_show, 0},
145 {"reset_counter", reset_counter_show, 0},
146 {"reset_pending", reset_pending_show, 0},
147 {"firewall_irq_counter", firewall_irq_counter_show, 0},
148 {"engine_reset_counter", engine_reset_counter_show, 0},
149};
150
151static int dvfs_mode_get(void *data, u64 *dvfs_mode)
152{
153 struct ivpu_device *vdev = (struct ivpu_device *)data;
154
155 *dvfs_mode = vdev->fw->dvfs_mode;
156 return 0;
157}
158
159static int dvfs_mode_set(void *data, u64 dvfs_mode)
160{
161 struct ivpu_device *vdev = (struct ivpu_device *)data;
162
163 vdev->fw->dvfs_mode = (u32)dvfs_mode;
164 return pci_try_reset_function(to_pci_dev(vdev->drm.dev));
165}
166
167DEFINE_DEBUGFS_ATTRIBUTE(dvfs_mode_fops, dvfs_mode_get, dvfs_mode_set, "%llu\n");
168
169static ssize_t
170fw_dyndbg_fops_write(struct file *file, const char __user *user_buf, size_t size, loff_t *pos)
171{
172 struct ivpu_device *vdev = file->private_data;
173 char buffer[VPU_DYNDBG_CMD_MAX_LEN] = {};
174 int ret;
175
176 if (size >= VPU_DYNDBG_CMD_MAX_LEN)
177 return -EINVAL;
178
179 ret = strncpy_from_user(buffer, user_buf, size);
180 if (ret < 0)
181 return ret;
182
183 ivpu_jsm_dyndbg_control(vdev, buffer, size);
184 return size;
185}
186
187static const struct file_operations fw_dyndbg_fops = {
188 .owner = THIS_MODULE,
189 .open = simple_open,
190 .write = fw_dyndbg_fops_write,
191};
192
193static int fw_log_show(struct seq_file *s, void *v)
194{
195 struct ivpu_device *vdev = s->private;
196 struct drm_printer p = drm_seq_file_printer(s);
197
198 ivpu_fw_log_print(vdev, true, &p);
199 return 0;
200}
201
202static int fw_log_fops_open(struct inode *inode, struct file *file)
203{
204 return single_open(file, fw_log_show, inode->i_private);
205}
206
207static ssize_t
208fw_log_fops_write(struct file *file, const char __user *user_buf, size_t size, loff_t *pos)
209{
210 struct seq_file *s = file->private_data;
211 struct ivpu_device *vdev = s->private;
212
213 if (!size)
214 return -EINVAL;
215
216 ivpu_fw_log_mark_read(vdev);
217 return size;
218}
219
220static const struct file_operations fw_log_fops = {
221 .owner = THIS_MODULE,
222 .open = fw_log_fops_open,
223 .write = fw_log_fops_write,
224 .read = seq_read,
225 .llseek = seq_lseek,
226 .release = single_release,
227};
228
229static ssize_t
230fw_profiling_freq_fops_write(struct file *file, const char __user *user_buf,
231 size_t size, loff_t *pos)
232{
233 struct ivpu_device *vdev = file->private_data;
234 bool enable;
235 int ret;
236
237 ret = kstrtobool_from_user(user_buf, size, &enable);
238 if (ret < 0)
239 return ret;
240
241 ivpu_hw_profiling_freq_drive(vdev, enable);
242
243 ret = pci_try_reset_function(to_pci_dev(vdev->drm.dev));
244 if (ret)
245 return ret;
246
247 return size;
248}
249
250static const struct file_operations fw_profiling_freq_fops = {
251 .owner = THIS_MODULE,
252 .open = simple_open,
253 .write = fw_profiling_freq_fops_write,
254};
255
256static ssize_t
257fw_trace_destination_mask_fops_write(struct file *file, const char __user *user_buf,
258 size_t size, loff_t *pos)
259{
260 struct ivpu_device *vdev = file->private_data;
261 struct ivpu_fw_info *fw = vdev->fw;
262 u32 trace_destination_mask;
263 int ret;
264
265 ret = kstrtou32_from_user(user_buf, size, 0, &trace_destination_mask);
266 if (ret < 0)
267 return ret;
268
269 fw->trace_destination_mask = trace_destination_mask;
270
271 ivpu_jsm_trace_set_config(vdev, fw->trace_level, trace_destination_mask,
272 fw->trace_hw_component_mask);
273
274 return size;
275}
276
277static const struct file_operations fw_trace_destination_mask_fops = {
278 .owner = THIS_MODULE,
279 .open = simple_open,
280 .write = fw_trace_destination_mask_fops_write,
281};
282
283static ssize_t
284fw_trace_hw_comp_mask_fops_write(struct file *file, const char __user *user_buf,
285 size_t size, loff_t *pos)
286{
287 struct ivpu_device *vdev = file->private_data;
288 struct ivpu_fw_info *fw = vdev->fw;
289 u64 trace_hw_component_mask;
290 int ret;
291
292 ret = kstrtou64_from_user(user_buf, size, 0, &trace_hw_component_mask);
293 if (ret < 0)
294 return ret;
295
296 fw->trace_hw_component_mask = trace_hw_component_mask;
297
298 ivpu_jsm_trace_set_config(vdev, fw->trace_level, fw->trace_destination_mask,
299 trace_hw_component_mask);
300
301 return size;
302}
303
304static const struct file_operations fw_trace_hw_comp_mask_fops = {
305 .owner = THIS_MODULE,
306 .open = simple_open,
307 .write = fw_trace_hw_comp_mask_fops_write,
308};
309
310static ssize_t
311fw_trace_level_fops_write(struct file *file, const char __user *user_buf, size_t size, loff_t *pos)
312{
313 struct ivpu_device *vdev = file->private_data;
314 struct ivpu_fw_info *fw = vdev->fw;
315 u32 trace_level;
316 int ret;
317
318 ret = kstrtou32_from_user(user_buf, size, 0, &trace_level);
319 if (ret < 0)
320 return ret;
321
322 fw->trace_level = trace_level;
323
324 ivpu_jsm_trace_set_config(vdev, trace_level, fw->trace_destination_mask,
325 fw->trace_hw_component_mask);
326
327 return size;
328}
329
330static const struct file_operations fw_trace_level_fops = {
331 .owner = THIS_MODULE,
332 .open = simple_open,
333 .write = fw_trace_level_fops_write,
334};
335
336static ssize_t
337ivpu_force_recovery_fn(struct file *file, const char __user *user_buf, size_t size, loff_t *pos)
338{
339 struct ivpu_device *vdev = file->private_data;
340 int ret;
341
342 if (!size)
343 return -EINVAL;
344
345 ret = ivpu_rpm_get(vdev);
346 if (ret < 0)
347 return ret;
348
349 ivpu_pm_trigger_recovery(vdev, "debugfs");
350 flush_work(&vdev->pm->recovery_work);
351 ivpu_rpm_put(vdev);
352 return size;
353}
354
355static const struct file_operations ivpu_force_recovery_fops = {
356 .owner = THIS_MODULE,
357 .open = simple_open,
358 .write = ivpu_force_recovery_fn,
359};
360
361static int ivpu_reset_engine_fn(void *data, u64 val)
362{
363 struct ivpu_device *vdev = (struct ivpu_device *)data;
364 struct vpu_jsm_msg resp;
365
366 return ivpu_jsm_reset_engine(vdev, (u32)val, &resp);
367}
368
369DEFINE_DEBUGFS_ATTRIBUTE(ivpu_reset_engine_fops, NULL, ivpu_reset_engine_fn, "0x%02llx\n");
370
371static int ivpu_resume_engine_fn(void *data, u64 val)
372{
373 struct ivpu_device *vdev = (struct ivpu_device *)data;
374
375 return ivpu_jsm_hws_resume_engine(vdev, (u32)val);
376}
377
378DEFINE_DEBUGFS_ATTRIBUTE(ivpu_resume_engine_fops, NULL, ivpu_resume_engine_fn, "0x%02llx\n");
379
380static int dct_active_get(void *data, u64 *active_percent)
381{
382 struct ivpu_device *vdev = data;
383
384 *active_percent = vdev->pm->dct_active_percent;
385
386 return 0;
387}
388
389static int dct_active_set(void *data, u64 active_percent)
390{
391 struct ivpu_device *vdev = data;
392 int ret;
393
394 if (active_percent > 100)
395 return -EINVAL;
396
397 ret = ivpu_rpm_get(vdev);
398 if (ret < 0)
399 return ret;
400
401 if (active_percent)
402 ret = ivpu_pm_dct_enable(vdev, active_percent);
403 else
404 ret = ivpu_pm_dct_disable(vdev);
405
406 ivpu_rpm_put(vdev);
407
408 return ret;
409}
410
411DEFINE_DEBUGFS_ATTRIBUTE(ivpu_dct_fops, dct_active_get, dct_active_set, "%llu\n");
412
413static void print_priority_band(struct seq_file *s, struct ivpu_hw_info *hw,
414 int band, const char *name)
415{
416 seq_printf(s, "%-9s: grace_period %9u process_grace_period %9u process_quantum %9u\n",
417 name,
418 hw->hws.grace_period[band],
419 hw->hws.process_grace_period[band],
420 hw->hws.process_quantum[band]);
421}
422
423static int priority_bands_show(struct seq_file *s, void *v)
424{
425 struct ivpu_device *vdev = s->private;
426 struct ivpu_hw_info *hw = vdev->hw;
427
428 print_priority_band(s, hw, VPU_JOB_SCHEDULING_PRIORITY_BAND_IDLE, "Idle");
429 print_priority_band(s, hw, VPU_JOB_SCHEDULING_PRIORITY_BAND_NORMAL, "Normal");
430 print_priority_band(s, hw, VPU_JOB_SCHEDULING_PRIORITY_BAND_FOCUS, "Focus");
431 print_priority_band(s, hw, VPU_JOB_SCHEDULING_PRIORITY_BAND_REALTIME, "Realtime");
432
433 return 0;
434}
435
436static int priority_bands_fops_open(struct inode *inode, struct file *file)
437{
438 return single_open(file, priority_bands_show, inode->i_private);
439}
440
441static ssize_t
442priority_bands_fops_write(struct file *file, const char __user *user_buf, size_t size, loff_t *pos)
443{
444 struct seq_file *s = file->private_data;
445 struct ivpu_device *vdev = s->private;
446 char buf[64];
447 u32 grace_period;
448 u32 process_grace_period;
449 u32 process_quantum;
450 u32 band;
451 int ret;
452
453 if (size >= sizeof(buf))
454 return -EINVAL;
455
456 ret = simple_write_to_buffer(buf, sizeof(buf) - 1, pos, user_buf, size);
457 if (ret < 0)
458 return ret;
459
460 buf[ret] = '\0';
461 ret = sscanf(buf, "%u %u %u %u", &band, &grace_period, &process_grace_period,
462 &process_quantum);
463 if (ret != 4)
464 return -EINVAL;
465
466 if (band >= VPU_JOB_SCHEDULING_PRIORITY_BAND_COUNT)
467 return -EINVAL;
468
469 vdev->hw->hws.grace_period[band] = grace_period;
470 vdev->hw->hws.process_grace_period[band] = process_grace_period;
471 vdev->hw->hws.process_quantum[band] = process_quantum;
472
473 return size;
474}
475
476static const struct file_operations ivpu_hws_priority_bands_fops = {
477 .owner = THIS_MODULE,
478 .open = priority_bands_fops_open,
479 .write = priority_bands_fops_write,
480 .read = seq_read,
481 .llseek = seq_lseek,
482 .release = single_release,
483};
484
485void ivpu_debugfs_init(struct ivpu_device *vdev)
486{
487 struct dentry *debugfs_root = vdev->drm.debugfs_root;
488
489 drm_debugfs_add_files(&vdev->drm, vdev_debugfs_list, ARRAY_SIZE(vdev_debugfs_list));
490
491 debugfs_create_file("force_recovery", 0200, debugfs_root, vdev,
492 &ivpu_force_recovery_fops);
493
494 debugfs_create_file("dvfs_mode", 0644, debugfs_root, vdev,
495 &dvfs_mode_fops);
496
497 debugfs_create_file("fw_dyndbg", 0200, debugfs_root, vdev,
498 &fw_dyndbg_fops);
499 debugfs_create_file("fw_log", 0644, debugfs_root, vdev,
500 &fw_log_fops);
501 debugfs_create_file("fw_trace_destination_mask", 0200, debugfs_root, vdev,
502 &fw_trace_destination_mask_fops);
503 debugfs_create_file("fw_trace_hw_comp_mask", 0200, debugfs_root, vdev,
504 &fw_trace_hw_comp_mask_fops);
505 debugfs_create_file("fw_trace_level", 0200, debugfs_root, vdev,
506 &fw_trace_level_fops);
507 debugfs_create_file("hws_priority_bands", 0200, debugfs_root, vdev,
508 &ivpu_hws_priority_bands_fops);
509
510 debugfs_create_file("reset_engine", 0200, debugfs_root, vdev,
511 &ivpu_reset_engine_fops);
512 debugfs_create_file("resume_engine", 0200, debugfs_root, vdev,
513 &ivpu_resume_engine_fops);
514
515 if (ivpu_hw_ip_gen(vdev) >= IVPU_HW_IP_40XX) {
516 debugfs_create_file("fw_profiling_freq_drive", 0200,
517 debugfs_root, vdev, &fw_profiling_freq_fops);
518 debugfs_create_file("dct", 0644, debugfs_root, vdev, &ivpu_dct_fops);
519 }
520
521#ifdef CONFIG_FAULT_INJECTION
522 fault_create_debugfs_attr("fail_hw", debugfs_root, &ivpu_hw_failure);
523#endif
524}