aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2009-01-07 07:07:15 -0500
committerJesse Barnes <jbarnes@virtuousgeek.org>2009-01-07 14:16:05 -0500
commit734104292ff77dc71fe626b4ebd91b314547ca1b (patch)
tree8e859beaa5065db26c7dc0d5e7a6a108c6f21163 /drivers/pci
parent07e836e8d1f3688311d97fe1bf46980b0f9ae9c1 (diff)
PCI PM: Avoid touching devices behind bridges in unknown state
It generally is better to avoid accessing devices behind bridges that may not be in the D0 power state, because in that case the bridges' secondary buses may not be accessible. For this reason, during the early phase of resume (ie. with interrupts disabled), before restoring the standard config registers of a device, check the power state of the bridge the device is behind and postpone the restoration of the device's config space, as well as any other operations that would involve accessing the device, if that state is not D0. In such cases the restoration of the device's config space will be retried during the "normal" phase of resume (ie. with interrupts enabled), so that the bridge can be put into D0 before that happens. Also, save standard configuration registers of PCI devices during the "normal" phase of suspend (ie. with interrupts enabled), so that the bridges the devices are behind can be put into low power states (we don't put bridges into low power states at the moment, but we may want to do it in the future and it seems reasonable to design for that). Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/pci-driver.c109
-rw-r--r--drivers/pci/pci.c2
-rw-r--r--drivers/pci/pci.h1
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 */
321static 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 */
331static void pci_default_pm_suspend_late(struct pci_dev *pci_dev) 321static 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 */
346static 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 */
356static int pci_default_pm_resume_late(struct pci_dev *pci_dev) 335static 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
413static 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
432static bool pci_is_bridge(struct pci_dev *pci_dev) 433static 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
438static 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
437static int pci_pm_default_resume(struct pci_dev *pci_dev) 444static 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
465static 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
445static void pci_pm_default_suspend(struct pci_dev *pci_dev) 476static 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 */
530static void pci_update_current_state(struct pci_dev *dev, pci_power_t state) 530void 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
46extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops); 46extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops);
47extern void pci_update_current_state(struct pci_dev *dev, pci_power_t state);
47extern void pci_disable_enabled_device(struct pci_dev *dev); 48extern void pci_disable_enabled_device(struct pci_dev *dev);
48extern void pci_pm_init(struct pci_dev *dev); 49extern void pci_pm_init(struct pci_dev *dev);
49extern void platform_pci_wakeup_init(struct pci_dev *dev); 50extern void platform_pci_wakeup_init(struct pci_dev *dev);