Fixes some regressions in recent kernels, and handles another special case observed when restoring a swsusp snapshot.
This fixes some bugs in EHCI suspend/resume that joined us over the past few releases (as usbcore, PCI, pmcore, and other components evolved): - Removes suspend and resume recursion from the EHCI driver, getting rid of the USB_SUSPEND special casing. - Updates the wakeup mechanism to work again; there's a newish usbcore call it needs to use. - Provide simpler tests for "do we need to restart from scratch", to address another case where PCI Vaux was lost. (In this case it was restoring a swsusp snapshot, but there could be others.) It also moves the spinlock init out of a pci-specific reset path. Signed-off-by: David Brownell <[EMAIL PROTECTED]> --- g26.orig/drivers/usb/host/ehci-hcd.c 2005-09-22 22:15:12.000000000 -0700 +++ g26/drivers/usb/host/ehci-hcd.c 2005-09-22 22:15:19.000000000 -0700 @@ -434,6 +434,7 @@ static int ehci_run (struct usb_hcd *hcd else // N microframes cached ehci->i_thresh = 2 + HCC_ISOC_THRES (hcc_params); + spin_lock_init (&ehci->lock); ehci->reclaim = NULL; ehci->reclaim_ready = 0; ehci->next_uframe = -1; @@ -628,9 +629,8 @@ static irqreturn_t ehci_irq (struct usb_ * stop that signaling. */ ehci->reset_done [i] = jiffies + msecs_to_jiffies (20); - mod_timer (&hcd->rh_timer, - ehci->reset_done [i] + 1); ehci_dbg (ehci, "port %d remote wakeup\n", i + 1); + usb_hcd_resume_root_hub(hcd); } } --- g26.orig/drivers/usb/host/ehci-pci.c 2005-09-22 22:15:12.000000000 -0700 +++ g26/drivers/usb/host/ehci-pci.c 2005-09-22 22:29:13.000000000 -0700 @@ -65,8 +65,6 @@ static int ehci_pci_reset (struct usb_hc u32 temp; unsigned count = 256/4; - spin_lock_init (&ehci->lock); - ehci->caps = hcd->regs; ehci->regs = hcd->regs + HC_LENGTH (readl (&ehci->caps->hc_capbase)); dbg_hcs_params (ehci, "reset"); @@ -235,10 +233,11 @@ static void ehci_pci_stop (struct usb_hc /* suspend/resume, section 4.3 */ -/* These routines rely on the bus (pci, platform, etc) +/* These routines rely on the PCI bus glue * to handle powerdown and wakeup, and currently also on * transceivers that don't need any software attention to set up * the right sort of wakeup. + * Also they depend on separate root hub suspend/resume. */ static int ehci_pci_suspend (struct usb_hcd *hcd, pm_message_t message) @@ -246,17 +245,9 @@ static int ehci_pci_suspend (struct usb_ struct ehci_hcd *ehci = hcd_to_ehci (hcd); if (time_before (jiffies, ehci->next_statechange)) - msleep (100); - -#ifdef CONFIG_USB_SUSPEND - (void) usb_suspend_device (hcd->self.root_hub); -#else - usb_lock_device (hcd->self.root_hub); - (void) ehci_hub_suspend (hcd); - usb_unlock_device (hcd->self.root_hub); -#endif + msleep (10); - // save (PCI) FLADJ in case of Vaux power loss + // could save FLADJ in case of Vaux power loss // ... we'd only use it to handle clock skew return 0; @@ -269,13 +260,18 @@ static int ehci_pci_resume (struct usb_h struct usb_device *root = hcd->self.root_hub; int retval = -EINVAL; - // maybe restore (PCI) FLADJ + // maybe restore FLADJ if (time_before (jiffies, ehci->next_statechange)) msleep (100); + /* If CF is clear, we lost PCI Vaux power and need to restart. */ + if (readl (&ehci->regs->configured_flag) != cpu_to_le32(FLAG_CF)) + goto restart; + /* If any port is suspended (or owned by the companion), * we know we can/must resume the HC (and mustn't reset it). + * We just defer that to the root hub code. */ for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; ) { u32 status; @@ -283,42 +279,43 @@ static int ehci_pci_resume (struct usb_h status = readl (&ehci->regs->port_status [port]); if (!(status & PORT_POWER)) continue; - if (status & (PORT_SUSPEND | PORT_OWNER)) { - down (&hcd->self.root_hub->serialize); - retval = ehci_hub_resume (hcd); - up (&hcd->self.root_hub->serialize); - break; + if (status & (PORT_SUSPEND | PORT_RESUME | PORT_OWNER)) { + usb_hcd_resume_root_hub(hcd); + return 0; } + } + +restart: + ehci_dbg(ehci, "lost power, restarting\n"); + for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; ) { + port--; if (!root->children [port]) continue; - dbg_port (ehci, __FUNCTION__, port + 1, status); usb_set_device_state (root->children[port], USB_STATE_NOTATTACHED); } /* Else reset, to cope with power loss or flush-to-storage - * style "resume" having activated BIOS during reboot. + * style "resume" having let BIOS kick in during reboot. */ - if (port == 0) { - (void) ehci_halt (ehci); - (void) ehci_reset (ehci); - (void) ehci_pci_reset (hcd); - - /* emptying the schedule aborts any urbs */ - spin_lock_irq (&ehci->lock); - if (ehci->reclaim) - ehci->reclaim_ready = 1; - ehci_work (ehci, NULL); - spin_unlock_irq (&ehci->lock); - - /* restart; khubd will disconnect devices */ - retval = ehci_run (hcd); - - /* here we "know" root ports should always stay powered; - * but some controllers may lose all power. - */ - ehci_port_power (ehci, 1); - } + (void) ehci_halt (ehci); + (void) ehci_reset (ehci); + (void) ehci_pci_reset (hcd); + + /* emptying the schedule aborts any urbs */ + spin_lock_irq (&ehci->lock); + if (ehci->reclaim) + ehci->reclaim_ready = 1; + ehci_work (ehci, NULL); + spin_unlock_irq (&ehci->lock); + + /* restart; khubd will disconnect devices */ + retval = ehci_run (hcd); + + /* here we "know" root ports should always stay powered; + * but some controllers may lose all power. + */ + ehci_port_power (ehci, 1); return retval; } --- g26.orig/drivers/usb/host/ehci-hub.c 2005-09-22 22:02:22.000000000 -0700 +++ g26/drivers/usb/host/ehci-hub.c 2005-09-22 22:15:19.000000000 -0700 @@ -94,6 +94,13 @@ static int ehci_hub_resume (struct usb_h msleep(5); spin_lock_irq (&ehci->lock); + /* Ideally and we've got a real resume here, and no port's power + * was lost. (For PCI, that means Vaux was maintained.) But we + * could instead be restoring a swsusp snapshot -- so that BIOS was + * the last user of the controller, not reset/pm hardware keeping + * state we gave to it. + */ + /* re-init operational registers in case we lost power */ if (readl (&ehci->regs->intr_enable) == 0) { /* at least some APM implementations will try to deliver