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>
This commit is contained in:
parent
17f060224f
commit
32fe01985a
|
@ -19,6 +19,8 @@
|
||||||
|
|
||||||
#ifdef __KERNEL__
|
#ifdef __KERNEL__
|
||||||
|
|
||||||
|
#include <linux/rwsem.h>
|
||||||
|
|
||||||
/* This file contains declarations of usbcore internals that are mostly
|
/* This file contains declarations of usbcore internals that are mostly
|
||||||
* used or exposed by Host Controller Drivers.
|
* used or exposed by Host Controller Drivers.
|
||||||
*/
|
*/
|
||||||
|
@ -470,5 +472,9 @@ static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb,
|
||||||
: (in_interrupt () ? "in_interrupt" : "can sleep"))
|
: (in_interrupt () ? "in_interrupt" : "can sleep"))
|
||||||
|
|
||||||
|
|
||||||
#endif /* __KERNEL__ */
|
/* This rwsem is for use only by the hub driver and ehci-hcd.
|
||||||
|
* Nobody else should touch it.
|
||||||
|
*/
|
||||||
|
extern struct rw_semaphore ehci_cf_port_reset_rwsem;
|
||||||
|
|
||||||
|
#endif /* __KERNEL__ */
|
||||||
|
|
|
@ -125,6 +125,12 @@ MODULE_PARM_DESC(use_both_schemes,
|
||||||
"try the other device initialization scheme if the "
|
"try the other device initialization scheme if the "
|
||||||
"first one fails");
|
"first one fails");
|
||||||
|
|
||||||
|
/* Mutual exclusion for EHCI CF initialization. This interferes with
|
||||||
|
* port reset on some companion controllers.
|
||||||
|
*/
|
||||||
|
DECLARE_RWSEM(ehci_cf_port_reset_rwsem);
|
||||||
|
EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
|
||||||
|
|
||||||
|
|
||||||
static inline char *portspeed(int portstatus)
|
static inline char *portspeed(int portstatus)
|
||||||
{
|
{
|
||||||
|
@ -1581,6 +1587,11 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
|
||||||
{
|
{
|
||||||
int i, status;
|
int i, status;
|
||||||
|
|
||||||
|
/* Block EHCI CF initialization during the port reset.
|
||||||
|
* Some companion controllers don't like it when they mix.
|
||||||
|
*/
|
||||||
|
down_read(&ehci_cf_port_reset_rwsem);
|
||||||
|
|
||||||
/* Reset the port */
|
/* Reset the port */
|
||||||
for (i = 0; i < PORT_RESET_TRIES; i++) {
|
for (i = 0; i < PORT_RESET_TRIES; i++) {
|
||||||
status = set_port_feature(hub->hdev,
|
status = set_port_feature(hub->hdev,
|
||||||
|
@ -1612,7 +1623,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
|
||||||
usb_set_device_state(udev, status
|
usb_set_device_state(udev, status
|
||||||
? USB_STATE_NOTATTACHED
|
? USB_STATE_NOTATTACHED
|
||||||
: USB_STATE_DEFAULT);
|
: USB_STATE_DEFAULT);
|
||||||
return status;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_dbg (hub->intfdev,
|
dev_dbg (hub->intfdev,
|
||||||
|
@ -1625,6 +1636,8 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
|
||||||
"Cannot enable port %i. Maybe the USB cable is bad?\n",
|
"Cannot enable port %i. Maybe the USB cable is bad?\n",
|
||||||
port1);
|
port1);
|
||||||
|
|
||||||
|
done:
|
||||||
|
up_read(&ehci_cf_port_reset_rwsem);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -570,10 +570,18 @@ static int ehci_run (struct usb_hcd *hcd)
|
||||||
* are explicitly handed to companion controller(s), so no TT is
|
* are explicitly handed to companion controller(s), so no TT is
|
||||||
* involved with the root hub. (Except where one is integrated,
|
* involved with the root hub. (Except where one is integrated,
|
||||||
* and there's no companion controller unless maybe for USB OTG.)
|
* and there's no companion controller unless maybe for USB OTG.)
|
||||||
|
*
|
||||||
|
* Turning on the CF flag will transfer ownership of all ports
|
||||||
|
* from the companions to the EHCI controller. If any of the
|
||||||
|
* companions are in the middle of a port reset at the time, it
|
||||||
|
* could cause trouble. Write-locking ehci_cf_port_reset_rwsem
|
||||||
|
* guarantees that no resets are in progress.
|
||||||
*/
|
*/
|
||||||
|
down_write(&ehci_cf_port_reset_rwsem);
|
||||||
hcd->state = HC_STATE_RUNNING;
|
hcd->state = HC_STATE_RUNNING;
|
||||||
ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
|
ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
|
||||||
ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
|
ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
|
||||||
|
up_write(&ehci_cf_port_reset_rwsem);
|
||||||
|
|
||||||
temp = HC_VERSION(ehci_readl(ehci, &ehci->caps->hc_capbase));
|
temp = HC_VERSION(ehci_readl(ehci, &ehci->caps->hc_capbase));
|
||||||
ehci_info (ehci,
|
ehci_info (ehci,
|
||||||
|
|
Loading…
Reference in a new issue