Power management updates for v4.15-rc1

- Relocate the OPP (Operating Performance Points) framework to its
    own directory under drivers/ and add support for power domain
    performance states to it (Viresh Kumar).
 
  - Modify the PM core, the PCI bus type and the ACPI PM domain to
    support power management driver flags allowing device drivers to
    specify their capabilities and preferences regarding the handling
    of devices with enabled runtime PM during system suspend/resume
    and clean up that code somewhat (Rafael Wysocki, Ulf Hansson).
 
  - Add frequency-invariant accounting support to the task scheduler
    on ARM and ARM64 (Dietmar Eggemann).
 
  - Fix PM QoS device resume latency framework to prevent "no
    restriction" requests from overriding requests with specific
    requirements and drop the confusing PM_QOS_FLAG_REMOTE_WAKEUP
    device PM QoS flag (Rafael Wysocki).
 
  - Drop legacy class suspend/resume operations from the PM core
    and drop legacy bus type suspend and resume callbacks from
    ARM/locomo (Rafael Wysocki).
 
  - Add min/max frequency support to devfreq and clean it up
    somewhat (Chanwoo Choi).
 
  - Rework wakeup support in the generic power domains (genpd)
    framework and update some of its users accordingly (Geert
    Uytterhoeven).
 
  - Convert timers in the PM core to use timer_setup() (Kees Cook).
 
  - Add support for exposing the SLP_S0 (Low Power S0 Idle)
    residency counter based on the LPIT ACPI table on Intel
    platforms (Srinivas Pandruvada).
 
  - Add per-CPU PM QoS resume latency support to the ladder cpuidle
    governor (Ramesh Thomas).
 
  - Fix a deadlock between the wakeup notify handler and the
    notifier removal in the ACPI core (Ville Syrjälä).
 
  - Fix a cpufreq schedutil governor issue causing it to use
    stale cached frequency values sometimes (Viresh Kumar).
 
  - Fix an issue in the system suspend core support code causing
    wakeup events detection to fail in some cases (Rajat Jain).
 
  - Fix the generic power domains (genpd) framework to prevent
    the PM core from using the direct-complete optimization with
    it as that is guaranteed to fail (Ulf Hansson).
 
  - Fix a minor issue in the cpuidle core and clean it up a bit
    (Gaurav Jindal, Nicholas Piggin).
 
  - Fix and clean up the intel_idle and ARM cpuidle drivers (Jason
    Baron, Len Brown, Leo Yan).
 
  - Fix a couple of minor issues in the OPP framework and clean it
    up (Arvind Yadav, Fabio Estevam, Sudeep Holla, Tobias Jordan).
 
  - Fix and clean up some cpufreq drivers and fix a minor issue in
    the cpufreq statistics code (Arvind Yadav, Bhumika Goyal, Fabio
    Estevam, Gautham Shenoy, Gustavo Silva, Marek Szyprowski, Masahiro
    Yamada, Robert Jarzmik, Zumeng Chen).
 
  - Fix minor issues in the system suspend and hibernation core, in
    power management documentation and in the AVS (Adaptive Voltage
    Scaling) framework (Helge Deller, Himanshu Jha, Joe Perches,
    Rafael Wysocki).
 
  - Fix some issues in the cpupower utility and document that Shuah
    Khan is going to maintain it going forward (Prarit Bhargava,
    Shuah Khan).
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQIcBAABCAAGBQJaCg2eAAoJEILEb/54YlRxGhAP/26D5TvfQ65wtf2W0Gas/tsP
 +24SzCLQO2GWalhbOXZbXhnBn/eCovKB6T8VB0V7Bff0VcUOK9szmBu9hOBJfXGN
 ec2oYKtWPwqzkgPfbqjZhQTp5EXg/dmWYOhAMA7HLMv7oVZqoRZ/MNOJPooXAmQj
 NEVWj3Eap0anic0ZgGMN4FaIMF6CHP2rAheqWQVXihhXpjIOWrJCjEoPZfbH1bFC
 +zd9Batd3rq+eZ5dYxg+znpYcZi69kmPw+KASYsaWTJzNjYbR+VLOxqzx7Icdgbz
 4glwWNe7lZGCAj+BIKGaHN5CR/fAXqkPvJ8csn6qISyUJ1Gph6otRfvuUaK58F4T
 1Rmcj+mGXgJBcjaQGmVQIITKD6drBW/l50MJlze5JUM4A7VM2Di/cctgoWmOJsnO
 2f6D6PYGuW0Fe8uUVGki/ddApXvoTGbEx+ncQ5+At+mLMKJwYfND9h2stOkCcrTy
 k4Pr+XpVU9hXwYVX3a1Au41bFQiXYwguxD1TH1LaY3liAWXvo0qNc/Ib6mW8e7pL
 wqPoe2/yxgVw5rsQPcKxVxAFFgjAAIdU3Xw44ETTPN315CLOoiuZgWkeTrnYCdix
 DaBWu1VN9tU5U6FWBlWXDb06i5qvSo3aYzLnSBC6fm7qX2SuDxGiQTcyOQ7H1NiQ
 d1wzhgObW98N7rZRaByu
 =QTnx
 -----END PGP SIGNATURE-----

Merge tag 'pm-4.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull power management updates from Rafael Wysocki:
 "There are no real big ticket items here this time.

  The most noticeable change is probably the relocation of the OPP
  (Operating Performance Points) framework to its own directory under
  drivers/ as it has grown big enough for that. Also Viresh is now going
  to maintain it and send pull requests for it to me, so you will see
  this change in the git history going forward (but still not right
  now).

  Another noticeable set of changes is the modifications of the PM core,
  the PCI subsystem and the ACPI PM domain to allow of more integration
  between system-wide suspend/resume and runtime PM. For now it's just a
  way to avoid resuming devices from runtime suspend unnecessarily
  during system suspend (if the driver sets a flag to indicate its
  readiness for that) and in the works is an analogous mechanism to
  allow devices to stay suspended after system resume.

  In addition to that, we have some changes related to supporting
  frequency-invariant CPU utilization metrics in the scheduler and in
  the schedutil cpufreq governor on ARM and changes to add support for
  device performance states to the generic power domains (genpd)
  framework.

  The rest is mostly fixes and cleanups of various sorts.

  Specifics:

   - Relocate the OPP (Operating Performance Points) framework to its
     own directory under drivers/ and add support for power domain
     performance states to it (Viresh Kumar).

   - Modify the PM core, the PCI bus type and the ACPI PM domain to
     support power management driver flags allowing device drivers to
     specify their capabilities and preferences regarding the handling
     of devices with enabled runtime PM during system suspend/resume and
     clean up that code somewhat (Rafael Wysocki, Ulf Hansson).

   - Add frequency-invariant accounting support to the task scheduler on
     ARM and ARM64 (Dietmar Eggemann).

   - Fix PM QoS device resume latency framework to prevent "no
     restriction" requests from overriding requests with specific
     requirements and drop the confusing PM_QOS_FLAG_REMOTE_WAKEUP
     device PM QoS flag (Rafael Wysocki).

   - Drop legacy class suspend/resume operations from the PM core and
     drop legacy bus type suspend and resume callbacks from ARM/locomo
     (Rafael Wysocki).

   - Add min/max frequency support to devfreq and clean it up somewhat
     (Chanwoo Choi).

   - Rework wakeup support in the generic power domains (genpd)
     framework and update some of its users accordingly (Geert
     Uytterhoeven).

   - Convert timers in the PM core to use timer_setup() (Kees Cook).

   - Add support for exposing the SLP_S0 (Low Power S0 Idle) residency
     counter based on the LPIT ACPI table on Intel platforms (Srinivas
     Pandruvada).

   - Add per-CPU PM QoS resume latency support to the ladder cpuidle
     governor (Ramesh Thomas).

   - Fix a deadlock between the wakeup notify handler and the notifier
     removal in the ACPI core (Ville Syrjälä).

   - Fix a cpufreq schedutil governor issue causing it to use stale
     cached frequency values sometimes (Viresh Kumar).

   - Fix an issue in the system suspend core support code causing wakeup
     events detection to fail in some cases (Rajat Jain).

   - Fix the generic power domains (genpd) framework to prevent the PM
     core from using the direct-complete optimization with it as that is
     guaranteed to fail (Ulf Hansson).

   - Fix a minor issue in the cpuidle core and clean it up a bit (Gaurav
     Jindal, Nicholas Piggin).

   - Fix and clean up the intel_idle and ARM cpuidle drivers (Jason
     Baron, Len Brown, Leo Yan).

   - Fix a couple of minor issues in the OPP framework and clean it up
     (Arvind Yadav, Fabio Estevam, Sudeep Holla, Tobias Jordan).

   - Fix and clean up some cpufreq drivers and fix a minor issue in the
     cpufreq statistics code (Arvind Yadav, Bhumika Goyal, Fabio
     Estevam, Gautham Shenoy, Gustavo Silva, Marek Szyprowski, Masahiro
     Yamada, Robert Jarzmik, Zumeng Chen).

   - Fix minor issues in the system suspend and hibernation core, in
     power management documentation and in the AVS (Adaptive Voltage
     Scaling) framework (Helge Deller, Himanshu Jha, Joe Perches, Rafael
     Wysocki).

   - Fix some issues in the cpupower utility and document that Shuah
     Khan is going to maintain it going forward (Prarit Bhargava, Shuah
     Khan)"

* tag 'pm-4.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (88 commits)
  tools/power/cpupower: add libcpupower.so.0.0.1 to .gitignore
  tools/power/cpupower: Add 64 bit library detection
  intel_idle: Graceful probe failure when MWAIT is disabled
  cpufreq: schedutil: Reset cached_raw_freq when not in sync with next_freq
  freezer: Fix typo in freezable_schedule_timeout() comment
  PM / s2idle: Clear the events_check_enabled flag
  cpufreq: stats: Handle the case when trans_table goes beyond PAGE_SIZE
  cpufreq: arm_big_little: make cpufreq_arm_bL_ops structures const
  cpufreq: arm_big_little: make function arguments and structure pointer const
  cpuidle: Avoid assignment in if () argument
  cpuidle: Clean up cpuidle_enable_device() error handling a bit
  ACPI / PM: Fix acpi_pm_notifier_lock vs flush_workqueue() deadlock
  PM / Domains: Fix genpd to deal with drivers returning 1 from ->prepare()
  cpuidle: ladder: Add per CPU PM QoS resume latency support
  PM / QoS: Fix device resume latency framework
  PM / domains: Rework governor code to be more consistent
  PM / Domains: Remove gpd_dev_ops.active_wakeup() callback
  soc: rockchip: power-domain: Use GENPD_FLAG_ACTIVE_WAKEUP
  soc: mediatek: Use GENPD_FLAG_ACTIVE_WAKEUP
  ARM: shmobile: pm-rmobile: Use GENPD_FLAG_ACTIVE_WAKEUP
  ...
This commit is contained in:
Linus Torvalds 2017-11-13 19:43:50 -08:00
commit bd2cd7d5a8
99 changed files with 1765 additions and 1070 deletions

View File

@ -211,7 +211,9 @@ Description:
device, after it has been suspended at run time, from a resume device, after it has been suspended at run time, from a resume
request to the moment the device will be ready to process I/O, request to the moment the device will be ready to process I/O,
in microseconds. If it is equal to 0, however, this means that in microseconds. If it is equal to 0, however, this means that
the PM QoS resume latency may be arbitrary. the PM QoS resume latency may be arbitrary and the special value
"n/a" means that user space cannot accept any resume latency at
all for the given device.
Not all drivers support this attribute. If it isn't supported, Not all drivers support this attribute. If it isn't supported,
it is not present. it is not present.
@ -258,19 +260,3 @@ Description:
This attribute has no effect on system-wide suspend/resume and This attribute has no effect on system-wide suspend/resume and
hibernation. hibernation.
What: /sys/devices/.../power/pm_qos_remote_wakeup
Date: September 2012
Contact: Rafael J. Wysocki <rjw@rjwysocki.net>
Description:
The /sys/devices/.../power/pm_qos_remote_wakeup attribute
is used for manipulating the PM QoS "remote wakeup required"
flag. If set, this flag indicates to the kernel that the
device is a source of user events that have to be signaled from
its low-power states.
Not all drivers support this attribute. If it isn't supported,
it is not present.
This attribute has no effect on system-wide suspend/resume and
hibernation.

View File

@ -0,0 +1,25 @@
To enumerate platform Low Power Idle states, Intel platforms are using
“Low Power Idle Table” (LPIT). More details about this table can be
downloaded from:
http://www.uefi.org/sites/default/files/resources/Intel_ACPI_Low_Power_S0_Idle.pdf
Residencies for each low power state can be read via FFH
(Function fixed hardware) or a memory mapped interface.
On platforms supporting S0ix sleep states, there can be two types of
residencies:
- CPU PKG C10 (Read via FFH interface)
- Platform Controller Hub (PCH) SLP_S0 (Read via memory mapped interface)
The following attributes are added dynamically to the cpuidle
sysfs attribute group:
/sys/devices/system/cpu/cpuidle/low_power_idle_cpu_residency_us
/sys/devices/system/cpu/cpuidle/low_power_idle_system_residency_us
The "low_power_idle_cpu_residency_us" attribute shows time spent
by the CPU package in PKG C10
The "low_power_idle_system_residency_us" attribute shows SLP_S0
residency, or system time spent with the SLP_S0# signal asserted.
This is the lowest possible system power state, achieved only when CPU is in
PKG C10 and all functional blocks in PCH are in a low power state.

View File

@ -90,6 +90,9 @@ Freq_i to Freq_j. Freq_i is in descending order with increasing rows and
Freq_j is in descending order with increasing columns. The output here also Freq_j is in descending order with increasing columns. The output here also
contains the actual freq values for each row and column for better readability. contains the actual freq values for each row and column for better readability.
If the transition table is bigger than PAGE_SIZE, reading this will
return an -EFBIG error.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
<mysystem>:/sys/devices/system/cpu/cpu0/cpufreq/stats # cat trans_table <mysystem>:/sys/devices/system/cpu/cpu0/cpufreq/stats # cat trans_table
From : To From : To

View File

@ -274,7 +274,7 @@ sleep states and the hibernation state ("suspend-to-disk"). Each phase involves
executing callbacks for every device before the next phase begins. Not all executing callbacks for every device before the next phase begins. Not all
buses or classes support all these callbacks and not all drivers use all the buses or classes support all these callbacks and not all drivers use all the
callbacks. The various phases always run after tasks have been frozen and callbacks. The various phases always run after tasks have been frozen and
before they are unfrozen. Furthermore, the ``*_noirq phases`` run at a time before they are unfrozen. Furthermore, the ``*_noirq`` phases run at a time
when IRQ handlers have been disabled (except for those marked with the when IRQ handlers have been disabled (except for those marked with the
IRQF_NO_SUSPEND flag). IRQF_NO_SUSPEND flag).
@ -328,7 +328,10 @@ the phases are: ``prepare``, ``suspend``, ``suspend_late``, ``suspend_noirq``.
After the ``->prepare`` callback method returns, no new children may be After the ``->prepare`` callback method returns, no new children may be
registered below the device. The method may also prepare the device or registered below the device. The method may also prepare the device or
driver in some way for the upcoming system power transition, but it driver in some way for the upcoming system power transition, but it
should not put the device into a low-power state. should not put the device into a low-power state. Moreover, if the
device supports runtime power management, the ``->prepare`` callback
method must not update its state in case it is necessary to resume it
from runtime suspend later on.
For devices supporting runtime power management, the return value of the For devices supporting runtime power management, the return value of the
prepare callback can be used to indicate to the PM core that it may prepare callback can be used to indicate to the PM core that it may
@ -351,11 +354,35 @@ the phases are: ``prepare``, ``suspend``, ``suspend_late``, ``suspend_noirq``.
is because all such devices are initially set to runtime-suspended with is because all such devices are initially set to runtime-suspended with
runtime PM disabled. runtime PM disabled.
This feature also can be controlled by device drivers by using the
``DPM_FLAG_NEVER_SKIP`` and ``DPM_FLAG_SMART_PREPARE`` driver power
management flags. [Typically, they are set at the time the driver is
probed against the device in question by passing them to the
:c:func:`dev_pm_set_driver_flags` helper function.] If the first of
these flags is set, the PM core will not apply the direct-complete
procedure described above to the given device and, consequenty, to any
of its ancestors. The second flag, when set, informs the middle layer
code (bus types, device types, PM domains, classes) that it should take
the return value of the ``->prepare`` callback provided by the driver
into account and it may only return a positive value from its own
``->prepare`` callback if the driver's one also has returned a positive
value.
2. The ``->suspend`` methods should quiesce the device to stop it from 2. The ``->suspend`` methods should quiesce the device to stop it from
performing I/O. They also may save the device registers and put it into performing I/O. They also may save the device registers and put it into
the appropriate low-power state, depending on the bus type the device is the appropriate low-power state, depending on the bus type the device is
on, and they may enable wakeup events. on, and they may enable wakeup events.
However, for devices supporting runtime power management, the
``->suspend`` methods provided by subsystems (bus types and PM domains
in particular) must follow an additional rule regarding what can be done
to the devices before their drivers' ``->suspend`` methods are called.
Namely, they can only resume the devices from runtime suspend by
calling :c:func:`pm_runtime_resume` for them, if that is necessary, and
they must not update the state of the devices in any other way at that
time (in case the drivers need to resume the devices from runtime
suspend in their ``->suspend`` methods).
3. For a number of devices it is convenient to split suspend into the 3. For a number of devices it is convenient to split suspend into the
"quiesce device" and "save device state" phases, in which cases "quiesce device" and "save device state" phases, in which cases
``suspend_late`` is meant to do the latter. It is always executed after ``suspend_late`` is meant to do the latter. It is always executed after
@ -729,6 +756,36 @@ state temporarily, for example so that its system wakeup capability can be
disabled. This all depends on the hardware and the design of the subsystem and disabled. This all depends on the hardware and the design of the subsystem and
device driver in question. device driver in question.
If it is necessary to resume a device from runtime suspend during a system-wide
transition into a sleep state, that can be done by calling
:c:func:`pm_runtime_resume` for it from the ``->suspend`` callback (or its
couterpart for transitions related to hibernation) of either the device's driver
or a subsystem responsible for it (for example, a bus type or a PM domain).
That is guaranteed to work by the requirement that subsystems must not change
the state of devices (possibly except for resuming them from runtime suspend)
from their ``->prepare`` and ``->suspend`` callbacks (or equivalent) *before*
invoking device drivers' ``->suspend`` callbacks (or equivalent).
Some bus types and PM domains have a policy to resume all devices from runtime
suspend upfront in their ``->suspend`` callbacks, but that may not be really
necessary if the driver of the device can cope with runtime-suspended devices.
The driver can indicate that by setting ``DPM_FLAG_SMART_SUSPEND`` in
:c:member:`power.driver_flags` at the probe time, by passing it to the
:c:func:`dev_pm_set_driver_flags` helper. That also may cause middle-layer code
(bus types, PM domains etc.) to skip the ``->suspend_late`` and
``->suspend_noirq`` callbacks provided by the driver if the device remains in
runtime suspend at the beginning of the ``suspend_late`` phase of system-wide
suspend (or in the ``poweroff_late`` phase of hibernation), when runtime PM
has been disabled for it, under the assumption that its state should not change
after that point until the system-wide transition is over. If that happens, the
driver's system-wide resume callbacks, if present, may still be invoked during
the subsequent system-wide resume transition and the device's runtime power
management status may be set to "active" before enabling runtime PM for it,
so the driver must be prepared to cope with the invocation of its system-wide
resume callbacks back-to-back with its ``->runtime_suspend`` one (without the
intervening ``->runtime_resume`` and so on) and the final state of the device
must reflect the "active" status for runtime PM in that case.
During system-wide resume from a sleep state it's easiest to put devices into During system-wide resume from a sleep state it's easiest to put devices into
the full-power state, as explained in :file:`Documentation/power/runtime_pm.txt`. the full-power state, as explained in :file:`Documentation/power/runtime_pm.txt`.
Refer to that document for more information regarding this particular issue as Refer to that document for more information regarding this particular issue as

View File

@ -961,6 +961,39 @@ dev_pm_ops to indicate that one suspend routine is to be pointed to by the
.suspend(), .freeze(), and .poweroff() members and one resume routine is to .suspend(), .freeze(), and .poweroff() members and one resume routine is to
be pointed to by the .resume(), .thaw(), and .restore() members. be pointed to by the .resume(), .thaw(), and .restore() members.
3.1.19. Driver Flags for Power Management
The PM core allows device drivers to set flags that influence the handling of
power management for the devices by the core itself and by middle layer code
including the PCI bus type. The flags should be set once at the driver probe
time with the help of the dev_pm_set_driver_flags() function and they should not
be updated directly afterwards.
The DPM_FLAG_NEVER_SKIP flag prevents the PM core from using the direct-complete
mechanism allowing device suspend/resume callbacks to be skipped if the device
is in runtime suspend when the system suspend starts. That also affects all of
the ancestors of the device, so this flag should only be used if absolutely
necessary.
The DPM_FLAG_SMART_PREPARE flag instructs the PCI bus type to only return a
positive value from pci_pm_prepare() if the ->prepare callback provided by the
driver of the device returns a positive value. That allows the driver to opt
out from using the direct-complete mechanism dynamically.
The DPM_FLAG_SMART_SUSPEND flag tells the PCI bus type that from the driver's
perspective the device can be safely left in runtime suspend during system
suspend. That causes pci_pm_suspend(), pci_pm_freeze() and pci_pm_poweroff()
to skip resuming the device from runtime suspend unless there are PCI-specific
reasons for doing that. Also, it causes pci_pm_suspend_late/noirq(),
pci_pm_freeze_late/noirq() and pci_pm_poweroff_late/noirq() to return early
if the device remains in runtime suspend in the beginning of the "late" phase
of the system-wide transition under way. Moreover, if the device is in
runtime suspend in pci_pm_resume_noirq() or pci_pm_restore_noirq(), its runtime
power management status will be changed to "active" (as it is going to be put
into D0 going forward), but if it is in runtime suspend in pci_pm_thaw_noirq(),
the function will set the power.direct_complete flag for it (to make the PM core
skip the subsequent "thaw" callbacks for it) and return.
3.2. Device Runtime Power Management 3.2. Device Runtime Power Management
------------------------------------ ------------------------------------
In addition to providing device power management callbacks PCI device drivers In addition to providing device power management callbacks PCI device drivers

View File

@ -98,8 +98,7 @@ Values are updated in response to changes of the request list.
The target values of resume latency and active state latency tolerance are The target values of resume latency and active state latency tolerance are
simply the minimum of the request values held in the parameter list elements. simply the minimum of the request values held in the parameter list elements.
The PM QoS flags aggregate value is a gather (bitwise OR) of all list elements' The PM QoS flags aggregate value is a gather (bitwise OR) of all list elements'
values. Two device PM QoS flags are defined currently: PM_QOS_FLAG_NO_POWER_OFF values. One device PM QoS flag is defined currently: PM_QOS_FLAG_NO_POWER_OFF.
and PM_QOS_FLAG_REMOTE_WAKEUP.
Note: The aggregated target values are implemented in such a way that reading Note: The aggregated target values are implemented in such a way that reading
the aggregated value does not require any locking mechanism. the aggregated value does not require any locking mechanism.
@ -153,14 +152,14 @@ PM QoS list of resume latency constraints and remove sysfs attribute
pm_qos_resume_latency_us from the device's power directory. pm_qos_resume_latency_us from the device's power directory.
int dev_pm_qos_expose_flags(device, value) int dev_pm_qos_expose_flags(device, value)
Add a request to the device's PM QoS list of flags and create sysfs attributes Add a request to the device's PM QoS list of flags and create sysfs attribute
pm_qos_no_power_off and pm_qos_remote_wakeup under the device's power directory pm_qos_no_power_off under the device's power directory allowing user space to
allowing user space to change these flags' value. change the value of the PM_QOS_FLAG_NO_POWER_OFF flag.
void dev_pm_qos_hide_flags(device) void dev_pm_qos_hide_flags(device)
Drop the request added by dev_pm_qos_expose_flags() from the device's PM QoS list Drop the request added by dev_pm_qos_expose_flags() from the device's PM QoS list
of flags and remove sysfs attributes pm_qos_no_power_off and pm_qos_remote_wakeup of flags and remove sysfs attribute pm_qos_no_power_off from the device's power
under the device's power directory. directory.
Notification mechanisms: Notification mechanisms:
The per-device PM QoS framework has a per-device notification tree. The per-device PM QoS framework has a per-device notification tree.

View File

@ -3637,6 +3637,8 @@ F: drivers/cpufreq/arm_big_little_dt.c
CPU POWER MONITORING SUBSYSTEM CPU POWER MONITORING SUBSYSTEM
M: Thomas Renninger <trenn@suse.com> M: Thomas Renninger <trenn@suse.com>
M: Shuah Khan <shuahkh@osg.samsung.com>
M: Shuah Khan <shuah@kernel.org>
L: linux-pm@vger.kernel.org L: linux-pm@vger.kernel.org
S: Maintained S: Maintained
F: tools/power/cpupower/ F: tools/power/cpupower/
@ -10060,7 +10062,7 @@ M: Stephen Boyd <sboyd@codeaurora.org>
L: linux-pm@vger.kernel.org L: linux-pm@vger.kernel.org
S: Maintained S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm.git
F: drivers/base/power/opp/ F: drivers/opp/
F: include/linux/pm_opp.h F: include/linux/pm_opp.h
F: Documentation/power/opp.txt F: Documentation/power/opp.txt
F: Documentation/devicetree/bindings/opp/ F: Documentation/devicetree/bindings/opp/

View File

@ -826,28 +826,6 @@ static int locomo_match(struct device *_dev, struct device_driver *_drv)
return dev->devid == drv->devid; return dev->devid == drv->devid;
} }
static int locomo_bus_suspend(struct device *dev, pm_message_t state)
{
struct locomo_dev *ldev = LOCOMO_DEV(dev);
struct locomo_driver *drv = LOCOMO_DRV(dev->driver);
int ret = 0;
if (drv && drv->suspend)
ret = drv->suspend(ldev, state);
return ret;
}
static int locomo_bus_resume(struct device *dev)
{
struct locomo_dev *ldev = LOCOMO_DEV(dev);
struct locomo_driver *drv = LOCOMO_DRV(dev->driver);
int ret = 0;
if (drv && drv->resume)
ret = drv->resume(ldev);
return ret;
}
static int locomo_bus_probe(struct device *dev) static int locomo_bus_probe(struct device *dev)
{ {
struct locomo_dev *ldev = LOCOMO_DEV(dev); struct locomo_dev *ldev = LOCOMO_DEV(dev);
@ -875,8 +853,6 @@ struct bus_type locomo_bus_type = {
.match = locomo_match, .match = locomo_match,
.probe = locomo_bus_probe, .probe = locomo_bus_probe,
.remove = locomo_bus_remove, .remove = locomo_bus_remove,
.suspend = locomo_bus_suspend,
.resume = locomo_bus_resume,
}; };
int locomo_driver_register(struct locomo_driver *driver) int locomo_driver_register(struct locomo_driver *driver)

View File

@ -189,8 +189,6 @@ struct locomo_driver {
unsigned int devid; unsigned int devid;
int (*probe)(struct locomo_dev *); int (*probe)(struct locomo_dev *);
int (*remove)(struct locomo_dev *); int (*remove)(struct locomo_dev *);
int (*suspend)(struct locomo_dev *, pm_message_t);
int (*resume)(struct locomo_dev *);
}; };
#define LOCOMO_DRV(_d) container_of((_d), struct locomo_driver, drv) #define LOCOMO_DRV(_d) container_of((_d), struct locomo_driver, drv)

View File

@ -25,6 +25,14 @@ void init_cpu_topology(void);
void store_cpu_topology(unsigned int cpuid); void store_cpu_topology(unsigned int cpuid);
const struct cpumask *cpu_coregroup_mask(int cpu); const struct cpumask *cpu_coregroup_mask(int cpu);
#include <linux/arch_topology.h>
/* Replace task scheduler's default frequency-invariant accounting */
#define arch_scale_freq_capacity topology_get_freq_scale
/* Replace task scheduler's default cpu-invariant accounting */
#define arch_scale_cpu_capacity topology_get_cpu_scale
#else #else
static inline void init_cpu_topology(void) { } static inline void init_cpu_topology(void) { }

View File

