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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 (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 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