xref: /openbmc/linux/drivers/usb/host/uhci-debug.c (revision b2441318)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * UHCI-specific debugging code. Invaluable when something
41da177e4SLinus Torvalds  * goes wrong, but don't get in my face.
51da177e4SLinus Torvalds  *
6687f5f34SAlan Stern  * Kernel visible pointers are surrounded in []s and bus
7687f5f34SAlan Stern  * visible pointers are surrounded in ()s
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * (C) Copyright 1999 Linus Torvalds
101da177e4SLinus Torvalds  * (C) Copyright 1999-2001 Johannes Erdfelt
111da177e4SLinus Torvalds  */
121da177e4SLinus Torvalds 
135a0e3ad6STejun Heo #include <linux/slab.h>
141da177e4SLinus Torvalds #include <linux/kernel.h>
151da177e4SLinus Torvalds #include <linux/debugfs.h>
161da177e4SLinus Torvalds #include <asm/io.h>
171da177e4SLinus Torvalds 
181da177e4SLinus Torvalds #include "uhci-hcd.h"
191da177e4SLinus Torvalds 
2013996ca7SChen Gang #define EXTRA_SPACE	1024
2113996ca7SChen Gang 
228d402e1aSAlan Stern static struct dentry *uhci_debugfs_root;
238d402e1aSAlan Stern 
241c20163dSOliver Neukum #ifdef CONFIG_DYNAMIC_DEBUG
251da177e4SLinus Torvalds 
26687f5f34SAlan Stern /* Handle REALLY large printks so we don't overflow buffers */
lprintk(char * buf)278d402e1aSAlan Stern static void lprintk(char *buf)
281da177e4SLinus Torvalds {
291da177e4SLinus Torvalds 	char *p;
301da177e4SLinus Torvalds 
311da177e4SLinus Torvalds 	/* Just write one line at a time */
321da177e4SLinus Torvalds 	while (buf) {
331da177e4SLinus Torvalds 		p = strchr(buf, '\n');
341da177e4SLinus Torvalds 		if (p)
351da177e4SLinus Torvalds 			*p = 0;
361da177e4SLinus Torvalds 		printk(KERN_DEBUG "%s\n", buf);
371da177e4SLinus Torvalds 		buf = p;
381da177e4SLinus Torvalds 		if (buf)
391da177e4SLinus Torvalds 			buf++;
401da177e4SLinus Torvalds 	}
411da177e4SLinus Torvalds }
421da177e4SLinus Torvalds 
uhci_show_td(struct uhci_hcd * uhci,struct uhci_td * td,char * buf,int len,int space)4351e2f62fSJan Andersson static int uhci_show_td(struct uhci_hcd *uhci, struct uhci_td *td, char *buf,
4451e2f62fSJan Andersson 			int len, int space)
451da177e4SLinus Torvalds {
461da177e4SLinus Torvalds 	char *out = buf;
471da177e4SLinus Torvalds 	char *spid;
481da177e4SLinus Torvalds 	u32 status, token;
491da177e4SLinus Torvalds 
5051e2f62fSJan Andersson 	status = td_status(uhci, td);
5151e2f62fSJan Andersson 	out += sprintf(out, "%*s[%p] link (%08x) ", space, "", td,
5251e2f62fSJan Andersson 		hc32_to_cpu(uhci, td->link));
531da177e4SLinus Torvalds 	out += sprintf(out, "e%d %s%s%s%s%s%s%s%s%s%sLength=%x ",
541da177e4SLinus Torvalds 		((status >> 27) & 3),
551da177e4SLinus Torvalds 		(status & TD_CTRL_SPD) ?      "SPD " : "",
561da177e4SLinus Torvalds 		(status & TD_CTRL_LS) ?       "LS " : "",
571da177e4SLinus Torvalds 		(status & TD_CTRL_IOC) ?      "IOC " : "",
581da177e4SLinus Torvalds 		(status & TD_CTRL_ACTIVE) ?   "Active " : "",
591da177e4SLinus Torvalds 		(status & TD_CTRL_STALLED) ?  "Stalled " : "",
601da177e4SLinus Torvalds 		(status & TD_CTRL_DBUFERR) ?  "DataBufErr " : "",
611da177e4SLinus Torvalds 		(status & TD_CTRL_BABBLE) ?   "Babble " : "",
621da177e4SLinus Torvalds 		(status & TD_CTRL_NAK) ?      "NAK " : "",
631da177e4SLinus Torvalds 		(status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "",
641da177e4SLinus Torvalds 		(status & TD_CTRL_BITSTUFF) ? "BitStuff " : "",
651da177e4SLinus Torvalds 		status & 0x7ff);
6613996ca7SChen Gang 	if (out - buf > len)
6713996ca7SChen Gang 		goto done;
681da177e4SLinus Torvalds 
6951e2f62fSJan Andersson 	token = td_token(uhci, td);
701da177e4SLinus Torvalds 	switch (uhci_packetid(token)) {
711da177e4SLinus Torvalds 		case USB_PID_SETUP:
721da177e4SLinus Torvalds 			spid = "SETUP";
731da177e4SLinus Torvalds 			break;
741da177e4SLinus Torvalds 		case USB_PID_OUT:
751da177e4SLinus Torvalds 			spid = "OUT";
761da177e4SLinus Torvalds 			break;
771da177e4SLinus Torvalds 		case USB_PID_IN:
781da177e4SLinus Torvalds 			spid = "IN";
791da177e4SLinus Torvalds 			break;
801da177e4SLinus Torvalds 		default:
811da177e4SLinus Torvalds 			spid = "?";
821da177e4SLinus Torvalds 			break;
831da177e4SLinus Torvalds 	}
841da177e4SLinus Torvalds 
851da177e4SLinus Torvalds 	out += sprintf(out, "MaxLen=%x DT%d EndPt=%x Dev=%x, PID=%x(%s) ",
861da177e4SLinus Torvalds 		token >> 21,
871da177e4SLinus Torvalds 		((token >> 19) & 1),
881da177e4SLinus Torvalds 		(token >> 15) & 15,
891da177e4SLinus Torvalds 		(token >> 8) & 127,
901da177e4SLinus Torvalds 		(token & 0xff),
911da177e4SLinus Torvalds 		spid);
9251e2f62fSJan Andersson 	out += sprintf(out, "(buf=%08x)\n", hc32_to_cpu(uhci, td->buffer));
931da177e4SLinus Torvalds 
9413996ca7SChen Gang done:
9513996ca7SChen Gang 	if (out - buf > len)
9613996ca7SChen Gang 		out += sprintf(out, " ...\n");
971da177e4SLinus Torvalds 	return out - buf;
981da177e4SLinus Torvalds }
991da177e4SLinus Torvalds 
uhci_show_urbp(struct uhci_hcd * uhci,struct urb_priv * urbp,char * buf,int len,int space)10051e2f62fSJan Andersson static int uhci_show_urbp(struct uhci_hcd *uhci, struct urb_priv *urbp,
10151e2f62fSJan Andersson 			char *buf, int len, int space)
102dccf4a48SAlan Stern {
103dccf4a48SAlan Stern 	char *out = buf;
104dccf4a48SAlan Stern 	struct uhci_td *td;
105dccf4a48SAlan Stern 	int i, nactive, ninactive;
1064de7d2c2SAlan Stern 	char *ptype;
107dccf4a48SAlan Stern 
108dccf4a48SAlan Stern 
109dccf4a48SAlan Stern 	out += sprintf(out, "urb_priv [%p] ", urbp);
110dccf4a48SAlan Stern 	out += sprintf(out, "urb [%p] ", urbp->urb);
111dccf4a48SAlan Stern 	out += sprintf(out, "qh [%p] ", urbp->qh);
112dccf4a48SAlan Stern 	out += sprintf(out, "Dev=%d ", usb_pipedevice(urbp->urb->pipe));
113dccf4a48SAlan Stern 	out += sprintf(out, "EP=%x(%s) ", usb_pipeendpoint(urbp->urb->pipe),
114dccf4a48SAlan Stern 			(usb_pipein(urbp->urb->pipe) ? "IN" : "OUT"));
11513996ca7SChen Gang 	if (out - buf > len)
11613996ca7SChen Gang 		goto done;
117dccf4a48SAlan Stern 
118dccf4a48SAlan Stern 	switch (usb_pipetype(urbp->urb->pipe)) {
1194de7d2c2SAlan Stern 	case PIPE_ISOCHRONOUS: ptype = "ISO"; break;
1204de7d2c2SAlan Stern 	case PIPE_INTERRUPT: ptype = "INT"; break;
1214de7d2c2SAlan Stern 	case PIPE_BULK: ptype = "BLK"; break;
1224de7d2c2SAlan Stern 	default:
1234de7d2c2SAlan Stern 	case PIPE_CONTROL: ptype = "CTL"; break;
124dccf4a48SAlan Stern 	}
125dccf4a48SAlan Stern 
1264de7d2c2SAlan Stern 	out += sprintf(out, "%s%s", ptype, (urbp->fsbr ? " FSBR" : ""));
1277ea0a2bcSAlan Stern 	out += sprintf(out, " Actlen=%d%s", urbp->urb->actual_length,
1287ea0a2bcSAlan Stern 			(urbp->qh->type == USB_ENDPOINT_XFER_CONTROL ?
1297ea0a2bcSAlan Stern 				"-8" : ""));
130dccf4a48SAlan Stern 
131eb231054SAlan Stern 	if (urbp->urb->unlinked)
132eb231054SAlan Stern 		out += sprintf(out, " Unlinked=%d", urbp->urb->unlinked);
133dccf4a48SAlan Stern 	out += sprintf(out, "\n");
134dccf4a48SAlan Stern 
13513996ca7SChen Gang 	if (out - buf > len)
13613996ca7SChen Gang 		goto done;
13713996ca7SChen Gang 
138dccf4a48SAlan Stern 	i = nactive = ninactive = 0;
139dccf4a48SAlan Stern 	list_for_each_entry(td, &urbp->td_list, list) {
140c8155cc5SAlan Stern 		if (urbp->qh->type != USB_ENDPOINT_XFER_ISOC &&
141c8155cc5SAlan Stern 				(++i <= 10 || debug > 2)) {
142dccf4a48SAlan Stern 			out += sprintf(out, "%*s%d: ", space + 2, "", i);
14351e2f62fSJan Andersson 			out += uhci_show_td(uhci, td, out,
14451e2f62fSJan Andersson 					len - (out - buf), 0);
14513996ca7SChen Gang 			if (out - buf > len)
14613996ca7SChen Gang 				goto tail;
147dccf4a48SAlan Stern 		} else {
14851e2f62fSJan Andersson 			if (td_status(uhci, td) & TD_CTRL_ACTIVE)
149dccf4a48SAlan Stern 				++nactive;
150dccf4a48SAlan Stern 			else
151dccf4a48SAlan Stern 				++ninactive;
152dccf4a48SAlan Stern 		}
153dccf4a48SAlan Stern 	}
154dccf4a48SAlan Stern 	if (nactive + ninactive > 0)
1553171fcabSChen Gang 		out += sprintf(out,
1563171fcabSChen Gang 				"%*s[skipped %d inactive and %d active TDs]\n",
157dccf4a48SAlan Stern 				space, "", ninactive, nactive);
15813996ca7SChen Gang done:
15913996ca7SChen Gang 	if (out - buf > len)
16013996ca7SChen Gang 		out += sprintf(out, " ...\n");
16113996ca7SChen Gang tail:
162dccf4a48SAlan Stern 	return out - buf;
163dccf4a48SAlan Stern }
164dccf4a48SAlan Stern 
uhci_show_qh(struct uhci_hcd * uhci,struct uhci_qh * qh,char * buf,int len,int space)165e009f1b2SAlan Stern static int uhci_show_qh(struct uhci_hcd *uhci,
166e009f1b2SAlan Stern 		struct uhci_qh *qh, char *buf, int len, int space)
1671da177e4SLinus Torvalds {
1681da177e4SLinus Torvalds 	char *out = buf;
169dccf4a48SAlan Stern 	int i, nurbs;
17051e2f62fSJan Andersson 	__hc32 element = qh_element(qh);
1714de7d2c2SAlan Stern 	char *qtype;
1721da177e4SLinus Torvalds 
1734de7d2c2SAlan Stern 	switch (qh->type) {
1744de7d2c2SAlan Stern 	case USB_ENDPOINT_XFER_ISOC: qtype = "ISO"; break;
1754de7d2c2SAlan Stern 	case USB_ENDPOINT_XFER_INT: qtype = "INT"; break;
1764de7d2c2SAlan Stern 	case USB_ENDPOINT_XFER_BULK: qtype = "BLK"; break;
1774de7d2c2SAlan Stern 	case USB_ENDPOINT_XFER_CONTROL: qtype = "CTL"; break;
1784de7d2c2SAlan Stern 	default: qtype = "Skel" ; break;
1794de7d2c2SAlan Stern 	}
1804de7d2c2SAlan Stern 
1814de7d2c2SAlan Stern 	out += sprintf(out, "%*s[%p] %s QH link (%08x) element (%08x)\n",
1824de7d2c2SAlan Stern 			space, "", qh, qtype,
18351e2f62fSJan Andersson 			hc32_to_cpu(uhci, qh->link),
18451e2f62fSJan Andersson 			hc32_to_cpu(uhci, element));
185caf3827aSAlan Stern 	if (qh->type == USB_ENDPOINT_XFER_ISOC)
1863171fcabSChen Gang 		out += sprintf(out,
1873171fcabSChen Gang 				"%*s    period %d phase %d load %d us, frame %x desc [%p]\n",
1883ca2a321SAlan Stern 				space, "", qh->period, qh->phase, qh->load,
1893ca2a321SAlan Stern 				qh->iso_frame, qh->iso_packet_desc);
1903ca2a321SAlan Stern 	else if (qh->type == USB_ENDPOINT_XFER_INT)
1913ca2a321SAlan Stern 		out += sprintf(out, "%*s    period %d phase %d load %d us\n",
1923ca2a321SAlan Stern 				space, "", qh->period, qh->phase, qh->load);
19313996ca7SChen Gang 	if (out - buf > len)
19413996ca7SChen Gang 		goto done;
1951da177e4SLinus Torvalds 
19651e2f62fSJan Andersson 	if (element & UHCI_PTR_QH(uhci))
1971da177e4SLinus Torvalds 		out += sprintf(out, "%*s  Element points to QH (bug?)\n", space, "");
1981da177e4SLinus Torvalds 
19951e2f62fSJan Andersson 	if (element & UHCI_PTR_DEPTH(uhci))
2001da177e4SLinus Torvalds 		out += sprintf(out, "%*s  Depth traverse\n", space, "");
2011da177e4SLinus Torvalds 
20251e2f62fSJan Andersson 	if (element & cpu_to_hc32(uhci, 8))
2031da177e4SLinus Torvalds 		out += sprintf(out, "%*s  Bit 3 set (bug?)\n", space, "");
2041da177e4SLinus Torvalds 
20551e2f62fSJan Andersson 	if (!(element & ~(UHCI_PTR_QH(uhci) | UHCI_PTR_DEPTH(uhci))))
2061da177e4SLinus Torvalds 		out += sprintf(out, "%*s  Element is NULL (bug?)\n", space, "");
2071da177e4SLinus Torvalds 
20813996ca7SChen Gang 	if (out - buf > len)
20913996ca7SChen Gang 		goto done;
21013996ca7SChen Gang 
211dccf4a48SAlan Stern 	if (list_empty(&qh->queue)) {
212dccf4a48SAlan Stern 		out += sprintf(out, "%*s  queue is empty\n", space, "");
21313996ca7SChen Gang 		if (qh == uhci->skel_async_qh) {
21451e2f62fSJan Andersson 			out += uhci_show_td(uhci, uhci->term_td, out,
215e009f1b2SAlan Stern 					len - (out - buf), 0);
21613996ca7SChen Gang 			if (out - buf > len)
21713996ca7SChen Gang 				goto tail;
21813996ca7SChen Gang 		}
219dccf4a48SAlan Stern 	} else {
220dccf4a48SAlan Stern 		struct urb_priv *urbp = list_entry(qh->queue.next,
221dccf4a48SAlan Stern 				struct urb_priv, node);
222dccf4a48SAlan Stern 		struct uhci_td *td = list_entry(urbp->td_list.next,
223dccf4a48SAlan Stern 				struct uhci_td, list);
2241da177e4SLinus Torvalds 
22551e2f62fSJan Andersson 		if (element != LINK_TO_TD(uhci, td))
226dccf4a48SAlan Stern 			out += sprintf(out, "%*s Element != First TD\n",
227dccf4a48SAlan Stern 					space, "");
228dccf4a48SAlan Stern 		i = nurbs = 0;
229dccf4a48SAlan Stern 		list_for_each_entry(urbp, &qh->queue, node) {
23013996ca7SChen Gang 			if (++i <= 10) {
23151e2f62fSJan Andersson 				out += uhci_show_urbp(uhci, urbp, out,
232dccf4a48SAlan Stern 						len - (out - buf), space + 2);
23313996ca7SChen Gang 				if (out - buf > len)
23413996ca7SChen Gang 					goto tail;
23513996ca7SChen Gang 			}
236dccf4a48SAlan Stern 			else
237dccf4a48SAlan Stern 				++nurbs;
238dccf4a48SAlan Stern 		}
239dccf4a48SAlan Stern 		if (nurbs > 0)
240dccf4a48SAlan Stern 			out += sprintf(out, "%*s Skipped %d URBs\n",
241dccf4a48SAlan Stern 					space, "", nurbs);
2421da177e4SLinus Torvalds 	}
2431da177e4SLinus Torvalds 
24413996ca7SChen Gang 	if (out - buf > len)
24513996ca7SChen Gang 		goto done;
24613996ca7SChen Gang 
24785a975d0SAlan Stern 	if (qh->dummy_td) {
248af0bb599SAlan Stern 		out += sprintf(out, "%*s  Dummy TD\n", space, "");
24951e2f62fSJan Andersson 		out += uhci_show_td(uhci, qh->dummy_td, out,
25051e2f62fSJan Andersson 				len - (out - buf), 0);
25113996ca7SChen Gang 		if (out - buf > len)
25213996ca7SChen Gang 			goto tail;
253af0bb599SAlan Stern 	}
254af0bb599SAlan Stern 
25513996ca7SChen Gang done:
25613996ca7SChen Gang 	if (out - buf > len)
25713996ca7SChen Gang 		out += sprintf(out, " ...\n");
25813996ca7SChen Gang tail:
2591da177e4SLinus Torvalds 	return out - buf;
2601da177e4SLinus Torvalds }
2611da177e4SLinus Torvalds 
uhci_show_sc(int port,unsigned short status,char * buf)26213996ca7SChen Gang static int uhci_show_sc(int port, unsigned short status, char *buf)
2631da177e4SLinus Torvalds {
26413996ca7SChen Gang 	return sprintf(buf, "  stat%d     =     %04x  %s%s%s%s%s%s%s%s%s%s\n",
2651da177e4SLinus Torvalds 		port,
2661da177e4SLinus Torvalds 		status,
2671da177e4SLinus Torvalds 		(status & USBPORTSC_SUSP) ?	" Suspend" : "",
2681da177e4SLinus Torvalds 		(status & USBPORTSC_OCC) ?	" OverCurrentChange" : "",
2691da177e4SLinus Torvalds 		(status & USBPORTSC_OC) ?	" OverCurrent" : "",
2701da177e4SLinus Torvalds 		(status & USBPORTSC_PR) ?	" Reset" : "",
2711da177e4SLinus Torvalds 		(status & USBPORTSC_LSDA) ?	" LowSpeed" : "",
2721da177e4SLinus Torvalds 		(status & USBPORTSC_RD) ?	" ResumeDetect" : "",
2731da177e4SLinus Torvalds 		(status & USBPORTSC_PEC) ?	" EnableChange" : "",
2741da177e4SLinus Torvalds 		(status & USBPORTSC_PE) ?	" Enabled" : "",
2751da177e4SLinus Torvalds 		(status & USBPORTSC_CSC) ?	" ConnectChange" : "",
2761da177e4SLinus Torvalds 		(status & USBPORTSC_CCS) ?	" Connected" : "");
2771da177e4SLinus Torvalds }
2781da177e4SLinus Torvalds 
uhci_show_root_hub_state(struct uhci_hcd * uhci,char * buf)27913996ca7SChen Gang static int uhci_show_root_hub_state(struct uhci_hcd *uhci, char *buf)
280c8f4fe43SAlan Stern {
281c8f4fe43SAlan Stern 	char *rh_state;
282c8f4fe43SAlan Stern 
283c8f4fe43SAlan Stern 	switch (uhci->rh_state) {
284c8f4fe43SAlan Stern 	    case UHCI_RH_RESET:
285c8f4fe43SAlan Stern 		rh_state = "reset";		break;
286c8f4fe43SAlan Stern 	    case UHCI_RH_SUSPENDED:
287c8f4fe43SAlan Stern 		rh_state = "suspended";		break;
288c8f4fe43SAlan Stern 	    case UHCI_RH_AUTO_STOPPED:
289c8f4fe43SAlan Stern 		rh_state = "auto-stopped";	break;
290c8f4fe43SAlan Stern 	    case UHCI_RH_RESUMING:
291c8f4fe43SAlan Stern 		rh_state = "resuming";		break;
292c8f4fe43SAlan Stern 	    case UHCI_RH_SUSPENDING:
293c8f4fe43SAlan Stern 		rh_state = "suspending";	break;
294c8f4fe43SAlan Stern 	    case UHCI_RH_RUNNING:
295c8f4fe43SAlan Stern 		rh_state = "running";		break;
296c8f4fe43SAlan Stern 	    case UHCI_RH_RUNNING_NODEVS:
297c8f4fe43SAlan Stern 		rh_state = "running, no devs";	break;
298c8f4fe43SAlan Stern 	    default:
299c8f4fe43SAlan Stern 		rh_state = "?";			break;
300c8f4fe43SAlan Stern 	}
30113996ca7SChen Gang 	return sprintf(buf, "Root-hub state: %s   FSBR: %d\n",
30284afddd7SAlan Stern 			rh_state, uhci->fsbr_is_on);
303c8f4fe43SAlan Stern }
304c8f4fe43SAlan Stern 
uhci_show_status(struct uhci_hcd * uhci,char * buf,int len)3051da177e4SLinus Torvalds static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len)
3061da177e4SLinus Torvalds {
3071da177e4SLinus Torvalds 	char *out = buf;
3081da177e4SLinus Torvalds 	unsigned short usbcmd, usbstat, usbint, usbfrnum;
3091da177e4SLinus Torvalds 	unsigned int flbaseadd;
3101da177e4SLinus Torvalds 	unsigned char sof;
3111da177e4SLinus Torvalds 	unsigned short portsc1, portsc2;
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds 
314f1125f81SDeng-Cheng Zhu 	usbcmd    = uhci_readw(uhci, USBCMD);
315f1125f81SDeng-Cheng Zhu 	usbstat   = uhci_readw(uhci, USBSTS);
316f1125f81SDeng-Cheng Zhu 	usbint    = uhci_readw(uhci, USBINTR);
317f1125f81SDeng-Cheng Zhu 	usbfrnum  = uhci_readw(uhci, USBFRNUM);
318f1125f81SDeng-Cheng Zhu 	flbaseadd = uhci_readl(uhci, USBFLBASEADD);
319f1125f81SDeng-Cheng Zhu 	sof       = uhci_readb(uhci, USBSOF);
320f1125f81SDeng-Cheng Zhu 	portsc1   = uhci_readw(uhci, USBPORTSC1);
321f1125f81SDeng-Cheng Zhu 	portsc2   = uhci_readw(uhci, USBPORTSC2);
3221da177e4SLinus Torvalds 
3231da177e4SLinus Torvalds 	out += sprintf(out, "  usbcmd    =     %04x   %s%s%s%s%s%s%s%s\n",
3241da177e4SLinus Torvalds 		usbcmd,
3251da177e4SLinus Torvalds 		(usbcmd & USBCMD_MAXP) ?    "Maxp64 " : "Maxp32 ",
3261da177e4SLinus Torvalds 		(usbcmd & USBCMD_CF) ?      "CF " : "",
3271da177e4SLinus Torvalds 		(usbcmd & USBCMD_SWDBG) ?   "SWDBG " : "",
3281da177e4SLinus Torvalds 		(usbcmd & USBCMD_FGR) ?     "FGR " : "",
3291da177e4SLinus Torvalds 		(usbcmd & USBCMD_EGSM) ?    "EGSM " : "",
3301da177e4SLinus Torvalds 		(usbcmd & USBCMD_GRESET) ?  "GRESET " : "",
3311da177e4SLinus Torvalds 		(usbcmd & USBCMD_HCRESET) ? "HCRESET " : "",
3321da177e4SLinus Torvalds 		(usbcmd & USBCMD_RS) ?      "RS " : "");
33313996ca7SChen Gang 	if (out - buf > len)
33413996ca7SChen Gang 		goto done;
3351da177e4SLinus Torvalds 
3361da177e4SLinus Torvalds 	out += sprintf(out, "  usbstat   =     %04x   %s%s%s%s%s%s\n",
3371da177e4SLinus Torvalds 		usbstat,
3381da177e4SLinus Torvalds 		(usbstat & USBSTS_HCH) ?    "HCHalted " : "",
3391da177e4SLinus Torvalds 		(usbstat & USBSTS_HCPE) ?   "HostControllerProcessError " : "",
3401da177e4SLinus Torvalds 		(usbstat & USBSTS_HSE) ?    "HostSystemError " : "",
3411da177e4SLinus Torvalds 		(usbstat & USBSTS_RD) ?     "ResumeDetect " : "",
3421da177e4SLinus Torvalds 		(usbstat & USBSTS_ERROR) ?  "USBError " : "",
3431da177e4SLinus Torvalds 		(usbstat & USBSTS_USBINT) ? "USBINT " : "");
34413996ca7SChen Gang 	if (out - buf > len)
34513996ca7SChen Gang 		goto done;
3461da177e4SLinus Torvalds 
3471da177e4SLinus Torvalds 	out += sprintf(out, "  usbint    =     %04x\n", usbint);
3481da177e4SLinus Torvalds 	out += sprintf(out, "  usbfrnum  =   (%d)%03x\n", (usbfrnum >> 10) & 1,
3491da177e4SLinus Torvalds 		0xfff & (4*(unsigned int)usbfrnum));
3501da177e4SLinus Torvalds 	out += sprintf(out, "  flbaseadd = %08x\n", flbaseadd);
3511da177e4SLinus Torvalds 	out += sprintf(out, "  sof       =       %02x\n", sof);
35213996ca7SChen Gang 	if (out - buf > len)
35313996ca7SChen Gang 		goto done;
35413996ca7SChen Gang 
35513996ca7SChen Gang 	out += uhci_show_sc(1, portsc1, out);
35613996ca7SChen Gang 	if (out - buf > len)
35713996ca7SChen Gang 		goto done;
35813996ca7SChen Gang 
35913996ca7SChen Gang 	out += uhci_show_sc(2, portsc2, out);
36013996ca7SChen Gang 	if (out - buf > len)
36113996ca7SChen Gang 		goto done;
36213996ca7SChen Gang 
36313996ca7SChen Gang 	out += sprintf(out,
36413996ca7SChen Gang 			"Most recent frame: %x (%d)   Last ISO frame: %x (%d)\n",
365c8155cc5SAlan Stern 			uhci->frame_number, uhci->frame_number & 1023,
366c8155cc5SAlan Stern 			uhci->last_iso_frame, uhci->last_iso_frame & 1023);
3671da177e4SLinus Torvalds 
36813996ca7SChen Gang done:
36913996ca7SChen Gang 	if (out - buf > len)
37013996ca7SChen Gang 		out += sprintf(out, " ...\n");
3711da177e4SLinus Torvalds 	return out - buf;
3721da177e4SLinus Torvalds }
3731da177e4SLinus Torvalds 
uhci_sprint_schedule(struct uhci_hcd * uhci,char * buf,int len)3741da177e4SLinus Torvalds static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len)
3751da177e4SLinus Torvalds {
3761da177e4SLinus Torvalds 	char *out = buf;
3771da177e4SLinus Torvalds 	int i, j;
3781da177e4SLinus Torvalds 	struct uhci_qh *qh;
3791da177e4SLinus Torvalds 	struct uhci_td *td;
3801da177e4SLinus Torvalds 	struct list_head *tmp, *head;
381f3fe239bSAlan Stern 	int nframes, nerrs;
38251e2f62fSJan Andersson 	__hc32 link;
38351e2f62fSJan Andersson 	__hc32 fsbr_link;
38417230acdSAlan Stern 
38517230acdSAlan Stern 	static const char * const qh_names[] = {
38617230acdSAlan Stern 		"unlink", "iso", "int128", "int64", "int32", "int16",
38717230acdSAlan Stern 		"int8", "int4", "int2", "async", "term"
38817230acdSAlan Stern 	};
3891da177e4SLinus Torvalds 
39013996ca7SChen Gang 	out += uhci_show_root_hub_state(uhci, out);
39113996ca7SChen Gang 	if (out - buf > len)
39213996ca7SChen Gang 		goto done;
3931da177e4SLinus Torvalds 	out += sprintf(out, "HC status\n");
3941da177e4SLinus Torvalds 	out += uhci_show_status(uhci, out, len - (out - buf));
39513996ca7SChen Gang 	if (out - buf > len)
39613996ca7SChen Gang 		goto tail;
3973ca2a321SAlan Stern 
3983ca2a321SAlan Stern 	out += sprintf(out, "Periodic load table\n");
3993ca2a321SAlan Stern 	for (i = 0; i < MAX_PHASE; ++i) {
4003ca2a321SAlan Stern 		out += sprintf(out, "\t%d", uhci->load[i]);
4013ca2a321SAlan Stern 		if (i % 8 == 7)
4023ca2a321SAlan Stern 			*out++ = '\n';
4033ca2a321SAlan Stern 	}
4043ca2a321SAlan Stern 	out += sprintf(out, "Total: %d, #INT: %d, #ISO: %d\n",
4053ca2a321SAlan Stern 			uhci->total_load,
4063ca2a321SAlan Stern 			uhci_to_hcd(uhci)->self.bandwidth_int_reqs,
4073ca2a321SAlan Stern 			uhci_to_hcd(uhci)->self.bandwidth_isoc_reqs);
408dccf4a48SAlan Stern 	if (debug <= 1)
40913996ca7SChen Gang 		goto tail;
4101da177e4SLinus Torvalds 
4111da177e4SLinus Torvalds 	out += sprintf(out, "Frame List\n");
412f3fe239bSAlan Stern 	nframes = 10;
413f3fe239bSAlan Stern 	nerrs = 0;
4141da177e4SLinus Torvalds 	for (i = 0; i < UHCI_NUMFRAMES; ++i) {
41551e2f62fSJan Andersson 		__hc32 qh_dma;
4161da177e4SLinus Torvalds 
41713996ca7SChen Gang 		if (out - buf > len)
41813996ca7SChen Gang 			goto done;
419f3fe239bSAlan Stern 		j = 0;
420f3fe239bSAlan Stern 		td = uhci->frame_cpu[i];
421f3fe239bSAlan Stern 		link = uhci->frame[i];
422f3fe239bSAlan Stern 		if (!td)
423f3fe239bSAlan Stern 			goto check_link;
424f3fe239bSAlan Stern 
425f3fe239bSAlan Stern 		if (nframes > 0) {
426f3fe239bSAlan Stern 			out += sprintf(out, "- Frame %d -> (%08x)\n",
42751e2f62fSJan Andersson 					i, hc32_to_cpu(uhci, link));
428f3fe239bSAlan Stern 			j = 1;
429f3fe239bSAlan Stern 		}
4301da177e4SLinus Torvalds 
4311da177e4SLinus Torvalds 		head = &td->fl_list;
4321da177e4SLinus Torvalds 		tmp = head;
4331da177e4SLinus Torvalds 		do {
4341da177e4SLinus Torvalds 			td = list_entry(tmp, struct uhci_td, fl_list);
4351da177e4SLinus Torvalds 			tmp = tmp->next;
43651e2f62fSJan Andersson 			if (link != LINK_TO_TD(uhci, td)) {
43713996ca7SChen Gang 				if (nframes > 0) {
4383171fcabSChen Gang 					out += sprintf(out,
4393171fcabSChen Gang 						"    link does not match list entry!\n");
44013996ca7SChen Gang 					if (out - buf > len)
44113996ca7SChen Gang 						goto done;
44213996ca7SChen Gang 				} else
443f3fe239bSAlan Stern 					++nerrs;
4441da177e4SLinus Torvalds 			}
44513996ca7SChen Gang 			if (nframes > 0) {
44651e2f62fSJan Andersson 				out += uhci_show_td(uhci, td, out,
447f3fe239bSAlan Stern 						len - (out - buf), 4);
44813996ca7SChen Gang 				if (out - buf > len)
44913996ca7SChen Gang 					goto tail;
45013996ca7SChen Gang 			}
451f3fe239bSAlan Stern 			link = td->link;
452f3fe239bSAlan Stern 		} while (tmp != head);
453f3fe239bSAlan Stern 
454f3fe239bSAlan Stern check_link:
455f3fe239bSAlan Stern 		qh_dma = uhci_frame_skel_link(uhci, i);
456f3fe239bSAlan Stern 		if (link != qh_dma) {
457f3fe239bSAlan Stern 			if (nframes > 0) {
458f3fe239bSAlan Stern 				if (!j) {
459f3fe239bSAlan Stern 					out += sprintf(out,
460f3fe239bSAlan Stern 						"- Frame %d -> (%08x)\n",
46151e2f62fSJan Andersson 						i, hc32_to_cpu(uhci, link));
462f3fe239bSAlan Stern 					j = 1;
463f3fe239bSAlan Stern 				}
4643171fcabSChen Gang 				out += sprintf(out,
4653171fcabSChen Gang 					"   link does not match QH (%08x)!\n",
46651e2f62fSJan Andersson 					hc32_to_cpu(uhci, qh_dma));
46713996ca7SChen Gang 				if (out - buf > len)
46813996ca7SChen Gang 					goto done;
469f3fe239bSAlan Stern 			} else
470f3fe239bSAlan Stern 				++nerrs;
471f3fe239bSAlan Stern 		}
472f3fe239bSAlan Stern 		nframes -= j;
473f3fe239bSAlan Stern 	}
474f3fe239bSAlan Stern 	if (nerrs > 0)
475f3fe239bSAlan Stern 		out += sprintf(out, "Skipped %d bad links\n", nerrs);
4761da177e4SLinus Torvalds 
477687f5f34SAlan Stern 	out += sprintf(out, "Skeleton QHs\n");
4781da177e4SLinus Torvalds 
47913996ca7SChen Gang 	if (out - buf > len)
48013996ca7SChen Gang 		goto done;
48113996ca7SChen Gang 
482e009f1b2SAlan Stern 	fsbr_link = 0;
4831da177e4SLinus Torvalds 	for (i = 0; i < UHCI_NUM_SKELQH; ++i) {
484dccf4a48SAlan Stern 		int cnt = 0;
4851da177e4SLinus Torvalds 
4861da177e4SLinus Torvalds 		qh = uhci->skelqh[i];
4873171fcabSChen Gang 		out += sprintf(out, "- skel_%s_qh\n", qh_names[i]);
488e009f1b2SAlan Stern 		out += uhci_show_qh(uhci, qh, out, len - (out - buf), 4);
48913996ca7SChen Gang 		if (out - buf > len)
49013996ca7SChen Gang 			goto tail;
4911da177e4SLinus Torvalds 
4921da177e4SLinus Torvalds 		/* Last QH is the Terminating QH, it's different */
49317230acdSAlan Stern 		if (i == SKEL_TERM) {
49413996ca7SChen Gang 			if (qh_element(qh) != LINK_TO_TD(uhci, uhci->term_td)) {
4953171fcabSChen Gang 				out += sprintf(out,
4963171fcabSChen Gang 					"    skel_term_qh element is not set to term_td!\n");
49713996ca7SChen Gang 				if (out - buf > len)
49813996ca7SChen Gang 					goto done;
49913996ca7SChen Gang 			}
500e009f1b2SAlan Stern 			link = fsbr_link;
501e009f1b2SAlan Stern 			if (!link)
50251e2f62fSJan Andersson 				link = LINK_TO_QH(uhci, uhci->skel_term_qh);
50317230acdSAlan Stern 			goto check_qh_link;
5041da177e4SLinus Torvalds 		}
5051da177e4SLinus Torvalds 
506dccf4a48SAlan Stern 		head = &qh->node;
5071da177e4SLinus Torvalds 		tmp = head->next;
5081da177e4SLinus Torvalds 
5091da177e4SLinus Torvalds 		while (tmp != head) {
510dccf4a48SAlan Stern 			qh = list_entry(tmp, struct uhci_qh, node);
5111da177e4SLinus Torvalds 			tmp = tmp->next;
51213996ca7SChen Gang 			if (++cnt <= 10) {
513e009f1b2SAlan Stern 				out += uhci_show_qh(uhci, qh, out,
514dccf4a48SAlan Stern 						len - (out - buf), 4);
51513996ca7SChen Gang 				if (out - buf > len)
51613996ca7SChen Gang 					goto tail;
51713996ca7SChen Gang 			}
51817230acdSAlan Stern 			if (!fsbr_link && qh->skel >= SKEL_FSBR)
51951e2f62fSJan Andersson 				fsbr_link = LINK_TO_QH(uhci, qh);
5201da177e4SLinus Torvalds 		}
521dccf4a48SAlan Stern 		if ((cnt -= 10) > 0)
522dccf4a48SAlan Stern 			out += sprintf(out, "    Skipped %d QHs\n", cnt);
5231da177e4SLinus Torvalds 
52451e2f62fSJan Andersson 		link = UHCI_PTR_TERM(uhci);
52517230acdSAlan Stern 		if (i <= SKEL_ISO)
52617230acdSAlan Stern 			;
52717230acdSAlan Stern 		else if (i < SKEL_ASYNC)
52851e2f62fSJan Andersson 			link = LINK_TO_QH(uhci, uhci->skel_async_qh);
52917230acdSAlan Stern 		else if (!uhci->fsbr_is_on)
53017230acdSAlan Stern 			;
53117230acdSAlan Stern 		else
53251e2f62fSJan Andersson 			link = LINK_TO_QH(uhci, uhci->skel_term_qh);
53317230acdSAlan Stern check_qh_link:
53417230acdSAlan Stern 		if (qh->link != link)
5353171fcabSChen Gang 			out += sprintf(out,
5363171fcabSChen Gang 				"    last QH not linked to next skeleton!\n");
53713996ca7SChen Gang 
53813996ca7SChen Gang 		if (out - buf > len)
53913996ca7SChen Gang 			goto done;
5401da177e4SLinus Torvalds 	}
5411da177e4SLinus Torvalds 
54213996ca7SChen Gang done:
54313996ca7SChen Gang 	if (out - buf > len)
54413996ca7SChen Gang 		out += sprintf(out, " ...\n");
54513996ca7SChen Gang tail:
5461da177e4SLinus Torvalds 	return out - buf;
5471da177e4SLinus Torvalds }
5481da177e4SLinus Torvalds 
5498d402e1aSAlan Stern #ifdef CONFIG_DEBUG_FS
5508d402e1aSAlan Stern 
5511da177e4SLinus Torvalds #define MAX_OUTPUT	(64 * 1024)
5521da177e4SLinus Torvalds 
5531da177e4SLinus Torvalds struct uhci_debug {
5541da177e4SLinus Torvalds 	int size;
5551da177e4SLinus Torvalds 	char *data;
5561da177e4SLinus Torvalds };
5571da177e4SLinus Torvalds 
uhci_debug_open(struct inode * inode,struct file * file)5581da177e4SLinus Torvalds static int uhci_debug_open(struct inode *inode, struct file *file)
5591da177e4SLinus Torvalds {
5608e18e294STheodore Ts'o 	struct uhci_hcd *uhci = inode->i_private;
5611da177e4SLinus Torvalds 	struct uhci_debug *up;
562dccf4a48SAlan Stern 	unsigned long flags;
5631da177e4SLinus Torvalds 
5641da177e4SLinus Torvalds 	up = kmalloc(sizeof(*up), GFP_KERNEL);
5651da177e4SLinus Torvalds 	if (!up)
56600b81fb2SAndi Kleen 		return -ENOMEM;
5671da177e4SLinus Torvalds 
5681da177e4SLinus Torvalds 	up->data = kmalloc(MAX_OUTPUT, GFP_KERNEL);
5691da177e4SLinus Torvalds 	if (!up->data) {
5701da177e4SLinus Torvalds 		kfree(up);
57100b81fb2SAndi Kleen 		return -ENOMEM;
5721da177e4SLinus Torvalds 	}
5731da177e4SLinus Torvalds 
5748d402e1aSAlan Stern 	up->size = 0;
575dccf4a48SAlan Stern 	spin_lock_irqsave(&uhci->lock, flags);
5768d402e1aSAlan Stern 	if (uhci->is_initialized)
57713996ca7SChen Gang 		up->size = uhci_sprint_schedule(uhci, up->data,
57813996ca7SChen Gang 					MAX_OUTPUT - EXTRA_SPACE);
579dccf4a48SAlan Stern 	spin_unlock_irqrestore(&uhci->lock, flags);
5801da177e4SLinus Torvalds 
5811da177e4SLinus Torvalds 	file->private_data = up;
5821da177e4SLinus Torvalds 
58300b81fb2SAndi Kleen 	return 0;
5841da177e4SLinus Torvalds }
5851da177e4SLinus Torvalds 
uhci_debug_lseek(struct file * file,loff_t off,int whence)5861da177e4SLinus Torvalds static loff_t uhci_debug_lseek(struct file *file, loff_t off, int whence)
5871da177e4SLinus Torvalds {
588b25472f9SAl Viro 	struct uhci_debug *up = file->private_data;
589b25472f9SAl Viro 	return no_seek_end_llseek_size(file, off, whence, up->size);
5901da177e4SLinus Torvalds }
5911da177e4SLinus Torvalds 
uhci_debug_read(struct file * file,char __user * buf,size_t nbytes,loff_t * ppos)5921da177e4SLinus Torvalds static ssize_t uhci_debug_read(struct file *file, char __user *buf,
5931da177e4SLinus Torvalds 				size_t nbytes, loff_t *ppos)
5941da177e4SLinus Torvalds {
5951da177e4SLinus Torvalds 	struct uhci_debug *up = file->private_data;
5961da177e4SLinus Torvalds 	return simple_read_from_buffer(buf, nbytes, ppos, up->data, up->size);
5971da177e4SLinus Torvalds }
5981da177e4SLinus Torvalds 
uhci_debug_release(struct inode * inode,struct file * file)5991da177e4SLinus Torvalds static int uhci_debug_release(struct inode *inode, struct file *file)
6001da177e4SLinus Torvalds {
6011da177e4SLinus Torvalds 	struct uhci_debug *up = file->private_data;
6021da177e4SLinus Torvalds 
6031da177e4SLinus Torvalds 	kfree(up->data);
6041da177e4SLinus Torvalds 	kfree(up);
6051da177e4SLinus Torvalds 
6061da177e4SLinus Torvalds 	return 0;
6071da177e4SLinus Torvalds }
6081da177e4SLinus Torvalds 
609066202ddSLuiz Fernando N. Capitulino static const struct file_operations uhci_debug_operations = {
6108d402e1aSAlan Stern 	.owner =	THIS_MODULE,
6111da177e4SLinus Torvalds 	.open =		uhci_debug_open,
6121da177e4SLinus Torvalds 	.llseek =	uhci_debug_lseek,
6131da177e4SLinus Torvalds 	.read =		uhci_debug_read,
6141da177e4SLinus Torvalds 	.release =	uhci_debug_release,
6151da177e4SLinus Torvalds };
616b409214cSAlan Stern #define UHCI_DEBUG_OPS
6171da177e4SLinus Torvalds 
6188d402e1aSAlan Stern #endif	/* CONFIG_DEBUG_FS */
6191da177e4SLinus Torvalds 
6201c20163dSOliver Neukum #else	/* CONFIG_DYNAMIC_DEBUG*/
6218d402e1aSAlan Stern 
lprintk(char * buf)6228d402e1aSAlan Stern static inline void lprintk(char *buf)
6238d402e1aSAlan Stern {}
6248d402e1aSAlan Stern 
uhci_show_qh(struct uhci_hcd * uhci,struct uhci_qh * qh,char * buf,int len,int space)625e009f1b2SAlan Stern static inline int uhci_show_qh(struct uhci_hcd *uhci,
626e009f1b2SAlan Stern 		struct uhci_qh *qh, char *buf, int len, int space)
6278d402e1aSAlan Stern {
6288d402e1aSAlan Stern 	return 0;
6298d402e1aSAlan Stern }
6308d402e1aSAlan Stern 
uhci_sprint_schedule(struct uhci_hcd * uhci,char * buf,int len)6318d402e1aSAlan Stern static inline int uhci_sprint_schedule(struct uhci_hcd *uhci,
6328d402e1aSAlan Stern 		char *buf, int len)
6338d402e1aSAlan Stern {
6348d402e1aSAlan Stern 	return 0;
6358d402e1aSAlan Stern }
6361da177e4SLinus Torvalds 
6371da177e4SLinus Torvalds #endif
638