• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  * Copyright (C) 2013 secunet Security Networks AG
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. The name of the author may not be used to endorse or promote products
14  *    derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 //#define USB_DEBUG
30 
31 #include <usb/usb.h>
32 #include "generic_hub.h"
33 #include "xhci_private.h"
34 #include "xhci.h"
35 
36 static int
xhci_rh_hub_status_changed(usbdev_t * const dev)37 xhci_rh_hub_status_changed(usbdev_t *const dev)
38 {
39 	xhci_t *const xhci = XHCI_INST(dev->controller);
40 	const int changed = !!(xhci->opreg->usbsts & USBSTS_PCD);
41 	if (changed)
42 		xhci->opreg->usbsts =
43 			(xhci->opreg->usbsts & USBSTS_PRSRV_MASK) | USBSTS_PCD;
44 	return changed;
45 }
46 
47 static int
xhci_rh_port_status_changed(usbdev_t * const dev,const int port)48 xhci_rh_port_status_changed(usbdev_t *const dev, const int port)
49 {
50 	xhci_t *const xhci = XHCI_INST(dev->controller);
51 	volatile u32 *const portsc = &xhci->opreg->prs[port - 1].portsc;
52 
53 	const int changed = !!(*portsc & (PORTSC_CSC | PORTSC_PRC));
54 	/* always clear all the status change bits */
55 	*portsc = (*portsc & PORTSC_RW_MASK) | 0x00fe0000;
56 	return changed;
57 }
58 
59 static int
xhci_rh_port_connected(usbdev_t * const dev,const int port)60 xhci_rh_port_connected(usbdev_t *const dev, const int port)
61 {
62 	xhci_t *const xhci = XHCI_INST(dev->controller);
63 	volatile u32 *const portsc = &xhci->opreg->prs[port - 1].portsc;
64 
65 	return *portsc & PORTSC_CCS;
66 }
67 
68 static int
xhci_rh_port_in_reset(usbdev_t * const dev,const int port)69 xhci_rh_port_in_reset(usbdev_t *const dev, const int port)
70 {
71 	xhci_t *const xhci = XHCI_INST(dev->controller);
72 	volatile u32 *const portsc = &xhci->opreg->prs[port - 1].portsc;
73 
74 	return !!(*portsc & PORTSC_PR);
75 }
76 
77 static int
xhci_rh_port_enabled(usbdev_t * const dev,const int port)78 xhci_rh_port_enabled(usbdev_t *const dev, const int port)
79 {
80 	xhci_t *const xhci = XHCI_INST(dev->controller);
81 	volatile u32 *const portsc = &xhci->opreg->prs[port - 1].portsc;
82 
83 	return !!(*portsc & PORTSC_PED);
84 }
85 
86 static usb_speed
xhci_rh_port_speed(usbdev_t * const dev,const int port)87 xhci_rh_port_speed(usbdev_t *const dev, const int port)
88 {
89 	xhci_t *const xhci = XHCI_INST(dev->controller);
90 	volatile u32 *const portsc = &xhci->opreg->prs[port - 1].portsc;
91 
92 	if (*portsc & PORTSC_PED) {
93 		return ((*portsc & PORTSC_PORT_SPEED_MASK)
94 				>> PORTSC_PORT_SPEED_START)
95 			- 1;
96 	} else {
97 		return UNKNOWN_SPEED;
98 	}
99 }
100 
101 static int
xhci_rh_reset_port(usbdev_t * const dev,const int port)102 xhci_rh_reset_port(usbdev_t *const dev, const int port)
103 {
104 	xhci_t *const xhci = XHCI_INST(dev->controller);
105 	volatile u32 *const portsc = &xhci->opreg->prs[port - 1].portsc;
106 
107 	/* Trigger port reset. */
108 	*portsc = (*portsc & PORTSC_RW_MASK) | PORTSC_PR;
109 
110 	/* Wait for port_in_reset == 0, up to 150 * 1000us = 150ms */
111 	if (generic_hub_wait_for_port(dev, port, 0, xhci_rh_port_in_reset,
112 				      150, 1000) == 0)
113 		usb_debug("xhci_rh: Reset timed out at port %d\n", port);
114 	else
115 		/* Clear reset status bits, since port is out of reset. */
116 		*portsc = (*portsc & PORTSC_RW_MASK) | PORTSC_PRC | PORTSC_WRC;
117 
118 	return 0;
119 }
120 
121 static int
xhci_rh_enable_port(usbdev_t * const dev,int port)122 xhci_rh_enable_port(usbdev_t *const dev, int port)
123 {
124 	if (CONFIG(LP_USB_XHCI_MTK_QUIRK)) {
125 		xhci_t *const xhci = XHCI_INST(dev->controller);
126 		volatile u32 *const portsc =
127 			&xhci->opreg->prs[port - 1].portsc;
128 
129 		/*
130 		 * Before sending commands to a port, the Port Power in
131 		 * PORTSC register should be enabled on MTK's xHCI.
132 		 */
133 		*portsc = (*portsc & PORTSC_RW_MASK) | PORTSC_PP;
134 	}
135 	return 0;
136 }
137 
138 static const generic_hub_ops_t xhci_rh_ops = {
139 	.hub_status_changed	= xhci_rh_hub_status_changed,
140 	.port_status_changed	= xhci_rh_port_status_changed,
141 	.port_connected		= xhci_rh_port_connected,
142 	.port_in_reset		= xhci_rh_port_in_reset,
143 	.port_enabled		= xhci_rh_port_enabled,
144 	.port_speed		= xhci_rh_port_speed,
145 	.enable_port		= xhci_rh_enable_port,
146 	.disable_port		= NULL,
147 	.start_port_reset	= NULL,
148 	.reset_port		= xhci_rh_reset_port,
149 };
150 
151 void
xhci_rh_init(usbdev_t * dev)152 xhci_rh_init(usbdev_t *dev)
153 {
154 	/* we can set them here because a root hub _really_ shouldn't
155 	   appear elsewhere */
156 	dev->address = 0;
157 	dev->hub = -1;
158 	dev->port = -1;
159 
160 	const int num_ports = /* TODO: maybe we need to read extended caps */
161 		CAP_GET(MAXPORTS, XHCI_INST(dev->controller)->capreg);
162 	generic_hub_init(dev, num_ports, &xhci_rh_ops);
163 
164 	usb_debug("xHCI: root hub init done\n");
165 }
166