aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2010-06-25 14:02:57 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2010-08-10 17:35:38 -0400
commit3da7cff4e79e4a7137b0dac1aaf6841b91bbff63 (patch)
treef41174132f184d036c4c1adf5676f2352681d0b6 /drivers/usb/core
parent0d436b425e07f9e4b0fe571cec061f5d136f1d8b (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.c76
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}
331EXPORT_SYMBOL_GPL(usb_hcd_pci_shutdown); 338EXPORT_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
336static void powermac_set_asic(struct pci_dev *pci_dev, int enable) 343static 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
369static int hcd_pci_suspend(struct device *dev) 376static 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
458static int hcd_pci_suspend(struct device *dev)
459{
460 return suspend_common(dev, device_may_wakeup(dev));
461}
462
457static int hcd_pci_suspend_noirq(struct device *dev) 463static 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
534static 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
545static 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
516const struct dev_pm_ops usb_hcd_pci_pm_ops = { 562const 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};
530EXPORT_SYMBOL_GPL(usb_hcd_pci_pm_ops); 578EXPORT_SYMBOL_GPL(usb_hcd_pci_pm_ops);
531 579
532#endif /* CONFIG_PM_SLEEP */ 580#endif /* CONFIG_PM_OPS */