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