1 | /* |
2 | * Copyright (C) 2009 Red Hat <bskeggs@redhat.com> |
3 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining |
5 | * a copy of this software and associated documentation files (the |
6 | * "Software"), to deal in the Software without restriction, including |
7 | * without limitation the rights to use, copy, modify, merge, publish, |
8 | * distribute, sublicense, and/or sell copies of the Software, and to |
9 | * permit persons to whom the Software is furnished to do so, subject to |
10 | * the following conditions: |
11 | * |
12 | * The above copyright notice and this permission notice (including the |
13 | * next paragraph) shall be included in all copies or substantial |
14 | * portions of the Software. |
15 | * |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
19 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE |
20 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
21 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
22 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
23 | * |
24 | */ |
25 | |
26 | /* |
27 | * Authors: |
28 | * Ben Skeggs <bskeggs@redhat.com> |
29 | */ |
30 | |
31 | #include <linux/debugfs.h> |
32 | #include <nvif/class.h> |
33 | #include <nvif/if0001.h> |
34 | #include "nouveau_debugfs.h" |
35 | #include "nouveau_drv.h" |
36 | |
37 | static int |
38 | nouveau_debugfs_vbios_image(struct seq_file *m, void *data) |
39 | { |
40 | struct drm_info_node *node = (struct drm_info_node *) m->private; |
41 | struct nouveau_drm *drm = nouveau_drm(dev: node->minor->dev); |
42 | int i; |
43 | |
44 | for (i = 0; i < drm->vbios.length; i++) |
45 | seq_printf(m, fmt: "%c" , drm->vbios.data[i]); |
46 | return 0; |
47 | } |
48 | |
49 | static int |
50 | nouveau_debugfs_strap_peek(struct seq_file *m, void *data) |
51 | { |
52 | struct drm_info_node *node = m->private; |
53 | struct nouveau_drm *drm = nouveau_drm(dev: node->minor->dev); |
54 | int ret; |
55 | |
56 | ret = pm_runtime_get_sync(drm->dev->dev); |
57 | if (ret < 0 && ret != -EACCES) { |
58 | pm_runtime_put_autosuspend(drm->dev->dev); |
59 | return ret; |
60 | } |
61 | |
62 | seq_printf(m, fmt: "0x%08x\n" , |
63 | nvif_rd32(&drm->client.device.object, 0x101000)); |
64 | |
65 | pm_runtime_mark_last_busy(drm->dev->dev); |
66 | pm_runtime_put_autosuspend(drm->dev->dev); |
67 | |
68 | return 0; |
69 | } |
70 | |
71 | static int |
72 | nouveau_debugfs_pstate_get(struct seq_file *m, void *data) |
73 | { |
74 | struct drm_device *drm = m->private; |
75 | struct nouveau_debugfs *debugfs = nouveau_debugfs(dev: drm); |
76 | struct nvif_object *ctrl; |
77 | struct nvif_control_pstate_info_v0 info = {}; |
78 | int ret, i; |
79 | |
80 | if (!debugfs) |
81 | return -ENODEV; |
82 | |
83 | ctrl = &debugfs->ctrl; |
84 | ret = nvif_mthd(ctrl, NVIF_CONTROL_PSTATE_INFO, &info, sizeof(info)); |
85 | if (ret) |
86 | return ret; |
87 | |
88 | for (i = 0; i < info.count + 1; i++) { |
89 | const s32 state = i < info.count ? i : |
90 | NVIF_CONTROL_PSTATE_ATTR_V0_STATE_CURRENT; |
91 | struct nvif_control_pstate_attr_v0 attr = { |
92 | .state = state, |
93 | .index = 0, |
94 | }; |
95 | |
96 | ret = nvif_mthd(ctrl, NVIF_CONTROL_PSTATE_ATTR, |
97 | &attr, sizeof(attr)); |
98 | if (ret) |
99 | return ret; |
100 | |
101 | if (i < info.count) |
102 | seq_printf(m, fmt: "%02x:" , attr.state); |
103 | else |
104 | seq_printf(m, fmt: "%s:" , info.pwrsrc == 0 ? "DC" : |
105 | info.pwrsrc == 1 ? "AC" : "--" ); |
106 | |
107 | attr.index = 0; |
108 | do { |
109 | attr.state = state; |
110 | ret = nvif_mthd(ctrl, NVIF_CONTROL_PSTATE_ATTR, |
111 | &attr, sizeof(attr)); |
112 | if (ret) |
113 | return ret; |
114 | |
115 | seq_printf(m, fmt: " %s %d" , attr.name, attr.min); |
116 | if (attr.min != attr.max) |
117 | seq_printf(m, fmt: "-%d" , attr.max); |
118 | seq_printf(m, fmt: " %s" , attr.unit); |
119 | } while (attr.index); |
120 | |
121 | if (state >= 0) { |
122 | if (info.ustate_ac == state) |
123 | seq_puts(m, s: " AC" ); |
124 | if (info.ustate_dc == state) |
125 | seq_puts(m, s: " DC" ); |
126 | if (info.pstate == state) |
127 | seq_puts(m, s: " *" ); |
128 | } else { |
129 | if (info.ustate_ac < -1) |
130 | seq_puts(m, s: " AC" ); |
131 | if (info.ustate_dc < -1) |
132 | seq_puts(m, s: " DC" ); |
133 | } |
134 | |
135 | seq_putc(m, c: '\n'); |
136 | } |
137 | |
138 | return 0; |
139 | } |
140 | |
141 | static ssize_t |
142 | nouveau_debugfs_pstate_set(struct file *file, const char __user *ubuf, |
143 | size_t len, loff_t *offp) |
144 | { |
145 | struct seq_file *m = file->private_data; |
146 | struct drm_device *drm = m->private; |
147 | struct nouveau_debugfs *debugfs = nouveau_debugfs(dev: drm); |
148 | struct nvif_control_pstate_user_v0 args = { .pwrsrc = -EINVAL }; |
149 | char buf[32] = {}, *tmp, *cur = buf; |
150 | long value, ret; |
151 | |
152 | if (!debugfs) |
153 | return -ENODEV; |
154 | |
155 | if (len >= sizeof(buf)) |
156 | return -EINVAL; |
157 | |
158 | if (copy_from_user(to: buf, from: ubuf, n: len)) |
159 | return -EFAULT; |
160 | |
161 | if ((tmp = strchr(buf, '\n'))) |
162 | *tmp = '\0'; |
163 | |
164 | if (!strncasecmp(s1: cur, s2: "dc:" , n: 3)) { |
165 | args.pwrsrc = 0; |
166 | cur += 3; |
167 | } else |
168 | if (!strncasecmp(s1: cur, s2: "ac:" , n: 3)) { |
169 | args.pwrsrc = 1; |
170 | cur += 3; |
171 | } |
172 | |
173 | if (!strcasecmp(s1: cur, s2: "none" )) |
174 | args.ustate = NVIF_CONTROL_PSTATE_USER_V0_STATE_UNKNOWN; |
175 | else |
176 | if (!strcasecmp(s1: cur, s2: "auto" )) |
177 | args.ustate = NVIF_CONTROL_PSTATE_USER_V0_STATE_PERFMON; |
178 | else { |
179 | ret = kstrtol(s: cur, base: 16, res: &value); |
180 | if (ret) |
181 | return ret; |
182 | args.ustate = value; |
183 | } |
184 | |
185 | ret = pm_runtime_get_sync(drm->dev); |
186 | if (ret < 0 && ret != -EACCES) { |
187 | pm_runtime_put_autosuspend(drm->dev); |
188 | return ret; |
189 | } |
190 | |
191 | ret = nvif_mthd(&debugfs->ctrl, NVIF_CONTROL_PSTATE_USER, |
192 | &args, sizeof(args)); |
193 | pm_runtime_put_autosuspend(drm->dev); |
194 | if (ret < 0) |
195 | return ret; |
196 | |
197 | return len; |
198 | } |
199 | |
200 | static int |
201 | nouveau_debugfs_pstate_open(struct inode *inode, struct file *file) |
202 | { |
203 | return single_open(file, nouveau_debugfs_pstate_get, inode->i_private); |
204 | } |
205 | |
206 | static void |
207 | nouveau_debugfs_gpuva_regions(struct seq_file *m, struct nouveau_uvmm *uvmm) |
208 | { |
209 | MA_STATE(mas, &uvmm->region_mt, 0, 0); |
210 | struct nouveau_uvma_region *reg; |
211 | |
212 | seq_puts (m, s: " VA regions | start | range | end \n" ); |
213 | seq_puts (m, s: "----------------------------------------------------------------------------\n" ); |
214 | mas_for_each(&mas, reg, ULONG_MAX) |
215 | seq_printf(m, fmt: " | 0x%016llx | 0x%016llx | 0x%016llx\n" , |
216 | reg->va.addr, reg->va.range, reg->va.addr + reg->va.range); |
217 | } |
218 | |
219 | static int |
220 | nouveau_debugfs_gpuva(struct seq_file *m, void *data) |
221 | { |
222 | struct drm_info_node *node = (struct drm_info_node *) m->private; |
223 | struct nouveau_drm *drm = nouveau_drm(dev: node->minor->dev); |
224 | struct nouveau_cli *cli; |
225 | |
226 | mutex_lock(&drm->clients_lock); |
227 | list_for_each_entry(cli, &drm->clients, head) { |
228 | struct nouveau_uvmm *uvmm = nouveau_cli_uvmm(cli); |
229 | |
230 | if (!uvmm) |
231 | continue; |
232 | |
233 | nouveau_uvmm_lock(uvmm); |
234 | drm_debugfs_gpuva_info(m, gpuvm: &uvmm->base); |
235 | seq_puts(m, s: "\n" ); |
236 | nouveau_debugfs_gpuva_regions(m, uvmm); |
237 | nouveau_uvmm_unlock(uvmm); |
238 | } |
239 | mutex_unlock(lock: &drm->clients_lock); |
240 | |
241 | return 0; |
242 | } |
243 | |
244 | static const struct file_operations nouveau_pstate_fops = { |
245 | .owner = THIS_MODULE, |
246 | .open = nouveau_debugfs_pstate_open, |
247 | .read = seq_read, |
248 | .write = nouveau_debugfs_pstate_set, |
249 | .release = single_release, |
250 | }; |
251 | |
252 | static struct drm_info_list nouveau_debugfs_list[] = { |
253 | { "vbios.rom" , nouveau_debugfs_vbios_image, 0, NULL }, |
254 | { "strap_peek" , nouveau_debugfs_strap_peek, 0, NULL }, |
255 | DRM_DEBUGFS_GPUVA_INFO(nouveau_debugfs_gpuva, NULL), |
256 | }; |
257 | #define NOUVEAU_DEBUGFS_ENTRIES ARRAY_SIZE(nouveau_debugfs_list) |
258 | |
259 | static const struct nouveau_debugfs_files { |
260 | const char *name; |
261 | const struct file_operations *fops; |
262 | } nouveau_debugfs_files[] = { |
263 | {"pstate" , &nouveau_pstate_fops}, |
264 | }; |
265 | |
266 | void |
267 | nouveau_drm_debugfs_init(struct drm_minor *minor) |
268 | { |
269 | struct nouveau_drm *drm = nouveau_drm(dev: minor->dev); |
270 | struct dentry *dentry; |
271 | int i; |
272 | |
273 | for (i = 0; i < ARRAY_SIZE(nouveau_debugfs_files); i++) { |
274 | debugfs_create_file(name: nouveau_debugfs_files[i].name, |
275 | S_IRUGO | S_IWUSR, |
276 | parent: minor->debugfs_root, data: minor->dev, |
277 | fops: nouveau_debugfs_files[i].fops); |
278 | } |
279 | |
280 | drm_debugfs_create_files(files: nouveau_debugfs_list, |
281 | NOUVEAU_DEBUGFS_ENTRIES, |
282 | root: minor->debugfs_root, minor); |
283 | |
284 | /* Set the size of the vbios since we know it, and it's confusing to |
285 | * userspace if it wants to seek() but the file has a length of 0 |
286 | */ |
287 | dentry = debugfs_lookup(name: "vbios.rom" , parent: minor->debugfs_root); |
288 | if (!dentry) |
289 | return; |
290 | |
291 | d_inode(dentry)->i_size = drm->vbios.length; |
292 | dput(dentry); |
293 | } |
294 | |
295 | int |
296 | nouveau_debugfs_init(struct nouveau_drm *drm) |
297 | { |
298 | drm->debugfs = kzalloc(size: sizeof(*drm->debugfs), GFP_KERNEL); |
299 | if (!drm->debugfs) |
300 | return -ENOMEM; |
301 | |
302 | return nvif_object_ctor(&drm->client.device.object, "debugfsCtrl" , 0, |
303 | NVIF_CLASS_CONTROL, NULL, 0, |
304 | &drm->debugfs->ctrl); |
305 | } |
306 | |
307 | void |
308 | nouveau_debugfs_fini(struct nouveau_drm *drm) |
309 | { |
310 | if (drm->debugfs && drm->debugfs->ctrl.priv) |
311 | nvif_object_dtor(&drm->debugfs->ctrl); |
312 | |
313 | kfree(objp: drm->debugfs); |
314 | drm->debugfs = NULL; |
315 | } |
316 | |