1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2020-2023 Intel Corporation |
4 | */ |
5 | |
6 | #include <drm/drm_debugfs.h> |
7 | #include <drm/drm_file.h> |
8 | #include <drm/drm_print.h> |
9 | |
10 | #include <uapi/drm/ivpu_accel.h> |
11 | |
12 | #include "ivpu_debugfs.h" |
13 | #include "ivpu_drv.h" |
14 | #include "ivpu_fw.h" |
15 | #include "ivpu_fw_log.h" |
16 | #include "ivpu_gem.h" |
17 | #include "ivpu_hw.h" |
18 | #include "ivpu_jsm_msg.h" |
19 | #include "ivpu_pm.h" |
20 | |
21 | static inline struct ivpu_device *seq_to_ivpu(struct seq_file *s) |
22 | { |
23 | struct drm_debugfs_entry *entry = s->private; |
24 | |
25 | return to_ivpu_device(dev: entry->dev); |
26 | } |
27 | |
28 | static int bo_list_show(struct seq_file *s, void *v) |
29 | { |
30 | struct drm_printer p = drm_seq_file_printer(f: s); |
31 | struct ivpu_device *vdev = seq_to_ivpu(s); |
32 | |
33 | ivpu_bo_list(dev: &vdev->drm, p: &p); |
34 | |
35 | return 0; |
36 | } |
37 | |
38 | static int fw_name_show(struct seq_file *s, void *v) |
39 | { |
40 | struct ivpu_device *vdev = seq_to_ivpu(s); |
41 | |
42 | seq_printf(m: s, fmt: "%s\n" , vdev->fw->name); |
43 | return 0; |
44 | } |
45 | |
46 | static int fw_trace_capability_show(struct seq_file *s, void *v) |
47 | { |
48 | struct ivpu_device *vdev = seq_to_ivpu(s); |
49 | u64 trace_hw_component_mask; |
50 | u32 trace_destination_mask; |
51 | int ret; |
52 | |
53 | ret = ivpu_jsm_trace_get_capability(vdev, trace_destination_mask: &trace_destination_mask, |
54 | trace_hw_component_mask: &trace_hw_component_mask); |
55 | if (!ret) { |
56 | seq_printf(m: s, |
57 | fmt: "trace_destination_mask: %#18x\n" |
58 | "trace_hw_component_mask: %#18llx\n" , |
59 | trace_destination_mask, trace_hw_component_mask); |
60 | } |
61 | return 0; |
62 | } |
63 | |
64 | static int fw_trace_config_show(struct seq_file *s, void *v) |
65 | { |
66 | struct ivpu_device *vdev = seq_to_ivpu(s); |
67 | /** |
68 | * WA: VPU_JSM_MSG_TRACE_GET_CONFIG command is not working yet, |
69 | * so we use values from vdev->fw instead of calling ivpu_jsm_trace_get_config() |
70 | */ |
71 | u32 trace_level = vdev->fw->trace_level; |
72 | u32 trace_destination_mask = vdev->fw->trace_destination_mask; |
73 | u64 trace_hw_component_mask = vdev->fw->trace_hw_component_mask; |
74 | |
75 | seq_printf(m: s, |
76 | fmt: "trace_level: %#18x\n" |
77 | "trace_destination_mask: %#18x\n" |
78 | "trace_hw_component_mask: %#18llx\n" , |
79 | trace_level, trace_destination_mask, trace_hw_component_mask); |
80 | |
81 | return 0; |
82 | } |
83 | |
84 | static int last_bootmode_show(struct seq_file *s, void *v) |
85 | { |
86 | struct ivpu_device *vdev = seq_to_ivpu(s); |
87 | |
88 | seq_printf(m: s, fmt: "%s\n" , (vdev->pm->is_warmboot) ? "warmboot" : "coldboot" ); |
89 | |
90 | return 0; |
91 | } |
92 | |
93 | static int reset_counter_show(struct seq_file *s, void *v) |
94 | { |
95 | struct ivpu_device *vdev = seq_to_ivpu(s); |
96 | |
97 | seq_printf(m: s, fmt: "%d\n" , atomic_read(v: &vdev->pm->reset_counter)); |
98 | return 0; |
99 | } |
100 | |
101 | static int reset_pending_show(struct seq_file *s, void *v) |
102 | { |
103 | struct ivpu_device *vdev = seq_to_ivpu(s); |
104 | |
105 | seq_printf(m: s, fmt: "%d\n" , atomic_read(v: &vdev->pm->reset_pending)); |
106 | return 0; |
107 | } |
108 | |
109 | static const struct drm_debugfs_info vdev_debugfs_list[] = { |
110 | {"bo_list" , bo_list_show, 0}, |
111 | {"fw_name" , fw_name_show, 0}, |
112 | {"fw_trace_capability" , fw_trace_capability_show, 0}, |
113 | {"fw_trace_config" , fw_trace_config_show, 0}, |
114 | {"last_bootmode" , last_bootmode_show, 0}, |
115 | {"reset_counter" , reset_counter_show, 0}, |
116 | {"reset_pending" , reset_pending_show, 0}, |
117 | }; |
118 | |
119 | static ssize_t |
120 | dvfs_mode_fops_write(struct file *file, const char __user *user_buf, size_t size, loff_t *pos) |
121 | { |
122 | struct ivpu_device *vdev = file->private_data; |
123 | struct ivpu_fw_info *fw = vdev->fw; |
124 | u32 dvfs_mode; |
125 | int ret; |
126 | |
127 | ret = kstrtou32_from_user(s: user_buf, count: size, base: 0, res: &dvfs_mode); |
128 | if (ret < 0) |
129 | return ret; |
130 | |
131 | fw->dvfs_mode = dvfs_mode; |
132 | |
133 | ret = pci_try_reset_function(to_pci_dev(vdev->drm.dev)); |
134 | if (ret) |
135 | return ret; |
136 | |
137 | return size; |
138 | } |
139 | |
140 | static const struct file_operations dvfs_mode_fops = { |
141 | .owner = THIS_MODULE, |
142 | .open = simple_open, |
143 | .write = dvfs_mode_fops_write, |
144 | }; |
145 | |
146 | static int fw_log_show(struct seq_file *s, void *v) |
147 | { |
148 | struct ivpu_device *vdev = s->private; |
149 | struct drm_printer p = drm_seq_file_printer(f: s); |
150 | |
151 | ivpu_fw_log_print(vdev, only_new_msgs: true, p: &p); |
152 | return 0; |
153 | } |
154 | |
155 | static int fw_log_fops_open(struct inode *inode, struct file *file) |
156 | { |
157 | return single_open(file, fw_log_show, inode->i_private); |
158 | } |
159 | |
160 | static ssize_t |
161 | fw_log_fops_write(struct file *file, const char __user *user_buf, size_t size, loff_t *pos) |
162 | { |
163 | struct seq_file *s = file->private_data; |
164 | struct ivpu_device *vdev = s->private; |
165 | |
166 | if (!size) |
167 | return -EINVAL; |
168 | |
169 | ivpu_fw_log_clear(vdev); |
170 | return size; |
171 | } |
172 | |
173 | static const struct file_operations fw_log_fops = { |
174 | .owner = THIS_MODULE, |
175 | .open = fw_log_fops_open, |
176 | .write = fw_log_fops_write, |
177 | .read = seq_read, |
178 | .llseek = seq_lseek, |
179 | .release = single_release, |
180 | }; |
181 | |
182 | static ssize_t |
183 | fw_profiling_freq_fops_write(struct file *file, const char __user *user_buf, |
184 | size_t size, loff_t *pos) |
185 | { |
186 | struct ivpu_device *vdev = file->private_data; |
187 | bool enable; |
188 | int ret; |
189 | |
190 | ret = kstrtobool_from_user(s: user_buf, count: size, res: &enable); |
191 | if (ret < 0) |
192 | return ret; |
193 | |
194 | ivpu_hw_profiling_freq_drive(vdev, enable); |
195 | |
196 | ret = pci_try_reset_function(to_pci_dev(vdev->drm.dev)); |
197 | if (ret) |
198 | return ret; |
199 | |
200 | return size; |
201 | } |
202 | |
203 | static const struct file_operations fw_profiling_freq_fops = { |
204 | .owner = THIS_MODULE, |
205 | .open = simple_open, |
206 | .write = fw_profiling_freq_fops_write, |
207 | }; |
208 | |
209 | static ssize_t |
210 | fw_trace_destination_mask_fops_write(struct file *file, const char __user *user_buf, |
211 | size_t size, loff_t *pos) |
212 | { |
213 | struct ivpu_device *vdev = file->private_data; |
214 | struct ivpu_fw_info *fw = vdev->fw; |
215 | u32 trace_destination_mask; |
216 | int ret; |
217 | |
218 | ret = kstrtou32_from_user(s: user_buf, count: size, base: 0, res: &trace_destination_mask); |
219 | if (ret < 0) |
220 | return ret; |
221 | |
222 | fw->trace_destination_mask = trace_destination_mask; |
223 | |
224 | ivpu_jsm_trace_set_config(vdev, trace_level: fw->trace_level, trace_destination_mask, |
225 | trace_hw_component_mask: fw->trace_hw_component_mask); |
226 | |
227 | return size; |
228 | } |
229 | |
230 | static const struct file_operations fw_trace_destination_mask_fops = { |
231 | .owner = THIS_MODULE, |
232 | .open = simple_open, |
233 | .write = fw_trace_destination_mask_fops_write, |
234 | }; |
235 | |
236 | static ssize_t |
237 | fw_trace_hw_comp_mask_fops_write(struct file *file, const char __user *user_buf, |
238 | size_t size, loff_t *pos) |
239 | { |
240 | struct ivpu_device *vdev = file->private_data; |
241 | struct ivpu_fw_info *fw = vdev->fw; |
242 | u64 trace_hw_component_mask; |
243 | int ret; |
244 | |
245 | ret = kstrtou64_from_user(s: user_buf, count: size, base: 0, res: &trace_hw_component_mask); |
246 | if (ret < 0) |
247 | return ret; |
248 | |
249 | fw->trace_hw_component_mask = trace_hw_component_mask; |
250 | |
251 | ivpu_jsm_trace_set_config(vdev, trace_level: fw->trace_level, trace_destination_mask: fw->trace_destination_mask, |
252 | trace_hw_component_mask); |
253 | |
254 | return size; |
255 | } |
256 | |
257 | static const struct file_operations fw_trace_hw_comp_mask_fops = { |
258 | .owner = THIS_MODULE, |
259 | .open = simple_open, |
260 | .write = fw_trace_hw_comp_mask_fops_write, |
261 | }; |
262 | |
263 | static ssize_t |
264 | fw_trace_level_fops_write(struct file *file, const char __user *user_buf, size_t size, loff_t *pos) |
265 | { |
266 | struct ivpu_device *vdev = file->private_data; |
267 | struct ivpu_fw_info *fw = vdev->fw; |
268 | u32 trace_level; |
269 | int ret; |
270 | |
271 | ret = kstrtou32_from_user(s: user_buf, count: size, base: 0, res: &trace_level); |
272 | if (ret < 0) |
273 | return ret; |
274 | |
275 | fw->trace_level = trace_level; |
276 | |
277 | ivpu_jsm_trace_set_config(vdev, trace_level, trace_destination_mask: fw->trace_destination_mask, |
278 | trace_hw_component_mask: fw->trace_hw_component_mask); |
279 | |
280 | return size; |
281 | } |
282 | |
283 | static const struct file_operations fw_trace_level_fops = { |
284 | .owner = THIS_MODULE, |
285 | .open = simple_open, |
286 | .write = fw_trace_level_fops_write, |
287 | }; |
288 | |
289 | static ssize_t |
290 | ivpu_force_recovery_fn(struct file *file, const char __user *user_buf, size_t size, loff_t *pos) |
291 | { |
292 | struct ivpu_device *vdev = file->private_data; |
293 | int ret; |
294 | |
295 | if (!size) |
296 | return -EINVAL; |
297 | |
298 | ret = ivpu_rpm_get(vdev); |
299 | if (ret) |
300 | return ret; |
301 | |
302 | ivpu_pm_trigger_recovery(vdev, reason: "debugfs" ); |
303 | flush_work(work: &vdev->pm->recovery_work); |
304 | ivpu_rpm_put(vdev); |
305 | return size; |
306 | } |
307 | |
308 | static const struct file_operations ivpu_force_recovery_fops = { |
309 | .owner = THIS_MODULE, |
310 | .open = simple_open, |
311 | .write = ivpu_force_recovery_fn, |
312 | }; |
313 | |
314 | static ssize_t |
315 | ivpu_reset_engine_fn(struct file *file, const char __user *user_buf, size_t size, loff_t *pos) |
316 | { |
317 | struct ivpu_device *vdev = file->private_data; |
318 | |
319 | if (!size) |
320 | return -EINVAL; |
321 | |
322 | if (ivpu_jsm_reset_engine(vdev, DRM_IVPU_ENGINE_COMPUTE)) |
323 | return -ENODEV; |
324 | if (ivpu_jsm_reset_engine(vdev, DRM_IVPU_ENGINE_COPY)) |
325 | return -ENODEV; |
326 | |
327 | return size; |
328 | } |
329 | |
330 | static const struct file_operations ivpu_reset_engine_fops = { |
331 | .owner = THIS_MODULE, |
332 | .open = simple_open, |
333 | .write = ivpu_reset_engine_fn, |
334 | }; |
335 | |
336 | void ivpu_debugfs_init(struct ivpu_device *vdev) |
337 | { |
338 | struct dentry *debugfs_root = vdev->drm.debugfs_root; |
339 | |
340 | drm_debugfs_add_files(dev: &vdev->drm, files: vdev_debugfs_list, ARRAY_SIZE(vdev_debugfs_list)); |
341 | |
342 | debugfs_create_file(name: "force_recovery" , mode: 0200, parent: debugfs_root, data: vdev, |
343 | fops: &ivpu_force_recovery_fops); |
344 | |
345 | debugfs_create_file(name: "dvfs_mode" , mode: 0200, parent: debugfs_root, data: vdev, |
346 | fops: &dvfs_mode_fops); |
347 | |
348 | debugfs_create_file(name: "fw_log" , mode: 0644, parent: debugfs_root, data: vdev, |
349 | fops: &fw_log_fops); |
350 | debugfs_create_file(name: "fw_trace_destination_mask" , mode: 0200, parent: debugfs_root, data: vdev, |
351 | fops: &fw_trace_destination_mask_fops); |
352 | debugfs_create_file(name: "fw_trace_hw_comp_mask" , mode: 0200, parent: debugfs_root, data: vdev, |
353 | fops: &fw_trace_hw_comp_mask_fops); |
354 | debugfs_create_file(name: "fw_trace_level" , mode: 0200, parent: debugfs_root, data: vdev, |
355 | fops: &fw_trace_level_fops); |
356 | |
357 | debugfs_create_file(name: "reset_engine" , mode: 0200, parent: debugfs_root, data: vdev, |
358 | fops: &ivpu_reset_engine_fops); |
359 | |
360 | if (ivpu_hw_gen(vdev) >= IVPU_HW_40XX) |
361 | debugfs_create_file(name: "fw_profiling_freq_drive" , mode: 0200, |
362 | parent: debugfs_root, data: vdev, fops: &fw_profiling_freq_fops); |
363 | } |
364 | |