With this and some other patches (OHCI, EHCI, and hub for starters), I'm
seeing swsusp partially behaving with usb loaded.

This one makes the usbcore pci suspend/resume logic treat "legacy"
devices the same as "pci pm" devices ... and vice versa.  It's that
second part which will be interesting to some folk:  legacy devices
never fail a suspend request, so now ones with pci-pm won't either.

As a side effect, that means it works around osdl bugid 2886 (where
the pm core code is using PCI-incompatible power states).

- Dave





Handle "legacy" (non-PM) devices better
 - basically just suspend hub and act as if the hardware were pm-aware
 - update dev->current_state, per API spec
 - accept all suspend states (no power reductions though)

Implement the PCI API better
 - use D3hot suspend if D1/D2 was requested but the hardware can't do that,
   to be just as forgiving as "legacy" devices (also doubles as a USB-specific
   workaround for OSDL bugid 2886).
 - handle "suspend deeper" (D1->D2, D2->D3hot, etc)

Minor fixes:
 - when debugging, dump the PCI PM states by name ("D3hot" etc)
 - there are no states beyond D3cold

Signed-off-by: David Brownell <[EMAIL PROTECTED]>


--- xu26/drivers/usb/core/hcd-pci.c	2004-09-09 18:46:18.000000000 -0700
+++ gadget-2.6/drivers/usb/core/hcd-pci.c	2004-09-10 18:02:18.000000000 -0700
@@ -266,6 +266,18 @@
 
 #ifdef	CONFIG_PM
 
+static char *pci_state(u32 state)
+{
+	switch (state) {
+	case 0:		return "D0";
+	case 1:		return "D1";
+	case 2:		return "D2";
+	case 3:		return "D3hot";
+	case 4:		return "D3cold";
+	}
+	return NULL;
+}
+
 /**
  * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
  * @dev: USB Host Controller being suspended
@@ -286,16 +298,34 @@
 	 * PM-sensitive HCDs may already have done this.
 	 */
 	has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
-	if (has_pci_pm)
-		dev_dbg(hcd->self.controller, "suspend D%d --> D%d\n",
-			dev->current_state, state);
+	if (state > 4)
+		state = 4;
 
 	switch (hcd->state) {
 	case USB_STATE_HALT:
 		dev_dbg (hcd->self.controller, "halted; hcd not suspended\n");
 		break;
 	case HCD_STATE_SUSPENDED:
-		dev_dbg (hcd->self.controller, "hcd already suspended\n");
+		dev_dbg (hcd->self.controller, "PCI %s --> %s\n",
+				pci_state(dev->current_state),
+				pci_state(state));
+		if (state > 3)
+			state = 3;
+
+		if (state == dev->current_state)
+			break;
+		else if (state < dev->current_state)
+			retval = -EIO;
+		else if (has_pci_pm)
+			retval = pci_set_power_state (dev, state);
+		else
+			dev->current_state = state;
+
+		if (retval == 0)
+			dev->dev.power.power_state = state;
+		else
+			dev_dbg (hcd->self.controller, 
+					"re-suspend fail, %d\n", retval);
 		break;
 	default:
 		retval = hcd->driver->suspend (hcd, state);
@@ -306,22 +336,48 @@
 		else {
 			hcd->state = HCD_STATE_SUSPENDED;
 			pci_save_state (dev, hcd->pci_state);
-#ifdef	CONFIG_USB_SUSPEND
-			pci_enable_wake (dev, state, hcd->remote_wakeup);
-			pci_enable_wake (dev, 4, hcd->remote_wakeup);
-#endif
+
 			/* no DMA or IRQs except in D0 */
 			pci_disable_device (dev);
 			free_irq (hcd->irq, hcd);
 			
-			if (has_pci_pm)
+			if (has_pci_pm) {
 				retval = pci_set_power_state (dev, state);
-			dev->dev.power.power_state = state;
+
+				/* POLICY: mask D1/D2/D3hot differences;
+				 * we know D3hot will always work.
+				 */
+				if (retval < 0 && state < 3) {
+					retval = pci_set_power_state (dev, 3);
+					if (retval == 0)
+						state = 3;
+				}
+				if (retval == 0) {
+					dev->dev.power.power_state = state;
+#ifdef	CONFIG_USB_SUSPEND
+					pci_enable_wake (dev, state,
+							hcd->remote_wakeup);
+					pci_enable_wake (dev, 4,
+							hcd->remote_wakeup);
+#endif
+				}
+			} else {
+				if (state > 3)
+					state = 3;
+				dev->current_state = state;
+				dev->dev.power.power_state = state;
+			}
 			if (retval < 0) {
 				dev_dbg (&dev->dev,
-						"PCI suspend fail, %d\n",
+						"PCI %s suspend fail, %d\n",
+						pci_state(state),
 						retval);
 				(void) usb_hcd_pci_resume (dev);
+			} else {
+				dev_dbg(hcd->self.controller,
+					"suspended to PCI %s%s\n",
+					pci_state(state),
+					has_pci_pm ? " (PM)" : "");
 			}
 		}
 	}
@@ -343,9 +399,10 @@
 
 	hcd = pci_get_drvdata(dev);
 	has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
-	if (has_pci_pm)
-		dev_dbg(hcd->self.controller, "resume from state D%d\n",
-				dev->current_state);
+
+	/* D3cold resume isn't usually reported this way... */
+	dev_dbg(hcd->self.controller, "resume from PCI %s\n",
+			pci_state(dev->current_state));
 
 	if (hcd->state != HCD_STATE_SUSPENDED) {
 		dev_dbg (hcd->self.controller, 
@@ -356,6 +413,8 @@
 
 	if (has_pci_pm)
 		pci_set_power_state (dev, 0);
+	else
+		dev->current_state = 0;
 	dev->dev.power.power_state = 0;
 	retval = request_irq (dev->irq, usb_hcd_irq, SA_SHIRQ,
 				hcd->description, hcd);

Reply via email to