@ -286,88 +286,6 @@ static void __init imx6q_init_machine(void)
imx6q_axi_init(); imx6q_axi_init();
} }
#define OCOTP_CFG3 0x440
#define OCOTP_CFG3_SPEED_SHIFT 16
#define OCOTP_CFG3_SPEED_1P2GHZ 0x3
#define OCOTP_CFG3_SPEED_996MHZ 0x2
#define OCOTP_CFG3_SPEED_852MHZ 0x1
static void __init imx6q_opp_check_speed_grading(struct device *cpu_dev)
{
struct device_node *np;
void __iomem *base;
u32 val;
np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-ocotp");
if (!np) {
pr_warn("failed to find ocotp node\n");
return;
}
base = of_iomap(np, 0);
if (!base) {
pr_warn("failed to map ocotp\n");
goto put_node;
}
/*
* SPEED_GRADING[1:0] defines the max speed of ARM:
* 2b'11: 1200000000Hz;
* 2b'10: 996000000Hz;
* 2b'01: 852000000Hz; -- i.MX6Q Only, exclusive with 996MHz.
* 2b'00: 792000000Hz;
* We need to set the max speed of ARM according to fuse map.
*/
val = readl_relaxed(base + OCOTP_CFG3);
val >>= OCOTP_CFG3_SPEED_SHIFT;
val &= 0x3;
if ((val != OCOTP_CFG3_SPEED_1P2GHZ) && cpu_is_imx6q())
if (dev_pm_opp_disable(cpu_dev, 1200000000))
pr_warn("failed to disable 1.2 GHz OPP\n");
if (val < OCOTP_CFG3_SPEED_996MHZ)
if (dev_pm_opp_disable(cpu_dev, 996000000))
pr_warn("failed to disable 996 MHz OPP\n");
if (cpu_is_imx6q()) {
if (val != OCOTP_CFG3_SPEED_852MHZ)
if (dev_pm_opp_disable(cpu_dev, 852000000))
pr_warn("failed to disable 852 MHz OPP\n");
}
iounmap(base);
put_node:
of_node_put(np);
}
static void __init imx6q_opp_init(void)
{
struct device_node *np;
struct device *cpu_dev = get_cpu_device(0);
if (!cpu_dev) {
pr_warn("failed to get cpu0 device\n");
return;
}
np = of_node_get(cpu_dev->of_node);
if (!np) {
pr_warn("failed to find cpu0 node\n");
return;
}
if (dev_pm_opp_of_add_table(cpu_dev)) {
pr_warn("failed to init OPP table\n");
goto put_node;
}
imx6q_opp_check_speed_grading(cpu_dev);
put_node:
of_node_put(np);
}
static struct platform_device imx6q_cpufreq_pdev = {
.name = "imx6q-cpufreq",
};
static void __init imx6q_init_late(void) static void __init imx6q_init_late(void)
{ {
/* /*
@ -377,10 +295,8 @@ static void __init imx6q_init_late(void)
if (imx_get_soc_revision() > IMX_CHIP_REVISION_1_1) if (imx_get_soc_revision() > IMX_CHIP_REVISION_1_1)
imx6q_cpuidle_init(); imx6q_cpuidle_init();
if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ)) { if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ))
imx6q_opp_init(); platform_device_register_simple("imx6q-cpufreq", -1, NULL, 0);
platform_device_register(&imx6q_cpufreq_pdev);
}
} }
static void __init imx6q_map_io(void) static void __init imx6q_map_io(void)

View File

@ -120,18 +120,12 @@ static int rmobile_pd_power_up(struct generic_pm_domain *genpd)
return __rmobile_pd_power_up(to_rmobile_pd(genpd), true); return __rmobile_pd_power_up(to_rmobile_pd(genpd), true);
} }
static bool rmobile_pd_active_wakeup(struct device *dev)
{
return true;
}
static void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd) static void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd)
{ {
struct generic_pm_domain *genpd = &rmobile_pd->genpd; struct generic_pm_domain *genpd = &rmobile_pd->genpd;
struct dev_power_governor *gov = rmobile_pd->gov; struct dev_power_governor *gov = rmobile_pd->gov;
genpd->flags |= GENPD_FLAG_PM_CLK; genpd->flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP;
genpd->dev_ops.active_wakeup = rmobile_pd_active_wakeup;
genpd->power_off = rmobile_pd_power_down; genpd->power_off = rmobile_pd_power_down;
genpd->power_on = rmobile_pd_power_up; genpd->power_on = rmobile_pd_power_up;
genpd->attach_dev = cpg_mstp_attach_dev; genpd->attach_dev = cpg_mstp_attach_dev;

View File

@ -33,6 +33,14 @@ int pcibus_to_node(struct pci_bus *bus);
#endif /* CONFIG_NUMA */ #endif /* CONFIG_NUMA */
#include <linux/arch_topology.h>
/* Replace task scheduler's default frequency-invariant accounting */
#define arch_scale_freq_capacity topology_get_freq_scale
/* Replace task scheduler's default cpu-invariant accounting */
#define arch_scale_cpu_capacity topology_get_cpu_scale
#include <asm-generic/topology.h> #include <asm-generic/topology.h>
#endif /* _ASM_ARM_TOPOLOGY_H */ #endif /* _ASM_ARM_TOPOLOGY_H */

View File

@ -209,4 +209,6 @@ source "drivers/tee/Kconfig"
source "drivers/mux/Kconfig" source "drivers/mux/Kconfig"
source "drivers/opp/Kconfig"
endmenu endmenu

View File

@ -126,6 +126,7 @@ obj-$(CONFIG_ACCESSIBILITY) += accessibility/
obj-$(CONFIG_ISDN) += isdn/ obj-$(CONFIG_ISDN) += isdn/
obj-$(CONFIG_EDAC) += edac/ obj-$(CONFIG_EDAC) += edac/
obj-$(CONFIG_EISA) += eisa/ obj-$(CONFIG_EISA) += eisa/
obj-$(CONFIG_PM_OPP) += opp/
obj-$(CONFIG_CPU_FREQ) += cpufreq/ obj-$(CONFIG_CPU_FREQ) += cpufreq/
obj-$(CONFIG_CPU_IDLE) += cpuidle/ obj-$(CONFIG_CPU_IDLE) += cpuidle/
obj-y += mmc/ obj-y += mmc/

View File

@ -81,6 +81,11 @@ endif
config ACPI_SPCR_TABLE config ACPI_SPCR_TABLE
bool bool
config ACPI_LPIT
bool
depends on X86_64
default y
config ACPI_SLEEP config ACPI_SLEEP
bool bool
depends on SUSPEND || HIBERNATION depends on SUSPEND || HIBERNATION

View File

@ -57,6 +57,7 @@ acpi-$(CONFIG_DEBUG_FS) += debugfs.o
acpi-$(CONFIG_ACPI_NUMA) += numa.o acpi-$(CONFIG_ACPI_NUMA) += numa.o
acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o
acpi-y += acpi_lpat.o acpi-y += acpi_lpat.o
acpi-$(CONFIG_ACPI_LPIT) += acpi_lpit.o
acpi-$(CONFIG_ACPI_GENERIC_GSI) += irq.o acpi-$(CONFIG_ACPI_GENERIC_GSI) += irq.o
acpi-$(CONFIG_ACPI_WATCHDOG) += acpi_watchdog.o acpi-$(CONFIG_ACPI_WATCHDOG) += acpi_watchdog.o

162
drivers/acpi/acpi_lpit.c Normal file
View File

@ -0,0 +1,162 @@
/*
* acpi_lpit.c - LPIT table processing functions
*
* Copyright (C) 2017 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/cpu.h>
#include <linux/acpi.h>
#include <asm/msr.h>
#include <asm/tsc.h>
struct lpit_residency_info {
struct acpi_generic_address gaddr;
u64 frequency;
void __iomem *iomem_addr;
};
/* Storage for an memory mapped and FFH based entries */
static struct lpit_residency_info residency_info_mem;
static struct lpit_residency_info residency_info_ffh;
static int lpit_read_residency_counter_us(u64 *counter, bool io_mem)
{
int err;
if (io_mem) {
u64 count = 0;
int error;
error = acpi_os_read_iomem(residency_info_mem.iomem_addr, &count,
residency_info_mem.gaddr.bit_width);
if (error)
return error;
*counter = div64_u64(count * 1000000ULL, residency_info_mem.frequency);
return 0;
}
err = rdmsrl_safe(residency_info_ffh.gaddr.address, counter);
if (!err) {
u64 mask = GENMASK_ULL(residency_info_ffh.gaddr.bit_offset +
residency_info_ffh.gaddr. bit_width - 1,
residency_info_ffh.gaddr.bit_offset);
*counter &= mask;
*counter >>= residency_info_ffh.gaddr.bit_offset;
*counter = div64_u64(*counter * 1000000ULL, residency_info_ffh.frequency);
return 0;
}
return -ENODATA;
}
static ssize_t low_power_idle_system_residency_us_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
u64 counter;
int ret;
ret = lpit_read_residency_counter_us(&counter, true);
if (ret)
return ret;
return sprintf(buf, "%llu\n", counter);
}
static DEVICE_ATTR_RO(low_power_idle_system_residency_us);
static ssize_t low_power_idle_cpu_residency_us_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
u64 counter;
int ret;
ret = lpit_read_residency_counter_us(&counter, false);
if (ret)
return ret;
return sprintf(buf, "%llu\n", counter);
}
static DEVICE_ATTR_RO(low_power_idle_cpu_residency_us);
int lpit_read_residency_count_address(u64 *address)
{
if (!residency_info_mem.gaddr.address)
return -EINVAL;
*address = residency_info_mem.gaddr.address;
return 0;
}
static void lpit_update_residency(struct lpit_residency_info *info,
struct acpi_lpit_native *lpit_native)
{
info->frequency = lpit_native->counter_frequency ?
lpit_native->counter_frequency : tsc_khz * 1000;
if (!info->frequency)
info->frequency = 1;
info->gaddr = lpit_native->residency_counter;
if (info->gaddr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
info->iomem_addr = ioremap_nocache(info->gaddr.address,
info->gaddr.bit_width / 8);
if (!info->iomem_addr)
return;
/* Silently fail, if cpuidle attribute group is not present */
sysfs_add_file_to_group(&cpu_subsys.dev_root->kobj,
&dev_attr_low_power_idle_system_residency_us.attr,
"cpuidle");
} else if (info->gaddr.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) {
/* Silently fail, if cpuidle attribute group is not present */
sysfs_add_file_to_group(&cpu_subsys.dev_root->kobj,
&dev_attr_low_power_idle_cpu_residency_us.attr,
"cpuidle");
}
}
static void lpit_process(u64 begin, u64 end)
{
while (begin + sizeof(struct acpi_lpit_native) < end) {
struct acpi_lpit_native *lpit_native = (struct acpi_lpit_native *)begin;
if (!lpit_native->header.type && !lpit_native->header.flags) {
if (lpit_native->residency_counter.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY &&
!residency_info_mem.gaddr.address) {
lpit_update_residency(&residency_info_mem, lpit_native);
} else if (lpit_native->residency_counter.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE &&
!residency_info_ffh.gaddr.address) {
lpit_update_residency(&residency_info_ffh, lpit_native);
}
}
begin += lpit_native->header.length;
}
}
void acpi_init_lpit(void)
{
acpi_status status;
u64 lpit_begin;
struct acpi_table_lpit *lpit;
status = acpi_get_table(ACPI_SIG_LPIT, 0, (struct acpi_table_header **)&lpit);
if (ACPI_FAILURE(status))
return;
lpit_begin = (u64)lpit + sizeof(*lpit);
lpit_process(lpit_begin, lpit_begin + lpit->header.length);
}

View File

