diff options
Diffstat (limited to 'drivers/pci/pci-driver.c')
-rw-r--r-- | drivers/pci/pci-driver.c | 164 |
1 files changed, 105 insertions, 59 deletions
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index ab1d615425a8..93eac1423585 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c | |||
@@ -355,6 +355,8 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state) | |||
355 | int i = 0; | 355 | int i = 0; |
356 | 356 | ||
357 | if (drv && drv->suspend) { | 357 | if (drv && drv->suspend) { |
358 | pci_power_t prev = pci_dev->current_state; | ||
359 | |||
358 | pci_dev->state_saved = false; | 360 | pci_dev->state_saved = false; |
359 | 361 | ||
360 | i = drv->suspend(pci_dev, state); | 362 | i = drv->suspend(pci_dev, state); |
@@ -365,12 +367,16 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state) | |||
365 | if (pci_dev->state_saved) | 367 | if (pci_dev->state_saved) |
366 | goto Fixup; | 368 | goto Fixup; |
367 | 369 | ||
368 | if (WARN_ON_ONCE(pci_dev->current_state != PCI_D0)) | 370 | if (pci_dev->current_state != PCI_D0 |
371 | && pci_dev->current_state != PCI_UNKNOWN) { | ||
372 | WARN_ONCE(pci_dev->current_state != prev, | ||
373 | "PCI PM: Device state not saved by %pF\n", | ||
374 | drv->suspend); | ||
369 | goto Fixup; | 375 | goto Fixup; |
376 | } | ||
370 | } | 377 | } |
371 | 378 | ||
372 | pci_save_state(pci_dev); | 379 | pci_save_state(pci_dev); |
373 | pci_dev->state_saved = true; | ||
374 | /* | 380 | /* |
375 | * This is for compatibility with existing code with legacy PM support. | 381 | * This is for compatibility with existing code with legacy PM support. |
376 | */ | 382 | */ |
@@ -424,35 +430,20 @@ static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev) | |||
424 | pci_fixup_device(pci_fixup_resume_early, pci_dev); | 430 | pci_fixup_device(pci_fixup_resume_early, pci_dev); |
425 | } | 431 | } |
426 | 432 | ||
427 | static int pci_pm_default_resume(struct pci_dev *pci_dev) | 433 | static void pci_pm_default_resume(struct pci_dev *pci_dev) |
428 | { | 434 | { |
429 | pci_fixup_device(pci_fixup_resume, pci_dev); | 435 | pci_fixup_device(pci_fixup_resume, pci_dev); |
430 | 436 | ||
431 | if (!pci_is_bridge(pci_dev)) | 437 | if (!pci_is_bridge(pci_dev)) |
432 | pci_enable_wake(pci_dev, PCI_D0, false); | 438 | pci_enable_wake(pci_dev, PCI_D0, false); |
433 | |||
434 | return pci_pm_reenable_device(pci_dev); | ||
435 | } | ||
436 | |||
437 | static void pci_pm_default_suspend_generic(struct pci_dev *pci_dev) | ||
438 | { | ||
439 | /* If device is enabled at this point, disable it */ | ||
440 | pci_disable_enabled_device(pci_dev); | ||
441 | /* | ||
442 | * Save state with interrupts enabled, because in principle the bus the | ||
443 | * device is on may be put into a low power state after this code runs. | ||
444 | */ | ||
445 | pci_save_state(pci_dev); | ||
446 | } | 439 | } |
447 | 440 | ||
448 | static void pci_pm_default_suspend(struct pci_dev *pci_dev) | 441 | static void pci_pm_default_suspend(struct pci_dev *pci_dev) |
449 | { | 442 | { |
450 | pci_pm_default_suspend_generic(pci_dev); | 443 | /* Disable non-bridge devices without PM support */ |
451 | |||
452 | if (!pci_is_bridge(pci_dev)) | 444 | if (!pci_is_bridge(pci_dev)) |
453 | pci_prepare_to_sleep(pci_dev); | 445 | pci_disable_enabled_device(pci_dev); |
454 | 446 | pci_save_state(pci_dev); | |
455 | pci_fixup_device(pci_fixup_suspend, pci_dev); | ||
456 | } | 447 | } |
457 | 448 | ||
458 | static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev) | 449 | static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev) |
@@ -497,21 +488,49 @@ static void pci_pm_complete(struct device *dev) | |||
497 | static int pci_pm_suspend(struct device *dev) | 488 | static int pci_pm_suspend(struct device *dev) |
498 | { | 489 | { |
499 | struct pci_dev *pci_dev = to_pci_dev(dev); | 490 | struct pci_dev *pci_dev = to_pci_dev(dev); |
500 | struct device_driver *drv = dev->driver; | 491 | struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; |
501 | int error = 0; | ||
502 | 492 | ||
503 | if (pci_has_legacy_pm_support(pci_dev)) | 493 | if (pci_has_legacy_pm_support(pci_dev)) |
504 | return pci_legacy_suspend(dev, PMSG_SUSPEND); | 494 | return pci_legacy_suspend(dev, PMSG_SUSPEND); |
505 | 495 | ||
506 | if (drv && drv->pm && drv->pm->suspend) { | 496 | if (!pm) { |
507 | error = drv->pm->suspend(dev); | 497 | pci_pm_default_suspend(pci_dev); |
508 | suspend_report_result(drv->pm->suspend, error); | 498 | goto Fixup; |
509 | } | 499 | } |
510 | 500 | ||
511 | if (!error) | 501 | pci_dev->state_saved = false; |
512 | pci_pm_default_suspend(pci_dev); | ||
513 | 502 | ||
514 | return error; | 503 | if (pm->suspend) { |
504 | pci_power_t prev = pci_dev->current_state; | ||
505 | int error; | ||
506 | |||
507 | error = pm->suspend(dev); | ||
508 | suspend_report_result(pm->suspend, error); | ||
509 | if (error) | ||
510 | return error; | ||
511 | |||
512 | if (pci_dev->state_saved) | ||
513 | goto Fixup; | ||
514 | |||
515 | if (pci_dev->current_state != PCI_D0 | ||
516 | && pci_dev->current_state != PCI_UNKNOWN) { | ||
517 | WARN_ONCE(pci_dev->current_state != prev, | ||
518 | "PCI PM: State of device not saved by %pF\n", | ||
519 | pm->suspend); | ||
520 | goto Fixup; | ||
521 | } | ||
522 | } | ||
523 | |||
524 | if (!pci_dev->state_saved) { | ||
525 | pci_save_state(pci_dev); | ||
526 | if (!pci_is_bridge(pci_dev)) | ||
527 | pci_prepare_to_sleep(pci_dev); | ||
528 | } | ||
529 | |||
530 | Fixup: | ||
531 | pci_fixup_device(pci_fixup_suspend, pci_dev); | ||
532 | |||
533 | return 0; | ||
515 | } | 534 | } |
516 | 535 | ||
517 | static int pci_pm_suspend_noirq(struct device *dev) | 536 | static int pci_pm_suspend_noirq(struct device *dev) |
@@ -554,7 +573,7 @@ static int pci_pm_resume_noirq(struct device *dev) | |||
554 | static int pci_pm_resume(struct device *dev) | 573 | static int pci_pm_resume(struct device *dev) |
555 | { | 574 | { |
556 | struct pci_dev *pci_dev = to_pci_dev(dev); | 575 | struct pci_dev *pci_dev = to_pci_dev(dev); |
557 | struct device_driver *drv = dev->driver; | 576 | struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; |
558 | int error = 0; | 577 | int error = 0; |
559 | 578 | ||
560 | /* | 579 | /* |
@@ -567,12 +586,16 @@ static int pci_pm_resume(struct device *dev) | |||
567 | if (pci_has_legacy_pm_support(pci_dev)) | 586 | if (pci_has_legacy_pm_support(pci_dev)) |
568 | return pci_legacy_resume(dev); | 587 | return pci_legacy_resume(dev); |
569 | 588 | ||
570 | error = pci_pm_default_resume(pci_dev); | 589 | pci_pm_default_resume(pci_dev); |
571 | 590 | ||
572 | if (!error && drv && drv->pm && drv->pm->resume) | 591 | if (pm) { |
573 | error = drv->pm->resume(dev); | 592 | if (pm->resume) |
593 | error = pm->resume(dev); | ||
594 | } else { | ||
595 | pci_pm_reenable_device(pci_dev); | ||
596 | } | ||
574 | 597 | ||
575 | return error; | 598 | return 0; |
576 | } | 599 | } |
577 | 600 | ||
578 | #else /* !CONFIG_SUSPEND */ | 601 | #else /* !CONFIG_SUSPEND */ |
@@ -589,21 +612,31 @@ static int pci_pm_resume(struct device *dev) | |||
589 | static int pci_pm_freeze(struct device *dev) | 612 | static int pci_pm_freeze(struct device *dev) |
590 | { | 613 | { |
591 | struct pci_dev *pci_dev = to_pci_dev(dev); | 614 | struct pci_dev *pci_dev = to_pci_dev(dev); |
592 | struct device_driver *drv = dev->driver; | 615 | struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; |
593 | int error = 0; | ||
594 | 616 | ||
595 | if (pci_has_legacy_pm_support(pci_dev)) | 617 | if (pci_has_legacy_pm_support(pci_dev)) |
596 | return pci_legacy_suspend(dev, PMSG_FREEZE); | 618 | return pci_legacy_suspend(dev, PMSG_FREEZE); |
597 | 619 | ||
598 | if (drv && drv->pm && drv->pm->freeze) { | 620 | if (!pm) { |
599 | error = drv->pm->freeze(dev); | 621 | pci_pm_default_suspend(pci_dev); |
600 | suspend_report_result(drv->pm->freeze, error); | 622 | return 0; |
601 | } | 623 | } |
602 | 624 | ||
603 | if (!error) | 625 | pci_dev->state_saved = false; |
604 | pci_pm_default_suspend_generic(pci_dev); | ||
605 | 626 | ||
606 | return error; | 627 | if (pm->freeze) { |
628 | int error; | ||
629 | |||
630 | error = pm->freeze(dev); | ||
631 | suspend_report_result(pm->freeze, error); | ||
632 | if (error) | ||
633 | return error; | ||
634 | } | ||
635 | |||
636 | if (!pci_dev->state_saved) | ||
637 | pci_save_state(pci_dev); | ||
638 | |||
639 | return 0; | ||
607 | } | 640 | } |
608 | 641 | ||
609 | static int pci_pm_freeze_noirq(struct device *dev) | 642 | static int pci_pm_freeze_noirq(struct device *dev) |
@@ -646,16 +679,18 @@ static int pci_pm_thaw_noirq(struct device *dev) | |||
646 | static int pci_pm_thaw(struct device *dev) | 679 | static int pci_pm_thaw(struct device *dev) |
647 | { | 680 | { |
648 | struct pci_dev *pci_dev = to_pci_dev(dev); | 681 | struct pci_dev *pci_dev = to_pci_dev(dev); |
649 | struct device_driver *drv = dev->driver; | 682 | struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; |
650 | int error = 0; | 683 | int error = 0; |
651 | 684 | ||
652 | if (pci_has_legacy_pm_support(pci_dev)) | 685 | if (pci_has_legacy_pm_support(pci_dev)) |
653 | return pci_legacy_resume(dev); | 686 | return pci_legacy_resume(dev); |
654 | 687 | ||
655 | pci_pm_reenable_device(pci_dev); | 688 | if (pm) { |
656 | 689 | if (pm->thaw) | |
657 | if (drv && drv->pm && drv->pm->thaw) | 690 | error = pm->thaw(dev); |
658 | error = drv->pm->thaw(dev); | 691 | } else { |
692 | pci_pm_reenable_device(pci_dev); | ||
693 | } | ||
659 | 694 | ||
660 | return error; | 695 | return error; |
661 | } | 696 | } |
@@ -663,22 +698,29 @@ static int pci_pm_thaw(struct device *dev) | |||
663 | static int pci_pm_poweroff(struct device *dev) | 698 | static int pci_pm_poweroff(struct device *dev) |
664 | { | 699 | { |
665 | struct pci_dev *pci_dev = to_pci_dev(dev); | 700 | struct pci_dev *pci_dev = to_pci_dev(dev); |
666 | struct device_driver *drv = dev->driver; | 701 | struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; |
667 | int error = 0; | 702 | int error = 0; |
668 | 703 | ||
669 | if (pci_has_legacy_pm_support(pci_dev)) | 704 | if (pci_has_legacy_pm_support(pci_dev)) |
670 | return pci_legacy_suspend(dev, PMSG_HIBERNATE); | 705 | return pci_legacy_suspend(dev, PMSG_HIBERNATE); |
671 | 706 | ||
672 | if (!drv || !drv->pm) | 707 | if (!pm) { |
673 | return 0; | 708 | pci_pm_default_suspend(pci_dev); |
709 | goto Fixup; | ||
710 | } | ||
711 | |||
712 | pci_dev->state_saved = false; | ||
674 | 713 | ||
675 | if (drv->pm->poweroff) { | 714 | if (pm->poweroff) { |
676 | error = drv->pm->poweroff(dev); | 715 | error = pm->poweroff(dev); |
677 | suspend_report_result(drv->pm->poweroff, error); | 716 | suspend_report_result(pm->poweroff, error); |
678 | } | 717 | } |
679 | 718 | ||
680 | if (!error) | 719 | if (!pci_dev->state_saved && !pci_is_bridge(pci_dev)) |
681 | pci_pm_default_suspend(pci_dev); | 720 | pci_prepare_to_sleep(pci_dev); |
721 | |||
722 | Fixup: | ||
723 | pci_fixup_device(pci_fixup_suspend, pci_dev); | ||
682 | 724 | ||
683 | return error; | 725 | return error; |
684 | } | 726 | } |
@@ -719,7 +761,7 @@ static int pci_pm_restore_noirq(struct device *dev) | |||
719 | static int pci_pm_restore(struct device *dev) | 761 | static int pci_pm_restore(struct device *dev) |
720 | { | 762 | { |
721 | struct pci_dev *pci_dev = to_pci_dev(dev); | 763 | struct pci_dev *pci_dev = to_pci_dev(dev); |
722 | struct device_driver *drv = dev->driver; | 764 | struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; |
723 | int error = 0; | 765 | int error = 0; |
724 | 766 | ||
725 | /* | 767 | /* |
@@ -732,10 +774,14 @@ static int pci_pm_restore(struct device *dev) | |||
732 | if (pci_has_legacy_pm_support(pci_dev)) | 774 | if (pci_has_legacy_pm_support(pci_dev)) |
733 | return pci_legacy_resume(dev); | 775 | return pci_legacy_resume(dev); |
734 | 776 | ||
735 | error = pci_pm_default_resume(pci_dev); | 777 | pci_pm_default_resume(pci_dev); |
736 | 778 | ||
737 | if (!error && drv && drv->pm && drv->pm->restore) | 779 | if (pm) { |
738 | error = drv->pm->restore(dev); | 780 | if (pm->restore) |
781 | error = pm->restore(dev); | ||
782 | } else { | ||
783 | pci_pm_reenable_device(pci_dev); | ||
784 | } | ||
739 | 785 | ||
740 | return error; | 786 | return error; |
741 | } | 787 | } |