Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c1db30a2 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman
Browse files

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 0aa2832d (USB: use "global suspend" for system sleep on
USB-2 buses) or a backported version thereof.

Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Reported-by: default avatarPeter Münster <pmlists@free.fr>
Tested-by: default avatarPeter Münster <pmlists@free.fr>
CC: <stable@vger.kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 4dd7aa0f
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -90,6 +90,24 @@ __acquires(ohci->lock)
	dl_done_list (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 */
	if (ohci_to_hcd(ohci)->self.root_hub->do_remote_wakeup || autostop) {
		ohci->hc_control |= OHCI_CTRL_RWE;
+1 −0
Original line number Diff line number Diff line
@@ -160,6 +160,7 @@ static int ohci_quirk_amd700(struct usb_hcd *hcd)
		ohci_dbg(ohci, "enabled AMD prefetch quirk\n");
	}

	ohci->flags |= OHCI_QUIRK_GLOBAL_SUSPEND;
	return 0;
}

+2 −0
Original line number Diff line number Diff line
@@ -405,6 +405,8 @@ struct ohci_hcd {
#define	OHCI_QUIRK_HUB_POWER	0x100			/* distrust firmware power/oc setup */
#define	OHCI_QUIRK_AMD_PLL	0x200			/* AMD PLL quirk*/
#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

	struct work_struct	nec_work;	/* Worker for NEC quirk */