diff options
Diffstat (limited to 'drivers/pci/pci-driver.c')
-rw-r--r-- | drivers/pci/pci-driver.c | 177 |
1 files changed, 112 insertions, 65 deletions
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index b522f883d674..16240390bcde 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c | |||
@@ -351,53 +351,60 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state) | |||
351 | { | 351 | { |
352 | struct pci_dev * pci_dev = to_pci_dev(dev); | 352 | struct pci_dev * pci_dev = to_pci_dev(dev); |
353 | struct pci_driver * drv = pci_dev->driver; | 353 | struct pci_driver * drv = pci_dev->driver; |
354 | int i = 0; | 354 | |
355 | pci_dev->state_saved = false; | ||
355 | 356 | ||
356 | if (drv && drv->suspend) { | 357 | if (drv && drv->suspend) { |
357 | pci_power_t prev = pci_dev->current_state; | 358 | pci_power_t prev = pci_dev->current_state; |
359 | int error; | ||
358 | 360 | ||
359 | pci_dev->state_saved = false; | 361 | error = drv->suspend(pci_dev, state); |
360 | 362 | suspend_report_result(drv->suspend, error); | |
361 | i = drv->suspend(pci_dev, state); | 363 | if (error) |
362 | suspend_report_result(drv->suspend, i); | 364 | return error; |
363 | if (i) | ||
364 | return i; | ||
365 | |||
366 | if (pci_dev->state_saved) | ||
367 | goto Fixup; | ||
368 | 365 | ||
369 | if (pci_dev->current_state != PCI_D0 | 366 | if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0 |
370 | && pci_dev->current_state != PCI_UNKNOWN) { | 367 | && pci_dev->current_state != PCI_UNKNOWN) { |
371 | WARN_ONCE(pci_dev->current_state != prev, | 368 | WARN_ONCE(pci_dev->current_state != prev, |
372 | "PCI PM: Device state not saved by %pF\n", | 369 | "PCI PM: Device state not saved by %pF\n", |
373 | drv->suspend); | 370 | drv->suspend); |
374 | goto Fixup; | ||
375 | } | 371 | } |
376 | } | 372 | } |
377 | 373 | ||
378 | pci_save_state(pci_dev); | ||
379 | /* | ||
380 | * This is for compatibility with existing code with legacy PM support. | ||
381 | */ | ||
382 | pci_pm_set_unknown_state(pci_dev); | ||
383 | |||
384 | Fixup: | ||
385 | pci_fixup_device(pci_fixup_suspend, pci_dev); | 374 | pci_fixup_device(pci_fixup_suspend, pci_dev); |
386 | 375 | ||
387 | return i; | 376 | return 0; |
388 | } | 377 | } |
389 | 378 | ||
390 | static int pci_legacy_suspend_late(struct device *dev, pm_message_t state) | 379 | static int pci_legacy_suspend_late(struct device *dev, pm_message_t state) |
391 | { | 380 | { |
392 | struct pci_dev * pci_dev = to_pci_dev(dev); | 381 | struct pci_dev * pci_dev = to_pci_dev(dev); |
393 | struct pci_driver * drv = pci_dev->driver; | 382 | struct pci_driver * drv = pci_dev->driver; |
394 | int i = 0; | ||
395 | 383 | ||
396 | if (drv && drv->suspend_late) { | 384 | if (drv && drv->suspend_late) { |
397 | i = drv->suspend_late(pci_dev, state); | 385 | pci_power_t prev = pci_dev->current_state; |
398 | suspend_report_result(drv->suspend_late, i); | 386 | int error; |
387 | |||
388 | error = drv->suspend_late(pci_dev, state); | ||
389 | suspend_report_result(drv->suspend_late, error); | ||
390 | if (error) | ||
391 | return error; | ||
392 | |||
393 | if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0 | ||
394 | && pci_dev->current_state != PCI_UNKNOWN) { | ||
395 | WARN_ONCE(pci_dev->current_state != prev, | ||
396 | "PCI PM: Device state not saved by %pF\n", | ||
397 | drv->suspend_late); | ||
398 | return 0; | ||
399 | } | ||
399 | } | 400 | } |
400 | return i; | 401 | |
402 | if (!pci_dev->state_saved) | ||
403 | pci_save_state(pci_dev); | ||
404 | |||
405 | pci_pm_set_unknown_state(pci_dev); | ||
406 | |||
407 | return 0; | ||
401 | } | 408 | } |
402 | 409 | ||
403 | static int pci_legacy_resume_early(struct device *dev) | 410 | static int pci_legacy_resume_early(struct device *dev) |
@@ -422,6 +429,23 @@ static int pci_legacy_resume(struct device *dev) | |||
422 | 429 | ||
423 | /* Auxiliary functions used by the new power management framework */ | 430 | /* Auxiliary functions used by the new power management framework */ |
424 | 431 | ||
432 | /** | ||
433 | * pci_restore_standard_config - restore standard config registers of PCI device | ||
434 | * @pci_dev: PCI device to handle | ||
435 | */ | ||
436 | static int pci_restore_standard_config(struct pci_dev *pci_dev) | ||
437 | { | ||
438 | pci_update_current_state(pci_dev, PCI_UNKNOWN); | ||
439 | |||
440 | if (pci_dev->current_state != PCI_D0) { | ||
441 | int error = pci_set_power_state(pci_dev, PCI_D0); | ||
442 | if (error) | ||
443 | return error; | ||
444 | } | ||
445 | |||
446 | return pci_dev->state_saved ? pci_restore_state(pci_dev) : 0; | ||
447 | } | ||
448 | |||
425 | static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev) | 449 | static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev) |
426 | { | 450 | { |
427 | pci_restore_standard_config(pci_dev); | 451 | pci_restore_standard_config(pci_dev); |
@@ -442,7 +466,6 @@ static void pci_pm_default_suspend(struct pci_dev *pci_dev) | |||
442 | /* Disable non-bridge devices without PM support */ | 466 | /* Disable non-bridge devices without PM support */ |
443 | if (!pci_is_bridge(pci_dev)) | 467 | if (!pci_is_bridge(pci_dev)) |
444 | pci_disable_enabled_device(pci_dev); | 468 | pci_disable_enabled_device(pci_dev); |
445 | pci_save_state(pci_dev); | ||
446 | } | 469 | } |
447 | 470 | ||
448 | static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev) | 471 | static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev) |
@@ -492,13 +515,13 @@ static int pci_pm_suspend(struct device *dev) | |||
492 | if (pci_has_legacy_pm_support(pci_dev)) | 515 | if (pci_has_legacy_pm_support(pci_dev)) |
493 | return pci_legacy_suspend(dev, PMSG_SUSPEND); | 516 | return pci_legacy_suspend(dev, PMSG_SUSPEND); |
494 | 517 | ||
518 | pci_dev->state_saved = false; | ||
519 | |||
495 | if (!pm) { | 520 | if (!pm) { |
496 | pci_pm_default_suspend(pci_dev); | 521 | pci_pm_default_suspend(pci_dev); |
497 | goto Fixup; | 522 | goto Fixup; |
498 | } | 523 | } |
499 | 524 | ||
500 | pci_dev->state_saved = false; | ||
501 | |||
502 | if (pm->suspend) { | 525 | if (pm->suspend) { |
503 | pci_power_t prev = pci_dev->current_state; | 526 | pci_power_t prev = pci_dev->current_state; |
504 | int error; | 527 | int error; |
@@ -508,24 +531,14 @@ static int pci_pm_suspend(struct device *dev) | |||
508 | if (error) | 531 | if (error) |
509 | return error; | 532 | return error; |
510 | 533 | ||
511 | if (pci_dev->state_saved) | 534 | if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0 |
512 | goto Fixup; | ||
513 | |||
514 | if (pci_dev->current_state != PCI_D0 | ||
515 | && pci_dev->current_state != PCI_UNKNOWN) { | 535 | && pci_dev->current_state != PCI_UNKNOWN) { |
516 | WARN_ONCE(pci_dev->current_state != prev, | 536 | WARN_ONCE(pci_dev->current_state != prev, |
517 | "PCI PM: State of device not saved by %pF\n", | 537 | "PCI PM: State of device not saved by %pF\n", |
518 | pm->suspend); | 538 | pm->suspend); |
519 | goto Fixup; | ||
520 | } | 539 | } |
521 | } | 540 | } |
522 | 541 | ||
523 | if (!pci_dev->state_saved) { | ||
524 | pci_save_state(pci_dev); | ||
525 | if (!pci_is_bridge(pci_dev)) | ||
526 | pci_prepare_to_sleep(pci_dev); | ||
527 | } | ||
528 | |||
529 | Fixup: | 542 | Fixup: |
530 | pci_fixup_device(pci_fixup_suspend, pci_dev); | 543 | pci_fixup_device(pci_fixup_suspend, pci_dev); |
531 | 544 | ||
@@ -535,21 +548,43 @@ static int pci_pm_suspend(struct device *dev) | |||
535 | static int pci_pm_suspend_noirq(struct device *dev) | 548 | static int pci_pm_suspend_noirq(struct device *dev) |
536 | { | 549 | { |
537 | struct pci_dev *pci_dev = to_pci_dev(dev); | 550 | struct pci_dev *pci_dev = to_pci_dev(dev); |
538 | struct device_driver *drv = dev->driver; | 551 | struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; |
539 | int error = 0; | ||
540 | 552 | ||
541 | if (pci_has_legacy_pm_support(pci_dev)) | 553 | if (pci_has_legacy_pm_support(pci_dev)) |
542 | return pci_legacy_suspend_late(dev, PMSG_SUSPEND); | 554 | return pci_legacy_suspend_late(dev, PMSG_SUSPEND); |
543 | 555 | ||
544 | if (drv && drv->pm && drv->pm->suspend_noirq) { | 556 | if (!pm) { |
545 | error = drv->pm->suspend_noirq(dev); | 557 | pci_save_state(pci_dev); |
546 | suspend_report_result(drv->pm->suspend_noirq, error); | 558 | return 0; |
547 | } | 559 | } |
548 | 560 | ||
549 | if (!error) | 561 | if (pm->suspend_noirq) { |
550 | pci_pm_set_unknown_state(pci_dev); | 562 | pci_power_t prev = pci_dev->current_state; |
563 | int error; | ||
551 | 564 | ||
552 | return error; | 565 | error = pm->suspend_noirq(dev); |
566 | suspend_report_result(pm->suspend_noirq, error); | ||
567 | if (error) | ||
568 | return error; | ||
569 | |||
570 | if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0 | ||
571 | && pci_dev->current_state != PCI_UNKNOWN) { | ||
572 | WARN_ONCE(pci_dev->current_state != prev, | ||
573 | "PCI PM: State of device not saved by %pF\n", | ||
574 | pm->suspend_noirq); | ||
575 | return 0; | ||
576 | } | ||
577 | } | ||
578 | |||
579 | if (!pci_dev->state_saved) { | ||
580 | pci_save_state(pci_dev); | ||
581 | if (!pci_is_bridge(pci_dev)) | ||
582 | pci_prepare_to_sleep(pci_dev); | ||
583 | } | ||
584 | |||
585 | pci_pm_set_unknown_state(pci_dev); | ||
586 | |||
587 | return 0; | ||
553 | } | 588 | } |
554 | 589 | ||
555 | static int pci_pm_resume_noirq(struct device *dev) | 590 | static int pci_pm_resume_noirq(struct device *dev) |
@@ -616,13 +651,13 @@ static int pci_pm_freeze(struct device *dev) | |||
616 | if (pci_has_legacy_pm_support(pci_dev)) | 651 | if (pci_has_legacy_pm_support(pci_dev)) |
617 | return pci_legacy_suspend(dev, PMSG_FREEZE); | 652 | return pci_legacy_suspend(dev, PMSG_FREEZE); |
618 | 653 | ||
654 | pci_dev->state_saved = false; | ||
655 | |||
619 | if (!pm) { | 656 | if (!pm) { |
620 | pci_pm_default_suspend(pci_dev); | 657 | pci_pm_default_suspend(pci_dev); |
621 | return 0; | 658 | return 0; |
622 | } | 659 | } |
623 | 660 | ||
624 | pci_dev->state_saved = false; | ||
625 | |||
626 | if (pm->freeze) { | 661 | if (pm->freeze) { |
627 | int error; | 662 | int error; |
628 | 663 | ||
@@ -632,9 +667,6 @@ static int pci_pm_freeze(struct device *dev) | |||
632 | return error; | 667 | return error; |
633 | } | 668 | } |
634 | 669 | ||
635 | if (!pci_dev->state_saved) | ||
636 | pci_save_state(pci_dev); | ||
637 | |||
638 | return 0; | 670 | return 0; |
639 | } | 671 | } |
640 | 672 | ||
@@ -642,20 +674,25 @@ static int pci_pm_freeze_noirq(struct device *dev) | |||
642 | { | 674 | { |
643 | struct pci_dev *pci_dev = to_pci_dev(dev); | 675 | struct pci_dev *pci_dev = to_pci_dev(dev); |
644 | struct device_driver *drv = dev->driver; | 676 | struct device_driver *drv = dev->driver; |
645 | int error = 0; | ||
646 | 677 | ||
647 | if (pci_has_legacy_pm_support(pci_dev)) | 678 | if (pci_has_legacy_pm_support(pci_dev)) |
648 | return pci_legacy_suspend_late(dev, PMSG_FREEZE); | 679 | return pci_legacy_suspend_late(dev, PMSG_FREEZE); |
649 | 680 | ||
650 | if (drv && drv->pm && drv->pm->freeze_noirq) { | 681 | if (drv && drv->pm && drv->pm->freeze_noirq) { |
682 | int error; | ||
683 | |||
651 | error = drv->pm->freeze_noirq(dev); | 684 | error = drv->pm->freeze_noirq(dev); |
652 | suspend_report_result(drv->pm->freeze_noirq, error); | 685 | suspend_report_result(drv->pm->freeze_noirq, error); |
686 | if (error) | ||
687 | return error; | ||
653 | } | 688 | } |
654 | 689 | ||
655 | if (!error) | 690 | if (!pci_dev->state_saved) |
656 | pci_pm_set_unknown_state(pci_dev); | 691 | pci_save_state(pci_dev); |
657 | 692 | ||
658 | return error; | 693 | pci_pm_set_unknown_state(pci_dev); |
694 | |||
695 | return 0; | ||
659 | } | 696 | } |
660 | 697 | ||
661 | static int pci_pm_thaw_noirq(struct device *dev) | 698 | static int pci_pm_thaw_noirq(struct device *dev) |
@@ -698,46 +735,56 @@ static int pci_pm_poweroff(struct device *dev) | |||
698 | { | 735 | { |
699 | struct pci_dev *pci_dev = to_pci_dev(dev); | 736 | struct pci_dev *pci_dev = to_pci_dev(dev); |
700 | struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; | 737 | struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; |
701 | int error = 0; | ||
702 | 738 | ||
703 | if (pci_has_legacy_pm_support(pci_dev)) | 739 | if (pci_has_legacy_pm_support(pci_dev)) |
704 | return pci_legacy_suspend(dev, PMSG_HIBERNATE); | 740 | return pci_legacy_suspend(dev, PMSG_HIBERNATE); |
705 | 741 | ||
742 | pci_dev->state_saved = false; | ||
743 | |||
706 | if (!pm) { | 744 | if (!pm) { |
707 | pci_pm_default_suspend(pci_dev); | 745 | pci_pm_default_suspend(pci_dev); |
708 | goto Fixup; | 746 | goto Fixup; |
709 | } | 747 | } |
710 | 748 | ||
711 | pci_dev->state_saved = false; | ||
712 | |||
713 | if (pm->poweroff) { | 749 | if (pm->poweroff) { |
750 | int error; | ||
751 | |||
714 | error = pm->poweroff(dev); | 752 | error = pm->poweroff(dev); |
715 | suspend_report_result(pm->poweroff, error); | 753 | suspend_report_result(pm->poweroff, error); |
754 | if (error) | ||
755 | return error; | ||
716 | } | 756 | } |
717 | 757 | ||
718 | if (!pci_dev->state_saved && !pci_is_bridge(pci_dev)) | ||
719 | pci_prepare_to_sleep(pci_dev); | ||
720 | |||
721 | Fixup: | 758 | Fixup: |
722 | pci_fixup_device(pci_fixup_suspend, pci_dev); | 759 | pci_fixup_device(pci_fixup_suspend, pci_dev); |
723 | 760 | ||
724 | return error; | 761 | return 0; |
725 | } | 762 | } |
726 | 763 | ||
727 | static int pci_pm_poweroff_noirq(struct device *dev) | 764 | static int pci_pm_poweroff_noirq(struct device *dev) |
728 | { | 765 | { |
766 | struct pci_dev *pci_dev = to_pci_dev(dev); | ||
729 | struct device_driver *drv = dev->driver; | 767 | struct device_driver *drv = dev->driver; |
730 | int error = 0; | ||
731 | 768 | ||
732 | if (pci_has_legacy_pm_support(to_pci_dev(dev))) | 769 | if (pci_has_legacy_pm_support(to_pci_dev(dev))) |
733 | return pci_legacy_suspend_late(dev, PMSG_HIBERNATE); | 770 | return pci_legacy_suspend_late(dev, PMSG_HIBERNATE); |
734 | 771 | ||
735 | if (drv && drv->pm && drv->pm->poweroff_noirq) { | 772 | if (!drv || !drv->pm) |
773 | return 0; | ||
774 | |||
775 | if (drv->pm->poweroff_noirq) { | ||
776 | int error; | ||
777 | |||
736 | error = drv->pm->poweroff_noirq(dev); | 778 | error = drv->pm->poweroff_noirq(dev); |
737 | suspend_report_result(drv->pm->poweroff_noirq, error); | 779 | suspend_report_result(drv->pm->poweroff_noirq, error); |
780 | if (error) | ||
781 | return error; | ||
738 | } | 782 | } |
739 | 783 | ||
740 | return error; | 784 | if (!pci_dev->state_saved && !pci_is_bridge(pci_dev)) |
785 | pci_prepare_to_sleep(pci_dev); | ||
786 | |||
787 | return 0; | ||
741 | } | 788 | } |
742 | 789 | ||
743 | static int pci_pm_restore_noirq(struct device *dev) | 790 | static int pci_pm_restore_noirq(struct device *dev) |