mirror of
https://github.com/FEX-Emu/linux.git
synced 2024-12-15 05:11:32 +00:00
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:
commit
bd2cd7d5a8
@ -211,7 +211,9 @@ Description:
|
||||
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,
|
||||
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,
|
||||
it is not present.
|
||||
@ -258,19 +260,3 @@ Description:
|
||||
|
||||
This attribute has no effect on system-wide suspend/resume and
|
||||
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.
|
||||
|
25
Documentation/acpi/lpit.txt
Normal file
25
Documentation/acpi/lpit.txt
Normal 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.
|
@ -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
|
||||
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
|
||||
From : To
|
||||
|
@ -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
|
||||
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
|
||||
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
|
||||
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
|
||||
registered below the device. The method may also prepare the device or
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
"quiesce device" and "save device state" phases, in which cases
|
||||
``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
|
||||
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
|
||||
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
|
||||
|
@ -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
|
||||
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
|
||||
------------------------------------
|
||||
In addition to providing device power management callbacks PCI device drivers
|
||||
|
@ -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
|
||||
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'
|
||||
values. Two device PM QoS flags are defined currently: PM_QOS_FLAG_NO_POWER_OFF
|
||||
and PM_QOS_FLAG_REMOTE_WAKEUP.
|
||||
values. One device PM QoS flag is defined currently: PM_QOS_FLAG_NO_POWER_OFF.
|
||||
|
||||
Note: The aggregated target values are implemented in such a way that reading
|
||||
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.
|
||||
|
||||
int dev_pm_qos_expose_flags(device, value)
|
||||
Add a request to the device's PM QoS list of flags and create sysfs attributes
|
||||
pm_qos_no_power_off and pm_qos_remote_wakeup under the device's power directory
|
||||
allowing user space to change these flags' value.
|
||||
Add a request to the device's PM QoS list of flags and create sysfs attribute
|
||||
pm_qos_no_power_off under the device's power directory allowing user space to
|
||||
change the value of the PM_QOS_FLAG_NO_POWER_OFF flag.
|
||||
|
||||
void dev_pm_qos_hide_flags(device)
|
||||
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
|
||||
under the device's power directory.
|
||||
of flags and remove sysfs attribute pm_qos_no_power_off from the device's power
|
||||
directory.
|
||||
|
||||
Notification mechanisms:
|
||||
The per-device PM QoS framework has a per-device notification tree.
|
||||
|
@ -3637,6 +3637,8 @@ F: drivers/cpufreq/arm_big_little_dt.c
|
||||
|
||||
CPU POWER MONITORING SUBSYSTEM
|
||||
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
|
||||
S: Maintained
|
||||
F: tools/power/cpupower/
|
||||
@ -10060,7 +10062,7 @@ M: Stephen Boyd <sboyd@codeaurora.org>
|
||||
L: linux-pm@vger.kernel.org
|
||||
S: Maintained
|
||||
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: Documentation/power/opp.txt
|
||||
F: Documentation/devicetree/bindings/opp/
|
||||
|
@ -826,28 +826,6 @@ static int locomo_match(struct device *_dev, struct device_driver *_drv)
|
||||
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)
|
||||
{
|
||||
struct locomo_dev *ldev = LOCOMO_DEV(dev);
|
||||
@ -875,8 +853,6 @@ struct bus_type locomo_bus_type = {
|
||||
.match = locomo_match,
|
||||
.probe = locomo_bus_probe,
|
||||
.remove = locomo_bus_remove,
|
||||
.suspend = locomo_bus_suspend,
|
||||
.resume = locomo_bus_resume,
|
||||
};
|
||||
|
||||
int locomo_driver_register(struct locomo_driver *driver)
|
||||
|
@ -189,8 +189,6 @@ struct locomo_driver {
|
||||
unsigned int devid;
|
||||
int (*probe)(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)
|
||||
|
@ -25,6 +25,14 @@ void init_cpu_topology(void);
|
||||
void store_cpu_topology(unsigned int cpuid);
|
||||
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
|
||||
|
||||
static inline void init_cpu_topology(void) { }
|
||||
|
@ -286,88 +286,6 @@ static void __init imx6q_init_machine(void)
|
||||
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)
|
||||
{
|
||||
/*
|
||||
@ -377,10 +295,8 @@ static void __init imx6q_init_late(void)
|
||||
if (imx_get_soc_revision() > IMX_CHIP_REVISION_1_1)
|
||||
imx6q_cpuidle_init();
|
||||
|
||||
if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ)) {
|
||||
imx6q_opp_init();
|
||||
platform_device_register(&imx6q_cpufreq_pdev);
|
||||
}
|
||||
if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ))
|
||||
platform_device_register_simple("imx6q-cpufreq", -1, NULL, 0);
|
||||
}
|
||||
|
||||
static void __init imx6q_map_io(void)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
static bool rmobile_pd_active_wakeup(struct device *dev)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd)
|
||||
{
|
||||
struct generic_pm_domain *genpd = &rmobile_pd->genpd;
|
||||
struct dev_power_governor *gov = rmobile_pd->gov;
|
||||
|
||||
genpd->flags |= GENPD_FLAG_PM_CLK;
|
||||
genpd->dev_ops.active_wakeup = rmobile_pd_active_wakeup;
|
||||
genpd->flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP;
|
||||
genpd->power_off = rmobile_pd_power_down;
|
||||
genpd->power_on = rmobile_pd_power_up;
|
||||
genpd->attach_dev = cpg_mstp_attach_dev;
|
||||
|
@ -33,6 +33,14 @@ int pcibus_to_node(struct pci_bus *bus);
|
||||
|
||||
#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>
|
||||
|
||||
#endif /* _ASM_ARM_TOPOLOGY_H */
|
||||
|
@ -209,4 +209,6 @@ source "drivers/tee/Kconfig"
|
||||
|
||||
source "drivers/mux/Kconfig"
|
||||
|
||||
source "drivers/opp/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
@ -126,6 +126,7 @@ obj-$(CONFIG_ACCESSIBILITY) += accessibility/
|
||||
obj-$(CONFIG_ISDN) += isdn/
|
||||
obj-$(CONFIG_EDAC) += edac/
|
||||
obj-$(CONFIG_EISA) += eisa/
|
||||
obj-$(CONFIG_PM_OPP) += opp/
|
||||
obj-$(CONFIG_CPU_FREQ) += cpufreq/
|
||||
obj-$(CONFIG_CPU_IDLE) += cpuidle/
|
||||
obj-y += mmc/
|
||||
|
@ -81,6 +81,11 @@ endif
|
||||
config ACPI_SPCR_TABLE
|
||||
bool
|
||||
|
||||
config ACPI_LPIT
|
||||
bool
|
||||
depends on X86_64
|
||||
default y
|
||||
|
||||
config ACPI_SLEEP
|
||||
bool
|
||||
depends on SUSPEND || HIBERNATION
|
||||
|
@ -57,6 +57,7 @@ acpi-$(CONFIG_DEBUG_FS) += debugfs.o
|
||||
acpi-$(CONFIG_ACPI_NUMA) += numa.o
|
||||
acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o
|
||||
acpi-y += acpi_lpat.o
|
||||
acpi-$(CONFIG_ACPI_LPIT) += acpi_lpit.o
|
||||
acpi-$(CONFIG_ACPI_GENERIC_GSI) += irq.o
|
||||
acpi-$(CONFIG_ACPI_WATCHDOG) += acpi_watchdog.o
|
||||
|
||||
|
162
drivers/acpi/acpi_lpit.c
Normal file
162
drivers/acpi/acpi_lpit.c
Normal 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);
|
||||
}
|
@ -693,7 +693,7 @@ static int acpi_lpss_activate(struct device *dev)
|
||||
struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev));
|
||||
int ret;
|
||||
|
||||
ret = acpi_dev_runtime_resume(dev);
|
||||
ret = acpi_dev_resume(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -713,43 +713,9 @@ static int acpi_lpss_activate(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 */
|
||||
#define LPSS_IOSF_UNIT_LPIOEP 0xA0
|
||||
#define LPSS_IOSF_UNIT_LPIO1 0xAB
|
||||
@ -835,19 +801,15 @@ static void lpss_iosf_exit_d3_state(void)
|
||||
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));
|
||||
int ret;
|
||||
|
||||
ret = pm_generic_runtime_suspend(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
|
||||
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
|
||||
@ -860,7 +822,7 @@ static int acpi_lpss_runtime_suspend(struct device *dev)
|
||||
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));
|
||||
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())
|
||||
lpss_iosf_exit_d3_state();
|
||||
|
||||
ret = acpi_dev_runtime_resume(dev);
|
||||
ret = acpi_dev_resume(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -881,7 +843,41 @@ static int acpi_lpss_runtime_resume(struct device *dev)
|
||||
if (pdata->dev_desc->flags & LPSS_SAVE_CTX)
|
||||
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 */
|
||||
|
||||
@ -894,13 +890,20 @@ static struct dev_pm_domain acpi_lpss_pm_domain = {
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.prepare = acpi_subsys_prepare,
|
||||
.complete = pm_complete_with_resume_check,
|
||||
.complete = acpi_subsys_complete,
|
||||
.suspend = acpi_subsys_suspend,
|
||||
.suspend_late = acpi_lpss_suspend_late,
|
||||
.suspend_noirq = acpi_subsys_suspend_noirq,
|
||||
.resume_noirq = acpi_subsys_resume_noirq,
|
||||
.resume_early = acpi_lpss_resume_early,
|
||||
.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_late = acpi_lpss_suspend_late,
|
||||
.poweroff_noirq = acpi_subsys_suspend_noirq,
|
||||
.restore_noirq = acpi_subsys_resume_noirq,
|
||||
.restore_early = acpi_lpss_resume_early,
|
||||
#endif
|
||||
.runtime_suspend = acpi_lpss_runtime_suspend,
|
||||
|
@ -387,6 +387,7 @@ EXPORT_SYMBOL(acpi_bus_power_manageable);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static DEFINE_MUTEX(acpi_pm_notifier_lock);
|
||||
static DEFINE_MUTEX(acpi_pm_notifier_install_lock);
|
||||
|
||||
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)
|
||||
return AE_BAD_PARAMETER;
|
||||
|
||||
mutex_lock(&acpi_pm_notifier_lock);
|
||||
mutex_lock(&acpi_pm_notifier_install_lock);
|
||||
|
||||
if (adev->wakeup.flags.notifier_present)
|
||||
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,
|
||||
acpi_pm_notify_handler, NULL);
|
||||
if (ACPI_FAILURE(status))
|
||||
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;
|
||||
mutex_unlock(&acpi_pm_notifier_lock);
|
||||
|
||||
out:
|
||||
mutex_unlock(&acpi_pm_notifier_lock);
|
||||
mutex_unlock(&acpi_pm_notifier_install_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -472,7 +474,7 @@ acpi_status acpi_remove_pm_notifier(struct acpi_device *adev)
|
||||
{
|
||||
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)
|
||||
goto out;
|
||||
@ -483,14 +485,15 @@ acpi_status acpi_remove_pm_notifier(struct acpi_device *adev)
|
||||
if (ACPI_FAILURE(status))
|
||||
goto out;
|
||||
|
||||
mutex_lock(&acpi_pm_notifier_lock);
|
||||
adev->wakeup.context.func = NULL;
|
||||
adev->wakeup.context.dev = NULL;
|
||||
wakeup_source_unregister(adev->wakeup.ws);
|
||||
|
||||
adev->wakeup.flags.notifier_present = false;
|
||||
mutex_unlock(&acpi_pm_notifier_lock);
|
||||
|
||||
out:
|
||||
mutex_unlock(&acpi_pm_notifier_lock);
|
||||
mutex_unlock(&acpi_pm_notifier_install_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -581,8 +584,7 @@ static int acpi_dev_pm_get_state(struct device *dev, struct acpi_device *adev,
|
||||
d_min = ret;
|
||||
wakeup = device_may_wakeup(dev) && adev->wakeup.flags.valid
|
||||
&& adev->wakeup.sleep_state >= target_state;
|
||||
} else if (dev_pm_qos_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP) !=
|
||||
PM_QOS_FLAGS_NONE) {
|
||||
} else {
|
||||
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.
|
||||
* @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
|
||||
* device into (this checks if remote wakeup is expected to work too), and set
|
||||
* 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);
|
||||
bool remote_wakeup;
|
||||
u32 target_state = acpi_target_system_state();
|
||||
int error;
|
||||
|
||||
if (!adev)
|
||||
return 0;
|
||||
|
||||
remote_wakeup = dev_pm_qos_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP) >
|
||||
PM_QOS_FLAGS_NONE;
|
||||
if (remote_wakeup) {
|
||||
error = acpi_device_wakeup_enable(adev, ACPI_STATE_S0);
|
||||
if (wakeup && acpi_device_can_wakeup(adev)) {
|
||||
error = acpi_device_wakeup_enable(adev, target_state);
|
||||
if (error)
|
||||
return -EAGAIN;
|
||||
} else {
|
||||
wakeup = false;
|
||||
}
|
||||
|
||||
error = acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0);
|
||||
if (error && remote_wakeup)
|
||||
error = acpi_dev_pm_low_power(dev, adev, target_state);
|
||||
if (error && wakeup)
|
||||
acpi_device_wakeup_disable(adev);
|
||||
|
||||
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.
|
||||
*
|
||||
* 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
|
||||
* disable remote wakeup.
|
||||
* mechanism. Set the power state of the device to ACPI D0 and disable wakeup.
|
||||
*/
|
||||
int acpi_dev_runtime_resume(struct device *dev)
|
||||
int acpi_dev_resume(struct device *dev)
|
||||
{
|
||||
struct acpi_device *adev = ACPI_COMPANION(dev);
|
||||
int error;
|
||||
@ -901,7 +903,7 @@ int acpi_dev_runtime_resume(struct device *dev)
|
||||
acpi_device_wakeup_disable(adev);
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_dev_runtime_resume);
|
||||
EXPORT_SYMBOL_GPL(acpi_dev_resume);
|
||||
|
||||
/**
|
||||
* 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 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);
|
||||
|
||||
@ -926,68 +928,33 @@ EXPORT_SYMBOL_GPL(acpi_subsys_runtime_suspend);
|
||||
*/
|
||||
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);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_subsys_runtime_resume);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/**
|
||||
* 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)
|
||||
static bool acpi_dev_needs_resume(struct device *dev, struct acpi_device *adev)
|
||||
{
|
||||
struct acpi_device *adev = ACPI_COMPANION(dev);
|
||||
u32 target_state;
|
||||
bool wakeup;
|
||||
int error;
|
||||
u32 sys_target = acpi_target_system_state();
|
||||
int ret, state;
|
||||
|
||||
if (!adev)
|
||||
return 0;
|
||||
if (!pm_runtime_suspended(dev) || !adev ||
|
||||
device_may_wakeup(dev) != !!adev->wakeup.prepare_count)
|
||||
return true;
|
||||
|
||||
target_state = acpi_target_system_state();
|
||||
wakeup = device_may_wakeup(dev) && acpi_device_can_wakeup(adev);
|
||||
if (wakeup) {
|
||||
error = acpi_device_wakeup_enable(adev, target_state);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
if (sys_target == ACPI_STATE_S0)
|
||||
return false;
|
||||
|
||||
error = acpi_dev_pm_low_power(dev, adev, target_state);
|
||||
if (error && wakeup)
|
||||
acpi_device_wakeup_disable(adev);
|
||||
if (adev->power.flags.dsw_present)
|
||||
return true;
|
||||
|
||||
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.
|
||||
@ -996,39 +963,53 @@ EXPORT_SYMBOL_GPL(acpi_dev_resume_early);
|
||||
int acpi_subsys_prepare(struct device *dev)
|
||||
{
|
||||
struct acpi_device *adev = ACPI_COMPANION(dev);
|
||||
u32 sys_target;
|
||||
int ret, state;
|
||||
|
||||
ret = pm_generic_prepare(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (dev->driver && dev->driver->pm && dev->driver->pm->prepare) {
|
||||
int ret = dev->driver->pm->prepare(dev);
|
||||
|
||||
if (!adev || !pm_runtime_suspended(dev)
|
||||
|| device_may_wakeup(dev) != !!adev->wakeup.prepare_count)
|
||||
return 0;
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
sys_target = acpi_target_system_state();
|
||||
if (sys_target == ACPI_STATE_S0)
|
||||
return 1;
|
||||
if (!ret && dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_PREPARE))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (adev->power.flags.dsw_present)
|
||||
return 0;
|
||||
|
||||
ret = acpi_dev_pm_get_state(dev, adev, sys_target, NULL, &state);
|
||||
return !ret && state == adev->power.state;
|
||||
return !acpi_dev_needs_resume(dev, adev);
|
||||
}
|
||||
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.
|
||||
* @dev: Device to handle.
|
||||
*
|
||||
* Follow PCI and resume devices suspended at run time before running their
|
||||
* system suspend callbacks.
|
||||
* Follow PCI and resume devices from runtime suspend before running their
|
||||
* 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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
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 ret = pm_generic_suspend_late(dev);
|
||||
return ret ? ret : acpi_dev_suspend_late(dev);
|
||||
int ret;
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @dev: Device to Resume.
|
||||
@ -1057,7 +1074,7 @@ EXPORT_SYMBOL_GPL(acpi_subsys_suspend_late);
|
||||
*/
|
||||
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);
|
||||
}
|
||||
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
|
||||
* 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);
|
||||
}
|
||||
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 */
|
||||
|
||||
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,
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.prepare = acpi_subsys_prepare,
|
||||
.complete = pm_complete_with_resume_check,
|
||||
.complete = acpi_subsys_complete,
|
||||
.suspend = acpi_subsys_suspend,
|
||||
.suspend_late = acpi_subsys_suspend_late,
|
||||
.suspend_noirq = acpi_subsys_suspend_noirq,
|
||||
.resume_noirq = acpi_subsys_resume_noirq,
|
||||
.resume_early = acpi_subsys_resume_early,
|
||||
.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_late = acpi_subsys_suspend_late,
|
||||
.poweroff_noirq = acpi_subsys_suspend_noirq,
|
||||
.restore_noirq = acpi_subsys_resume_noirq,
|
||||
.restore_early = acpi_subsys_resume_early,
|
||||
#endif
|
||||
},
|
||||
|
@ -248,4 +248,10 @@ void acpi_watchdog_init(void);
|
||||
static inline void acpi_watchdog_init(void) {}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI_LPIT
|
||||
void acpi_init_lpit(void);
|
||||
#else
|
||||
static inline void acpi_init_lpit(void) { }
|
||||
#endif
|
||||
|
||||
#endif /* _ACPI_INTERNAL_H_ */
|
||||
|
@ -663,26 +663,8 @@ acpi_status acpi_os_write_port(acpi_io_address port, u32 value, u32 width)
|
||||
|
||||
EXPORT_SYMBOL(acpi_os_write_port);
|
||||
|
||||
acpi_status
|
||||
acpi_os_read_memory(acpi_physical_address phys_addr, u64 *value, u32 width)
|
||||
int acpi_os_read_iomem(void __iomem *virt_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) {
|
||||
case 8:
|
||||
@ -698,9 +680,37 @@ acpi_os_read_memory(acpi_physical_address phys_addr, u64 *value, u32 width)
|
||||
*(u64 *) value = readq(virt_addr);
|
||||
break;
|
||||
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)
|
||||
iounmap(virt_addr);
|
||||
else
|
||||
|
@ -2122,6 +2122,7 @@ int __init acpi_scan_init(void)
|
||||
acpi_int340x_thermal_init();
|
||||
acpi_amba_init();
|
||||
acpi_watchdog_init();
|
||||
acpi_init_lpit();
|
||||
|
||||
acpi_scan_add_handler(&generic_device_handler);
|
||||
|
||||
|
@ -22,14 +22,23 @@
|
||||
#include <linux/string.h>
|
||||
#include <linux/sched/topology.h>
|
||||
|
||||
static DEFINE_MUTEX(cpu_scale_mutex);
|
||||
static DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE;
|
||||
DEFINE_PER_CPU(unsigned long, freq_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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* on ACPI-based systems we need to use the default cpu capacity
|
||||
* 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);
|
||||
|
||||
return cpufreq_register_notifier(&init_cpu_capacity_notifier,
|
||||
CPUFREQ_POLICY_NOTIFIER);
|
||||
ret = cpufreq_register_notifier(&init_cpu_capacity_notifier,
|
||||
CPUFREQ_POLICY_NOTIFIER);
|
||||
|
||||
if (ret)
|
||||
free_cpumask_var(cpus_to_visit);
|
||||
|
||||
return ret;
|
||||
}
|
||||
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_POLICY_NOTIFIER);
|
||||
free_cpumask_var(cpus_to_visit);
|
||||
}
|
||||
|
||||
#else
|
||||
|
@ -386,7 +386,8 @@ int register_cpu(struct cpu *cpu, int num)
|
||||
|
||||
per_cpu(cpu_sys_devices, num) = &cpu->dev;
|
||||
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;
|
||||
}
|
||||
|
@ -464,6 +464,7 @@ pinctrl_bind_failed:
|
||||
if (dev->pm_domain && dev->pm_domain->dismiss)
|
||||
dev->pm_domain->dismiss(dev);
|
||||
pm_runtime_reinit(dev);
|
||||
dev_pm_set_driver_flags(dev, 0);
|
||||
|
||||
switch (ret) {
|
||||
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)
|
||||
dev->pm_domain->dismiss(dev);
|
||||
pm_runtime_reinit(dev);
|
||||
dev_pm_set_driver_flags(dev, 0);
|
||||
|
||||
klist_remove(&dev->p->knode_driver);
|
||||
device_pm_check_callbacks(dev);
|
||||
|
@ -2,7 +2,6 @@
|
||||
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_TRACE_RTC) += trace.o
|
||||
obj-$(CONFIG_PM_OPP) += opp/
|
||||
obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o
|
||||
obj-$(CONFIG_HAVE_CLK) += clock_ops.o
|
||||
|
||||
|
@ -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_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_active_wakeup(genpd) (genpd->flags & GENPD_FLAG_ACTIVE_WAKEUP)
|
||||
|
||||
static inline bool irq_safe_dev_in_no_sleep_domain(struct device *dev,
|
||||
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) {}
|
||||
#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)
|
||||
{
|
||||
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;
|
||||
|
||||
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)
|
||||
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) {
|
||||
enum pm_qos_flags_status stat;
|
||||
|
||||
stat = dev_pm_qos_flags(pdd->dev,
|
||||
PM_QOS_FLAG_NO_POWER_OFF
|
||||
| PM_QOS_FLAG_REMOTE_WAKEUP);
|
||||
stat = dev_pm_qos_flags(pdd->dev, PM_QOS_FLAG_NO_POWER_OFF);
|
||||
if (stat > PM_QOS_FLAGS_NONE)
|
||||
return -EBUSY;
|
||||
|
||||
@ -749,11 +846,7 @@ late_initcall(genpd_power_off_unused);
|
||||
|
||||
#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_GENERIC_DOMAINS_OF)
|
||||
|
||||
/**
|
||||
* 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)
|
||||
static bool genpd_present(const struct generic_pm_domain *genpd)
|
||||
{
|
||||
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
|
||||
|
||||
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: 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.
|
||||
*
|
||||
* 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
|
||||
* 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
|
||||
@ -881,12 +968,12 @@ static bool resume_needed(struct device *dev,
|
||||
if (!device_can_wakeup(dev))
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
* 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;
|
||||
int ret;
|
||||
@ -921,7 +1008,7 @@ static int pm_genpd_prepare(struct device *dev)
|
||||
genpd_unlock(genpd);
|
||||
|
||||
ret = pm_generic_prepare(dev);
|
||||
if (ret) {
|
||||
if (ret < 0) {
|
||||
genpd_lock(genpd);
|
||||
|
||||
genpd->prepared_count--;
|
||||
@ -929,7 +1016,8 @@ static int pm_genpd_prepare(struct device *dev)
|
||||
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))
|
||||
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;
|
||||
|
||||
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.
|
||||
*
|
||||
* Stop the device and remove power from the domain if all devices in it have
|
||||
* 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__);
|
||||
|
||||
@ -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.
|
||||
*
|
||||
* 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;
|
||||
int ret = 0;
|
||||
@ -1005,7 +1093,7 @@ static int pm_genpd_resume_noirq(struct device *dev)
|
||||
if (IS_ERR(genpd))
|
||||
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;
|
||||
|
||||
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.
|
||||
*
|
||||
* 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
|
||||
* devices.
|
||||
*/
|
||||
static int pm_genpd_freeze_noirq(struct device *dev)
|
||||
static int genpd_freeze_noirq(struct device *dev)
|
||||
{
|
||||
const struct generic_pm_domain *genpd;
|
||||
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.
|
||||
*
|
||||
* Start the device, unless power has been removed from the domain already
|
||||
* 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;
|
||||
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.
|
||||
* @dev: Device to poweroff.
|
||||
*
|
||||
* Stop the device and remove power from the domain if all devices in it have
|
||||
* 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__);
|
||||
|
||||
@ -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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
static int pm_genpd_restore_noirq(struct device *dev)
|
||||
static int genpd_restore_noirq(struct device *dev)
|
||||
{
|
||||
struct generic_pm_domain *genpd;
|
||||
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.
|
||||
*
|
||||
* 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
|
||||
* 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;
|
||||
|
||||
@ -1180,7 +1268,7 @@ static void genpd_syscore_switch(struct device *dev, bool suspend)
|
||||
struct generic_pm_domain *genpd;
|
||||
|
||||
genpd = dev_to_genpd(dev);
|
||||
if (!pm_genpd_present(genpd))
|
||||
if (!genpd_present(genpd))
|
||||
return;
|
||||
|
||||
if (suspend) {
|
||||
@ -1206,14 +1294,14 @@ EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweron);
|
||||
|
||||
#else /* !CONFIG_PM_SLEEP */
|
||||
|
||||
#define pm_genpd_prepare NULL
|
||||
#define pm_genpd_suspend_noirq NULL
|
||||
#define pm_genpd_resume_noirq NULL
|
||||
#define pm_genpd_freeze_noirq NULL
|
||||
#define pm_genpd_thaw_noirq NULL
|
||||
#define pm_genpd_poweroff_noirq NULL
|
||||
#define pm_genpd_restore_noirq NULL
|
||||
#define pm_genpd_complete NULL
|
||||
#define genpd_prepare NULL
|
||||
#define genpd_suspend_noirq NULL
|
||||
#define genpd_resume_noirq NULL
|
||||
#define genpd_freeze_noirq NULL
|
||||
#define genpd_thaw_noirq NULL
|
||||
#define genpd_poweroff_noirq NULL
|
||||
#define genpd_restore_noirq NULL
|
||||
#define genpd_complete NULL
|
||||
|
||||
#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->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;
|
||||
|
||||
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->domain.ops.runtime_suspend = genpd_runtime_suspend;
|
||||
genpd->domain.ops.runtime_resume = genpd_runtime_resume;
|
||||
genpd->domain.ops.prepare = pm_genpd_prepare;
|
||||
genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
|
||||
genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
|
||||
genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
|
||||
genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
|
||||
genpd->domain.ops.poweroff_noirq = pm_genpd_poweroff_noirq;
|
||||
genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
|
||||
genpd->domain.ops.complete = pm_genpd_complete;
|
||||
genpd->domain.ops.prepare = genpd_prepare;
|
||||
genpd->domain.ops.suspend_noirq = genpd_suspend_noirq;
|
||||
genpd->domain.ops.resume_noirq = genpd_resume_noirq;
|
||||
genpd->domain.ops.freeze_noirq = genpd_freeze_noirq;
|
||||
genpd->domain.ops.thaw_noirq = genpd_thaw_noirq;
|
||||
genpd->domain.ops.poweroff_noirq = genpd_poweroff_noirq;
|
||||
genpd->domain.ops.restore_noirq = genpd_restore_noirq;
|
||||
genpd->domain.ops.complete = genpd_complete;
|
||||
|
||||
if (genpd->flags & GENPD_FLAG_PM_CLK) {
|
||||
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);
|
||||
|
||||
if (pm_genpd_present(genpd)) {
|
||||
if (genpd_present(genpd)) {
|
||||
ret = genpd_add_provider(np, genpd_xlate_simple, genpd);
|
||||
if (!ret) {
|
||||
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++) {
|
||||
if (!data->domains[i])
|
||||
continue;
|
||||
if (!pm_genpd_present(data->domains[i]))
|
||||
if (!genpd_present(data->domains[i]))
|
||||
goto error;
|
||||
|
||||
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/init.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
|
||||
@ -2302,8 +2390,8 @@ static void rtpm_status_str(struct seq_file *s, struct device *dev)
|
||||
seq_puts(s, p);
|
||||
}
|
||||
|
||||
static int pm_genpd_summary_one(struct seq_file *s,
|
||||
struct generic_pm_domain *genpd)
|
||||
static int genpd_summary_one(struct seq_file *s,
|
||||
struct generic_pm_domain *genpd)
|
||||
{
|
||||
static const char * const status_lookup[] = {
|
||||
[GPD_STATE_ACTIVE] = "on",
|
||||
@ -2373,7 +2461,7 @@ static int genpd_summary_show(struct seq_file *s, void *data)
|
||||
return -ERESTARTSYS;
|
||||
|
||||
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)
|
||||
break;
|
||||
}
|
||||
@ -2559,23 +2647,23 @@ define_genpd_debugfs_fops(active_time);
|
||||
define_genpd_debugfs_fops(total_idle_time);
|
||||
define_genpd_debugfs_fops(devices);
|
||||
|
||||
static int __init pm_genpd_debug_init(void)
|
||||
static int __init genpd_debug_init(void)
|
||||
{
|
||||
struct dentry *d;
|
||||
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;
|
||||
|
||||
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)
|
||||
return -ENOMEM;
|
||||
|
||||
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)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -2595,11 +2683,11 @@ static int __init pm_genpd_debug_init(void)
|
||||
|
||||
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 */
|
||||
|
@ -14,23 +14,29 @@
|
||||
static int dev_update_qos_constraint(struct device *dev, void *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;
|
||||
|
||||
if (constraint_ns < 0) {
|
||||
} else {
|
||||
/*
|
||||
* 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 *= NSEC_PER_USEC;
|
||||
}
|
||||
if (constraint_ns == 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* constraint_ns cannot be negative here, because the device has been
|
||||
* suspended.
|
||||
*/
|
||||
if (constraint_ns < *constraint_ns_p || *constraint_ns_p == 0)
|
||||
if (constraint_ns < *constraint_ns_p)
|
||||
*constraint_ns_p = constraint_ns;
|
||||
|
||||
return 0;
|
||||
@ -58,12 +64,12 @@ static bool default_suspend_ok(struct device *dev)
|
||||
}
|
||||
td->constraint_changed = false;
|
||||
td->cached_suspend_ok = false;
|
||||
td->effective_constraint_ns = -1;
|
||||
td->effective_constraint_ns = 0;
|
||||
constraint_ns = __dev_pm_qos_read_value(dev);
|
||||
|
||||
spin_unlock_irqrestore(&dev->power.lock, flags);
|
||||
|
||||
if (constraint_ns < 0)
|
||||
if (constraint_ns == 0)
|
||||
return false;
|
||||
|
||||
constraint_ns *= NSEC_PER_USEC;
|
||||
@ -76,14 +82,32 @@ static bool default_suspend_ok(struct device *dev)
|
||||
device_for_each_child(dev, &constraint_ns,
|
||||
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 +
|
||||
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;
|
||||
|
||||
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
|
||||
@ -144,18 +168,13 @@ static bool __default_power_down_ok(struct dev_pm_domain *pd,
|
||||
*/
|
||||
td = &to_gpd_data(pdd)->td;
|
||||
constraint_ns = td->effective_constraint_ns;
|
||||
/* default_suspend_ok() need not be called before us. */
|
||||
if (constraint_ns < 0) {
|
||||
constraint_ns = dev_pm_qos_read_value(pdd->dev);
|
||||
constraint_ns *= NSEC_PER_USEC;
|
||||
}
|
||||
if (constraint_ns == 0)
|
||||
/*
|
||||
* Zero means "no suspend at all" and this runs only when all
|
||||
* devices in the domain are suspended, so it must be positive.
|
||||
*/
|
||||
if (constraint_ns == PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* constraint_ns cannot be negative here, because the device has
|
||||
* been suspended.
|
||||
*/
|
||||
if (constraint_ns <= off_on_time_ns)
|
||||
return false;
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/suspend.h>
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/**
|
||||
@ -298,26 +297,4 @@ void pm_generic_complete(struct device *dev)
|
||||
if (drv && drv->pm && drv->pm->complete)
|
||||
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 */
|
||||
|
@ -526,7 +526,7 @@ static void dpm_watchdog_clear(struct dpm_watchdog *wd)
|
||||
/*------------------------- 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.
|
||||
* @state: PM transition of the system being carried out.
|
||||
* @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;
|
||||
}
|
||||
|
||||
if (dev->class) {
|
||||
if (dev->class->pm) {
|
||||
info = "class ";
|
||||
callback = pm_op(dev->class->pm, state);
|
||||
goto Driver;
|
||||
} else if (dev->class->resume) {
|
||||
info = "legacy class ";
|
||||
callback = dev->class->resume;
|
||||
goto End;
|
||||
}
|
||||
if (dev->class && dev->class->pm) {
|
||||
info = "class ";
|
||||
callback = pm_op(dev->class->pm, state);
|
||||
goto Driver;
|
||||
}
|
||||
|
||||
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.
|
||||
* @state: PM transition of the system being carried out.
|
||||
* @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.
|
||||
* @state: PM transition of the system being carried out.
|
||||
* @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.
|
||||
* @state: PM transition of the system being carried out.
|
||||
* @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;
|
||||
}
|
||||
|
||||
if (dev->class) {
|
||||
if (dev->class->pm) {
|
||||
info = "class ";
|
||||
callback = pm_op(dev->class->pm, state);
|
||||
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->class && dev->class->pm) {
|
||||
info = "class ";
|
||||
callback = pm_op(dev->class->pm, state);
|
||||
goto Run;
|
||||
}
|
||||
|
||||
if (dev->bus) {
|
||||
@ -1663,6 +1650,9 @@ static int device_prepare(struct device *dev, pm_message_t state)
|
||||
if (dev->power.syscore)
|
||||
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,
|
||||
* it won't be possible to resume the device. To prevent this we
|
||||
@ -1711,7 +1701,9 @@ unlock:
|
||||
* applies to suspend transitions, however.
|
||||
*/
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
@ -1860,11 +1852,16 @@ void device_pm_check_callbacks(struct device *dev)
|
||||
dev->power.no_pm_callbacks =
|
||||
(!dev->bus || (pm_ops_is_empty(dev->bus->pm) &&
|
||||
!dev->bus->suspend && !dev->bus->resume)) &&
|
||||
(!dev->class || (pm_ops_is_empty(dev->class->pm) &&
|
||||
!dev->class->suspend && !dev->class->resume)) &&
|
||||
(!dev->class || pm_ops_is_empty(dev->class->pm)) &&
|
||||
(!dev->type || pm_ops_is_empty(dev->type->pm)) &&
|
||||
(!dev->pm_domain || pm_ops_is_empty(&dev->pm_domain->ops)) &&
|
||||
(!dev->driver || (pm_ops_is_empty(dev->driver->pm) &&
|
||||
!dev->driver->suspend && !dev->driver->resume));
|
||||
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);
|
||||
}
|
||||
|
@ -139,6 +139,9 @@ static int apply_constraint(struct dev_pm_qos_request *req,
|
||||
|
||||
switch(req->type) {
|
||||
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,
|
||||
&req->data.pnode, action, value);
|
||||
break;
|
||||
@ -189,7 +192,7 @@ static int dev_pm_qos_constraints_allocate(struct device *dev)
|
||||
plist_head_init(&c->list);
|
||||
c->target_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->notifiers = n;
|
||||
|
||||
|
@ -253,7 +253,7 @@ static int rpm_check_suspend_allowed(struct device *dev)
|
||||
|| (dev->power.request_pending
|
||||
&& dev->power.request == RPM_REQ_RESUME))
|
||||
retval = -EAGAIN;
|
||||
else if (__dev_pm_qos_read_value(dev) < 0)
|
||||
else if (__dev_pm_qos_read_value(dev) == 0)
|
||||
retval = -EPERM;
|
||||
else if (dev->power.runtime_status == RPM_SUSPENDED)
|
||||
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.
|
||||
*/
|
||||
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 expires;
|
||||
|
||||
@ -1499,8 +1499,7 @@ void pm_runtime_init(struct device *dev)
|
||||
INIT_WORK(&dev->power.work, pm_runtime_work);
|
||||
|
||||
dev->power.timer_expires = 0;
|
||||
setup_timer(&dev->power.suspend_timer, pm_suspend_timer_fn,
|
||||
(unsigned long)dev);
|
||||
timer_setup(&dev->power.suspend_timer, pm_suspend_timer_fn, 0);
|
||||
|
||||
init_waitqueue_head(&dev->power.wait_queue);
|
||||
}
|
||||
|
@ -218,7 +218,14 @@ static ssize_t pm_qos_resume_latency_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
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,
|
||||
@ -228,11 +235,21 @@ static ssize_t pm_qos_resume_latency_store(struct device *dev,
|
||||
s32 value;
|
||||
int ret;
|
||||
|
||||
if (kstrtos32(buf, 0, &value))
|
||||
return -EINVAL;
|
||||
if (!kstrtos32(buf, 0, &value)) {
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
ret = dev_pm_qos_update_request(dev->power.qos->resume_latency_req,
|
||||
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,
|
||||
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
|
||||
static const char _enabled[] = "enabled";
|
||||
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[] = {
|
||||
&dev_attr_pm_qos_no_power_off.attr,
|
||||
&dev_attr_pm_qos_remote_wakeup.attr,
|
||||
NULL,
|
||||
};
|
||||
static const struct attribute_group pm_qos_flags_attr_group = {
|
||||
|
@ -54,7 +54,7 @@ static unsigned int saved_count;
|
||||
|
||||
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);
|
||||
|
||||
@ -176,7 +176,7 @@ void wakeup_source_add(struct wakeup_source *ws)
|
||||
return;
|
||||
|
||||
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->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
|
||||
* by wakeup_source_add.
|
||||
*/
|
||||
return ws->timer.function != pm_wakeup_timer_fn ||
|
||||
ws->timer.data != (unsigned long)ws;
|
||||
return ws->timer.function != (TIMER_FUNC_TYPE)pm_wakeup_timer_fn;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -724,9 +723,9 @@ EXPORT_SYMBOL_GPL(pm_relax);
|
||||
* 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.
|
||||
*/
|
||||
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;
|
||||
|
||||
spin_lock_irqsave(&ws->lock, flags);
|
||||
|
@ -57,7 +57,7 @@ static bool bL_switching_enabled;
|
||||
#define VIRT_FREQ(cluster, freq) ((cluster == A7_CLUSTER) ? freq >> 1 : freq)
|
||||
|
||||
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 cpufreq_frequency_table *freq_table[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;
|
||||
unsigned int freqs_new;
|
||||
int ret;
|
||||
|
||||
cur_cluster = cpu_to_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)
|
||||
@ -609,7 +617,7 @@ static int __bLs_register_notifier(void) { return 0; }
|
||||
static int __bLs_unregister_notifier(void) { return 0; }
|
||||
#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;
|
||||
|
||||
@ -653,7 +661,7 @@ int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops)
|
||||
}
|
||||
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) {
|
||||
pr_err("%s: Registered with: %s, can't unregister, exiting\n",
|
||||
|
@ -37,7 +37,7 @@ struct cpufreq_arm_bL_ops {
|
||||
void (*free_opp_table)(const struct cpumask *cpumask);
|
||||
};
|
||||
|
||||
int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops);
|
||||
void bL_cpufreq_unregister(struct cpufreq_arm_bL_ops *ops);
|
||||
int bL_cpufreq_register(const struct cpufreq_arm_bL_ops *ops);
|
||||
void bL_cpufreq_unregister(const struct cpufreq_arm_bL_ops *ops);
|
||||
|
||||
#endif /* CPUFREQ_ARM_BIG_LITTLE_H */
|
||||
|
@ -61,7 +61,7 @@ static int dt_get_transition_latency(struct device *cpu_dev)
|
||||
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",
|
||||
.get_transition_latency = dt_get_transition_latency,
|
||||
.init_opp_table = dev_pm_opp_of_cpumask_add_table,
|
||||
|
@ -48,7 +48,6 @@ static const struct of_device_id whitelist[] __initconst = {
|
||||
|
||||
{ .compatible = "samsung,exynos3250", },
|
||||
{ .compatible = "samsung,exynos4210", },
|
||||
{ .compatible = "samsung,exynos4212", },
|
||||
{ .compatible = "samsung,exynos5250", },
|
||||
#ifndef CONFIG_BL_SWITCHER
|
||||
{ .compatible = "samsung,exynos5800", },
|
||||
@ -83,8 +82,6 @@ static const struct of_device_id whitelist[] __initconst = {
|
||||
{ .compatible = "rockchip,rk3368", },
|
||||
{ .compatible = "rockchip,rk3399", },
|
||||
|
||||
{ .compatible = "socionext,uniphier-ld6b", },
|
||||
|
||||
{ .compatible = "st-ericsson,u8500", },
|
||||
{ .compatible = "st-ericsson,u8540", },
|
||||
{ .compatible = "st-ericsson,u9500", },
|
||||
|
@ -43,9 +43,17 @@ static struct freq_attr *cpufreq_dt_attr[] = {
|
||||
static int set_target(struct cpufreq_policy *policy, unsigned int index)
|
||||
{
|
||||
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,
|
||||
policy->freq_table[index].frequency * 1000);
|
||||
ret = dev_pm_opp_set_rate(priv->cpu_dev, freq * 1000);
|
||||
|
||||
if (!ret) {
|
||||
arch_set_freq_scale(policy->related_cpus, freq,
|
||||
policy->cpuinfo.max_freq);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -161,6 +161,12 @@ u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy)
|
||||
}
|
||||
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
|
||||
* drivers of SMP systems. It will do following:
|
||||
|
@ -118,8 +118,11 @@ static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
|
||||
break;
|
||||
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;
|
||||
}
|
||||
cpufreq_freq_attr_ro(trans_table);
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
@ -191,6 +192,57 @@ static struct cpufreq_driver imx6q_cpufreq_driver = {
|
||||
.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)
|
||||
{
|
||||
struct device_node *np;
|
||||
@ -252,28 +304,21 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
|
||||
goto put_reg;
|
||||
}
|
||||
|
||||
/*
|
||||
* We expect an OPP table supplied by platform.
|
||||
* Just, incase the platform did not supply the OPP
|
||||
* table, it will try to get it.
|
||||
*/
|
||||
ret = dev_pm_opp_of_add_table(cpu_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(cpu_dev, "failed to init OPP table: %d\n", ret);
|
||||
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);
|
||||
if (num < 0) {
|
||||
ret = dev_pm_opp_of_add_table(cpu_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(cpu_dev, "failed to init OPP table: %d\n", ret);
|
||||
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 = 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);
|
||||
|
@ -1043,7 +1043,7 @@ static int powernowk8_cpu_init(struct cpufreq_policy *pol)
|
||||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
pr_err("unable to alloc powernow_k8_data");
|
||||
pr_err("unable to alloc powernow_k8_data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
@ -58,56 +58,40 @@ module_param(pxa27x_maxfreq, uint, 0);
|
||||
MODULE_PARM_DESC(pxa27x_maxfreq, "Set the pxa27x maxfreq in MHz"
|
||||
"(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 {
|
||||
unsigned int khz;
|
||||
unsigned int membus;
|
||||
unsigned int cccr;
|
||||
unsigned int div2;
|
||||
unsigned int cclkcfg;
|
||||
int vmin;
|
||||
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
|
||||
*/
|
||||
/* 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[] =
|
||||
{
|
||||
/* CPU MEMBUS CCCR DIV2 CCLKCFG run turbo PXbus SDRAM */
|
||||
{ 99500, 99500, 0x121, 1, CCLKCFG, -1, -1}, /* 99, 99, 50, 50 */
|
||||
{132700, 132700, 0x123, 1, CCLKCFG, -1, -1}, /* 133, 133, 66, 66 */
|
||||
{199100, 99500, 0x141, 0, CCLKCFG, -1, -1}, /* 199, 199, 99, 99 */
|
||||
{265400, 132700, 0x143, 1, CCLKCFG, -1, -1}, /* 265, 265, 133, 66 */
|
||||
{331800, 165900, 0x145, 1, CCLKCFG, -1, -1}, /* 331, 331, 166, 83 */
|
||||
{398100, 99500, 0x161, 0, CCLKCFG, -1, -1}, /* 398, 398, 196, 99 */
|
||||
/* CPU MEMBUS run turbo PXbus SDRAM */
|
||||
{ 99500, -1, -1}, /* 99, 99, 50, 50 */
|
||||
{132700, -1, -1}, /* 133, 133, 66, 66 */
|
||||
{199100, -1, -1}, /* 199, 199, 99, 99 */
|
||||
{265400, -1, -1}, /* 265, 265, 133, 66 */
|
||||
{331800, -1, -1}, /* 331, 331, 166, 83 */
|
||||
{398100, -1, -1}, /* 398, 398, 196, 99 */
|
||||
};
|
||||
|
||||
/* Use the turbo mode frequencies for the CPUFREQ_POLICY_POWERSAVE policy */
|
||||
static const struct pxa_freqs pxa255_turbo_freqs[] =
|
||||
{
|
||||
/* CPU MEMBUS CCCR DIV2 CCLKCFG run turbo PXbus SDRAM */
|
||||
{ 99500, 99500, 0x121, 1, CCLKCFG, -1, -1}, /* 99, 99, 50, 50 */
|
||||
{199100, 99500, 0x221, 0, CCLKCFG, -1, -1}, /* 99, 199, 50, 99 */
|
||||
{298500, 99500, 0x321, 0, CCLKCFG, -1, -1}, /* 99, 287, 50, 99 */
|
||||
{298600, 99500, 0x1c1, 0, CCLKCFG, -1, -1}, /* 199, 287, 99, 99 */
|
||||
{398100, 99500, 0x241, 0, CCLKCFG, -1, -1}, /* 199, 398, 99, 99 */
|
||||
/* CPU run turbo PXbus SDRAM */
|
||||
{ 99500, -1, -1}, /* 99, 99, 50, 50 */
|
||||
{199100, -1, -1}, /* 99, 199, 50, 99 */
|
||||
{298500, -1, -1}, /* 99, 287, 50, 99 */
|
||||
{298600, -1, -1}, /* 199, 287, 99, 99 */
|
||||
{398100, -1, -1}, /* 199, 398, 99, 99 */
|
||||
};
|
||||
|
||||
#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_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[] = {
|
||||
{104000, 104000, PXA27x_CCCR(1, 8, 2), 0, CCLKCFG2(1, 0, 1), 900000, 1705000 },
|
||||
{156000, 104000, PXA27x_CCCR(1, 8, 3), 0, CCLKCFG2(1, 0, 1), 1000000, 1705000 },
|
||||
{208000, 208000, PXA27x_CCCR(0, 16, 2), 1, CCLKCFG2(0, 0, 1), 1180000, 1705000 },
|
||||
{312000, 208000, PXA27x_CCCR(1, 16, 3), 1, CCLKCFG2(1, 0, 1), 1250000, 1705000 },
|
||||
{416000, 208000, PXA27x_CCCR(1, 16, 4), 1, CCLKCFG2(1, 0, 1), 1350000, 1705000 },
|
||||
{520000, 208000, PXA27x_CCCR(1, 16, 5), 1, CCLKCFG2(1, 0, 1), 1450000, 1705000 },
|
||||
{624000, 208000, PXA27x_CCCR(1, 16, 6), 1, CCLKCFG2(1, 0, 1), 1550000, 1705000 }
|
||||
{104000, 900000, 1705000 },
|
||||
{156000, 1000000, 1705000 },
|
||||
{208000, 1180000, 1705000 },
|
||||
{312000, 1250000, 1705000 },
|
||||
{416000, 1350000, 1705000 },
|
||||
{520000, 1450000, 1705000 },
|
||||
{624000, 1550000, 1705000 }
|
||||
};
|
||||
|
||||
#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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
struct cpufreq_frequency_table *pxa_freqs_table;
|
||||
const struct pxa_freqs *pxa_freq_settings;
|
||||
unsigned long flags;
|
||||
unsigned int new_freq_cpu, new_freq_mem;
|
||||
unsigned int unused, preset_mdrefr, postset_mdrefr, cclkcfg;
|
||||
struct pxa_cpufreq_data *data = cpufreq_get_driver_data();
|
||||
unsigned int new_freq_cpu;
|
||||
int ret = 0;
|
||||
|
||||
/* Get the current policy */
|
||||
find_freq_tables(&pxa_freqs_table, &pxa_freq_settings);
|
||||
|
||||
new_freq_cpu = pxa_freq_settings[idx].khz;
|
||||
new_freq_mem = pxa_freq_settings[idx].membus;
|
||||
|
||||
if (freq_debug)
|
||||
pr_debug("Changing CPU frequency to %d Mhz, (SDRAM %d Mhz)\n",
|
||||
new_freq_cpu / 1000, (pxa_freq_settings[idx].div2) ?
|
||||
(new_freq_mem / 2000) : (new_freq_mem / 1000));
|
||||
pr_debug("Changing CPU frequency from %d Mhz to %d Mhz\n",
|
||||
policy->cur / 1000, new_freq_cpu / 1000);
|
||||
|
||||
if (vcc_core && new_freq_cpu > policy->cur) {
|
||||
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;
|
||||
}
|
||||
|
||||
/* Calculate the next MDREFR. If we're slowing down the SDRAM clock
|
||||
* 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);
|
||||
clk_set_rate(data->clk_core, new_freq_cpu * 1000);
|
||||
|
||||
/*
|
||||
* 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();
|
||||
|
||||
init_sdram_rows();
|
||||
|
||||
/* set default policy and cpuinfo */
|
||||
policy->cpuinfo.transition_latency = 1000; /* FIXME: 1 ms, assumed */
|
||||
|
||||
@ -429,11 +310,17 @@ static struct cpufreq_driver pxa_cpufreq_driver = {
|
||||
.init = pxa_cpufreq_init,
|
||||
.get = pxa_cpufreq_get,
|
||||
.name = "PXA2xx",
|
||||
.driver_data = &pxa_cpufreq_data,
|
||||
};
|
||||
|
||||
static int __init pxa_cpu_init(void)
|
||||
{
|
||||
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())
|
||||
ret = cpufreq_register_driver(&pxa_cpufreq_driver);
|
||||
return ret;
|
||||
|
@ -53,7 +53,7 @@ static int scpi_init_opp_table(const struct cpumask *cpumask)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct cpufreq_arm_bL_ops scpi_cpufreq_ops = {
|
||||
static const struct cpufreq_arm_bL_ops scpi_cpufreq_ops = {
|
||||
.name = "scpi",
|
||||
.get_transition_latency = scpi_get_transition_latency,
|
||||
.init_opp_table = scpi_init_opp_table,
|
||||
|
@ -177,7 +177,7 @@ static int spear_cpufreq_probe(struct platform_device *pdev)
|
||||
|
||||
np = of_cpu_device_node_get(0);
|
||||
if (!np) {
|
||||
pr_err("No cpu node found");
|
||||
pr_err("No cpu node found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@ -187,7 +187,7 @@ static int spear_cpufreq_probe(struct platform_device *pdev)
|
||||
|
||||
prop = of_find_property(np, "cpufreq_tbl", NULL);
|
||||
if (!prop || !prop->value) {
|
||||
pr_err("Invalid cpufreq_tbl");
|
||||
pr_err("Invalid cpufreq_tbl\n");
|
||||
ret = -ENODEV;
|
||||
goto out_put_node;
|
||||
}
|
||||
|
@ -367,7 +367,7 @@ unsigned int speedstep_detect_processor(void)
|
||||
} else
|
||||
return SPEEDSTEP_CPU_PIII_C;
|
||||
}
|
||||
|
||||
/* fall through */
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
@ -205,6 +205,7 @@ static int ti_cpufreq_init(void)
|
||||
|
||||
np = of_find_node_by_path("/");
|
||||
match = of_match_node(ti_cpufreq_of_match, np);
|
||||
of_node_put(np);
|
||||
if (!match)
|
||||
return -ENODEV;
|
||||
|
||||
@ -217,7 +218,8 @@ static int ti_cpufreq_init(void)
|
||||
opp_data->cpu_dev = get_cpu_device(0);
|
||||
if (!opp_data->cpu_dev) {
|
||||
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);
|
||||
@ -262,6 +264,8 @@ register_cpufreq_dt:
|
||||
|
||||
fail_put_node:
|
||||
of_node_put(opp_data->opp_node);
|
||||
free_opp_data:
|
||||
kfree(opp_data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ static int ve_spc_get_transition_latency(struct device *cpu_dev)
|
||||
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",
|
||||
.get_transition_latency = ve_spc_get_transition_latency,
|
||||
.init_opp_table = ve_spc_init_opp_table,
|
||||
|
@ -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
|
||||
* framework. It relies on core code to parse the idle states
|
||||
* 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)
|
||||
{
|
||||
int cpu, ret;
|
||||
@ -85,79 +167,20 @@ static int __init arm_idle_init(void)
|
||||
struct cpuidle_device *dev;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
|
||||
drv = kmemdup(&arm_idle_driver, sizeof(*drv), GFP_KERNEL);
|
||||
if (!drv) {
|
||||
ret = -ENOMEM;
|
||||
ret = arm_idle_init_cpu(cpu);
|
||||
if (ret)
|
||||
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;
|
||||
init_fail:
|
||||
kfree(drv);
|
||||
|
||||
out_fail:
|
||||
while (--cpu >= 0) {
|
||||
dev = per_cpu(cpuidle_devices, cpu);
|
||||
drv = cpuidle_get_cpu_driver(dev);
|
||||
cpuidle_unregister_device(dev);
|
||||
kfree(dev);
|
||||
drv = cpuidle_get_driver();
|
||||
cpuidle_unregister_driver(drv);
|
||||
kfree(dev);
|
||||
kfree(drv);
|
||||
}
|
||||
|
||||
|
@ -208,6 +208,7 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
|
||||
return -EBUSY;
|
||||
}
|
||||
target_state = &drv->states[index];
|
||||
broadcast = false;
|
||||
}
|
||||
|
||||
/* Take note of the planned idle state. */
|
||||
@ -387,9 +388,12 @@ int cpuidle_enable_device(struct cpuidle_device *dev)
|
||||
if (dev->enabled)
|
||||
return 0;
|
||||
|
||||
if (!cpuidle_curr_governor)
|
||||
return -EIO;
|
||||
|
||||
drv = cpuidle_get_cpu_driver(dev);
|
||||
|
||||
if (!drv || !cpuidle_curr_governor)
|
||||
if (!drv)
|
||||
return -EIO;
|
||||
|
||||
if (!dev->registered)
|
||||
@ -399,9 +403,11 @@ int cpuidle_enable_device(struct cpuidle_device *dev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (cpuidle_curr_governor->enable &&
|
||||
(ret = cpuidle_curr_governor->enable(drv, dev)))
|
||||
goto fail_sysfs;
|
||||
if (cpuidle_curr_governor->enable) {
|
||||
ret = cpuidle_curr_governor->enable(drv, dev);
|
||||
if (ret)
|
||||
goto fail_sysfs;
|
||||
}
|
||||
|
||||
smp_wmb();
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/tick.h>
|
||||
#include <linux/cpu.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <linux/uaccess.h>
|
||||
@ -67,10 +68,16 @@ static int ladder_select_state(struct cpuidle_driver *drv,
|
||||
struct cpuidle_device *dev)
|
||||
{
|
||||
struct ladder_device *ldev = this_cpu_ptr(&ladder_devices);
|
||||
struct device *device = get_cpu_device(dev->cpu);
|
||||
struct ladder_device_state *last_state;
|
||||
int last_residency, last_idx = ldev->last_state_idx;
|
||||
int first_idx = drv->states[0].flags & CPUIDLE_FLAG_POLLING ? 1 : 0;
|
||||
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 */
|
||||
if (unlikely(latency_req == 0)) {
|
||||
|
@ -298,8 +298,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
|
||||
data->needs_update = 0;
|
||||
}
|
||||
|
||||
/* resume_latency is 0 means no restriction */
|
||||
if (resume_latency && resume_latency < latency_req)
|
||||
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 */
|
||||
|
@ -28,6 +28,9 @@
|
||||
#include <linux/of.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;
|
||||
|
||||
/*
|
||||
@ -69,6 +72,34 @@ static struct devfreq *find_device_devfreq(struct device *dev)
|
||||
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: the devfreq instance
|
||||
@ -85,11 +116,7 @@ static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* devfreq_set_freq_table() - Initialize freq_table for the frequency
|
||||
* @devfreq: the devfreq instance
|
||||
*/
|
||||
static void devfreq_set_freq_table(struct devfreq *devfreq)
|
||||
static int set_freq_table(struct devfreq *devfreq)
|
||||
{
|
||||
struct devfreq_dev_profile *profile = devfreq->profile;
|
||||
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 */
|
||||
count = dev_pm_opp_get_opp_count(devfreq->dev.parent);
|
||||
if (count <= 0)
|
||||
return;
|
||||
return -EINVAL;
|
||||
|
||||
profile->max_state = count;
|
||||
profile->freq_table = devm_kcalloc(devfreq->dev.parent,
|
||||
@ -108,7 +135,7 @@ static void devfreq_set_freq_table(struct devfreq *devfreq)
|
||||
GFP_KERNEL);
|
||||
if (!profile->freq_table) {
|
||||
profile->max_state = 0;
|
||||
return;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
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)) {
|
||||
devm_kfree(devfreq->dev.parent, profile->freq_table);
|
||||
profile->max_state = 0;
|
||||
return;
|
||||
return PTR_ERR(opp);
|
||||
}
|
||||
dev_pm_opp_put(opp);
|
||||
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)
|
||||
{
|
||||
struct devfreq_freqs freqs;
|
||||
unsigned long freq, cur_freq;
|
||||
unsigned long freq, cur_freq, min_freq, max_freq;
|
||||
int err = 0;
|
||||
u32 flags = 0;
|
||||
|
||||
@ -245,19 +274,21 @@ int update_devfreq(struct devfreq *devfreq)
|
||||
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
|
||||
* max_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) {
|
||||
freq = devfreq->min_freq;
|
||||
if (min_freq && freq < min_freq) {
|
||||
freq = min_freq;
|
||||
flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use GLB */
|
||||
}
|
||||
if (devfreq->max_freq && freq > devfreq->max_freq) {
|
||||
freq = devfreq->max_freq;
|
||||
if (max_freq && freq > max_freq) {
|
||||
freq = max_freq;
|
||||
flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */
|
||||
}
|
||||
|
||||
@ -280,10 +311,9 @@ int update_devfreq(struct devfreq *devfreq)
|
||||
freqs.new = freq;
|
||||
devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE);
|
||||
|
||||
if (devfreq->profile->freq_table)
|
||||
if (devfreq_update_status(devfreq, freq))
|
||||
dev_err(&devfreq->dev,
|
||||
"Couldn't update frequency transition information.\n");
|
||||
if (devfreq_update_status(devfreq, freq))
|
||||
dev_err(&devfreq->dev,
|
||||
"Couldn't update frequency transition information.\n");
|
||||
|
||||
devfreq->previous_freq = freq;
|
||||
return err;
|
||||
@ -466,6 +496,19 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type,
|
||||
int ret;
|
||||
|
||||
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);
|
||||
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) {
|
||||
mutex_unlock(&devfreq->lock);
|
||||
devfreq_set_freq_table(devfreq);
|
||||
err = set_freq_table(devfreq);
|
||||
if (err < 0)
|
||||
goto err_out;
|
||||
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",
|
||||
atomic_inc_return(&devfreq_no));
|
||||
err = device_register(&devfreq->dev);
|
||||
@ -1082,6 +1143,14 @@ unlock:
|
||||
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,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
@ -1108,17 +1177,15 @@ unlock:
|
||||
mutex_unlock(&df->lock);
|
||||
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 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 ssize_t available_frequencies_show(struct device *d,
|
||||
@ -1126,22 +1193,16 @@ static ssize_t available_frequencies_show(struct device *d,
|
||||
char *buf)
|
||||
{
|
||||
struct devfreq *df = to_devfreq(d);
|
||||
struct device *dev = df->dev.parent;
|
||||
struct dev_pm_opp *opp;
|
||||
ssize_t count = 0;
|
||||
unsigned long freq = 0;
|
||||
int i;
|
||||
|
||||
do {
|
||||
opp = dev_pm_opp_find_freq_ceil(dev, &freq);
|
||||
if (IS_ERR(opp))
|
||||
break;
|
||||
mutex_lock(&df->lock);
|
||||
|
||||
dev_pm_opp_put(opp);
|
||||
for (i = 0; i < df->profile->max_state; i++)
|
||||
count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
|
||||
"%lu ", freq);
|
||||
freq++;
|
||||
} while (1);
|
||||
"%lu ", df->profile->freq_table[i]);
|
||||
|
||||
mutex_unlock(&df->lock);
|
||||
/* Truncate the trailing space */
|
||||
if (count)
|
||||
count--;
|
||||
|
@ -436,7 +436,8 @@ static int exynos_bus_probe(struct platform_device *pdev)
|
||||
ondemand_data->downdifferential = 5;
|
||||
|
||||
/* 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);
|
||||
if (IS_ERR(bus->devfreq)) {
|
||||
dev_err(dev, "failed to add devfreq device\n");
|
||||
@ -488,7 +489,7 @@ passive:
|
||||
passive_data->parent = parent_devfreq;
|
||||
|
||||
/* 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);
|
||||
if (IS_ERR(bus->devfreq)) {
|
||||
dev_err(dev,
|
||||
|
@ -183,7 +183,7 @@ static int devfreq_passive_event_handler(struct devfreq *devfreq,
|
||||
}
|
||||
|
||||
static struct devfreq_governor devfreq_passive = {
|
||||
.name = "passive",
|
||||
.name = DEVFREQ_GOV_PASSIVE,
|
||||
.immutable = 1,
|
||||
.get_target_freq = devfreq_passive_get_target_freq,
|
||||
.event_handler = devfreq_passive_event_handler,
|
||||
|
@ -42,7 +42,7 @@ static int devfreq_performance_handler(struct devfreq *devfreq,
|
||||
}
|
||||
|
||||
static struct devfreq_governor devfreq_performance = {
|
||||
.name = "performance",
|
||||
.name = DEVFREQ_GOV_PERFORMANCE,
|
||||
.get_target_freq = devfreq_performance_func,
|
||||
.event_handler = devfreq_performance_handler,
|
||||
};
|
||||
|
@ -39,7 +39,7 @@ static int devfreq_powersave_handler(struct devfreq *devfreq,
|
||||
}
|
||||
|
||||
static struct devfreq_governor devfreq_powersave = {
|
||||
.name = "powersave",
|
||||
.name = DEVFREQ_GOV_POWERSAVE,
|
||||
.get_target_freq = devfreq_powersave_func,
|
||||
.event_handler = devfreq_powersave_handler,
|
||||
};
|
||||
|
@ -125,7 +125,7 @@ static int devfreq_simple_ondemand_handler(struct devfreq *devfreq,
|
||||
}
|
||||
|
||||
static struct devfreq_governor devfreq_simple_ondemand = {
|
||||
.name = "simple_ondemand",
|
||||
.name = DEVFREQ_GOV_SIMPLE_ONDEMAND,
|
||||
.get_target_freq = devfreq_simple_ondemand_func,
|
||||
.event_handler = devfreq_simple_ondemand_handler,
|
||||
};
|
||||
|
@ -87,7 +87,7 @@ static struct attribute *dev_entries[] = {
|
||||
NULL,
|
||||
};
|
||||
static const struct attribute_group dev_attr_group = {
|
||||
.name = "userspace",
|
||||
.name = DEVFREQ_GOV_USERSPACE,
|
||||
.attrs = dev_entries,
|
||||
};
|
||||
|
||||
|
@ -431,7 +431,7 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev)
|
||||
|
||||
data->devfreq = devm_devfreq_add_device(dev,
|
||||
&rk3399_devfreq_dmc_profile,
|
||||
"simple_ondemand",
|
||||
DEVFREQ_GOV_SIMPLE_ONDEMAND,
|
||||
&data->ondemand_data);
|
||||
if (IS_ERR(data->devfreq))
|
||||
return PTR_ERR(data->devfreq);
|
||||
|
@ -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
|
||||
* 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);
|
||||
if (ret < 0)
|
||||
|
@ -913,10 +913,9 @@ static __cpuidle int intel_idle(struct cpuidle_device *dev,
|
||||
struct cpuidle_state *state = &drv->states[index];
|
||||
unsigned long eax = flg2MWAIT(state->flags);
|
||||
unsigned int cstate;
|
||||
bool uninitialized_var(tick);
|
||||
int cpu = smp_processor_id();
|
||||
|
||||
cstate = (((eax) >> MWAIT_SUBSTATE_SIZE) & MWAIT_CSTATE_MASK) + 1;
|
||||
|
||||
/*
|
||||
* leave_mm() to avoid costly and often unnecessary wakeups
|
||||
* 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)
|
||||
leave_mm(cpu);
|
||||
|
||||
if (!(lapic_timer_reliable_states & (1 << (cstate))))
|
||||
tick_broadcast_enter();
|
||||
if (!static_cpu_has(X86_FEATURE_ARAT)) {
|
||||
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);
|
||||
|
||||
if (!(lapic_timer_reliable_states & (1 << (cstate))))
|
||||
if (!static_cpu_has(X86_FEATURE_ARAT) && tick)
|
||||
tick_broadcast_exit();
|
||||
|
||||
return index;
|
||||
@ -1061,7 +1067,7 @@ static const struct idle_cpu idle_cpu_dnv = {
|
||||
};
|
||||
|
||||
#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 = {
|
||||
ICPU(INTEL_FAM6_NEHALEM_EP, idle_cpu_nehalem),
|
||||
@ -1125,6 +1131,11 @@ static int __init intel_idle_probe(void)
|
||||
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)
|
||||
return -ENODEV;
|
||||
|
||||
|
@ -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
|
||||
* 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,
|
||||
|
@ -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
|
||||
* 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,
|
||||
|
13
drivers/opp/Kconfig
Normal file
13
drivers/opp/Kconfig
Normal 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>
|
@ -19,6 +19,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "opp.h"
|
||||
@ -296,7 +297,7 @@ int dev_pm_opp_get_opp_count(struct device *dev)
|
||||
opp_table = _find_opp_table(dev);
|
||||
if (IS_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);
|
||||
return count;
|
||||
}
|
||||
@ -535,6 +536,44 @@ _generic_set_opp_clk_only(struct device *dev, struct clk *clk,
|
||||
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,
|
||||
struct device *dev,
|
||||
unsigned long old_freq,
|
||||
@ -653,7 +692,16 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
|
||||
|
||||
/* Only frequency scaling */
|
||||
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) {
|
||||
ret = _generic_set_opp_regulator(opp_table, dev, old_freq, freq,
|
||||
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;
|
||||
}
|
||||
|
||||
if (opp_table->get_pstate)
|
||||
new_opp->pstate = opp_table->get_pstate(dev, new_opp->rate);
|
||||
|
||||
list_add(&new_opp->node, head);
|
||||
mutex_unlock(&opp_table->lock);
|
||||
|
||||
@ -1476,13 +1527,13 @@ err:
|
||||
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
|
||||
* @opp_table: OPP table returned from dev_pm_opp_register_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) {
|
||||
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);
|
||||
}
|
||||
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
|
||||
@ -1706,6 +1832,13 @@ void _dev_pm_opp_remove_table(struct opp_table *opp_table, struct device *dev,
|
||||
if (remove_all || !opp->dynamic)
|
||||
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 {
|
||||
_remove_opp_dev(_find_opp_dev(dev, opp_table), opp_table);
|
||||
}
|
@ -41,16 +41,15 @@ static bool opp_debug_create_supplies(struct dev_pm_opp *opp,
|
||||
{
|
||||
struct dentry *d;
|
||||
int i;
|
||||
char *name;
|
||||
|
||||
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 */
|
||||
d = debugfs_create_dir(name, pdentry);
|
||||
|
||||
kfree(name);
|
||||
|
||||
if (!d)
|
||||
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))
|
||||
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))
|
||||
return -ENOMEM;
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/slab.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__,
|
||||
ret);
|
||||
_dev_pm_opp_remove_table(opp_table, dev, false);
|
||||
of_node_put(np);
|
||||
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)
|
||||
continue;
|
||||
|
||||
cpu_np = of_get_cpu_node(cpu, NULL);
|
||||
cpu_np = of_cpu_device_node_get(cpu);
|
||||
if (!cpu_np) {
|
||||
dev_err(cpu_dev, "%s: failed to get cpu%d node\n",
|
||||
__func__, cpu);
|
||||
@ -613,6 +614,7 @@ int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev,
|
||||
|
||||
/* Get OPP descriptor node */
|
||||
tmp_np = _opp_of_get_opp_desc_node(cpu_np);
|
||||
of_node_put(cpu_np);
|
||||
if (!tmp_np) {
|
||||
pr_err("%pOF: Couldn't find opp node\n", cpu_np);
|
||||
ret = -ENOENT;
|
@ -58,6 +58,7 @@ extern struct list_head opp_tables;
|
||||
* @dynamic: not-created from static DT entries.
|
||||
* @turbo: true if turbo (boost) OPP
|
||||
* @suspend: true if suspend OPP
|
||||
* @pstate: Device's power domain's performance state.
|
||||
* @rate: Frequency in hertz
|
||||
* @supplies: Power supplies voltage/current values
|
||||
* @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's
|
||||
@ -76,6 +77,7 @@ struct dev_pm_opp {
|
||||
bool dynamic;
|
||||
bool turbo;
|
||||
bool suspend;
|
||||
unsigned int pstate;
|
||||
unsigned long rate;
|
||||
|
||||
struct dev_pm_opp_supply *supplies;
|
||||
@ -135,8 +137,10 @@ enum opp_table_access {
|
||||
* @clk: Device's clock handle
|
||||
* @regulators: 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_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_name: Name of the real dentry.
|
||||
*
|
||||
@ -170,9 +174,11 @@ struct opp_table {
|
||||
struct clk *clk;
|
||||
struct regulator **regulators;
|
||||
unsigned int regulator_count;
|
||||
bool genpd_performance_state;
|
||||
|
||||
int (*set_opp)(struct dev_pm_set_opp_data *data);
|
||||
struct dev_pm_set_opp_data *set_opp_data;
|
||||
int (*get_pstate)(struct device *dev, unsigned long rate);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *dentry;
|
@ -680,17 +680,13 @@ static int pci_pm_prepare(struct device *dev)
|
||||
{
|
||||
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) {
|
||||
int error = drv->pm->prepare(dev);
|
||||
if (error)
|
||||
if (error < 0)
|
||||
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));
|
||||
}
|
||||
@ -731,18 +727,25 @@ static int pci_pm_suspend(struct device *dev)
|
||||
|
||||
if (!pm) {
|
||||
pci_pm_default_suspend(pci_dev);
|
||||
goto Fixup;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* PCI devices suspended at run time need to be resumed at this point,
|
||||
* because in general it is necessary to reconfigure them for system
|
||||
* suspend. Namely, if the device is supposed to wake up the system
|
||||
* from the sleep state, we may need to reconfigure it for this purpose.
|
||||
* In turn, if the device is not supposed to wake up the system from the
|
||||
* sleep state, we'll have to prevent it from signaling wake-up.
|
||||
* PCI devices suspended at run time may need to be resumed at this
|
||||
* point, because in general it may be necessary to reconfigure them for
|
||||
* system suspend. Namely, if the device is expected to wake up the
|
||||
* system from the sleep state, it may have to be reconfigured for this
|
||||
* purpose, or if the device is not expected to wake up the system from
|
||||
* 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;
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
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))
|
||||
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);
|
||||
}
|
||||
|
||||
dev_dbg(dev, "PCI PM: Suspend power state: %s\n",
|
||||
pci_power_name(pci_dev->current_state));
|
||||
|
||||
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;
|
||||
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);
|
||||
|
||||
if (pci_has_legacy_pm_support(pci_dev))
|
||||
@ -873,6 +897,7 @@ static int pci_pm_resume(struct device *dev)
|
||||
#else /* !CONFIG_SUSPEND */
|
||||
|
||||
#define pci_pm_suspend NULL
|
||||
#define pci_pm_suspend_late NULL
|
||||
#define pci_pm_suspend_noirq NULL
|
||||
#define pci_pm_resume 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,
|
||||
* 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;
|
||||
if (pm->freeze) {
|
||||
@ -919,17 +945,25 @@ static int pci_pm_freeze(struct device *dev)
|
||||
return error;
|
||||
}
|
||||
|
||||
if (pcibios_pm_ops.freeze)
|
||||
return pcibios_pm_ops.freeze(dev);
|
||||
|
||||
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)
|
||||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
struct device_driver *drv = dev->driver;
|
||||
|
||||
if (dev_pm_smart_suspend_and_suspended(dev))
|
||||
return 0;
|
||||
|
||||
if (pci_has_legacy_pm_support(pci_dev))
|
||||
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;
|
||||
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) {
|
||||
error = pcibios_pm_ops.thaw_noirq(dev);
|
||||
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;
|
||||
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))
|
||||
return pci_legacy_resume(dev);
|
||||
|
||||
@ -1014,11 +1052,13 @@ static int pci_pm_poweroff(struct device *dev)
|
||||
|
||||
if (!pm) {
|
||||
pci_pm_default_suspend(pci_dev);
|
||||
goto Fixup;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
if (pm->poweroff) {
|
||||
@ -1030,20 +1070,27 @@ static int pci_pm_poweroff(struct device *dev)
|
||||
return error;
|
||||
}
|
||||
|
||||
Fixup:
|
||||
pci_fixup_device(pci_fixup_suspend, pci_dev);
|
||||
|
||||
if (pcibios_pm_ops.poweroff)
|
||||
return pcibios_pm_ops.poweroff(dev);
|
||||
|
||||
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)
|
||||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
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)))
|
||||
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;
|
||||
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) {
|
||||
error = pcibios_pm_ops.restore_noirq(dev);
|
||||
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;
|
||||
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
|
||||
* 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 */
|
||||
|
||||
#define pci_pm_freeze NULL
|
||||
#define pci_pm_freeze_late NULL
|
||||
#define pci_pm_freeze_noirq NULL
|
||||
#define pci_pm_thaw NULL
|
||||
#define pci_pm_thaw_noirq NULL
|
||||
#define pci_pm_poweroff NULL
|
||||
#define pci_pm_poweroff_late NULL
|
||||
#define pci_pm_poweroff_noirq NULL
|
||||
#define pci_pm_restore 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,
|
||||
.complete = pci_pm_complete,
|
||||
.suspend = pci_pm_suspend,
|
||||
.suspend_late = pci_pm_suspend_late,
|
||||
.resume = pci_pm_resume,
|
||||
.freeze = pci_pm_freeze,
|
||||
.freeze_late = pci_pm_freeze_late,
|
||||
.thaw = pci_pm_thaw,
|
||||
.poweroff = pci_pm_poweroff,
|
||||
.poweroff_late = pci_pm_poweroff_late,
|
||||
.restore = pci_pm_restore,
|
||||
.suspend_noirq = pci_pm_suspend_noirq,
|
||||
.resume_noirq = pci_pm_resume_noirq,
|
||||
|
@ -2166,8 +2166,7 @@ bool pci_dev_keep_suspended(struct pci_dev *pci_dev)
|
||||
|
||||
if (!pm_runtime_suspended(dev)
|
||||
|| pci_target_state(pci_dev, wakeup) != pci_dev->current_state
|
||||
|| platform_pci_need_resume(pci_dev)
|
||||
|| (pci_dev->dev_flags & PCI_DEV_FLAGS_NEEDS_RESUME))
|
||||
|| platform_pci_need_resume(pci_dev))
|
||||
return false;
|
||||
|
||||
/*
|
||||
|
@ -355,7 +355,7 @@ int sr_configure_errgen(struct omap_sr *sr)
|
||||
u8 senp_shift, senn_shift;
|
||||
|
||||
if (!sr) {
|
||||
pr_warn("%s: NULL omap_sr from %pF\n",
|
||||
pr_warn("%s: NULL omap_sr from %pS\n",
|
||||
__func__, (void *)_RET_IP_);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -422,7 +422,7 @@ int sr_disable_errgen(struct omap_sr *sr)
|
||||
u32 vpboundint_en, vpboundint_st;
|
||||
|
||||
if (!sr) {
|
||||
pr_warn("%s: NULL omap_sr from %pF\n",
|
||||
pr_warn("%s: NULL omap_sr from %pS\n",
|
||||
__func__, (void *)_RET_IP_);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -477,7 +477,7 @@ int sr_configure_minmax(struct omap_sr *sr)
|
||||
u8 senp_shift, senn_shift;
|
||||
|
||||
if (!sr) {
|
||||
pr_warn("%s: NULL omap_sr from %pF\n",
|
||||
pr_warn("%s: NULL omap_sr from %pS\n",
|
||||
__func__, (void *)_RET_IP_);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -562,7 +562,7 @@ int sr_enable(struct omap_sr *sr, unsigned long volt)
|
||||
int ret;
|
||||
|
||||
if (!sr) {
|
||||
pr_warn("%s: NULL omap_sr from %pF\n",
|
||||
pr_warn("%s: NULL omap_sr from %pS\n",
|
||||
__func__, (void *)_RET_IP_);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -614,7 +614,7 @@ int sr_enable(struct omap_sr *sr, unsigned long volt)
|
||||
void sr_disable(struct omap_sr *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_);
|
||||
return;
|
||||
}
|
||||
|
@ -361,17 +361,6 @@ out:
|
||||
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)
|
||||
{
|
||||
int i;
|
||||
@ -466,7 +455,8 @@ static struct scp *init_scp(struct platform_device *pdev,
|
||||
genpd->name = data->name;
|
||||
genpd->power_off = scpsys_power_off;
|
||||
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;
|
||||
|
@ -358,17 +358,6 @@ static void rockchip_pd_detach_dev(struct generic_pm_domain *genpd,
|
||||
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,
|
||||
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.attach_dev = rockchip_pd_attach_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;
|
||||
if (pd_info->active_wakeup)
|
||||
pd->genpd.flags |= GENPD_FLAG_ACTIVE_WAKEUP;
|
||||
pm_genpd_init(&pd->genpd, NULL, false);
|
||||
|
||||
pmu->genpd_data.domains[id] = &pd->genpd;
|
||||
|
@ -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
|
||||
*/
|
||||
int acpi_os_read_iomem(void __iomem *virt_addr, u64 *value, u32 width);
|
||||
|
||||
#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_read_memory
|
||||
acpi_status
|
||||
acpi_os_read_memory(acpi_physical_address address, u64 *value, u32 width);
|
||||
|
@ -864,21 +864,16 @@ static inline void arch_reserve_mem_area(acpi_physical_address addr,
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ACPI) && defined(CONFIG_PM)
|
||||
int acpi_dev_runtime_suspend(struct device *dev);
|
||||
int acpi_dev_runtime_resume(struct device *dev);
|
||||
int acpi_dev_suspend(struct device *dev, bool wakeup);
|
||||
int acpi_dev_resume(struct device *dev);
|
||||
int acpi_subsys_runtime_suspend(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);
|
||||
#else
|
||||
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_subsys_runtime_suspend(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)
|
||||
{
|
||||
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)
|
||||
int acpi_dev_suspend_late(struct device *dev);
|
||||
int acpi_dev_resume_early(struct device *dev);
|
||||
int acpi_subsys_prepare(struct device *dev);
|
||||
void acpi_subsys_complete(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_suspend(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
|
||||
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_subsys_prepare(struct device *dev) { return 0; }
|
||||
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_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_suspend(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
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
@ -1254,4 +1257,13 @@ int acpi_irq_get(acpi_handle handle, unsigned int index, struct resource *res)
|
||||
}
|
||||
#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*/
|
||||
|
@ -6,15 +6,30 @@
|
||||
#define _LINUX_ARCH_TOPOLOGY_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/percpu.h>
|
||||
|
||||
void topology_normalize_cpu_scale(void);
|
||||
|
||||
struct device_node;
|
||||
bool topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu);
|
||||
|
||||
DECLARE_PER_CPU(unsigned long, cpu_scale);
|
||||
|
||||
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);
|
||||
|
||||
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_ */
|
||||
|
@ -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 void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq,
|
||||
unsigned long max_freq);
|
||||
|
||||
/* the following are really really optional */
|
||||
extern struct freq_attr cpufreq_freq_attr_scaling_available_freqs;
|
||||
extern struct freq_attr cpufreq_freq_attr_scaling_boost_freqs;
|
||||
|
@ -19,6 +19,13 @@
|
||||
|
||||
#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 */
|
||||
#define DEVFREQ_TRANSITION_NOTIFIER (0)
|
||||
|
||||
@ -84,8 +91,9 @@ struct devfreq_dev_status {
|
||||
* from devfreq_remove_device() call. If the user
|
||||
* has registered devfreq->nb at a notifier-head,
|
||||
* this is the time to unregister it.
|
||||
* @freq_table: Optional list of frequencies to support statistics.
|
||||
* @max_state: The size of freq_table.
|
||||
* @freq_table: Optional list of frequencies to support statistics
|
||||
* and freq_table must be generated in ascending order.
|
||||
* @max_state: The size of freq_table.
|
||||
*/
|
||||
struct devfreq_dev_profile {
|
||||
unsigned long initial_freq;
|
||||
@ -120,6 +128,8 @@ struct devfreq_dev_profile {
|
||||
* touch this.
|
||||
* @min_freq: Limit minimum 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.
|
||||
* @total_trans: Number of devfreq transitions
|
||||
* @trans_table: Statistics of devfreq transitions
|
||||
@ -153,6 +163,8 @@ struct devfreq {
|
||||
|
||||
unsigned long min_freq;
|
||||
unsigned long max_freq;
|
||||
unsigned long scaling_min_freq;
|
||||
unsigned long scaling_max_freq;
|
||||
bool stop_polling;
|
||||
|
||||
/* information for device frequency transition */
|
||||
|
@ -370,9 +370,6 @@ int subsys_virtual_register(struct bus_type *subsys,
|
||||
* @devnode: Callback to provide the devtmpfs.
|
||||
* @class_release: Called to release this class.
|
||||
* @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.
|
||||
* @ns_type: Callbacks so sysfs can detemine namespaces.
|
||||
* @namespace: Namespace of the device belongs to this class.
|
||||
@ -400,8 +397,6 @@ struct class {
|
||||
void (*class_release)(struct class *class);
|
||||
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);
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
mutex_lock(&dev->mutex);
|
||||
|
@ -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.
|
||||
*/
|
||||
static inline long freezable_schedule_timeout(long timeout)
|
||||
|
@ -206,13 +206,8 @@ enum pci_dev_flags {
|
||||
PCI_DEV_FLAGS_BRIDGE_XLATE_ROOT = (__force pci_dev_flags_t) (1 << 9),
|
||||
/* Do not use FLR even if device advertises PCI_AF_CAP */
|
||||
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 */
|
||||
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 {
|
||||
|
@ -550,6 +550,33 @@ struct pm_subsys_data {
|
||||
#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 {
|
||||
pm_message_t power_state;
|
||||
unsigned int can_wakeup:1;
|
||||
@ -561,6 +588,7 @@ struct dev_pm_info {
|
||||
bool is_late_suspended:1;
|
||||
bool early_init:1; /* Owned by the PM core */
|
||||
bool direct_complete:1; /* Owned by the PM core */
|
||||
u32 driver_flags;
|
||||
spinlock_t lock;
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
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(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 */
|
||||
|
||||
|
@ -18,9 +18,10 @@
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
/* 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_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_PM_CLK (1U << 0) /* PM domain uses PM clk */
|
||||
#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_ACTIVE_WAKEUP (1U << 3) /* Keep devices active if wakeup */
|
||||
|
||||
enum gpd_status {
|
||||
GPD_STATE_ACTIVE = 0, /* PM domain is active */
|
||||
@ -35,7 +36,6 @@ struct dev_power_governor {
|
||||
struct gpd_dev_ops {
|
||||
int (*start)(struct device *dev);
|
||||
int (*stop)(struct device *dev);
|
||||
bool (*active_wakeup)(struct device *dev);
|
||||
};
|
||||
|
||||
struct genpd_power_state {
|
||||
@ -64,8 +64,11 @@ struct generic_pm_domain {
|
||||
unsigned int device_count; /* Number of devices */
|
||||
unsigned int suspended_count; /* System suspend device counter */
|
||||
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_on)(struct generic_pm_domain *domain);
|
||||
int (*set_performance_state)(struct generic_pm_domain *genpd,
|
||||
unsigned int state);
|
||||
struct gpd_dev_ops dev_ops;
|
||||
s64 max_off_time_ns; /* Maximum allowed "suspended" time. */
|
||||
bool max_off_time_changed;
|
||||
@ -121,6 +124,7 @@ struct generic_pm_domain_data {
|
||||
struct pm_domain_data base;
|
||||
struct gpd_timing_data td;
|
||||
struct notifier_block nb;
|
||||
unsigned int performance_state;
|
||||
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,
|
||||
struct dev_power_governor *gov, bool is_off);
|
||||
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 pm_domain_always_on_gov;
|
||||
@ -188,6 +194,12 @@ static inline int pm_genpd_remove(struct generic_pm_domain *genpd)
|
||||
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 pm_domain_always_on_gov (*(struct dev_power_governor *)(NULL))
|
||||
#endif
|
||||
|
@ -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);
|
||||
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));
|
||||
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_sharing_cpus(struct device *cpu_dev, const 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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -28,19 +28,21 @@ enum pm_qos_flags_status {
|
||||
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_NETWORK_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC)
|
||||
#define PM_QOS_NETWORK_THROUGHPUT_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_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_REMOTE_WAKEUP (1 << 1)
|
||||
|
||||
struct pm_qos_request {
|
||||
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)
|
||||
{
|
||||
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
|
||||
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)
|
||||
{ return PM_QOS_FLAGS_UNDEFINED; }
|
||||
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)
|
||||
{ return 0; }
|
||||
{ return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; }
|
||||
static inline int dev_pm_qos_add_request(struct device *dev,
|
||||
struct dev_pm_qos_request *req,
|
||||
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; }
|
||||
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_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
|
||||
|
@ -259,20 +259,6 @@ config APM_EMULATION
|
||||
anything, try disabling/enabling this option (or disabling/enabling
|
||||
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
|
||||
def_bool y
|
||||
depends on PM && HAVE_CLK
|
||||
|
@ -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++) {
|
||||
ret = register_pm_qos_misc(pm_qos_array[i], d);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "pm_qos_param: %s setup failed\n",
|
||||
pm_qos_array[i]->name);
|
||||
pr_err("%s: %s setup failed\n",
|
||||
__func__, pm_qos_array[i]->name);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "PM: " fmt
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mm.h>
|
||||
@ -967,7 +969,7 @@ void __init __register_nosave_region(unsigned long start_pfn,
|
||||
region->end_pfn = end_pfn;
|
||||
list_add_tail(®ion->list, &nosave_regions);
|
||||
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) 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) {
|
||||
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->end_pfn << PAGE_SHIFT)
|
||||
- 1);
|
||||
@ -1095,7 +1097,7 @@ int create_basic_memory_bitmaps(void)
|
||||
free_pages_map = bm2;
|
||||
mark_nosave_pages(forbidden_pages_map);
|
||||
|
||||
pr_debug("PM: Basic memory bitmaps created\n");
|
||||
pr_debug("Basic memory bitmaps created\n");
|
||||
|
||||
return 0;
|
||||
|
||||
@ -1131,7 +1133,7 @@ void free_basic_memory_bitmaps(void)
|
||||
memory_bm_free(bm2, PG_UNSAFE_CLEAR);
|
||||
kfree(bm2);
|
||||
|
||||
pr_debug("PM: Basic memory bitmaps freed\n");
|
||||
pr_debug("Basic memory bitmaps freed\n");
|
||||
}
|
||||
|
||||
void clear_free_pages(void)
|
||||
@ -1152,7 +1154,7 @@ void clear_free_pages(void)
|
||||
pfn = memory_bm_next_pfn(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 */
|
||||
}
|
||||
|
||||
@ -1690,7 +1692,7 @@ int hibernate_preallocate_memory(void)
|
||||
ktime_t start, stop;
|
||||
int error;
|
||||
|
||||
printk(KERN_INFO "PM: Preallocating image memory... ");
|
||||
pr_info("Preallocating image memory... ");
|
||||
start = ktime_get();
|
||||
|
||||
error = memory_bm_create(&orig_bm, GFP_IMAGE, PG_ANY);
|
||||
@ -1821,13 +1823,13 @@ int hibernate_preallocate_memory(void)
|
||||
|
||||
out:
|
||||
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");
|
||||
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
printk(KERN_CONT "\n");
|
||||
pr_cont("\n");
|
||||
swsusp_free();
|
||||
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);
|
||||
|
||||
nr_pages += count_pages_for_highmem(nr_highmem);
|
||||
pr_debug("PM: Normal pages needed: %u + %u, available pages: %u\n",
|
||||
nr_pages, PAGES_FOR_IO, free);
|
||||
pr_debug("Normal pages needed: %u + %u, available pages: %u\n",
|
||||
nr_pages, PAGES_FOR_IO, free);
|
||||
|
||||
return free > nr_pages + PAGES_FOR_IO;
|
||||
}
|
||||
@ -1961,20 +1963,20 @@ asmlinkage __visible int swsusp_save(void)
|
||||
{
|
||||
unsigned int nr_pages, nr_highmem;
|
||||
|
||||
printk(KERN_INFO "PM: Creating hibernation image:\n");
|
||||
pr_info("Creating hibernation image:\n");
|
||||
|
||||
drain_local_pages(NULL);
|
||||
nr_pages = count_data_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)) {
|
||||
printk(KERN_ERR "PM: Not enough free memory\n");
|
||||
pr_err("Not enough free memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (swsusp_alloc(©_bm, nr_pages, nr_highmem)) {
|
||||
printk(KERN_ERR "PM: Memory allocation failed\n");
|
||||
pr_err("Memory allocation failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@ -1995,8 +1997,7 @@ asmlinkage __visible int swsusp_save(void)
|
||||
nr_copy_pages = nr_pages;
|
||||
nr_meta_pages = DIV_ROUND_UP(nr_pages * sizeof(long), PAGE_SIZE);
|
||||
|
||||
printk(KERN_INFO "PM: Hibernation image created (%d pages copied)\n",
|
||||
nr_pages);
|
||||
pr_info("Hibernation image created (%d pages copied)\n", nr_pages);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2170,7 +2171,7 @@ static int check_header(struct swsusp_info *info)
|
||||
if (!reason && info->num_physpages != get_num_physpages())
|
||||
reason = "memory size";
|
||||
if (reason) {
|
||||
printk(KERN_ERR "PM: Image mismatch: %s\n", reason);
|
||||
pr_err("Image mismatch: %s\n", reason);
|
||||
return -EPERM;
|
||||
}
|
||||
return 0;
|
||||
|
@ -437,7 +437,6 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
|
||||
error = suspend_ops->enter(state);
|
||||
trace_suspend_resume(TPS("machine_suspend"),
|
||||
state, false);
|
||||
events_check_enabled = false;
|
||||
} else if (*wakeup) {
|
||||
error = -EBUSY;
|
||||
}
|
||||
@ -582,6 +581,7 @@ static int enter_state(suspend_state_t state)
|
||||
pm_restore_gfp_mask();
|
||||
|
||||
Finish:
|
||||
events_check_enabled = false;
|
||||
pm_pr_dbg("Finishing wakeup.\n");
|
||||
suspend_finish();
|
||||
Unlock:
|
||||
|
@ -12,6 +12,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "PM: " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/file.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;
|
||||
|
||||
if (bio->bi_status) {
|
||||
printk(KERN_ALERT "Read-error on swap-device (%u:%u:%Lu)\n",
|
||||
MAJOR(bio_dev(bio)), MINOR(bio_dev(bio)),
|
||||
(unsigned long long)bio->bi_iter.bi_sector);
|
||||
pr_alert("Read-error on swap-device (%u:%u:%Lu)\n",
|
||||
MAJOR(bio_dev(bio)), MINOR(bio_dev(bio)),
|
||||
(unsigned long long)bio->bi_iter.bi_sector);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) {
|
||||
printk(KERN_ERR "PM: Adding page to bio failed at %llu\n",
|
||||
(unsigned long long)bio->bi_iter.bi_sector);
|
||||
pr_err("Adding page to bio failed at %llu\n",
|
||||
(unsigned long long)bio->bi_iter.bi_sector);
|
||||
bio_put(bio);
|
||||
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,
|
||||
swsusp_resume_block, swsusp_header, NULL);
|
||||
} else {
|
||||
printk(KERN_ERR "PM: Swap header not found!\n");
|
||||
pr_err("Swap header not found!\n");
|
||||
error = -ENODEV;
|
||||
}
|
||||
return error;
|
||||
@ -413,8 +415,7 @@ static int get_swap_writer(struct swap_map_handle *handle)
|
||||
ret = swsusp_swap_check();
|
||||
if (ret) {
|
||||
if (ret != -ENOSPC)
|
||||
printk(KERN_ERR "PM: Cannot find swap device, try "
|
||||
"swapon -a.\n");
|
||||
pr_err("Cannot find swap device, try swapon -a\n");
|
||||
return ret;
|
||||
}
|
||||
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) {
|
||||
flush_swap_writer(handle);
|
||||
printk(KERN_INFO "PM: S");
|
||||
pr_info("S");
|
||||
error = mark_swapfiles(handle, flags);
|
||||
printk("|\n");
|
||||
pr_cont("|\n");
|
||||
}
|
||||
|
||||
if (error)
|
||||
@ -542,7 +543,7 @@ static int save_image(struct swap_map_handle *handle,
|
||||
|
||||
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);
|
||||
m = nr_to_write / 10;
|
||||
if (!m)
|
||||
@ -557,8 +558,8 @@ static int save_image(struct swap_map_handle *handle,
|
||||
if (ret)
|
||||
break;
|
||||
if (!(nr_pages % m))
|
||||
printk(KERN_INFO "PM: Image saving progress: %3d%%\n",
|
||||
nr_pages / m * 10);
|
||||
pr_info("Image saving progress: %3d%%\n",
|
||||
nr_pages / m * 10);
|
||||
nr_pages++;
|
||||
}
|
||||
err2 = hib_wait_io(&hb);
|
||||
@ -566,7 +567,7 @@ static int save_image(struct swap_map_handle *handle,
|
||||
if (!ret)
|
||||
ret = err2;
|
||||
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");
|
||||
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);
|
||||
if (!page) {
|
||||
printk(KERN_ERR "PM: Failed to allocate LZO page\n");
|
||||
pr_err("Failed to allocate LZO page\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_clean;
|
||||
}
|
||||
|
||||
data = vmalloc(sizeof(*data) * nr_threads);
|
||||
if (!data) {
|
||||
printk(KERN_ERR "PM: Failed to allocate LZO data\n");
|
||||
pr_err("Failed to allocate LZO data\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_clean;
|
||||
}
|
||||
@ -708,7 +709,7 @@ static int save_image_lzo(struct swap_map_handle *handle,
|
||||
|
||||
crc = kmalloc(sizeof(*crc), GFP_KERNEL);
|
||||
if (!crc) {
|
||||
printk(KERN_ERR "PM: Failed to allocate crc\n");
|
||||
pr_err("Failed to allocate crc\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_clean;
|
||||
}
|
||||
@ -726,8 +727,7 @@ static int save_image_lzo(struct swap_map_handle *handle,
|
||||
"image_compress/%u", thr);
|
||||
if (IS_ERR(data[thr].thr)) {
|
||||
data[thr].thr = NULL;
|
||||
printk(KERN_ERR
|
||||
"PM: Cannot start compression threads\n");
|
||||
pr_err("Cannot start compression threads\n");
|
||||
ret = -ENOMEM;
|
||||
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");
|
||||
if (IS_ERR(crc->thr)) {
|
||||
crc->thr = NULL;
|
||||
printk(KERN_ERR "PM: Cannot start CRC32 thread\n");
|
||||
pr_err("Cannot start CRC32 thread\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_clean;
|
||||
}
|
||||
@ -760,10 +760,9 @@ static int save_image_lzo(struct swap_map_handle *handle,
|
||||
*/
|
||||
handle->reqd_free_pages = reqd_free_pages();
|
||||
|
||||
printk(KERN_INFO
|
||||
"PM: Using %u thread(s) for compression.\n"
|
||||
"PM: Compressing and saving image data (%u pages)...\n",
|
||||
nr_threads, nr_to_write);
|
||||
pr_info("Using %u thread(s) for compression\n", nr_threads);
|
||||
pr_info("Compressing and saving image data (%u pages)...\n",
|
||||
nr_to_write);
|
||||
m = nr_to_write / 10;
|
||||
if (!m)
|
||||
m = 1;
|
||||
@ -783,10 +782,8 @@ static int save_image_lzo(struct swap_map_handle *handle,
|
||||
data_of(*snapshot), PAGE_SIZE);
|
||||
|
||||
if (!(nr_pages % m))
|
||||
printk(KERN_INFO
|
||||
"PM: Image saving progress: "
|
||||
"%3d%%\n",
|
||||
nr_pages / m * 10);
|
||||
pr_info("Image saving progress: %3d%%\n",
|
||||
nr_pages / m * 10);
|
||||
nr_pages++;
|
||||
}
|
||||
if (!off)
|
||||
@ -813,15 +810,14 @@ static int save_image_lzo(struct swap_map_handle *handle,
|
||||
ret = data[thr].ret;
|
||||
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "PM: LZO compression failed\n");
|
||||
pr_err("LZO compression failed\n");
|
||||
goto out_finish;
|
||||
}
|
||||
|
||||
if (unlikely(!data[thr].cmp_len ||
|
||||
data[thr].cmp_len >
|
||||
lzo1x_worst_compress(data[thr].unc_len))) {
|
||||
printk(KERN_ERR
|
||||
"PM: Invalid LZO compressed length\n");
|
||||
pr_err("Invalid LZO compressed length\n");
|
||||
ret = -1;
|
||||
goto out_finish;
|
||||
}
|
||||
@ -857,7 +853,7 @@ out_finish:
|
||||
if (!ret)
|
||||
ret = err2;
|
||||
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");
|
||||
out_clean:
|
||||
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 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;
|
||||
return free_swap > required;
|
||||
@ -915,12 +911,12 @@ int swsusp_write(unsigned int flags)
|
||||
pages = snapshot_get_image_size();
|
||||
error = get_swap_writer(&handle);
|
||||
if (error) {
|
||||
printk(KERN_ERR "PM: Cannot get swap writer\n");
|
||||
pr_err("Cannot get swap writer\n");
|
||||
return error;
|
||||
}
|
||||
if (flags & SF_NOCOMPRESS_MODE) {
|
||||
if (!enough_swap(pages, flags)) {
|
||||
printk(KERN_ERR "PM: Not enough free swap\n");
|
||||
pr_err("Not enough free swap\n");
|
||||
error = -ENOSPC;
|
||||
goto out_finish;
|
||||
}
|
||||
@ -1068,8 +1064,7 @@ static int load_image(struct swap_map_handle *handle,
|
||||
hib_init_batch(&hb);
|
||||
|
||||
clean_pages_on_read = true;
|
||||
printk(KERN_INFO "PM: Loading image data pages (%u pages)...\n",
|
||||
nr_to_read);
|
||||
pr_info("Loading image data pages (%u pages)...\n", nr_to_read);
|
||||
m = nr_to_read / 10;
|
||||
if (!m)
|
||||
m = 1;
|
||||
@ -1087,8 +1082,8 @@ static int load_image(struct swap_map_handle *handle,
|
||||
if (ret)
|
||||
break;
|
||||
if (!(nr_pages % m))
|
||||
printk(KERN_INFO "PM: Image loading progress: %3d%%\n",
|
||||
nr_pages / m * 10);
|
||||
pr_info("Image loading progress: %3d%%\n",
|
||||
nr_pages / m * 10);
|
||||
nr_pages++;
|
||||
}
|
||||
err2 = hib_wait_io(&hb);
|
||||
@ -1096,7 +1091,7 @@ static int load_image(struct swap_map_handle *handle,
|
||||
if (!ret)
|
||||
ret = err2;
|
||||
if (!ret) {
|
||||
printk(KERN_INFO "PM: Image loading done.\n");
|
||||
pr_info("Image loading done\n");
|
||||
snapshot_write_finalize(snapshot);
|
||||
if (!snapshot_image_loaded(snapshot))
|
||||
ret = -ENODATA;
|
||||
@ -1190,14 +1185,14 @@ static int load_image_lzo(struct swap_map_handle *handle,
|
||||
|
||||
page = vmalloc(sizeof(*page) * LZO_MAX_RD_PAGES);
|
||||
if (!page) {
|
||||
printk(KERN_ERR "PM: Failed to allocate LZO page\n");
|
||||
pr_err("Failed to allocate LZO page\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_clean;
|
||||
}
|
||||
|
||||
data = vmalloc(sizeof(*data) * nr_threads);
|
||||
if (!data) {
|
||||
printk(KERN_ERR "PM: Failed to allocate LZO data\n");
|
||||
pr_err("Failed to allocate LZO data\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_clean;
|
||||
}
|
||||
@ -1206,7 +1201,7 @@ static int load_image_lzo(struct swap_map_handle *handle,
|
||||
|
||||
crc = kmalloc(sizeof(*crc), GFP_KERNEL);
|
||||
if (!crc) {
|
||||
printk(KERN_ERR "PM: Failed to allocate crc\n");
|
||||
pr_err("Failed to allocate crc\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_clean;
|
||||
}
|
||||
@ -1226,8 +1221,7 @@ static int load_image_lzo(struct swap_map_handle *handle,
|
||||
"image_decompress/%u", thr);
|
||||
if (IS_ERR(data[thr].thr)) {
|
||||
data[thr].thr = NULL;
|
||||
printk(KERN_ERR
|
||||
"PM: Cannot start decompression threads\n");
|
||||
pr_err("Cannot start decompression threads\n");
|
||||
ret = -ENOMEM;
|
||||
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");
|
||||
if (IS_ERR(crc->thr)) {
|
||||
crc->thr = NULL;
|
||||
printk(KERN_ERR "PM: Cannot start CRC32 thread\n");
|
||||
pr_err("Cannot start CRC32 thread\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_clean;
|
||||
}
|
||||
@ -1274,8 +1268,7 @@ static int load_image_lzo(struct swap_map_handle *handle,
|
||||
if (!page[i]) {
|
||||
if (i < LZO_CMP_PAGES) {
|
||||
ring_size = i;
|
||||
printk(KERN_ERR
|
||||
"PM: Failed to allocate LZO pages\n");
|
||||
pr_err("Failed to allocate LZO pages\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_clean;
|
||||
} else {
|
||||
@ -1285,10 +1278,9 @@ static int load_image_lzo(struct swap_map_handle *handle,
|
||||
}
|
||||
want = ring_size = i;
|
||||
|
||||
printk(KERN_INFO
|
||||
"PM: Using %u thread(s) for decompression.\n"
|
||||
"PM: Loading and decompressing image data (%u pages)...\n",
|
||||
nr_threads, nr_to_read);
|
||||
pr_info("Using %u thread(s) for decompression\n", nr_threads);
|
||||
pr_info("Loading and decompressing image data (%u pages)...\n",
|
||||
nr_to_read);
|
||||
m = nr_to_read / 10;
|
||||
if (!m)
|
||||
m = 1;
|
||||
@ -1348,8 +1340,7 @@ static int load_image_lzo(struct swap_map_handle *handle,
|
||||
if (unlikely(!data[thr].cmp_len ||
|
||||
data[thr].cmp_len >
|
||||
lzo1x_worst_compress(LZO_UNC_SIZE))) {
|
||||
printk(KERN_ERR
|
||||
"PM: Invalid LZO compressed length\n");
|
||||
pr_err("Invalid LZO compressed length\n");
|
||||
ret = -1;
|
||||
goto out_finish;
|
||||
}
|
||||
@ -1400,16 +1391,14 @@ static int load_image_lzo(struct swap_map_handle *handle,
|
||||
ret = data[thr].ret;
|
||||
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR
|
||||
"PM: LZO decompression failed\n");
|
||||
pr_err("LZO decompression failed\n");
|
||||
goto out_finish;
|
||||
}
|
||||
|
||||
if (unlikely(!data[thr].unc_len ||
|
||||
data[thr].unc_len > LZO_UNC_SIZE ||
|
||||
data[thr].unc_len & (PAGE_SIZE - 1))) {
|
||||
printk(KERN_ERR
|
||||
"PM: Invalid LZO uncompressed length\n");
|
||||
pr_err("Invalid LZO uncompressed length\n");
|
||||
ret = -1;
|
||||
goto out_finish;
|
||||
}
|
||||
@ -1420,10 +1409,8 @@ static int load_image_lzo(struct swap_map_handle *handle,
|
||||
data[thr].unc + off, PAGE_SIZE);
|
||||
|
||||
if (!(nr_pages % m))
|
||||
printk(KERN_INFO
|
||||
"PM: Image loading progress: "
|
||||
"%3d%%\n",
|
||||
nr_pages / m * 10);
|
||||
pr_info("Image loading progress: %3d%%\n",
|
||||
nr_pages / m * 10);
|
||||
nr_pages++;
|
||||
|
||||
ret = snapshot_write_next(snapshot);
|
||||
@ -1448,15 +1435,14 @@ out_finish:
|
||||
}
|
||||
stop = ktime_get();
|
||||
if (!ret) {
|
||||
printk(KERN_INFO "PM: Image loading done.\n");
|
||||
pr_info("Image loading done\n");
|
||||
snapshot_write_finalize(snapshot);
|
||||
if (!snapshot_image_loaded(snapshot))
|
||||
ret = -ENODATA;
|
||||
if (!ret) {
|
||||
if (swsusp_header->flags & SF_CRC32_MODE) {
|
||||
if(handle->crc32 != swsusp_header->crc32) {
|
||||
printk(KERN_ERR
|
||||
"PM: Invalid image CRC32!\n");
|
||||
pr_err("Invalid image CRC32!\n");
|
||||
ret = -ENODATA;
|
||||
}
|
||||
}
|
||||
@ -1513,9 +1499,9 @@ int swsusp_read(unsigned int *flags_p)
|
||||
swap_reader_finish(&handle);
|
||||
end:
|
||||
if (!error)
|
||||
pr_debug("PM: Image successfully loaded\n");
|
||||
pr_debug("Image successfully loaded\n");
|
||||
else
|
||||
pr_debug("PM: Error %d resuming\n", error);
|
||||
pr_debug("Error %d resuming\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -1552,13 +1538,13 @@ put:
|
||||
if (error)
|
||||
blkdev_put(hib_resume_bdev, FMODE_READ);
|
||||
else
|
||||
pr_debug("PM: Image signature found, resuming\n");
|
||||
pr_debug("Image signature found, resuming\n");
|
||||
} else {
|
||||
error = PTR_ERR(hib_resume_bdev);
|
||||
}
|
||||
|
||||
if (error)
|
||||
pr_debug("PM: Image not found (code %d)\n", error);
|
||||
pr_debug("Image not found (code %d)\n", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -1570,7 +1556,7 @@ put:
|
||||
void swsusp_close(fmode_t mode)
|
||||
{
|
||||
if (IS_ERR(hib_resume_bdev)) {
|
||||
pr_debug("PM: Image device not initialised\n");
|
||||
pr_debug("Image device not initialised\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1594,7 +1580,7 @@ int swsusp_unmark(void)
|
||||
swsusp_resume_block,
|
||||
swsusp_header, NULL);
|
||||
} else {
|
||||
printk(KERN_ERR "PM: Cannot find swsusp signature!\n");
|
||||
pr_err("Cannot find swsusp signature!\n");
|
||||
error = -ENODEV;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
* 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;
|
||||
|
||||
/* Reset cached freq as next_freq has changed */
|
||||
sg_policy->cached_raw_freq = 0;
|
||||
}
|
||||
}
|
||||
sugov_update_commit(sg_policy, time, next_f);
|
||||
}
|
||||
|
3
tools/power/cpupower/.gitignore
vendored
3
tools/power/cpupower/.gitignore
vendored
@ -1,7 +1,6 @@
|
||||
.libs
|
||||
libcpupower.so
|
||||
libcpupower.so.0
|
||||
libcpupower.so.0.0.0
|
||||
libcpupower.so.*
|
||||
build/ccdv
|
||||
cpufreq-info
|
||||
cpufreq-set
|
||||
|
@ -30,6 +30,8 @@ OUTDIR := $(shell cd $(OUTPUT) && /bin/pwd)
|
||||
$(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist))
|
||||
endif
|
||||
|
||||
include ../../scripts/Makefile.arch
|
||||
|
||||
# --- CONFIGURATION BEGIN ---
|
||||
|
||||
# Set the following to `true' to make a unstripped, unoptimized
|
||||
@ -79,7 +81,11 @@ bindir ?= /usr/bin
|
||||
sbindir ?= /usr/sbin
|
||||
mandir ?= /usr/man
|
||||
includedir ?= /usr/include
|
||||
ifeq ($(IS_64_BIT), 1)
|
||||
libdir ?= /usr/lib64
|
||||
else
|
||||
libdir ?= /usr/lib
|
||||
endif
|
||||
localedir ?= /usr/share/locale
|
||||
docdir ?= /usr/share/doc/packages/cpupower
|
||||
confdir ?= /etc/
|
||||
|
@ -93,8 +93,6 @@ static void print_speed(unsigned long speed)
|
||||
if (speed > 1000000)
|
||||
printf("%u.%06u GHz", ((unsigned int) speed/1000000),
|
||||
((unsigned int) speed%1000000));
|
||||
else if (speed > 100000)
|
||||
printf("%u MHz", (unsigned int) speed);
|
||||
else if (speed > 1000)
|
||||
printf("%u.%03u MHz", ((unsigned int) speed/1000),
|
||||
(unsigned int) (speed%1000));
|
||||
|
Loading…
Reference in New Issue
Block a user