/* UHCI controllers don't automatically stop resume signalling after 20 msec,
* so we have to poll and check timeouts in order to take care of it.
- * FIXME: Synchronize access to these fields by a spinlock.
*/
static void uhci_finish_suspend(struct uhci_hcd *uhci, int port,
unsigned long port_addr)
}
}
-static void uhci_check_resume(struct uhci_hcd *uhci)
+static void uhci_check_ports(struct uhci_hcd *uhci)
{
unsigned int port;
unsigned long port_addr;
+ int status;
for (port = 0; port < uhci->rh_numports; ++port) {
port_addr = uhci->io_addr + USBPORTSC1 + 2 * port;
- if (unlikely(inw(port_addr) & USBPORTSC_RD)) {
+ status = inw(port_addr);
+ if (unlikely(status & USBPORTSC_PR)) {
+ if (time_after_eq(jiffies, uhci->ports_timeout)) {
+ CLR_RH_PORTSTAT(USBPORTSC_PR);
+ udelay(10);
+
+ /* If the port was enabled before, turning
+ * reset on caused a port enable change.
+ * Turning reset off causes a port connect
+ * status change. Clear these changes. */
+ CLR_RH_PORTSTAT(USBPORTSC_CSC | USBPORTSC_PEC);
+ SET_RH_PORTSTAT(USBPORTSC_PE);
+ }
+ }
+ if (unlikely(status & USBPORTSC_RD)) {
if (!test_bit(port, &uhci->resuming_ports)) {
/* Port received a wakeup request */
set_bit(port, &uhci->resuming_ports);
- uhci->resume_timeout = jiffies +
+ uhci->ports_timeout = jiffies +
msecs_to_jiffies(20);
} else if (time_after_eq(jiffies,
- uhci->resume_timeout)) {
+ uhci->ports_timeout)) {
uhci_finish_suspend(uhci, port, port_addr);
}
}
unsigned int port = wIndex - 1;
unsigned long port_addr = uhci->io_addr + USBPORTSC1 + 2 * port;
u16 wPortChange, wPortStatus;
+ unsigned long flags;
+ spin_lock_irqsave(&uhci->lock, flags);
switch (typeReq) {
case GetHubStatus:
if (port >= uhci->rh_numports)
goto err;
- if (uhci->resuming_ports)
- uhci_check_resume(uhci);
-
+ uhci_check_ports(uhci);
status = inw(port_addr);
/* Intel controllers report the OverCurrent bit active on.
OK(0);
case USB_PORT_FEAT_RESET:
SET_RH_PORTSTAT(USBPORTSC_PR);
- mdelay(50); /* USB v1.1 7.1.7.3 */
- CLR_RH_PORTSTAT(USBPORTSC_PR);
- udelay(10);
/* Reset terminates Resume signalling */
uhci_finish_suspend(uhci, port, port_addr);
- SET_RH_PORTSTAT(USBPORTSC_PE);
- mdelay(10);
- CLR_RH_PORTSTAT(USBPORTSC_PEC|USBPORTSC_CSC);
+
+ /* USB v2.0 7.1.7.5 */
+ uhci->ports_timeout = jiffies + msecs_to_jiffies(50);
OK(0);
case USB_PORT_FEAT_POWER:
/* UHCI has no power switching */
if (test_bit(port, &uhci->suspended_ports) &&
!test_and_set_bit(port,
&uhci->resuming_ports)) {
- uhci->resume_timeout = jiffies +
- msecs_to_jiffies(20);
SET_RH_PORTSTAT(USBPORTSC_RD);
/* The controller won't allow RD to be set
if (!(inw(port_addr) & USBPORTSC_RD))
uhci_finish_suspend(uhci, port,
port_addr);
+ else
+ /* USB v2.0 7.1.7.7 */
+ uhci->ports_timeout = jiffies +
+ msecs_to_jiffies(20);
}
OK(0);
case USB_PORT_FEAT_C_SUSPEND:
err:
retval = -EPIPE;
}
+ spin_unlock_irqrestore(&uhci->lock, flags);
return retval;
}