@ -693,7 +693,7 @@ static int acpi_lpss_activate(struct device *dev)
struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
int ret; int ret;
ret = acpi_dev_runtime_resume(dev); ret = acpi_dev_resume(dev);
if (ret) if (ret)
return ret; return ret;
@ -713,43 +713,9 @@ static int acpi_lpss_activate(struct device *dev)
static void acpi_lpss_dismiss(struct device *dev) static void acpi_lpss_dismiss(struct device *dev)
{ {
acpi_dev_runtime_suspend(dev); acpi_dev_suspend(dev, false);
} }
#ifdef CONFIG_PM_SLEEP
static int acpi_lpss_suspend_late(struct device *dev)
{
struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
int ret;
ret = pm_generic_suspend_late(dev);
if (ret)
return ret;
if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
acpi_lpss_save_ctx(dev, pdata);
return acpi_dev_suspend_late(dev);
}
static int acpi_lpss_resume_early(struct device *dev)
{
struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
int ret;
ret = acpi_dev_resume_early(dev);
if (ret)
return ret;
acpi_lpss_d3_to_d0_delay(pdata);
if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
acpi_lpss_restore_ctx(dev, pdata);
return pm_generic_resume_early(dev);
}
#endif /* CONFIG_PM_SLEEP */
/* IOSF SB for LPSS island */ /* IOSF SB for LPSS island */
#define LPSS_IOSF_UNIT_LPIOEP 0xA0 #define LPSS_IOSF_UNIT_LPIOEP 0xA0
#define LPSS_IOSF_UNIT_LPIO1 0xAB #define LPSS_IOSF_UNIT_LPIO1 0xAB
@ -835,19 +801,15 @@ static void lpss_iosf_exit_d3_state(void)
mutex_unlock(&lpss_iosf_mutex); mutex_unlock(&lpss_iosf_mutex);
} }
static int acpi_lpss_runtime_suspend(struct device *dev) static int acpi_lpss_suspend(struct device *dev, bool wakeup)
{ {
struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
int ret; int ret;
ret = pm_generic_runtime_suspend(dev);
if (ret)
return ret;
if (pdata->dev_desc->flags & LPSS_SAVE_CTX) if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
acpi_lpss_save_ctx(dev, pdata); acpi_lpss_save_ctx(dev, pdata);
ret = acpi_dev_runtime_suspend(dev); ret = acpi_dev_suspend(dev, wakeup);
/* /*
* This call must be last in the sequence, otherwise PMC will return * This call must be last in the sequence, otherwise PMC will return
@ -860,7 +822,7 @@ static int acpi_lpss_runtime_suspend(struct device *dev)
return ret; return ret;
} }
static int acpi_lpss_runtime_resume(struct device *dev) static int acpi_lpss_resume(struct device *dev)
{ {
struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
int ret; int ret;
@ -872,7 +834,7 @@ static int acpi_lpss_runtime_resume(struct device *dev)
if (lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available()) if (lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available())
lpss_iosf_exit_d3_state(); lpss_iosf_exit_d3_state();
ret = acpi_dev_runtime_resume(dev); ret = acpi_dev_resume(dev);
if (ret) if (ret)
return ret; return ret;
@ -881,7 +843,41 @@ static int acpi_lpss_runtime_resume(struct device *dev)
if (pdata->dev_desc->flags & LPSS_SAVE_CTX) if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
acpi_lpss_restore_ctx(dev, pdata); acpi_lpss_restore_ctx(dev, pdata);
return pm_generic_runtime_resume(dev); return 0;
}
#ifdef CONFIG_PM_SLEEP
static int acpi_lpss_suspend_late(struct device *dev)
{
int ret;
if (dev_pm_smart_suspend_and_suspended(dev))
return 0;
ret = pm_generic_suspend_late(dev);
return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev));
}
static int acpi_lpss_resume_early(struct device *dev)
{
int ret = acpi_lpss_resume(dev);
return ret ? ret : pm_generic_resume_early(dev);
}
#endif /* CONFIG_PM_SLEEP */
static int acpi_lpss_runtime_suspend(struct device *dev)
{
int ret = pm_generic_runtime_suspend(dev);
return ret ? ret : acpi_lpss_suspend(dev, true);
}
static int acpi_lpss_runtime_resume(struct device *dev)
{
int ret = acpi_lpss_resume(dev);
return ret ? ret : pm_generic_runtime_resume(dev);
} }
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
@ -894,13 +890,20 @@ static struct dev_pm_domain acpi_lpss_pm_domain = {
#ifdef CONFIG_PM #ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
.prepare = acpi_subsys_prepare, .prepare = acpi_subsys_prepare,
.complete = pm_complete_with_resume_check, .complete = acpi_subsys_complete,
.suspend = acpi_subsys_suspend, .suspend = acpi_subsys_suspend,
.suspend_late = acpi_lpss_suspend_late, .suspend_late = acpi_lpss_suspend_late,
.suspend_noirq = acpi_subsys_suspend_noirq,
.resume_noirq = acpi_subsys_resume_noirq,
.resume_early = acpi_lpss_resume_early, .resume_early = acpi_lpss_resume_early,
.freeze = acpi_subsys_freeze, .freeze = acpi_subsys_freeze,
.freeze_late = acpi_subsys_freeze_late,
.freeze_noirq = acpi_subsys_freeze_noirq,
.thaw_noirq = acpi_subsys_thaw_noirq,
.poweroff = acpi_subsys_suspend, .poweroff = acpi_subsys_suspend,
.poweroff_late = acpi_lpss_suspend_late, .poweroff_late = acpi_lpss_suspend_late,
.poweroff_noirq = acpi_subsys_suspend_noirq,
.restore_noirq = acpi_subsys_resume_noirq,
.restore_early = acpi_lpss_resume_early, .restore_early = acpi_lpss_resume_early,
#endif #endif
.runtime_suspend = acpi_lpss_runtime_suspend, .runtime_suspend = acpi_lpss_runtime_suspend,

View File

@ -387,6 +387,7 @@ EXPORT_SYMBOL(acpi_bus_power_manageable);
#ifdef CONFIG_PM #ifdef CONFIG_PM
static DEFINE_MUTEX(acpi_pm_notifier_lock); static DEFINE_MUTEX(acpi_pm_notifier_lock);
static DEFINE_MUTEX(acpi_pm_notifier_install_lock);
void acpi_pm_wakeup_event(struct device *dev) void acpi_pm_wakeup_event(struct device *dev)
{ {
@ -443,24 +444,25 @@ acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev,
if (!dev && !func) if (!dev && !func)
return AE_BAD_PARAMETER; return AE_BAD_PARAMETER;
mutex_lock(&acpi_pm_notifier_lock); mutex_lock(&acpi_pm_notifier_install_lock);
if (adev->wakeup.flags.notifier_present) if (adev->wakeup.flags.notifier_present)
goto out; goto out;
adev->wakeup.ws = wakeup_source_register(dev_name(&adev->dev));
adev->wakeup.context.dev = dev;
adev->wakeup.context.func = func;
status = acpi_install_notify_handler(adev->handle, ACPI_SYSTEM_NOTIFY, status = acpi_install_notify_handler(adev->handle, ACPI_SYSTEM_NOTIFY,
acpi_pm_notify_handler, NULL); acpi_pm_notify_handler, NULL);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
goto out; goto out;
mutex_lock(&acpi_pm_notifier_lock);
adev->wakeup.ws = wakeup_source_register(dev_name(&adev->dev));
adev->wakeup.context.dev = dev;
adev->wakeup.context.func = func;
adev->wakeup.flags.notifier_present = true; adev->wakeup.flags.notifier_present = true;
mutex_unlock(&acpi_pm_notifier_lock);
out: out:
mutex_unlock(&acpi_pm_notifier_lock); mutex_unlock(&acpi_pm_notifier_install_lock);
return status; return status;
} }
@ -472,7 +474,7 @@ acpi_status acpi_remove_pm_notifier(struct acpi_device *adev)
{ {
acpi_status status = AE_BAD_PARAMETER; acpi_status status = AE_BAD_PARAMETER;
mutex_lock(&acpi_pm_notifier_lock); mutex_lock(&acpi_pm_notifier_install_lock);
if (!adev->wakeup.flags.notifier_present) if (!adev->wakeup.flags.notifier_present)
goto out; goto out;
@ -483,14 +485,15 @@ acpi_status acpi_remove_pm_notifier(struct acpi_device *adev)
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
goto out; goto out;
mutex_lock(&acpi_pm_notifier_lock);
adev->wakeup.context.func = NULL; adev->wakeup.context.func = NULL;
adev->wakeup.context.dev = NULL; adev->wakeup.context.dev = NULL;
wakeup_source_unregister(adev->wakeup.ws); wakeup_source_unregister(adev->wakeup.ws);
adev->wakeup.flags.notifier_present = false; adev->wakeup.flags.notifier_present = false;
mutex_unlock(&acpi_pm_notifier_lock);
out: out:
mutex_unlock(&acpi_pm_notifier_lock); mutex_unlock(&acpi_pm_notifier_install_lock);
return status; return status;
} }
@ -581,8 +584,7 @@ static int acpi_dev_pm_get_state(struct device *dev, struct acpi_device *adev,
d_min = ret; d_min = ret;
wakeup = device_may_wakeup(dev) && adev->wakeup.flags.valid wakeup = device_may_wakeup(dev) && adev->wakeup.flags.valid
&& adev->wakeup.sleep_state >= target_state; && adev->wakeup.sleep_state >= target_state;
} else if (dev_pm_qos_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP) != } else {
PM_QOS_FLAGS_NONE) {
wakeup = adev->wakeup.flags.valid; wakeup = adev->wakeup.flags.valid;
} }
@ -848,48 +850,48 @@ static int acpi_dev_pm_full_power(struct acpi_device *adev)
} }
/** /**
* acpi_dev_runtime_suspend - Put device into a low-power state using ACPI. * acpi_dev_suspend - Put device into a low-power state using ACPI.
* @dev: Device to put into a low-power state. * @dev: Device to put into a low-power state.
* @wakeup: Whether or not to enable wakeup for the device.
* *
* Put the given device into a runtime low-power state using the standard ACPI * Put the given device into a low-power state using the standard ACPI
* mechanism. Set up remote wakeup if desired, choose the state to put the * mechanism. Set up remote wakeup if desired, choose the state to put the
* device into (this checks if remote wakeup is expected to work too), and set * device into (this checks if remote wakeup is expected to work too), and set
* the power state of the device. * the power state of the device.
*/ */
int acpi_dev_runtime_suspend(struct device *dev) int acpi_dev_suspend(struct device *dev, bool wakeup)
{ {
struct acpi_device *adev = ACPI_COMPANION(dev); struct acpi_device *adev = ACPI_COMPANION(dev);
bool remote_wakeup; u32 target_state = acpi_target_system_state();
int error; int error;
if (!adev) if (!adev)
return 0; return 0;
remote_wakeup = dev_pm_qos_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP) > if (wakeup && acpi_device_can_wakeup(adev)) {
PM_QOS_FLAGS_NONE; error = acpi_device_wakeup_enable(adev, target_state);
if (remote_wakeup) {
error = acpi_device_wakeup_enable(adev, ACPI_STATE_S0);
if (error) if (error)
return -EAGAIN; return -EAGAIN;
} else {
wakeup = false;
} }
error = acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0); error = acpi_dev_pm_low_power(dev, adev, target_state);
if (error && remote_wakeup) if (error && wakeup)
acpi_device_wakeup_disable(adev); acpi_device_wakeup_disable(adev);
return error; return error;
} }
EXPORT_SYMBOL_GPL(acpi_dev_runtime_suspend); EXPORT_SYMBOL_GPL(acpi_dev_suspend);
/** /**
* acpi_dev_runtime_resume - Put device into the full-power state using ACPI. * acpi_dev_resume - Put device into the full-power state using ACPI.
* @dev: Device to put into the full-power state. * @dev: Device to put into the full-power state.
* *
* Put the given device into the full-power state using the standard ACPI * Put the given device into the full-power state using the standard ACPI
* mechanism at run time. Set the power state of the device to ACPI D0 and * mechanism. Set the power state of the device to ACPI D0 and disable wakeup.
* disable remote wakeup.
*/ */
int acpi_dev_runtime_resume(struct device *dev) int acpi_dev_resume(struct device *dev)
{ {
struct acpi_device *adev = ACPI_COMPANION(dev); struct acpi_device *adev = ACPI_COMPANION(dev);
int error; int error;
@ -901,7 +903,7 @@ int acpi_dev_runtime_resume(struct device *dev)
acpi_device_wakeup_disable(adev); acpi_device_wakeup_disable(adev);
return error; return error;
} }
EXPORT_SYMBOL_GPL(acpi_dev_runtime_resume); EXPORT_SYMBOL_GPL(acpi_dev_resume);
/** /**
* acpi_subsys_runtime_suspend - Suspend device using ACPI. * acpi_subsys_runtime_suspend - Suspend device using ACPI.
@ -913,7 +915,7 @@ EXPORT_SYMBOL_GPL(acpi_dev_runtime_resume);
int acpi_subsys_runtime_suspend(struct device *dev) int acpi_subsys_runtime_suspend(struct device *dev)
{ {
int ret = pm_generic_runtime_suspend(dev); int ret = pm_generic_runtime_suspend(dev);
return ret ? ret : acpi_dev_runtime_suspend(dev); return ret ? ret : acpi_dev_suspend(dev, true);
} }
EXPORT_SYMBOL_GPL(acpi_subsys_runtime_suspend); EXPORT_SYMBOL_GPL(acpi_subsys_runtime_suspend);
@ -926,68 +928,33 @@ EXPORT_SYMBOL_GPL(acpi_subsys_runtime_suspend);
*/ */
int acpi_subsys_runtime_resume(struct device *dev) int acpi_subsys_runtime_resume(struct device *dev)
{ {
int ret = acpi_dev_runtime_resume(dev); int ret = acpi_dev_resume(dev);
return ret ? ret : pm_generic_runtime_resume(dev); return ret ? ret : pm_generic_runtime_resume(dev);
} }
EXPORT_SYMBOL_GPL(acpi_subsys_runtime_resume); EXPORT_SYMBOL_GPL(acpi_subsys_runtime_resume);
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
/** static bool acpi_dev_needs_resume(struct device *dev, struct acpi_device *adev)
* acpi_dev_suspend_late - Put device into a low-power state using ACPI.
* @dev: Device to put into a low-power state.
*
* Put the given device into a low-power state during system transition to a
* sleep state using the standard ACPI mechanism. Set up system wakeup if
* desired, choose the state to put the device into (this checks if system
* wakeup is expected to work too), and set the power state of the device.
*/
int acpi_dev_suspend_late(struct device *dev)
{ {
struct acpi_device *adev = ACPI_COMPANION(dev); u32 sys_target = acpi_target_system_state();
u32 target_state; int ret, state;
bool wakeup;
int error;
if (!adev) if (!pm_runtime_suspended(dev) || !adev ||
return 0; device_may_wakeup(dev) != !!adev->wakeup.prepare_count)
return true;
target_state = acpi_target_system_state(); if (sys_target == ACPI_STATE_S0)
wakeup = device_may_wakeup(dev) && acpi_device_can_wakeup(adev); return false;
if (wakeup) {
error = acpi_device_wakeup_enable(adev, target_state);
if (error)
return error;
}
error = acpi_dev_pm_low_power(dev, adev, target_state); if (adev->power.flags.dsw_present)
if (error && wakeup) return true;
acpi_device_wakeup_disable(adev);
return error; ret = acpi_dev_pm_get_state(dev, adev, sys_target, NULL, &state);
if (ret)
return true;
return state != adev->power.state;
} }
EXPORT_SYMBOL_GPL(acpi_dev_suspend_late);
/**
* acpi_dev_resume_early - Put device into the full-power state using ACPI.
* @dev: Device to put into the full-power state.
*
* Put the given device into the full-power state using the standard ACPI
* mechanism during system transition to the working state. Set the power
* state of the device to ACPI D0 and disable remote wakeup.
*/
int acpi_dev_resume_early(struct device *dev)
{
struct acpi_device *adev = ACPI_COMPANION(dev);
int error;
if (!adev)
return 0;
error = acpi_dev_pm_full_power(adev);
acpi_device_wakeup_disable(adev);
return error;
}
EXPORT_SYMBOL_GPL(acpi_dev_resume_early);
/** /**
* acpi_subsys_prepare - Prepare device for system transition to a sleep state. * acpi_subsys_prepare - Prepare device for system transition to a sleep state.
@ -996,39 +963,53 @@ EXPORT_SYMBOL_GPL(acpi_dev_resume_early);
int acpi_subsys_prepare(struct device *dev) int acpi_subsys_prepare(struct device *dev)
{ {
struct acpi_device *adev = ACPI_COMPANION(dev); struct acpi_device *adev = ACPI_COMPANION(dev);
u32 sys_target;
int ret, state;
ret = pm_generic_prepare(dev); if (dev->driver && dev->driver->pm && dev->driver->pm->prepare) {
if (ret < 0) int ret = dev->driver->pm->prepare(dev);
return ret;
if (!adev || !pm_runtime_suspended(dev) if (ret < 0)
|| device_may_wakeup(dev) != !!adev->wakeup.prepare_count) return ret;
return 0;
sys_target = acpi_target_system_state(); if (!ret && dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_PREPARE))
if (sys_target == ACPI_STATE_S0) return 0;
return 1; }
if (adev->power.flags.dsw_present) return !acpi_dev_needs_resume(dev, adev);
return 0;
ret = acpi_dev_pm_get_state(dev, adev, sys_target, NULL, &state);
return !ret && state == adev->power.state;
} }
EXPORT_SYMBOL_GPL(acpi_subsys_prepare); EXPORT_SYMBOL_GPL(acpi_subsys_prepare);
/**
* acpi_subsys_complete - Finalize device's resume during system resume.
* @dev: Device to handle.
*/
void acpi_subsys_complete(struct device *dev)
{
pm_generic_complete(dev);
/*
* If the device had been runtime-suspended before the system went into
* the sleep state it is going out of and it has never been resumed till
* now, resume it in case the firmware powered it up.
*/
if (dev->power.direct_complete && pm_resume_via_firmware())
pm_request_resume(dev);
}
EXPORT_SYMBOL_GPL(acpi_subsys_complete);
/** /**
* acpi_subsys_suspend - Run the device driver's suspend callback. * acpi_subsys_suspend - Run the device driver's suspend callback.
* @dev: Device to handle. * @dev: Device to handle.
* *
* Follow PCI and resume devices suspended at run time before running their * Follow PCI and resume devices from runtime suspend before running their
* system suspend callbacks. * system suspend callbacks, unless the driver can cope with runtime-suspended
* devices during system suspend and there are no ACPI-specific reasons for
* resuming them.
*/ */
int acpi_subsys_suspend(struct device *dev) int acpi_subsys_suspend(struct device *dev)
{ {
pm_runtime_resume(dev); if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
acpi_dev_needs_resume(dev, ACPI_COMPANION(dev)))
pm_runtime_resume(dev);
return pm_generic_suspend(dev); return pm_generic_suspend(dev);
} }
EXPORT_SYMBOL_GPL(acpi_subsys_suspend); EXPORT_SYMBOL_GPL(acpi_subsys_suspend);
@ -1042,11 +1023,47 @@ EXPORT_SYMBOL_GPL(acpi_subsys_suspend);
*/ */
int acpi_subsys_suspend_late(struct device *dev) int acpi_subsys_suspend_late(struct device *dev)
{ {
int ret = pm_generic_suspend_late(dev); int ret;
return ret ? ret : acpi_dev_suspend_late(dev);
if (dev_pm_smart_suspend_and_suspended(dev))
return 0;
ret = pm_generic_suspend_late(dev);
return ret ? ret : acpi_dev_suspend(dev, device_may_wakeup(dev));
} }
EXPORT_SYMBOL_GPL(acpi_subsys_suspend_late); EXPORT_SYMBOL_GPL(acpi_subsys_suspend_late);
/**
* acpi_subsys_suspend_noirq - Run the device driver's "noirq" suspend callback.
* @dev: Device to suspend.
*/
int acpi_subsys_suspend_noirq(struct device *dev)
{
if (dev_pm_smart_suspend_and_suspended(dev))
return 0;
return pm_generic_suspend_noirq(dev);
}
EXPORT_SYMBOL_GPL(acpi_subsys_suspend_noirq);
/**
* acpi_subsys_resume_noirq - Run the device driver's "noirq" resume callback.
* @dev: Device to handle.
*/
int acpi_subsys_resume_noirq(struct device *dev)
{
/*
* Devices with DPM_FLAG_SMART_SUSPEND may be left in runtime suspend
* during system suspend, so update their runtime PM status to "active"
* as they will be put into D0 going forward.
*/
if (dev_pm_smart_suspend_and_suspended(dev))
pm_runtime_set_active(dev);
return pm_generic_resume_noirq(dev);
}
EXPORT_SYMBOL_GPL(acpi_subsys_resume_noirq);
/** /**
* acpi_subsys_resume_early - Resume device using ACPI. * acpi_subsys_resume_early - Resume device using ACPI.
* @dev: Device to Resume. * @dev: Device to Resume.
@ -1057,7 +1074,7 @@ EXPORT_SYMBOL_GPL(acpi_subsys_suspend_late);
*/ */
int acpi_subsys_resume_early(struct device *dev) int acpi_subsys_resume_early(struct device *dev)
{ {
int ret = acpi_dev_resume_early(dev); int ret = acpi_dev_resume(dev);
return ret ? ret : pm_generic_resume_early(dev); return ret ? ret : pm_generic_resume_early(dev);
} }
EXPORT_SYMBOL_GPL(acpi_subsys_resume_early); EXPORT_SYMBOL_GPL(acpi_subsys_resume_early);
@ -1074,11 +1091,60 @@ int acpi_subsys_freeze(struct device *dev)
* runtime-suspended devices should not be touched during freeze/thaw * runtime-suspended devices should not be touched during freeze/thaw
* transitions. * transitions.
*/ */
pm_runtime_resume(dev); if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND))
pm_runtime_resume(dev);
return pm_generic_freeze(dev); return pm_generic_freeze(dev);
} }
EXPORT_SYMBOL_GPL(acpi_subsys_freeze); EXPORT_SYMBOL_GPL(acpi_subsys_freeze);
/**
* acpi_subsys_freeze_late - Run the device driver's "late" freeze callback.
* @dev: Device to handle.
*/
int acpi_subsys_freeze_late(struct device *dev)
{
if (dev_pm_smart_suspend_and_suspended(dev))
return 0;
return pm_generic_freeze_late(dev);
}
EXPORT_SYMBOL_GPL(acpi_subsys_freeze_late);
/**
* acpi_subsys_freeze_noirq - Run the device driver's "noirq" freeze callback.
* @dev: Device to handle.
*/
int acpi_subsys_freeze_noirq(struct device *dev)
{
if (dev_pm_smart_suspend_and_suspended(dev))
return 0;
return pm_generic_freeze_noirq(dev);
}
EXPORT_SYMBOL_GPL(acpi_subsys_freeze_noirq);
/**
* acpi_subsys_thaw_noirq - Run the device driver's "noirq" thaw callback.
* @dev: Device to handle.
*/
int acpi_subsys_thaw_noirq(struct device *dev)
{
/*
* If the device is in runtime suspend, the "thaw" code may not work
* correctly with it, so skip the driver callback and make the PM core
* skip all of the subsequent "thaw" callbacks for the device.
*/
if (dev_pm_smart_suspend_and_suspended(dev)) {
dev->power.direct_complete = true;
return 0;
}
return pm_generic_thaw_noirq(dev);
}
EXPORT_SYMBOL_GPL(acpi_subsys_thaw_noirq);
#endif /* CONFIG_PM_SLEEP */ #endif /* CONFIG_PM_SLEEP */
static struct dev_pm_domain acpi_general_pm_domain = { static struct dev_pm_domain acpi_general_pm_domain = {
@ -1087,13 +1153,20 @@ static struct dev_pm_domain acpi_general_pm_domain = {
.runtime_resume = acpi_subsys_runtime_resume, .runtime_resume = acpi_subsys_runtime_resume,
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
.prepare = acpi_subsys_prepare, .prepare = acpi_subsys_prepare,
.complete = pm_complete_with_resume_check, .complete = acpi_subsys_complete,
.suspend = acpi_subsys_suspend, .suspend = acpi_subsys_suspend,
.suspend_late = acpi_subsys_suspend_late, .suspend_late = acpi_subsys_suspend_late,
.suspend_noirq = acpi_subsys_suspend_noirq,
.resume_noirq = acpi_subsys_resume_noirq,
.resume_early = acpi_subsys_resume_early, .resume_early = acpi_subsys_resume_early,
.freeze = acpi_subsys_freeze, .freeze = acpi_subsys_freeze,
.freeze_late = acpi_subsys_freeze_late,
.freeze_noirq = acpi_subsys_freeze_noirq,
.thaw_noirq = acpi_subsys_thaw_noirq,
.poweroff = acpi_subsys_suspend, .poweroff = acpi_subsys_suspend,
.poweroff_late = acpi_subsys_suspend_late, .poweroff_late = acpi_subsys_suspend_late,
.poweroff_noirq = acpi_subsys_suspend_noirq,
.restore_noirq = acpi_subsys_resume_noirq,
.restore_early = acpi_subsys_resume_early, .restore_early = acpi_subsys_resume_early,
#endif #endif
}, },

View File

@ -248,4 +248,10 @@ void acpi_watchdog_init(void);
static inline void acpi_watchdog_init(void) {} static inline void acpi_watchdog_init(void) {}
#endif #endif
#ifdef CONFIG_ACPI_LPIT
void acpi_init_lpit(void);
#else
static inline void acpi_init_lpit(void) { }
#endif
#endif /* _ACPI_INTERNAL_H_ */ #endif /* _ACPI_INTERNAL_H_ */

View File

@ -663,26 +663,8 @@ acpi_status acpi_os_write_port(acpi_io_address port, u32 value, u32 width)
EXPORT_SYMBOL(acpi_os_write_port); EXPORT_SYMBOL(acpi_os_write_port);
acpi_status int acpi_os_read_iomem(void __iomem *virt_addr, u64 *value, u32 width)
acpi_os_read_memory(acpi_physical_address phys_addr, u64 *value, u32 width)
{ {
void __iomem *virt_addr;
unsigned int size = width / 8;
bool unmap = false;
u64 dummy;
rcu_read_lock();
virt_addr = acpi_map_vaddr_lookup(phys_addr, size);
if (!virt_addr) {
rcu_read_unlock();
virt_addr = acpi_os_ioremap(phys_addr, size);
if (!virt_addr)
return AE_BAD_ADDRESS;
unmap = true;
}
if (!value)
value = &dummy;
switch (width) { switch (width) {
case 8: case 8:
@ -698,9 +680,37 @@ acpi_os_read_memory(acpi_physical_address phys_addr, u64 *value, u32 width)
*(u64 *) value = readq(virt_addr); *(u64 *) value = readq(virt_addr);
break; break;
default: default:
BUG(); return -EINVAL;
} }
return 0;
}
acpi_status
acpi_os_read_memory(acpi_physical_address phys_addr, u64 *value, u32 width)
{
void __iomem *virt_addr;
unsigned int size = width / 8;
bool unmap = false;
u64 dummy;
int error;
rcu_read_lock();
virt_addr = acpi_map_vaddr_lookup(phys_addr, size);
if (!virt_addr) {
rcu_read_unlock();
virt_addr = acpi_os_ioremap(phys_addr, size);
if (!virt_addr)
return AE_BAD_ADDRESS;
unmap = true;
}
if (!value)
value = &dummy;
error = acpi_os_read_iomem(virt_addr, value, width);
BUG_ON(error);
if (unmap) if (unmap)
iounmap(virt_addr); iounmap(virt_addr);
else else

View File

@ -2122,6 +2122,7 @@ int __init acpi_scan_init(void)
acpi_int340x_thermal_init(); acpi_int340x_thermal_init();
acpi_amba_init(); acpi_amba_init();
acpi_watchdog_init(); acpi_watchdog_init();
acpi_init_lpit();
acpi_scan_add_handler(&generic_device_handler); acpi_scan_add_handler(&generic_device_handler);

View File

@ -22,14 +22,23 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/sched/topology.h> #include <linux/sched/topology.h>
static DEFINE_MUTEX(cpu_scale_mutex); DEFINE_PER_CPU(unsigned long, freq_scale) = SCHED_CAPACITY_SCALE;
static DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE;
unsigned long topology_get_cpu_scale(struct sched_domain *sd, int cpu) void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq,
unsigned long max_freq)
{ {
return per_cpu(cpu_scale, cpu); unsigned long scale;
int i;
scale = (cur_freq << SCHED_CAPACITY_SHIFT) / max_freq;
for_each_cpu(i, cpus)
per_cpu(freq_scale, i) = scale;
} }
static DEFINE_MUTEX(cpu_scale_mutex);
DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE;
void topology_set_cpu_scale(unsigned int cpu, unsigned long capacity) void topology_set_cpu_scale(unsigned int cpu, unsigned long capacity)
{ {
per_cpu(cpu_scale, cpu) = capacity; per_cpu(cpu_scale, cpu) = capacity;
@ -212,6 +221,8 @@ static struct notifier_block init_cpu_capacity_notifier __initdata = {
static int __init register_cpufreq_notifier(void) static int __init register_cpufreq_notifier(void)
{ {
int ret;
/* /*
* on ACPI-based systems we need to use the default cpu capacity * on ACPI-based systems we need to use the default cpu capacity
* until we have the necessary code to parse the cpu capacity, so * until we have the necessary code to parse the cpu capacity, so
@ -227,8 +238,13 @@ static int __init register_cpufreq_notifier(void)
cpumask_copy(cpus_to_visit, cpu_possible_mask); cpumask_copy(cpus_to_visit, cpu_possible_mask);
return cpufreq_register_notifier(&init_cpu_capacity_notifier, ret = cpufreq_register_notifier(&init_cpu_capacity_notifier,
CPUFREQ_POLICY_NOTIFIER); CPUFREQ_POLICY_NOTIFIER);
if (ret)
free_cpumask_var(cpus_to_visit);
return ret;
} }
core_initcall(register_cpufreq_notifier); core_initcall(register_cpufreq_notifier);
@ -236,6 +252,7 @@ static void __init parsing_done_workfn(struct work_struct *work)
{ {
cpufreq_unregister_notifier(&init_cpu_capacity_notifier, cpufreq_unregister_notifier(&init_cpu_capacity_notifier,
CPUFREQ_POLICY_NOTIFIER); CPUFREQ_POLICY_NOTIFIER);
free_cpumask_var(cpus_to_visit);
} }
#else #else

View File

@ -386,7 +386,8 @@ int register_cpu(struct cpu *cpu, int num)
per_cpu(cpu_sys_devices, num) = &cpu->dev; per_cpu(cpu_sys_devices, num) = &cpu->dev;
register_cpu_under_node(num, cpu_to_node(num)); register_cpu_under_node(num, cpu_to_node(num));
dev_pm_qos_expose_latency_limit(&cpu->dev, 0); dev_pm_qos_expose_latency_limit(&cpu->dev,
PM_QOS_RESUME_LATENCY_NO_CONSTRAINT);
return 0; return 0;
} }

View File

@ -464,6 +464,7 @@ pinctrl_bind_failed:
if (dev->pm_domain && dev->pm_domain->dismiss) if (dev->pm_domain && dev->pm_domain->dismiss)
dev->pm_domain->dismiss(dev); dev->pm_domain->dismiss(dev);
pm_runtime_reinit(dev); pm_runtime_reinit(dev);
dev_pm_set_driver_flags(dev, 0);
switch (ret) { switch (ret) {
case -EPROBE_DEFER: case -EPROBE_DEFER:
@ -869,6 +870,7 @@ static void __device_release_driver(struct device *dev, struct device *parent)
if (dev->pm_domain && dev->pm_domain->dismiss) if (dev->pm_domain && dev->pm_domain->dismiss)
dev->pm_domain->dismiss(dev); dev->pm_domain->dismiss(dev);
pm_runtime_reinit(dev); pm_runtime_reinit(dev);
dev_pm_set_driver_flags(dev, 0);
klist_remove(&dev->p->knode_driver); klist_remove(&dev->p->knode_driver);
device_pm_check_callbacks(dev); device_pm_check_callbacks(dev);

View File

@ -2,7 +2,6 @@
obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o runtime.o wakeirq.o obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o runtime.o wakeirq.o
obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o
obj-$(CONFIG_PM_TRACE_RTC) += trace.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o
obj-$(CONFIG_PM_OPP) += opp/
obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o
obj-$(CONFIG_HAVE_CLK) += clock_ops.o obj-$(CONFIG_HAVE_CLK) += clock_ops.o

View File

@ -124,6 +124,7 @@ static const struct genpd_lock_ops genpd_spin_ops = {
#define genpd_status_on(genpd) (genpd->status == GPD_STATE_ACTIVE) #define genpd_status_on(genpd) (genpd->status == GPD_STATE_ACTIVE)
#define genpd_is_irq_safe(genpd) (genpd->flags & GENPD_FLAG_IRQ_SAFE) #define genpd_is_irq_safe(genpd) (genpd->flags & GENPD_FLAG_IRQ_SAFE)
#define genpd_is_always_on(genpd) (genpd->flags & GENPD_FLAG_ALWAYS_ON) #define genpd_is_always_on(genpd) (genpd->flags & GENPD_FLAG_ALWAYS_ON)
#define genpd_is_active_wakeup(genpd) (genpd->flags & GENPD_FLAG_ACTIVE_WAKEUP)
static inline bool irq_safe_dev_in_no_sleep_domain(struct device *dev, static inline bool irq_safe_dev_in_no_sleep_domain(struct device *dev,
const struct generic_pm_domain *genpd) const struct generic_pm_domain *genpd)
@ -237,6 +238,95 @@ static void genpd_update_accounting(struct generic_pm_domain *genpd)
static inline void genpd_update_accounting(struct generic_pm_domain *genpd) {} static inline void genpd_update_accounting(struct generic_pm_domain *genpd) {}
#endif #endif
/**
* dev_pm_genpd_set_performance_state- Set performance state of device's power
* domain.
*
* @dev: Device for which the performance-state needs to be set.
* @state: Target performance state of the device. This can be set as 0 when the
* device doesn't have any performance state constraints left (And so
* the device wouldn't participate anymore to find the target
* performance state of the genpd).
*
* It is assumed that the users guarantee that the genpd wouldn't be detached
* while this routine is getting called.
*
* Returns 0 on success and negative error values on failures.
*/
int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state)
{
struct generic_pm_domain *genpd;
struct generic_pm_domain_data *gpd_data, *pd_data;
struct pm_domain_data *pdd;
unsigned int prev;
int ret = 0;
genpd = dev_to_genpd(dev);
if (IS_ERR(genpd))
return -ENODEV;
if (unlikely(!genpd->set_performance_state))
return -EINVAL;
if (unlikely(!dev->power.subsys_data ||
!dev->power.subsys_data->domain_data)) {
WARN_ON(1);
return -EINVAL;
}
genpd_lock(genpd);
gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
prev = gpd_data->performance_state;
gpd_data->performance_state = state;
/* New requested state is same as Max requested state */
if (state == genpd->performance_state)
goto unlock;
/* New requested state is higher than Max requested state */
if (state > genpd->performance_state)
goto update_state;
/* Traverse all devices within the domain */
list_for_each_entry(pdd, &genpd->dev_list, list_node) {
pd_data = to_gpd_data(pdd);
if (pd_data->performance_state > state)
state = pd_data->performance_state;
}
if (state == genpd->performance_state)
goto unlock;
/*
* We aren't propagating performance state changes of a subdomain to its
* masters as we don't have hardware that needs it. Over that, the
* performance states of subdomain and its masters may not have
* one-to-one mapping and would require additional information. We can
* get back to this once we have hardware that needs it. For that
* reason, we don't have to consider performance state of the subdomains
* of genpd here.
*/
update_state:
if (genpd_status_on(genpd)) {
ret = genpd->set_performance_state(genpd, state);
if (ret) {
gpd_data->performance_state = prev;
goto unlock;
}
}
genpd->performance_state = state;
unlock:
genpd_unlock(genpd);
return ret;
}
EXPORT_SYMBOL_GPL(dev_pm_genpd_set_performance_state);
static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed) static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed)
{ {
unsigned int state_idx = genpd->state_idx; unsigned int state_idx = genpd->state_idx;
@ -256,6 +346,15 @@ static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed)
return ret; return ret;
elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
if (unlikely(genpd->set_performance_state)) {
ret = genpd->set_performance_state(genpd, genpd->performance_state);
if (ret) {
pr_warn("%s: Failed to set performance state %d (%d)\n",
genpd->name, genpd->performance_state, ret);
}
}
if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns) if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns)
return ret; return ret;
@ -346,9 +445,7 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
list_for_each_entry(pdd, &genpd->dev_list, list_node) { list_for_each_entry(pdd, &genpd->dev_list, list_node) {
enum pm_qos_flags_status stat; enum pm_qos_flags_status stat;
stat = dev_pm_qos_flags(pdd->dev, stat = dev_pm_qos_flags(pdd->dev, PM_QOS_FLAG_NO_POWER_OFF);
PM_QOS_FLAG_NO_POWER_OFF
| PM_QOS_FLAG_REMOTE_WAKEUP);
if (stat > PM_QOS_FLAGS_NONE) if (stat > PM_QOS_FLAGS_NONE)
return -EBUSY; return -EBUSY;
@ -749,11 +846,7 @@ late_initcall(genpd_power_off_unused);
#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_GENERIC_DOMAINS_OF) #if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_GENERIC_DOMAINS_OF)
/** static bool genpd_present(const struct generic_pm_domain *genpd)
* pm_genpd_present - Check if the given PM domain has been initialized.
* @genpd: PM domain to check.
*/
static bool pm_genpd_present(const struct generic_pm_domain *genpd)
{ {
const struct generic_pm_domain *gpd; const struct generic_pm_domain *gpd;
@ -771,12 +864,6 @@ static bool pm_genpd_present(const struct generic_pm_domain *genpd)
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static bool genpd_dev_active_wakeup(const struct generic_pm_domain *genpd,
struct device *dev)
{
return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev);
}
/** /**
* genpd_sync_power_off - Synchronously power off a PM domain and its masters. * genpd_sync_power_off - Synchronously power off a PM domain and its masters.
* @genpd: PM domain to power off, if possible. * @genpd: PM domain to power off, if possible.
@ -863,7 +950,7 @@ static void genpd_sync_power_on(struct generic_pm_domain *genpd, bool use_lock,
* @genpd: PM domain the device belongs to. * @genpd: PM domain the device belongs to.
* *
* There are two cases in which a device that can wake up the system from sleep * There are two cases in which a device that can wake up the system from sleep
* states should be resumed by pm_genpd_prepare(): (1) if the device is enabled * states should be resumed by genpd_prepare(): (1) if the device is enabled
* to wake up the system and it has to remain active for this purpose while the * to wake up the system and it has to remain active for this purpose while the
* system is in the sleep state and (2) if the device is not enabled to wake up * system is in the sleep state and (2) if the device is not enabled to wake up
* the system from sleep states and it generally doesn't generate wakeup signals * the system from sleep states and it generally doesn't generate wakeup signals
@ -881,12 +968,12 @@ static bool resume_needed(struct device *dev,
if (!device_can_wakeup(dev)) if (!device_can_wakeup(dev))
return false; return false;
active_wakeup = genpd_dev_active_wakeup(genpd, dev); active_wakeup = genpd_is_active_wakeup(genpd);
return device_may_wakeup(dev) ? active_wakeup : !active_wakeup; return device_may_wakeup(dev) ? active_wakeup : !active_wakeup;
} }
/** /**
* pm_genpd_prepare - Start power transition of a device in a PM domain. * genpd_prepare - Start power transition of a device in a PM domain.
* @dev: Device to start the transition of. * @dev: Device to start the transition of.
* *
* Start a power transition of a device (during a system-wide power transition) * Start a power transition of a device (during a system-wide power transition)
@ -894,7 +981,7 @@ static bool resume_needed(struct device *dev,
* an object of type struct generic_pm_domain representing a PM domain * an object of type struct generic_pm_domain representing a PM domain
* consisting of I/O devices. * consisting of I/O devices.
*/ */
static int pm_genpd_prepare(struct device *dev) static int genpd_prepare(struct device *dev)
{ {
struct generic_pm_domain *genpd; struct generic_pm_domain *genpd;
int ret; int ret;
@ -921,7 +1008,7 @@ static int pm_genpd_prepare(struct device *dev)
genpd_unlock(genpd); genpd_unlock(genpd);
ret = pm_generic_prepare(dev); ret = pm_generic_prepare(dev);
if (ret) { if (ret < 0) {
genpd_lock(genpd); genpd_lock(genpd);
genpd->prepared_count--; genpd->prepared_count--;
@ -929,7 +1016,8 @@ static int pm_genpd_prepare(struct device *dev)
genpd_unlock(genpd); genpd_unlock(genpd);
} }
return ret; /* Never return 1, as genpd don't cope with the direct_complete path. */
return ret >= 0 ? 0 : ret;
} }
/** /**
@ -950,7 +1038,7 @@ static int genpd_finish_suspend(struct device *dev, bool poweroff)
if (IS_ERR(genpd)) if (IS_ERR(genpd))
return -EINVAL; return -EINVAL;
if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)) if (dev->power.wakeup_path && genpd_is_active_wakeup(genpd))
return 0; return 0;
if (poweroff) if (poweroff)
@ -975,13 +1063,13 @@ static int genpd_finish_suspend(struct device *dev, bool poweroff)
} }
/** /**
* pm_genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain. * genpd_suspend_noirq - Completion of suspend of device in an I/O PM domain.
* @dev: Device to suspend. * @dev: Device to suspend.
* *
* Stop the device and remove power from the domain if all devices in it have * Stop the device and remove power from the domain if all devices in it have
* been stopped. * been stopped.
*/ */
static int pm_genpd_suspend_noirq(struct device *dev) static int genpd_suspend_noirq(struct device *dev)
{ {
dev_dbg(dev, "%s()\n", __func__); dev_dbg(dev, "%s()\n", __func__);
@ -989,12 +1077,12 @@ static int pm_genpd_suspend_noirq(struct device *dev)
} }
/** /**
* pm_genpd_resume_noirq - Start of resume of device in an I/O PM domain. * genpd_resume_noirq - Start of resume of device in an I/O PM domain.
* @dev: Device to resume. * @dev: Device to resume.
* *
* Restore power to the device's PM domain, if necessary, and start the device. * Restore power to the device's PM domain, if necessary, and start the device.
*/ */
static int pm_genpd_resume_noirq(struct device *dev) static int genpd_resume_noirq(struct device *dev)
{ {
struct generic_pm_domain *genpd; struct generic_pm_domain *genpd;
int ret = 0; int ret = 0;
@ -1005,7 +1093,7 @@ static int pm_genpd_resume_noirq(struct device *dev)
if (IS_ERR(genpd)) if (IS_ERR(genpd))
return -EINVAL; return -EINVAL;
if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)) if (dev->power.wakeup_path && genpd_is_active_wakeup(genpd))
return 0; return 0;
genpd_lock(genpd); genpd_lock(genpd);
@ -1024,7 +1112,7 @@ static int pm_genpd_resume_noirq(struct device *dev)
} }
/** /**
* pm_genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain. * genpd_freeze_noirq - Completion of freezing a device in an I/O PM domain.
* @dev: Device to freeze. * @dev: Device to freeze.
* *
* Carry out a late freeze of a device under the assumption that its * Carry out a late freeze of a device under the assumption that its
@ -1032,7 +1120,7 @@ static int pm_genpd_resume_noirq(struct device *dev)
* struct generic_pm_domain representing a power domain consisting of I/O * struct generic_pm_domain representing a power domain consisting of I/O
* devices. * devices.
*/ */
static int pm_genpd_freeze_noirq(struct device *dev) static int genpd_freeze_noirq(struct device *dev)
{ {
const struct generic_pm_domain *genpd; const struct generic_pm_domain *genpd;
int ret = 0; int ret = 0;
@ -1054,13 +1142,13 @@ static int pm_genpd_freeze_noirq(struct device *dev)
} }
/** /**
* pm_genpd_thaw_noirq - Early thaw of device in an I/O PM domain. * genpd_thaw_noirq - Early thaw of device in an I/O PM domain.
* @dev: Device to thaw. * @dev: Device to thaw.
* *
* Start the device, unless power has been removed from the domain already * Start the device, unless power has been removed from the domain already
* before the system transition. * before the system transition.
*/ */
static int pm_genpd_thaw_noirq(struct device *dev) static int genpd_thaw_noirq(struct device *dev)
{ {
const struct generic_pm_domain *genpd; const struct generic_pm_domain *genpd;
int ret = 0; int ret = 0;
@ -1081,14 +1169,14 @@ static int pm_genpd_thaw_noirq(struct device *dev)
} }
/** /**
* pm_genpd_poweroff_noirq - Completion of hibernation of device in an * genpd_poweroff_noirq - Completion of hibernation of device in an
* I/O PM domain. * I/O PM domain.
* @dev: Device to poweroff. * @dev: Device to poweroff.
* *
* Stop the device and remove power from the domain if all devices in it have * Stop the device and remove power from the domain if all devices in it have
* been stopped. * been stopped.
*/ */
static int pm_genpd_poweroff_noirq(struct device *dev) static int genpd_poweroff_noirq(struct device *dev)
{ {
dev_dbg(dev, "%s()\n", __func__); dev_dbg(dev, "%s()\n", __func__);
@ -1096,13 +1184,13 @@ static int pm_genpd_poweroff_noirq(struct device *dev)
} }
/** /**
* pm_genpd_restore_noirq - Start of restore of device in an I/O PM domain. * genpd_restore_noirq - Start of restore of device in an I/O PM domain.
* @dev: Device to resume. * @dev: Device to resume.
* *
* Make sure the domain will be in the same power state as before the * Make sure the domain will be in the same power state as before the
* hibernation the system is resuming from and start the device if necessary. * hibernation the system is resuming from and start the device if necessary.
*/ */
static int pm_genpd_restore_noirq(struct device *dev) static int genpd_restore_noirq(struct device *dev)
{ {
struct generic_pm_domain *genpd; struct generic_pm_domain *genpd;
int ret = 0; int ret = 0;
@ -1139,7 +1227,7 @@ static int pm_genpd_restore_noirq(struct device *dev)
} }
/** /**
* pm_genpd_complete - Complete power transition of a device in a power domain. * genpd_complete - Complete power transition of a device in a power domain.
* @dev: Device to complete the transition of. * @dev: Device to complete the transition of.
* *
* Complete a power transition of a device (during a system-wide power * Complete a power transition of a device (during a system-wide power
@ -1147,7 +1235,7 @@ static int pm_genpd_restore_noirq(struct device *dev)
* domain member of an object of type struct generic_pm_domain representing * domain member of an object of type struct generic_pm_domain representing
* a power domain consisting of I/O devices. * a power domain consisting of I/O devices.
*/ */
static void pm_genpd_complete(struct device *dev) static void genpd_complete(struct device *dev)
{ {
struct generic_pm_domain *genpd; struct generic_pm_domain *genpd;
@ -1180,7 +1268,7 @@ static void genpd_syscore_switch(struct device *dev, bool suspend)
struct generic_pm_domain *genpd; struct generic_pm_domain *genpd;
genpd = dev_to_genpd(dev); genpd = dev_to_genpd(dev);
if (!pm_genpd_present(genpd)) if (!genpd_present(genpd))
return; return;
if (suspend) { if (suspend) {
@ -1206,14 +1294,14 @@ EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweron);
#else /* !CONFIG_PM_SLEEP */ #else /* !CONFIG_PM_SLEEP */
#define pm_genpd_prepare NULL #define genpd_prepare NULL
#define pm_genpd_suspend_noirq NULL #define genpd_suspend_noirq NULL
#define pm_genpd_resume_noirq NULL #define genpd_resume_noirq NULL
#define pm_genpd_freeze_noirq NULL #define genpd_freeze_noirq NULL
#define pm_genpd_thaw_noirq NULL #define genpd_thaw_noirq NULL
#define pm_genpd_poweroff_noirq NULL #define genpd_poweroff_noirq NULL
#define pm_genpd_restore_noirq NULL #define genpd_restore_noirq NULL
#define pm_genpd_complete NULL #define genpd_complete NULL
#endif /* CONFIG_PM_SLEEP */ #endif /* CONFIG_PM_SLEEP */
@ -1239,7 +1327,7 @@ static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev,
gpd_data->base.dev = dev; gpd_data->base.dev = dev;
gpd_data->td.constraint_changed = true; gpd_data->td.constraint_changed = true;
gpd_data->td.effective_constraint_ns = -1; gpd_data->td.effective_constraint_ns = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS;
gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier;
spin_lock_irq(&dev->power.lock); spin_lock_irq(&dev->power.lock);
@ -1574,14 +1662,14 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
genpd->accounting_time = ktime_get(); genpd->accounting_time = ktime_get();
genpd->domain.ops.runtime_suspend = genpd_runtime_suspend; genpd->domain.ops.runtime_suspend = genpd_runtime_suspend;
genpd->domain.ops.runtime_resume = genpd_runtime_resume; genpd->domain.ops.runtime_resume = genpd_runtime_resume;
genpd->domain.ops.prepare = pm_genpd_prepare; genpd->domain.ops.prepare = genpd_prepare;
genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq; genpd->domain.ops.suspend_noirq = genpd_suspend_noirq;
genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq; genpd->domain.ops.resume_noirq = genpd_resume_noirq;
genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq; genpd->domain.ops.freeze_noirq = genpd_freeze_noirq;
genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq; genpd->domain.ops.thaw_noirq = genpd_thaw_noirq;
genpd->domain.ops.poweroff_noirq = pm_genpd_poweroff_noirq; genpd->domain.ops.poweroff_noirq = genpd_poweroff_noirq;
genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq; genpd->domain.ops.restore_noirq = genpd_restore_noirq;
genpd->domain.ops.complete = pm_genpd_complete; genpd->domain.ops.complete = genpd_complete;
if (genpd->flags & GENPD_FLAG_PM_CLK) { if (genpd->flags & GENPD_FLAG_PM_CLK) {
genpd->dev_ops.stop = pm_clk_suspend; genpd->dev_ops.stop = pm_clk_suspend;
@ -1795,7 +1883,7 @@ int of_genpd_add_provider_simple(struct device_node *np,
mutex_lock(&gpd_list_lock); mutex_lock(&gpd_list_lock);
if (pm_genpd_present(genpd)) { if (genpd_present(genpd)) {
ret = genpd_add_provider(np, genpd_xlate_simple, genpd); ret = genpd_add_provider(np, genpd_xlate_simple, genpd);
if (!ret) { if (!ret) {
genpd->provider = &np->fwnode; genpd->provider = &np->fwnode;
@ -1831,7 +1919,7 @@ int of_genpd_add_provider_onecell(struct device_node *np,
for (i = 0; i < data->num_domains; i++) { for (i = 0; i < data->num_domains; i++) {
if (!data->domains[i]) if (!data->domains[i])
continue; continue;
if (!pm_genpd_present(data->domains[i])) if (!genpd_present(data->domains[i]))
goto error; goto error;
data->domains[i]->provider = &np->fwnode; data->domains[i]->provider = &np->fwnode;
@ -2274,7 +2362,7 @@ EXPORT_SYMBOL_GPL(of_genpd_parse_idle_states);
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/kobject.h> #include <linux/kobject.h>
static struct dentry *pm_genpd_debugfs_dir; static struct dentry *genpd_debugfs_dir;
/* /*
* TODO: This function is a slightly modified version of rtpm_status_show * TODO: This function is a slightly modified version of rtpm_status_show
@ -2302,8 +2390,8 @@ static void rtpm_status_str(struct seq_file *s, struct device *dev)
seq_puts(s, p); seq_puts(s, p);
} }
static int pm_genpd_summary_one(struct seq_file *s, static int genpd_summary_one(struct seq_file *s,
struct generic_pm_domain *genpd) struct generic_pm_domain *genpd)
{ {
static const char * const status_lookup[] = { static const char * const status_lookup[] = {
[GPD_STATE_ACTIVE] = "on", [GPD_STATE_ACTIVE] = "on",
@ -2373,7 +2461,7 @@ static int genpd_summary_show(struct seq_file *s, void *data)
return -ERESTARTSYS; return -ERESTARTSYS;
list_for_each_entry(genpd, &gpd_list, gpd_list_node) { list_for_each_entry(genpd, &gpd_list, gpd_list_node) {
ret = pm_genpd_summary_one(s, genpd); ret = genpd_summary_one(s, genpd);
if (ret) if (ret)
break; break;
} }
@ -2559,23 +2647,23 @@ define_genpd_debugfs_fops(active_time);
define_genpd_debugfs_fops(total_idle_time); define_genpd_debugfs_fops(total_idle_time);
define_genpd_debugfs_fops(devices); define_genpd_debugfs_fops(devices);
static int __init pm_genpd_debug_init(void) static int __init genpd_debug_init(void)
{ {
struct dentry *d; struct dentry *d;
struct generic_pm_domain *genpd; struct generic_pm_domain *genpd;
pm_genpd_debugfs_dir = debugfs_create_dir("pm_genpd", NULL); genpd_debugfs_dir = debugfs_create_dir("pm_genpd", NULL);
if (!pm_genpd_debugfs_dir) if (!genpd_debugfs_dir)
return -ENOMEM; return -ENOMEM;
d = debugfs_create_file("pm_genpd_summary", S_IRUGO, d = debugfs_create_file("pm_genpd_summary", S_IRUGO,
pm_genpd_debugfs_dir, NULL, &genpd_summary_fops); genpd_debugfs_dir, NULL, &genpd_summary_fops);
if (!d) if (!d)
return -ENOMEM; return -ENOMEM;
list_for_each_entry(genpd, &gpd_list, gpd_list_node) { list_for_each_entry(genpd, &gpd_list, gpd_list_node) {
d = debugfs_create_dir(genpd->name, pm_genpd_debugfs_dir); d = debugfs_create_dir(genpd->name, genpd_debugfs_dir);
if (!d) if (!d)
return -ENOMEM; return -ENOMEM;
@ -2595,11 +2683,11 @@ static int __init pm_genpd_debug_init(void)
return 0; return 0;
} }
late_initcall(pm_genpd_debug_init); late_initcall(genpd_debug_init);
static void __exit pm_genpd_debug_exit(void) static void __exit genpd_debug_exit(void)
{ {
debugfs_remove_recursive(pm_genpd_debugfs_dir); debugfs_remove_recursive(genpd_debugfs_dir);
} }
__exitcall(pm_genpd_debug_exit); __exitcall(genpd_debug_exit);
#endif /* CONFIG_DEBUG_FS */ #endif /* CONFIG_DEBUG_FS */

View File

@ -14,23 +14,29 @@
static int dev_update_qos_constraint(struct device *dev, void *data) static int dev_update_qos_constraint(struct device *dev, void *data)
{ {
s64 *constraint_ns_p = data; s64 *constraint_ns_p = data;
s32 constraint_ns = -1; s64 constraint_ns;
if (dev->power.subsys_data && dev->power.subsys_data->domain_data) if (dev->power.subsys_data && dev->power.subsys_data->domain_data) {
/*
* Only take suspend-time QoS constraints of devices into
* account, because constraints updated after the device has
* been suspended are not guaranteed to be taken into account
* anyway. In order for them to take effect, the device has to
* be resumed and suspended again.
*/
constraint_ns = dev_gpd_data(dev)->td.effective_constraint_ns; constraint_ns = dev_gpd_data(dev)->td.effective_constraint_ns;
} else {
if (constraint_ns < 0) { /*
* The child is not in a domain and there's no info on its
* suspend/resume latencies, so assume them to be negligible and
* take its current PM QoS constraint (that's the only thing
* known at this point anyway).
*/
constraint_ns = dev_pm_qos_read_value(dev); constraint_ns = dev_pm_qos_read_value(dev);
constraint_ns *= NSEC_PER_USEC; constraint_ns *= NSEC_PER_USEC;
} }
if (constraint_ns == 0)
return 0;
/* if (constraint_ns < *constraint_ns_p)
* constraint_ns cannot be negative here, because the device has been
* suspended.
*/
if (constraint_ns < *constraint_ns_p || *constraint_ns_p == 0)
*constraint_ns_p = constraint_ns; *constraint_ns_p = constraint_ns;
return 0; return 0;
@ -58,12 +64,12 @@ static bool default_suspend_ok(struct device *dev)
} }
td->constraint_changed = false; td->constraint_changed = false;
td->cached_suspend_ok = false; td->cached_suspend_ok = false;
td->effective_constraint_ns = -1; td->effective_constraint_ns = 0;
constraint_ns = __dev_pm_qos_read_value(dev); constraint_ns = __dev_pm_qos_read_value(dev);
spin_unlock_irqrestore(&dev->power.lock, flags); spin_unlock_irqrestore(&dev->power.lock, flags);
if (constraint_ns < 0) if (constraint_ns == 0)
return false; return false;
constraint_ns *= NSEC_PER_USEC; constraint_ns *= NSEC_PER_USEC;
@ -76,14 +82,32 @@ static bool default_suspend_ok(struct device *dev)
device_for_each_child(dev, &constraint_ns, device_for_each_child(dev, &constraint_ns,
dev_update_qos_constraint); dev_update_qos_constraint);
if (constraint_ns > 0) { if (constraint_ns == PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS) {
/* "No restriction", so the device is allowed to suspend. */
td->effective_constraint_ns = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS;
td->cached_suspend_ok = true;
} else if (constraint_ns == 0) {
/*
* This triggers if one of the children that don't belong to a
* domain has a zero PM QoS constraint and it's better not to
* suspend then. effective_constraint_ns is zero already and
* cached_suspend_ok is false, so bail out.
*/
return false;
} else {
constraint_ns -= td->suspend_latency_ns + constraint_ns -= td->suspend_latency_ns +
td->resume_latency_ns; td->resume_latency_ns;
if (constraint_ns == 0) /*
* effective_constraint_ns is zero already and cached_suspend_ok
* is false, so if the computed value is not positive, return
* right away.
*/
if (constraint_ns <= 0)
return false; return false;
td->effective_constraint_ns = constraint_ns;
td->cached_suspend_ok = true;
} }
td->effective_constraint_ns = constraint_ns;
td->cached_suspend_ok = constraint_ns >= 0;
/* /*
* The children have been suspended already, so we don't need to take * The children have been suspended already, so we don't need to take
@ -144,18 +168,13 @@ static bool __default_power_down_ok(struct dev_pm_domain *pd,
*/ */
td = &to_gpd_data(pdd)->td; td = &to_gpd_data(pdd)->td;
constraint_ns = td->effective_constraint_ns; constraint_ns = td->effective_constraint_ns;
/* default_suspend_ok() need not be called before us. */ /*
if (constraint_ns < 0) { * Zero means "no suspend at all" and this runs only when all
constraint_ns = dev_pm_qos_read_value(pdd->dev); * devices in the domain are suspended, so it must be positive.
constraint_ns *= NSEC_PER_USEC; */
} if (constraint_ns == PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS)
if (constraint_ns == 0)
continue; continue;
/*
* constraint_ns cannot be negative here, because the device has
* been suspended.
*/
if (constraint_ns <= off_on_time_ns) if (constraint_ns <= off_on_time_ns)
return false; return false;

View File

@ -9,7 +9,6 @@
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/suspend.h>
#ifdef CONFIG_PM #ifdef CONFIG_PM
/** /**
@ -298,26 +297,4 @@ void pm_generic_complete(struct device *dev)
if (drv && drv->pm && drv->pm->complete) if (drv && drv->pm && drv->pm->complete)
drv->pm->complete(dev); drv->pm->complete(dev);
} }
/**
* pm_complete_with_resume_check - Complete a device power transition.
* @dev: Device to handle.
*
* Complete a device power transition during a system-wide power transition and
* optionally schedule a runtime resume of the device if the system resume in
* progress has been initated by the platform firmware and the device had its
* power.direct_complete flag set.
*/
void pm_complete_with_resume_check(struct device *dev)
{
pm_generic_complete(dev);
/*
* If the device had been runtime-suspended before the system went into
* the sleep state it is going out of and it has never been resumed till
* now, resume it in case the firmware powered it up.
*/
if (dev->power.direct_complete && pm_resume_via_firmware())
pm_request_resume(dev);
}
EXPORT_SYMBOL_GPL(pm_complete_with_resume_check);
#endif /* CONFIG_PM_SLEEP */ #endif /* CONFIG_PM_SLEEP */

View File

@ -526,7 +526,7 @@ static void dpm_watchdog_clear(struct dpm_watchdog *wd)
/*------------------------- Resume routines -------------------------*/ /*------------------------- Resume routines -------------------------*/
/** /**
* device_resume_noirq - Execute an "early resume" callback for given device. * device_resume_noirq - Execute a "noirq resume" callback for given device.
* @dev: Device to handle. * @dev: Device to handle.
* @state: PM transition of the system being carried out. * @state: PM transition of the system being carried out.
* @async: If true, the device is being resumed asynchronously. * @async: If true, the device is being resumed asynchronously.
@ -846,16 +846,10 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)
goto Driver; goto Driver;
} }
if (dev->class) { if (dev->class && dev->class->pm) {
if (dev->class->pm) { info = "class ";
info = "class "; callback = pm_op(dev->class->pm, state);
callback = pm_op(dev->class->pm, state); goto Driver;
goto Driver;
} else if (dev->class->resume) {
info = "legacy class ";
callback = dev->class->resume;
goto End;
}
} }
if (dev->bus) { if (dev->bus) {
@ -1081,7 +1075,7 @@ static pm_message_t resume_event(pm_message_t sleep_state)
} }
/** /**
* device_suspend_noirq - Execute a "late suspend" callback for given device. * __device_suspend_noirq - Execute a "noirq suspend" callback for given device.
* @dev: Device to handle. * @dev: Device to handle.
* @state: PM transition of the system being carried out. * @state: PM transition of the system being carried out.
* @async: If true, the device is being suspended asynchronously. * @async: If true, the device is being suspended asynchronously.
@ -1241,7 +1235,7 @@ int dpm_suspend_noirq(pm_message_t state)
} }
/** /**
* device_suspend_late - Execute a "late suspend" callback for given device. * __device_suspend_late - Execute a "late suspend" callback for given device.
* @dev: Device to handle. * @dev: Device to handle.
* @state: PM transition of the system being carried out. * @state: PM transition of the system being carried out.
* @async: If true, the device is being suspended asynchronously. * @async: If true, the device is being suspended asynchronously.
@ -1443,7 +1437,7 @@ static void dpm_clear_suppliers_direct_complete(struct device *dev)
} }
/** /**
* device_suspend - Execute "suspend" callbacks for given device. * __device_suspend - Execute "suspend" callbacks for given device.
* @dev: Device to handle. * @dev: Device to handle.
* @state: PM transition of the system being carried out. * @state: PM transition of the system being carried out.
* @async: If true, the device is being suspended asynchronously. * @async: If true, the device is being suspended asynchronously.
@ -1506,17 +1500,10 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
goto Run; goto Run;
} }
if (dev->class) { if (dev->class && dev->class->pm) {
if (dev->class->pm) { info = "class ";
info = "class "; callback = pm_op(dev->class->pm, state);
callback = pm_op(dev->class->pm, state); goto Run;
goto Run;
} else if (dev->class->suspend) {
pm_dev_dbg(dev, state, "legacy class ");
error = legacy_suspend(dev, state, dev->class->suspend,
"legacy class ");
goto End;
}
} }
if (dev->bus) { if (dev->bus) {
@ -1663,6 +1650,9 @@ static int device_prepare(struct device *dev, pm_message_t state)
if (dev->power.syscore) if (dev->power.syscore)
return 0; return 0;
WARN_ON(dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) &&
!pm_runtime_enabled(dev));
/* /*
* If a device's parent goes into runtime suspend at the wrong time, * If a device's parent goes into runtime suspend at the wrong time,
* it won't be possible to resume the device. To prevent this we * it won't be possible to resume the device. To prevent this we
@ -1711,7 +1701,9 @@ unlock:
* applies to suspend transitions, however. * applies to suspend transitions, however.
*/ */
spin_lock_irq(&dev->power.lock); spin_lock_irq(&dev->power.lock);
dev->power.direct_complete = ret > 0 && state.event == PM_EVENT_SUSPEND; dev->power.direct_complete = state.event == PM_EVENT_SUSPEND &&
pm_runtime_suspended(dev) && ret > 0 &&
!dev_pm_test_driver_flags(dev, DPM_FLAG_NEVER_SKIP);
spin_unlock_irq(&dev->power.lock); spin_unlock_irq(&dev->power.lock);
return 0; return 0;
} }
@ -1860,11 +1852,16 @@ void device_pm_check_callbacks(struct device *dev)
dev->power.no_pm_callbacks = dev->power.no_pm_callbacks =
(!dev->bus || (pm_ops_is_empty(dev->bus->pm) && (!dev->bus || (pm_ops_is_empty(dev->bus->pm) &&
!dev->bus->suspend && !dev->bus->resume)) && !dev->bus->suspend && !dev->bus->resume)) &&
(!dev->class || (pm_ops_is_empty(dev->class->pm) && (!dev->class || pm_ops_is_empty(dev->class->pm)) &&
!dev->class->suspend && !dev->class->resume)) &&
(!dev->type || pm_ops_is_empty(dev->type->pm)) && (!dev->type || pm_ops_is_empty(dev->type->pm)) &&
(!dev->pm_domain || pm_ops_is_empty(&dev->pm_domain->ops)) && (!dev->pm_domain || pm_ops_is_empty(&dev->pm_domain->ops)) &&
(!dev->driver || (pm_ops_is_empty(dev->driver->pm) && (!dev->driver || (pm_ops_is_empty(dev->driver->pm) &&
!dev->driver->suspend && !dev->driver->resume)); !dev->driver->suspend && !dev->driver->resume));
spin_unlock_irq(&dev->power.lock); spin_unlock_irq(&dev->power.lock);
} }
bool dev_pm_smart_suspend_and_suspended(struct device *dev)
{
return dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) &&
pm_runtime_status_suspended(dev);
}

View File

@ -139,6 +139,9 @@ static int apply_constraint(struct dev_pm_qos_request *req,
switch(req->type) { switch(req->type) {
case DEV_PM_QOS_RESUME_LATENCY: case DEV_PM_QOS_RESUME_LATENCY:
if (WARN_ON(action != PM_QOS_REMOVE_REQ && value < 0))
value = 0;
ret = pm_qos_update_target(&qos->resume_latency, ret = pm_qos_update_target(&qos->resume_latency,
&req->data.pnode, action, value); &req->data.pnode, action, value);
break; break;
@ -189,7 +192,7 @@ static int dev_pm_qos_constraints_allocate(struct device *dev)
plist_head_init(&c->list); plist_head_init(&c->list);
c->target_value = PM_QOS_RESUME_LATENCY_DEFAULT_VALUE; c->target_value = PM_QOS_RESUME_LATENCY_DEFAULT_VALUE;
c->default_value = PM_QOS_RESUME_LATENCY_DEFAULT_VALUE; c->default_value = PM_QOS_RESUME_LATENCY_DEFAULT_VALUE;
c->no_constraint_value = PM_QOS_RESUME_LATENCY_DEFAULT_VALUE; c->no_constraint_value = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
c->type = PM_QOS_MIN; c->type = PM_QOS_MIN;
c->notifiers = n; c->notifiers = n;

View File

@ -253,7 +253,7 @@ static int rpm_check_suspend_allowed(struct device *dev)
|| (dev->power.request_pending || (dev->power.request_pending
&& dev->power.request == RPM_REQ_RESUME)) && dev->power.request == RPM_REQ_RESUME))
retval = -EAGAIN; retval = -EAGAIN;
else if (__dev_pm_qos_read_value(dev) < 0) else if (__dev_pm_qos_read_value(dev) == 0)
retval = -EPERM; retval = -EPERM;
else if (dev->power.runtime_status == RPM_SUSPENDED) else if (dev->power.runtime_status == RPM_SUSPENDED)
retval = 1; retval = 1;
@ -894,9 +894,9 @@ static void pm_runtime_work(struct work_struct *work)
* *
* Check if the time is right and queue a suspend request. * Check if the time is right and queue a suspend request.
*/ */
static void pm_suspend_timer_fn(unsigned long data) static void pm_suspend_timer_fn(struct timer_list *t)
{ {
struct device *dev = (struct device *)data; struct device *dev = from_timer(dev, t, power.suspend_timer);
unsigned long flags; unsigned long flags;
unsigned long expires; unsigned long expires;
@ -1499,8 +1499,7 @@ void pm_runtime_init(struct device *dev)
INIT_WORK(&dev->power.work, pm_runtime_work); INIT_WORK(&dev->power.work, pm_runtime_work);
dev->power.timer_expires = 0; dev->power.timer_expires = 0;
setup_timer(&dev->power.suspend_timer, pm_suspend_timer_fn, timer_setup(&dev->power.suspend_timer, pm_suspend_timer_fn, 0);
(unsigned long)dev);
init_waitqueue_head(&dev->power.wait_queue); init_waitqueue_head(&dev->power.wait_queue);
} }

View File

@ -218,7 +218,14 @@ static ssize_t pm_qos_resume_latency_show(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
{ {
return sprintf(buf, "%d\n", dev_pm_qos_requested_resume_latency(dev)); s32 value = dev_pm_qos_requested_resume_latency(dev);
if (value == 0)
return sprintf(buf, "n/a\n");
else if (value == PM_QOS_RESUME_LATENCY_NO_CONSTRAINT)
value = 0;
return sprintf(buf, "%d\n", value);
} }
static ssize_t pm_qos_resume_latency_store(struct device *dev, static ssize_t pm_qos_resume_latency_store(struct device *dev,
@ -228,11 +235,21 @@ static ssize_t pm_qos_resume_latency_store(struct device *dev,
s32 value; s32 value;
int ret; int ret;
if (kstrtos32(buf, 0, &value)) if (!kstrtos32(buf, 0, &value)) {
return -EINVAL; /*
* Prevent users from writing negative or "no constraint" values
* directly.
*/
if (value < 0 || value == PM_QOS_RESUME_LATENCY_NO_CONSTRAINT)
return -EINVAL;
if (value < 0) if (value == 0)
value = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
} else if (!strcmp(buf, "n/a") || !strcmp(buf, "n/a\n")) {
value = 0;
} else {
return -EINVAL; return -EINVAL;
}
ret = dev_pm_qos_update_request(dev->power.qos->resume_latency_req, ret = dev_pm_qos_update_request(dev->power.qos->resume_latency_req,
value); value);
@ -309,33 +326,6 @@ static ssize_t pm_qos_no_power_off_store(struct device *dev,
static DEVICE_ATTR(pm_qos_no_power_off, 0644, static DEVICE_ATTR(pm_qos_no_power_off, 0644,
pm_qos_no_power_off_show, pm_qos_no_power_off_store); pm_qos_no_power_off_show, pm_qos_no_power_off_store);
static ssize_t pm_qos_remote_wakeup_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return sprintf(buf, "%d\n", !!(dev_pm_qos_requested_flags(dev)
& PM_QOS_FLAG_REMOTE_WAKEUP));
}
static ssize_t pm_qos_remote_wakeup_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t n)
{
int ret;
if (kstrtoint(buf, 0, &ret))
return -EINVAL;
if (ret != 0 && ret != 1)
return -EINVAL;
ret = dev_pm_qos_update_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP, ret);
return ret < 0 ? ret : n;
}
static DEVICE_ATTR(pm_qos_remote_wakeup, 0644,
pm_qos_remote_wakeup_show, pm_qos_remote_wakeup_store);
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static const char _enabled[] = "enabled"; static const char _enabled[] = "enabled";
static const char _disabled[] = "disabled"; static const char _disabled[] = "disabled";
@ -671,7 +661,6 @@ static const struct attribute_group pm_qos_latency_tolerance_attr_group = {
static struct attribute *pm_qos_flags_attrs[] = { static struct attribute *pm_qos_flags_attrs[] = {
&dev_attr_pm_qos_no_power_off.attr, &dev_attr_pm_qos_no_power_off.attr,
&dev_attr_pm_qos_remote_wakeup.attr,
NULL, NULL,
}; };
static const struct attribute_group pm_qos_flags_attr_group = { static const struct attribute_group pm_qos_flags_attr_group = {

View File

@ -54,7 +54,7 @@ static unsigned int saved_count;
static DEFINE_SPINLOCK(events_lock); static DEFINE_SPINLOCK(events_lock);
static void pm_wakeup_timer_fn(unsigned long data); static void pm_wakeup_timer_fn(struct timer_list *t);
static LIST_HEAD(wakeup_sources); static LIST_HEAD(wakeup_sources);
@ -176,7 +176,7 @@ void wakeup_source_add(struct wakeup_source *ws)
return; return;
spin_lock_init(&ws->lock); spin_lock_init(&ws->lock);
setup_timer(&ws->timer, pm_wakeup_timer_fn, (unsigned long)ws); timer_setup(&ws->timer, pm_wakeup_timer_fn, 0);
ws->active = false; ws->active = false;
ws->last_time = ktime_get(); ws->last_time = ktime_get();
@ -481,8 +481,7 @@ static bool wakeup_source_not_registered(struct wakeup_source *ws)
* Use timer struct to check if the given source is initialized * Use timer struct to check if the given source is initialized
* by wakeup_source_add. * by wakeup_source_add.
*/ */
return ws->timer.function != pm_wakeup_timer_fn || return ws->timer.function != (TIMER_FUNC_TYPE)pm_wakeup_timer_fn;
ws->timer.data != (unsigned long)ws;
} }
/* /*
@ -724,9 +723,9 @@ EXPORT_SYMBOL_GPL(pm_relax);
* in @data if it is currently active and its timer has not been canceled and * in @data if it is currently active and its timer has not been canceled and
* the expiration time of the timer is not in future. * the expiration time of the timer is not in future.
*/ */
static void pm_wakeup_timer_fn(unsigned long data) static void pm_wakeup_timer_fn(struct timer_list *t)
{ {
struct wakeup_source *ws = (struct wakeup_source *)data; struct wakeup_source *ws = from_timer(ws, t, timer);
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&ws->lock, flags); spin_lock_irqsave(&ws->lock, flags);

View File

@ -57,7 +57,7 @@ static bool bL_switching_enabled;
#define VIRT_FREQ(cluster, freq) ((cluster == A7_CLUSTER) ? freq >> 1 : freq) #define VIRT_FREQ(cluster, freq) ((cluster == A7_CLUSTER) ? freq >> 1 : freq)
static struct thermal_cooling_device *cdev[MAX_CLUSTERS]; static struct thermal_cooling_device *cdev[MAX_CLUSTERS];
static struct cpufreq_arm_bL_ops *arm_bL_ops; static const struct cpufreq_arm_bL_ops *arm_bL_ops;
static struct clk *clk[MAX_CLUSTERS]; static struct clk *clk[MAX_CLUSTERS];
static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS + 1]; static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS + 1];
static atomic_t cluster_usage[MAX_CLUSTERS + 1]; static atomic_t cluster_usage[MAX_CLUSTERS + 1];
@ -213,6 +213,7 @@ static int bL_cpufreq_set_target(struct cpufreq_policy *policy,
{ {
u32 cpu = policy->cpu, cur_cluster, new_cluster, actual_cluster; u32 cpu = policy->cpu, cur_cluster, new_cluster, actual_cluster;
unsigned int freqs_new; unsigned int freqs_new;
int ret;
cur_cluster = cpu_to_cluster(cpu); cur_cluster = cpu_to_cluster(cpu);
new_cluster = actual_cluster = per_cpu(physical_cluster, cpu); new_cluster = actual_cluster = per_cpu(physical_cluster, cpu);
@ -229,7 +230,14 @@ static int bL_cpufreq_set_target(struct cpufreq_policy *policy,
} }
} }
return bL_cpufreq_set_rate(cpu, actual_cluster, new_cluster, freqs_new); ret = bL_cpufreq_set_rate(cpu, actual_cluster, new_cluster, freqs_new);
if (!ret) {
arch_set_freq_scale(policy->related_cpus, freqs_new,
policy->cpuinfo.max_freq);
}
return ret;
} }
static inline u32 get_table_count(struct cpufreq_frequency_table *table) static inline u32 get_table_count(struct cpufreq_frequency_table *table)
@ -609,7 +617,7 @@ static int __bLs_register_notifier(void) { return 0; }
static int __bLs_unregister_notifier(void) { return 0; } static int __bLs_unregister_notifier(void) { return 0; }
#endif #endif
int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops) int bL_cpufreq_register(const struct cpufreq_arm_bL_ops *ops)
{ {
int ret, i; int ret, i;
@ -653,7 +661,7 @@ int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops)
} }
EXPORT_SYMBOL_GPL(bL_cpufreq_register); EXPORT_SYMBOL_GPL(bL_cpufreq_register);
void bL_cpufreq_unregister(struct cpufreq_arm_bL_ops *ops) void bL_cpufreq_unregister(const struct cpufreq_arm_bL_ops *ops)
{ {
if (arm_bL_ops != ops) { if (arm_bL_ops != ops) {
pr_err("%s: Registered with: %s, can't unregister, exiting\n", pr_err("%s: Registered with: %s, can't unregister, exiting\n",

View File

@ -37,7 +37,7 @@ struct cpufreq_arm_bL_ops {
void (*free_opp_table)(const struct cpumask *cpumask); void (*free_opp_table)(const struct cpumask *cpumask);
}; };
int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops); int bL_cpufreq_register(const struct cpufreq_arm_bL_ops *ops);
void bL_cpufreq_unregister(struct cpufreq_arm_bL_ops *ops); void bL_cpufreq_unregister(const struct cpufreq_arm_bL_ops *ops);
#endif /* CPUFREQ_ARM_BIG_LITTLE_H */ #endif /* CPUFREQ_ARM_BIG_LITTLE_H */

View File

@ -61,7 +61,7 @@ static int dt_get_transition_latency(struct device *cpu_dev)
return transition_latency; return transition_latency;
} }
static struct cpufreq_arm_bL_ops dt_bL_ops = { static const struct cpufreq_arm_bL_ops dt_bL_ops = {
.name = "dt-bl", .name = "dt-bl",
.get_transition_latency = dt_get_transition_latency, .get_transition_latency = dt_get_transition_latency,
.init_opp_table = dev_pm_opp_of_cpumask_add_table, .init_opp_table = dev_pm_opp_of_cpumask_add_table,

View File

@ -48,7 +48,6 @@ static const struct of_device_id whitelist[] __initconst = {
{ .compatible = "samsung,exynos3250", }, { .compatible = "samsung,exynos3250", },
{ .compatible = "samsung,exynos4210", }, { .compatible = "samsung,exynos4210", },
{ .compatible = "samsung,exynos4212", },
{ .compatible = "samsung,exynos5250", }, { .compatible = "samsung,exynos5250", },
#ifndef CONFIG_BL_SWITCHER #ifndef CONFIG_BL_SWITCHER
{ .compatible = "samsung,exynos5800", }, { .compatible = "samsung,exynos5800", },
@ -83,8 +82,6 @@ static const struct of_device_id whitelist[] __initconst = {
{ .compatible = "rockchip,rk3368", }, { .compatible = "rockchip,rk3368", },
{ .compatible = "rockchip,rk3399", }, { .compatible = "rockchip,rk3399", },
{ .compatible = "socionext,uniphier-ld6b", },
{ .compatible = "st-ericsson,u8500", }, { .compatible = "st-ericsson,u8500", },
{ .compatible = "st-ericsson,u8540", }, { .compatible = "st-ericsson,u8540", },
{ .compatible = "st-ericsson,u9500", }, { .compatible = "st-ericsson,u9500", },

View File

@ -43,9 +43,17 @@ static struct freq_attr *cpufreq_dt_attr[] = {
static int set_target(struct cpufreq_policy *policy, unsigned int index) static int set_target(struct cpufreq_policy *policy, unsigned int index)
{ {
struct private_data *priv = policy->driver_data; struct private_data *priv = policy->driver_data;
unsigned long freq = policy->freq_table[index].frequency;
int ret;
return dev_pm_opp_set_rate(priv->cpu_dev, ret = dev_pm_opp_set_rate(priv->cpu_dev, freq * 1000);
policy->freq_table[index].frequency * 1000);
if (!ret) {
arch_set_freq_scale(policy->related_cpus, freq,
policy->cpuinfo.max_freq);
}
return ret;
} }
/* /*

View File

@ -161,6 +161,12 @@ u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy)
} }
EXPORT_SYMBOL_GPL(get_cpu_idle_time); EXPORT_SYMBOL_GPL(get_cpu_idle_time);
__weak void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq,
unsigned long max_freq)
{
}
EXPORT_SYMBOL_GPL(arch_set_freq_scale);
/* /*
* This is a generic cpufreq init() routine which can be used by cpufreq * This is a generic cpufreq init() routine which can be used by cpufreq
* drivers of SMP systems. It will do following: * drivers of SMP systems. It will do following:

View File

@ -118,8 +118,11 @@ static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
break; break;
len += snprintf(buf + len, PAGE_SIZE - len, "\n"); len += snprintf(buf + len, PAGE_SIZE - len, "\n");
} }
if (len >= PAGE_SIZE)
return PAGE_SIZE; if (len >= PAGE_SIZE) {
pr_warn_once("cpufreq transition table exceeds PAGE_SIZE. Disabling\n");
return -EFBIG;
}
return len; return len;
} }
cpufreq_freq_attr_ro(trans_table); cpufreq_freq_attr_ro(trans_table);

View File

@ -12,6 +12,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h>
#include <linux/pm_opp.h> #include <linux/pm_opp.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
@ -191,6 +192,57 @@ static struct cpufreq_driver imx6q_cpufreq_driver = {
.suspend = cpufreq_generic_suspend, .suspend = cpufreq_generic_suspend,
}; };
#define OCOTP_CFG3 0x440
#define OCOTP_CFG3_SPEED_SHIFT 16
#define OCOTP_CFG3_SPEED_1P2GHZ 0x3
#define OCOTP_CFG3_SPEED_996MHZ 0x2
#define OCOTP_CFG3_SPEED_852MHZ 0x1
static void imx6q_opp_check_speed_grading(struct device *dev)
{
struct device_node *np;
void __iomem *base;
u32 val;
np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-ocotp");
if (!np)
return;
base = of_iomap(np, 0);
if (!base) {
dev_err(dev, "failed to map ocotp\n");
goto put_node;
}
/*
* SPEED_GRADING[1:0] defines the max speed of ARM:
* 2b'11: 1200000000Hz;
* 2b'10: 996000000Hz;
* 2b'01: 852000000Hz; -- i.MX6Q Only, exclusive with 996MHz.
* 2b'00: 792000000Hz;
* We need to set the max speed of ARM according to fuse map.
*/
val = readl_relaxed(base + OCOTP_CFG3);
val >>= OCOTP_CFG3_SPEED_SHIFT;
val &= 0x3;
if ((val != OCOTP_CFG3_SPEED_1P2GHZ) &&
of_machine_is_compatible("fsl,imx6q"))
if (dev_pm_opp_disable(dev, 1200000000))
dev_warn(dev, "failed to disable 1.2GHz OPP\n");
if (val < OCOTP_CFG3_SPEED_996MHZ)
if (dev_pm_opp_disable(dev, 996000000))
dev_warn(dev, "failed to disable 996MHz OPP\n");
if (of_machine_is_compatible("fsl,imx6q")) {
if (val != OCOTP_CFG3_SPEED_852MHZ)
if (dev_pm_opp_disable(dev, 852000000))
dev_warn(dev, "failed to disable 852MHz OPP\n");
}
iounmap(base);
put_node:
of_node_put(np);
}
static int imx6q_cpufreq_probe(struct platform_device *pdev) static int imx6q_cpufreq_probe(struct platform_device *pdev)
{ {
struct device_node *np; struct device_node *np;
@ -252,28 +304,21 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
goto put_reg; goto put_reg;
} }
/* ret = dev_pm_opp_of_add_table(cpu_dev);
* We expect an OPP table supplied by platform. if (ret < 0) {
* Just, incase the platform did not supply the OPP dev_err(cpu_dev, "failed to init OPP table: %d\n", ret);
* table, it will try to get it. goto put_reg;
*/ }
imx6q_opp_check_speed_grading(cpu_dev);
/* Because we have added the OPPs here, we must free them */
free_opp = true;
num = dev_pm_opp_get_opp_count(cpu_dev); num = dev_pm_opp_get_opp_count(cpu_dev);
if (num < 0) { if (num < 0) {
ret = dev_pm_opp_of_add_table(cpu_dev); ret = num;
if (ret < 0) { dev_err(cpu_dev, "no OPP table is found: %d\n", ret);
dev_err(cpu_dev, "failed to init OPP table: %d\n", ret); goto out_free_opp;
goto put_reg;
}
/* Because we have added the OPPs here, we must free them */
free_opp = true;
num = dev_pm_opp_get_opp_count(cpu_dev);
if (num < 0) {
ret = num;
dev_err(cpu_dev, "no OPP table is found: %d\n", ret);
goto out_free_opp;
}
} }
ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);

View File

@ -1043,7 +1043,7 @@ static int powernowk8_cpu_init(struct cpufreq_policy *pol)
data = kzalloc(sizeof(*data), GFP_KERNEL); data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) { if (!data) {
pr_err("unable to alloc powernow_k8_data"); pr_err("unable to alloc powernow_k8_data\n");
return -ENOMEM; return -ENOMEM;
} }

View File

@ -58,56 +58,40 @@ module_param(pxa27x_maxfreq, uint, 0);
MODULE_PARM_DESC(pxa27x_maxfreq, "Set the pxa27x maxfreq in MHz" MODULE_PARM_DESC(pxa27x_maxfreq, "Set the pxa27x maxfreq in MHz"
"(typically 624=>pxa270, 416=>pxa271, 520=>pxa272)"); "(typically 624=>pxa270, 416=>pxa271, 520=>pxa272)");
struct pxa_cpufreq_data {
struct clk *clk_core;
};
static struct pxa_cpufreq_data pxa_cpufreq_data;
struct pxa_freqs { struct pxa_freqs {
unsigned int khz; unsigned int khz;
unsigned int membus;
unsigned int cccr;
unsigned int div2;
unsigned int cclkcfg;
int vmin; int vmin;
int vmax; int vmax;
}; };
/* Define the refresh period in mSec for the SDRAM and the number of rows */
#define SDRAM_TREF 64 /* standard 64ms SDRAM */
static unsigned int sdram_rows;
#define CCLKCFG_TURBO 0x1
#define CCLKCFG_FCS 0x2
#define CCLKCFG_HALFTURBO 0x4
#define CCLKCFG_FASTBUS 0x8
#define MDREFR_DB2_MASK (MDREFR_K2DB2 | MDREFR_K1DB2)
#define MDREFR_DRI_MASK 0xFFF
#define MDCNFG_DRAC2(mdcnfg) (((mdcnfg) >> 21) & 0x3)
#define MDCNFG_DRAC0(mdcnfg) (((mdcnfg) >> 5) & 0x3)
/* /*
* PXA255 definitions * PXA255 definitions
*/ */
/* Use the run mode frequencies for the CPUFREQ_POLICY_PERFORMANCE policy */
#define CCLKCFG CCLKCFG_TURBO | CCLKCFG_FCS
static const struct pxa_freqs pxa255_run_freqs[] = static const struct pxa_freqs pxa255_run_freqs[] =
{ {
/* CPU MEMBUS CCCR DIV2 CCLKCFG run turbo PXbus SDRAM */ /* CPU MEMBUS run turbo PXbus SDRAM */
{ 99500, 99500, 0x121, 1, CCLKCFG, -1, -1}, /* 99, 99, 50, 50 */ { 99500, -1, -1}, /* 99, 99, 50, 50 */
{132700, 132700, 0x123, 1, CCLKCFG, -1, -1}, /* 133, 133, 66, 66 */ {132700, -1, -1}, /* 133, 133, 66, 66 */
{199100, 99500, 0x141, 0, CCLKCFG, -1, -1}, /* 199, 199, 99, 99 */ {199100, -1, -1}, /* 199, 199, 99, 99 */
{265400, 132700, 0x143, 1, CCLKCFG, -1, -1}, /* 265, 265, 133, 66 */ {265400, -1, -1}, /* 265, 265, 133, 66 */
{331800, 165900, 0x145, 1, CCLKCFG, -1, -1}, /* 331, 331, 166, 83 */ {331800, -1, -1}, /* 331, 331, 166, 83 */
{398100, 99500, 0x161, 0, CCLKCFG, -1, -1}, /* 398, 398, 196, 99 */ {398100, -1, -1}, /* 398, 398, 196, 99 */
}; };
/* Use the turbo mode frequencies for the CPUFREQ_POLICY_POWERSAVE policy */ /* Use the turbo mode frequencies for the CPUFREQ_POLICY_POWERSAVE policy */
static const struct pxa_freqs pxa255_turbo_freqs[] = static const struct pxa_freqs pxa255_turbo_freqs[] =
{ {
/* CPU MEMBUS CCCR DIV2 CCLKCFG run turbo PXbus SDRAM */ /* CPU run turbo PXbus SDRAM */
{ 99500, 99500, 0x121, 1, CCLKCFG, -1, -1}, /* 99, 99, 50, 50 */ { 99500, -1, -1}, /* 99, 99, 50, 50 */
{199100, 99500, 0x221, 0, CCLKCFG, -1, -1}, /* 99, 199, 50, 99 */ {199100, -1, -1}, /* 99, 199, 50, 99 */
{298500, 99500, 0x321, 0, CCLKCFG, -1, -1}, /* 99, 287, 50, 99 */ {298500, -1, -1}, /* 99, 287, 50, 99 */
{298600, 99500, 0x1c1, 0, CCLKCFG, -1, -1}, /* 199, 287, 99, 99 */ {298600, -1, -1}, /* 199, 287, 99, 99 */
{398100, 99500, 0x241, 0, CCLKCFG, -1, -1}, /* 199, 398, 99, 99 */ {398100, -1, -1}, /* 199, 398, 99, 99 */
}; };
#define NUM_PXA25x_RUN_FREQS ARRAY_SIZE(pxa255_run_freqs) #define NUM_PXA25x_RUN_FREQS ARRAY_SIZE(pxa255_run_freqs)
@ -122,47 +106,14 @@ static unsigned int pxa255_turbo_table;
module_param(pxa255_turbo_table, uint, 0); module_param(pxa255_turbo_table, uint, 0);
MODULE_PARM_DESC(pxa255_turbo_table, "Selects the frequency table (0 = run table, !0 = turbo table)"); MODULE_PARM_DESC(pxa255_turbo_table, "Selects the frequency table (0 = run table, !0 = turbo table)");
/*
* PXA270 definitions
*
* For the PXA27x:
* Control variables are A, L, 2N for CCCR; B, HT, T for CLKCFG.
*
* A = 0 => memory controller clock from table 3-7,
* A = 1 => memory controller clock = system bus clock
* Run mode frequency = 13 MHz * L
* Turbo mode frequency = 13 MHz * L * N
* System bus frequency = 13 MHz * L / (B + 1)
*
* In CCCR:
* A = 1
* L = 16 oscillator to run mode ratio
* 2N = 6 2 * (turbo mode to run mode ratio)
*
* In CCLKCFG:
* B = 1 Fast bus mode
* HT = 0 Half-Turbo mode
* T = 1 Turbo mode
*
* For now, just support some of the combinations in table 3-7 of
* PXA27x Processor Family Developer's Manual to simplify frequency
* change sequences.
*/
#define PXA27x_CCCR(A, L, N2) (A << 25 | N2 << 7 | L)
#define CCLKCFG2(B, HT, T) \
(CCLKCFG_FCS | \
((B) ? CCLKCFG_FASTBUS : 0) | \
((HT) ? CCLKCFG_HALFTURBO : 0) | \
((T) ? CCLKCFG_TURBO : 0))
static struct pxa_freqs pxa27x_freqs[] = { static struct pxa_freqs pxa27x_freqs[] = {
{104000, 104000, PXA27x_CCCR(1, 8, 2), 0, CCLKCFG2(1, 0, 1), 900000, 1705000 }, {104000, 900000, 1705000 },
{156000, 104000, PXA27x_CCCR(1, 8, 3), 0, CCLKCFG2(1, 0, 1), 1000000, 1705000 }, {156000, 1000000, 1705000 },
{208000, 208000, PXA27x_CCCR(0, 16, 2), 1, CCLKCFG2(0, 0, 1), 1180000, 1705000 }, {208000, 1180000, 1705000 },
{312000, 208000, PXA27x_CCCR(1, 16, 3), 1, CCLKCFG2(1, 0, 1), 1250000, 1705000 }, {312000, 1250000, 1705000 },
{416000, 208000, PXA27x_CCCR(1, 16, 4), 1, CCLKCFG2(1, 0, 1), 1350000, 1705000 }, {416000, 1350000, 1705000 },
{520000, 208000, PXA27x_CCCR(1, 16, 5), 1, CCLKCFG2(1, 0, 1), 1450000, 1705000 }, {520000, 1450000, 1705000 },
{624000, 208000, PXA27x_CCCR(1, 16, 6), 1, CCLKCFG2(1, 0, 1), 1550000, 1705000 } {624000, 1550000, 1705000 }
}; };
#define NUM_PXA27x_FREQS ARRAY_SIZE(pxa27x_freqs) #define NUM_PXA27x_FREQS ARRAY_SIZE(pxa27x_freqs)
@ -241,51 +192,29 @@ static void pxa27x_guess_max_freq(void)
} }
} }
static void init_sdram_rows(void)
{
uint32_t mdcnfg = __raw_readl(MDCNFG);
unsigned int drac2 = 0, drac0 = 0;
if (mdcnfg & (MDCNFG_DE2 | MDCNFG_DE3))
drac2 = MDCNFG_DRAC2(mdcnfg);
if (mdcnfg & (MDCNFG_DE0 | MDCNFG_DE1))
drac0 = MDCNFG_DRAC0(mdcnfg);
sdram_rows = 1 << (11 + max(drac0, drac2));
}
static u32 mdrefr_dri(unsigned int freq)
{
u32 interval = freq * SDRAM_TREF / sdram_rows;
return (interval - (cpu_is_pxa27x() ? 31 : 0)) / 32;
}
static unsigned int pxa_cpufreq_get(unsigned int cpu) static unsigned int pxa_cpufreq_get(unsigned int cpu)
{ {
return get_clk_frequency_khz(0); struct pxa_cpufreq_data *data = cpufreq_get_driver_data();
return (unsigned int) clk_get_rate(data->clk_core) / 1000;
} }
static int pxa_set_target(struct cpufreq_policy *policy, unsigned int idx) static int pxa_set_target(struct cpufreq_policy *policy, unsigned int idx)
{ {
struct cpufreq_frequency_table *pxa_freqs_table; struct cpufreq_frequency_table *pxa_freqs_table;
const struct pxa_freqs *pxa_freq_settings; const struct pxa_freqs *pxa_freq_settings;
unsigned long flags; struct pxa_cpufreq_data *data = cpufreq_get_driver_data();
unsigned int new_freq_cpu, new_freq_mem; unsigned int new_freq_cpu;
unsigned int unused, preset_mdrefr, postset_mdrefr, cclkcfg;
int ret = 0; int ret = 0;
/* Get the current policy */ /* Get the current policy */
find_freq_tables(&pxa_freqs_table, &pxa_freq_settings); find_freq_tables(&pxa_freqs_table, &pxa_freq_settings);
new_freq_cpu = pxa_freq_settings[idx].khz; new_freq_cpu = pxa_freq_settings[idx].khz;
new_freq_mem = pxa_freq_settings[idx].membus;
if (freq_debug) if (freq_debug)
pr_debug("Changing CPU frequency to %d Mhz, (SDRAM %d Mhz)\n", pr_debug("Changing CPU frequency from %d Mhz to %d Mhz\n",
new_freq_cpu / 1000, (pxa_freq_settings[idx].div2) ? policy->cur / 1000, new_freq_cpu / 1000);
(new_freq_mem / 2000) : (new_freq_mem / 1000));
if (vcc_core && new_freq_cpu > policy->cur) { if (vcc_core && new_freq_cpu > policy->cur) {
ret = pxa_cpufreq_change_voltage(&pxa_freq_settings[idx]); ret = pxa_cpufreq_change_voltage(&pxa_freq_settings[idx]);
@ -293,53 +222,7 @@ static int pxa_set_target(struct cpufreq_policy *policy, unsigned int idx)
return ret; return ret;
} }
/* Calculate the next MDREFR. If we're slowing down the SDRAM clock clk_set_rate(data->clk_core, new_freq_cpu * 1000);
* we need to preset the smaller DRI before the change. If we're
* speeding up we need to set the larger DRI value after the change.
*/
preset_mdrefr = postset_mdrefr = __raw_readl(MDREFR);
if ((preset_mdrefr & MDREFR_DRI_MASK) > mdrefr_dri(new_freq_mem)) {
preset_mdrefr = (preset_mdrefr & ~MDREFR_DRI_MASK);
preset_mdrefr |= mdrefr_dri(new_freq_mem);
}
postset_mdrefr =
(postset_mdrefr & ~MDREFR_DRI_MASK) | mdrefr_dri(new_freq_mem);
/* If we're dividing the memory clock by two for the SDRAM clock, this
* must be set prior to the change. Clearing the divide must be done
* after the change.
*/
if (pxa_freq_settings[idx].div2) {
preset_mdrefr |= MDREFR_DB2_MASK;
postset_mdrefr |= MDREFR_DB2_MASK;
} else {
postset_mdrefr &= ~MDREFR_DB2_MASK;
}
local_irq_save(flags);
/* Set new the CCCR and prepare CCLKCFG */
writel(pxa_freq_settings[idx].cccr, CCCR);
cclkcfg = pxa_freq_settings[idx].cclkcfg;
asm volatile(" \n\
ldr r4, [%1] /* load MDREFR */ \n\
b 2f \n\
.align 5 \n\
1: \n\
str %3, [%1] /* preset the MDREFR */ \n\
mcr p14, 0, %2, c6, c0, 0 /* set CCLKCFG[FCS] */ \n\
str %4, [%1] /* postset the MDREFR */ \n\
\n\
b 3f \n\
2: b 1b \n\
3: nop \n\
"
: "=&r" (unused)
: "r" (MDREFR), "r" (cclkcfg),
"r" (preset_mdrefr), "r" (postset_mdrefr)
: "r4", "r5");
local_irq_restore(flags);
/* /*
* Even if voltage setting fails, we don't report it, as the frequency * Even if voltage setting fails, we don't report it, as the frequency
@ -369,8 +252,6 @@ static int pxa_cpufreq_init(struct cpufreq_policy *policy)
pxa_cpufreq_init_voltages(); pxa_cpufreq_init_voltages();
init_sdram_rows();
/* set default policy and cpuinfo */ /* set default policy and cpuinfo */
policy->cpuinfo.transition_latency = 1000; /* FIXME: 1 ms, assumed */ policy->cpuinfo.transition_latency = 1000; /* FIXME: 1 ms, assumed */
@ -429,11 +310,17 @@ static struct cpufreq_driver pxa_cpufreq_driver = {
.init = pxa_cpufreq_init, .init = pxa_cpufreq_init,
.get = pxa_cpufreq_get, .get = pxa_cpufreq_get,
.name = "PXA2xx", .name = "PXA2xx",
.driver_data = &pxa_cpufreq_data,
}; };
static int __init pxa_cpu_init(void) static int __init pxa_cpu_init(void)
{ {
int ret = -ENODEV; int ret = -ENODEV;
pxa_cpufreq_data.clk_core = clk_get_sys(NULL, "core");
if (IS_ERR(pxa_cpufreq_data.clk_core))
return PTR_ERR(pxa_cpufreq_data.clk_core);
if (cpu_is_pxa25x() || cpu_is_pxa27x()) if (cpu_is_pxa25x() || cpu_is_pxa27x())
ret = cpufreq_register_driver(&pxa_cpufreq_driver); ret = cpufreq_register_driver(&pxa_cpufreq_driver);
return ret; return ret;

View File

@ -53,7 +53,7 @@ static int scpi_init_opp_table(const struct cpumask *cpumask)
return ret; return ret;
} }
static struct cpufreq_arm_bL_ops scpi_cpufreq_ops = { static const struct cpufreq_arm_bL_ops scpi_cpufreq_ops = {
.name = "scpi", .name = "scpi",
.get_transition_latency = scpi_get_transition_latency, .get_transition_latency = scpi_get_transition_latency,
.init_opp_table = scpi_init_opp_table, .init_opp_table = scpi_init_opp_table,

View File

@ -177,7 +177,7 @@ static int spear_cpufreq_probe(struct platform_device *pdev)
np = of_cpu_device_node_get(0); np = of_cpu_device_node_get(0);
if (!np) { if (!np) {
pr_err("No cpu node found"); pr_err("No cpu node found\n");
return -ENODEV; return -ENODEV;
} }
@ -187,7 +187,7 @@ static int spear_cpufreq_probe(struct platform_device *pdev)
prop = of_find_property(np, "cpufreq_tbl", NULL); prop = of_find_property(np, "cpufreq_tbl", NULL);
if (!prop || !prop->value) { if (!prop || !prop->value) {
pr_err("Invalid cpufreq_tbl"); pr_err("Invalid cpufreq_tbl\n");
ret = -ENODEV; ret = -ENODEV;
goto out_put_node; goto out_put_node;
} }

View File

@ -367,7 +367,7 @@ unsigned int speedstep_detect_processor(void)
} else } else
return SPEEDSTEP_CPU_PIII_C; return SPEEDSTEP_CPU_PIII_C;
} }
/* fall through */
default: default:
return 0; return 0;
} }

View File

@ -205,6 +205,7 @@ static int ti_cpufreq_init(void)
np = of_find_node_by_path("/"); np = of_find_node_by_path("/");
match = of_match_node(ti_cpufreq_of_match, np); match = of_match_node(ti_cpufreq_of_match, np);
of_node_put(np);
if (!match) if (!match)
return -ENODEV; return -ENODEV;
@ -217,7 +218,8 @@ static int ti_cpufreq_init(void)
opp_data->cpu_dev = get_cpu_device(0); opp_data->cpu_dev = get_cpu_device(0);
if (!opp_data->cpu_dev) { if (!opp_data->cpu_dev) {
pr_err("%s: Failed to get device for CPU0\n", __func__); pr_err("%s: Failed to get device for CPU0\n", __func__);
return -ENODEV; ret = ENODEV;
goto free_opp_data;
} }
opp_data->opp_node = dev_pm_opp_of_get_opp_desc_node(opp_data->cpu_dev); opp_data->opp_node = dev_pm_opp_of_get_opp_desc_node(opp_data->cpu_dev);
@ -262,6 +264,8 @@ register_cpufreq_dt:
fail_put_node: fail_put_node:
of_node_put(opp_data->opp_node); of_node_put(opp_data->opp_node);
free_opp_data:
kfree(opp_data);
return ret; return ret;
} }

View File

@ -42,7 +42,7 @@ static int ve_spc_get_transition_latency(struct device *cpu_dev)
return 1000000; /* 1 ms */ return 1000000; /* 1 ms */
} }
static struct cpufreq_arm_bL_ops ve_spc_cpufreq_ops = { static const struct cpufreq_arm_bL_ops ve_spc_cpufreq_ops = {
.name = "vexpress-spc", .name = "vexpress-spc",
.get_transition_latency = ve_spc_get_transition_latency, .get_transition_latency = ve_spc_get_transition_latency,
.init_opp_table = ve_spc_init_opp_table, .init_opp_table = ve_spc_init_opp_table,

View File

@ -72,12 +72,94 @@ static const struct of_device_id arm_idle_state_match[] __initconst = {
}; };
/* /*
* arm_idle_init * arm_idle_init_cpu
* *
* Registers the arm specific cpuidle driver with the cpuidle * Registers the arm specific cpuidle driver with the cpuidle
* framework. It relies on core code to parse the idle states * framework. It relies on core code to parse the idle states
* and initialize them using driver data structures accordingly. * and initialize them using driver data structures accordingly.
*/ */
static int __init arm_idle_init_cpu(int cpu)
{
int ret;
struct cpuidle_driver *drv;
struct cpuidle_device *dev;
drv = kmemdup(&arm_idle_driver, sizeof(*drv), GFP_KERNEL);
if (!drv)
return -ENOMEM;
drv->cpumask = (struct cpumask *)cpumask_of(cpu);
/*
* Initialize idle states data, starting at index 1. This
* driver is DT only, if no DT idle states are detected (ret
* == 0) let the driver initialization fail accordingly since
* there is no reason to initialize the idle driver if only
* wfi is supported.
*/
ret = dt_init_idle_driver(drv, arm_idle_state_match, 1);
if (ret <= 0) {
ret = ret ? : -ENODEV;
goto out_kfree_drv;
}
ret = cpuidle_register_driver(drv);
if (ret) {
pr_err("Failed to register cpuidle driver\n");
goto out_kfree_drv;
}
/*
* Call arch CPU operations in order to initialize
* idle states suspend back-end specific data
*/
ret = arm_cpuidle_init(cpu);
/*
* Skip the cpuidle device initialization if the reported
* failure is a HW misconfiguration/breakage (-ENXIO).
*/
if (ret == -ENXIO)
return 0;
if (ret) {
pr_err("CPU %d failed to init idle CPU ops\n", cpu);
goto out_unregister_drv;
}
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
pr_err("Failed to allocate cpuidle device\n");
ret = -ENOMEM;
goto out_unregister_drv;
}
dev->cpu = cpu;
ret = cpuidle_register_device(dev);
if (ret) {
pr_err("Failed to register cpuidle device for CPU %d\n",
cpu);
goto out_kfree_dev;
}
return 0;
out_kfree_dev:
kfree(dev);
out_unregister_drv:
cpuidle_unregister_driver(drv);
out_kfree_drv:
kfree(drv);
return ret;
}
/*
* arm_idle_init - Initializes arm cpuidle driver
*
* Initializes arm cpuidle driver for all CPUs, if any CPU fails
* to register cpuidle driver then rollback to cancel all CPUs
* registeration.
*/
static int __init arm_idle_init(void) static int __init arm_idle_init(void)
{ {
int cpu, ret; int cpu, ret;
@ -85,79 +167,20 @@ static int __init arm_idle_init(void)
struct cpuidle_device *dev; struct cpuidle_device *dev;
for_each_possible_cpu(cpu) { for_each_possible_cpu(cpu) {
ret = arm_idle_init_cpu(cpu);
drv = kmemdup(&arm_idle_driver, sizeof(*drv), GFP_KERNEL); if (ret)
if (!drv) {
ret = -ENOMEM;
goto out_fail; goto out_fail;
}
drv->cpumask = (struct cpumask *)cpumask_of(cpu);
/*
* Initialize idle states data, starting at index 1. This
* driver is DT only, if no DT idle states are detected (ret
* == 0) let the driver initialization fail accordingly since
* there is no reason to initialize the idle driver if only
* wfi is supported.
*/
ret = dt_init_idle_driver(drv, arm_idle_state_match, 1);
if (ret <= 0) {
ret = ret ? : -ENODEV;
goto init_fail;
}
ret = cpuidle_register_driver(drv);
if (ret) {
pr_err("Failed to register cpuidle driver\n");
goto init_fail;
}
/*
* Call arch CPU operations in order to initialize
* idle states suspend back-end specific data
*/
ret = arm_cpuidle_init(cpu);
/*
* Skip the cpuidle device initialization if the reported
* failure is a HW misconfiguration/breakage (-ENXIO).
*/
if (ret == -ENXIO)
continue;
if (ret) {
pr_err("CPU %d failed to init idle CPU ops\n", cpu);
goto out_fail;
}
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
pr_err("Failed to allocate cpuidle device\n");
ret = -ENOMEM;
goto out_fail;
}
dev->cpu = cpu;
ret = cpuidle_register_device(dev);
if (ret) {
pr_err("Failed to register cpuidle device for CPU %d\n",
cpu);
kfree(dev);
goto out_fail;
}
} }
return 0; return 0;
init_fail:
kfree(drv);
out_fail: out_fail:
while (--cpu >= 0) { while (--cpu >= 0) {
dev = per_cpu(cpuidle_devices, cpu); dev = per_cpu(cpuidle_devices, cpu);
drv = cpuidle_get_cpu_driver(dev);
cpuidle_unregister_device(dev); cpuidle_unregister_device(dev);
kfree(dev);
drv = cpuidle_get_driver();
cpuidle_unregister_driver(drv); cpuidle_unregister_driver(drv);
kfree(dev);
kfree(drv); kfree(drv);
} }

View File

@ -208,6 +208,7 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
return -EBUSY; return -EBUSY;
} }
target_state = &drv->states[index]; target_state = &drv->states[index];
broadcast = false;
} }
/* Take note of the planned idle state. */ /* Take note of the planned idle state. */
@ -387,9 +388,12 @@ int cpuidle_enable_device(struct cpuidle_device *dev)
if (dev->enabled) if (dev->enabled)
return 0; return 0;
if (!cpuidle_curr_governor)
return -EIO;
drv = cpuidle_get_cpu_driver(dev); drv = cpuidle_get_cpu_driver(dev);
if (!drv || !cpuidle_curr_governor) if (!drv)
return -EIO; return -EIO;
if (!dev->registered) if (!dev->registered)
@ -399,9 +403,11 @@ int cpuidle_enable_device(struct cpuidle_device *dev)
if (ret) if (ret)
return ret; return ret;
if (cpuidle_curr_governor->enable && if (cpuidle_curr_governor->enable) {
(ret = cpuidle_curr_governor->enable(drv, dev))) ret = cpuidle_curr_governor->enable(drv, dev);
goto fail_sysfs; if (ret)
goto fail_sysfs;
}
smp_wmb(); smp_wmb();

