3 * Copyright (C) 2010 Patrick Georgi
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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
31 #include <libpayload.h>
32 #include "ohci_private.h"
40 #define RH_INST(dev) ((rh_inst_t*)(dev)->data)
43 ohci_rh_enable_port(usbdev_t
*dev
, int port
)
45 /* Reset RH port should hold 50ms with pulses of at least 10ms and
46 * gaps of at most 3ms (usb20 spec 7.1.7.5).
47 * After reset, the port will be enabled automatically (ohci spec
50 int total_delay
= 100; /* 100 * 500us == 50ms */
51 while (total_delay
> 0) {
52 if (!(OHCI_INST(dev
->controller
)->opreg
->HcRhPortStatus
[port
]
53 & CurrentConnectStatus
))
57 OHCI_INST(dev
->controller
)->opreg
->HcRhPortStatus
[port
] =
59 int timeout
= 200; /* timeout after 200 * 500us == 100ms */
60 while ((OHCI_INST(dev
->controller
)->opreg
->HcRhPortStatus
[port
]
63 udelay(500); total_delay
--;
65 if (OHCI_INST(dev
->controller
)->opreg
->HcRhPortStatus
[port
]
67 usb_debug("Warning: root-hub port reset timed out.\n");
70 if ((200-timeout
) < 20)
71 usb_debug("Warning: port reset too short: %dms; "
72 "should be at least 10ms.\n",
74 /* clear reset status change */
75 OHCI_INST(dev
->controller
)->opreg
->HcRhPortStatus
[port
] =
76 PortResetStatusChange
;
77 usb_debug("rh port reset finished after %dms.\n", (200-timeout
)/2);
81 /* disable root hub */
83 ohci_rh_disable_port(usbdev_t
*dev
, int port
)
85 if (RH_INST(dev
)->port
[port
] != -1) {
86 usb_detach_device(dev
->controller
, RH_INST(dev
)->port
[port
]);
87 RH_INST(dev
)->port
[port
] = -1;
90 OHCI_INST(dev
->controller
)->opreg
->HcRhPortStatus
[port
] = ClearPortEnable
; // disable port
91 int timeout
= 50; /* timeout after 50 * 100us == 5ms */
92 while ((OHCI_INST(dev
->controller
)->opreg
->HcRhPortStatus
[port
]
100 ohci_rh_scanport(usbdev_t
*dev
, int port
)
102 if (port
>= RH_INST(dev
)->numports
) {
103 usb_debug("Invalid port %d\n", port
);
107 /* device registered, and device change logged, so something must have happened */
108 if (RH_INST(dev
)->port
[port
] != -1) {
109 usb_detach_device(dev
->controller
, RH_INST(dev
)->port
[port
]);
110 RH_INST(dev
)->port
[port
] = -1;
113 /* no device attached
114 previously registered devices are detached, nothing left to do */
115 if (!(OHCI_INST(dev
->controller
)->opreg
->HcRhPortStatus
[port
] & CurrentConnectStatus
))
118 OHCI_INST(dev
->controller
)->opreg
->HcRhPortStatus
[port
] = ConnectStatusChange
; // clear port state change
119 ohci_rh_enable_port(dev
, port
);
121 mdelay(100); // wait for signal to stabilize
123 if (!(OHCI_INST(dev
->controller
)->opreg
->HcRhPortStatus
[port
] & PortEnableStatus
)) {
124 usb_debug("port enable failed\n");
128 usb_speed speed
= (OHCI_INST(dev
->controller
)->opreg
->HcRhPortStatus
[port
] & LowSpeedDeviceAttached
) != 0;
129 RH_INST(dev
)->port
[port
] = usb_attach_device(dev
->controller
, dev
->address
, port
, speed
);
133 ohci_rh_report_port_changes(usbdev_t
*dev
)
135 ohci_t
*const ohcic
= OHCI_INST(dev
->controller
);
139 for (i
= 0; i
< RH_INST(dev
)->numports
; i
++) {
140 // maybe detach+attach happened between two scans?
141 if (ohcic
->opreg
->HcRhPortStatus
[i
] & ConnectStatusChange
) {
142 ohcic
->opreg
->HcRhPortStatus
[i
] = ConnectStatusChange
;
143 usb_debug("attachment change on port %d\n", i
);
153 ohci_rh_destroy(usbdev_t
*dev
)
156 for (i
= 0; i
< RH_INST(dev
)->numports
; i
++)
157 ohci_rh_disable_port(dev
, i
);
162 ohci_rh_poll(usbdev_t
*dev
)
164 ohci_t
*const ohcic
= OHCI_INST(dev
->controller
);
168 /* Check if anything changed. */
169 if (!(ohcic
->opreg
->HcInterruptStatus
& RootHubStatusChange
))
171 ohcic
->opreg
->HcInterruptStatus
= RootHubStatusChange
;
172 usb_debug("root hub status change\n");
174 /* Scan ports with changed connection status. */
175 while ((port
= ohci_rh_report_port_changes(dev
)) != -1)
176 ohci_rh_scanport(dev
, port
);
180 ohci_rh_init(usbdev_t
*dev
)
184 dev
->destroy
= ohci_rh_destroy
;
185 dev
->poll
= ohci_rh_poll
;
187 dev
->data
= xmalloc(sizeof(rh_inst_t
));
188 RH_INST(dev
)->numports
= OHCI_INST(dev
->controller
)->opreg
->HcRhDescriptorA
& NumberDownstreamPortsMask
;
189 RH_INST(dev
)->port
= xmalloc(sizeof(int) * RH_INST(dev
)->numports
);
190 usb_debug("%d ports registered\n", RH_INST(dev
)->numports
);
192 for (i
= 0; i
< RH_INST(dev
)->numports
; i
++) {
193 ohci_rh_enable_port(dev
, i
);
194 RH_INST(dev
)->port
[i
] = -1;
197 /* we can set them here because a root hub _really_ shouldn't
203 usb_debug("rh init done\n");