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