[Cbe-oss-dev] [PATCH 1/5] usb: Fix PS3 EHCI suspend

Geoff Levand geoff at infradead.org
Tue Nov 15 13:43:57 EST 2011


The PS3 EHCI HC stops the root hub after both root hub ports are
suspended.  The current EHCI driver can't handle this behavior.
Three workarounds are needed in the PS3 bus glue.

1) The EHCI root hub driver expects the root hub to still be running when
ehci_bus_suspend() is called.  Add a new routine ps3_ehci_bus_suspend()
that forces the HC into the HALT state before calling ehci_bus_suspend().
This will allow ehci_bus_suspend() to succeed.

2) Add a new routine ps3_ehci_bus_resume() to get the HC back into a
running state before ehci_bus_resume() is called.

3) Add a call to ps3_ehci_bus_resume() in ps3_ehci_remove() to
assure usb_remove_hcd() is not called when the HC is suspended.

Fixes runtime errors similar to these:

  ps3-ehci-driver: force halt; handhake d0000800802e0014 0000c000 00000000 -> -110
  kernel BUG at drivers/usb/host/ehci-mem.c:74!

Related bug reports:

  http://bugzilla.kernel.org/show_bug.cgi?id=11819

Signed-off-by: Geoff Levand <geoff at infradead.org>
---
 drivers/usb/host/ehci-ps3.c |   63 +++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 61 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c
index 2dc32da..b173d94 100644
--- a/drivers/usb/host/ehci-ps3.c
+++ b/drivers/usb/host/ehci-ps3.c
@@ -52,6 +52,57 @@ static int ps3_ehci_hc_reset(struct usb_hcd *hcd)
 	return result;
 }
 
+static int __maybe_unused ps3_ehci_bus_suspend(struct usb_hcd *hcd)
+{
+	int result = 0;
+	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+
+	ehci_dbg(ehci, "%s:%d\n", __func__, __LINE__);
+
+	/*
+	 * The PS3 EHCI HC stops the root hub after both root hub ports are
+	 * suspended.  The EHCI root hub driver expects the root hub to still
+	 * be running when ehci_bus_suspend() is called.  Forcing the HC into
+	 * the HALT state here will allow a successful suspend.
+	 */
+
+	ehci_halt(ehci);
+	ehci_to_hcd(ehci)->state = HC_STATE_SUSPENDED;
+
+#if defined(CONFIG_PM)
+	result = ehci_bus_suspend(hcd);
+#endif
+	WARN_ON(result);
+
+	return result;
+}
+
+static int __maybe_unused ps3_ehci_bus_resume(struct usb_hcd *hcd)
+{
+	int result;
+	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+
+	ehci_dbg(ehci, "%s:%d\n", __func__, __LINE__);
+
+	/*
+	 * Putting the HC into the RUN state here will get the root hub
+	 * running and allow a successful resume.
+	 */
+
+	ehci->command |= CMD_RUN;
+	ehci_writel(ehci, ehci->command, &ehci->regs->command);
+
+	result = handshake(ehci, &ehci->regs->status, STS_HALT, 0, 250 * 1000);
+	WARN_ON(result);
+
+#if defined(CONFIG_PM)
+	result = ehci_bus_resume(hcd);
+#endif
+	WARN_ON(result);
+
+	return result;
+}
+
 static const struct hc_driver ps3_ehci_hc_driver = {
 	.description		= hcd_name,
 	.product_desc		= "PS3 EHCI Host Controller",
@@ -70,8 +121,8 @@ static const struct hc_driver ps3_ehci_hc_driver = {
 	.hub_status_data	= ehci_hub_status_data,
 	.hub_control		= ehci_hub_control,
 #if defined(CONFIG_PM)
-	.bus_suspend		= ehci_bus_suspend,
-	.bus_resume		= ehci_bus_resume,
+	.bus_suspend		= ps3_ehci_bus_suspend,
+	.bus_resume		= ps3_ehci_bus_resume,
 #endif
 	.relinquish_port	= ehci_relinquish_port,
 	.port_handed_over	= ehci_port_handed_over,
@@ -207,6 +258,14 @@ static int ps3_ehci_remove(struct ps3_system_bus_device *dev)
 
 	tmp = hcd->irq;
 
+	/*
+	 * Putting the HC into the RUN state here will get the root hub
+	 * running and allow a successful usb_remove_hcd().
+	 */
+
+#if defined(CONFIG_PM)
+	ps3_ehci_bus_resume(hcd);
+#endif
 	ehci_shutdown(hcd);
 	usb_remove_hcd(hcd);
 
-- 
1.7.4.1




More information about the cbe-oss-dev mailing list