diff options
Diffstat (limited to 'drivers/pci/pci-driver.c')
-rw-r--r-- | drivers/pci/pci-driver.c | 91 |
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 | ||
387 | static int pci_legacy_resume_early(struct device *dev) | 397 | static 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 | ||
400 | static int pci_legacy_resume(struct device *dev) | 406 | static 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 | ||
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) | 419 | static 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 | ||
451 | static int pci_pm_default_resume(struct pci_dev *pci_dev) | 425 | static 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 | ||