Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Alan Stern and committed by
Greg Kroah-Hartman
c1db30a2 4dd7aa0f

+21
+18
drivers/usb/host/ohci-hub.c
··· 90 90 dl_done_list (ohci); 91 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 111 /* maybe resume can wake root hub */ 94 112 if (ohci_to_hcd(ohci)->self.root_hub->do_remote_wakeup || autostop) { 95 113 ohci->hc_control |= OHCI_CTRL_RWE;
+1
drivers/usb/host/ohci-pci.c
··· 160 160 ohci_dbg(ohci, "enabled AMD prefetch quirk\n"); 161 161 } 162 162 163 + ohci->flags |= OHCI_QUIRK_GLOBAL_SUSPEND; 163 164 return 0; 164 165 } 165 166
+2
drivers/usb/host/ohci.h
··· 405 405 #define OHCI_QUIRK_HUB_POWER 0x100 /* distrust firmware power/oc setup */ 406 406 #define OHCI_QUIRK_AMD_PLL 0x200 /* AMD PLL quirk*/ 407 407 #define OHCI_QUIRK_AMD_PREFETCH 0x400 /* pre-fetch for ISO transfer */ 408 + #define OHCI_QUIRK_GLOBAL_SUSPEND 0x800 /* must suspend ports */ 409 + 408 410 // there are also chip quirks/bugs in init logic 409 411 410 412 struct work_struct nec_work; /* Worker for NEC quirk */