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
seq_to_ivpu(struct seq_file * s)25 static 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
bo_list_show(struct seq_file * s,void * v)32 static 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
fw_name_show(struct seq_file * s,void * v)42 static 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
fw_version_show(struct seq_file * s,void * v)50 static 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
fw_trace_capability_show(struct seq_file * s,void * v)58 static 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
fw_trace_config_show(struct seq_file * s,void * v)76 static 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
last_bootmode_show(struct seq_file * s,void * v)96 static 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
reset_counter_show(struct seq_file * s,void * v)106 static 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
reset_pending_show(struct seq_file * s,void * v)114 static 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
firewall_irq_counter_show(struct seq_file * s,void * v)122 static 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
engine_reset_counter_show(struct seq_file * s,void * v)130 static 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
138 static 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
dvfs_mode_get(void * data,u64 * dvfs_mode)151 static 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
dvfs_mode_set(void * data,u64 dvfs_mode)159 static 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
167 DEFINE_DEBUGFS_ATTRIBUTE(dvfs_mode_fops, dvfs_mode_get, dvfs_mode_set, "%llu\n");
168
169 static ssize_t
fw_dyndbg_fops_write(struct file * file,const char __user * user_buf,size_t size,loff_t * pos)170 fw_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
187 static const struct file_operations fw_dyndbg_fops = {
188 .owner = THIS_MODULE,
189 .open = simple_open,
190 .write = fw_dyndbg_fops_write,
191 };
192
fw_log_show(struct seq_file * s,void * v)193 static 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
fw_log_fops_open(struct inode * inode,struct file * file)202 static 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
207 static ssize_t
fw_log_fops_write(struct file * file,const char __user * user_buf,size_t size,loff_t * pos)208 fw_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
220 static 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
229 static ssize_t
fw_profiling_freq_fops_write(struct file * file,const char __user * user_buf,size_t size,loff_t * pos)230 fw_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
250 static 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
256 static ssize_t
fw_trace_destination_mask_fops_write(struct file * file,const char __user * user_buf,size_t size,loff_t * pos)257 fw_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
277 static 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
283 static ssize_t
fw_trace_hw_comp_mask_fops_write(struct file * file,const char __user * user_buf,size_t size,loff_t * pos)284 fw_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
304 static 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
310 static ssize_t
fw_trace_level_fops_write(struct file * file,const char __user * user_buf,size_t size,loff_t * pos)311 fw_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
330 static 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
336 static ssize_t
ivpu_force_recovery_fn(struct file * file,const char __user * user_buf,size_t size,loff_t * pos)337 ivpu_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
355 static const struct file_operations ivpu_force_recovery_fops = {
356 .owner = THIS_MODULE,
357 .open = simple_open,
358 .write = ivpu_force_recovery_fn,
359 };
360
ivpu_reset_engine_fn(void * data,u64 val)361 static 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
369 DEFINE_DEBUGFS_ATTRIBUTE(ivpu_reset_engine_fops, NULL, ivpu_reset_engine_fn, "0x%02llx\n");
370
ivpu_resume_engine_fn(void * data,u64 val)371 static 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
378 DEFINE_DEBUGFS_ATTRIBUTE(ivpu_resume_engine_fops, NULL, ivpu_resume_engine_fn, "0x%02llx\n");
379
dct_active_get(void * data,u64 * active_percent)380 static 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
dct_active_set(void * data,u64 active_percent)389 static 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
411 DEFINE_DEBUGFS_ATTRIBUTE(ivpu_dct_fops, dct_active_get, dct_active_set, "%llu\n");
412
print_priority_band(struct seq_file * s,struct ivpu_hw_info * hw,int band,const char * name)413 static 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
priority_bands_show(struct seq_file * s,void * v)423 static 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
priority_bands_fops_open(struct inode * inode,struct file * file)436 static 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
441 static ssize_t
priority_bands_fops_write(struct file * file,const char __user * user_buf,size_t size,loff_t * pos)442 priority_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 (*pos != 0 || 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
476 static 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
ivpu_debugfs_init(struct ivpu_device * vdev)485 void 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 }
525