Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
846 | giacomo | 1 | /* |
2 | * Copyright (c) 2001-2002 by David Brownell |
||
3 | * |
||
4 | * This program is free software; you can redistribute it and/or modify it |
||
5 | * under the terms of the GNU General Public License as published by the |
||
6 | * Free Software Foundation; either version 2 of the License, or (at your |
||
7 | * option) any later version. |
||
8 | * |
||
9 | * This program is distributed in the hope that it will be useful, but |
||
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
||
11 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
||
12 | * for more details. |
||
13 | * |
||
14 | * You should have received a copy of the GNU General Public License |
||
15 | * along with this program; if not, write to the Free Software Foundation, |
||
16 | * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
||
17 | */ |
||
18 | |||
19 | /* this file is part of ehci-hcd.c */ |
||
20 | |||
21 | #define ehci_dbg(ehci, fmt, args...) \ |
||
22 | dev_dbg ((ehci)->hcd.controller , fmt , ## args ) |
||
23 | #define ehci_err(ehci, fmt, args...) \ |
||
24 | dev_err ((ehci)->hcd.controller , fmt , ## args ) |
||
25 | #define ehci_info(ehci, fmt, args...) \ |
||
26 | dev_info ((ehci)->hcd.controller , fmt , ## args ) |
||
27 | #define ehci_warn(ehci, fmt, args...) \ |
||
28 | dev_warn ((ehci)->hcd.controller , fmt , ## args ) |
||
29 | |||
30 | #ifdef EHCI_VERBOSE_DEBUG |
||
31 | # define vdbg dbg |
||
32 | # define ehci_vdbg ehci_dbg |
||
33 | #else |
||
34 | # define vdbg(fmt,args...) do { } while (0) |
||
35 | # define ehci_vdbg(ehci, fmt, args...) do { } while (0) |
||
36 | #endif |
||
37 | |||
38 | #ifdef DEBUG |
||
39 | |||
40 | /* check the values in the HCSPARAMS register |
||
41 | * (host controller _Structural_ parameters) |
||
42 | * see EHCI spec, Table 2-4 for each value |
||
43 | */ |
||
44 | static void dbg_hcs_params (struct ehci_hcd *ehci, char *label) |
||
45 | { |
||
46 | u32 params = readl (&ehci->caps->hcs_params); |
||
47 | |||
48 | ehci_dbg (ehci, |
||
49 | "%s hcs_params 0x%x dbg=%d%s cc=%d pcc=%d%s%s ports=%d\n", |
||
50 | label, params, |
||
51 | HCS_DEBUG_PORT (params), |
||
52 | HCS_INDICATOR (params) ? " ind" : "", |
||
53 | HCS_N_CC (params), |
||
54 | HCS_N_PCC (params), |
||
55 | HCS_PORTROUTED (params) ? "" : " ordered", |
||
56 | HCS_PPC (params) ? "" : " !ppc", |
||
57 | HCS_N_PORTS (params) |
||
58 | ); |
||
59 | /* Port routing, per EHCI 0.95 Spec, Section 2.2.5 */ |
||
60 | if (HCS_PORTROUTED (params)) { |
||
61 | int i; |
||
62 | char buf [46], tmp [7], byte; |
||
63 | |||
64 | buf[0] = 0; |
||
65 | for (i = 0; i < HCS_N_PORTS (params); i++) { |
||
66 | byte = readb (&ehci->caps->portroute[(i>>1)]); |
||
67 | sprintf26(tmp, "%d ", |
||
68 | ((i & 0x1) ? ((byte)&0xf) : ((byte>>4)&0xf))); |
||
69 | strcat(buf, tmp); |
||
70 | } |
||
71 | ehci_dbg (ehci, "%s portroute %s\n", |
||
72 | label, buf); |
||
73 | } |
||
74 | } |
||
75 | #else |
||
76 | |||
77 | static inline void dbg_hcs_params (struct ehci_hcd *ehci, char *label) {} |
||
78 | |||
79 | #endif |
||
80 | |||
81 | #ifdef DEBUG |
||
82 | |||
83 | /* check the values in the HCCPARAMS register |
||
84 | * (host controller _Capability_ parameters) |
||
85 | * see EHCI Spec, Table 2-5 for each value |
||
86 | * */ |
||
87 | static void dbg_hcc_params (struct ehci_hcd *ehci, char *label) |
||
88 | { |
||
89 | u32 params = readl (&ehci->caps->hcc_params); |
||
90 | |||
91 | if (HCC_ISOC_CACHE (params)) { |
||
92 | ehci_dbg (ehci, |
||
93 | "%s hcc_params %04x caching frame %s%s%s\n", |
||
94 | label, params, |
||
95 | HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024", |
||
96 | HCC_CANPARK (params) ? " park" : "", |
||
97 | HCC_64BIT_ADDR (params) ? " 64 bit addr" : ""); |
||
98 | } else { |
||
99 | ehci_dbg (ehci, |
||
100 | "%s hcc_params %04x thresh %d uframes %s%s%s\n", |
||
101 | label, |
||
102 | params, |
||
103 | HCC_ISOC_THRES (params), |
||
104 | HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024", |
||
105 | HCC_CANPARK (params) ? " park" : "", |
||
106 | HCC_64BIT_ADDR (params) ? " 64 bit addr" : ""); |
||
107 | } |
||
108 | } |
||
109 | #else |
||
110 | |||
111 | static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {} |
||
112 | |||
113 | #endif |
||
114 | |||
115 | #ifdef DEBUG |
||
116 | |||
117 | static void __attribute__((__unused__)) |
||
118 | dbg_qtd (char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd) |
||
119 | { |
||
120 | ehci_dbg (ehci, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd, |
||
121 | cpu_to_le32p (&qtd->hw_next), |
||
122 | cpu_to_le32p (&qtd->hw_alt_next), |
||
123 | cpu_to_le32p (&qtd->hw_token), |
||
124 | cpu_to_le32p (&qtd->hw_buf [0])); |
||
125 | if (qtd->hw_buf [1]) |
||
126 | ehci_dbg (ehci, " p1=%08x p2=%08x p3=%08x p4=%08x\n", |
||
127 | cpu_to_le32p (&qtd->hw_buf [1]), |
||
128 | cpu_to_le32p (&qtd->hw_buf [2]), |
||
129 | cpu_to_le32p (&qtd->hw_buf [3]), |
||
130 | cpu_to_le32p (&qtd->hw_buf [4])); |
||
131 | } |
||
132 | |||
133 | static void __attribute__((__unused__)) |
||
134 | dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) |
||
135 | { |
||
136 | ehci_dbg (ehci, "%s qh %p n%08x info %x %x qtd %x\n", label, |
||
137 | qh, qh->hw_next, qh->hw_info1, qh->hw_info2, |
||
138 | qh->hw_current); |
||
139 | dbg_qtd ("overlay", ehci, (struct ehci_qtd *) &qh->hw_qtd_next); |
||
140 | } |
||
141 | |||
142 | static int __attribute__((__unused__)) |
||
143 | dbg_status_buf (char *buf, unsigned len, char *label, u32 status) |
||
144 | { |
||
145 | return snprintf26(buf, len, |
||
146 | "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", |
||
147 | label, label [0] ? " " : "", status, |
||
148 | (status & STS_ASS) ? " Async" : "", |
||
149 | (status & STS_PSS) ? " Periodic" : "", |
||
150 | (status & STS_RECL) ? " Recl" : "", |
||
151 | (status & STS_HALT) ? " Halt" : "", |
||
152 | (status & STS_IAA) ? " IAA" : "", |
||
153 | (status & STS_FATAL) ? " FATAL" : "", |
||
154 | (status & STS_FLR) ? " FLR" : "", |
||
155 | (status & STS_PCD) ? " PCD" : "", |
||
156 | (status & STS_ERR) ? " ERR" : "", |
||
157 | (status & STS_INT) ? " INT" : "" |
||
158 | ); |
||
159 | } |
||
160 | |||
161 | static int __attribute__((__unused__)) |
||
162 | dbg_intr_buf (char *buf, unsigned len, char *label, u32 enable) |
||
163 | { |
||
164 | return snprintf26(buf, len, |
||
165 | "%s%sintrenable %02x%s%s%s%s%s%s", |
||
166 | label, label [0] ? " " : "", enable, |
||
167 | (enable & STS_IAA) ? " IAA" : "", |
||
168 | (enable & STS_FATAL) ? " FATAL" : "", |
||
169 | (enable & STS_FLR) ? " FLR" : "", |
||
170 | (enable & STS_PCD) ? " PCD" : "", |
||
171 | (enable & STS_ERR) ? " ERR" : "", |
||
172 | (enable & STS_INT) ? " INT" : "" |
||
173 | ); |
||
174 | } |
||
175 | |||
176 | static const char *const fls_strings [] = |
||
177 | { "1024", "512", "256", "??" }; |
||
178 | |||
179 | static int dbg_command_buf (char *buf, unsigned len, char *label, u32 command) |
||
180 | { |
||
181 | return snprintf26(buf, len, |
||
182 | "%s%scommand %06x %s=%d ithresh=%d%s%s%s%s period=%s%s %s", |
||
183 | label, label [0] ? " " : "", command, |
||
184 | (command & CMD_PARK) ? "park" : "(park)", |
||
185 | CMD_PARK_CNT (command), |
||
186 | (command >> 16) & 0x3f, |
||
187 | (command & CMD_LRESET) ? " LReset" : "", |
||
188 | (command & CMD_IAAD) ? " IAAD" : "", |
||
189 | (command & CMD_ASE) ? " Async" : "", |
||
190 | (command & CMD_PSE) ? " Periodic" : "", |
||
191 | fls_strings [(command >> 2) & 0x3], |
||
192 | (command & CMD_RESET) ? " Reset" : "", |
||
193 | (command & CMD_RUN) ? "RUN" : "HALT" |
||
194 | ); |
||
195 | } |
||
196 | |||
197 | static int |
||
198 | dbg_port_buf (char *buf, unsigned len, char *label, int port, u32 status) |
||
199 | { |
||
200 | char *sig; |
||
201 | |||
202 | /* signaling state */ |
||
203 | switch (status & (3 << 10)) { |
||
204 | case 0 << 10: sig = "se0"; break; |
||
205 | case 1 << 10: sig = "k"; break; /* low speed */ |
||
206 | case 2 << 10: sig = "j"; break; |
||
207 | default: sig = "?"; break; |
||
208 | } |
||
209 | |||
210 | return snprintf26(buf, len, |
||
211 | "%s%sport %d status %06x%s%s sig=%s %s%s%s%s%s%s%s%s%s", |
||
212 | label, label [0] ? " " : "", port, status, |
||
213 | (status & PORT_POWER) ? " POWER" : "", |
||
214 | (status & PORT_OWNER) ? " OWNER" : "", |
||
215 | sig, |
||
216 | (status & PORT_RESET) ? " RESET" : "", |
||
217 | (status & PORT_SUSPEND) ? " SUSPEND" : "", |
||
218 | (status & PORT_RESUME) ? " RESUME" : "", |
||
219 | (status & PORT_OCC) ? " OCC" : "", |
||
220 | (status & PORT_OC) ? " OC" : "", |
||
221 | (status & PORT_PEC) ? " PEC" : "", |
||
222 | (status & PORT_PE) ? " PE" : "", |
||
223 | (status & PORT_CSC) ? " CSC" : "", |
||
224 | (status & PORT_CONNECT) ? " CONNECT" : "" |
||
225 | ); |
||
226 | } |
||
227 | |||
228 | #else |
||
229 | static inline void __attribute__((__unused__)) |
||
230 | dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) |
||
231 | {} |
||
232 | |||
233 | static inline int __attribute__((__unused__)) |
||
234 | dbg_status_buf (char *buf, unsigned len, char *label, u32 status) |
||
235 | { return 0; } |
||
236 | |||
237 | static inline int __attribute__((__unused__)) |
||
238 | dbg_command_buf (char *buf, unsigned len, char *label, u32 command) |
||
239 | { return 0; } |
||
240 | |||
241 | static inline int __attribute__((__unused__)) |
||
242 | dbg_intr_buf (char *buf, unsigned len, char *label, u32 enable) |
||
243 | { return 0; } |
||
244 | |||
245 | static inline int __attribute__((__unused__)) |
||
246 | dbg_port_buf (char *buf, unsigned len, char *label, int port, u32 status) |
||
247 | { return 0; } |
||
248 | |||
249 | #endif /* DEBUG */ |
||
250 | |||
251 | /* functions have the "wrong" filename when they're output... */ |
||
252 | #define dbg_status(ehci, label, status) { \ |
||
253 | char _buf [80]; \ |
||
254 | dbg_status_buf (_buf, sizeof _buf, label, status); \ |
||
255 | ehci_dbg (ehci, "%s\n", _buf); \ |
||
256 | } |
||
257 | |||
258 | #define dbg_cmd(ehci, label, command) { \ |
||
259 | char _buf [80]; \ |
||
260 | dbg_command_buf (_buf, sizeof _buf, label, command); \ |
||
261 | ehci_dbg (ehci, "%s\n", _buf); \ |
||
262 | } |
||
263 | |||
264 | #define dbg_port(ehci, label, port, status) { \ |
||
265 | char _buf [80]; \ |
||
266 | dbg_port_buf (_buf, sizeof _buf, label, port, status); \ |
||
267 | ehci_dbg (ehci, "%s\n", _buf); \ |
||
268 | } |
||
269 | |||
270 | /*-------------------------------------------------------------------------*/ |
||
271 | |||
272 | #ifdef STUB_DEBUG_FILES |
||
273 | |||
274 | static inline void create_debug_files (struct ehci_hcd *bus) { } |
||
275 | static inline void remove_debug_files (struct ehci_hcd *bus) { } |
||
276 | |||
277 | #else |
||
278 | |||
279 | /* troubleshooting help: expose state in driverfs */ |
||
280 | |||
281 | #define speed_char(info1) ({ char tmp; \ |
||
282 | switch (info1 & (3 << 12)) { \ |
||
283 | case 0 << 12: tmp = 'f'; break; \ |
||
284 | case 1 << 12: tmp = 'l'; break; \ |
||
285 | case 2 << 12: tmp = 'h'; break; \ |
||
286 | default: tmp = '?'; break; \ |
||
287 | }; tmp; }) |
||
288 | |||
289 | static inline char token_mark (u32 token) |
||
290 | { |
||
291 | token = le32_to_cpu (token); |
||
292 | if (token & QTD_STS_ACTIVE) |
||
293 | return '*'; |
||
294 | if (token & QTD_STS_HALT) |
||
295 | return '-'; |
||
296 | if (!IS_SHORT_READ (token)) |
||
297 | return ' '; |
||
298 | /* tries to advance through hw_alt_next */ |
||
299 | return '/'; |
||
300 | } |
||
301 | |||
302 | static void qh_lines ( |
||
303 | struct ehci_hcd *ehci, |
||
304 | struct ehci_qh *qh, |
||
305 | char **nextp, |
||
306 | unsigned *sizep |
||
307 | ) |
||
308 | { |
||
309 | u32 scratch; |
||
310 | u32 hw_curr; |
||
311 | struct list_head *entry; |
||
312 | struct ehci_qtd *td; |
||
313 | unsigned temp; |
||
314 | unsigned size = *sizep; |
||
315 | char *next = *nextp; |
||
316 | char mark; |
||
317 | |||
318 | if (qh->hw_qtd_next == EHCI_LIST_END) /* NEC does this */ |
||
319 | mark = '@'; |
||
320 | else |
||
321 | mark = token_mark (qh->hw_token); |
||
322 | if (mark == '/') { /* qh_alt_next controls qh advance? */ |
||
323 | if ((qh->hw_alt_next & QTD_MASK) == ehci->async->hw_alt_next) |
||
324 | mark = '#'; /* blocked */ |
||
325 | else if (qh->hw_alt_next == EHCI_LIST_END) |
||
326 | mark = '.'; /* use hw_qtd_next */ |
||
327 | /* else alt_next points to some other qtd */ |
||
328 | } |
||
329 | scratch = cpu_to_le32p (&qh->hw_info1); |
||
330 | hw_curr = (mark == '*') ? cpu_to_le32p (&qh->hw_current) : 0; |
||
331 | temp = snprintf26(next, size, |
||
332 | "qh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d)", |
||
333 | qh, scratch & 0x007f, |
||
334 | speed_char (scratch), |
||
335 | (scratch >> 8) & 0x000f, |
||
336 | scratch, cpu_to_le32p (&qh->hw_info2), |
||
337 | cpu_to_le32p (&qh->hw_token), mark, |
||
338 | (__constant_cpu_to_le32 (QTD_TOGGLE) & qh->hw_token) |
||
339 | ? "data0" : "data1", |
||
340 | (cpu_to_le32p (&qh->hw_alt_next) >> 1) & 0x0f); |
||
341 | size -= temp; |
||
342 | next += temp; |
||
343 | |||
344 | /* hc may be modifying the list as we read it ... */ |
||
345 | list_for_each (entry, &qh->qtd_list) { |
||
346 | td = list_entry (entry, struct ehci_qtd, qtd_list); |
||
347 | scratch = cpu_to_le32p (&td->hw_token); |
||
348 | mark = ' '; |
||
349 | if (hw_curr == td->qtd_dma) |
||
350 | mark = '*'; |
||
351 | else if (qh->hw_qtd_next == td->qtd_dma) |
||
352 | mark = '+'; |
||
353 | else if (QTD_LENGTH (scratch)) { |
||
354 | if (td->hw_alt_next == ehci->async->hw_alt_next) |
||
355 | mark = '#'; |
||
356 | else if (td->hw_alt_next != EHCI_LIST_END) |
||
357 | mark = '/'; |
||
358 | } |
||
359 | temp = snprintf26(next, size, |
||
360 | "\n\t%p%c%s len=%d %08x urb %p", |
||
361 | td, mark, ({ char *tmp; |
||
362 | switch ((scratch>>8)&0x03) { |
||
363 | case 0: tmp = "out"; break; |
||
364 | case 1: tmp = "in"; break; |
||
365 | case 2: tmp = "setup"; break; |
||
366 | default: tmp = "?"; break; |
||
367 | } tmp;}), |
||
368 | (scratch >> 16) & 0x7fff, |
||
369 | scratch, |
||
370 | td->urb); |
||
371 | if (temp < 0) |
||
372 | temp = 0; |
||
373 | else if (size < temp) |
||
374 | temp = size; |
||
375 | size -= temp; |
||
376 | next += temp; |
||
377 | if (temp == size) |
||
378 | goto done; |
||
379 | } |
||
380 | |||
381 | temp = snprintf26(next, size, "\n"); |
||
382 | if (temp < 0) |
||
383 | temp = 0; |
||
384 | else if (size < temp) |
||
385 | temp = size; |
||
386 | size -= temp; |
||
387 | next += temp; |
||
388 | |||
389 | done: |
||
390 | *sizep = size; |
||
391 | *nextp = next; |
||
392 | } |
||
393 | |||
394 | static ssize_t |
||
395 | show_async (struct class_device *class_dev, char *buf) |
||
396 | { |
||
397 | struct usb_bus *bus; |
||
398 | struct usb_hcd *hcd; |
||
399 | struct ehci_hcd *ehci; |
||
400 | unsigned long flags; |
||
401 | unsigned temp, size; |
||
402 | char *next; |
||
403 | struct ehci_qh *qh; |
||
404 | |||
405 | *buf = 0; |
||
406 | |||
407 | bus = to_usb_bus(class_dev); |
||
408 | hcd = bus->hcpriv; |
||
409 | ehci = hcd_to_ehci (hcd); |
||
410 | next = buf; |
||
411 | size = PAGE_SIZE; |
||
412 | |||
413 | /* dumps a snapshot of the async schedule. |
||
414 | * usually empty except for long-term bulk reads, or head. |
||
415 | * one QH per line, and TDs we know about |
||
416 | */ |
||
417 | spin_lock_irqsave (&ehci->lock, flags); |
||
418 | for (qh = ehci->async->qh_next.qh; size > 0 && qh; qh = qh->qh_next.qh) |
||
419 | qh_lines (ehci, qh, &next, &size); |
||
420 | if (ehci->reclaim && size > 0) { |
||
421 | temp = snprintf26(next, size, "\nreclaim =\n"); |
||
422 | size -= temp; |
||
423 | next += temp; |
||
424 | |||
425 | for (qh = ehci->reclaim; size > 0 && qh; qh = qh->reclaim) |
||
426 | qh_lines (ehci, qh, &next, &size); |
||
427 | } |
||
428 | spin_unlock_irqrestore (&ehci->lock, flags); |
||
429 | |||
430 | return strlen (buf); |
||
431 | } |
||
432 | static CLASS_DEVICE_ATTR (async, S_IRUGO, show_async, NULL); |
||
433 | |||
434 | #define DBG_SCHED_LIMIT 64 |
||
435 | |||
436 | static ssize_t |
||
437 | show_periodic (struct class_device *class_dev, char *buf) |
||
438 | { |
||
439 | struct usb_bus *bus; |
||
440 | struct usb_hcd *hcd; |
||
441 | struct ehci_hcd *ehci; |
||
442 | unsigned long flags; |
||
443 | union ehci_shadow p, *seen; |
||
444 | unsigned temp, size, seen_count; |
||
445 | char *next; |
||
446 | unsigned i, tag; |
||
447 | |||
448 | if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, SLAB_ATOMIC))) |
||
449 | return 0; |
||
450 | seen_count = 0; |
||
451 | |||
452 | bus = to_usb_bus(class_dev); |
||
453 | hcd = bus->hcpriv; |
||
454 | ehci = hcd_to_ehci (hcd); |
||
455 | next = buf; |
||
456 | size = PAGE_SIZE; |
||
457 | |||
458 | temp = snprintf26(next, size, "size = %d\n", ehci->periodic_size); |
||
459 | size -= temp; |
||
460 | next += temp; |
||
461 | |||
462 | /* dump a snapshot of the periodic schedule. |
||
463 | * iso changes, interrupt usually doesn't. |
||
464 | */ |
||
465 | spin_lock_irqsave (&ehci->lock, flags); |
||
466 | for (i = 0; i < ehci->periodic_size; i++) { |
||
467 | p = ehci->pshadow [i]; |
||
468 | if (likely (!p.ptr)) |
||
469 | continue; |
||
470 | tag = Q_NEXT_TYPE (ehci->periodic [i]); |
||
471 | |||
472 | temp = snprintf26(next, size, "%4d: ", i); |
||
473 | size -= temp; |
||
474 | next += temp; |
||
475 | |||
476 | do { |
||
477 | switch (tag) { |
||
478 | case Q_TYPE_QH: |
||
479 | temp = snprintf26(next, size, " qh%d-%04x/%p", |
||
480 | p.qh->period, |
||
481 | le32_to_cpup (&p.qh->hw_info2) |
||
482 | /* uframe masks */ |
||
483 | & 0xffff, |
||
484 | p.qh); |
||
485 | size -= temp; |
||
486 | next += temp; |
||
487 | /* don't repeat what follows this qh */ |
||
488 | for (temp = 0; temp < seen_count; temp++) { |
||
489 | if (seen [temp].ptr != p.ptr) |
||
490 | continue; |
||
491 | if (p.qh->qh_next.ptr) |
||
492 | temp = snprintf26(next, size, |
||
493 | " ..."); |
||
494 | p.ptr = 0; |
||
495 | break; |
||
496 | } |
||
497 | /* show more info the first time around */ |
||
498 | if (temp == seen_count && p.ptr) { |
||
499 | u32 scratch = cpu_to_le32p ( |
||
500 | &p.qh->hw_info1); |
||
501 | struct ehci_qtd *qtd; |
||
502 | char *type = ""; |
||
503 | |||
504 | /* count tds, get ep direction */ |
||
505 | temp = 0; |
||
506 | list_for_each_entry (qtd, |
||
507 | &p.qh->qtd_list, |
||
508 | qtd_list) { |
||
509 | temp++; |
||
510 | switch (0x03 & (le32_to_cpu ( |
||
511 | qtd->hw_token) >> 8)) { |
||
512 | case 0: type = "out"; continue; |
||
513 | case 1: type = "in"; continue; |
||
514 | } |
||
515 | } |
||
516 | |||
517 | temp = snprintf26(next, size, |
||
518 | " (%c%d ep%d%s " |
||
519 | "[%d/%d] q%d p%d)", |
||
520 | speed_char (scratch), |
||
521 | scratch & 0x007f, |
||
522 | (scratch >> 8) & 0x000f, type, |
||
523 | p.qh->usecs, p.qh->c_usecs, |
||
524 | temp, |
||
525 | 0x7ff & (scratch >> 16)); |
||
526 | |||
527 | if (seen_count < DBG_SCHED_LIMIT) |
||
528 | seen [seen_count++].qh = p.qh; |
||
529 | } else |
||
530 | temp = 0; |
||
531 | if (p.qh) { |
||
532 | tag = Q_NEXT_TYPE (p.qh->hw_next); |
||
533 | p = p.qh->qh_next; |
||
534 | } |
||
535 | break; |
||
536 | case Q_TYPE_FSTN: |
||
537 | temp = snprintf26(next, size, |
||
538 | " fstn-%8x/%p", p.fstn->hw_prev, |
||
539 | p.fstn); |
||
540 | tag = Q_NEXT_TYPE (p.fstn->hw_next); |
||
541 | p = p.fstn->fstn_next; |
||
542 | break; |
||
543 | case Q_TYPE_ITD: |
||
544 | temp = snprintf26(next, size, |
||
545 | " itd/%p", p.itd); |
||
546 | tag = Q_NEXT_TYPE (p.itd->hw_next); |
||
547 | p = p.itd->itd_next; |
||
548 | break; |
||
549 | case Q_TYPE_SITD: |
||
550 | temp = snprintf26(next, size, |
||
551 | " sitd/%p", p.sitd); |
||
552 | tag = Q_NEXT_TYPE (p.sitd->hw_next); |
||
553 | p = p.sitd->sitd_next; |
||
554 | break; |
||
555 | } |
||
556 | size -= temp; |
||
557 | next += temp; |
||
558 | } while (p.ptr); |
||
559 | |||
560 | temp = snprintf26(next, size, "\n"); |
||
561 | size -= temp; |
||
562 | next += temp; |
||
563 | } |
||
564 | spin_unlock_irqrestore (&ehci->lock, flags); |
||
565 | kfree (seen); |
||
566 | |||
567 | return PAGE_SIZE - size; |
||
568 | } |
||
569 | static CLASS_DEVICE_ATTR (periodic, S_IRUGO, show_periodic, NULL); |
||
570 | |||
571 | #undef DBG_SCHED_LIMIT |
||
572 | |||
573 | static ssize_t |
||
574 | show_registers (struct class_device *class_dev, char *buf) |
||
575 | { |
||
576 | struct usb_bus *bus; |
||
577 | struct usb_hcd *hcd; |
||
578 | struct ehci_hcd *ehci; |
||
579 | unsigned long flags; |
||
580 | unsigned temp, size, i; |
||
581 | char *next, scratch [80]; |
||
582 | static char fmt [] = "%*s\n"; |
||
583 | static char label [] = ""; |
||
584 | |||
585 | bus = to_usb_bus(class_dev); |
||
586 | hcd = bus->hcpriv; |
||
587 | ehci = hcd_to_ehci (hcd); |
||
588 | next = buf; |
||
589 | size = PAGE_SIZE; |
||
590 | |||
591 | spin_lock_irqsave (&ehci->lock, flags); |
||
592 | |||
593 | /* Capability Registers */ |
||
594 | i = readw (&ehci->caps->hci_version); |
||
595 | temp = snprintf26(next, size, |
||
596 | "PCI device %s\nEHCI %x.%02x, hcd state %d (driver " DRIVER_VERSION ")\n", |
||
597 | pci_name(hcd->pdev), |
||
598 | i >> 8, i & 0x0ff, ehci->hcd.state); |
||
599 | size -= temp; |
||
600 | next += temp; |
||
601 | |||
602 | // FIXME interpret both types of params |
||
603 | i = readl (&ehci->caps->hcs_params); |
||
604 | temp = snprintf26(next, size, "structural params 0x%08x\n", i); |
||
605 | size -= temp; |
||
606 | next += temp; |
||
607 | |||
608 | i = readl (&ehci->caps->hcc_params); |
||
609 | temp = snprintf26(next, size, "capability params 0x%08x\n", i); |
||
610 | size -= temp; |
||
611 | next += temp; |
||
612 | |||
613 | /* Operational Registers */ |
||
614 | temp = dbg_status_buf (scratch, sizeof scratch, label, |
||
615 | readl (&ehci->regs->status)); |
||
616 | temp = snprintf26(next, size, fmt, temp, scratch); |
||
617 | size -= temp; |
||
618 | next += temp; |
||
619 | |||
620 | temp = dbg_command_buf (scratch, sizeof scratch, label, |
||
621 | readl (&ehci->regs->command)); |
||
622 | temp = snprintf26(next, size, fmt, temp, scratch); |
||
623 | size -= temp; |
||
624 | next += temp; |
||
625 | |||
626 | temp = dbg_intr_buf (scratch, sizeof scratch, label, |
||
627 | readl (&ehci->regs->intr_enable)); |
||
628 | temp = snprintf26(next, size, fmt, temp, scratch); |
||
629 | size -= temp; |
||
630 | next += temp; |
||
631 | |||
632 | temp = snprintf26(next, size, "uframe %04x\n", |
||
633 | readl (&ehci->regs->frame_index)); |
||
634 | size -= temp; |
||
635 | next += temp; |
||
636 | |||
637 | for (i = 0; i < HCS_N_PORTS (ehci->hcs_params); i++) { |
||
638 | temp = dbg_port_buf (scratch, sizeof scratch, label, i, |
||
639 | readl (&ehci->regs->port_status [i])); |
||
640 | temp = snprintf26(next, size, fmt, temp, scratch); |
||
641 | size -= temp; |
||
642 | next += temp; |
||
643 | } |
||
644 | |||
645 | if (ehci->reclaim) { |
||
646 | temp = snprintf26(next, size, "reclaim qh %p%s\n", |
||
647 | ehci->reclaim, |
||
648 | ehci->reclaim_ready ? " ready" : ""); |
||
649 | size -= temp; |
||
650 | next += temp; |
||
651 | } |
||
652 | |||
653 | #ifdef EHCI_STATS |
||
654 | temp = snprintf26(next, size, |
||
655 | "irq normal %ld err %ld reclaim %ld (lost %ld)\n", |
||
656 | ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim, |
||
657 | ehci->stats.lost_iaa); |
||
658 | size -= temp; |
||
659 | next += temp; |
||
660 | |||
661 | temp = snprintf26(next, size, "complete %ld unlink %ld\n", |
||
662 | ehci->stats.complete, ehci->stats.unlink); |
||
663 | size -= temp; |
||
664 | next += temp; |
||
665 | #endif |
||
666 | |||
667 | spin_unlock_irqrestore (&ehci->lock, flags); |
||
668 | |||
669 | return PAGE_SIZE - size; |
||
670 | } |
||
671 | static CLASS_DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL); |
||
672 | |||
673 | static inline void create_debug_files (struct ehci_hcd *bus) |
||
674 | { |
||
675 | class_device_create_file(&bus->hcd.self.class_dev, &class_device_attr_async); |
||
676 | class_device_create_file(&bus->hcd.self.class_dev, &class_device_attr_periodic); |
||
677 | class_device_create_file(&bus->hcd.self.class_dev, &class_device_attr_registers); |
||
678 | } |
||
679 | |||
680 | static inline void remove_debug_files (struct ehci_hcd *bus) |
||
681 | { |
||
682 | class_device_remove_file(&bus->hcd.self.class_dev, &class_device_attr_async); |
||
683 | class_device_remove_file(&bus->hcd.self.class_dev, &class_device_attr_periodic); |
||
684 | class_device_remove_file(&bus->hcd.self.class_dev, &class_device_attr_registers); |
||
685 | } |
||
686 | |||
687 | #endif /* STUB_DEBUG_FILES */ |
||
688 |