diff options
author | Ingo Molnar <mingo@elte.hu> | 2009-02-11 03:22:04 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-02-11 03:22:04 -0500 |
commit | 95fd4845ed0ffcab305b4f30ce1c12dc34f1b56c (patch) | |
tree | aa2aac22a5b329b778a6771a87bbf1945ad49bbd /drivers/pci/pci-driver.c | |
parent | d278c48435625cb6b7edcf6a547620768b175709 (diff) | |
parent | 8e4921515c1a379539607eb443d51c30f4f7f338 (diff) |
Merge commit 'v2.6.29-rc4' into perfcounters/core
Conflicts:
arch/x86/kernel/setup_percpu.c
arch/x86/mm/fault.c
drivers/acpi/processor_idle.c
kernel/irq/handle.c
Diffstat (limited to 'drivers/pci/pci-driver.c')
-rw-r--r-- | drivers/pci/pci-driver.c | 266 |
1 files changed, 147 insertions, 119 deletions
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index c697f2680856..93eac1423585 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c | |||
@@ -355,17 +355,34 @@ 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 | |||
360 | pci_dev->state_saved = false; | ||
361 | |||
358 | i = drv->suspend(pci_dev, state); | 362 | i = drv->suspend(pci_dev, state); |
359 | suspend_report_result(drv->suspend, i); | 363 | suspend_report_result(drv->suspend, i); |
360 | } else { | 364 | if (i) |
361 | pci_save_state(pci_dev); | 365 | return i; |
362 | /* | 366 | |
363 | * This is for compatibility with existing code with legacy PM | 367 | if (pci_dev->state_saved) |
364 | * support. | 368 | goto Fixup; |
365 | */ | 369 | |
366 | pci_pm_set_unknown_state(pci_dev); | 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); | ||
375 | goto Fixup; | ||
376 | } | ||
367 | } | 377 | } |
368 | 378 | ||
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: | ||
369 | pci_fixup_device(pci_fixup_suspend, pci_dev); | 386 | pci_fixup_device(pci_fixup_suspend, pci_dev); |
370 | 387 | ||
371 | return i; | 388 | return i; |
@@ -386,108 +403,47 @@ static int pci_legacy_suspend_late(struct device *dev, pm_message_t state) | |||
386 | 403 | ||
387 | static int pci_legacy_resume_early(struct device *dev) | 404 | static int pci_legacy_resume_early(struct device *dev) |
388 | { | 405 | { |
389 | int error = 0; | ||
390 | struct pci_dev * pci_dev = to_pci_dev(dev); | 406 | struct pci_dev * pci_dev = to_pci_dev(dev); |
391 | struct pci_driver * drv = pci_dev->driver; | 407 | struct pci_driver * drv = pci_dev->driver; |
392 | 408 | ||
393 | pci_fixup_device(pci_fixup_resume_early, pci_dev); | 409 | return drv && drv->resume_early ? |
394 | 410 | drv->resume_early(pci_dev) : 0; | |
395 | if (drv && drv->resume_early) | ||
396 | error = drv->resume_early(pci_dev); | ||
397 | return error; | ||
398 | } | 411 | } |
399 | 412 | ||
400 | static int pci_legacy_resume(struct device *dev) | 413 | static int pci_legacy_resume(struct device *dev) |
401 | { | 414 | { |
402 | int error; | ||
403 | struct pci_dev * pci_dev = to_pci_dev(dev); | 415 | struct pci_dev * pci_dev = to_pci_dev(dev); |
404 | struct pci_driver * drv = pci_dev->driver; | 416 | struct pci_driver * drv = pci_dev->driver; |
405 | 417 | ||
406 | pci_fixup_device(pci_fixup_resume, pci_dev); | 418 | pci_fixup_device(pci_fixup_resume, pci_dev); |
407 | 419 | ||
408 | if (drv && drv->resume) { | 420 | return drv && drv->resume ? |
409 | error = drv->resume(pci_dev); | 421 | drv->resume(pci_dev) : pci_pm_reenable_device(pci_dev); |
410 | } else { | ||
411 | /* restore the PCI config space */ | ||
412 | pci_restore_state(pci_dev); | ||
413 | error = pci_pm_reenable_device(pci_dev); | ||
414 | } | ||
415 | return error; | ||
416 | } | 422 | } |
417 | 423 | ||
418 | /* Auxiliary functions used by the new power management framework */ | 424 | /* Auxiliary functions used by the new power management framework */ |
419 | 425 | ||
420 | static int pci_restore_standard_config(struct pci_dev *pci_dev) | ||
421 | { | ||
422 | struct pci_dev *parent = pci_dev->bus->self; | ||
423 | int error = 0; | ||
424 | |||
425 | /* Check if the device's bus is operational */ | ||
426 | if (!parent || parent->current_state == PCI_D0) { | ||
427 | pci_restore_state(pci_dev); | ||
428 | pci_update_current_state(pci_dev, PCI_D0); | ||
429 | } else { | ||
430 | dev_warn(&pci_dev->dev, "unable to restore config, " | ||
431 | "bridge %s in low power state D%d\n", pci_name(parent), | ||
432 | parent->current_state); | ||
433 | pci_dev->current_state = PCI_UNKNOWN; | ||
434 | error = -EAGAIN; | ||
435 | } | ||
436 | |||
437 | return error; | ||
438 | } | ||
439 | |||
440 | static bool pci_is_bridge(struct pci_dev *pci_dev) | ||
441 | { | ||
442 | return !!(pci_dev->subordinate); | ||
443 | } | ||
444 | |||
445 | static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev) | 426 | static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev) |
446 | { | 427 | { |
447 | if (pci_restore_standard_config(pci_dev)) | 428 | pci_restore_standard_config(pci_dev); |
448 | pci_fixup_device(pci_fixup_resume_early, pci_dev); | 429 | pci_dev->state_saved = false; |
430 | pci_fixup_device(pci_fixup_resume_early, pci_dev); | ||
449 | } | 431 | } |
450 | 432 | ||
451 | static int pci_pm_default_resume(struct pci_dev *pci_dev) | 433 | static void pci_pm_default_resume(struct pci_dev *pci_dev) |
452 | { | 434 | { |
453 | /* | ||
454 | * pci_restore_standard_config() should have been called once already, | ||
455 | * but it would have failed if the device's parent bridge had not been | ||
456 | * in power state D0 at that time. Check it and try again if necessary. | ||
457 | */ | ||
458 | if (pci_dev->current_state == PCI_UNKNOWN) { | ||
459 | int error = pci_restore_standard_config(pci_dev); | ||
460 | if (error) | ||
461 | return error; | ||
462 | } | ||
463 | |||
464 | pci_fixup_device(pci_fixup_resume, pci_dev); | 435 | pci_fixup_device(pci_fixup_resume, pci_dev); |
465 | 436 | ||
466 | if (!pci_is_bridge(pci_dev)) | 437 | if (!pci_is_bridge(pci_dev)) |
467 | pci_enable_wake(pci_dev, PCI_D0, false); | 438 | pci_enable_wake(pci_dev, PCI_D0, false); |
468 | |||
469 | return pci_pm_reenable_device(pci_dev); | ||
470 | } | ||
471 | |||
472 | static void pci_pm_default_suspend_generic(struct pci_dev *pci_dev) | ||
473 | { | ||
474 | /* If device is enabled at this point, disable it */ | ||
475 | pci_disable_enabled_device(pci_dev); | ||
476 | /* | ||
477 | * Save state with interrupts enabled, because in principle the bus the | ||
478 | * device is on may be put into a low power state after this code runs. | ||
479 | */ | ||
480 | pci_save_state(pci_dev); | ||
481 | } | 439 | } |
482 | 440 | ||
483 | static void pci_pm_default_suspend(struct pci_dev *pci_dev) | 441 | static void pci_pm_default_suspend(struct pci_dev *pci_dev) |
484 | { | 442 | { |
485 | pci_pm_default_suspend_generic(pci_dev); | 443 | /* Disable non-bridge devices without PM support */ |
486 | |||
487 | if (!pci_is_bridge(pci_dev)) | 444 | if (!pci_is_bridge(pci_dev)) |
488 | pci_prepare_to_sleep(pci_dev); | 445 | pci_disable_enabled_device(pci_dev); |
489 | 446 | pci_save_state(pci_dev); | |
490 | pci_fixup_device(pci_fixup_suspend, pci_dev); | ||
491 | } | 447 | } |
492 | 448 | ||
493 | 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) |
@@ -532,21 +488,49 @@ static void pci_pm_complete(struct device *dev) | |||
532 | static int pci_pm_suspend(struct device *dev) | 488 | static int pci_pm_suspend(struct device *dev) |
533 | { | 489 | { |
534 | struct pci_dev *pci_dev = to_pci_dev(dev); | 490 | struct pci_dev *pci_dev = to_pci_dev(dev); |
535 | struct device_driver *drv = dev->driver; | 491 | struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; |
536 | int error = 0; | ||
537 | 492 | ||
538 | if (pci_has_legacy_pm_support(pci_dev)) | 493 | if (pci_has_legacy_pm_support(pci_dev)) |
539 | return pci_legacy_suspend(dev, PMSG_SUSPEND); | 494 | return pci_legacy_suspend(dev, PMSG_SUSPEND); |
540 | 495 | ||
541 | if (drv && drv->pm && drv->pm->suspend) { | 496 | if (!pm) { |
542 | error = drv->pm->suspend(dev); | 497 | pci_pm_default_suspend(pci_dev); |
543 | suspend_report_result(drv->pm->suspend, error); | 498 | goto Fixup; |
544 | } | 499 | } |
545 | 500 | ||
546 | if (!error) | 501 | pci_dev->state_saved = false; |
547 | pci_pm_default_suspend(pci_dev); | ||
548 | 502 | ||
549 | 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; | ||
550 | } | 534 | } |
551 | 535 | ||
552 | static int pci_pm_suspend_noirq(struct device *dev) | 536 | static int pci_pm_suspend_noirq(struct device *dev) |
@@ -575,11 +559,11 @@ static int pci_pm_resume_noirq(struct device *dev) | |||
575 | struct device_driver *drv = dev->driver; | 559 | struct device_driver *drv = dev->driver; |
576 | int error = 0; | 560 | int error = 0; |
577 | 561 | ||
562 | pci_pm_default_resume_noirq(pci_dev); | ||
563 | |||
578 | if (pci_has_legacy_pm_support(pci_dev)) | 564 | if (pci_has_legacy_pm_support(pci_dev)) |
579 | return pci_legacy_resume_early(dev); | 565 | return pci_legacy_resume_early(dev); |
580 | 566 | ||
581 | pci_pm_default_resume_noirq(pci_dev); | ||
582 | |||
583 | if (drv && drv->pm && drv->pm->resume_noirq) | 567 | if (drv && drv->pm && drv->pm->resume_noirq) |
584 | error = drv->pm->resume_noirq(dev); | 568 | error = drv->pm->resume_noirq(dev); |
585 | 569 | ||
@@ -589,18 +573,29 @@ static int pci_pm_resume_noirq(struct device *dev) | |||
589 | static int pci_pm_resume(struct device *dev) | 573 | static int pci_pm_resume(struct device *dev) |
590 | { | 574 | { |
591 | struct pci_dev *pci_dev = to_pci_dev(dev); | 575 | struct pci_dev *pci_dev = to_pci_dev(dev); |
592 | struct device_driver *drv = dev->driver; | 576 | struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; |
593 | int error = 0; | 577 | int error = 0; |
594 | 578 | ||
579 | /* | ||
580 | * This is necessary for the suspend error path in which resume is | ||
581 | * called without restoring the standard config registers of the device. | ||
582 | */ | ||
583 | if (pci_dev->state_saved) | ||
584 | pci_restore_standard_config(pci_dev); | ||
585 | |||
595 | if (pci_has_legacy_pm_support(pci_dev)) | 586 | if (pci_has_legacy_pm_support(pci_dev)) |
596 | return pci_legacy_resume(dev); | 587 | return pci_legacy_resume(dev); |
597 | 588 | ||
598 | error = pci_pm_default_resume(pci_dev); | 589 | pci_pm_default_resume(pci_dev); |
599 | 590 | ||
600 | if (!error && drv && drv->pm && drv->pm->resume) | 591 | if (pm) { |
601 | error = drv->pm->resume(dev); | 592 | if (pm->resume) |
593 | error = pm->resume(dev); | ||
594 | } else { | ||
595 | pci_pm_reenable_device(pci_dev); | ||
596 | } | ||
602 | 597 | ||
603 | return error; | 598 | return 0; |
604 | } | 599 | } |
605 | 600 | ||
606 | #else /* !CONFIG_SUSPEND */ | 601 | #else /* !CONFIG_SUSPEND */ |
@@ -617,21 +612,31 @@ static int pci_pm_resume(struct device *dev) | |||
617 | static int pci_pm_freeze(struct device *dev) | 612 | static int pci_pm_freeze(struct device *dev) |
618 | { | 613 | { |
619 | struct pci_dev *pci_dev = to_pci_dev(dev); | 614 | struct pci_dev *pci_dev = to_pci_dev(dev); |
620 | struct device_driver *drv = dev->driver; | 615 | struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; |
621 | int error = 0; | ||
622 | 616 | ||
623 | if (pci_has_legacy_pm_support(pci_dev)) | 617 | if (pci_has_legacy_pm_support(pci_dev)) |
624 | return pci_legacy_suspend(dev, PMSG_FREEZE); | 618 | return pci_legacy_suspend(dev, PMSG_FREEZE); |
625 | 619 | ||
626 | if (drv && drv->pm && drv->pm->freeze) { | 620 | if (!pm) { |
627 | error = drv->pm->freeze(dev); | 621 | pci_pm_default_suspend(pci_dev); |
628 | suspend_report_result(drv->pm->freeze, error); | 622 | return 0; |
629 | } | 623 | } |
630 | 624 | ||
631 | if (!error) | 625 | pci_dev->state_saved = false; |
632 | pci_pm_default_suspend_generic(pci_dev); | ||
633 | 626 | ||
634 | 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; | ||
635 | } | 640 | } |
636 | 641 | ||
637 | static int pci_pm_freeze_noirq(struct device *dev) | 642 | static int pci_pm_freeze_noirq(struct device *dev) |
@@ -674,16 +679,18 @@ static int pci_pm_thaw_noirq(struct device *dev) | |||
674 | static int pci_pm_thaw(struct device *dev) | 679 | static int pci_pm_thaw(struct device *dev) |
675 | { | 680 | { |
676 | struct pci_dev *pci_dev = to_pci_dev(dev); | 681 | struct pci_dev *pci_dev = to_pci_dev(dev); |
677 | struct device_driver *drv = dev->driver; | 682 | struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; |
678 | int error = 0; | 683 | int error = 0; |
679 | 684 | ||
680 | if (pci_has_legacy_pm_support(pci_dev)) | 685 | if (pci_has_legacy_pm_support(pci_dev)) |
681 | return pci_legacy_resume(dev); | 686 | return pci_legacy_resume(dev); |
682 | 687 | ||
683 | pci_pm_reenable_device(pci_dev); | 688 | if (pm) { |
684 | 689 | if (pm->thaw) | |
685 | if (drv && drv->pm && drv->pm->thaw) | 690 | error = pm->thaw(dev); |
686 | error = drv->pm->thaw(dev); | 691 | } else { |
692 | pci_pm_reenable_device(pci_dev); | ||
693 | } | ||
687 | 694 | ||
688 | return error; | 695 | return error; |
689 | } | 696 | } |
@@ -691,19 +698,29 @@ static int pci_pm_thaw(struct device *dev) | |||
691 | static int pci_pm_poweroff(struct device *dev) | 698 | static int pci_pm_poweroff(struct device *dev) |
692 | { | 699 | { |
693 | struct pci_dev *pci_dev = to_pci_dev(dev); | 700 | struct pci_dev *pci_dev = to_pci_dev(dev); |
694 | struct device_driver *drv = dev->driver; | 701 | struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; |
695 | int error = 0; | 702 | int error = 0; |
696 | 703 | ||
697 | if (pci_has_legacy_pm_support(pci_dev)) | 704 | if (pci_has_legacy_pm_support(pci_dev)) |
698 | return pci_legacy_suspend(dev, PMSG_HIBERNATE); | 705 | return pci_legacy_suspend(dev, PMSG_HIBERNATE); |
699 | 706 | ||
700 | if (drv && drv->pm && drv->pm->poweroff) { | 707 | if (!pm) { |
701 | error = drv->pm->poweroff(dev); | 708 | pci_pm_default_suspend(pci_dev); |
702 | suspend_report_result(drv->pm->poweroff, error); | 709 | goto Fixup; |
703 | } | 710 | } |
704 | 711 | ||
705 | if (!error) | 712 | pci_dev->state_saved = false; |
706 | pci_pm_default_suspend(pci_dev); | 713 | |
714 | if (pm->poweroff) { | ||
715 | error = pm->poweroff(dev); | ||
716 | suspend_report_result(pm->poweroff, error); | ||
717 | } | ||
718 | |||
719 | if (!pci_dev->state_saved && !pci_is_bridge(pci_dev)) | ||
720 | pci_prepare_to_sleep(pci_dev); | ||
721 | |||
722 | Fixup: | ||
723 | pci_fixup_device(pci_fixup_suspend, pci_dev); | ||
707 | 724 | ||
708 | return error; | 725 | return error; |
709 | } | 726 | } |
@@ -730,11 +747,11 @@ static int pci_pm_restore_noirq(struct device *dev) | |||
730 | struct device_driver *drv = dev->driver; | 747 | struct device_driver *drv = dev->driver; |
731 | int error = 0; | 748 | int error = 0; |
732 | 749 | ||
750 | pci_pm_default_resume_noirq(pci_dev); | ||
751 | |||
733 | if (pci_has_legacy_pm_support(pci_dev)) | 752 | if (pci_has_legacy_pm_support(pci_dev)) |
734 | return pci_legacy_resume_early(dev); | 753 | return pci_legacy_resume_early(dev); |
735 | 754 | ||
736 | pci_pm_default_resume_noirq(pci_dev); | ||
737 | |||
738 | if (drv && drv->pm && drv->pm->restore_noirq) | 755 | if (drv && drv->pm && drv->pm->restore_noirq) |
739 | error = drv->pm->restore_noirq(dev); | 756 | error = drv->pm->restore_noirq(dev); |
740 | 757 | ||
@@ -744,16 +761,27 @@ static int pci_pm_restore_noirq(struct device *dev) | |||
744 | static int pci_pm_restore(struct device *dev) | 761 | static int pci_pm_restore(struct device *dev) |
745 | { | 762 | { |
746 | struct pci_dev *pci_dev = to_pci_dev(dev); | 763 | struct pci_dev *pci_dev = to_pci_dev(dev); |
747 | struct device_driver *drv = dev->driver; | 764 | struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; |
748 | int error = 0; | 765 | int error = 0; |
749 | 766 | ||
767 | /* | ||
768 | * This is necessary for the hibernation error path in which restore is | ||
769 | * called without restoring the standard config registers of the device. | ||
770 | */ | ||
771 | if (pci_dev->state_saved) | ||
772 | pci_restore_standard_config(pci_dev); | ||
773 | |||
750 | if (pci_has_legacy_pm_support(pci_dev)) | 774 | if (pci_has_legacy_pm_support(pci_dev)) |
751 | return pci_legacy_resume(dev); | 775 | return pci_legacy_resume(dev); |
752 | 776 | ||
753 | error = pci_pm_default_resume(pci_dev); | 777 | pci_pm_default_resume(pci_dev); |
754 | 778 | ||
755 | if (!error && drv && drv->pm && drv->pm->restore) | 779 | if (pm) { |
756 | error = drv->pm->restore(dev); | 780 | if (pm->restore) |
781 | error = pm->restore(dev); | ||
782 | } else { | ||
783 | pci_pm_reenable_device(pci_dev); | ||
784 | } | ||
757 | 785 | ||
758 | return error; | 786 | return error; |
759 | } | 787 | } |