aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/pci-driver.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2009-01-16 15:54:43 -0500
committerJesse Barnes <jbarnes@virtuousgeek.org>2009-01-16 15:57:58 -0500
commitaa8c6c93747f7b55fa11e1624fec8ca33763a805 (patch)
treee40bf643ec9916dd2738ef9aaafdfa49ad8b4781 /drivers/pci/pci-driver.c
parent0db29af1e767464d71b89410d61a1e5b668d0370 (diff)
PCI PM: Restore standard config registers of all devices early
There is a problem in our handling of suspend-resume of PCI devices that many of them have their standard config registers restored with interrupts enabled and they are put into the full power state with interrupts enabled as well. This may lead to the following scenario: * an interrupt vector is shared between two or more devices * one device is resumed earlier and generates an interrupt * the interrupt handler of another device tries to handle it and attempts to access the device the config space of which hasn't been restored yet and/or which still is in a low power state * the system crashes as a result To prevent this from happening we should restore the standard configuration registers of all devices with interrupts disabled and we should put them into the D0 power state right after that. Unfortunately, this cannot be done using the existing pci_set_power_state(), because it can sleep. Also, to do it we have to make sure that the config spaces of all devices were actually saved during suspend. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Acked-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/pci/pci-driver.c')
-rw-r--r--drivers/pci/pci-driver.c91
1 files changed, 27 insertions, 64 deletions
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index c697f2680856..9de07b75b993 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -355,17 +355,27 @@ 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_dev->state_saved = false;
359
358 i = drv->suspend(pci_dev, state); 360 i = drv->suspend(pci_dev, state);
359 suspend_report_result(drv->suspend, i); 361 suspend_report_result(drv->suspend, i);
360 } else { 362 if (i)
361 pci_save_state(pci_dev); 363 return i;
362 /* 364
363 * This is for compatibility with existing code with legacy PM 365 if (pci_dev->state_saved)
364 * support. 366 goto Fixup;
365 */ 367
366 pci_pm_set_unknown_state(pci_dev); 368 if (WARN_ON_ONCE(pci_dev->current_state != PCI_D0))
369 goto Fixup;
367 } 370 }
368 371
372 pci_save_state(pci_dev);
373 /*
374 * This is for compatibility with existing code with legacy PM support.
375 */
376 pci_pm_set_unknown_state(pci_dev);
377
378 Fixup:
369 pci_fixup_device(pci_fixup_suspend, pci_dev); 379 pci_fixup_device(pci_fixup_suspend, pci_dev);
370 380
371 return i; 381 return i;
@@ -386,81 +396,34 @@ static int pci_legacy_suspend_late(struct device *dev, pm_message_t state)
386 396
387static int pci_legacy_resume_early(struct device *dev) 397static int pci_legacy_resume_early(struct device *dev)
388{ 398{
389 int error = 0;
390 struct pci_dev * pci_dev = to_pci_dev(dev); 399 struct pci_dev * pci_dev = to_pci_dev(dev);
391 struct pci_driver * drv = pci_dev->driver; 400 struct pci_driver * drv = pci_dev->driver;
392 401
393 pci_fixup_device(pci_fixup_resume_early, pci_dev); 402 return drv && drv->resume_early ?
394 403 drv->resume_early(pci_dev) : 0;
395 if (drv && drv->resume_early)
396 error = drv->resume_early(pci_dev);
397 return error;
398} 404}
399 405
400static int pci_legacy_resume(struct device *dev) 406static int pci_legacy_resume(struct device *dev)
401{ 407{
402 int error;
403 struct pci_dev * pci_dev = to_pci_dev(dev); 408 struct pci_dev * pci_dev = to_pci_dev(dev);
404 struct pci_driver * drv = pci_dev->driver; 409 struct pci_driver * drv = pci_dev->driver;
405 410
406 pci_fixup_device(pci_fixup_resume, pci_dev); 411 pci_fixup_device(pci_fixup_resume, pci_dev);
407 412
408 if (drv && drv->resume) { 413 return drv && drv->resume ?
409 error = drv->resume(pci_dev); 414 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} 415}
417 416
418/* Auxiliary functions used by the new power management framework */ 417/* Auxiliary functions used by the new power management framework */
419 418
420static 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
440static bool pci_is_bridge(struct pci_dev *pci_dev)
441{
442 return !!(pci_dev->subordinate);
443}
444
445static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev) 419static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev)
446{ 420{
447 if (pci_restore_standard_config(pci_dev)) 421 pci_restore_standard_config(pci_dev);
448 pci_fixup_device(pci_fixup_resume_early, pci_dev); 422 pci_fixup_device(pci_fixup_resume_early, pci_dev);
449} 423}
450 424
451static int pci_pm_default_resume(struct pci_dev *pci_dev) 425static int pci_pm_default_resume(struct pci_dev *pci_dev)
452{ 426{
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); 427 pci_fixup_device(pci_fixup_resume, pci_dev);
465 428
466 if (!pci_is_bridge(pci_dev)) 429 if (!pci_is_bridge(pci_dev))
@@ -575,11 +538,11 @@ static int pci_pm_resume_noirq(struct device *dev)
575 struct device_driver *drv = dev->driver; 538 struct device_driver *drv = dev->driver;
576 int error = 0; 539 int error = 0;
577 540
541 pci_pm_default_resume_noirq(pci_dev);
542
578 if (pci_has_legacy_pm_support(pci_dev)) 543 if (pci_has_legacy_pm_support(pci_dev))
579 return pci_legacy_resume_early(dev); 544 return pci_legacy_resume_early(dev);
580 545
581 pci_pm_default_resume_noirq(pci_dev);
582
583 if (drv && drv->pm && drv->pm->resume_noirq) 546 if (drv && drv->pm && drv->pm->resume_noirq)
584 error = drv->pm->resume_noirq(dev); 547 error = drv->pm->resume_noirq(dev);
585 548
@@ -730,11 +693,11 @@ static int pci_pm_restore_noirq(struct device *dev)
730 struct device_driver *drv = dev->driver; 693 struct device_driver *drv = dev->driver;
731 int error = 0; 694 int error = 0;
732 695
696 pci_pm_default_resume_noirq(pci_dev);
697
733 if (pci_has_legacy_pm_support(pci_dev)) 698 if (pci_has_legacy_pm_support(pci_dev))
734 return pci_legacy_resume_early(dev); 699 return pci_legacy_resume_early(dev);
735 700
736 pci_pm_default_resume_noirq(pci_dev);
737
738 if (drv && drv->pm && drv->pm->restore_noirq) 701 if (drv && drv->pm && drv->pm->restore_noirq)
739 error = drv->pm->restore_noirq(dev); 702 error = drv->pm->restore_noirq(dev);
740 703