diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-07-04 13:39:41 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-07-04 13:39:41 -0700 |
commit | 408c9861c6979db974455b9e7a9bcadd60e0934c (patch) | |
tree | 9bdb862da2883cd4f74297d01ec8ce3b4619dd66 /drivers/pci | |
parent | b39de277b02ffd8e3dccb01e9159bd45cb07b95d (diff) | |
parent | 8f8e5c3e2796eaf150d6262115af12707c2616dd (diff) |
Merge tag 'pm-4.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull power management updates from Rafael Wysocki:
"The big ticket items here are the rework of suspend-to-idle in order
to add proper support for power button wakeup from it on recent Dell
laptops and the rework of interfaces exporting the current CPU
frequency on x86.
In addition to that, support for a few new pieces of hardware is
added, the PCI/ACPI device wakeup infrastructure is simplified
significantly and the wakeup IRQ framework is fixed to unbreak the IRQ
bus locking infrastructure.
Also, there are some functional improvements for intel_pstate, tools
updates and small fixes and cleanups all over.
Specifics:
- Rework suspend-to-idle to allow it to take wakeup events signaled
by the EC into account on ACPI-based platforms in order to properly
support power button wakeup from suspend-to-idle on recent Dell
laptops (Rafael Wysocki).
That includes the core suspend-to-idle code rework, support for the
Low Power S0 _DSM interface, and support for the ACPI INT0002
Virtual GPIO device from Hans de Goede (required for USB keyboard
wakeup from suspend-to-idle to work on some machines).
- Stop trying to export the current CPU frequency via /proc/cpuinfo
on x86 as that is inaccurate and confusing (Len Brown).
- Rework the way in which the current CPU frequency is exported by
the kernel (over the cpufreq sysfs interface) on x86 systems with
the APERF and MPERF registers by always using values read from
these registers, when available, to compute the current frequency
regardless of which cpufreq driver is in use (Len Brown).
- Rework the PCI/ACPI device wakeup infrastructure to remove the
questionable and artificial distinction between "devices that can
wake up the system from sleep states" and "devices that can
generate wakeup signals in the working state" from it, which allows
the code to be simplified quite a bit (Rafael Wysocki).
- Fix the wakeup IRQ framework by making it use SRCU instead of RCU
which doesn't allow sleeping in the read-side critical sections,
but which in turn is expected to be allowed by the IRQ bus locking
infrastructure (Thomas Gleixner).
- Modify some computations in the intel_pstate driver to avoid
rounding errors resulting from them (Srinivas Pandruvada).
- Reduce the overhead of the intel_pstate driver in the HWP
(hardware-managed P-states) mode and when the "performance" P-state
selection algorithm is in use by making it avoid registering
scheduler callbacks in those cases (Len Brown).
- Rework the energy_performance_preference sysfs knob in intel_pstate
by changing the values that correspond to different symbolic hint
names used by it (Len Brown).
- Make it possible to use more than one cpuidle driver at the same
time on ARM (Daniel Lezcano).
- Make it possible to prevent the cpuidle menu governor from using
the 0 state by disabling it via sysfs (Nicholas Piggin).
- Add support for FFH (Fixed Functional Hardware) MWAIT in ACPI C1 on
AMD systems (Yazen Ghannam).
- Make the CPPC cpufreq driver take the lowest nonlinear performance
information into account (Prashanth Prakash).
- Add support for hi3660 to the cpufreq-dt driver, fix the imx6q
driver and clean up the sfi, exynos5440 and intel_pstate drivers
(Colin Ian King, Krzysztof Kozlowski, Octavian Purdila, Rafael
Wysocki, Tao Wang).
- Fix a few minor issues in the generic power domains (genpd)
framework and clean it up somewhat (Krzysztof Kozlowski, Mikko
Perttunen, Viresh Kumar).
- Fix a couple of minor issues in the operating performance points
(OPP) framework and clean it up somewhat (Viresh Kumar).
- Fix a CONFIG dependency in the hibernation core and clean it up
slightly (Balbir Singh, Arvind Yadav, BaoJun Luo).
- Add rk3228 support to the rockchip-io adaptive voltage scaling
(AVS) driver (David Wu).
- Fix an incorrect bit shift operation in the RAPL power capping
driver (Adam Lessnau).
- Add support for the EPP field in the HWP (hardware managed
P-states) control register, HWP.EPP, to the x86_energy_perf_policy
tool and update msr-index.h with HWP.EPP values (Len Brown).
- Fix some minor issues in the turbostat tool (Len Brown).
- Add support for AMD family 0x17 CPUs to the cpupower tool and fix a
minor issue in it (Sherry Hurwitz).
- Assorted cleanups, mostly related to the constification of some
data structures (Arvind Yadav, Joe Perches, Kees Cook, Krzysztof
Kozlowski)"
* tag 'pm-4.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (69 commits)
cpufreq: Update scaling_cur_freq documentation
cpufreq: intel_pstate: Clean up after performance governor changes
PM: hibernate: constify attribute_group structures.
cpuidle: menu: allow state 0 to be disabled
intel_idle: Use more common logging style
PM / Domains: Fix missing default_power_down_ok comment
PM / Domains: Fix unsafe iteration over modified list of domains
PM / Domains: Fix unsafe iteration over modified list of domain providers
PM / Domains: Fix unsafe iteration over modified list of device links
PM / Domains: Handle safely genpd_syscore_switch() call on non-genpd device
PM / Domains: Call driver's noirq callbacks
PM / core: Drop run_wake flag from struct dev_pm_info
PCI / PM: Simplify device wakeup settings code
PCI / PM: Drop pme_interrupt flag from struct pci_dev
ACPI / PM: Consolidate device wakeup settings code
ACPI / PM: Drop run_wake from struct acpi_device_wakeup_flags
PM / QoS: constify *_attribute_group.
PM / AVS: rockchip-io: add io selectors and supplies for rk3228
powercap/RAPL: prevent overridding bits outside of the mask
PM / sysfs: Constify attribute groups
...
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/pci-acpi.c | 90 | ||||
-rw-r--r-- | drivers/pci/pci-driver.c | 2 | ||||
-rw-r--r-- | drivers/pci/pci-mid.c | 10 | ||||
-rw-r--r-- | drivers/pci/pci.c | 68 | ||||
-rw-r--r-- | drivers/pci/pci.h | 9 | ||||
-rw-r--r-- | drivers/pci/pcie/pme.c | 16 |
6 files changed, 71 insertions, 124 deletions
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 47070cff508c..e70c1c7ba1bf 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -394,29 +394,26 @@ bool pciehp_is_native(struct pci_dev *pdev) /** * pci_acpi_wake_bus - Root bus wakeup notification fork function. - * @work: Work item to handle. + * @context: Device wakeup context. */ -static void pci_acpi_wake_bus(struct work_struct *work) +static void pci_acpi_wake_bus(struct acpi_device_wakeup_context *context) { struct acpi_device *adev; struct acpi_pci_root *root; - adev = container_of(work, struct acpi_device, wakeup.context.work); + adev = container_of(context, struct acpi_device, wakeup.context); root = acpi_driver_data(adev); pci_pme_wakeup_bus(root->bus); } /** * pci_acpi_wake_dev - PCI device wakeup notification work function. - * @handle: ACPI handle of a device the notification is for. - * @work: Work item to handle. + * @context: Device wakeup context. */ -static void pci_acpi_wake_dev(struct work_struct *work) +static void pci_acpi_wake_dev(struct acpi_device_wakeup_context *context) { - struct acpi_device_wakeup_context *context; struct pci_dev *pci_dev; - context = container_of(work, struct acpi_device_wakeup_context, work); pci_dev = to_pci_dev(context->dev); if (pci_dev->pme_poll) @@ -424,7 +421,7 @@ static void pci_acpi_wake_dev(struct work_struct *work) if (pci_dev->current_state == PCI_D3cold) { pci_wakeup_event(pci_dev); - pm_runtime_resume(&pci_dev->dev); + pm_request_resume(&pci_dev->dev); return; } @@ -433,7 +430,7 @@ static void pci_acpi_wake_dev(struct work_struct *work) pci_check_pme_status(pci_dev); pci_wakeup_event(pci_dev); - pm_runtime_resume(&pci_dev->dev); + pm_request_resume(&pci_dev->dev); pci_pme_wakeup_bus(pci_dev->subordinate); } @@ -572,67 +569,29 @@ static pci_power_t acpi_pci_get_power_state(struct pci_dev *dev) return state_conv[state]; } -static bool acpi_pci_can_wakeup(struct pci_dev *dev) -{ - struct acpi_device *adev = ACPI_COMPANION(&dev->dev); - return adev ? acpi_device_can_wakeup(adev) : false; -} - -static void acpi_pci_propagate_wakeup_enable(struct pci_bus *bus, bool enable) -{ - while (bus->parent) { - if (!acpi_pm_device_sleep_wake(&bus->self->dev, enable)) - return; - bus = bus->parent; - } - - /* We have reached the root bus. */ - if (bus->bridge) - acpi_pm_device_sleep_wake(bus->bridge, enable); -} - -static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable) -{ - if (acpi_pci_can_wakeup(dev)) - return acpi_pm_device_sleep_wake(&dev->dev, enable); - - acpi_pci_propagate_wakeup_enable(dev->bus, enable); - return 0; -} - -static void acpi_pci_propagate_run_wake(struct pci_bus *bus, bool enable) +static int acpi_pci_propagate_wakeup(struct pci_bus *bus, bool enable) { while (bus->parent) { - struct pci_dev *bridge = bus->self; + if (acpi_pm_device_can_wakeup(&bus->self->dev)) + return acpi_pm_set_device_wakeup(&bus->self->dev, enable); - if (bridge->pme_interrupt) - return; - if (!acpi_pm_device_run_wake(&bridge->dev, enable)) - return; bus = bus->parent; } /* We have reached the root bus. */ - if (bus->bridge) - acpi_pm_device_run_wake(bus->bridge, enable); + if (bus->bridge) { + if (acpi_pm_device_can_wakeup(bus->bridge)) + return acpi_pm_set_device_wakeup(bus->bridge, enable); + } + return 0; } -static int acpi_pci_run_wake(struct pci_dev *dev, bool enable) +static int acpi_pci_wakeup(struct pci_dev *dev, bool enable) { - /* - * Per PCI Express Base Specification Revision 2.0 section - * 5.3.3.2 Link Wakeup, platform support is needed for D3cold - * waking up to power on the main link even if there is PME - * support for D3cold - */ - if (dev->pme_interrupt && !dev->runtime_d3cold) - return 0; - - if (!acpi_pm_device_run_wake(&dev->dev, enable)) - return 0; + if (acpi_pm_device_can_wakeup(&dev->dev)) + return acpi_pm_set_device_wakeup(&dev->dev, enable); - acpi_pci_propagate_run_wake(dev->bus, enable); - return 0; + return acpi_pci_propagate_wakeup(dev->bus, enable); } static bool acpi_pci_need_resume(struct pci_dev *dev) @@ -656,8 +615,7 @@ static const struct pci_platform_pm_ops acpi_pci_platform_pm = { .set_state = acpi_pci_set_power_state, .get_state = acpi_pci_get_power_state, .choose_state = acpi_pci_choose_state, - .sleep_wake = acpi_pci_sleep_wake, - .run_wake = acpi_pci_run_wake, + .set_wakeup = acpi_pci_wakeup, .need_resume = acpi_pci_need_resume, }; @@ -780,9 +738,7 @@ static void pci_acpi_setup(struct device *dev) return; device_set_wakeup_capable(dev, true); - acpi_pci_sleep_wake(pci_dev, false); - if (adev->wakeup.flags.run_wake) - device_set_run_wake(dev, true); + acpi_pci_wakeup(pci_dev, false); } static void pci_acpi_cleanup(struct device *dev) @@ -793,10 +749,8 @@ static void pci_acpi_cleanup(struct device *dev) return; pci_acpi_remove_pm_notifier(adev); - if (adev->wakeup.flags.valid) { + if (adev->wakeup.flags.valid) device_set_wakeup_capable(dev, false); - device_set_run_wake(dev, false); - } } static bool pci_acpi_bus_match(struct device *dev) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 00e10bf7f6a2..df4aead394f2 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -1219,7 +1219,7 @@ static int pci_pm_runtime_resume(struct device *dev) pci_restore_standard_config(pci_dev); pci_fixup_device(pci_fixup_resume_early, pci_dev); - __pci_enable_wake(pci_dev, PCI_D0, true, false); + pci_enable_wake(pci_dev, PCI_D0, false); pci_fixup_device(pci_fixup_resume, pci_dev); rc = pm->runtime_resume(dev); diff --git a/drivers/pci/pci-mid.c b/drivers/pci/pci-mid.c index 1c4af7227bca..a4ac940c7696 100644 --- a/drivers/pci/pci-mid.c +++ b/drivers/pci/pci-mid.c @@ -39,12 +39,7 @@ static pci_power_t mid_pci_choose_state(struct pci_dev *pdev) return PCI_D3hot; } -static int mid_pci_sleep_wake(struct pci_dev *dev, bool enable) -{ - return 0; -} - -static int mid_pci_run_wake(struct pci_dev *dev, bool enable) +static int mid_pci_wakeup(struct pci_dev *dev, bool enable) { return 0; } @@ -59,8 +54,7 @@ static const struct pci_platform_pm_ops mid_pci_platform_pm = { .set_state = mid_pci_set_power_state, .get_state = mid_pci_get_power_state, .choose_state = mid_pci_choose_state, - .sleep_wake = mid_pci_sleep_wake, - .run_wake = mid_pci_run_wake, + .set_wakeup = mid_pci_wakeup, .need_resume = mid_pci_need_resume, }; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 563901cd9c06..0b5302a9fdae 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -574,8 +574,7 @@ static const struct pci_platform_pm_ops *pci_platform_pm; int pci_set_platform_pm(const struct pci_platform_pm_ops *ops) { if (!ops->is_manageable || !ops->set_state || !ops->get_state || - !ops->choose_state || !ops->sleep_wake || !ops->run_wake || - !ops->need_resume) + !ops->choose_state || !ops->set_wakeup || !ops->need_resume) return -EINVAL; pci_platform_pm = ops; return 0; @@ -603,16 +602,10 @@ static inline pci_power_t platform_pci_choose_state(struct pci_dev *dev) pci_platform_pm->choose_state(dev) : PCI_POWER_ERROR; } -static inline int platform_pci_sleep_wake(struct pci_dev *dev, bool enable) +static inline int platform_pci_set_wakeup(struct pci_dev *dev, bool enable) { return pci_platform_pm ? - pci_platform_pm->sleep_wake(dev, enable) : -ENODEV; -} - -static inline int platform_pci_run_wake(struct pci_dev *dev, bool enable) -{ - return pci_platform_pm ? - pci_platform_pm->run_wake(dev, enable) : -ENODEV; + pci_platform_pm->set_wakeup(dev, enable) : -ENODEV; } static inline bool platform_pci_need_resume(struct pci_dev *dev) @@ -1805,6 +1798,23 @@ static void __pci_pme_active(struct pci_dev *dev, bool enable) pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr); } +static void pci_pme_restore(struct pci_dev *dev) +{ + u16 pmcsr; + + if (!dev->pme_support) + return; + + pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); + if (dev->wakeup_prepared) { + pmcsr |= PCI_PM_CTRL_PME_ENABLE; + } else { + pmcsr &= ~PCI_PM_CTRL_PME_ENABLE; + pmcsr |= PCI_PM_CTRL_PME_STATUS; + } + pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr); +} + /** * pci_pme_active - enable or disable PCI device's PME# function * @dev: PCI device to handle. @@ -1872,10 +1882,9 @@ void pci_pme_active(struct pci_dev *dev, bool enable) EXPORT_SYMBOL(pci_pme_active); /** - * __pci_enable_wake - enable PCI device as wakeup event source + * pci_enable_wake - enable PCI device as wakeup event source * @dev: PCI device affected * @state: PCI state from which device will issue wakeup events - * @runtime: True if the events are to be generated at run time * @enable: True to enable event generation; false to disable * * This enables the device as a wakeup event source, or disables it. @@ -1891,17 +1900,18 @@ EXPORT_SYMBOL(pci_pme_active); * Error code depending on the platform is returned if both the platform and * the native mechanism fail to enable the generation of wake-up events */ -int __pci_enable_wake(struct pci_dev *dev, pci_power_t state, - bool runtime, bool enable) +int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable) { int ret = 0; - if (enable && !runtime && !device_may_wakeup(&dev->dev)) - return -EINVAL; - - /* Don't do the same thing twice in a row for one device. */ - if (!!enable == !!dev->wakeup_prepared) + /* + * Don't do the same thing twice in a row for one device, but restore + * PME Enable in case it has been updated by config space restoration. + */ + if (!!enable == !!dev->wakeup_prepared) { + pci_pme_restore(dev); return 0; + } /* * According to "PCI System Architecture" 4th ed. by Tom Shanley & Don @@ -1916,24 +1926,20 @@ int __pci_enable_wake(struct pci_dev *dev, pci_power_t state, pci_pme_active(dev, true); else ret = 1; - error = runtime ? platform_pci_run_wake(dev, true) : - platform_pci_sleep_wake(dev, true); + error = platform_pci_set_wakeup(dev, true); if (ret) ret = error; if (!ret) dev->wakeup_prepared = true; } else { - if (runtime) - platform_pci_run_wake(dev, false); - else - platform_pci_sleep_wake(dev, false); + platform_pci_set_wakeup(dev, false); pci_pme_active(dev, false); dev->wakeup_prepared = false; } return ret; } -EXPORT_SYMBOL(__pci_enable_wake); +EXPORT_SYMBOL(pci_enable_wake); /** * pci_wake_from_d3 - enable/disable device to wake up from D3_hot or D3_cold @@ -2075,12 +2081,12 @@ int pci_finish_runtime_suspend(struct pci_dev *dev) dev->runtime_d3cold = target_state == PCI_D3cold; - __pci_enable_wake(dev, target_state, true, pci_dev_run_wake(dev)); + pci_enable_wake(dev, target_state, pci_dev_run_wake(dev)); error = pci_set_power_state(dev, target_state); if (error) { - __pci_enable_wake(dev, target_state, true, false); + pci_enable_wake(dev, target_state, false); dev->runtime_d3cold = false; } @@ -2099,7 +2105,7 @@ bool pci_dev_run_wake(struct pci_dev *dev) { struct pci_bus *bus = dev->bus; - if (device_run_wake(&dev->dev)) + if (device_can_wakeup(&dev->dev)) return true; if (!dev->pme_support) @@ -2112,7 +2118,7 @@ bool pci_dev_run_wake(struct pci_dev *dev) while (bus->parent) { struct pci_dev *bridge = bus->self; - if (device_run_wake(&bridge->dev)) + if (device_can_wakeup(&bridge->dev)) return true; bus = bus->parent; @@ -2120,7 +2126,7 @@ bool pci_dev_run_wake(struct pci_dev *dev) /* We have reached the root bus. */ if (bus->bridge) - return device_run_wake(bus->bridge); + return device_can_wakeup(bus->bridge); return false; } diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index f8113e5b9812..240b2c0fed4b 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -47,11 +47,7 @@ int pci_probe_reset_function(struct pci_dev *dev); * platform; to be used during system-wide transitions from a * sleeping state to the working state and vice versa * - * @sleep_wake: enables/disables the system wake up capability of given device - * - * @run_wake: enables/disables the platform to generate run-time wake-up events - * for given device (the device's wake-up capability has to be - * enabled by @sleep_wake for this feature to work) + * @set_wakeup: enables/disables wakeup capability for the device * * @need_resume: returns 'true' if the given device (which is currently * suspended) needs to be resumed to be configured for system @@ -65,8 +61,7 @@ struct pci_platform_pm_ops { int (*set_state)(struct pci_dev *dev, pci_power_t state); pci_power_t (*get_state)(struct pci_dev *dev); pci_power_t (*choose_state)(struct pci_dev *dev); - int (*sleep_wake)(struct pci_dev *dev, bool enable); - int (*run_wake)(struct pci_dev *dev, bool enable); + int (*set_wakeup)(struct pci_dev *dev, bool enable); bool (*need_resume)(struct pci_dev *dev); }; diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c index 2dd1c68e6de8..80e58d25006d 100644 --- a/drivers/pci/pcie/pme.c +++ b/drivers/pci/pcie/pme.c @@ -294,31 +294,29 @@ static irqreturn_t pcie_pme_irq(int irq, void *context) } /** - * pcie_pme_set_native - Set the PME interrupt flag for given device. + * pcie_pme_can_wakeup - Set the wakeup capability flag. * @dev: PCI device to handle. * @ign: Ignored. */ -static int pcie_pme_set_native(struct pci_dev *dev, void *ign) +static int pcie_pme_can_wakeup(struct pci_dev *dev, void *ign) { - device_set_run_wake(&dev->dev, true); - dev->pme_interrupt = true; + device_set_wakeup_capable(&dev->dev, true); return 0; } /** - * pcie_pme_mark_devices - Set the PME interrupt flag for devices below a port. + * pcie_pme_mark_devices - Set the wakeup flag for devices below a port. * @port: PCIe root port or event collector to handle. * * For each device below given root port, including the port itself (or for each * root complex integrated endpoint if @port is a root complex event collector) - * set the flag indicating that it can signal run-time wake-up events via PCIe - * PME interrupts. + * set the flag indicating that it can signal run-time wake-up events. */ static void pcie_pme_mark_devices(struct pci_dev *port) { - pcie_pme_set_native(port, NULL); + pcie_pme_can_wakeup(port, NULL); if (port->subordinate) - pci_walk_bus(port->subordinate, pcie_pme_set_native, NULL); + pci_walk_bus(port->subordinate, pcie_pme_can_wakeup, NULL); } /** |