Commit | Line | Data |
---|---|---|
f70ee2ad | 1 | // SPDX-License-Identifier: GPL-2.0 |
02b6fdc2 LB |
2 | /* |
3 | * xhci-debugfs.c - xHCI debugfs interface | |
4 | * | |
5 | * Copyright (C) 2017 Intel Corporation | |
6 | * | |
7 | * Author: Lu Baolu <baolu.lu@linux.intel.com> | |
02b6fdc2 LB |
8 | */ |
9 | ||
10 | #include <linux/slab.h> | |
11 | ||
12 | #include "xhci.h" | |
13 | #include "xhci-debugfs.h" | |
14 | ||
15 | static const struct debugfs_reg32 xhci_cap_regs[] = { | |
16 | dump_register(CAPLENGTH), | |
17 | dump_register(HCSPARAMS1), | |
18 | dump_register(HCSPARAMS2), | |
19 | dump_register(HCSPARAMS3), | |
20 | dump_register(HCCPARAMS1), | |
21 | dump_register(DOORBELLOFF), | |
22 | dump_register(RUNTIMEOFF), | |
23 | dump_register(HCCPARAMS2), | |
24 | }; | |
25 | ||
26 | static const struct debugfs_reg32 xhci_op_regs[] = { | |
27 | dump_register(USBCMD), | |
28 | dump_register(USBSTS), | |
29 | dump_register(PAGESIZE), | |
30 | dump_register(DNCTRL), | |
31 | dump_register(CRCR), | |
32 | dump_register(DCBAAP_LOW), | |
33 | dump_register(DCBAAP_HIGH), | |
34 | dump_register(CONFIG), | |
35 | }; | |
36 | ||
37 | static const struct debugfs_reg32 xhci_runtime_regs[] = { | |
38 | dump_register(MFINDEX), | |
39 | dump_register(IR0_IMAN), | |
40 | dump_register(IR0_IMOD), | |
41 | dump_register(IR0_ERSTSZ), | |
42 | dump_register(IR0_ERSTBA_LOW), | |
43 | dump_register(IR0_ERSTBA_HIGH), | |
44 | dump_register(IR0_ERDP_LOW), | |
45 | dump_register(IR0_ERDP_HIGH), | |
46 | }; | |
47 | ||
48 | static const struct debugfs_reg32 xhci_extcap_legsup[] = { | |
49 | dump_register(EXTCAP_USBLEGSUP), | |
50 | dump_register(EXTCAP_USBLEGCTLSTS), | |
51 | }; | |
52 | ||
53 | static const struct debugfs_reg32 xhci_extcap_protocol[] = { | |
54 | dump_register(EXTCAP_REVISION), | |
55 | dump_register(EXTCAP_NAME), | |
56 | dump_register(EXTCAP_PORTINFO), | |
57 | dump_register(EXTCAP_PORTTYPE), | |
58 | dump_register(EXTCAP_MANTISSA1), | |
59 | dump_register(EXTCAP_MANTISSA2), | |
60 | dump_register(EXTCAP_MANTISSA3), | |
61 | dump_register(EXTCAP_MANTISSA4), | |
62 | dump_register(EXTCAP_MANTISSA5), | |
63 | dump_register(EXTCAP_MANTISSA6), | |
64 | }; | |
65 | ||
66 | static const struct debugfs_reg32 xhci_extcap_dbc[] = { | |
67 | dump_register(EXTCAP_DBC_CAPABILITY), | |
68 | dump_register(EXTCAP_DBC_DOORBELL), | |
69 | dump_register(EXTCAP_DBC_ERSTSIZE), | |
70 | dump_register(EXTCAP_DBC_ERST_LOW), | |
71 | dump_register(EXTCAP_DBC_ERST_HIGH), | |
72 | dump_register(EXTCAP_DBC_ERDP_LOW), | |
73 | dump_register(EXTCAP_DBC_ERDP_HIGH), | |
74 | dump_register(EXTCAP_DBC_CONTROL), | |
75 | dump_register(EXTCAP_DBC_STATUS), | |
76 | dump_register(EXTCAP_DBC_PORTSC), | |
77 | dump_register(EXTCAP_DBC_CONT_LOW), | |
78 | dump_register(EXTCAP_DBC_CONT_HIGH), | |
79 | dump_register(EXTCAP_DBC_DEVINFO1), | |
80 | dump_register(EXTCAP_DBC_DEVINFO2), | |
81 | }; | |
82 | ||
83 | static struct dentry *xhci_debugfs_root; | |
84 | ||
85 | static struct xhci_regset *xhci_debugfs_alloc_regset(struct xhci_hcd *xhci) | |
86 | { | |
87 | struct xhci_regset *regset; | |
88 | ||
89 | regset = kzalloc(sizeof(*regset), GFP_KERNEL); | |
90 | if (!regset) | |
91 | return NULL; | |
92 | ||
93 | /* | |
94 | * The allocation and free of regset are executed in order. | |
95 | * We needn't a lock here. | |
96 | */ | |
97 | INIT_LIST_HEAD(®set->list); | |
98 | list_add_tail(®set->list, &xhci->regset_list); | |
99 | ||
100 | return regset; | |
101 | } | |
102 | ||
103 | static void xhci_debugfs_free_regset(struct xhci_regset *regset) | |
104 | { | |
105 | if (!regset) | |
106 | return; | |
107 | ||
108 | list_del(®set->list); | |
109 | kfree(regset); | |
110 | } | |
111 | ||
112 | static void xhci_debugfs_regset(struct xhci_hcd *xhci, u32 base, | |
113 | const struct debugfs_reg32 *regs, | |
114 | size_t nregs, struct dentry *parent, | |
115 | const char *fmt, ...) | |
116 | { | |
117 | struct xhci_regset *rgs; | |
118 | va_list args; | |
119 | struct debugfs_regset32 *regset; | |
120 | struct usb_hcd *hcd = xhci_to_hcd(xhci); | |
121 | ||
122 | rgs = xhci_debugfs_alloc_regset(xhci); | |
123 | if (!rgs) | |
124 | return; | |
125 | ||
126 | va_start(args, fmt); | |
127 | vsnprintf(rgs->name, sizeof(rgs->name), fmt, args); | |
128 | va_end(args); | |
129 | ||
130 | regset = &rgs->regset; | |
131 | regset->regs = regs; | |
132 | regset->nregs = nregs; | |
133 | regset->base = hcd->regs + base; | |
134 | ||
135 | debugfs_create_regset32((const char *)rgs->name, 0444, parent, regset); | |
136 | } | |
137 | ||
138 | static void xhci_debugfs_extcap_regset(struct xhci_hcd *xhci, int cap_id, | |
139 | const struct debugfs_reg32 *regs, | |
140 | size_t n, const char *cap_name) | |
141 | { | |
142 | u32 offset; | |
143 | int index = 0; | |
144 | size_t psic, nregs = n; | |
145 | void __iomem *base = &xhci->cap_regs->hc_capbase; | |
146 | ||
147 | offset = xhci_find_next_ext_cap(base, 0, cap_id); | |
148 | while (offset) { | |
149 | if (cap_id == XHCI_EXT_CAPS_PROTOCOL) { | |
150 | psic = XHCI_EXT_PORT_PSIC(readl(base + offset + 8)); | |
151 | nregs = min(4 + psic, n); | |
152 | } | |
153 | ||
154 | xhci_debugfs_regset(xhci, offset, regs, nregs, | |
155 | xhci->debugfs_root, "%s:%02d", | |
156 | cap_name, index); | |
157 | offset = xhci_find_next_ext_cap(base, offset, cap_id); | |
158 | index++; | |
159 | } | |
160 | } | |
161 | ||
162 | static int xhci_ring_enqueue_show(struct seq_file *s, void *unused) | |
163 | { | |
164 | dma_addr_t dma; | |
dde63405 | 165 | struct xhci_ring *ring = *(struct xhci_ring **)s->private; |
02b6fdc2 LB |
166 | |
167 | dma = xhci_trb_virt_to_dma(ring->enq_seg, ring->enqueue); | |
168 | seq_printf(s, "%pad\n", &dma); | |
169 | ||
170 | return 0; | |
171 | } | |
172 | ||
173 | static int xhci_ring_dequeue_show(struct seq_file *s, void *unused) | |
174 | { | |
175 | dma_addr_t dma; | |
dde63405 | 176 | struct xhci_ring *ring = *(struct xhci_ring **)s->private; |
02b6fdc2 LB |
177 | |
178 | dma = xhci_trb_virt_to_dma(ring->deq_seg, ring->dequeue); | |
179 | seq_printf(s, "%pad\n", &dma); | |
180 | ||
181 | return 0; | |
182 | } | |
183 | ||
184 | static int xhci_ring_cycle_show(struct seq_file *s, void *unused) | |
185 | { | |
dde63405 | 186 | struct xhci_ring *ring = *(struct xhci_ring **)s->private; |
02b6fdc2 LB |
187 | |
188 | seq_printf(s, "%d\n", ring->cycle_state); | |
189 | ||
190 | return 0; | |
191 | } | |
192 | ||
193 | static void xhci_ring_dump_segment(struct seq_file *s, | |
194 | struct xhci_segment *seg) | |
195 | { | |
196 | int i; | |
197 | dma_addr_t dma; | |
198 | union xhci_trb *trb; | |
199 | ||
200 | for (i = 0; i < TRBS_PER_SEGMENT; i++) { | |
201 | trb = &seg->trbs[i]; | |
202 | dma = seg->dma + i * sizeof(*trb); | |
203 | seq_printf(s, "%pad: %s\n", &dma, | |
204 | xhci_decode_trb(trb->generic.field[0], | |
205 | trb->generic.field[1], | |
206 | trb->generic.field[2], | |
207 | trb->generic.field[3])); | |
208 | } | |
209 | } | |
210 | ||
211 | static int xhci_ring_trb_show(struct seq_file *s, void *unused) | |
212 | { | |
213 | int i; | |
214 | struct xhci_ring *ring = s->private; | |
215 | struct xhci_segment *seg = ring->first_seg; | |
216 | ||
217 | for (i = 0; i < ring->num_segs; i++) { | |
218 | xhci_ring_dump_segment(s, seg); | |
219 | seg = seg->next; | |
220 | } | |
221 | ||
222 | return 0; | |
223 | } | |
224 | ||
225 | static struct xhci_file_map ring_files[] = { | |
226 | {"enqueue", xhci_ring_enqueue_show, }, | |
227 | {"dequeue", xhci_ring_dequeue_show, }, | |
228 | {"cycle", xhci_ring_cycle_show, }, | |
229 | {"trbs", xhci_ring_trb_show, }, | |
230 | }; | |
231 | ||
232 | static int xhci_ring_open(struct inode *inode, struct file *file) | |
233 | { | |
234 | int i; | |
235 | struct xhci_file_map *f_map; | |
236 | const char *file_name = file_dentry(file)->d_iname; | |
237 | ||
238 | for (i = 0; i < ARRAY_SIZE(ring_files); i++) { | |
239 | f_map = &ring_files[i]; | |
240 | ||
241 | if (strcmp(f_map->name, file_name) == 0) | |
242 | break; | |
243 | } | |
244 | ||
245 | return single_open(file, f_map->show, inode->i_private); | |
246 | } | |
247 | ||
248 | static const struct file_operations xhci_ring_fops = { | |
249 | .open = xhci_ring_open, | |
250 | .read = seq_read, | |
251 | .llseek = seq_lseek, | |
252 | .release = single_release, | |
253 | }; | |
254 | ||
255 | static int xhci_slot_context_show(struct seq_file *s, void *unused) | |
256 | { | |
257 | struct xhci_hcd *xhci; | |
258 | struct xhci_slot_ctx *slot_ctx; | |
259 | struct xhci_slot_priv *priv = s->private; | |
260 | struct xhci_virt_device *dev = priv->dev; | |
261 | ||
262 | xhci = hcd_to_xhci(bus_to_hcd(dev->udev->bus)); | |
263 | slot_ctx = xhci_get_slot_ctx(xhci, dev->out_ctx); | |
264 | seq_printf(s, "%pad: %s\n", &dev->out_ctx->dma, | |
265 | xhci_decode_slot_context(slot_ctx->dev_info, | |
266 | slot_ctx->dev_info2, | |
267 | slot_ctx->tt_info, | |
268 | slot_ctx->dev_state)); | |
269 | ||
270 | return 0; | |
271 | } | |
272 | ||
273 | static int xhci_endpoint_context_show(struct seq_file *s, void *unused) | |
274 | { | |
275 | int dci; | |
276 | dma_addr_t dma; | |
277 | struct xhci_hcd *xhci; | |
278 | struct xhci_ep_ctx *ep_ctx; | |
279 | struct xhci_slot_priv *priv = s->private; | |
280 | struct xhci_virt_device *dev = priv->dev; | |
281 | ||
282 | xhci = hcd_to_xhci(bus_to_hcd(dev->udev->bus)); | |
283 | ||
284 | for (dci = 1; dci < 32; dci++) { | |
285 | ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, dci); | |
286 | dma = dev->out_ctx->dma + dci * CTX_SIZE(xhci->hcc_params); | |
287 | seq_printf(s, "%pad: %s\n", &dma, | |
288 | xhci_decode_ep_context(ep_ctx->ep_info, | |
289 | ep_ctx->ep_info2, | |
290 | ep_ctx->deq, | |
291 | ep_ctx->tx_info)); | |
292 | } | |
293 | ||
294 | return 0; | |
295 | } | |
296 | ||
297 | static int xhci_device_name_show(struct seq_file *s, void *unused) | |
298 | { | |
299 | struct xhci_slot_priv *priv = s->private; | |
300 | struct xhci_virt_device *dev = priv->dev; | |
301 | ||
302 | seq_printf(s, "%s\n", dev_name(&dev->udev->dev)); | |
303 | ||
304 | return 0; | |
305 | } | |
306 | ||
307 | static struct xhci_file_map context_files[] = { | |
308 | {"name", xhci_device_name_show, }, | |
309 | {"slot-context", xhci_slot_context_show, }, | |
310 | {"ep-context", xhci_endpoint_context_show, }, | |
311 | }; | |
312 | ||
313 | static int xhci_context_open(struct inode *inode, struct file *file) | |
314 | { | |
315 | int i; | |
316 | struct xhci_file_map *f_map; | |
317 | const char *file_name = file_dentry(file)->d_iname; | |
318 | ||
319 | for (i = 0; i < ARRAY_SIZE(context_files); i++) { | |
320 | f_map = &context_files[i]; | |
321 | ||
322 | if (strcmp(f_map->name, file_name) == 0) | |
323 | break; | |
324 | } | |
325 | ||
326 | return single_open(file, f_map->show, inode->i_private); | |
327 | } | |
328 | ||
329 | static const struct file_operations xhci_context_fops = { | |
330 | .open = xhci_context_open, | |
331 | .read = seq_read, | |
332 | .llseek = seq_lseek, | |
333 | .release = single_release, | |
334 | }; | |
335 | ||
336 | static void xhci_debugfs_create_files(struct xhci_hcd *xhci, | |
337 | struct xhci_file_map *files, | |
338 | size_t nentries, void *data, | |
339 | struct dentry *parent, | |
340 | const struct file_operations *fops) | |
341 | { | |
342 | int i; | |
343 | ||
344 | for (i = 0; i < nentries; i++) | |
345 | debugfs_create_file(files[i].name, 0444, parent, data, fops); | |
346 | } | |
347 | ||
348 | static struct dentry *xhci_debugfs_create_ring_dir(struct xhci_hcd *xhci, | |
dde63405 | 349 | struct xhci_ring **ring, |
02b6fdc2 LB |
350 | const char *name, |
351 | struct dentry *parent) | |
352 | { | |
353 | struct dentry *dir; | |
354 | ||
355 | dir = debugfs_create_dir(name, parent); | |
356 | xhci_debugfs_create_files(xhci, ring_files, ARRAY_SIZE(ring_files), | |
357 | ring, dir, &xhci_ring_fops); | |
358 | ||
359 | return dir; | |
360 | } | |
361 | ||
362 | static void xhci_debugfs_create_context_files(struct xhci_hcd *xhci, | |
363 | struct dentry *parent, | |
364 | int slot_id) | |
365 | { | |
366 | struct xhci_virt_device *dev = xhci->devs[slot_id]; | |
367 | ||
368 | xhci_debugfs_create_files(xhci, context_files, | |
369 | ARRAY_SIZE(context_files), | |
370 | dev->debugfs_private, | |
371 | parent, &xhci_context_fops); | |
372 | } | |
373 | ||
374 | void xhci_debugfs_create_endpoint(struct xhci_hcd *xhci, | |
375 | struct xhci_virt_device *dev, | |
376 | int ep_index) | |
377 | { | |
378 | struct xhci_ep_priv *epriv; | |
379 | struct xhci_slot_priv *spriv = dev->debugfs_private; | |
380 | ||
381 | if (spriv->eps[ep_index]) | |
382 | return; | |
383 | ||
384 | epriv = kzalloc(sizeof(*epriv), GFP_KERNEL); | |
385 | if (!epriv) | |
386 | return; | |
387 | ||
388 | snprintf(epriv->name, sizeof(epriv->name), "ep%02d", ep_index); | |
389 | epriv->root = xhci_debugfs_create_ring_dir(xhci, | |
dde63405 | 390 | &dev->eps[ep_index].new_ring, |
02b6fdc2 LB |
391 | epriv->name, |
392 | spriv->root); | |
393 | spriv->eps[ep_index] = epriv; | |
394 | } | |
395 | ||
396 | void xhci_debugfs_remove_endpoint(struct xhci_hcd *xhci, | |
397 | struct xhci_virt_device *dev, | |
398 | int ep_index) | |
399 | { | |
400 | struct xhci_ep_priv *epriv; | |
401 | struct xhci_slot_priv *spriv = dev->debugfs_private; | |
402 | ||
403 | if (!spriv || !spriv->eps[ep_index]) | |
404 | return; | |
405 | ||
406 | epriv = spriv->eps[ep_index]; | |
407 | debugfs_remove_recursive(epriv->root); | |
408 | spriv->eps[ep_index] = NULL; | |
409 | kfree(epriv); | |
410 | } | |
411 | ||
412 | void xhci_debugfs_create_slot(struct xhci_hcd *xhci, int slot_id) | |
413 | { | |
414 | struct xhci_slot_priv *priv; | |
415 | struct xhci_virt_device *dev = xhci->devs[slot_id]; | |
416 | ||
417 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | |
418 | if (!priv) | |
419 | return; | |
420 | ||
421 | snprintf(priv->name, sizeof(priv->name), "%02d", slot_id); | |
422 | priv->root = debugfs_create_dir(priv->name, xhci->debugfs_slots); | |
423 | priv->dev = dev; | |
424 | dev->debugfs_private = priv; | |
425 | ||
dde63405 | 426 | xhci_debugfs_create_ring_dir(xhci, &dev->eps[0].ring, |
02b6fdc2 LB |
427 | "ep00", priv->root); |
428 | ||
429 | xhci_debugfs_create_context_files(xhci, priv->root, slot_id); | |
430 | } | |
431 | ||
432 | void xhci_debugfs_remove_slot(struct xhci_hcd *xhci, int slot_id) | |
433 | { | |
434 | int i; | |
435 | struct xhci_slot_priv *priv; | |
436 | struct xhci_virt_device *dev = xhci->devs[slot_id]; | |
437 | ||
438 | if (!dev || !dev->debugfs_private) | |
439 | return; | |
440 | ||
441 | priv = dev->debugfs_private; | |
442 | ||
443 | debugfs_remove_recursive(priv->root); | |
444 | ||
445 | for (i = 0; i < 31; i++) | |
446 | kfree(priv->eps[i]); | |
447 | ||
448 | kfree(priv); | |
449 | dev->debugfs_private = NULL; | |
450 | } | |
451 | ||
452 | void xhci_debugfs_init(struct xhci_hcd *xhci) | |
453 | { | |
454 | struct device *dev = xhci_to_hcd(xhci)->self.controller; | |
455 | ||
456 | xhci->debugfs_root = debugfs_create_dir(dev_name(dev), | |
457 | xhci_debugfs_root); | |
458 | ||
459 | INIT_LIST_HEAD(&xhci->regset_list); | |
460 | ||
461 | xhci_debugfs_regset(xhci, | |
462 | 0, | |
463 | xhci_cap_regs, ARRAY_SIZE(xhci_cap_regs), | |
464 | xhci->debugfs_root, "reg-cap"); | |
465 | ||
466 | xhci_debugfs_regset(xhci, | |
467 | HC_LENGTH(readl(&xhci->cap_regs->hc_capbase)), | |
468 | xhci_op_regs, ARRAY_SIZE(xhci_op_regs), | |
469 | xhci->debugfs_root, "reg-op"); | |
470 | ||
471 | xhci_debugfs_regset(xhci, | |
472 | readl(&xhci->cap_regs->run_regs_off) & RTSOFF_MASK, | |
473 | xhci_runtime_regs, ARRAY_SIZE(xhci_runtime_regs), | |
474 | xhci->debugfs_root, "reg-runtime"); | |
475 | ||
476 | xhci_debugfs_extcap_regset(xhci, XHCI_EXT_CAPS_LEGACY, | |
477 | xhci_extcap_legsup, | |
478 | ARRAY_SIZE(xhci_extcap_legsup), | |
479 | "reg-ext-legsup"); | |
480 | ||
481 | xhci_debugfs_extcap_regset(xhci, XHCI_EXT_CAPS_PROTOCOL, | |
482 | xhci_extcap_protocol, | |
483 | ARRAY_SIZE(xhci_extcap_protocol), | |
484 | "reg-ext-protocol"); | |
485 | ||
486 | xhci_debugfs_extcap_regset(xhci, XHCI_EXT_CAPS_DEBUG, | |
487 | xhci_extcap_dbc, | |
488 | ARRAY_SIZE(xhci_extcap_dbc), | |
489 | "reg-ext-dbc"); | |
490 | ||
dde63405 | 491 | xhci_debugfs_create_ring_dir(xhci, &xhci->cmd_ring, |
02b6fdc2 LB |
492 | "command-ring", |
493 | xhci->debugfs_root); | |
494 | ||
dde63405 | 495 | xhci_debugfs_create_ring_dir(xhci, &xhci->event_ring, |
02b6fdc2 LB |
496 | "event-ring", |
497 | xhci->debugfs_root); | |
498 | ||
499 | xhci->debugfs_slots = debugfs_create_dir("devices", xhci->debugfs_root); | |
500 | } | |
501 | ||
502 | void xhci_debugfs_exit(struct xhci_hcd *xhci) | |
503 | { | |
504 | struct xhci_regset *rgs, *tmp; | |
505 | ||
506 | debugfs_remove_recursive(xhci->debugfs_root); | |
507 | xhci->debugfs_root = NULL; | |
508 | xhci->debugfs_slots = NULL; | |
509 | ||
510 | list_for_each_entry_safe(rgs, tmp, &xhci->regset_list, list) | |
511 | xhci_debugfs_free_regset(rgs); | |
512 | } | |
513 | ||
514 | void __init xhci_debugfs_create_root(void) | |
515 | { | |
516 | xhci_debugfs_root = debugfs_create_dir("xhci", usb_debug_root); | |
517 | } | |
518 | ||
519 | void __exit xhci_debugfs_remove_root(void) | |
520 | { | |
521 | debugfs_remove_recursive(xhci_debugfs_root); | |
522 | xhci_debugfs_root = NULL; | |
523 | } |