Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PCI PM: Make pci_set_power_state() handle devices with no PM support

There is a problem with PCI devices without any PM support (either
native or through the platform) that pci_set_power_state() always
returns error code for them, even if they are being put into D0.
However, such devices are always in D0, so pci_set_power_state()
should return success when attempting to put such a device into D0.
It also should update the current_state field for these devices as
appropriate. This modification is necessary so that the standard
configuration registers of these devices are successfully restored by
pci_restore_standard_config() during the "early" phase of resume.

In addition, pci_set_power_state() should check the value of
current_state before calling the platform to change the power state
of the device to avoid doing that unnecessarily.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Ingo Molnar <mingo@elte.hu>
Acked-by: Jesse Barnes <jbarnes@virtuousgeek.org>

+12 -6
+12 -6
drivers/pci/pci.c
··· 439 439 u16 pmcsr; 440 440 bool need_restore = false; 441 441 442 + /* Check if we're already there */ 443 + if (dev->current_state == state) 444 + return 0; 445 + 442 446 if (!dev->pm_cap) 443 447 return -EIO; 444 448 ··· 453 449 * Can enter D0 from any state, but if we can only go deeper 454 450 * to sleep if we're already in a low power state 455 451 */ 456 - if (dev->current_state == state) { 457 - /* we're already there */ 458 - return 0; 459 - } else if (state != PCI_D0 && dev->current_state <= PCI_D3cold 452 + if (state != PCI_D0 && dev->current_state <= PCI_D3cold 460 453 && dev->current_state > state) { 461 454 dev_err(&dev->dev, "invalid power transition " 462 455 "(from state %d to %d)\n", dev->current_state, state); ··· 571 570 */ 572 571 return 0; 573 572 574 - if (state == PCI_D0 && platform_pci_power_manageable(dev)) { 573 + /* Check if we're already there */ 574 + if (dev->current_state == state) 575 + return 0; 576 + 577 + if (state == PCI_D0) { 575 578 /* 576 579 * Allow the platform to change the state, for example via ACPI 577 580 * _PR0, _PS0 and some such, but do not trust it. 578 581 */ 579 - int ret = platform_pci_set_power_state(dev, PCI_D0); 582 + int ret = platform_pci_power_manageable(dev) ? 583 + platform_pci_set_power_state(dev, PCI_D0) : 0; 580 584 if (!ret) 581 585 pci_update_current_state(dev, PCI_D0); 582 586 }