aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2009-03-16 17:40:26 -0400
committerRafael J. Wysocki <rjw@sisk.pl>2009-03-30 15:46:55 -0400
commit46939f8b15e44f065d052e89ea4f2adc81fdc740 (patch)
treee8af8e84d9f84bbf08e24ceae46a36eaad6764f6
parent0128a89cf75124500b5b69f0c3c7b7c5aa60676f (diff)
PCI PM: Put devices into low power states during late suspend (rev. 2)
Once we have allowed timer interrupts to be enabled during the late phase of suspending devices, we are now able to use the generic pci_set_power_state() to put PCI devices into low power states at that time. We can also use some related platform callbacks, like the ones preparing devices for wake-up, during the late suspend. Doing this will allow us to avoid the race condition where a device using shared interrupts is put into a low power state with interrupts enabled and then an interrupt (for another device) comes in and confuses its driver. At the same time, devices that don't support the native PCI PM or that require some additional, platform-specific operations to be carried out to put them into low power states will be handled as appropriate. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Acked-by: Ingo Molnar <mingo@elte.hu> Acked-by: Jesse Barnes <jbarnes@virtuousgeek.org>
-rw-r--r--drivers/pci/pci-driver.c134
1 files changed, 81 insertions, 53 deletions
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 8395206d1aee..3c1831c82f5b 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -352,53 +352,60 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state)
352{ 352{
353 struct pci_dev * pci_dev = to_pci_dev(dev); 353 struct pci_dev * pci_dev = to_pci_dev(dev);
354 struct pci_driver * drv = pci_dev->driver; 354 struct pci_driver * drv = pci_dev->driver;
355 int error = 0; 355
356 pci_dev->state_saved = false;
356 357
357 if (drv && drv->suspend) { 358 if (drv && drv->suspend) {
358 pci_power_t prev = pci_dev->current_state; 359 pci_power_t prev = pci_dev->current_state;
359 360 int error;
360 pci_dev->state_saved = false;
361 361
362 error = drv->suspend(pci_dev, state); 362 error = drv->suspend(pci_dev, state);
363 suspend_report_result(drv->suspend, error); 363 suspend_report_result(drv->suspend, error);
364 if (error) 364 if (error)
365 return error; 365 return error;
366 366
367 if (pci_dev->state_saved) 367 if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0
368 goto Fixup;
369
370 if (pci_dev->current_state != PCI_D0
371 && pci_dev->current_state != PCI_UNKNOWN) { 368 && pci_dev->current_state != PCI_UNKNOWN) {
372 WARN_ONCE(pci_dev->current_state != prev, 369 WARN_ONCE(pci_dev->current_state != prev,
373 "PCI PM: Device state not saved by %pF\n", 370 "PCI PM: Device state not saved by %pF\n",
374 drv->suspend); 371 drv->suspend);
375 goto Fixup;
376 } 372 }
377 } 373 }
378 374
379 pci_save_state(pci_dev);
380 /*
381 * This is for compatibility with existing code with legacy PM support.
382 */
383 pci_pm_set_unknown_state(pci_dev);
384
385 Fixup:
386 pci_fixup_device(pci_fixup_suspend, pci_dev); 375 pci_fixup_device(pci_fixup_suspend, pci_dev);
387 376
388 return error; 377 return 0;
389} 378}
390 379
391static int pci_legacy_suspend_late(struct device *dev, pm_message_t state) 380static int pci_legacy_suspend_late(struct device *dev, pm_message_t state)
392{ 381{
393 struct pci_dev * pci_dev = to_pci_dev(dev); 382 struct pci_dev * pci_dev = to_pci_dev(dev);
394 struct pci_driver * drv = pci_dev->driver; 383 struct pci_driver * drv = pci_dev->driver;
395 int error = 0;
396 384
397 if (drv && drv->suspend_late) { 385 if (drv && drv->suspend_late) {
386 pci_power_t prev = pci_dev->current_state;
387 int error;
388
398 error = drv->suspend_late(pci_dev, state); 389 error = drv->suspend_late(pci_dev, state);
399 suspend_report_result(drv->suspend_late, error); 390 suspend_report_result(drv->suspend_late, error);
391 if (error)
392 return error;
393
394 if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0
395 && pci_dev->current_state != PCI_UNKNOWN) {
396 WARN_ONCE(pci_dev->current_state != prev,
397 "PCI PM: Device state not saved by %pF\n",
398 drv->suspend_late);
399 return 0;
400 }
400 } 401 }
401 return error; 402
403 if (!pci_dev->state_saved)
404 pci_save_state(pci_dev);
405
406 pci_pm_set_unknown_state(pci_dev);
407
408 return 0;
402} 409}
403 410
404static int pci_legacy_resume_early(struct device *dev) 411static int pci_legacy_resume_early(struct device *dev)
@@ -460,7 +467,6 @@ static void pci_pm_default_suspend(struct pci_dev *pci_dev)
460 /* Disable non-bridge devices without PM support */ 467 /* Disable non-bridge devices without PM support */
461 if (!pci_is_bridge(pci_dev)) 468 if (!pci_is_bridge(pci_dev))
462 pci_disable_enabled_device(pci_dev); 469 pci_disable_enabled_device(pci_dev);
463 pci_save_state(pci_dev);
464} 470}
465 471
466static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev) 472static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev)
@@ -526,24 +532,14 @@ static int pci_pm_suspend(struct device *dev)
526 if (error) 532 if (error)
527 return error; 533 return error;
528 534
529 if (pci_dev->state_saved) 535 if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0
530 goto Fixup;
531
532 if (pci_dev->current_state != PCI_D0
533 && pci_dev->current_state != PCI_UNKNOWN) { 536 && pci_dev->current_state != PCI_UNKNOWN) {
534 WARN_ONCE(pci_dev->current_state != prev, 537 WARN_ONCE(pci_dev->current_state != prev,
535 "PCI PM: State of device not saved by %pF\n", 538 "PCI PM: State of device not saved by %pF\n",
536 pm->suspend); 539 pm->suspend);
537 goto Fixup;
538 } 540 }
539 } 541 }
540 542
541 if (!pci_dev->state_saved) {
542 pci_save_state(pci_dev);
543 if (!pci_is_bridge(pci_dev))
544 pci_prepare_to_sleep(pci_dev);
545 }
546
547 Fixup: 543 Fixup:
548 pci_fixup_device(pci_fixup_suspend, pci_dev); 544 pci_fixup_device(pci_fixup_suspend, pci_dev);
549 545
@@ -553,21 +549,41 @@ static int pci_pm_suspend(struct device *dev)
553static int pci_pm_suspend_noirq(struct device *dev) 549static int pci_pm_suspend_noirq(struct device *dev)
554{ 550{
555 struct pci_dev *pci_dev = to_pci_dev(dev); 551 struct pci_dev *pci_dev = to_pci_dev(dev);
556 struct device_driver *drv = dev->driver; 552 struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
557 int error = 0;
558 553
559 if (pci_has_legacy_pm_support(pci_dev)) 554 if (pci_has_legacy_pm_support(pci_dev))
560 return pci_legacy_suspend_late(dev, PMSG_SUSPEND); 555 return pci_legacy_suspend_late(dev, PMSG_SUSPEND);
561 556
562 if (drv && drv->pm && drv->pm->suspend_noirq) { 557 if (!pm)
563 error = drv->pm->suspend_noirq(dev); 558 return 0;
564 suspend_report_result(drv->pm->suspend_noirq, error); 559
560 if (pm->suspend_noirq) {
561 pci_power_t prev = pci_dev->current_state;
562 int error;
563
564 error = pm->suspend_noirq(dev);
565 suspend_report_result(pm->suspend_noirq, error);
566 if (error)
567 return error;
568
569 if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0
570 && pci_dev->current_state != PCI_UNKNOWN) {
571 WARN_ONCE(pci_dev->current_state != prev,
572 "PCI PM: State of device not saved by %pF\n",
573 pm->suspend_noirq);
574 return 0;
575 }
565 } 576 }
566 577
567 if (!error) 578 if (!pci_dev->state_saved) {
568 pci_pm_set_unknown_state(pci_dev); 579 pci_save_state(pci_dev);
580 if (!pci_is_bridge(pci_dev))
581 pci_prepare_to_sleep(pci_dev);
582 }
569 583
570 return error; 584 pci_pm_set_unknown_state(pci_dev);
585
586 return 0;
571} 587}
572 588
573static int pci_pm_resume_noirq(struct device *dev) 589static int pci_pm_resume_noirq(struct device *dev)
@@ -650,9 +666,6 @@ static int pci_pm_freeze(struct device *dev)
650 return error; 666 return error;
651 } 667 }
652 668
653 if (!pci_dev->state_saved)
654 pci_save_state(pci_dev);
655
656 return 0; 669 return 0;
657} 670}
658 671
@@ -660,20 +673,25 @@ static int pci_pm_freeze_noirq(struct device *dev)
660{ 673{
661 struct pci_dev *pci_dev = to_pci_dev(dev); 674 struct pci_dev *pci_dev = to_pci_dev(dev);
662 struct device_driver *drv = dev->driver; 675 struct device_driver *drv = dev->driver;
663 int error = 0;
664 676
665 if (pci_has_legacy_pm_support(pci_dev)) 677 if (pci_has_legacy_pm_support(pci_dev))
666 return pci_legacy_suspend_late(dev, PMSG_FREEZE); 678 return pci_legacy_suspend_late(dev, PMSG_FREEZE);
667 679
668 if (drv && drv->pm && drv->pm->freeze_noirq) { 680 if (drv && drv->pm && drv->pm->freeze_noirq) {
681 int error;
682
669 error = drv->pm->freeze_noirq(dev); 683 error = drv->pm->freeze_noirq(dev);
670 suspend_report_result(drv->pm->freeze_noirq, error); 684 suspend_report_result(drv->pm->freeze_noirq, error);
685 if (error)
686 return error;
671 } 687 }
672 688
673 if (!error) 689 if (!pci_dev->state_saved)
674 pci_pm_set_unknown_state(pci_dev); 690 pci_save_state(pci_dev);
675 691
676 return error; 692 pci_pm_set_unknown_state(pci_dev);
693
694 return 0;
677} 695}
678 696
679static int pci_pm_thaw_noirq(struct device *dev) 697static int pci_pm_thaw_noirq(struct device *dev)
@@ -716,7 +734,6 @@ static int pci_pm_poweroff(struct device *dev)
716{ 734{
717 struct pci_dev *pci_dev = to_pci_dev(dev); 735 struct pci_dev *pci_dev = to_pci_dev(dev);
718 struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; 736 struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
719 int error = 0;
720 737
721 if (pci_has_legacy_pm_support(pci_dev)) 738 if (pci_has_legacy_pm_support(pci_dev))
722 return pci_legacy_suspend(dev, PMSG_HIBERNATE); 739 return pci_legacy_suspend(dev, PMSG_HIBERNATE);
@@ -729,33 +746,44 @@ static int pci_pm_poweroff(struct device *dev)
729 pci_dev->state_saved = false; 746 pci_dev->state_saved = false;
730 747
731 if (pm->poweroff) { 748 if (pm->poweroff) {
749 int error;
750
732 error = pm->poweroff(dev); 751 error = pm->poweroff(dev);
733 suspend_report_result(pm->poweroff, error); 752 suspend_report_result(pm->poweroff, error);
753 if (error)
754 return error;
734 } 755 }
735 756
736 if (!pci_dev->state_saved && !pci_is_bridge(pci_dev))
737 pci_prepare_to_sleep(pci_dev);
738
739 Fixup: 757 Fixup:
740 pci_fixup_device(pci_fixup_suspend, pci_dev); 758 pci_fixup_device(pci_fixup_suspend, pci_dev);
741 759
742 return error; 760 return 0;
743} 761}
744 762
745static int pci_pm_poweroff_noirq(struct device *dev) 763static int pci_pm_poweroff_noirq(struct device *dev)
746{ 764{
765 struct pci_dev *pci_dev = to_pci_dev(dev);
747 struct device_driver *drv = dev->driver; 766 struct device_driver *drv = dev->driver;
748 int error = 0;
749 767
750 if (pci_has_legacy_pm_support(to_pci_dev(dev))) 768 if (pci_has_legacy_pm_support(to_pci_dev(dev)))
751 return pci_legacy_suspend_late(dev, PMSG_HIBERNATE); 769 return pci_legacy_suspend_late(dev, PMSG_HIBERNATE);
752 770
753 if (drv && drv->pm && drv->pm->poweroff_noirq) { 771 if (!drv || !drv->pm)
772 return 0;
773
774 if (drv->pm->poweroff_noirq) {
775 int error;
776
754 error = drv->pm->poweroff_noirq(dev); 777 error = drv->pm->poweroff_noirq(dev);
755 suspend_report_result(drv->pm->poweroff_noirq, error); 778 suspend_report_result(drv->pm->poweroff_noirq, error);
779 if (error)
780 return error;
756 } 781 }
757 782
758 return error; 783 if (!pci_dev->state_saved && !pci_is_bridge(pci_dev))
784 pci_prepare_to_sleep(pci_dev);
785
786 return 0;
759} 787}
760 788
761static int pci_pm_restore_noirq(struct device *dev) 789static int pci_pm_restore_noirq(struct device *dev)