diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2010-06-25 14:02:57 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-08-10 17:35:38 -0400 |
commit | 3da7cff4e79e4a7137b0dac1aaf6841b91bbff63 (patch) | |
tree | f41174132f184d036c4c1adf5676f2352681d0b6 /drivers/usb/core | |
parent | 0d436b425e07f9e4b0fe571cec061f5d136f1d8b (diff) |
USB: add runtime PM for PCI-based host controllers
This patch (as1386) adds runtime-PM support for PCI-based USB host
controllers. By default autosuspend is disallowed; the user must
enable it by writing "auto" to the controller's power/control sysfs
attribute.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/hcd-pci.c | 76 |
1 files changed, 62 insertions, 14 deletions
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 352577baa53d..fe6b8d40a506 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c | |||
@@ -250,6 +250,9 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) | |||
250 | if (retval != 0) | 250 | if (retval != 0) |
251 | goto err4; | 251 | goto err4; |
252 | set_hs_companion(dev, hcd); | 252 | set_hs_companion(dev, hcd); |
253 | |||
254 | if (pci_dev_run_wake(dev)) | ||
255 | pm_runtime_put_noidle(&dev->dev); | ||
253 | return retval; | 256 | return retval; |
254 | 257 | ||
255 | err4: | 258 | err4: |
@@ -292,6 +295,9 @@ void usb_hcd_pci_remove(struct pci_dev *dev) | |||
292 | if (!hcd) | 295 | if (!hcd) |
293 | return; | 296 | return; |
294 | 297 | ||
298 | if (pci_dev_run_wake(dev)) | ||
299 | pm_runtime_get_noresume(&dev->dev); | ||
300 | |||
295 | /* Fake an interrupt request in order to give the driver a chance | 301 | /* Fake an interrupt request in order to give the driver a chance |
296 | * to test whether the controller hardware has been removed (e.g., | 302 | * to test whether the controller hardware has been removed (e.g., |
297 | * cardbus physical eject). | 303 | * cardbus physical eject). |
@@ -325,12 +331,13 @@ void usb_hcd_pci_shutdown(struct pci_dev *dev) | |||
325 | if (!hcd) | 331 | if (!hcd) |
326 | return; | 332 | return; |
327 | 333 | ||
328 | if (hcd->driver->shutdown) | 334 | if (test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) && |
335 | hcd->driver->shutdown) | ||
329 | hcd->driver->shutdown(hcd); | 336 | hcd->driver->shutdown(hcd); |
330 | } | 337 | } |
331 | EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown); | 338 | EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown); |
332 | 339 | ||
333 | #ifdef CONFIG_PM_SLEEP | 340 | #ifdef CONFIG_PM_OPS |
334 | 341 | ||
335 | #ifdef CONFIG_PPC_PMAC | 342 | #ifdef CONFIG_PPC_PMAC |
336 | static void powermac_set_asic(struct pci_dev *pci_dev, int enable) | 343 | static void powermac_set_asic(struct pci_dev *pci_dev, int enable) |
@@ -366,7 +373,7 @@ static int check_root_hub_suspended(struct device *dev) | |||
366 | return 0; | 373 | return 0; |
367 | } | 374 | } |
368 | 375 | ||
369 | static int hcd_pci_suspend(struct device *dev) | 376 | static int suspend_common(struct device *dev, bool do_wakeup) |
370 | { | 377 | { |
371 | struct pci_dev *pci_dev = to_pci_dev(dev); | 378 | struct pci_dev *pci_dev = to_pci_dev(dev); |
372 | struct usb_hcd *hcd = pci_get_drvdata(pci_dev); | 379 | struct usb_hcd *hcd = pci_get_drvdata(pci_dev); |
@@ -381,13 +388,7 @@ static int hcd_pci_suspend(struct device *dev) | |||
381 | if (retval) | 388 | if (retval) |
382 | return retval; | 389 | return retval; |
383 | 390 | ||
384 | /* We might already be suspended (runtime PM -- not yet written) */ | ||
385 | if (pci_dev->current_state != PCI_D0) | ||
386 | return retval; | ||
387 | |||
388 | if (hcd->driver->pci_suspend) { | 391 | if (hcd->driver->pci_suspend) { |
389 | bool do_wakeup = device_may_wakeup(dev); | ||
390 | |||
391 | /* Optimization: Don't suspend if a root-hub wakeup is | 392 | /* Optimization: Don't suspend if a root-hub wakeup is |
392 | * pending and it would cause the HCD to wake up anyway. | 393 | * pending and it would cause the HCD to wake up anyway. |
393 | */ | 394 | */ |
@@ -439,10 +440,8 @@ static int resume_common(struct device *dev, int event) | |||
439 | clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); | 440 | clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); |
440 | 441 | ||
441 | if (hcd->driver->pci_resume) { | 442 | if (hcd->driver->pci_resume) { |
442 | /* This call should be made only during system resume, | 443 | if (event != PM_EVENT_AUTO_RESUME) |
443 | * not during runtime resume. | 444 | wait_for_companions(pci_dev, hcd); |
444 | */ | ||
445 | wait_for_companions(pci_dev, hcd); | ||
446 | 445 | ||
447 | retval = hcd->driver->pci_resume(hcd, | 446 | retval = hcd->driver->pci_resume(hcd, |
448 | event == PM_EVENT_RESTORE); | 447 | event == PM_EVENT_RESTORE); |
@@ -454,6 +453,13 @@ static int resume_common(struct device *dev, int event) | |||
454 | return retval; | 453 | return retval; |
455 | } | 454 | } |
456 | 455 | ||
456 | #ifdef CONFIG_PM_SLEEP | ||
457 | |||
458 | static int hcd_pci_suspend(struct device *dev) | ||
459 | { | ||
460 | return suspend_common(dev, device_may_wakeup(dev)); | ||
461 | } | ||
462 | |||
457 | static int hcd_pci_suspend_noirq(struct device *dev) | 463 | static int hcd_pci_suspend_noirq(struct device *dev) |
458 | { | 464 | { |
459 | struct pci_dev *pci_dev = to_pci_dev(dev); | 465 | struct pci_dev *pci_dev = to_pci_dev(dev); |
@@ -513,6 +519,46 @@ static int hcd_pci_restore(struct device *dev) | |||
513 | return resume_common(dev, PM_EVENT_RESTORE); | 519 | return resume_common(dev, PM_EVENT_RESTORE); |
514 | } | 520 | } |
515 | 521 | ||
522 | #else | ||
523 | |||
524 | #define hcd_pci_suspend NULL | ||
525 | #define hcd_pci_suspend_noirq NULL | ||
526 | #define hcd_pci_resume_noirq NULL | ||
527 | #define hcd_pci_resume NULL | ||
528 | #define hcd_pci_restore NULL | ||
529 | |||
530 | #endif /* CONFIG_PM_SLEEP */ | ||
531 | |||
532 | #ifdef CONFIG_PM_RUNTIME | ||
533 | |||
534 | static int hcd_pci_runtime_suspend(struct device *dev) | ||
535 | { | ||
536 | int retval; | ||
537 | |||
538 | retval = suspend_common(dev, true); | ||
539 | if (retval == 0) | ||
540 | powermac_set_asic(to_pci_dev(dev), 0); | ||
541 | dev_dbg(dev, "hcd_pci_runtime_suspend: %d\n", retval); | ||
542 | return retval; | ||
543 | } | ||
544 | |||
545 | static int hcd_pci_runtime_resume(struct device *dev) | ||
546 | { | ||
547 | int retval; | ||
548 | |||
549 | powermac_set_asic(to_pci_dev(dev), 1); | ||
550 | retval = resume_common(dev, PM_EVENT_AUTO_RESUME); | ||
551 | dev_dbg(dev, "hcd_pci_runtime_resume: %d\n", retval); | ||
552 | return retval; | ||
553 | } | ||
554 | |||
555 | #else | ||
556 | |||
557 | #define hcd_pci_runtime_suspend NULL | ||
558 | #define hcd_pci_runtime_resume NULL | ||
559 | |||
560 | #endif /* CONFIG_PM_RUNTIME */ | ||
561 | |||
516 | const struct dev_pm_ops usb_hcd_pci_pm_ops = { | 562 | const struct dev_pm_ops usb_hcd_pci_pm_ops = { |
517 | .suspend = hcd_pci_suspend, | 563 | .suspend = hcd_pci_suspend, |
518 | .suspend_noirq = hcd_pci_suspend_noirq, | 564 | .suspend_noirq = hcd_pci_suspend_noirq, |
@@ -526,7 +572,9 @@ const struct dev_pm_ops usb_hcd_pci_pm_ops = { | |||
526 | .poweroff_noirq = hcd_pci_suspend_noirq, | 572 | .poweroff_noirq = hcd_pci_suspend_noirq, |
527 | .restore_noirq = hcd_pci_resume_noirq, | 573 | .restore_noirq = hcd_pci_resume_noirq, |
528 | .restore = hcd_pci_restore, | 574 | .restore = hcd_pci_restore, |
575 | .runtime_suspend = hcd_pci_runtime_suspend, | ||
576 | .runtime_resume = hcd_pci_runtime_resume, | ||
529 | }; | 577 | }; |
530 | EXPORT_SYMBOL_GPL(usb_hcd_pci_pm_ops); | 578 | EXPORT_SYMBOL_GPL(usb_hcd_pci_pm_ops); |
531 | 579 | ||
532 | #endif /* CONFIG_PM_SLEEP */ | 580 | #endif /* CONFIG_PM_OPS */ |