View File

@ -17,6 +17,7 @@
#include <linux/pm_qos.h> #include <linux/pm_qos.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/tick.h> #include <linux/tick.h>
#include <linux/cpu.h>
#include <asm/io.h> #include <asm/io.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
@ -67,10 +68,16 @@ static int ladder_select_state(struct cpuidle_driver *drv,
struct cpuidle_device *dev) struct cpuidle_device *dev)
{ {
struct ladder_device *ldev = this_cpu_ptr(&ladder_devices); struct ladder_device *ldev = this_cpu_ptr(&ladder_devices);
struct device *device = get_cpu_device(dev->cpu);
struct ladder_device_state *last_state; struct ladder_device_state *last_state;
int last_residency, last_idx = ldev->last_state_idx; int last_residency, last_idx = ldev->last_state_idx;
int first_idx = drv->states[0].flags & CPUIDLE_FLAG_POLLING ? 1 : 0; int first_idx = drv->states[0].flags & CPUIDLE_FLAG_POLLING ? 1 : 0;
int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY); int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
int resume_latency = dev_pm_qos_raw_read_value(device);
if (resume_latency < latency_req &&
resume_latency != PM_QOS_RESUME_LATENCY_NO_CONSTRAINT)
latency_req = resume_latency;
/* Special case when user has set very strict latency requirement */ /* Special case when user has set very strict latency requirement */
if (unlikely(latency_req == 0)) { if (unlikely(latency_req == 0)) {

View File

@ -298,8 +298,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
data->needs_update = 0; data->needs_update = 0;
} }
/* resume_latency is 0 means no restriction */ if (resume_latency < latency_req &&
if (resume_latency && resume_latency < latency_req) resume_latency != PM_QOS_RESUME_LATENCY_NO_CONSTRAINT)
latency_req = resume_latency; latency_req = resume_latency;
/* Special case when user has set very strict latency requirement */ /* Special case when user has set very strict latency requirement */

View File

@ -28,6 +28,9 @@
#include <linux/of.h> #include <linux/of.h>
#include "governor.h" #include "governor.h"
#define MAX(a,b) ((a > b) ? a : b)
#define MIN(a,b) ((a < b) ? a : b)
static struct class *devfreq_class; static struct class *devfreq_class;
/* /*
@ -69,6 +72,34 @@ static struct devfreq *find_device_devfreq(struct device *dev)
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);
} }
static unsigned long find_available_min_freq(struct devfreq *devfreq)
{
struct dev_pm_opp *opp;
unsigned long min_freq = 0;
opp = dev_pm_opp_find_freq_ceil(devfreq->dev.parent, &min_freq);
if (IS_ERR(opp))
min_freq = 0;
else
dev_pm_opp_put(opp);
return min_freq;
}
static unsigned long find_available_max_freq(struct devfreq *devfreq)
{
struct dev_pm_opp *opp;
unsigned long max_freq = ULONG_MAX;
opp = dev_pm_opp_find_freq_floor(devfreq->dev.parent, &max_freq);
if (IS_ERR(opp))
max_freq = 0;
else
dev_pm_opp_put(opp);
return max_freq;
}
/** /**
* devfreq_get_freq_level() - Lookup freq_table for the frequency * devfreq_get_freq_level() - Lookup freq_table for the frequency
* @devfreq: the devfreq instance * @devfreq: the devfreq instance
@ -85,11 +116,7 @@ static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq)
return -EINVAL; return -EINVAL;
} }
/** static int set_freq_table(struct devfreq *devfreq)
* devfreq_set_freq_table() - Initialize freq_table for the frequency
* @devfreq: the devfreq instance
*/
static void devfreq_set_freq_table(struct devfreq *devfreq)
{ {
struct devfreq_dev_profile *profile = devfreq->profile; struct devfreq_dev_profile *profile = devfreq->profile;
struct dev_pm_opp *opp; struct dev_pm_opp *opp;
@ -99,7 +126,7 @@ static void devfreq_set_freq_table(struct devfreq *devfreq)
/* Initialize the freq_table from OPP table */ /* Initialize the freq_table from OPP table */
count = dev_pm_opp_get_opp_count(devfreq->dev.parent); count = dev_pm_opp_get_opp_count(devfreq->dev.parent);
if (count <= 0) if (count <= 0)
return; return -EINVAL;
profile->max_state = count; profile->max_state = count;
profile->freq_table = devm_kcalloc(devfreq->dev.parent, profile->freq_table = devm_kcalloc(devfreq->dev.parent,
@ -108,7 +135,7 @@ static void devfreq_set_freq_table(struct devfreq *devfreq)
GFP_KERNEL); GFP_KERNEL);
if (!profile->freq_table) { if (!profile->freq_table) {
profile->max_state = 0; profile->max_state = 0;
return; return -ENOMEM;
} }
for (i = 0, freq = 0; i < profile->max_state; i++, freq++) { for (i = 0, freq = 0; i < profile->max_state; i++, freq++) {
@ -116,11 +143,13 @@ static void devfreq_set_freq_table(struct devfreq *devfreq)
if (IS_ERR(opp)) { if (IS_ERR(opp)) {
devm_kfree(devfreq->dev.parent, profile->freq_table); devm_kfree(devfreq->dev.parent, profile->freq_table);
profile->max_state = 0; profile->max_state = 0;
return; return PTR_ERR(opp);
} }
dev_pm_opp_put(opp); dev_pm_opp_put(opp);
profile->freq_table[i] = freq; profile->freq_table[i] = freq;
} }
return 0;
} }
/** /**
@ -227,7 +256,7 @@ static int devfreq_notify_transition(struct devfreq *devfreq,
int update_devfreq(struct devfreq *devfreq) int update_devfreq(struct devfreq *devfreq)
{ {
struct devfreq_freqs freqs; struct devfreq_freqs freqs;
unsigned long freq, cur_freq; unsigned long freq, cur_freq, min_freq, max_freq;
int err = 0; int err = 0;
u32 flags = 0; u32 flags = 0;
@ -245,19 +274,21 @@ int update_devfreq(struct devfreq *devfreq)
return err; return err;
/* /*
* Adjust the frequency with user freq and QoS. * Adjust the frequency with user freq, QoS and available freq.
* *
* List from the highest priority * List from the highest priority
* max_freq * max_freq
* min_freq * min_freq
*/ */
max_freq = MIN(devfreq->scaling_max_freq, devfreq->max_freq);
min_freq = MAX(devfreq->scaling_min_freq, devfreq->min_freq);
if (devfreq->min_freq && freq < devfreq->min_freq) { if (min_freq && freq < min_freq) {
freq = devfreq->min_freq; freq = min_freq;
flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use GLB */ flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use GLB */
} }
if (devfreq->max_freq && freq > devfreq->max_freq) { if (max_freq && freq > max_freq) {
freq = devfreq->max_freq; freq = max_freq;
flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */ flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */
} }
@ -280,10 +311,9 @@ int update_devfreq(struct devfreq *devfreq)
freqs.new = freq; freqs.new = freq;
devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE); devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE);
if (devfreq->profile->freq_table) if (devfreq_update_status(devfreq, freq))
if (devfreq_update_status(devfreq, freq)) dev_err(&devfreq->dev,
dev_err(&devfreq->dev, "Couldn't update frequency transition information.\n");
"Couldn't update frequency transition information.\n");
devfreq->previous_freq = freq; devfreq->previous_freq = freq;
return err; return err;
@ -466,6 +496,19 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type,
int ret; int ret;
mutex_lock(&devfreq->lock); mutex_lock(&devfreq->lock);
devfreq->scaling_min_freq = find_available_min_freq(devfreq);
if (!devfreq->scaling_min_freq) {
mutex_unlock(&devfreq->lock);
return -EINVAL;
}
devfreq->scaling_max_freq = find_available_max_freq(devfreq);
if (!devfreq->scaling_max_freq) {
mutex_unlock(&devfreq->lock);
return -EINVAL;
}
ret = update_devfreq(devfreq); ret = update_devfreq(devfreq);
mutex_unlock(&devfreq->lock); mutex_unlock(&devfreq->lock);
@ -555,10 +598,28 @@ struct devfreq *devfreq_add_device(struct device *dev,
if (!devfreq->profile->max_state && !devfreq->profile->freq_table) { if (!devfreq->profile->max_state && !devfreq->profile->freq_table) {
mutex_unlock(&devfreq->lock); mutex_unlock(&devfreq->lock);
devfreq_set_freq_table(devfreq); err = set_freq_table(devfreq);
if (err < 0)
goto err_out;
mutex_lock(&devfreq->lock); mutex_lock(&devfreq->lock);
} }
devfreq->min_freq = find_available_min_freq(devfreq);
if (!devfreq->min_freq) {
mutex_unlock(&devfreq->lock);
err = -EINVAL;
goto err_dev;
}
devfreq->scaling_min_freq = devfreq->min_freq;
devfreq->max_freq = find_available_max_freq(devfreq);
if (!devfreq->max_freq) {
mutex_unlock(&devfreq->lock);
err = -EINVAL;
goto err_dev;
}
devfreq->scaling_max_freq = devfreq->max_freq;
dev_set_name(&devfreq->dev, "devfreq%d", dev_set_name(&devfreq->dev, "devfreq%d",
atomic_inc_return(&devfreq_no)); atomic_inc_return(&devfreq_no));
err = device_register(&devfreq->dev); err = device_register(&devfreq->dev);
@ -1082,6 +1143,14 @@ unlock:
return ret; return ret;
} }
static ssize_t min_freq_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct devfreq *df = to_devfreq(dev);
return sprintf(buf, "%lu\n", MAX(df->scaling_min_freq, df->min_freq));
}
static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr, static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
@ -1108,17 +1177,15 @@ unlock:
mutex_unlock(&df->lock); mutex_unlock(&df->lock);
return ret; return ret;
} }
#define show_one(name) \
static ssize_t name##_show \
(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
return sprintf(buf, "%lu\n", to_devfreq(dev)->name); \
}
show_one(min_freq);
show_one(max_freq);
static DEVICE_ATTR_RW(min_freq); static DEVICE_ATTR_RW(min_freq);
static ssize_t max_freq_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct devfreq *df = to_devfreq(dev);
return sprintf(buf, "%lu\n", MIN(df->scaling_max_freq, df->max_freq));
}
static DEVICE_ATTR_RW(max_freq); static DEVICE_ATTR_RW(max_freq);
static ssize_t available_frequencies_show(struct device *d, static ssize_t available_frequencies_show(struct device *d,
@ -1126,22 +1193,16 @@ static ssize_t available_frequencies_show(struct device *d,
char *buf) char *buf)
{ {
struct devfreq *df = to_devfreq(d); struct devfreq *df = to_devfreq(d);
struct device *dev = df->dev.parent;
struct dev_pm_opp *opp;
ssize_t count = 0; ssize_t count = 0;
unsigned long freq = 0; int i;
do { mutex_lock(&df->lock);
opp = dev_pm_opp_find_freq_ceil(dev, &freq);
if (IS_ERR(opp))
break;
dev_pm_opp_put(opp); for (i = 0; i < df->profile->max_state; i++)
count += scnprintf(&buf[count], (PAGE_SIZE - count - 2), count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
"%lu ", freq); "%lu ", df->profile->freq_table[i]);
freq++;
} while (1);
mutex_unlock(&df->lock);
/* Truncate the trailing space */ /* Truncate the trailing space */
if (count) if (count)
count--; count--;

View File

@ -436,7 +436,8 @@ static int exynos_bus_probe(struct platform_device *pdev)
ondemand_data->downdifferential = 5; ondemand_data->downdifferential = 5;
/* Add devfreq device to monitor and handle the exynos bus */ /* Add devfreq device to monitor and handle the exynos bus */
bus->devfreq = devm_devfreq_add_device(dev, profile, "simple_ondemand", bus->devfreq = devm_devfreq_add_device(dev, profile,
DEVFREQ_GOV_SIMPLE_ONDEMAND,
ondemand_data); ondemand_data);
if (IS_ERR(bus->devfreq)) { if (IS_ERR(bus->devfreq)) {
dev_err(dev, "failed to add devfreq device\n"); dev_err(dev, "failed to add devfreq device\n");
@ -488,7 +489,7 @@ passive:
passive_data->parent = parent_devfreq; passive_data->parent = parent_devfreq;
/* Add devfreq device for exynos bus with passive governor */ /* Add devfreq device for exynos bus with passive governor */
bus->devfreq = devm_devfreq_add_device(dev, profile, "passive", bus->devfreq = devm_devfreq_add_device(dev, profile, DEVFREQ_GOV_PASSIVE,
passive_data); passive_data);
if (IS_ERR(bus->devfreq)) { if (IS_ERR(bus->devfreq)) {
dev_err(dev, dev_err(dev,

View File

@ -183,7 +183,7 @@ static int devfreq_passive_event_handler(struct devfreq *devfreq,
} }
static struct devfreq_governor devfreq_passive = { static struct devfreq_governor devfreq_passive = {
.name = "passive", .name = DEVFREQ_GOV_PASSIVE,
.immutable = 1, .immutable = 1,
.get_target_freq = devfreq_passive_get_target_freq, .get_target_freq = devfreq_passive_get_target_freq,
.event_handler = devfreq_passive_event_handler, .event_handler = devfreq_passive_event_handler,

View File

@ -42,7 +42,7 @@ static int devfreq_performance_handler(struct devfreq *devfreq,
} }
static struct devfreq_governor devfreq_performance = { static struct devfreq_governor devfreq_performance = {
.name = "performance", .name = DEVFREQ_GOV_PERFORMANCE,
.get_target_freq = devfreq_performance_func, .get_target_freq = devfreq_performance_func,
.event_handler = devfreq_performance_handler, .event_handler = devfreq_performance_handler,
}; };

View File

@ -39,7 +39,7 @@ static int devfreq_powersave_handler(struct devfreq *devfreq,
} }
static struct devfreq_governor devfreq_powersave = { static struct devfreq_governor devfreq_powersave = {
.name = "powersave", .name = DEVFREQ_GOV_POWERSAVE,
.get_target_freq = devfreq_powersave_func, .get_target_freq = devfreq_powersave_func,
.event_handler = devfreq_powersave_handler, .event_handler = devfreq_powersave_handler,
}; };

View File

@ -125,7 +125,7 @@ static int devfreq_simple_ondemand_handler(struct devfreq *devfreq,
} }
static struct devfreq_governor devfreq_simple_ondemand = { static struct devfreq_governor devfreq_simple_ondemand = {
.name = "simple_ondemand", .name = DEVFREQ_GOV_SIMPLE_ONDEMAND,
.get_target_freq = devfreq_simple_ondemand_func, .get_target_freq = devfreq_simple_ondemand_func,
.event_handler = devfreq_simple_ondemand_handler, .event_handler = devfreq_simple_ondemand_handler,
}; };

View File

@ -87,7 +87,7 @@ static struct attribute *dev_entries[] = {
NULL, NULL,
}; };
static const struct attribute_group dev_attr_group = { static const struct attribute_group dev_attr_group = {
.name = "userspace", .name = DEVFREQ_GOV_USERSPACE,
.attrs = dev_entries, .attrs = dev_entries,
}; };

View File

@ -431,7 +431,7 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev)
data->devfreq = devm_devfreq_add_device(dev, data->devfreq = devm_devfreq_add_device(dev,
&rk3399_devfreq_dmc_profile, &rk3399_devfreq_dmc_profile,
"simple_ondemand", DEVFREQ_GOV_SIMPLE_ONDEMAND,
&data->ondemand_data); &data->ondemand_data);
if (IS_ERR(data->devfreq)) if (IS_ERR(data->devfreq))
return PTR_ERR(data->devfreq); return PTR_ERR(data->devfreq);

View File

@ -1304,7 +1304,7 @@ int i915_driver_load(struct pci_dev *pdev, const struct pci_device_id *ent)
* becaue the HDA driver may require us to enable the audio power * becaue the HDA driver may require us to enable the audio power
* domain during system suspend. * domain during system suspend.
*/ */
pdev->dev_flags |= PCI_DEV_FLAGS_NEEDS_RESUME; dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NEVER_SKIP);
ret = i915_driver_init_early(dev_priv, ent); ret = i915_driver_init_early(dev_priv, ent);
if (ret < 0) if (ret < 0)

View File

@ -913,10 +913,9 @@ static __cpuidle int intel_idle(struct cpuidle_device *dev,
struct cpuidle_state *state = &drv->states[index]; struct cpuidle_state *state = &drv->states[index];
unsigned long eax = flg2MWAIT(state->flags); unsigned long eax = flg2MWAIT(state->flags);
unsigned int cstate; unsigned int cstate;
bool uninitialized_var(tick);
int cpu = smp_processor_id(); int cpu = smp_processor_id();
cstate = (((eax) >> MWAIT_SUBSTATE_SIZE) & MWAIT_CSTATE_MASK) + 1;
/* /*
* leave_mm() to avoid costly and often unnecessary wakeups * leave_mm() to avoid costly and often unnecessary wakeups
* for flushing the user TLB's associated with the active mm. * for flushing the user TLB's associated with the active mm.
@ -924,12 +923,19 @@ static __cpuidle int intel_idle(struct cpuidle_device *dev,
if (state->flags & CPUIDLE_FLAG_TLB_FLUSHED) if (state->flags & CPUIDLE_FLAG_TLB_FLUSHED)
leave_mm(cpu); leave_mm(cpu);
if (!(lapic_timer_reliable_states & (1 << (cstate)))) if (!static_cpu_has(X86_FEATURE_ARAT)) {
tick_broadcast_enter(); cstate = (((eax) >> MWAIT_SUBSTATE_SIZE) &
MWAIT_CSTATE_MASK) + 1;
tick = false;
if (!(lapic_timer_reliable_states & (1 << (cstate)))) {
tick = true;
tick_broadcast_enter();
}
}
mwait_idle_with_hints(eax, ecx); mwait_idle_with_hints(eax, ecx);
if (!(lapic_timer_reliable_states & (1 << (cstate)))) if (!static_cpu_has(X86_FEATURE_ARAT) && tick)
tick_broadcast_exit(); tick_broadcast_exit();
return index; return index;
@ -1061,7 +1067,7 @@ static const struct idle_cpu idle_cpu_dnv = {
}; };
#define ICPU(model, cpu) \ #define ICPU(model, cpu) \
{ X86_VENDOR_INTEL, 6, model, X86_FEATURE_MWAIT, (unsigned long)&cpu } { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (unsigned long)&cpu }
static const struct x86_cpu_id intel_idle_ids[] __initconst = { static const struct x86_cpu_id intel_idle_ids[] __initconst = {
ICPU(INTEL_FAM6_NEHALEM_EP, idle_cpu_nehalem), ICPU(INTEL_FAM6_NEHALEM_EP, idle_cpu_nehalem),
@ -1125,6 +1131,11 @@ static int __init intel_idle_probe(void)
return -ENODEV; return -ENODEV;
} }
if (!boot_cpu_has(X86_FEATURE_MWAIT)) {
pr_debug("Please enable MWAIT in BIOS SETUP\n");
return -ENODEV;
}
if (boot_cpu_data.cpuid_level < CPUID_MWAIT_LEAF) if (boot_cpu_data.cpuid_level < CPUID_MWAIT_LEAF)
return -ENODEV; return -ENODEV;

View File

@ -225,7 +225,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
* MEI requires to resume from runtime suspend mode * MEI requires to resume from runtime suspend mode
* in order to perform link reset flow upon system suspend. * in order to perform link reset flow upon system suspend.
*/ */
pdev->dev_flags |= PCI_DEV_FLAGS_NEEDS_RESUME; dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NEVER_SKIP);
/* /*
* ME maps runtime suspend/resume to D0i states, * ME maps runtime suspend/resume to D0i states,

View File

@ -141,7 +141,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
* MEI requires to resume from runtime suspend mode * MEI requires to resume from runtime suspend mode
* in order to perform link reset flow upon system suspend. * in order to perform link reset flow upon system suspend.
*/ */
pdev->dev_flags |= PCI_DEV_FLAGS_NEEDS_RESUME; dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NEVER_SKIP);
/* /*
* TXE maps runtime suspend/resume to own power gating states, * TXE maps runtime suspend/resume to own power gating states,

13
drivers/opp/Kconfig Normal file
View File

@ -0,0 +1,13 @@
config PM_OPP
bool
select SRCU
---help---
SOCs have a standard set of tuples consisting of frequency and
voltage pairs that the device will support per voltage domain. This
is called Operating Performance Point or OPP. The actual definitions
of OPP varies over silicon within the same family of devices.
OPP layer organizes the data internally using device pointers
representing individual voltage domains and provides SOC
implementations a ready to use framework to manage OPPs.
For more information, read <file:Documentation/power/opp.txt>

View File

@ -19,6 +19,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/pm_domain.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include "opp.h" #include "opp.h"
@ -296,7 +297,7 @@ int dev_pm_opp_get_opp_count(struct device *dev)
opp_table = _find_opp_table(dev); opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table)) { if (IS_ERR(opp_table)) {
count = PTR_ERR(opp_table); count = PTR_ERR(opp_table);
dev_err(dev, "%s: OPP table not found (%d)\n", dev_dbg(dev, "%s: OPP table not found (%d)\n",
__func__, count); __func__, count);
return count; return count;
} }
@ -535,6 +536,44 @@ _generic_set_opp_clk_only(struct device *dev, struct clk *clk,
return ret; return ret;
} }
static inline int
_generic_set_opp_domain(struct device *dev, struct clk *clk,
unsigned long old_freq, unsigned long freq,
unsigned int old_pstate, unsigned int new_pstate)
{
int ret;
/* Scaling up? Scale domain performance state before frequency */
if (freq > old_freq) {
ret = dev_pm_genpd_set_performance_state(dev, new_pstate);
if (ret)
return ret;
}
ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq);
if (ret)
goto restore_domain_state;
/* Scaling down? Scale domain performance state after frequency */
if (freq < old_freq) {
ret = dev_pm_genpd_set_performance_state(dev, new_pstate);
if (ret)
goto restore_freq;
}
return 0;
restore_freq:
if (_generic_set_opp_clk_only(dev, clk, freq, old_freq))
dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n",
__func__, old_freq);
restore_domain_state:
if (freq > old_freq)
dev_pm_genpd_set_performance_state(dev, old_pstate);
return ret;
}
static int _generic_set_opp_regulator(const struct opp_table *opp_table, static int _generic_set_opp_regulator(const struct opp_table *opp_table,
struct device *dev, struct device *dev,
unsigned long old_freq, unsigned long old_freq,
@ -653,7 +692,16 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
/* Only frequency scaling */ /* Only frequency scaling */
if (!opp_table->regulators) { if (!opp_table->regulators) {
ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq); /*
* We don't support devices with both regulator and
* domain performance-state for now.
*/
if (opp_table->genpd_performance_state)
ret = _generic_set_opp_domain(dev, clk, old_freq, freq,
IS_ERR(old_opp) ? 0 : old_opp->pstate,
opp->pstate);
else
ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq);
} else if (!opp_table->set_opp) { } else if (!opp_table->set_opp) {
ret = _generic_set_opp_regulator(opp_table, dev, old_freq, freq, ret = _generic_set_opp_regulator(opp_table, dev, old_freq, freq,
IS_ERR(old_opp) ? NULL : old_opp->supplies, IS_ERR(old_opp) ? NULL : old_opp->supplies,
@ -988,6 +1036,9 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
return ret; return ret;
} }
if (opp_table->get_pstate)
new_opp->pstate = opp_table->get_pstate(dev, new_opp->rate);
list_add(&new_opp->node, head); list_add(&new_opp->node, head);
mutex_unlock(&opp_table->lock); mutex_unlock(&opp_table->lock);
@ -1476,13 +1527,13 @@ err:
EXPORT_SYMBOL_GPL(dev_pm_opp_register_set_opp_helper); EXPORT_SYMBOL_GPL(dev_pm_opp_register_set_opp_helper);
/** /**
* dev_pm_opp_register_put_opp_helper() - Releases resources blocked for * dev_pm_opp_unregister_set_opp_helper() - Releases resources blocked for
* set_opp helper * set_opp helper
* @opp_table: OPP table returned from dev_pm_opp_register_set_opp_helper(). * @opp_table: OPP table returned from dev_pm_opp_register_set_opp_helper().
* *
* Release resources blocked for platform specific set_opp helper. * Release resources blocked for platform specific set_opp helper.
*/ */
void dev_pm_opp_register_put_opp_helper(struct opp_table *opp_table) void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table)
{ {
if (!opp_table->set_opp) { if (!opp_table->set_opp) {
pr_err("%s: Doesn't have custom set_opp helper set\n", pr_err("%s: Doesn't have custom set_opp helper set\n",
@ -1497,7 +1548,82 @@ void dev_pm_opp_register_put_opp_helper(struct opp_table *opp_table)
dev_pm_opp_put_opp_table(opp_table); dev_pm_opp_put_opp_table(opp_table);
} }
EXPORT_SYMBOL_GPL(dev_pm_opp_register_put_opp_helper); EXPORT_SYMBOL_GPL(dev_pm_opp_unregister_set_opp_helper);
/**
* dev_pm_opp_register_get_pstate_helper() - Register get_pstate() helper.
* @dev: Device for which the helper is getting registered.
* @get_pstate: Helper.
*
* TODO: Remove this callback after the same information is available via Device
* Tree.
*
* This allows a platform to initialize the performance states of individual
* OPPs for its devices, until we get similar information directly from DT.
*
* This must be called before the OPPs are initialized for the device.
*/
struct opp_table *dev_pm_opp_register_get_pstate_helper(struct device *dev,
int (*get_pstate)(struct device *dev, unsigned long rate))
{
struct opp_table *opp_table;
int ret;
if (!get_pstate)
return ERR_PTR(-EINVAL);
opp_table = dev_pm_opp_get_opp_table(dev);
if (!opp_table)
return ERR_PTR(-ENOMEM);
/* This should be called before OPPs are initialized */
if (WARN_ON(!list_empty(&opp_table->opp_list))) {
ret = -EBUSY;
goto err;
}
/* Already have genpd_performance_state set */
if (WARN_ON(opp_table->genpd_performance_state)) {
ret = -EBUSY;
goto err;
}
opp_table->genpd_performance_state = true;
opp_table->get_pstate = get_pstate;
return opp_table;
err:
dev_pm_opp_put_opp_table(opp_table);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_register_get_pstate_helper);
/**
* dev_pm_opp_unregister_get_pstate_helper() - Releases resources blocked for
* get_pstate() helper
* @opp_table: OPP table returned from dev_pm_opp_register_get_pstate_helper().
*
* Release resources blocked for platform specific get_pstate() helper.
*/
void dev_pm_opp_unregister_get_pstate_helper(struct opp_table *opp_table)
{
if (!opp_table->genpd_performance_state) {
pr_err("%s: Doesn't have performance states set\n",
__func__);
return;
}
/* Make sure there are no concurrent readers while updating opp_table */
WARN_ON(!list_empty(&opp_table->opp_list));
opp_table->genpd_performance_state = false;
opp_table->get_pstate = NULL;
dev_pm_opp_put_opp_table(opp_table);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_unregister_get_pstate_helper);
/** /**
* dev_pm_opp_add() - Add an OPP table from a table definitions * dev_pm_opp_add() - Add an OPP table from a table definitions
@ -1706,6 +1832,13 @@ void _dev_pm_opp_remove_table(struct opp_table *opp_table, struct device *dev,
if (remove_all || !opp->dynamic) if (remove_all || !opp->dynamic)
dev_pm_opp_put(opp); dev_pm_opp_put(opp);
} }
/*
* The OPP table is getting removed, drop the performance state
* constraints.
*/
if (opp_table->genpd_performance_state)
dev_pm_genpd_set_performance_state(dev, 0);
} else { } else {
_remove_opp_dev(_find_opp_dev(dev, opp_table), opp_table); _remove_opp_dev(_find_opp_dev(dev, opp_table), opp_table);
} }

View File

@ -41,16 +41,15 @@ static bool opp_debug_create_supplies(struct dev_pm_opp *opp,
{ {
struct dentry *d; struct dentry *d;
int i; int i;
char *name;
for (i = 0; i < opp_table->regulator_count; i++) { for (i = 0; i < opp_table->regulator_count; i++) {
name = kasprintf(GFP_KERNEL, "supply-%d", i); char name[15];
snprintf(name, sizeof(name), "supply-%d", i);
/* Create per-opp directory */ /* Create per-opp directory */
d = debugfs_create_dir(name, pdentry); d = debugfs_create_dir(name, pdentry);
kfree(name);
if (!d) if (!d)
return false; return false;
@ -100,6 +99,9 @@ int opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table)
if (!debugfs_create_bool("suspend", S_IRUGO, d, &opp->suspend)) if (!debugfs_create_bool("suspend", S_IRUGO, d, &opp->suspend))
return -ENOMEM; return -ENOMEM;
if (!debugfs_create_u32("performance_state", S_IRUGO, d, &opp->pstate))
return -ENOMEM;
if (!debugfs_create_ulong("rate_hz", S_IRUGO, d, &opp->rate)) if (!debugfs_create_ulong("rate_hz", S_IRUGO, d, &opp->rate))
return -ENOMEM; return -ENOMEM;

View File

@ -16,7 +16,7 @@
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/of.h> #include <linux/of_device.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/export.h> #include <linux/export.h>
@ -397,6 +397,7 @@ static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np)
dev_err(dev, "%s: Failed to add OPP, %d\n", __func__, dev_err(dev, "%s: Failed to add OPP, %d\n", __func__,
ret); ret);
_dev_pm_opp_remove_table(opp_table, dev, false); _dev_pm_opp_remove_table(opp_table, dev, false);
of_node_put(np);
goto put_opp_table; goto put_opp_table;
} }
} }
@ -603,7 +604,7 @@ int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev,
if (cpu == cpu_dev->id) if (cpu == cpu_dev->id)
continue; continue;
cpu_np = of_get_cpu_node(cpu, NULL); cpu_np = of_cpu_device_node_get(cpu);
if (!cpu_np) { if (!cpu_np) {
dev_err(cpu_dev, "%s: failed to get cpu%d node\n", dev_err(cpu_dev, "%s: failed to get cpu%d node\n",
__func__, cpu); __func__, cpu);
@ -613,6 +614,7 @@ int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev,
/* Get OPP descriptor node */ /* Get OPP descriptor node */
tmp_np = _opp_of_get_opp_desc_node(cpu_np); tmp_np = _opp_of_get_opp_desc_node(cpu_np);
of_node_put(cpu_np);
if (!tmp_np) { if (!tmp_np) {
pr_err("%pOF: Couldn't find opp node\n", cpu_np); pr_err("%pOF: Couldn't find opp node\n", cpu_np);
ret = -ENOENT; ret = -ENOENT;

View File

@ -58,6 +58,7 @@ extern struct list_head opp_tables;
* @dynamic: not-created from static DT entries. * @dynamic: not-created from static DT entries.
* @turbo: true if turbo (boost) OPP * @turbo: true if turbo (boost) OPP
* @suspend: true if suspend OPP * @suspend: true if suspend OPP
* @pstate: Device's power domain's performance state.
* @rate: Frequency in hertz * @rate: Frequency in hertz
* @supplies: Power supplies voltage/current values * @supplies: Power supplies voltage/current values
* @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's * @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's
@ -76,6 +77,7 @@ struct dev_pm_opp {
bool dynamic; bool dynamic;
bool turbo; bool turbo;
bool suspend; bool suspend;
unsigned int pstate;
unsigned long rate; unsigned long rate;
struct dev_pm_opp_supply *supplies; struct dev_pm_opp_supply *supplies;
@ -135,8 +137,10 @@ enum opp_table_access {
* @clk: Device's clock handle * @clk: Device's clock handle
* @regulators: Supply regulators * @regulators: Supply regulators
* @regulator_count: Number of power supply regulators * @regulator_count: Number of power supply regulators
* @genpd_performance_state: Device's power domain support performance state.
* @set_opp: Platform specific set_opp callback * @set_opp: Platform specific set_opp callback
* @set_opp_data: Data to be passed to set_opp callback * @set_opp_data: Data to be passed to set_opp callback
* @get_pstate: Platform specific get_pstate callback
* @dentry: debugfs dentry pointer of the real device directory (not links). * @dentry: debugfs dentry pointer of the real device directory (not links).
* @dentry_name: Name of the real dentry. * @dentry_name: Name of the real dentry.
* *
@ -170,9 +174,11 @@ struct opp_table {
struct clk *clk; struct clk *clk;
struct regulator **regulators; struct regulator **regulators;
unsigned int regulator_count; unsigned int regulator_count;
bool genpd_performance_state;
int (*set_opp)(struct dev_pm_set_opp_data *data); int (*set_opp)(struct dev_pm_set_opp_data *data);
struct dev_pm_set_opp_data *set_opp_data; struct dev_pm_set_opp_data *set_opp_data;
int (*get_pstate)(struct device *dev, unsigned long rate);
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
struct dentry *dentry; struct dentry *dentry;

View File

@ -680,17 +680,13 @@ static int pci_pm_prepare(struct device *dev)
{ {
struct device_driver *drv = dev->driver; struct device_driver *drv = dev->driver;
/*
* Devices having power.ignore_children set may still be necessary for
* suspending their children in the next phase of device suspend.
*/
if (dev->power.ignore_children)
pm_runtime_resume(dev);
if (drv && drv->pm && drv->pm->prepare) { if (drv && drv->pm && drv->pm->prepare) {
int error = drv->pm->prepare(dev); int error = drv->pm->prepare(dev);
if (error) if (error < 0)
return error; return error;
if (!error && dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_PREPARE))
return 0;
} }
return pci_dev_keep_suspended(to_pci_dev(dev)); return pci_dev_keep_suspended(to_pci_dev(dev));
} }
@ -731,18 +727,25 @@ static int pci_pm_suspend(struct device *dev)
if (!pm) { if (!pm) {
pci_pm_default_suspend(pci_dev); pci_pm_default_suspend(pci_dev);
goto Fixup; return 0;
} }
/* /*
* PCI devices suspended at run time need to be resumed at this point, * PCI devices suspended at run time may need to be resumed at this
* because in general it is necessary to reconfigure them for system * point, because in general it may be necessary to reconfigure them for
* suspend. Namely, if the device is supposed to wake up the system * system suspend. Namely, if the device is expected to wake up the
* from the sleep state, we may need to reconfigure it for this purpose. * system from the sleep state, it may have to be reconfigured for this
* In turn, if the device is not supposed to wake up the system from the * purpose, or if the device is not expected to wake up the system from
* sleep state, we'll have to prevent it from signaling wake-up. * the sleep state, it should be prevented from signaling wakeup events
* going forward.
*
* Also if the driver of the device does not indicate that its system
* suspend callbacks can cope with runtime-suspended devices, it is
* better to resume the device from runtime suspend here.
*/ */
pm_runtime_resume(dev); if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
!pci_dev_keep_suspended(pci_dev))
pm_runtime_resume(dev);
pci_dev->state_saved = false; pci_dev->state_saved = false;
if (pm->suspend) { if (pm->suspend) {
@ -762,17 +765,27 @@ static int pci_pm_suspend(struct device *dev)
} }
} }
Fixup:
pci_fixup_device(pci_fixup_suspend, pci_dev);
return 0; return 0;
} }
static int pci_pm_suspend_late(struct device *dev)
{
if (dev_pm_smart_suspend_and_suspended(dev))
return 0;
pci_fixup_device(pci_fixup_suspend, to_pci_dev(dev));
return pm_generic_suspend_late(dev);
}
static int pci_pm_suspend_noirq(struct device *dev) static int pci_pm_suspend_noirq(struct device *dev)
{ {
struct pci_dev *pci_dev = to_pci_dev(dev); struct pci_dev *pci_dev = to_pci_dev(dev);
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
if (dev_pm_smart_suspend_and_suspended(dev))
return 0;
if (pci_has_legacy_pm_support(pci_dev)) if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_suspend_late(dev, PMSG_SUSPEND); return pci_legacy_suspend_late(dev, PMSG_SUSPEND);
@ -805,6 +818,9 @@ static int pci_pm_suspend_noirq(struct device *dev)
pci_prepare_to_sleep(pci_dev); pci_prepare_to_sleep(pci_dev);
} }
dev_dbg(dev, "PCI PM: Suspend power state: %s\n",
pci_power_name(pci_dev->current_state));
pci_pm_set_unknown_state(pci_dev); pci_pm_set_unknown_state(pci_dev);
/* /*
@ -831,6 +847,14 @@ static int pci_pm_resume_noirq(struct device *dev)
struct device_driver *drv = dev->driver; struct device_driver *drv = dev->driver;
int error = 0; int error = 0;
/*
* Devices with DPM_FLAG_SMART_SUSPEND may be left in runtime suspend
* during system suspend, so update their runtime PM status to "active"
* as they are going to be put into D0 shortly.
*/
if (dev_pm_smart_suspend_and_suspended(dev))
pm_runtime_set_active(dev);
pci_pm_default_resume_early(pci_dev); pci_pm_default_resume_early(pci_dev);
if (pci_has_legacy_pm_support(pci_dev)) if (pci_has_legacy_pm_support(pci_dev))
@ -873,6 +897,7 @@ static int pci_pm_resume(struct device *dev)
#else /* !CONFIG_SUSPEND */ #else /* !CONFIG_SUSPEND */
#define pci_pm_suspend NULL #define pci_pm_suspend NULL
#define pci_pm_suspend_late NULL
#define pci_pm_suspend_noirq NULL #define pci_pm_suspend_noirq NULL
#define pci_pm_resume NULL #define pci_pm_resume NULL
#define pci_pm_resume_noirq NULL #define pci_pm_resume_noirq NULL
@ -907,7 +932,8 @@ static int pci_pm_freeze(struct device *dev)
* devices should not be touched during freeze/thaw transitions, * devices should not be touched during freeze/thaw transitions,
* however. * however.
*/ */
pm_runtime_resume(dev); if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND))
pm_runtime_resume(dev);
pci_dev->state_saved = false; pci_dev->state_saved = false;
if (pm->freeze) { if (pm->freeze) {
@ -919,17 +945,25 @@ static int pci_pm_freeze(struct device *dev)
return error; return error;
} }
if (pcibios_pm_ops.freeze)
return pcibios_pm_ops.freeze(dev);
return 0; return 0;
} }
static int pci_pm_freeze_late(struct device *dev)
{
if (dev_pm_smart_suspend_and_suspended(dev))
return 0;
return pm_generic_freeze_late(dev);;
}
static int pci_pm_freeze_noirq(struct device *dev) static int pci_pm_freeze_noirq(struct device *dev)
{ {
struct pci_dev *pci_dev = to_pci_dev(dev); struct pci_dev *pci_dev = to_pci_dev(dev);
struct device_driver *drv = dev->driver; struct device_driver *drv = dev->driver;
if (dev_pm_smart_suspend_and_suspended(dev))
return 0;
if (pci_has_legacy_pm_support(pci_dev)) if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_suspend_late(dev, PMSG_FREEZE); return pci_legacy_suspend_late(dev, PMSG_FREEZE);
@ -959,6 +993,16 @@ static int pci_pm_thaw_noirq(struct device *dev)
struct device_driver *drv = dev->driver; struct device_driver *drv = dev->driver;
int error = 0; int error = 0;
/*
* If the device is in runtime suspend, the code below may not work
* correctly with it, so skip that code and make the PM core skip all of
* the subsequent "thaw" callbacks for the device.
*/
if (dev_pm_smart_suspend_and_suspended(dev)) {
dev->power.direct_complete = true;
return 0;
}
if (pcibios_pm_ops.thaw_noirq) { if (pcibios_pm_ops.thaw_noirq) {
error = pcibios_pm_ops.thaw_noirq(dev); error = pcibios_pm_ops.thaw_noirq(dev);
if (error) if (error)
@ -983,12 +1027,6 @@ static int pci_pm_thaw(struct device *dev)
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
int error = 0; int error = 0;
if (pcibios_pm_ops.thaw) {
error = pcibios_pm_ops.thaw(dev);
if (error)
return error;
}
if (pci_has_legacy_pm_support(pci_dev)) if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_resume(dev); return pci_legacy_resume(dev);
@ -1014,11 +1052,13 @@ static int pci_pm_poweroff(struct device *dev)
if (!pm) { if (!pm) {
pci_pm_default_suspend(pci_dev); pci_pm_default_suspend(pci_dev);
goto Fixup; return 0;
} }
/* The reason to do that is the same as in pci_pm_suspend(). */ /* The reason to do that is the same as in pci_pm_suspend(). */
pm_runtime_resume(dev); if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) ||
!pci_dev_keep_suspended(pci_dev))
pm_runtime_resume(dev);
pci_dev->state_saved = false; pci_dev->state_saved = false;
if (pm->poweroff) { if (pm->poweroff) {
@ -1030,20 +1070,27 @@ static int pci_pm_poweroff(struct device *dev)
return error; return error;
} }
Fixup:
pci_fixup_device(pci_fixup_suspend, pci_dev);
if (pcibios_pm_ops.poweroff)
return pcibios_pm_ops.poweroff(dev);
return 0; return 0;
} }
static int pci_pm_poweroff_late(struct device *dev)
{
if (dev_pm_smart_suspend_and_suspended(dev))
return 0;
pci_fixup_device(pci_fixup_suspend, to_pci_dev(dev));
return pm_generic_poweroff_late(dev);
}
static int pci_pm_poweroff_noirq(struct device *dev) static int pci_pm_poweroff_noirq(struct device *dev)
{ {
struct pci_dev *pci_dev = to_pci_dev(dev); struct pci_dev *pci_dev = to_pci_dev(dev);
struct device_driver *drv = dev->driver; struct device_driver *drv = dev->driver;
if (dev_pm_smart_suspend_and_suspended(dev))
return 0;
if (pci_has_legacy_pm_support(to_pci_dev(dev))) if (pci_has_legacy_pm_support(to_pci_dev(dev)))
return pci_legacy_suspend_late(dev, PMSG_HIBERNATE); return pci_legacy_suspend_late(dev, PMSG_HIBERNATE);
@ -1085,6 +1132,10 @@ static int pci_pm_restore_noirq(struct device *dev)
struct device_driver *drv = dev->driver; struct device_driver *drv = dev->driver;
int error = 0; int error = 0;
/* This is analogous to the pci_pm_resume_noirq() case. */
if (dev_pm_smart_suspend_and_suspended(dev))
pm_runtime_set_active(dev);
if (pcibios_pm_ops.restore_noirq) { if (pcibios_pm_ops.restore_noirq) {
error = pcibios_pm_ops.restore_noirq(dev); error = pcibios_pm_ops.restore_noirq(dev);
if (error) if (error)
@ -1108,12 +1159,6 @@ static int pci_pm_restore(struct device *dev)
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
int error = 0; int error = 0;
if (pcibios_pm_ops.restore) {
error = pcibios_pm_ops.restore(dev);
if (error)
return error;
}
/* /*
* This is necessary for the hibernation error path in which restore is * This is necessary for the hibernation error path in which restore is
* called without restoring the standard config registers of the device. * called without restoring the standard config registers of the device.
@ -1139,10 +1184,12 @@ static int pci_pm_restore(struct device *dev)
#else /* !CONFIG_HIBERNATE_CALLBACKS */ #else /* !CONFIG_HIBERNATE_CALLBACKS */
#define pci_pm_freeze NULL #define pci_pm_freeze NULL
#define pci_pm_freeze_late NULL
#define pci_pm_freeze_noirq NULL #define pci_pm_freeze_noirq NULL
#define pci_pm_thaw NULL #define pci_pm_thaw NULL
#define pci_pm_thaw_noirq NULL #define pci_pm_thaw_noirq NULL
#define pci_pm_poweroff NULL #define pci_pm_poweroff NULL
#define pci_pm_poweroff_late NULL
#define pci_pm_poweroff_noirq NULL #define pci_pm_poweroff_noirq NULL
#define pci_pm_restore NULL #define pci_pm_restore NULL
#define pci_pm_restore_noirq NULL #define pci_pm_restore_noirq NULL
@ -1258,10 +1305,13 @@ static const struct dev_pm_ops pci_dev_pm_ops = {
.prepare = pci_pm_prepare, .prepare = pci_pm_prepare,
.complete = pci_pm_complete, .complete = pci_pm_complete,
.suspend = pci_pm_suspend, .suspend = pci_pm_suspend,
.suspend_late = pci_pm_suspend_late,
.resume = pci_pm_resume, .resume = pci_pm_resume,
.freeze = pci_pm_freeze, .freeze = pci_pm_freeze,
.freeze_late = pci_pm_freeze_late,
.thaw = pci_pm_thaw, .thaw = pci_pm_thaw,
.poweroff = pci_pm_poweroff, .poweroff = pci_pm_poweroff,
.poweroff_late = pci_pm_poweroff_late,
.restore = pci_pm_restore, .restore = pci_pm_restore,
.suspend_noirq = pci_pm_suspend_noirq, .suspend_noirq = pci_pm_suspend_noirq,
.resume_noirq = pci_pm_resume_noirq, .resume_noirq = pci_pm_resume_noirq,

View File

@ -2166,8 +2166,7 @@ bool pci_dev_keep_suspended(struct pci_dev *pci_dev)
if (!pm_runtime_suspended(dev) if (!pm_runtime_suspended(dev)
|| pci_target_state(pci_dev, wakeup) != pci_dev->current_state || pci_target_state(pci_dev, wakeup) != pci_dev->current_state
|| platform_pci_need_resume(pci_dev) || platform_pci_need_resume(pci_dev))
|| (pci_dev->dev_flags & PCI_DEV_FLAGS_NEEDS_RESUME))
return false; return false;
/* /*

View File

@ -355,7 +355,7 @@ int sr_configure_errgen(struct omap_sr *sr)
u8 senp_shift, senn_shift; u8 senp_shift, senn_shift;
if (!sr) { if (!sr) {
pr_warn("%s: NULL omap_sr from %pF\n", pr_warn("%s: NULL omap_sr from %pS\n",
__func__, (void *)_RET_IP_); __func__, (void *)_RET_IP_);
return -EINVAL; return -EINVAL;
} }
@ -422,7 +422,7 @@ int sr_disable_errgen(struct omap_sr *sr)
u32 vpboundint_en, vpboundint_st; u32 vpboundint_en, vpboundint_st;
if (!sr) { if (!sr) {
pr_warn("%s: NULL omap_sr from %pF\n", pr_warn("%s: NULL omap_sr from %pS\n",
__func__, (void *)_RET_IP_); __func__, (void *)_RET_IP_);
return -EINVAL; return -EINVAL;
} }
@ -477,7 +477,7 @@ int sr_configure_minmax(struct omap_sr *sr)
u8 senp_shift, senn_shift; u8 senp_shift, senn_shift;
if (!sr) { if (!sr) {
pr_warn("%s: NULL omap_sr from %pF\n", pr_warn("%s: NULL omap_sr from %pS\n",
__func__, (void *)_RET_IP_); __func__, (void *)_RET_IP_);
return -EINVAL; return -EINVAL;
} }
@ -562,7 +562,7 @@ int sr_enable(struct omap_sr *sr, unsigned long volt)
int ret; int ret;
if (!sr) { if (!sr) {
pr_warn("%s: NULL omap_sr from %pF\n", pr_warn("%s: NULL omap_sr from %pS\n",
__func__, (void *)_RET_IP_); __func__, (void *)_RET_IP_);
return -EINVAL; return -EINVAL;
} }
@ -614,7 +614,7 @@ int sr_enable(struct omap_sr *sr, unsigned long volt)
void sr_disable(struct omap_sr *sr) void sr_disable(struct omap_sr *sr)
{ {
if (!sr) { if (!sr) {
pr_warn("%s: NULL omap_sr from %pF\n", pr_warn("%s: NULL omap_sr from %pS\n",
__func__, (void *)_RET_IP_); __func__, (void *)_RET_IP_);
return; return;
} }

View File

@ -361,17 +361,6 @@ out:
return ret; return ret;
} }
static bool scpsys_active_wakeup(struct device *dev)
{
struct generic_pm_domain *genpd;
struct scp_domain *scpd;
genpd = pd_to_genpd(dev->pm_domain);
scpd = container_of(genpd, struct scp_domain, genpd);
return scpd->data->active_wakeup;
}
static void init_clks(struct platform_device *pdev, struct clk **clk) static void init_clks(struct platform_device *pdev, struct clk **clk)
{ {
int i; int i;
@ -466,7 +455,8 @@ static struct scp *init_scp(struct platform_device *pdev,
genpd->name = data->name; genpd->name = data->name;
genpd->power_off = scpsys_power_off; genpd->power_off = scpsys_power_off;
genpd->power_on = scpsys_power_on; genpd->power_on = scpsys_power_on;
genpd->dev_ops.active_wakeup = scpsys_active_wakeup; if (scpd->data->active_wakeup)
genpd->flags |= GENPD_FLAG_ACTIVE_WAKEUP;
} }
return scp; return scp;

View File

@ -358,17 +358,6 @@ static void rockchip_pd_detach_dev(struct generic_pm_domain *genpd,
pm_clk_destroy(dev); pm_clk_destroy(dev);
} }
static bool rockchip_active_wakeup(struct device *dev)
{
struct generic_pm_domain *genpd;
struct rockchip_pm_domain *pd;
genpd = pd_to_genpd(dev->pm_domain);
pd = container_of(genpd, struct rockchip_pm_domain, genpd);
return pd->info->active_wakeup;
}
static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu, static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
struct device_node *node) struct device_node *node)
{ {
@ -489,8 +478,9 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
pd->genpd.power_on = rockchip_pd_power_on; pd->genpd.power_on = rockchip_pd_power_on;
pd->genpd.attach_dev = rockchip_pd_attach_dev; pd->genpd.attach_dev = rockchip_pd_attach_dev;
pd->genpd.detach_dev = rockchip_pd_detach_dev; pd->genpd.detach_dev = rockchip_pd_detach_dev;
pd->genpd.dev_ops.active_wakeup = rockchip_active_wakeup;
pd->genpd.flags = GENPD_FLAG_PM_CLK; pd->genpd.flags = GENPD_FLAG_PM_CLK;
if (pd_info->active_wakeup)
pd->genpd.flags |= GENPD_FLAG_ACTIVE_WAKEUP;
pm_genpd_init(&pd->genpd, NULL, false); pm_genpd_init(&pd->genpd, NULL, false);
pmu->genpd_data.domains[id] = &pd->genpd; pmu->genpd_data.domains[id] = &pd->genpd;

View File

@ -287,6 +287,8 @@ acpi_status acpi_os_write_port(acpi_io_address address, u32 value, u32 width);
/* /*
* Platform and hardware-independent physical memory interfaces * Platform and hardware-independent physical memory interfaces
*/ */
int acpi_os_read_iomem(void __iomem *virt_addr, u64 *value, u32 width);
#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_read_memory #ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_read_memory
acpi_status acpi_status
acpi_os_read_memory(acpi_physical_address address, u64 *value, u32 width); acpi_os_read_memory(acpi_physical_address address, u64 *value, u32 width);

View File

@ -864,21 +864,16 @@ static inline void arch_reserve_mem_area(acpi_physical_address addr,
#endif #endif
#if defined(CONFIG_ACPI) && defined(CONFIG_PM) #if defined(CONFIG_ACPI) && defined(CONFIG_PM)
int acpi_dev_runtime_suspend(struct device *dev); int acpi_dev_suspend(struct device *dev, bool wakeup);
int acpi_dev_runtime_resume(struct device *dev); int acpi_dev_resume(struct device *dev);
int acpi_subsys_runtime_suspend(struct device *dev); int acpi_subsys_runtime_suspend(struct device *dev);
int acpi_subsys_runtime_resume(struct device *dev); int acpi_subsys_runtime_resume(struct device *dev);
struct acpi_device *acpi_dev_pm_get_node(struct device *dev);
int acpi_dev_pm_attach(struct device *dev, bool power_on); int acpi_dev_pm_attach(struct device *dev, bool power_on);
#else #else
static inline int acpi_dev_runtime_suspend(struct device *dev) { return 0; } static inline int acpi_dev_runtime_suspend(struct device *dev) { return 0; }
static inline int acpi_dev_runtime_resume(struct device *dev) { return 0; } static inline int acpi_dev_runtime_resume(struct device *dev) { return 0; }
static inline int acpi_subsys_runtime_suspend(struct device *dev) { return 0; } static inline int acpi_subsys_runtime_suspend(struct device *dev) { return 0; }
static inline int acpi_subsys_runtime_resume(struct device *dev) { return 0; } static inline int acpi_subsys_runtime_resume(struct device *dev) { return 0; }
static inline struct acpi_device *acpi_dev_pm_get_node(struct device *dev)
{
return NULL;
}
static inline int acpi_dev_pm_attach(struct device *dev, bool power_on) static inline int acpi_dev_pm_attach(struct device *dev, bool power_on)
{ {
return -ENODEV; return -ENODEV;
@ -887,22 +882,30 @@ static inline int acpi_dev_pm_attach(struct device *dev, bool power_on)
#if defined(CONFIG_ACPI) && defined(CONFIG_PM_SLEEP) #if defined(CONFIG_ACPI) && defined(CONFIG_PM_SLEEP)
int acpi_dev_suspend_late(struct device *dev); int acpi_dev_suspend_late(struct device *dev);
int acpi_dev_resume_early(struct device *dev);
int acpi_subsys_prepare(struct device *dev); int acpi_subsys_prepare(struct device *dev);
void acpi_subsys_complete(struct device *dev); void acpi_subsys_complete(struct device *dev);
int acpi_subsys_suspend_late(struct device *dev); int acpi_subsys_suspend_late(struct device *dev);
int acpi_subsys_suspend_noirq(struct device *dev);
int acpi_subsys_resume_noirq(struct device *dev);
int acpi_subsys_resume_early(struct device *dev); int acpi_subsys_resume_early(struct device *dev);
int acpi_subsys_suspend(struct device *dev); int acpi_subsys_suspend(struct device *dev);
int acpi_subsys_freeze(struct device *dev); int acpi_subsys_freeze(struct device *dev);
int acpi_subsys_freeze_late(struct device *dev);
int acpi_subsys_freeze_noirq(struct device *dev);
int acpi_subsys_thaw_noirq(struct device *dev);
#else #else
static inline int acpi_dev_suspend_late(struct device *dev) { return 0; }
static inline int acpi_dev_resume_early(struct device *dev) { return 0; } static inline int acpi_dev_resume_early(struct device *dev) { return 0; }
static inline int acpi_subsys_prepare(struct device *dev) { return 0; } static inline int acpi_subsys_prepare(struct device *dev) { return 0; }
static inline void acpi_subsys_complete(struct device *dev) {} static inline void acpi_subsys_complete(struct device *dev) {}
static inline int acpi_subsys_suspend_late(struct device *dev) { return 0; } static inline int acpi_subsys_suspend_late(struct device *dev) { return 0; }
static inline int acpi_subsys_suspend_noirq(struct device *dev) { return 0; }
static inline int acpi_subsys_resume_noirq(struct device *dev) { return 0; }
static inline int acpi_subsys_resume_early(struct device *dev) { return 0; } static inline int acpi_subsys_resume_early(struct device *dev) { return 0; }
static inline int acpi_subsys_suspend(struct device *dev) { return 0; } static inline int acpi_subsys_suspend(struct device *dev) { return 0; }
static inline int acpi_subsys_freeze(struct device *dev) { return 0; } static inline int acpi_subsys_freeze(struct device *dev) { return 0; }
static inline int acpi_subsys_freeze_late(struct device *dev) { return 0; }
static inline int acpi_subsys_freeze_noirq(struct device *dev) { return 0; }
static inline int acpi_subsys_thaw_noirq(struct device *dev) { return 0; }
#endif #endif
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
@ -1254,4 +1257,13 @@ int acpi_irq_get(acpi_handle handle, unsigned int index, struct resource *res)
} }
#endif #endif
#ifdef CONFIG_ACPI_LPIT
int lpit_read_residency_count_address(u64 *address);
#else
static inline int lpit_read_residency_count_address(u64 *address)
{
return -EINVAL;
}
#endif
#endif /*_LINUX_ACPI_H*/ #endif /*_LINUX_ACPI_H*/

View File

@ -6,15 +6,30 @@
#define _LINUX_ARCH_TOPOLOGY_H_ #define _LINUX_ARCH_TOPOLOGY_H_
#include <linux/types.h> #include <linux/types.h>
#include <linux/percpu.h>
void topology_normalize_cpu_scale(void); void topology_normalize_cpu_scale(void);
struct device_node; struct device_node;
bool topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu); bool topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu);
DECLARE_PER_CPU(unsigned long, cpu_scale);
struct sched_domain; struct sched_domain;
unsigned long topology_get_cpu_scale(struct sched_domain *sd, int cpu); static inline
unsigned long topology_get_cpu_scale(struct sched_domain *sd, int cpu)
{
return per_cpu(cpu_scale, cpu);
}
void topology_set_cpu_scale(unsigned int cpu, unsigned long capacity); void topology_set_cpu_scale(unsigned int cpu, unsigned long capacity);
DECLARE_PER_CPU(unsigned long, freq_scale);
static inline
unsigned long topology_get_freq_scale(struct sched_domain *sd, int cpu)
{
return per_cpu(freq_scale, cpu);
}
#endif /* _LINUX_ARCH_TOPOLOGY_H_ */ #endif /* _LINUX_ARCH_TOPOLOGY_H_ */

View File

@ -919,6 +919,9 @@ static inline bool policy_has_boost_freq(struct cpufreq_policy *policy)
extern unsigned int arch_freq_get_on_cpu(int cpu); extern unsigned int arch_freq_get_on_cpu(int cpu);
extern void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq,
unsigned long max_freq);
/* the following are really really optional */ /* the following are really really optional */
extern struct freq_attr cpufreq_freq_attr_scaling_available_freqs; extern struct freq_attr cpufreq_freq_attr_scaling_available_freqs;
extern struct freq_attr cpufreq_freq_attr_scaling_boost_freqs; extern struct freq_attr cpufreq_freq_attr_scaling_boost_freqs;

View File

@ -19,6 +19,13 @@
#define DEVFREQ_NAME_LEN 16 #define DEVFREQ_NAME_LEN 16
/* DEVFREQ governor name */
#define DEVFREQ_GOV_SIMPLE_ONDEMAND "simple_ondemand"
#define DEVFREQ_GOV_PERFORMANCE "performance"
#define DEVFREQ_GOV_POWERSAVE "powersave"
#define DEVFREQ_GOV_USERSPACE "userspace"
#define DEVFREQ_GOV_PASSIVE "passive"
/* DEVFREQ notifier interface */ /* DEVFREQ notifier interface */
#define DEVFREQ_TRANSITION_NOTIFIER (0) #define DEVFREQ_TRANSITION_NOTIFIER (0)
@ -84,8 +91,9 @@ struct devfreq_dev_status {
* from devfreq_remove_device() call. If the user * from devfreq_remove_device() call. If the user
* has registered devfreq->nb at a notifier-head, * has registered devfreq->nb at a notifier-head,
* this is the time to unregister it. * this is the time to unregister it.
* @freq_table: Optional list of frequencies to support statistics. * @freq_table: Optional list of frequencies to support statistics
* @max_state: The size of freq_table. * and freq_table must be generated in ascending order.
* @max_state: The size of freq_table.
*/ */
struct devfreq_dev_profile { struct devfreq_dev_profile {
unsigned long initial_freq; unsigned long initial_freq;
@ -120,6 +128,8 @@ struct devfreq_dev_profile {
* touch this. * touch this.
* @min_freq: Limit minimum frequency requested by user (0: none) * @min_freq: Limit minimum frequency requested by user (0: none)
* @max_freq: Limit maximum frequency requested by user (0: none) * @max_freq: Limit maximum frequency requested by user (0: none)
* @scaling_min_freq: Limit minimum frequency requested by OPP interface
* @scaling_max_freq: Limit maximum frequency requested by OPP interface
* @stop_polling: devfreq polling status of a device. * @stop_polling: devfreq polling status of a device.
* @total_trans: Number of devfreq transitions * @total_trans: Number of devfreq transitions
* @trans_table: Statistics of devfreq transitions * @trans_table: Statistics of devfreq transitions
@ -153,6 +163,8 @@ struct devfreq {
unsigned long min_freq; unsigned long min_freq;
unsigned long max_freq; unsigned long max_freq;
unsigned long scaling_min_freq;
unsigned long scaling_max_freq;
bool stop_polling; bool stop_polling;
/* information for device frequency transition */ /* information for device frequency transition */

View File

@ -370,9 +370,6 @@ int subsys_virtual_register(struct bus_type *subsys,
* @devnode: Callback to provide the devtmpfs. * @devnode: Callback to provide the devtmpfs.
* @class_release: Called to release this class. * @class_release: Called to release this class.
* @dev_release: Called to release the device. * @dev_release: Called to release the device.
* @suspend: Used to put the device to sleep mode, usually to a low power
* state.
* @resume: Used to bring the device from the sleep mode.
* @shutdown_pre: Called at shut-down time before driver shutdown. * @shutdown_pre: Called at shut-down time before driver shutdown.
* @ns_type: Callbacks so sysfs can detemine namespaces. * @ns_type: Callbacks so sysfs can detemine namespaces.
* @namespace: Namespace of the device belongs to this class. * @namespace: Namespace of the device belongs to this class.
@ -400,8 +397,6 @@ struct class {
void (*class_release)(struct class *class); void (*class_release)(struct class *class);
void (*dev_release)(struct device *dev); void (*dev_release)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
int (*shutdown_pre)(struct device *dev); int (*shutdown_pre)(struct device *dev);
const struct kobj_ns_type_operations *ns_type; const struct kobj_ns_type_operations *ns_type;
@ -1075,6 +1070,16 @@ static inline void dev_pm_syscore_device(struct device *dev, bool val)
#endif #endif
} }
static inline void dev_pm_set_driver_flags(struct device *dev, u32 flags)
{
dev->power.driver_flags = flags;
}
static inline bool dev_pm_test_driver_flags(struct device *dev, u32 flags)
{
return !!(dev->power.driver_flags & flags);
}
static inline void device_lock(struct device *dev) static inline void device_lock(struct device *dev)
{ {
mutex_lock(&dev->mutex); mutex_lock(&dev->mutex);

View File

@ -182,7 +182,7 @@ static inline void freezable_schedule_unsafe(void)
} }
/* /*
* Like freezable_schedule_timeout(), but should not block the freezer. Do not * Like schedule_timeout(), but should not block the freezer. Do not
* call this with locks held. * call this with locks held.
*/ */
static inline long freezable_schedule_timeout(long timeout) static inline long freezable_schedule_timeout(long timeout)

View File

@ -206,13 +206,8 @@ enum pci_dev_flags {
PCI_DEV_FLAGS_BRIDGE_XLATE_ROOT = (__force pci_dev_flags_t) (1 << 9), PCI_DEV_FLAGS_BRIDGE_XLATE_ROOT = (__force pci_dev_flags_t) (1 << 9),
/* Do not use FLR even if device advertises PCI_AF_CAP */ /* Do not use FLR even if device advertises PCI_AF_CAP */
PCI_DEV_FLAGS_NO_FLR_RESET = (__force pci_dev_flags_t) (1 << 10), PCI_DEV_FLAGS_NO_FLR_RESET = (__force pci_dev_flags_t) (1 << 10),
/*
* Resume before calling the driver's system suspend hooks, disabling
* the direct_complete optimization.
*/
PCI_DEV_FLAGS_NEEDS_RESUME = (__force pci_dev_flags_t) (1 << 11),
/* Don't use Relaxed Ordering for TLPs directed at this device */ /* Don't use Relaxed Ordering for TLPs directed at this device */
PCI_DEV_FLAGS_NO_RELAXED_ORDERING = (__force pci_dev_flags_t) (1 << 12), PCI_DEV_FLAGS_NO_RELAXED_ORDERING = (__force pci_dev_flags_t) (1 << 11),
}; };
enum pci_irq_reroute_variant { enum pci_irq_reroute_variant {

View File

@ -550,6 +550,33 @@ struct pm_subsys_data {
#endif #endif
}; };
/*
* Driver flags to control system suspend/resume behavior.
*
* These flags can be set by device drivers at the probe time. They need not be
* cleared by the drivers as the driver core will take care of that.
*
* NEVER_SKIP: Do not skip system suspend/resume callbacks for the device.
* SMART_PREPARE: Check the return value of the driver's ->prepare callback.
* SMART_SUSPEND: No need to resume the device from runtime suspend.
*
* Setting SMART_PREPARE instructs bus types and PM domains which may want
* system suspend/resume callbacks to be skipped for the device to return 0 from
* their ->prepare callbacks if the driver's ->prepare callback returns 0 (in
* other words, the system suspend/resume callbacks can only be skipped for the
* device if its driver doesn't object against that). This flag has no effect
* if NEVER_SKIP is set.
*
* Setting SMART_SUSPEND instructs bus types and PM domains which may want to
* runtime resume the device upfront during system suspend that doing so is not
* necessary from the driver's perspective. It also may cause them to skip
* invocations of the ->suspend_late and ->suspend_noirq callbacks provided by
* the driver if they decide to leave the device in runtime suspend.
*/
#define DPM_FLAG_NEVER_SKIP BIT(0)
#define DPM_FLAG_SMART_PREPARE BIT(1)
#define DPM_FLAG_SMART_SUSPEND BIT(2)
struct dev_pm_info { struct dev_pm_info {
pm_message_t power_state; pm_message_t power_state;
unsigned int can_wakeup:1; unsigned int can_wakeup:1;
@ -561,6 +588,7 @@ struct dev_pm_info {
bool is_late_suspended:1; bool is_late_suspended:1;
bool early_init:1; /* Owned by the PM core */ bool early_init:1; /* Owned by the PM core */
bool direct_complete:1; /* Owned by the PM core */ bool direct_complete:1; /* Owned by the PM core */
u32 driver_flags;
spinlock_t lock; spinlock_t lock;
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
struct list_head entry; struct list_head entry;
@ -736,7 +764,8 @@ extern int pm_generic_poweroff_noirq(struct device *dev);
extern int pm_generic_poweroff_late(struct device *dev); extern int pm_generic_poweroff_late(struct device *dev);
extern int pm_generic_poweroff(struct device *dev); extern int pm_generic_poweroff(struct device *dev);
extern void pm_generic_complete(struct device *dev); extern void pm_generic_complete(struct device *dev);
extern void pm_complete_with_resume_check(struct device *dev);
extern bool dev_pm_smart_suspend_and_suspended(struct device *dev);
#else /* !CONFIG_PM_SLEEP */ #else /* !CONFIG_PM_SLEEP */

View File

@ -18,9 +18,10 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
/* Defines used for the flags field in the struct generic_pm_domain */ /* Defines used for the flags field in the struct generic_pm_domain */
#define GENPD_FLAG_PM_CLK (1U << 0) /* PM domain uses PM clk */ #define GENPD_FLAG_PM_CLK (1U << 0) /* PM domain uses PM clk */
#define GENPD_FLAG_IRQ_SAFE (1U << 1) /* PM domain operates in atomic */ #define GENPD_FLAG_IRQ_SAFE (1U << 1) /* PM domain operates in atomic */
#define GENPD_FLAG_ALWAYS_ON (1U << 2) /* PM domain is always powered on */ #define GENPD_FLAG_ALWAYS_ON (1U << 2) /* PM domain is always powered on */
#define GENPD_FLAG_ACTIVE_WAKEUP (1U << 3) /* Keep devices active if wakeup */
enum gpd_status { enum gpd_status {
GPD_STATE_ACTIVE = 0, /* PM domain is active */ GPD_STATE_ACTIVE = 0, /* PM domain is active */
@ -35,7 +36,6 @@ struct dev_power_governor {
struct gpd_dev_ops { struct gpd_dev_ops {
int (*start)(struct device *dev); int (*start)(struct device *dev);
int (*stop)(struct device *dev); int (*stop)(struct device *dev);
bool (*active_wakeup)(struct device *dev);
}; };
struct genpd_power_state { struct genpd_power_state {
@ -64,8 +64,11 @@ struct generic_pm_domain {
unsigned int device_count; /* Number of devices */ unsigned int device_count; /* Number of devices */
unsigned int suspended_count; /* System suspend device counter */ unsigned int suspended_count; /* System suspend device counter */
unsigned int prepared_count; /* Suspend counter of prepared devices */ unsigned int prepared_count; /* Suspend counter of prepared devices */
unsigned int performance_state; /* Aggregated max performance state */
int (*power_off)(struct generic_pm_domain *domain); int (*power_off)(struct generic_pm_domain *domain);
int (*power_on)(struct generic_pm_domain *domain); int (*power_on)(struct generic_pm_domain *domain);
int (*set_performance_state)(struct generic_pm_domain *genpd,
unsigned int state);
struct gpd_dev_ops dev_ops; struct gpd_dev_ops dev_ops;
s64 max_off_time_ns; /* Maximum allowed "suspended" time. */ s64 max_off_time_ns; /* Maximum allowed "suspended" time. */
bool max_off_time_changed; bool max_off_time_changed;
@ -121,6 +124,7 @@ struct generic_pm_domain_data {
struct pm_domain_data base; struct pm_domain_data base;
struct gpd_timing_data td; struct gpd_timing_data td;
struct notifier_block nb; struct notifier_block nb;
unsigned int performance_state;
void *data; void *data;
}; };
@ -148,6 +152,8 @@ extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
extern int pm_genpd_init(struct generic_pm_domain *genpd, extern int pm_genpd_init(struct generic_pm_domain *genpd,
struct dev_power_governor *gov, bool is_off); struct dev_power_governor *gov, bool is_off);
extern int pm_genpd_remove(struct generic_pm_domain *genpd); extern int pm_genpd_remove(struct generic_pm_domain *genpd);
extern int dev_pm_genpd_set_performance_state(struct device *dev,
unsigned int state);
extern struct dev_power_governor simple_qos_governor; extern struct dev_power_governor simple_qos_governor;
extern struct dev_power_governor pm_domain_always_on_gov; extern struct dev_power_governor pm_domain_always_on_gov;
@ -188,6 +194,12 @@ static inline int pm_genpd_remove(struct generic_pm_domain *genpd)
return -ENOTSUPP; return -ENOTSUPP;
} }
static inline int dev_pm_genpd_set_performance_state(struct device *dev,
unsigned int state)
{
return -ENOTSUPP;
}
#define simple_qos_governor (*(struct dev_power_governor *)(NULL)) #define simple_qos_governor (*(struct dev_power_governor *)(NULL))
#define pm_domain_always_on_gov (*(struct dev_power_governor *)(NULL)) #define pm_domain_always_on_gov (*(struct dev_power_governor *)(NULL))
#endif #endif

View File

@ -124,7 +124,9 @@ void dev_pm_opp_put_regulators(struct opp_table *opp_table);
struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char * name); struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char * name);
void dev_pm_opp_put_clkname(struct opp_table *opp_table); void dev_pm_opp_put_clkname(struct opp_table *opp_table);
struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)); struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data));
void dev_pm_opp_register_put_opp_helper(struct opp_table *opp_table); void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table);
struct opp_table *dev_pm_opp_register_get_pstate_helper(struct device *dev, int (*get_pstate)(struct device *dev, unsigned long rate));
void dev_pm_opp_unregister_get_pstate_helper(struct opp_table *opp_table);
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq); int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask); int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask);
int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask); int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask);
@ -243,7 +245,15 @@ static inline struct opp_table *dev_pm_opp_register_set_opp_helper(struct device
return ERR_PTR(-ENOTSUPP); return ERR_PTR(-ENOTSUPP);
} }
static inline void dev_pm_opp_register_put_opp_helper(struct opp_table *opp_table) {} static inline void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table) {}
static inline struct opp_table *dev_pm_opp_register_get_pstate_helper(struct device *dev,
int (*get_pstate)(struct device *dev, unsigned long rate))
{
return ERR_PTR(-ENOTSUPP);
}
static inline void dev_pm_opp_unregister_get_pstate_helper(struct opp_table *opp_table) {}
static inline struct opp_table *dev_pm_opp_set_prop_name(struct device *dev, const char *name) static inline struct opp_table *dev_pm_opp_set_prop_name(struct device *dev, const char *name)
{ {

View File

@ -28,19 +28,21 @@ enum pm_qos_flags_status {
PM_QOS_FLAGS_ALL, PM_QOS_FLAGS_ALL,
}; };
#define PM_QOS_DEFAULT_VALUE -1 #define PM_QOS_DEFAULT_VALUE (-1)
#define PM_QOS_LATENCY_ANY S32_MAX
#define PM_QOS_LATENCY_ANY_NS ((s64)PM_QOS_LATENCY_ANY * NSEC_PER_USEC)
#define PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC) #define PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC)
#define PM_QOS_NETWORK_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC) #define PM_QOS_NETWORK_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC)
#define PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE 0 #define PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE 0
#define PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE 0 #define PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE 0
#define PM_QOS_RESUME_LATENCY_DEFAULT_VALUE 0 #define PM_QOS_RESUME_LATENCY_DEFAULT_VALUE PM_QOS_LATENCY_ANY
#define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT PM_QOS_LATENCY_ANY
#define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS PM_QOS_LATENCY_ANY_NS
#define PM_QOS_LATENCY_TOLERANCE_DEFAULT_VALUE 0 #define PM_QOS_LATENCY_TOLERANCE_DEFAULT_VALUE 0
#define PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT (-1) #define PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT (-1)
#define PM_QOS_LATENCY_ANY ((s32)(~(__u32)0 >> 1))
#define PM_QOS_FLAG_NO_POWER_OFF (1 << 0) #define PM_QOS_FLAG_NO_POWER_OFF (1 << 0)
#define PM_QOS_FLAG_REMOTE_WAKEUP (1 << 1)
struct pm_qos_request { struct pm_qos_request {
struct plist_node node; struct plist_node node;
@ -175,7 +177,8 @@ static inline s32 dev_pm_qos_requested_flags(struct device *dev)
static inline s32 dev_pm_qos_raw_read_value(struct device *dev) static inline s32 dev_pm_qos_raw_read_value(struct device *dev)
{ {
return IS_ERR_OR_NULL(dev->power.qos) ? return IS_ERR_OR_NULL(dev->power.qos) ?
0 : pm_qos_read_value(&dev->power.qos->resume_latency); PM_QOS_RESUME_LATENCY_NO_CONSTRAINT :
pm_qos_read_value(&dev->power.qos->resume_latency);
} }
#else #else
static inline enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, static inline enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev,
@ -185,9 +188,9 @@ static inline enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev,
s32 mask) s32 mask)
{ return PM_QOS_FLAGS_UNDEFINED; } { return PM_QOS_FLAGS_UNDEFINED; }
static inline s32 __dev_pm_qos_read_value(struct device *dev) static inline s32 __dev_pm_qos_read_value(struct device *dev)
{ return 0; } { return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; }
static inline s32 dev_pm_qos_read_value(struct device *dev) static inline s32 dev_pm_qos_read_value(struct device *dev)
{ return 0; } { return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; }
static inline int dev_pm_qos_add_request(struct device *dev, static inline int dev_pm_qos_add_request(struct device *dev,
struct dev_pm_qos_request *req, struct dev_pm_qos_request *req,
enum dev_pm_qos_req_type type, enum dev_pm_qos_req_type type,
@ -233,9 +236,15 @@ static inline int dev_pm_qos_expose_latency_tolerance(struct device *dev)
{ return 0; } { return 0; }
static inline void dev_pm_qos_hide_latency_tolerance(struct device *dev) {} static inline void dev_pm_qos_hide_latency_tolerance(struct device *dev) {}
static inline s32 dev_pm_qos_requested_resume_latency(struct device *dev) { return 0; } static inline s32 dev_pm_qos_requested_resume_latency(struct device *dev)
{
return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
}
static inline s32 dev_pm_qos_requested_flags(struct device *dev) { return 0; } static inline s32 dev_pm_qos_requested_flags(struct device *dev) { return 0; }
static inline s32 dev_pm_qos_raw_read_value(struct device *dev) { return 0; } static inline s32 dev_pm_qos_raw_read_value(struct device *dev)
{
return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
}
#endif #endif
#endif #endif

View File

@ -259,20 +259,6 @@ config APM_EMULATION
anything, try disabling/enabling this option (or disabling/enabling anything, try disabling/enabling this option (or disabling/enabling
APM in your BIOS). APM in your BIOS).
config PM_OPP
bool
select SRCU
---help---
SOCs have a standard set of tuples consisting of frequency and
voltage pairs that the device will support per voltage domain. This
is called Operating Performance Point or OPP. The actual definitions
of OPP varies over silicon within the same family of devices.
OPP layer organizes the data internally using device pointers
representing individual voltage domains and provides SOC
implementations a ready to use framework to manage OPPs.
For more information, read <file:Documentation/power/opp.txt>
config PM_CLK config PM_CLK
def_bool y def_bool y
depends on PM && HAVE_CLK depends on PM && HAVE_CLK

View File

@ -701,8 +701,8 @@ static int __init pm_qos_power_init(void)
for (i = PM_QOS_CPU_DMA_LATENCY; i < PM_QOS_NUM_CLASSES; i++) { for (i = PM_QOS_CPU_DMA_LATENCY; i < PM_QOS_NUM_CLASSES; i++) {
ret = register_pm_qos_misc(pm_qos_array[i], d); ret = register_pm_qos_misc(pm_qos_array[i], d);
if (ret < 0) { if (ret < 0) {
printk(KERN_ERR "pm_qos_param: %s setup failed\n", pr_err("%s: %s setup failed\n",
pm_qos_array[i]->name); __func__, pm_qos_array[i]->name);
return ret; return ret;
} }
} }

View File

@ -10,6 +10,8 @@
* *
*/ */
#define pr_fmt(fmt) "PM: " fmt
#include <linux/version.h> #include <linux/version.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mm.h> #include <linux/mm.h>
@ -967,7 +969,7 @@ void __init __register_nosave_region(unsigned long start_pfn,
region->end_pfn = end_pfn; region->end_pfn = end_pfn;
list_add_tail(&region->list, &nosave_regions); list_add_tail(&region->list, &nosave_regions);
Report: Report:
printk(KERN_INFO "PM: Registered nosave memory: [mem %#010llx-%#010llx]\n", pr_info("Registered nosave memory: [mem %#010llx-%#010llx]\n",
(unsigned long long) start_pfn << PAGE_SHIFT, (unsigned long long) start_pfn << PAGE_SHIFT,
((unsigned long long) end_pfn << PAGE_SHIFT) - 1); ((unsigned long long) end_pfn << PAGE_SHIFT) - 1);
} }
@ -1039,7 +1041,7 @@ static void mark_nosave_pages(struct memory_bitmap *bm)
list_for_each_entry(region, &nosave_regions, list) { list_for_each_entry(region, &nosave_regions, list) {
unsigned long pfn; unsigned long pfn;
pr_debug("PM: Marking nosave pages: [mem %#010llx-%#010llx]\n", pr_debug("Marking nosave pages: [mem %#010llx-%#010llx]\n",
(unsigned long long) region->start_pfn << PAGE_SHIFT, (unsigned long long) region->start_pfn << PAGE_SHIFT,
((unsigned long long) region->end_pfn << PAGE_SHIFT) ((unsigned long long) region->end_pfn << PAGE_SHIFT)
- 1); - 1);
@ -1095,7 +1097,7 @@ int create_basic_memory_bitmaps(void)
free_pages_map = bm2; free_pages_map = bm2;
mark_nosave_pages(forbidden_pages_map); mark_nosave_pages(forbidden_pages_map);
pr_debug("PM: Basic memory bitmaps created\n"); pr_debug("Basic memory bitmaps created\n");
return 0; return 0;
@ -1131,7 +1133,7 @@ void free_basic_memory_bitmaps(void)
memory_bm_free(bm2, PG_UNSAFE_CLEAR); memory_bm_free(bm2, PG_UNSAFE_CLEAR);
kfree(bm2); kfree(bm2);
pr_debug("PM: Basic memory bitmaps freed\n"); pr_debug("Basic memory bitmaps freed\n");
} }
void clear_free_pages(void) void clear_free_pages(void)
@ -1152,7 +1154,7 @@ void clear_free_pages(void)
pfn = memory_bm_next_pfn(bm); pfn = memory_bm_next_pfn(bm);
} }
memory_bm_position_reset(bm); memory_bm_position_reset(bm);
pr_info("PM: free pages cleared after restore\n"); pr_info("free pages cleared after restore\n");
#endif /* PAGE_POISONING_ZERO */ #endif /* PAGE_POISONING_ZERO */
} }
@ -1690,7 +1692,7 @@ int hibernate_preallocate_memory(void)
ktime_t start, stop; ktime_t start, stop;
int error; int error;
printk(KERN_INFO "PM: Preallocating image memory... "); pr_info("Preallocating image memory... ");
start = ktime_get(); start = ktime_get();
error = memory_bm_create(&orig_bm, GFP_IMAGE, PG_ANY); error = memory_bm_create(&orig_bm, GFP_IMAGE, PG_ANY);
@ -1821,13 +1823,13 @@ int hibernate_preallocate_memory(void)
out: out:
stop = ktime_get(); stop = ktime_get();
printk(KERN_CONT "done (allocated %lu pages)\n", pages); pr_cont("done (allocated %lu pages)\n", pages);
swsusp_show_speed(start, stop, pages, "Allocated"); swsusp_show_speed(start, stop, pages, "Allocated");
return 0; return 0;
err_out: err_out:
printk(KERN_CONT "\n"); pr_cont("\n");
swsusp_free(); swsusp_free();
return -ENOMEM; return -ENOMEM;
} }
@ -1867,8 +1869,8 @@ static int enough_free_mem(unsigned int nr_pages, unsigned int nr_highmem)
free += zone_page_state(zone, NR_FREE_PAGES); free += zone_page_state(zone, NR_FREE_PAGES);
nr_pages += count_pages_for_highmem(nr_highmem); nr_pages += count_pages_for_highmem(nr_highmem);
pr_debug("PM: Normal pages needed: %u + %u, available pages: %u\n", pr_debug("Normal pages needed: %u + %u, available pages: %u\n",
nr_pages, PAGES_FOR_IO, free); nr_pages, PAGES_FOR_IO, free);
return free > nr_pages + PAGES_FOR_IO; return free > nr_pages + PAGES_FOR_IO;
} }
@ -1961,20 +1963,20 @@ asmlinkage __visible int swsusp_save(void)
{ {
unsigned int nr_pages, nr_highmem; unsigned int nr_pages, nr_highmem;
printk(KERN_INFO "PM: Creating hibernation image:\n"); pr_info("Creating hibernation image:\n");
drain_local_pages(NULL); drain_local_pages(NULL);
nr_pages = count_data_pages(); nr_pages = count_data_pages();
nr_highmem = count_highmem_pages(); nr_highmem = count_highmem_pages();
printk(KERN_INFO "PM: Need to copy %u pages\n", nr_pages + nr_highmem); pr_info("Need to copy %u pages\n", nr_pages + nr_highmem);
if (!enough_free_mem(nr_pages, nr_highmem)) { if (!enough_free_mem(nr_pages, nr_highmem)) {
printk(KERN_ERR "PM: Not enough free memory\n"); pr_err("Not enough free memory\n");
return -ENOMEM; return -ENOMEM;
} }
if (swsusp_alloc(&copy_bm, nr_pages, nr_highmem)) { if (swsusp_alloc(&copy_bm, nr_pages, nr_highmem)) {
printk(KERN_ERR "PM: Memory allocation failed\n"); pr_err("Memory allocation failed\n");
return -ENOMEM; return -ENOMEM;
} }
@ -1995,8 +1997,7 @@ asmlinkage __visible int swsusp_save(void)
nr_copy_pages = nr_pages; nr_copy_pages = nr_pages;
nr_meta_pages = DIV_ROUND_UP(nr_pages * sizeof(long), PAGE_SIZE); nr_meta_pages = DIV_ROUND_UP(nr_pages * sizeof(long), PAGE_SIZE);
printk(KERN_INFO "PM: Hibernation image created (%d pages copied)\n", pr_info("Hibernation image created (%d pages copied)\n", nr_pages);
nr_pages);
return 0; return 0;
} }
@ -2170,7 +2171,7 @@ static int check_header(struct swsusp_info *info)
if (!reason && info->num_physpages != get_num_physpages()) if (!reason && info->num_physpages != get_num_physpages())
reason = "memory size"; reason = "memory size";
if (reason) { if (reason) {
printk(KERN_ERR "PM: Image mismatch: %s\n", reason); pr_err("Image mismatch: %s\n", reason);
return -EPERM; return -EPERM;
} }
return 0; return 0;

