diff options
Diffstat (limited to 'drivers/pci/pci-driver.c')
-rw-r--r-- | drivers/pci/pci-driver.c | 134 |
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 | ||
391 | static int pci_legacy_suspend_late(struct device *dev, pm_message_t state) | 380 | static 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 | ||
404 | static int pci_legacy_resume_early(struct device *dev) | 411 | static 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 | ||
466 | static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev) | 472 | static 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) | |||
553 | static int pci_pm_suspend_noirq(struct device *dev) | 549 | static 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 | ||
573 | static int pci_pm_resume_noirq(struct device *dev) | 589 | static 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 | ||
679 | static int pci_pm_thaw_noirq(struct device *dev) | 697 | static 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 | ||
745 | static int pci_pm_poweroff_noirq(struct device *dev) | 763 | static 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 | ||
761 | static int pci_pm_restore_noirq(struct device *dev) | 789 | static int pci_pm_restore_noirq(struct device *dev) |