diff options
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/pci-driver.c | 109 | ||||
-rw-r--r-- | drivers/pci/pci.c | 2 | ||||
-rw-r--r-- | drivers/pci/pci.h | 1 |
3 files changed, 74 insertions, 38 deletions
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index bfaa77d88537..750ee79c178f 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c | |||
@@ -316,21 +316,10 @@ static void pci_device_shutdown(struct device *dev) | |||
316 | 316 | ||
317 | /* | 317 | /* |
318 | * Default "suspend" method for devices that have no driver provided suspend, | 318 | * Default "suspend" method for devices that have no driver provided suspend, |
319 | * or not even a driver at all (first part). | ||
320 | */ | ||
321 | static void pci_default_pm_suspend_early(struct pci_dev *pci_dev) | ||
322 | { | ||
323 | /* If device is enabled at this point, disable it */ | ||
324 | pci_disable_enabled_device(pci_dev); | ||
325 | } | ||
326 | |||
327 | /* | ||
328 | * Default "suspend" method for devices that have no driver provided suspend, | ||
329 | * or not even a driver at all (second part). | 319 | * or not even a driver at all (second part). |
330 | */ | 320 | */ |
331 | static void pci_default_pm_suspend_late(struct pci_dev *pci_dev) | 321 | static void pci_default_pm_suspend_late(struct pci_dev *pci_dev) |
332 | { | 322 | { |
333 | pci_save_state(pci_dev); | ||
334 | /* | 323 | /* |
335 | * mark its power state as "unknown", since we don't know if | 324 | * mark its power state as "unknown", since we don't know if |
336 | * e.g. the BIOS will change its device state when we suspend. | 325 | * e.g. the BIOS will change its device state when we suspend. |
@@ -341,16 +330,6 @@ static void pci_default_pm_suspend_late(struct pci_dev *pci_dev) | |||
341 | 330 | ||
342 | /* | 331 | /* |
343 | * Default "resume" method for devices that have no driver provided resume, | 332 | * Default "resume" method for devices that have no driver provided resume, |
344 | * or not even a driver at all (first part). | ||
345 | */ | ||
346 | static void pci_default_pm_resume_early(struct pci_dev *pci_dev) | ||
347 | { | ||
348 | /* restore the PCI config space */ | ||
349 | pci_restore_state(pci_dev); | ||
350 | } | ||
351 | |||
352 | /* | ||
353 | * Default "resume" method for devices that have no driver provided resume, | ||
354 | * or not even a driver at all (second part). | 333 | * or not even a driver at all (second part). |
355 | */ | 334 | */ |
356 | static int pci_default_pm_resume_late(struct pci_dev *pci_dev) | 335 | static int pci_default_pm_resume_late(struct pci_dev *pci_dev) |
@@ -379,9 +358,10 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state) | |||
379 | i = drv->suspend(pci_dev, state); | 358 | i = drv->suspend(pci_dev, state); |
380 | suspend_report_result(drv->suspend, i); | 359 | suspend_report_result(drv->suspend, i); |
381 | } else { | 360 | } else { |
361 | pci_save_state(pci_dev); | ||
382 | /* | 362 | /* |
383 | * For compatibility with existing code with legacy PM support | 363 | * This is for compatibility with existing code with legacy PM |
384 | * don't call pci_default_pm_suspend_early() here. | 364 | * support. |
385 | */ | 365 | */ |
386 | pci_default_pm_suspend_late(pci_dev); | 366 | pci_default_pm_suspend_late(pci_dev); |
387 | } | 367 | } |
@@ -410,7 +390,8 @@ static int pci_legacy_resume(struct device *dev) | |||
410 | if (drv && drv->resume) { | 390 | if (drv && drv->resume) { |
411 | error = drv->resume(pci_dev); | 391 | error = drv->resume(pci_dev); |
412 | } else { | 392 | } else { |
413 | pci_default_pm_resume_early(pci_dev); | 393 | /* restore the PCI config space */ |
394 | pci_restore_state(pci_dev); | ||
414 | error = pci_default_pm_resume_late(pci_dev); | 395 | error = pci_default_pm_resume_late(pci_dev); |
415 | } | 396 | } |
416 | return error; | 397 | return error; |
@@ -429,22 +410,72 @@ static int pci_legacy_resume_early(struct device *dev) | |||
429 | 410 | ||
430 | /* Auxiliary functions used by the new power management framework */ | 411 | /* Auxiliary functions used by the new power management framework */ |
431 | 412 | ||
413 | static int pci_restore_standard_config(struct pci_dev *pci_dev) | ||
414 | { | ||
415 | struct pci_dev *parent = pci_dev->bus->self; | ||
416 | int error = 0; | ||
417 | |||
418 | /* Check if the device's bus is operational */ | ||
419 | if (!parent || parent->current_state == PCI_D0) { | ||
420 | pci_restore_state(pci_dev); | ||
421 | pci_update_current_state(pci_dev, PCI_D0); | ||
422 | } else { | ||
423 | dev_warn(&pci_dev->dev, "unable to restore config, " | ||
424 | "bridge %s in low power state D%d\n", pci_name(parent), | ||
425 | parent->current_state); | ||
426 | pci_dev->current_state = PCI_UNKNOWN; | ||
427 | error = -EAGAIN; | ||
428 | } | ||
429 | |||
430 | return error; | ||
431 | } | ||
432 | |||
432 | static bool pci_is_bridge(struct pci_dev *pci_dev) | 433 | static bool pci_is_bridge(struct pci_dev *pci_dev) |
433 | { | 434 | { |
434 | return !!(pci_dev->subordinate); | 435 | return !!(pci_dev->subordinate); |
435 | } | 436 | } |
436 | 437 | ||
438 | static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev) | ||
439 | { | ||
440 | if (pci_restore_standard_config(pci_dev)) | ||
441 | pci_fixup_device(pci_fixup_resume_early, pci_dev); | ||
442 | } | ||
443 | |||
437 | static int pci_pm_default_resume(struct pci_dev *pci_dev) | 444 | static int pci_pm_default_resume(struct pci_dev *pci_dev) |
438 | { | 445 | { |
446 | /* | ||
447 | * pci_restore_standard_config() should have been called once already, | ||
448 | * but it would have failed if the device's parent bridge had not been | ||
449 | * in power state D0 at that time. Check it and try again if necessary. | ||
450 | */ | ||
451 | if (pci_dev->current_state == PCI_UNKNOWN) { | ||
452 | int error = pci_restore_standard_config(pci_dev); | ||
453 | if (error) | ||
454 | return error; | ||
455 | } | ||
456 | |||
457 | pci_fixup_device(pci_fixup_resume, pci_dev); | ||
458 | |||
439 | if (!pci_is_bridge(pci_dev)) | 459 | if (!pci_is_bridge(pci_dev)) |
440 | pci_enable_wake(pci_dev, PCI_D0, false); | 460 | pci_enable_wake(pci_dev, PCI_D0, false); |
441 | 461 | ||
442 | return pci_default_pm_resume_late(pci_dev); | 462 | return pci_default_pm_resume_late(pci_dev); |
443 | } | 463 | } |
444 | 464 | ||
465 | static void pci_pm_default_suspend_generic(struct pci_dev *pci_dev) | ||
466 | { | ||
467 | /* If device is enabled at this point, disable it */ | ||
468 | pci_disable_enabled_device(pci_dev); | ||
469 | /* | ||
470 | * Save state with interrupts enabled, because in principle the bus the | ||
471 | * device is on may be put into a low power state after this code runs. | ||
472 | */ | ||
473 | pci_save_state(pci_dev); | ||
474 | } | ||
475 | |||
445 | static void pci_pm_default_suspend(struct pci_dev *pci_dev) | 476 | static void pci_pm_default_suspend(struct pci_dev *pci_dev) |
446 | { | 477 | { |
447 | pci_default_pm_suspend_early(pci_dev); | 478 | pci_pm_default_suspend_generic(pci_dev); |
448 | 479 | ||
449 | if (!pci_is_bridge(pci_dev)) | 480 | if (!pci_is_bridge(pci_dev)) |
450 | pci_prepare_to_sleep(pci_dev); | 481 | pci_prepare_to_sleep(pci_dev); |
@@ -529,12 +560,13 @@ static int pci_pm_resume(struct device *dev) | |||
529 | struct device_driver *drv = dev->driver; | 560 | struct device_driver *drv = dev->driver; |
530 | int error = 0; | 561 | int error = 0; |
531 | 562 | ||
532 | pci_fixup_device(pci_fixup_resume, pci_dev); | ||
533 | |||
534 | if (drv && drv->pm) { | 563 | if (drv && drv->pm) { |
564 | pci_fixup_device(pci_fixup_resume, pci_dev); | ||
565 | |||
535 | if (drv->pm->resume) | 566 | if (drv->pm->resume) |
536 | error = drv->pm->resume(dev); | 567 | error = drv->pm->resume(dev); |
537 | } else if (pci_has_legacy_pm_support(pci_dev)) { | 568 | } else if (pci_has_legacy_pm_support(pci_dev)) { |
569 | pci_fixup_device(pci_fixup_resume, pci_dev); | ||
538 | error = pci_legacy_resume(dev); | 570 | error = pci_legacy_resume(dev); |
539 | } else { | 571 | } else { |
540 | error = pci_pm_default_resume(pci_dev); | 572 | error = pci_pm_default_resume(pci_dev); |
@@ -549,15 +581,16 @@ static int pci_pm_resume_noirq(struct device *dev) | |||
549 | struct device_driver *drv = dev->driver; | 581 | struct device_driver *drv = dev->driver; |
550 | int error = 0; | 582 | int error = 0; |
551 | 583 | ||
552 | pci_fixup_device(pci_fixup_resume_early, to_pci_dev(dev)); | ||
553 | |||
554 | if (drv && drv->pm) { | 584 | if (drv && drv->pm) { |
585 | pci_fixup_device(pci_fixup_resume_early, pci_dev); | ||
586 | |||
555 | if (drv->pm->resume_noirq) | 587 | if (drv->pm->resume_noirq) |
556 | error = drv->pm->resume_noirq(dev); | 588 | error = drv->pm->resume_noirq(dev); |
557 | } else if (pci_has_legacy_pm_support(pci_dev)) { | 589 | } else if (pci_has_legacy_pm_support(pci_dev)) { |
590 | pci_fixup_device(pci_fixup_resume_early, pci_dev); | ||
558 | error = pci_legacy_resume_early(dev); | 591 | error = pci_legacy_resume_early(dev); |
559 | } else { | 592 | } else { |
560 | pci_default_pm_resume_early(pci_dev); | 593 | pci_pm_default_resume_noirq(pci_dev); |
561 | } | 594 | } |
562 | 595 | ||
563 | return error; | 596 | return error; |
@@ -589,7 +622,7 @@ static int pci_pm_freeze(struct device *dev) | |||
589 | error = pci_legacy_suspend(dev, PMSG_FREEZE); | 622 | error = pci_legacy_suspend(dev, PMSG_FREEZE); |
590 | pci_fixup_device(pci_fixup_suspend, pci_dev); | 623 | pci_fixup_device(pci_fixup_suspend, pci_dev); |
591 | } else { | 624 | } else { |
592 | pci_default_pm_suspend_early(pci_dev); | 625 | pci_pm_default_suspend_generic(pci_dev); |
593 | } | 626 | } |
594 | 627 | ||
595 | return error; | 628 | return error; |
@@ -647,7 +680,7 @@ static int pci_pm_thaw_noirq(struct device *dev) | |||
647 | pci_fixup_device(pci_fixup_resume_early, to_pci_dev(dev)); | 680 | pci_fixup_device(pci_fixup_resume_early, to_pci_dev(dev)); |
648 | error = pci_legacy_resume_early(dev); | 681 | error = pci_legacy_resume_early(dev); |
649 | } else { | 682 | } else { |
650 | pci_default_pm_resume_early(pci_dev); | 683 | pci_update_current_state(pci_dev, PCI_D0); |
651 | } | 684 | } |
652 | 685 | ||
653 | return error; | 686 | return error; |
@@ -698,12 +731,13 @@ static int pci_pm_restore(struct device *dev) | |||
698 | struct device_driver *drv = dev->driver; | 731 | struct device_driver *drv = dev->driver; |
699 | int error = 0; | 732 | int error = 0; |
700 | 733 | ||
701 | pci_fixup_device(pci_fixup_resume, pci_dev); | ||
702 | |||
703 | if (drv && drv->pm) { | 734 | if (drv && drv->pm) { |
735 | pci_fixup_device(pci_fixup_resume, pci_dev); | ||
736 | |||
704 | if (drv->pm->restore) | 737 | if (drv->pm->restore) |
705 | error = drv->pm->restore(dev); | 738 | error = drv->pm->restore(dev); |
706 | } else if (pci_has_legacy_pm_support(pci_dev)) { | 739 | } else if (pci_has_legacy_pm_support(pci_dev)) { |
740 | pci_fixup_device(pci_fixup_resume, pci_dev); | ||
707 | error = pci_legacy_resume(dev); | 741 | error = pci_legacy_resume(dev); |
708 | } else { | 742 | } else { |
709 | error = pci_pm_default_resume(pci_dev); | 743 | error = pci_pm_default_resume(pci_dev); |
@@ -718,15 +752,16 @@ static int pci_pm_restore_noirq(struct device *dev) | |||
718 | struct device_driver *drv = dev->driver; | 752 | struct device_driver *drv = dev->driver; |
719 | int error = 0; | 753 | int error = 0; |
720 | 754 | ||
721 | pci_fixup_device(pci_fixup_resume_early, pci_dev); | ||
722 | |||
723 | if (drv && drv->pm) { | 755 | if (drv && drv->pm) { |
756 | pci_fixup_device(pci_fixup_resume_early, pci_dev); | ||
757 | |||
724 | if (drv->pm->restore_noirq) | 758 | if (drv->pm->restore_noirq) |
725 | error = drv->pm->restore_noirq(dev); | 759 | error = drv->pm->restore_noirq(dev); |
726 | } else if (pci_has_legacy_pm_support(pci_dev)) { | 760 | } else if (pci_has_legacy_pm_support(pci_dev)) { |
761 | pci_fixup_device(pci_fixup_resume_early, pci_dev); | ||
727 | error = pci_legacy_resume_early(dev); | 762 | error = pci_legacy_resume_early(dev); |
728 | } else { | 763 | } else { |
729 | pci_default_pm_resume_early(pci_dev); | 764 | pci_pm_default_resume_noirq(pci_dev); |
730 | } | 765 | } |
731 | 766 | ||
732 | return error; | 767 | return error; |
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 6e309c8b47df..e491fdedf705 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
@@ -527,7 +527,7 @@ pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) | |||
527 | * @dev: PCI device to handle. | 527 | * @dev: PCI device to handle. |
528 | * @state: State to cache in case the device doesn't have the PM capability | 528 | * @state: State to cache in case the device doesn't have the PM capability |
529 | */ | 529 | */ |
530 | static void pci_update_current_state(struct pci_dev *dev, pci_power_t state) | 530 | void pci_update_current_state(struct pci_dev *dev, pci_power_t state) |
531 | { | 531 | { |
532 | if (dev->pm_cap) { | 532 | if (dev->pm_cap) { |
533 | u16 pmcsr; | 533 | u16 pmcsr; |
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 881dc15f8efd..1351bb4addde 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h | |||
@@ -44,6 +44,7 @@ struct pci_platform_pm_ops { | |||
44 | }; | 44 | }; |
45 | 45 | ||
46 | extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops); | 46 | extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops); |
47 | extern void pci_update_current_state(struct pci_dev *dev, pci_power_t state); | ||
47 | extern void pci_disable_enabled_device(struct pci_dev *dev); | 48 | extern void pci_disable_enabled_device(struct pci_dev *dev); |
48 | extern void pci_pm_init(struct pci_dev *dev); | 49 | extern void pci_pm_init(struct pci_dev *dev); |
49 | extern void platform_pci_wakeup_init(struct pci_dev *dev); | 50 | extern void platform_pci_wakeup_init(struct pci_dev *dev); |