diff options
| author | Alan Stern <stern@rowland.harvard.edu> | 2014-05-01 15:21:42 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-05-03 17:58:46 -0400 |
| commit | c1db30a2a79eb59997b13b8cabf2a50bea9f04e1 (patch) | |
| tree | b026621ae7b87879322513c4b4fc19dee2a7a5bb | |
| parent | 4dd7aa0f3c8ade14da682311cdc0e1298226763e (diff) | |
USB: OHCI: fix problem with global suspend on ATI controllers
Some OHCI controllers from ATI/AMD seem to have difficulty with
"global" USB suspend, that is, suspending an entire USB bus without
setting the suspend feature for each port connected to a device. When
we try to resume the child devices, the controller gives timeout
errors on the unsuspended ports, requiring resets, and can even cause
ohci-hcd to hang; see
http://marc.info/?l=linux-usb&m=139514332820398&w=2
and the following messages.
This patch fixes the problem by adding a new quirk flag to ohci-hcd.
The flag causes the ohci_rh_suspend() routine to suspend each
unsuspended, enabled port before suspending the root hub. This
effectively converts the "global" suspend to an ordinary root-hub
suspend. There is no need to unsuspend these ports when the root hub
is resumed, because the child devices will be resumed anyway in the
course of a normal system resume ("global" suspend is never used for
runtime PM).
This patch should be applied to all stable kernels which include
commit 0aa2832dd0d9 (USB: use "global suspend" for system sleep on
USB-2 buses) or a backported version thereof.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Reported-by: Peter Münster <pmlists@free.fr>
Tested-by: Peter Münster <pmlists@free.fr>
CC: <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
| -rw-r--r-- | drivers/usb/host/ohci-hub.c | 18 | ||||
| -rw-r--r-- | drivers/usb/host/ohci-pci.c | 1 | ||||
| -rw-r--r-- | drivers/usb/host/ohci.h | 2 |
3 files changed, 21 insertions, 0 deletions
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index c81c8721cc5a..cd871b895013 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c | |||
| @@ -90,6 +90,24 @@ __acquires(ohci->lock) | |||
| 90 | dl_done_list (ohci); | 90 | dl_done_list (ohci); |
| 91 | finish_unlinks (ohci, ohci_frame_no(ohci)); | 91 | finish_unlinks (ohci, ohci_frame_no(ohci)); |
| 92 | 92 | ||
| 93 | /* | ||
| 94 | * Some controllers don't handle "global" suspend properly if | ||
| 95 | * there are unsuspended ports. For these controllers, put all | ||
| 96 | * the enabled ports into suspend before suspending the root hub. | ||
| 97 | */ | ||
| 98 | if (ohci->flags & OHCI_QUIRK_GLOBAL_SUSPEND) { | ||
| 99 | __hc32 __iomem *portstat = ohci->regs->roothub.portstatus; | ||
| 100 | int i; | ||
| 101 | unsigned temp; | ||
| 102 | |||
| 103 | for (i = 0; i < ohci->num_ports; (++i, ++portstat)) { | ||
| 104 | temp = ohci_readl(ohci, portstat); | ||
| 105 | if ((temp & (RH_PS_PES | RH_PS_PSS)) == | ||
| 106 | RH_PS_PES) | ||
| 107 | ohci_writel(ohci, RH_PS_PSS, portstat); | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 93 | /* maybe resume can wake root hub */ | 111 | /* maybe resume can wake root hub */ |
| 94 | if (ohci_to_hcd(ohci)->self.root_hub->do_remote_wakeup || autostop) { | 112 | if (ohci_to_hcd(ohci)->self.root_hub->do_remote_wakeup || autostop) { |
| 95 | ohci->hc_control |= OHCI_CTRL_RWE; | 113 | ohci->hc_control |= OHCI_CTRL_RWE; |
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 90879e9ccbec..bb1509675727 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c | |||
| @@ -160,6 +160,7 @@ static int ohci_quirk_amd700(struct usb_hcd *hcd) | |||
| 160 | ohci_dbg(ohci, "enabled AMD prefetch quirk\n"); | 160 | ohci_dbg(ohci, "enabled AMD prefetch quirk\n"); |
| 161 | } | 161 | } |
| 162 | 162 | ||
| 163 | ohci->flags |= OHCI_QUIRK_GLOBAL_SUSPEND; | ||
| 163 | return 0; | 164 | return 0; |
| 164 | } | 165 | } |
| 165 | 166 | ||
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 9250cada13f0..4550ce05af7f 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h | |||
| @@ -405,6 +405,8 @@ struct ohci_hcd { | |||
| 405 | #define OHCI_QUIRK_HUB_POWER 0x100 /* distrust firmware power/oc setup */ | 405 | #define OHCI_QUIRK_HUB_POWER 0x100 /* distrust firmware power/oc setup */ |
| 406 | #define OHCI_QUIRK_AMD_PLL 0x200 /* AMD PLL quirk*/ | 406 | #define OHCI_QUIRK_AMD_PLL 0x200 /* AMD PLL quirk*/ |
| 407 | #define OHCI_QUIRK_AMD_PREFETCH 0x400 /* pre-fetch for ISO transfer */ | 407 | #define OHCI_QUIRK_AMD_PREFETCH 0x400 /* pre-fetch for ISO transfer */ |
| 408 | #define OHCI_QUIRK_GLOBAL_SUSPEND 0x800 /* must suspend ports */ | ||
| 409 | |||
| 408 | // there are also chip quirks/bugs in init logic | 410 | // there are also chip quirks/bugs in init logic |
| 409 | 411 | ||
| 410 | struct work_struct nec_work; /* Worker for NEC quirk */ | 412 | struct work_struct nec_work; /* Worker for NEC quirk */ |
