mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-10 07:00:48 +00:00
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>
This commit is contained in:
parent
4dd7aa0f3c
commit
c1db30a2a7
@ -90,6 +90,24 @@ __acquires(ohci->lock)
|
|||||||
dl_done_list (ohci);
|
dl_done_list (ohci);
|
||||||
finish_unlinks (ohci, ohci_frame_no(ohci));
|
finish_unlinks (ohci, ohci_frame_no(ohci));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some controllers don't handle "global" suspend properly if
|
||||||
|
* there are unsuspended ports. For these controllers, put all
|
||||||
|
* the enabled ports into suspend before suspending the root hub.
|
||||||
|
*/
|
||||||
|
if (ohci->flags & OHCI_QUIRK_GLOBAL_SUSPEND) {
|
||||||
|
__hc32 __iomem *portstat = ohci->regs->roothub.portstatus;
|
||||||
|
int i;
|
||||||
|
unsigned temp;
|
||||||
|
|
||||||
|
for (i = 0; i < ohci->num_ports; (++i, ++portstat)) {
|
||||||
|
temp = ohci_readl(ohci, portstat);
|
||||||
|
if ((temp & (RH_PS_PES | RH_PS_PSS)) ==
|
||||||
|
RH_PS_PES)
|
||||||
|
ohci_writel(ohci, RH_PS_PSS, portstat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* maybe resume can wake root hub */
|
/* maybe resume can wake root hub */
|
||||||
if (ohci_to_hcd(ohci)->self.root_hub->do_remote_wakeup || autostop) {
|
if (ohci_to_hcd(ohci)->self.root_hub->do_remote_wakeup || autostop) {
|
||||||
ohci->hc_control |= OHCI_CTRL_RWE;
|
ohci->hc_control |= OHCI_CTRL_RWE;
|
||||||
|
@ -160,6 +160,7 @@ static int ohci_quirk_amd700(struct usb_hcd *hcd)
|
|||||||
ohci_dbg(ohci, "enabled AMD prefetch quirk\n");
|
ohci_dbg(ohci, "enabled AMD prefetch quirk\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ohci->flags |= OHCI_QUIRK_GLOBAL_SUSPEND;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,6 +405,8 @@ struct ohci_hcd {
|
|||||||
#define OHCI_QUIRK_HUB_POWER 0x100 /* distrust firmware power/oc setup */
|
#define OHCI_QUIRK_HUB_POWER 0x100 /* distrust firmware power/oc setup */
|
||||||
#define OHCI_QUIRK_AMD_PLL 0x200 /* AMD PLL quirk*/
|
#define OHCI_QUIRK_AMD_PLL 0x200 /* AMD PLL quirk*/
|
||||||
#define OHCI_QUIRK_AMD_PREFETCH 0x400 /* pre-fetch for ISO transfer */
|
#define OHCI_QUIRK_AMD_PREFETCH 0x400 /* pre-fetch for ISO transfer */
|
||||||
|
#define OHCI_QUIRK_GLOBAL_SUSPEND 0x800 /* must suspend ports */
|
||||||
|
|
||||||
// there are also chip quirks/bugs in init logic
|
// there are also chip quirks/bugs in init logic
|
||||||
|
|
||||||
struct work_struct nec_work; /* Worker for NEC quirk */
|
struct work_struct nec_work; /* Worker for NEC quirk */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user