aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2007-10-10 16:27:07 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2007-10-12 17:55:34 -0400
commit32fe01985aa2cb2562f6fc171e526e279abe10db (patch)
tree0c1865a1d3e91ae0839147430480c4099b16a06f /drivers/usb
parent17f060224fb9f435c6f9306b7b61419d038def13 (diff)
USB: mutual exclusion for EHCI init and port resets
This patch (as999) fixes a problem that sometimes shows up when host controller driver modules are loaded in the wrong order. If ehci-hcd happens to initialize an EHCI controller while the companion OHCI or UHCI controller is in the middle of a port reset, the reset can fail and the companion may get very confused. The patch adds an rw-semaphore and uses it to keep EHCI initialization and port resets mutually exclusive. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Acked-by: David Brownell <david-b@pacbell.net> Cc: David Miller <davem@davemloft.net> Cc: Dely L Sy <dely.l.sy@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/core/hcd.h8
-rw-r--r--drivers/usb/core/hub.c15
-rw-r--r--drivers/usb/host/ehci-hcd.c8
3 files changed, 29 insertions, 2 deletions
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index 1396141274f1..98e24194a4ab 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -19,6 +19,8 @@
19 19
20#ifdef __KERNEL__ 20#ifdef __KERNEL__
21 21
22#include <linux/rwsem.h>
23
22/* This file contains declarations of usbcore internals that are mostly 24/* This file contains declarations of usbcore internals that are mostly
23 * used or exposed by Host Controller Drivers. 25 * used or exposed by Host Controller Drivers.
24 */ 26 */
@@ -470,5 +472,9 @@ static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb,
470 : (in_interrupt () ? "in_interrupt" : "can sleep")) 472 : (in_interrupt () ? "in_interrupt" : "can sleep"))
471 473
472 474
473#endif /* __KERNEL__ */ 475/* This rwsem is for use only by the hub driver and ehci-hcd.
476 * Nobody else should touch it.
477 */
478extern struct rw_semaphore ehci_cf_port_reset_rwsem;
474 479
480#endif /* __KERNEL__ */
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 481dca641ea2..d20cb545a6e4 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -125,6 +125,12 @@ MODULE_PARM_DESC(use_both_schemes,
125 "try the other device initialization scheme if the " 125 "try the other device initialization scheme if the "
126 "first one fails"); 126 "first one fails");
127 127
128/* Mutual exclusion for EHCI CF initialization. This interferes with
129 * port reset on some companion controllers.
130 */
131DECLARE_RWSEM(ehci_cf_port_reset_rwsem);
132EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
133
128 134
129static inline char *portspeed(int portstatus) 135static inline char *portspeed(int portstatus)
130{ 136{
@@ -1581,6 +1587,11 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
1581{ 1587{
1582 int i, status; 1588 int i, status;
1583 1589
1590 /* Block EHCI CF initialization during the port reset.
1591 * Some companion controllers don't like it when they mix.
1592 */
1593 down_read(&ehci_cf_port_reset_rwsem);
1594
1584 /* Reset the port */ 1595 /* Reset the port */
1585 for (i = 0; i < PORT_RESET_TRIES; i++) { 1596 for (i = 0; i < PORT_RESET_TRIES; i++) {
1586 status = set_port_feature(hub->hdev, 1597 status = set_port_feature(hub->hdev,
@@ -1612,7 +1623,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
1612 usb_set_device_state(udev, status 1623 usb_set_device_state(udev, status
1613 ? USB_STATE_NOTATTACHED 1624 ? USB_STATE_NOTATTACHED
1614 : USB_STATE_DEFAULT); 1625 : USB_STATE_DEFAULT);
1615 return status; 1626 goto done;
1616 } 1627 }
1617 1628
1618 dev_dbg (hub->intfdev, 1629 dev_dbg (hub->intfdev,
@@ -1625,6 +1636,8 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
1625 "Cannot enable port %i. Maybe the USB cable is bad?\n", 1636 "Cannot enable port %i. Maybe the USB cable is bad?\n",
1626 port1); 1637 port1);
1627 1638
1639 done:
1640 up_read(&ehci_cf_port_reset_rwsem);
1628 return status; 1641 return status;
1629} 1642}
1630 1643
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index db00492588b6..c1514442883e 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -570,10 +570,18 @@ static int ehci_run (struct usb_hcd *hcd)
570 * are explicitly handed to companion controller(s), so no TT is 570 * are explicitly handed to companion controller(s), so no TT is
571 * involved with the root hub. (Except where one is integrated, 571 * involved with the root hub. (Except where one is integrated,
572 * and there's no companion controller unless maybe for USB OTG.) 572 * and there's no companion controller unless maybe for USB OTG.)
573 *
574 * Turning on the CF flag will transfer ownership of all ports
575 * from the companions to the EHCI controller. If any of the
576 * companions are in the middle of a port reset at the time, it
577 * could cause trouble. Write-locking ehci_cf_port_reset_rwsem
578 * guarantees that no resets are in progress.
573 */ 579 */
580 down_write(&ehci_cf_port_reset_rwsem);
574 hcd->state = HC_STATE_RUNNING; 581 hcd->state = HC_STATE_RUNNING;
575 ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); 582 ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
576 ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ 583 ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
584 up_write(&ehci_cf_port_reset_rwsem);
577 585
578 temp = HC_VERSION(ehci_readl(ehci, &ehci->caps->hc_capbase)); 586 temp = HC_VERSION(ehci_readl(ehci, &ehci->caps->hc_capbase));
579 ehci_info (ehci, 587 ehci_info (ehci,