On Thu, Jun 09, 2005 at 09:14:52PM -0700, David Brownell wrote:
> On Thursday 09 June 2005 8:39 pm, Todd Poynor wrote:
...
> > So far as I thought I knew, pm_message_t will eventually become a struct
> > with fields that can be used to properly describe what sort of driver
> > behavior is needed at suspend and resume...
> 
> That's one theory; I can't see it checking out though...

OK, I know a cell phone vendor or two that want more info on system
suspend/resume behaviors available to drivers, so I'll pitch in to try
to turn this parameter into something useful.

> It was only swsusp that forced "3"; the other 2.6 code, now deleted,
> was doing PM_SUSPEND_MEM (3) or PM_SUSPEND_DISK (4).

Ah I see, I meant that both "standby" and "mem" suspends (which we can
map to two different sleep modes on the PXA27x that have different power
cycling behavior for I/O devices) both suspend devices to state 3, was
forgetting about the suspend-to-disk option.

> This patch reads fine to me except that you're not setting the
> dev->power.power_state to (bleech) PMSG_SUSPEND or PMSG_ON,
> which means that operations through sysfs power/state files will
> be more borked than usual ... not all paths through the driver
> core will maintain that field, so drivers need to.  At least,
> so long as the field exists.  It's mostly just trouble.  :)

Ugh, at one point in the not-too-distant past both system suspend/resume
and individual device "runtime" suspend/resume managed that field on
behalf of drivers, but I see system suspend/resume no longer does so.
Here's a new patch that assigns a "message" to the "state" field (may
this simply be a passing phase).

Thanks -- Todd

Power Management for the Intel XScale PXA27x OHCI USB host controller

Signed-off-by: Todd Poynor <tpoynor@mvista.com>

Index: linux-2.6.12-rc4/drivers/usb/host/ohci-pxa27x.c
===================================================================
--- linux-2.6.12-rc4.orig/drivers/usb/host/ohci-pxa27x.c	2005-06-09 02:56:43.000000000 +0000
+++ linux-2.6.12-rc4/drivers/usb/host/ohci-pxa27x.c	2005-06-10 20:27:04.000000000 +0000
@@ -310,6 +310,7 @@
 	.hub_suspend =		ohci_hub_suspend,
 	.hub_resume =		ohci_hub_resume,
 #endif
+	.start_port_reset =	ohci_start_port_reset,
 };
 
 /*-------------------------------------------------------------------------*/
@@ -337,32 +338,69 @@
 	return 0;
 }
 
-static int ohci_hcd_pxa27x_drv_suspend(struct device *dev, pm_message_t state, u32 level)
+
+#ifdef	CONFIG_PM
+
+static int ohci_hcd_pxa27x_drv_suspend(struct device *dev, pm_message_t message, u32 level)
 {
-//	struct platform_device *pdev = to_platform_device(dev);
-//	struct usb_hcd *hcd = dev_get_drvdata(dev);
-	printk("%s: not implemented yet\n", __FUNCTION__);
+	struct platform_device *pdev = to_platform_device(dev);
+	struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev));
+	int status = -EINVAL;
 
-	return 0;
+	if (level != SUSPEND_POWER_DOWN)
+		return 0;
+
+	down(&ohci_to_hcd(ohci)->self.root_hub->serialize);
+	status = ohci_hub_suspend(ohci_to_hcd(ohci));
+	if (status == 0) {
+		pxa27x_stop_hc(pdev);
+		ohci_to_hcd(ohci)->self.root_hub->state =
+			USB_STATE_SUSPENDED;
+		ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED;
+		dev->power.power_state = PMSG_SUSPEND;
+	}
+	up(&ohci_to_hcd(ohci)->self.root_hub->serialize);
+	return status;
 }
 
 static int ohci_hcd_pxa27x_drv_resume(struct device *dev, u32 level)
 {
-//	struct platform_device *pdev = to_platform_device(dev);
-//	struct usb_hcd *hcd = dev_get_drvdata(dev);
-	printk("%s: not implemented yet\n", __FUNCTION__);
+	struct platform_device *pdev = to_platform_device(dev);
+	struct ohci_hcd	*ohci = hcd_to_ohci(dev_get_drvdata(dev));
+	int		status = 0;
 
-	return 0;
+	if (level != RESUME_POWER_ON)
+		return 0;
+
+	if (time_before(jiffies, ohci->next_statechange))
+		msleep(5);
+	ohci->next_statechange = jiffies;
+	pxa27x_start_hc(pdev);
+#ifdef	CONFIG_USB_SUSPEND
+	/* get extra cleanup even if remote wakeup isn't in use */
+	status = usb_resume_device(ohci_to_hcd(ohci)->self.root_hub);
+#else
+	down(&ohci_to_hcd(ohci)->self.root_hub->serialize);
+	status = ohci_hub_resume(ohci_to_hcd(ohci));
+	up(&ohci_to_hcd(ohci)->self.root_hub->serialize);
+#endif
+	if (status == 0)
+		dev->power.power_state = PMSG_ON;
+
+	return status;
 }
 
+#endif
 
 static struct device_driver ohci_hcd_pxa27x_driver = {
 	.name		= "pxa27x-ohci",
 	.bus		= &platform_bus_type,
 	.probe		= ohci_hcd_pxa27x_drv_probe,
 	.remove		= ohci_hcd_pxa27x_drv_remove,
+#ifdef	CONFIG_PM
 	.suspend	= ohci_hcd_pxa27x_drv_suspend, 
-	.resume		= ohci_hcd_pxa27x_drv_resume, 
+	.resume		= ohci_hcd_pxa27x_drv_resume,
+#endif 
 };
 
 static int __init ohci_hcd_pxa27x_init (void)

