[PATCH] USB: EHCI updates

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.)

Un-exports a symbol that was temporarily exported.

A notable change from previous version is that this doesn't move
the spinlock init, so there's still a resume/reinit path bug.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by David Brownell and committed by Linus Torvalds f03c17fc b4723ae3

+43 -39
-1
drivers/usb/core/hub.c
··· 1669 1669 return 0; 1670 1670 #endif 1671 1671 } 1672 - EXPORT_SYMBOL_GPL(usb_suspend_device); 1673 1672 1674 1673 /* 1675 1674 * If the USB "suspend" state is in use (rather than "global suspend"),
+1 -2
drivers/usb/host/ehci-hcd.c
··· 636 636 * stop that signaling. 637 637 */ 638 638 ehci->reset_done [i] = jiffies + msecs_to_jiffies (20); 639 - mod_timer (&hcd->rh_timer, 640 - ehci->reset_done [i] + 1); 641 639 ehci_dbg (ehci, "port %d remote wakeup\n", i + 1); 640 + usb_hcd_resume_root_hub(hcd); 642 641 } 643 642 } 644 643
+7
drivers/usb/host/ehci-hub.c
··· 94 94 msleep(5); 95 95 spin_lock_irq (&ehci->lock); 96 96 97 + /* Ideally and we've got a real resume here, and no port's power 98 + * was lost. (For PCI, that means Vaux was maintained.) But we 99 + * could instead be restoring a swsusp snapshot -- so that BIOS was 100 + * the last user of the controller, not reset/pm hardware keeping 101 + * state we gave to it. 102 + */ 103 + 97 104 /* re-init operational registers in case we lost power */ 98 105 if (readl (&ehci->regs->intr_enable) == 0) { 99 106 /* at least some APM implementations will try to deliver
+35 -36
drivers/usb/host/ehci-pci.c
··· 235 235 236 236 /* suspend/resume, section 4.3 */ 237 237 238 - /* These routines rely on the bus (pci, platform, etc) 238 + /* These routines rely on the PCI bus glue 239 239 * to handle powerdown and wakeup, and currently also on 240 240 * transceivers that don't need any software attention to set up 241 241 * the right sort of wakeup. 242 + * Also they depend on separate root hub suspend/resume. 242 243 */ 243 244 244 245 static int ehci_pci_suspend (struct usb_hcd *hcd, pm_message_t message) ··· 247 246 struct ehci_hcd *ehci = hcd_to_ehci (hcd); 248 247 249 248 if (time_before (jiffies, ehci->next_statechange)) 250 - msleep (100); 249 + msleep (10); 251 250 252 - #ifdef CONFIG_USB_SUSPEND 253 - (void) usb_suspend_device (hcd->self.root_hub); 254 - #else 255 - usb_lock_device (hcd->self.root_hub); 256 - (void) ehci_bus_suspend (hcd); 257 - usb_unlock_device (hcd->self.root_hub); 258 - #endif 259 - 260 - // save (PCI) FLADJ in case of Vaux power loss 251 + // could save FLADJ in case of Vaux power loss 261 252 // ... we'd only use it to handle clock skew 262 253 263 254 return 0; ··· 262 269 struct usb_device *root = hcd->self.root_hub; 263 270 int retval = -EINVAL; 264 271 265 - // maybe restore (PCI) FLADJ 272 + // maybe restore FLADJ 266 273 267 274 if (time_before (jiffies, ehci->next_statechange)) 268 275 msleep (100); 269 276 277 + /* If CF is clear, we lost PCI Vaux power and need to restart. */ 278 + if (readl (&ehci->regs->configured_flag) != cpu_to_le32(FLAG_CF)) 279 + goto restart; 280 + 270 281 /* If any port is suspended (or owned by the companion), 271 282 * we know we can/must resume the HC (and mustn't reset it). 283 + * We just defer that to the root hub code. 272 284 */ 273 285 for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; ) { 274 286 u32 status; ··· 281 283 status = readl (&ehci->regs->port_status [port]); 282 284 if (!(status & PORT_POWER)) 283 285 continue; 284 - if (status & (PORT_SUSPEND | PORT_OWNER)) { 285 - down (&hcd->self.root_hub->serialize); 286 - retval = ehci_bus_resume (hcd); 287 - up (&hcd->self.root_hub->serialize); 288 - break; 286 + if (status & (PORT_SUSPEND | PORT_RESUME | PORT_OWNER)) { 287 + usb_hcd_resume_root_hub(hcd); 288 + return 0; 289 289 } 290 + } 291 + 292 + restart: 293 + ehci_dbg(ehci, "lost power, restarting\n"); 294 + for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; ) { 295 + port--; 290 296 if (!root->children [port]) 291 297 continue; 292 - dbg_port (ehci, __FUNCTION__, port + 1, status); 293 298 usb_set_device_state (root->children[port], 294 299 USB_STATE_NOTATTACHED); 295 300 } 296 301 297 302 /* Else reset, to cope with power loss or flush-to-storage 298 - * style "resume" having activated BIOS during reboot. 303 + * style "resume" having let BIOS kick in during reboot. 299 304 */ 300 - if (port == 0) { 301 - (void) ehci_halt (ehci); 302 - (void) ehci_reset (ehci); 303 - (void) ehci_pci_reset (hcd); 305 + (void) ehci_halt (ehci); 306 + (void) ehci_reset (ehci); 307 + (void) ehci_pci_reset (hcd); 304 308 305 - /* emptying the schedule aborts any urbs */ 306 - spin_lock_irq (&ehci->lock); 307 - if (ehci->reclaim) 308 - ehci->reclaim_ready = 1; 309 - ehci_work (ehci, NULL); 310 - spin_unlock_irq (&ehci->lock); 309 + /* emptying the schedule aborts any urbs */ 310 + spin_lock_irq (&ehci->lock); 311 + if (ehci->reclaim) 312 + ehci->reclaim_ready = 1; 313 + ehci_work (ehci, NULL); 314 + spin_unlock_irq (&ehci->lock); 311 315 312 - /* restart; khubd will disconnect devices */ 313 - retval = ehci_run (hcd); 316 + /* restart; khubd will disconnect devices */ 317 + retval = ehci_run (hcd); 314 318 315 - /* here we "know" root ports should always stay powered; 316 - * but some controllers may lose all power. 317 - */ 318 - ehci_port_power (ehci, 1); 319 - } 319 + /* here we "know" root ports should always stay powered; 320 + * but some controllers may lose all power. 321 + */ 322 + ehci_port_power (ehci, 1); 320 323 321 324 return retval; 322 325 }