mb/google/nissa: Create pujjogatwin variant
[coreboot2.git] / payloads / libpayload / drivers / usb / generic_hub.c
blobdef8d0da830d9be4d18ec8504459649710270eab
1 /*
3 * Copyright (C) 2013 secunet Security Networks AG
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.
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.
29 //#define USB_DEBUG
31 #include <stdlib.h>
32 #include <usb/usb.h>
33 #include "generic_hub.h"
35 void
36 generic_hub_destroy(usbdev_t *const dev)
38 generic_hub_t *const hub = GEN_HUB(dev);
39 if (!hub)
40 return;
42 /* First, detach all devices behind this hub */
43 int port;
44 for (port = 1; port <= hub->num_ports; ++port) {
45 if (hub->ports[port] >= 0) {
46 usb_debug("generic_hub: Detachment at port %d\n", port);
47 usb_detach_device(dev->controller, hub->ports[port]);
48 hub->ports[port] = NO_DEV;
52 /* Disable all ports */
53 if (hub->ops->disable_port) {
54 for (port = 1; port <= hub->num_ports; ++port)
55 hub->ops->disable_port(dev, port);
58 free(hub->ports);
59 free(hub);
62 static int
63 generic_hub_debounce(usbdev_t *const dev, const int port)
65 generic_hub_t *const hub = GEN_HUB(dev);
67 const int step_ms = 1; /* linux uses 25ms, we're busy anyway */
68 const int at_least_ms = 100; /* 100ms as in usb20 spec 9.1.2 */
69 const int timeout_ms = 1500; /* linux uses this value */
71 int total_ms = 0;
72 int stable_ms = 0;
73 while (stable_ms < at_least_ms && total_ms < timeout_ms) {
74 mdelay(step_ms);
76 const int changed = hub->ops->port_status_changed(dev, port);
77 const int connected = hub->ops->port_connected(dev, port);
78 if (changed < 0 || connected < 0)
79 return -1;
81 if (!changed && connected) {
82 stable_ms += step_ms;
83 } else {
84 usb_debug("generic_hub: Unstable connection at %d\n",
85 port);
86 stable_ms = 0;
88 total_ms += step_ms;
90 if (total_ms >= timeout_ms)
91 usb_debug("generic_hub: Debouncing timed out at %d\n", port);
92 return 0; /* ignore timeouts, try to always go on */
95 int
96 generic_hub_wait_for_port(usbdev_t *const dev, const int port,
97 const int wait_for,
98 int (*const port_op)(usbdev_t *, int),
99 int timeout_steps, const int step_us)
101 int state;
102 do {
103 state = port_op(dev, port);
104 if (state < 0)
105 return -1;
106 else if (!!state == wait_for)
107 return timeout_steps;
108 udelay(step_us);
109 --timeout_steps;
110 } while (timeout_steps);
111 return 0;
115 generic_hub_resetport(usbdev_t *const dev, const int port)
117 generic_hub_t *const hub = GEN_HUB(dev);
119 if (hub->ops->start_port_reset(dev, port) < 0)
120 return -1;
122 /* wait for 10ms (usb20 spec 11.5.1.5: reset should take 10 to 20ms) */
123 mdelay(10);
125 /* now wait 12ms for the hub to finish the reset */
126 const int ret = generic_hub_wait_for_port(
127 /* time out after 120 * 100us = 12ms */
128 dev, port, 0, hub->ops->port_in_reset, 120, 100);
129 if (ret < 0)
130 return -1;
131 else if (!ret)
132 usb_debug("generic_hub: Reset timed out at port %d\n", port);
134 return 0; /* ignore timeouts, try to always go on */
137 static int
138 generic_hub_detach_dev(usbdev_t *const dev, const int port)
140 generic_hub_t *const hub = GEN_HUB(dev);
142 usb_detach_device(dev->controller, hub->ports[port]);
143 hub->ports[port] = NO_DEV;
145 return 0;
148 static int
149 generic_hub_attach_dev(usbdev_t *const dev, const int port)
151 generic_hub_t *const hub = GEN_HUB(dev);
153 if (generic_hub_debounce(dev, port) < 0)
154 return -1;
156 if (hub->ops->reset_port) {
157 if (hub->ops->reset_port(dev, port) < 0)
158 return -1;
160 if (!hub->ops->port_connected(dev, port)) {
161 usb_debug(
162 "generic_hub: Port %d disconnected after "
163 "reset. Possibly upgraded, rescan required.\n",
164 port);
165 return 0;
168 /* after reset the port will be enabled automatically */
169 const int ret = generic_hub_wait_for_port(
170 /* time out after 1,000 * 10us = 10ms */
171 dev, port, 1, hub->ops->port_enabled, 1000, 10);
172 if (ret < 0)
173 return -1;
174 else if (!ret)
175 usb_debug("generic_hub: Port %d still "
176 "disabled after 10ms\n", port);
179 const usb_speed speed = hub->ops->port_speed(dev, port);
180 if (speed >= 0) {
181 usb_debug("generic_hub: Success at port %d\n", port);
182 if (hub->ops->reset_port)
183 mdelay(10); /* Reset recovery time
184 (usb20 spec 7.1.7.5) */
185 hub->ports[port] = usb_attach_device(
186 dev->controller, dev->address, port, speed);
188 return 0;
192 generic_hub_scanport(usbdev_t *const dev, const int port)
194 generic_hub_t *const hub = GEN_HUB(dev);
196 if (hub->ports[port] >= 0) {
197 usb_debug("generic_hub: Detachment at port %d\n", port);
199 const int ret = generic_hub_detach_dev(dev, port);
200 if (ret < 0)
201 return ret;
204 if (hub->ops->port_connected(dev, port)) {
205 usb_debug("generic_hub: Attachment at port %d\n", port);
207 return generic_hub_attach_dev(dev, port);
210 return 0;
213 static void
214 generic_hub_poll(usbdev_t *const dev)
216 generic_hub_t *const hub = GEN_HUB(dev);
217 if (!hub)
218 return;
220 if (!(dev->quirks & USB_QUIRK_HUB_NO_USBSTS_PCD) &&
221 hub->ops->hub_status_changed &&
222 hub->ops->hub_status_changed(dev) != 1) {
223 return;
226 int port;
227 for (port = 1; port <= hub->num_ports; ++port) {
228 const int ret = hub->ops->port_status_changed(dev, port);
229 if (ret < 0) {
230 return;
231 } else if (ret == 1) {
232 usb_debug("generic_hub: Port change at %d\n", port);
233 if (generic_hub_scanport(dev, port) < 0)
234 return;
240 generic_hub_init(usbdev_t *const dev, const int num_ports,
241 const generic_hub_ops_t *const ops)
243 int port;
245 dev->destroy = generic_hub_destroy;
246 dev->poll = generic_hub_poll;
247 dev->data = malloc(sizeof(generic_hub_t));
248 if (!dev->data) {
249 usb_debug("generic_hub: ERROR: Out of memory\n");
250 return -1;
253 generic_hub_t *const hub = GEN_HUB(dev);
254 hub->num_ports = num_ports;
255 hub->ports = malloc(sizeof(*hub->ports) * (num_ports + 1));
256 hub->ops = ops;
257 if (!hub->ports) {
258 usb_debug("generic_hub: ERROR: Out of memory\n");
259 free(dev->data);
260 dev->data = NULL;
261 return -1;
263 for (port = 1; port <= num_ports; ++port)
264 hub->ports[port] = NO_DEV;
266 /* Enable all ports */
267 if (ops->enable_port) {
268 for (port = 1; port <= num_ports; ++port)
269 ops->enable_port(dev, port);
270 /* wait once for all ports */
271 mdelay(20);
274 return 0;