View File

@ -437,7 +437,6 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
error = suspend_ops->enter(state); error = suspend_ops->enter(state);
trace_suspend_resume(TPS("machine_suspend"), trace_suspend_resume(TPS("machine_suspend"),
state, false); state, false);
events_check_enabled = false;
} else if (*wakeup) { } else if (*wakeup) {
error = -EBUSY; error = -EBUSY;
} }
@ -582,6 +581,7 @@ static int enter_state(suspend_state_t state)
pm_restore_gfp_mask(); pm_restore_gfp_mask();
Finish: Finish:
events_check_enabled = false;
pm_pr_dbg("Finishing wakeup.\n"); pm_pr_dbg("Finishing wakeup.\n");
suspend_finish(); suspend_finish();
Unlock: Unlock:

View File

@ -12,6 +12,8 @@
* *
*/ */
#define pr_fmt(fmt) "PM: " fmt
#include <linux/module.h> #include <linux/module.h>
#include <linux/file.h> #include <linux/file.h>
#include <linux/delay.h> #include <linux/delay.h>
@ -241,9 +243,9 @@ static void hib_end_io(struct bio *bio)
struct page *page = bio->bi_io_vec[0].bv_page; struct page *page = bio->bi_io_vec[0].bv_page;
if (bio->bi_status) { if (bio->bi_status) {
printk(KERN_ALERT "Read-error on swap-device (%u:%u:%Lu)\n", pr_alert("Read-error on swap-device (%u:%u:%Lu)\n",
MAJOR(bio_dev(bio)), MINOR(bio_dev(bio)), MAJOR(bio_dev(bio)), MINOR(bio_dev(bio)),
(unsigned long long)bio->bi_iter.bi_sector); (unsigned long long)bio->bi_iter.bi_sector);
} }
if (bio_data_dir(bio) == WRITE) if (bio_data_dir(bio) == WRITE)
@ -273,8 +275,8 @@ static int hib_submit_io(int op, int op_flags, pgoff_t page_off, void *addr,
bio_set_op_attrs(bio, op, op_flags); bio_set_op_attrs(bio, op, op_flags);
if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) { if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) {
printk(KERN_ERR "PM: Adding page to bio failed at %llu\n", pr_err("Adding page to bio failed at %llu\n",
(unsigned long long)bio->bi_iter.bi_sector); (unsigned long long)bio->bi_iter.bi_sector);
bio_put(bio); bio_put(bio);
return -EFAULT; return -EFAULT;
} }
@ -319,7 +321,7 @@ static int mark_swapfiles(struct swap_map_handle *handle, unsigned int flags)
error = hib_submit_io(REQ_OP_WRITE, REQ_SYNC, error = hib_submit_io(REQ_OP_WRITE, REQ_SYNC,
swsusp_resume_block, swsusp_header, NULL); swsusp_resume_block, swsusp_header, NULL);
} else { } else {
printk(KERN_ERR "PM: Swap header not found!\n"); pr_err("Swap header not found!\n");
error = -ENODEV; error = -ENODEV;
} }
return error; return error;
@ -413,8 +415,7 @@ static int get_swap_writer(struct swap_map_handle *handle)
ret = swsusp_swap_check(); ret = swsusp_swap_check();
if (ret) { if (ret) {
if (ret != -ENOSPC) if (ret != -ENOSPC)
printk(KERN_ERR "PM: Cannot find swap device, try " pr_err("Cannot find swap device, try swapon -a\n");
"swapon -a.\n");
return ret; return ret;
} }
handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_KERNEL); handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_KERNEL);
@ -491,9 +492,9 @@ static int swap_writer_finish(struct swap_map_handle *handle,
{ {
if (!error) { if (!error) {
flush_swap_writer(handle); flush_swap_writer(handle);
printk(KERN_INFO "PM: S"); pr_info("S");
error = mark_swapfiles(handle, flags); error = mark_swapfiles(handle, flags);
printk("|\n"); pr_cont("|\n");
} }
if (error) if (error)
@ -542,7 +543,7 @@ static int save_image(struct swap_map_handle *handle,
hib_init_batch(&hb); hib_init_batch(&hb);
printk(KERN_INFO "PM: Saving image data pages (%u pages)...\n", pr_info("Saving image data pages (%u pages)...\n",
nr_to_write); nr_to_write);
m = nr_to_write / 10; m = nr_to_write / 10;
if (!m) if (!m)
@ -557,8 +558,8 @@ static int save_image(struct swap_map_handle *handle,
if (ret) if (ret)
break; break;
if (!(nr_pages % m)) if (!(nr_pages % m))
printk(KERN_INFO "PM: Image saving progress: %3d%%\n", pr_info("Image saving progress: %3d%%\n",
nr_pages / m * 10); nr_pages / m * 10);
nr_pages++; nr_pages++;
} }
err2 = hib_wait_io(&hb); err2 = hib_wait_io(&hb);
@ -566,7 +567,7 @@ static int save_image(struct swap_map_handle *handle,
if (!ret) if (!ret)
ret = err2; ret = err2;
if (!ret) if (!ret)
printk(KERN_INFO "PM: Image saving done.\n"); pr_info("Image saving done\n");
swsusp_show_speed(start, stop, nr_to_write, "Wrote"); swsusp_show_speed(start, stop, nr_to_write, "Wrote");
return ret; return ret;
} }
@ -692,14 +693,14 @@ static int save_image_lzo(struct swap_map_handle *handle,
page = (void *)__get_free_page(__GFP_RECLAIM | __GFP_HIGH); page = (void *)__get_free_page(__GFP_RECLAIM | __GFP_HIGH);
if (!page) { if (!page) {
printk(KERN_ERR "PM: Failed to allocate LZO page\n"); pr_err("Failed to allocate LZO page\n");
ret = -ENOMEM; ret = -ENOMEM;
goto out_clean; goto out_clean;
} }
data = vmalloc(sizeof(*data) * nr_threads); data = vmalloc(sizeof(*data) * nr_threads);
if (!data) { if (!data) {
printk(KERN_ERR "PM: Failed to allocate LZO data\n"); pr_err("Failed to allocate LZO data\n");
ret = -ENOMEM; ret = -ENOMEM;
goto out_clean; goto out_clean;
} }
@ -708,7 +709,7 @@ static int save_image_lzo(struct swap_map_handle *handle,
crc = kmalloc(sizeof(*crc), GFP_KERNEL); crc = kmalloc(sizeof(*crc), GFP_KERNEL);
if (!crc) { if (!crc) {
printk(KERN_ERR "PM: Failed to allocate crc\n"); pr_err("Failed to allocate crc\n");
ret = -ENOMEM; ret = -ENOMEM;
goto out_clean; goto out_clean;
} }
@ -726,8 +727,7 @@ static int save_image_lzo(struct swap_map_handle *handle,
"image_compress/%u", thr); "image_compress/%u", thr);
if (IS_ERR(data[thr].thr)) { if (IS_ERR(data[thr].thr)) {
data[thr].thr = NULL; data[thr].thr = NULL;
printk(KERN_ERR pr_err("Cannot start compression threads\n");
"PM: Cannot start compression threads\n");
ret = -ENOMEM; ret = -ENOMEM;
goto out_clean; goto out_clean;
} }
@ -749,7 +749,7 @@ static int save_image_lzo(struct swap_map_handle *handle,
crc->thr = kthread_run(crc32_threadfn, crc, "image_crc32"); crc->thr = kthread_run(crc32_threadfn, crc, "image_crc32");
if (IS_ERR(crc->thr)) { if (IS_ERR(crc->thr)) {
crc->thr = NULL; crc->thr = NULL;
printk(KERN_ERR "PM: Cannot start CRC32 thread\n"); pr_err("Cannot start CRC32 thread\n");
ret = -ENOMEM; ret = -ENOMEM;
goto out_clean; goto out_clean;
} }
@ -760,10 +760,9 @@ static int save_image_lzo(struct swap_map_handle *handle,
*/ */
handle->reqd_free_pages = reqd_free_pages(); handle->reqd_free_pages = reqd_free_pages();
printk(KERN_INFO pr_info("Using %u thread(s) for compression\n", nr_threads);
"PM: Using %u thread(s) for compression.\n" pr_info("Compressing and saving image data (%u pages)...\n",
"PM: Compressing and saving image data (%u pages)...\n", nr_to_write);
nr_threads, nr_to_write);
m = nr_to_write / 10; m = nr_to_write / 10;
if (!m) if (!m)
m = 1; m = 1;
@ -783,10 +782,8 @@ static int save_image_lzo(struct swap_map_handle *handle,
data_of(*snapshot), PAGE_SIZE); data_of(*snapshot), PAGE_SIZE);
if (!(nr_pages % m)) if (!(nr_pages % m))
printk(KERN_INFO pr_info("Image saving progress: %3d%%\n",
"PM: Image saving progress: " nr_pages / m * 10);
"%3d%%\n",
nr_pages / m * 10);
nr_pages++; nr_pages++;
} }
if (!off) if (!off)
@ -813,15 +810,14 @@ static int save_image_lzo(struct swap_map_handle *handle,
ret = data[thr].ret; ret = data[thr].ret;
if (ret < 0) { if (ret < 0) {
printk(KERN_ERR "PM: LZO compression failed\n"); pr_err("LZO compression failed\n");
goto out_finish; goto out_finish;
} }
if (unlikely(!data[thr].cmp_len || if (unlikely(!data[thr].cmp_len ||
data[thr].cmp_len > data[thr].cmp_len >
lzo1x_worst_compress(data[thr].unc_len))) { lzo1x_worst_compress(data[thr].unc_len))) {
printk(KERN_ERR pr_err("Invalid LZO compressed length\n");
"PM: Invalid LZO compressed length\n");
ret = -1; ret = -1;
goto out_finish; goto out_finish;
} }
@ -857,7 +853,7 @@ out_finish:
if (!ret) if (!ret)
ret = err2; ret = err2;
if (!ret) if (!ret)
printk(KERN_INFO "PM: Image saving done.\n"); pr_info("Image saving done\n");
swsusp_show_speed(start, stop, nr_to_write, "Wrote"); swsusp_show_speed(start, stop, nr_to_write, "Wrote");
out_clean: out_clean:
if (crc) { if (crc) {
@ -888,7 +884,7 @@ static int enough_swap(unsigned int nr_pages, unsigned int flags)
unsigned int free_swap = count_swap_pages(root_swap, 1); unsigned int free_swap = count_swap_pages(root_swap, 1);
unsigned int required; unsigned int required;
pr_debug("PM: Free swap pages: %u\n", free_swap); pr_debug("Free swap pages: %u\n", free_swap);
required = PAGES_FOR_IO + nr_pages; required = PAGES_FOR_IO + nr_pages;
return free_swap > required; return free_swap > required;
@ -915,12 +911,12 @@ int swsusp_write(unsigned int flags)
pages = snapshot_get_image_size(); pages = snapshot_get_image_size();
error = get_swap_writer(&handle); error = get_swap_writer(&handle);
if (error) { if (error) {
printk(KERN_ERR "PM: Cannot get swap writer\n"); pr_err("Cannot get swap writer\n");
return error; return error;
} }
if (flags & SF_NOCOMPRESS_MODE) { if (flags & SF_NOCOMPRESS_MODE) {
if (!enough_swap(pages, flags)) { if (!enough_swap(pages, flags)) {
printk(KERN_ERR "PM: Not enough free swap\n"); pr_err("Not enough free swap\n");
error = -ENOSPC; error = -ENOSPC;
goto out_finish; goto out_finish;
} }
@ -1068,8 +1064,7 @@ static int load_image(struct swap_map_handle *handle,
hib_init_batch(&hb); hib_init_batch(&hb);
clean_pages_on_read = true; clean_pages_on_read = true;
printk(KERN_INFO "PM: Loading image data pages (%u pages)...\n", pr_info("Loading image data pages (%u pages)...\n", nr_to_read);
nr_to_read);
m = nr_to_read / 10; m = nr_to_read / 10;
if (!m) if (!m)
m = 1; m = 1;
@ -1087,8 +1082,8 @@ static int load_image(struct swap_map_handle *handle,
if (ret) if (ret)
break; break;
if (!(nr_pages % m)) if (!(nr_pages % m))
printk(KERN_INFO "PM: Image loading progress: %3d%%\n", pr_info("Image loading progress: %3d%%\n",
nr_pages / m * 10); nr_pages / m * 10);
nr_pages++; nr_pages++;
} }
err2 = hib_wait_io(&hb); err2 = hib_wait_io(&hb);
@ -1096,7 +1091,7 @@ static int load_image(struct swap_map_handle *handle,
if (!ret) if (!ret)
ret = err2; ret = err2;
if (!ret) { if (!ret) {
printk(KERN_INFO "PM: Image loading done.\n"); pr_info("Image loading done\n");
snapshot_write_finalize(snapshot); snapshot_write_finalize(snapshot);
if (!snapshot_image_loaded(snapshot)) if (!snapshot_image_loaded(snapshot))
ret = -ENODATA; ret = -ENODATA;
@ -1190,14 +1185,14 @@ static int load_image_lzo(struct swap_map_handle *handle,
page = vmalloc(sizeof(*page) * LZO_MAX_RD_PAGES); page = vmalloc(sizeof(*page) * LZO_MAX_RD_PAGES);
if (!page) { if (!page) {
printk(KERN_ERR "PM: Failed to allocate LZO page\n"); pr_err("Failed to allocate LZO page\n");
ret = -ENOMEM; ret = -ENOMEM;
goto out_clean; goto out_clean;
} }
data = vmalloc(sizeof(*data) * nr_threads); data = vmalloc(sizeof(*data) * nr_threads);
if (!data) { if (!data) {
printk(KERN_ERR "PM: Failed to allocate LZO data\n"); pr_err("Failed to allocate LZO data\n");
ret = -ENOMEM; ret = -ENOMEM;
goto out_clean; goto out_clean;
} }
@ -1206,7 +1201,7 @@ static int load_image_lzo(struct swap_map_handle *handle,
crc = kmalloc(sizeof(*crc), GFP_KERNEL); crc = kmalloc(sizeof(*crc), GFP_KERNEL);
if (!crc) { if (!crc) {
printk(KERN_ERR "PM: Failed to allocate crc\n"); pr_err("Failed to allocate crc\n");
ret = -ENOMEM; ret = -ENOMEM;
goto out_clean; goto out_clean;
} }
@ -1226,8 +1221,7 @@ static int load_image_lzo(struct swap_map_handle *handle,
"image_decompress/%u", thr); "image_decompress/%u", thr);
if (IS_ERR(data[thr].thr)) { if (IS_ERR(data[thr].thr)) {
data[thr].thr = NULL; data[thr].thr = NULL;
printk(KERN_ERR pr_err("Cannot start decompression threads\n");
"PM: Cannot start decompression threads\n");
ret = -ENOMEM; ret = -ENOMEM;
goto out_clean; goto out_clean;
} }
@ -1249,7 +1243,7 @@ static int load_image_lzo(struct swap_map_handle *handle,
crc->thr = kthread_run(crc32_threadfn, crc, "image_crc32"); crc->thr = kthread_run(crc32_threadfn, crc, "image_crc32");
if (IS_ERR(crc->thr)) { if (IS_ERR(crc->thr)) {
crc->thr = NULL; crc->thr = NULL;
printk(KERN_ERR "PM: Cannot start CRC32 thread\n"); pr_err("Cannot start CRC32 thread\n");
ret = -ENOMEM; ret = -ENOMEM;
goto out_clean; goto out_clean;
} }
@ -1274,8 +1268,7 @@ static int load_image_lzo(struct swap_map_handle *handle,
if (!page[i]) { if (!page[i]) {
if (i < LZO_CMP_PAGES) { if (i < LZO_CMP_PAGES) {
ring_size = i; ring_size = i;
printk(KERN_ERR pr_err("Failed to allocate LZO pages\n");
"PM: Failed to allocate LZO pages\n");
ret = -ENOMEM; ret = -ENOMEM;
goto out_clean; goto out_clean;
} else { } else {
@ -1285,10 +1278,9 @@ static int load_image_lzo(struct swap_map_handle *handle,
} }
want = ring_size = i; want = ring_size = i;
printk(KERN_INFO pr_info("Using %u thread(s) for decompression\n", nr_threads);
"PM: Using %u thread(s) for decompression.\n" pr_info("Loading and decompressing image data (%u pages)...\n",
"PM: Loading and decompressing image data (%u pages)...\n", nr_to_read);
nr_threads, nr_to_read);
m = nr_to_read / 10; m = nr_to_read / 10;
if (!m) if (!m)
m = 1; m = 1;
@ -1348,8 +1340,7 @@ static int load_image_lzo(struct swap_map_handle *handle,
if (unlikely(!data[thr].cmp_len || if (unlikely(!data[thr].cmp_len ||
data[thr].cmp_len > data[thr].cmp_len >
lzo1x_worst_compress(LZO_UNC_SIZE))) { lzo1x_worst_compress(LZO_UNC_SIZE))) {
printk(KERN_ERR pr_err("Invalid LZO compressed length\n");
"PM: Invalid LZO compressed length\n");
ret = -1; ret = -1;
goto out_finish; goto out_finish;
} }
@ -1400,16 +1391,14 @@ static int load_image_lzo(struct swap_map_handle *handle,
ret = data[thr].ret; ret = data[thr].ret;
if (ret < 0) { if (ret < 0) {
printk(KERN_ERR pr_err("LZO decompression failed\n");
"PM: LZO decompression failed\n");
goto out_finish; goto out_finish;
} }
if (unlikely(!data[thr].unc_len || if (unlikely(!data[thr].unc_len ||
data[thr].unc_len > LZO_UNC_SIZE || data[thr].unc_len > LZO_UNC_SIZE ||
data[thr].unc_len & (PAGE_SIZE - 1))) { data[thr].unc_len & (PAGE_SIZE - 1))) {
printk(KERN_ERR pr_err("Invalid LZO uncompressed length\n");
"PM: Invalid LZO uncompressed length\n");
ret = -1; ret = -1;
goto out_finish; goto out_finish;
} }
@ -1420,10 +1409,8 @@ static int load_image_lzo(struct swap_map_handle *handle,
data[thr].unc + off, PAGE_SIZE); data[thr].unc + off, PAGE_SIZE);
if (!(nr_pages % m)) if (!(nr_pages % m))
printk(KERN_INFO pr_info("Image loading progress: %3d%%\n",
"PM: Image loading progress: " nr_pages / m * 10);
"%3d%%\n",
nr_pages / m * 10);
nr_pages++; nr_pages++;
ret = snapshot_write_next(snapshot); ret = snapshot_write_next(snapshot);
@ -1448,15 +1435,14 @@ out_finish:
} }
stop = ktime_get(); stop = ktime_get();
if (!ret) { if (!ret) {
printk(KERN_INFO "PM: Image loading done.\n"); pr_info("Image loading done\n");
snapshot_write_finalize(snapshot); snapshot_write_finalize(snapshot);
if (!snapshot_image_loaded(snapshot)) if (!snapshot_image_loaded(snapshot))
ret = -ENODATA; ret = -ENODATA;
if (!ret) { if (!ret) {
if (swsusp_header->flags & SF_CRC32_MODE) { if (swsusp_header->flags & SF_CRC32_MODE) {
if(handle->crc32 != swsusp_header->crc32) { if(handle->crc32 != swsusp_header->crc32) {
printk(KERN_ERR pr_err("Invalid image CRC32!\n");
"PM: Invalid image CRC32!\n");
ret = -ENODATA; ret = -ENODATA;
} }
} }
@ -1513,9 +1499,9 @@ int swsusp_read(unsigned int *flags_p)
swap_reader_finish(&handle); swap_reader_finish(&handle);
end: end:
if (!error) if (!error)
pr_debug("PM: Image successfully loaded\n"); pr_debug("Image successfully loaded\n");
else else
pr_debug("PM: Error %d resuming\n", error); pr_debug("Error %d resuming\n", error);
return error; return error;
} }
@ -1552,13 +1538,13 @@ put:
if (error) if (error)
blkdev_put(hib_resume_bdev, FMODE_READ); blkdev_put(hib_resume_bdev, FMODE_READ);
else else
pr_debug("PM: Image signature found, resuming\n"); pr_debug("Image signature found, resuming\n");
} else { } else {
error = PTR_ERR(hib_resume_bdev); error = PTR_ERR(hib_resume_bdev);
} }
if (error) if (error)
pr_debug("PM: Image not found (code %d)\n", error); pr_debug("Image not found (code %d)\n", error);
return error; return error;
} }
@ -1570,7 +1556,7 @@ put:
void swsusp_close(fmode_t mode) void swsusp_close(fmode_t mode)
{ {
if (IS_ERR(hib_resume_bdev)) { if (IS_ERR(hib_resume_bdev)) {
pr_debug("PM: Image device not initialised\n"); pr_debug("Image device not initialised\n");
return; return;
} }
@ -1594,7 +1580,7 @@ int swsusp_unmark(void)
swsusp_resume_block, swsusp_resume_block,
swsusp_header, NULL); swsusp_header, NULL);
} else { } else {
printk(KERN_ERR "PM: Cannot find swsusp signature!\n"); pr_err("Cannot find swsusp signature!\n");
error = -ENODEV; error = -ENODEV;
} }

View File

@ -282,8 +282,12 @@ static void sugov_update_single(struct update_util_data *hook, u64 time,
* Do not reduce the frequency if the CPU has not been idle * Do not reduce the frequency if the CPU has not been idle
* recently, as the reduction is likely to be premature then. * recently, as the reduction is likely to be premature then.
*/ */
if (busy && next_f < sg_policy->next_freq) if (busy && next_f < sg_policy->next_freq) {
next_f = sg_policy->next_freq; next_f = sg_policy->next_freq;
/* Reset cached freq as next_freq has changed */
sg_policy->cached_raw_freq = 0;
}
} }
sugov_update_commit(sg_policy, time, next_f); sugov_update_commit(sg_policy, time, next_f);
} }

View File

@ -1,7 +1,6 @@
.libs .libs
libcpupower.so libcpupower.so
libcpupower.so.0 libcpupower.so.*
libcpupower.so.0.0.0
build/ccdv build/ccdv
cpufreq-info cpufreq-info
cpufreq-set cpufreq-set

View File

@ -30,6 +30,8 @@ OUTDIR := $(shell cd $(OUTPUT) && /bin/pwd)
$(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist)) $(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist))
endif endif
include ../../scripts/Makefile.arch
# --- CONFIGURATION BEGIN --- # --- CONFIGURATION BEGIN ---
# Set the following to `true' to make a unstripped, unoptimized # Set the following to `true' to make a unstripped, unoptimized
@ -79,7 +81,11 @@ bindir ?= /usr/bin
sbindir ?= /usr/sbin sbindir ?= /usr/sbin
mandir ?= /usr/man mandir ?= /usr/man
includedir ?= /usr/include includedir ?= /usr/include
ifeq ($(IS_64_BIT), 1)
libdir ?= /usr/lib64
else
libdir ?= /usr/lib libdir ?= /usr/lib
endif
localedir ?= /usr/share/locale localedir ?= /usr/share/locale
docdir ?= /usr/share/doc/packages/cpupower docdir ?= /usr/share/doc/packages/cpupower
confdir ?= /etc/ confdir ?= /etc/

View File

@ -93,8 +93,6 @@ static void print_speed(unsigned long speed)
if (speed > 1000000) if (speed > 1000000)
printf("%u.%06u GHz", ((unsigned int) speed/1000000), printf("%u.%06u GHz", ((unsigned int) speed/1000000),
((unsigned int) speed%1000000)); ((unsigned int) speed%1000000));
else if (speed > 100000)
printf("%u MHz", (unsigned int) speed);
else if (speed > 1000) else if (speed > 1000)
printf("%u.%03u MHz", ((unsigned int) speed/1000), printf("%u.%03u MHz", ((unsigned int) speed/1000),
(unsigned int) (speed%1000)); (unsigned int) (speed%1000));