You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6: (26 commits) PM / Wakeup: Show wakeup sources statistics in debugfs PM: Introduce library for device-specific OPPs (v7) PM: Add sysfs attr for rechecking dev hash from PM trace PM: Lock PM device list mutex in show_dev_hash() PM / Runtime: Remove idle notification after failing suspend PM / Hibernate: Modify signature used to mark swap PM / Runtime: Reduce code duplication in core helper functions PM: Allow wakeup events to abort freezing of tasks PM: runtime: add missed pm_request_autosuspend PM / Hibernate: Make some boot messages look less scary PM / Runtime: Implement autosuspend support PM / Runtime: Add no_callbacks flag PM / Runtime: Combine runtime PM entry points PM / Runtime: Merge synchronous and async runtime routines PM / Runtime: Replace boolean arguments with bitflags PM / Runtime: Move code in drivers/base/power/runtime.c sysfs: Add sysfs_merge_group() and sysfs_unmerge_group() PM: Fix potential issue with failing asynchronous suspend PM / Wakeup: Introduce wakeup source objects and event statistics (v3) PM: Fix signed/unsigned warning in dpm_show_time() ...
This commit is contained in:
@@ -77,3 +77,91 @@ Description:
|
||||
devices this attribute is set to "enabled" by bus type code or
|
||||
device drivers and in that cases it should be safe to leave the
|
||||
default value.
|
||||
|
||||
What: /sys/devices/.../power/wakeup_count
|
||||
Date: September 2010
|
||||
Contact: Rafael J. Wysocki <rjw@sisk.pl>
|
||||
Description:
|
||||
The /sys/devices/.../wakeup_count attribute contains the number
|
||||
of signaled wakeup events associated with the device. This
|
||||
attribute is read-only. If the device is not enabled to wake up
|
||||
the system from sleep states, this attribute is empty.
|
||||
|
||||
What: /sys/devices/.../power/wakeup_active_count
|
||||
Date: September 2010
|
||||
Contact: Rafael J. Wysocki <rjw@sisk.pl>
|
||||
Description:
|
||||
The /sys/devices/.../wakeup_active_count attribute contains the
|
||||
number of times the processing of wakeup events associated with
|
||||
the device was completed (at the kernel level). This attribute
|
||||
is read-only. If the device is not enabled to wake up the
|
||||
system from sleep states, this attribute is empty.
|
||||
|
||||
What: /sys/devices/.../power/wakeup_hit_count
|
||||
Date: September 2010
|
||||
Contact: Rafael J. Wysocki <rjw@sisk.pl>
|
||||
Description:
|
||||
The /sys/devices/.../wakeup_hit_count attribute contains the
|
||||
number of times the processing of a wakeup event associated with
|
||||
the device might prevent the system from entering a sleep state.
|
||||
This attribute is read-only. If the device is not enabled to
|
||||
wake up the system from sleep states, this attribute is empty.
|
||||
|
||||
What: /sys/devices/.../power/wakeup_active
|
||||
Date: September 2010
|
||||
Contact: Rafael J. Wysocki <rjw@sisk.pl>
|
||||
Description:
|
||||
The /sys/devices/.../wakeup_active attribute contains either 1,
|
||||
or 0, depending on whether or not a wakeup event associated with
|
||||
the device is being processed (1). This attribute is read-only.
|
||||
If the device is not enabled to wake up the system from sleep
|
||||
states, this attribute is empty.
|
||||
|
||||
What: /sys/devices/.../power/wakeup_total_time_ms
|
||||
Date: September 2010
|
||||
Contact: Rafael J. Wysocki <rjw@sisk.pl>
|
||||
Description:
|
||||
The /sys/devices/.../wakeup_total_time_ms attribute contains
|
||||
the total time of processing wakeup events associated with the
|
||||
device, in milliseconds. This attribute is read-only. If the
|
||||
device is not enabled to wake up the system from sleep states,
|
||||
this attribute is empty.
|
||||
|
||||
What: /sys/devices/.../power/wakeup_max_time_ms
|
||||
Date: September 2010
|
||||
Contact: Rafael J. Wysocki <rjw@sisk.pl>
|
||||
Description:
|
||||
The /sys/devices/.../wakeup_max_time_ms attribute contains
|
||||
the maximum time of processing a single wakeup event associated
|
||||
with the device, in milliseconds. This attribute is read-only.
|
||||
If the device is not enabled to wake up the system from sleep
|
||||
states, this attribute is empty.
|
||||
|
||||
What: /sys/devices/.../power/wakeup_last_time_ms
|
||||
Date: September 2010
|
||||
Contact: Rafael J. Wysocki <rjw@sisk.pl>
|
||||
Description:
|
||||
The /sys/devices/.../wakeup_last_time_ms attribute contains
|
||||
the value of the monotonic clock corresponding to the time of
|
||||
signaling the last wakeup event associated with the device, in
|
||||
milliseconds. This attribute is read-only. If the device is
|
||||
not enabled to wake up the system from sleep states, this
|
||||
attribute is empty.
|
||||
|
||||
What: /sys/devices/.../power/autosuspend_delay_ms
|
||||
Date: September 2010
|
||||
Contact: Alan Stern <stern@rowland.harvard.edu>
|
||||
Description:
|
||||
The /sys/devices/.../power/autosuspend_delay_ms attribute
|
||||
contains the autosuspend delay value (in milliseconds). Some
|
||||
drivers do not want their device to suspend as soon as it
|
||||
becomes idle at run time; they want the device to remain
|
||||
inactive for a certain minimum period of time first. That
|
||||
period is called the autosuspend delay. Negative values will
|
||||
prevent the device from being suspended at run time (similar
|
||||
to writing "on" to the power/control attribute). Values >=
|
||||
1000 will cause the autosuspend timer expiration to be rounded
|
||||
up to the nearest second.
|
||||
|
||||
Not all drivers support this attribute. If it isn't supported,
|
||||
attempts to read or write it will yield I/O errors.
|
||||
|
||||
@@ -99,9 +99,38 @@ Description:
|
||||
|
||||
dmesg -s 1000000 | grep 'hash matches'
|
||||
|
||||
If you do not get any matches (or they appear to be false
|
||||
positives), it is possible that the last PM event point
|
||||
referred to a device created by a loadable kernel module. In
|
||||
this case cat /sys/power/pm_trace_dev_match (see below) after
|
||||
your system is started up and the kernel modules are loaded.
|
||||
|
||||
CAUTION: Using it will cause your machine's real-time (CMOS)
|
||||
clock to be set to a random invalid time after a resume.
|
||||
|
||||
What; /sys/power/pm_trace_dev_match
|
||||
Date: October 2010
|
||||
Contact: James Hogan <james@albanarts.com>
|
||||
Description:
|
||||
The /sys/power/pm_trace_dev_match file contains the name of the
|
||||
device associated with the last PM event point saved in the RTC
|
||||
across reboots when pm_trace has been used. More precisely it
|
||||
contains the list of current devices (including those
|
||||
registered by loadable kernel modules since boot) which match
|
||||
the device hash in the RTC at boot, with a newline after each
|
||||
one.
|
||||
|
||||
The advantage of this file over the hash matches printed to the
|
||||
kernel log (see /sys/power/pm_trace), is that it includes
|
||||
devices created after boot by loadable kernel modules.
|
||||
|
||||
Due to the small hash size necessary to fit in the RTC, it is
|
||||
possible that more than one device matches the hash, in which
|
||||
case further investigation is required to determine which
|
||||
device is causing the problem. Note that genuine RTC clock
|
||||
values (such as when pm_trace has not been used), can still
|
||||
match a device and output it's name here.
|
||||
|
||||
What: /sys/power/pm_async
|
||||
Date: January 2009
|
||||
Contact: Rafael J. Wysocki <rjw@sisk.pl>
|
||||
|
||||
@@ -2170,6 +2170,11 @@ and is between 256 and 4096 characters. It is defined in the file
|
||||
in <PAGE_SIZE> units (needed only for swap files).
|
||||
See Documentation/power/swsusp-and-swap-files.txt
|
||||
|
||||
hibernate= [HIBERNATION]
|
||||
noresume Don't check if there's a hibernation image
|
||||
present during boot.
|
||||
nocompress Don't compress/decompress hibernation images.
|
||||
|
||||
retain_initrd [RAM] Keep initrd memory after extraction
|
||||
|
||||
rhash_entries= [KNL,NET]
|
||||
|
||||
@@ -14,6 +14,8 @@ interface.txt
|
||||
- Power management user interface in /sys/power
|
||||
notifiers.txt
|
||||
- Registering suspend notifiers in device drivers
|
||||
opp.txt
|
||||
- Operating Performance Point library
|
||||
pci.txt
|
||||
- How the PCI Subsystem Does Power Management
|
||||
pm_qos_interface.txt
|
||||
|
||||
@@ -57,7 +57,7 @@ smallest image possible. In particular, if "0" is written to this file, the
|
||||
suspend image will be as small as possible.
|
||||
|
||||
Reading from this file will display the current image size limit, which
|
||||
is set to 500 MB by default.
|
||||
is set to 2/5 of available RAM by default.
|
||||
|
||||
/sys/power/pm_trace controls the code which saves the last PM event point in
|
||||
the RTC across reboots, so that you can debug a machine that just hangs
|
||||
|
||||
@@ -0,0 +1,375 @@
|
||||
*=============*
|
||||
* OPP Library *
|
||||
*=============*
|
||||
|
||||
(C) 2009-2010 Nishanth Menon <nm@ti.com>, Texas Instruments Incorporated
|
||||
|
||||
Contents
|
||||
--------
|
||||
1. Introduction
|
||||
2. Initial OPP List Registration
|
||||
3. OPP Search Functions
|
||||
4. OPP Availability Control Functions
|
||||
5. OPP Data Retrieval Functions
|
||||
6. Cpufreq Table Generation
|
||||
7. Data Structures
|
||||
|
||||
1. Introduction
|
||||
===============
|
||||
Complex SoCs of today consists of a multiple sub-modules working in conjunction.
|
||||
In an operational system executing varied use cases, not all modules in the SoC
|
||||
need to function at their highest performing frequency all the time. To
|
||||
facilitate this, sub-modules in a SoC are grouped into domains, allowing some
|
||||
domains to run at lower voltage and frequency while other domains are loaded
|
||||
more. The set of discrete tuples consisting of frequency and voltage pairs that
|
||||
the device will support per domain are called Operating Performance Points or
|
||||
OPPs.
|
||||
|
||||
OPP library provides a set of helper functions to organize and query the OPP
|
||||
information. The library is located in drivers/base/power/opp.c and the header
|
||||
is located in include/linux/opp.h. OPP library can be enabled by enabling
|
||||
CONFIG_PM_OPP from power management menuconfig menu. OPP library depends on
|
||||
CONFIG_PM as certain SoCs such as Texas Instrument's OMAP framework allows to
|
||||
optionally boot at a certain OPP without needing cpufreq.
|
||||
|
||||
Typical usage of the OPP library is as follows:
|
||||
(users) -> registers a set of default OPPs -> (library)
|
||||
SoC framework -> modifies on required cases certain OPPs -> OPP layer
|
||||
-> queries to search/retrieve information ->
|
||||
|
||||
OPP layer expects each domain to be represented by a unique device pointer. SoC
|
||||
framework registers a set of initial OPPs per device with the OPP layer. This
|
||||
list is expected to be an optimally small number typically around 5 per device.
|
||||
This initial list contains a set of OPPs that the framework expects to be safely
|
||||
enabled by default in the system.
|
||||
|
||||
Note on OPP Availability:
|
||||
------------------------
|
||||
As the system proceeds to operate, SoC framework may choose to make certain
|
||||
OPPs available or not available on each device based on various external
|
||||
factors. Example usage: Thermal management or other exceptional situations where
|
||||
SoC framework might choose to disable a higher frequency OPP to safely continue
|
||||
operations until that OPP could be re-enabled if possible.
|
||||
|
||||
OPP library facilitates this concept in it's implementation. The following
|
||||
operational functions operate only on available opps:
|
||||
opp_find_freq_{ceil, floor}, opp_get_voltage, opp_get_freq, opp_get_opp_count
|
||||
and opp_init_cpufreq_table
|
||||
|
||||
opp_find_freq_exact is meant to be used to find the opp pointer which can then
|
||||
be used for opp_enable/disable functions to make an opp available as required.
|
||||
|
||||
WARNING: Users of OPP library should refresh their availability count using
|
||||
get_opp_count if opp_enable/disable functions are invoked for a device, the
|
||||
exact mechanism to trigger these or the notification mechanism to other
|
||||
dependent subsystems such as cpufreq are left to the discretion of the SoC
|
||||
specific framework which uses the OPP library. Similar care needs to be taken
|
||||
care to refresh the cpufreq table in cases of these operations.
|
||||
|
||||
WARNING on OPP List locking mechanism:
|
||||
-------------------------------------------------
|
||||
OPP library uses RCU for exclusivity. RCU allows the query functions to operate
|
||||
in multiple contexts and this synchronization mechanism is optimal for a read
|
||||
intensive operations on data structure as the OPP library caters to.
|
||||
|
||||
To ensure that the data retrieved are sane, the users such as SoC framework
|
||||
should ensure that the section of code operating on OPP queries are locked
|
||||
using RCU read locks. The opp_find_freq_{exact,ceil,floor},
|
||||
opp_get_{voltage, freq, opp_count} fall into this category.
|
||||
|
||||
opp_{add,enable,disable} are updaters which use mutex and implement it's own
|
||||
RCU locking mechanisms. opp_init_cpufreq_table acts as an updater and uses
|
||||
mutex to implment RCU updater strategy. These functions should *NOT* be called
|
||||
under RCU locks and other contexts that prevent blocking functions in RCU or
|
||||
mutex operations from working.
|
||||
|
||||
2. Initial OPP List Registration
|
||||
================================
|
||||
The SoC implementation calls opp_add function iteratively to add OPPs per
|
||||
device. It is expected that the SoC framework will register the OPP entries
|
||||
optimally- typical numbers range to be less than 5. The list generated by
|
||||
registering the OPPs is maintained by OPP library throughout the device
|
||||
operation. The SoC framework can subsequently control the availability of the
|
||||
OPPs dynamically using the opp_enable / disable functions.
|
||||
|
||||
opp_add - Add a new OPP for a specific domain represented by the device pointer.
|
||||
The OPP is defined using the frequency and voltage. Once added, the OPP
|
||||
is assumed to be available and control of it's availability can be done
|
||||
with the opp_enable/disable functions. OPP library internally stores
|
||||
and manages this information in the opp struct. This function may be
|
||||
used by SoC framework to define a optimal list as per the demands of
|
||||
SoC usage environment.
|
||||
|
||||
WARNING: Do not use this function in interrupt context.
|
||||
|
||||
Example:
|
||||
soc_pm_init()
|
||||
{
|
||||
/* Do things */
|
||||
r = opp_add(mpu_dev, 1000000, 900000);
|
||||
if (!r) {
|
||||
pr_err("%s: unable to register mpu opp(%d)\n", r);
|
||||
goto no_cpufreq;
|
||||
}
|
||||
/* Do cpufreq things */
|
||||
no_cpufreq:
|
||||
/* Do remaining things */
|
||||
}
|
||||
|
||||
3. OPP Search Functions
|
||||
=======================
|
||||
High level framework such as cpufreq operates on frequencies. To map the
|
||||
frequency back to the corresponding OPP, OPP library provides handy functions
|
||||
to search the OPP list that OPP library internally manages. These search
|
||||
functions return the matching pointer representing the opp if a match is
|
||||
found, else returns error. These errors are expected to be handled by standard
|
||||
error checks such as IS_ERR() and appropriate actions taken by the caller.
|
||||
|
||||
opp_find_freq_exact - Search for an OPP based on an *exact* frequency and
|
||||
availability. This function is especially useful to enable an OPP which
|
||||
is not available by default.
|
||||
Example: In a case when SoC framework detects a situation where a
|
||||
higher frequency could be made available, it can use this function to
|
||||
find the OPP prior to call the opp_enable to actually make it available.
|
||||
rcu_read_lock();
|
||||
opp = opp_find_freq_exact(dev, 1000000000, false);
|
||||
rcu_read_unlock();
|
||||
/* dont operate on the pointer.. just do a sanity check.. */
|
||||
if (IS_ERR(opp)) {
|
||||
pr_err("frequency not disabled!\n");
|
||||
/* trigger appropriate actions.. */
|
||||
} else {
|
||||
opp_enable(dev,1000000000);
|
||||
}
|
||||
|
||||
NOTE: This is the only search function that operates on OPPs which are
|
||||
not available.
|
||||
|
||||
opp_find_freq_floor - Search for an available OPP which is *at most* the
|
||||
provided frequency. This function is useful while searching for a lesser
|
||||
match OR operating on OPP information in the order of decreasing
|
||||
frequency.
|
||||
Example: To find the highest opp for a device:
|
||||
freq = ULONG_MAX;
|
||||
rcu_read_lock();
|
||||
opp_find_freq_floor(dev, &freq);
|
||||
rcu_read_unlock();
|
||||
|
||||
opp_find_freq_ceil - Search for an available OPP which is *at least* the
|
||||
provided frequency. This function is useful while searching for a
|
||||
higher match OR operating on OPP information in the order of increasing
|
||||
frequency.
|
||||
Example 1: To find the lowest opp for a device:
|
||||
freq = 0;
|
||||
rcu_read_lock();
|
||||
opp_find_freq_ceil(dev, &freq);
|
||||
rcu_read_unlock();
|
||||
Example 2: A simplified implementation of a SoC cpufreq_driver->target:
|
||||
soc_cpufreq_target(..)
|
||||
{
|
||||
/* Do stuff like policy checks etc. */
|
||||
/* Find the best frequency match for the req */
|
||||
rcu_read_lock();
|
||||
opp = opp_find_freq_ceil(dev, &freq);
|
||||
rcu_read_unlock();
|
||||
if (!IS_ERR(opp))
|
||||
soc_switch_to_freq_voltage(freq);
|
||||
else
|
||||
/* do something when we cant satisfy the req */
|
||||
/* do other stuff */
|
||||
}
|
||||
|
||||
4. OPP Availability Control Functions
|
||||
=====================================
|
||||
A default OPP list registered with the OPP library may not cater to all possible
|
||||
situation. The OPP library provides a set of functions to modify the
|
||||
availability of a OPP within the OPP list. This allows SoC frameworks to have
|
||||
fine grained dynamic control of which sets of OPPs are operationally available.
|
||||
These functions are intended to *temporarily* remove an OPP in conditions such
|
||||
as thermal considerations (e.g. don't use OPPx until the temperature drops).
|
||||
|
||||
WARNING: Do not use these functions in interrupt context.
|
||||
|
||||
opp_enable - Make a OPP available for operation.
|
||||
Example: Lets say that 1GHz OPP is to be made available only if the
|
||||
SoC temperature is lower than a certain threshold. The SoC framework
|
||||
implementation might choose to do something as follows:
|
||||
if (cur_temp < temp_low_thresh) {
|
||||
/* Enable 1GHz if it was disabled */
|
||||
rcu_read_lock();
|
||||
opp = opp_find_freq_exact(dev, 1000000000, false);
|
||||
rcu_read_unlock();
|
||||
/* just error check */
|
||||
if (!IS_ERR(opp))
|
||||
ret = opp_enable(dev, 1000000000);
|
||||
else
|
||||
goto try_something_else;
|
||||
}
|
||||
|
||||
opp_disable - Make an OPP to be not available for operation
|
||||
Example: Lets say that 1GHz OPP is to be disabled if the temperature
|
||||
exceeds a threshold value. The SoC framework implementation might
|
||||
choose to do something as follows:
|
||||
if (cur_temp > temp_high_thresh) {
|
||||
/* Disable 1GHz if it was enabled */
|
||||
rcu_read_lock();
|
||||
opp = opp_find_freq_exact(dev, 1000000000, true);
|
||||
rcu_read_unlock();
|
||||
/* just error check */
|
||||
if (!IS_ERR(opp))
|
||||
ret = opp_disable(dev, 1000000000);
|
||||
else
|
||||
goto try_something_else;
|
||||
}
|
||||
|
||||
5. OPP Data Retrieval Functions
|
||||
===============================
|
||||
Since OPP library abstracts away the OPP information, a set of functions to pull
|
||||
information from the OPP structure is necessary. Once an OPP pointer is
|
||||
retrieved using the search functions, the following functions can be used by SoC
|
||||
framework to retrieve the information represented inside the OPP layer.
|
||||
|
||||
opp_get_voltage - Retrieve the voltage represented by the opp pointer.
|
||||
Example: At a cpufreq transition to a different frequency, SoC
|
||||
framework requires to set the voltage represented by the OPP using
|
||||
the regulator framework to the Power Management chip providing the
|
||||
voltage.
|
||||
soc_switch_to_freq_voltage(freq)
|
||||
{
|
||||
/* do things */
|
||||
rcu_read_lock();
|
||||
opp = opp_find_freq_ceil(dev, &freq);
|
||||
v = opp_get_voltage(opp);
|
||||
rcu_read_unlock();
|
||||
if (v)
|
||||
regulator_set_voltage(.., v);
|
||||
/* do other things */
|
||||
}
|
||||
|
||||
opp_get_freq - Retrieve the freq represented by the opp pointer.
|
||||
Example: Lets say the SoC framework uses a couple of helper functions
|
||||
we could pass opp pointers instead of doing additional parameters to
|
||||
handle quiet a bit of data parameters.
|
||||
soc_cpufreq_target(..)
|
||||
{
|
||||
/* do things.. */
|
||||
max_freq = ULONG_MAX;
|
||||
rcu_read_lock();
|
||||
max_opp = opp_find_freq_floor(dev,&max_freq);
|
||||
requested_opp = opp_find_freq_ceil(dev,&freq);
|
||||
if (!IS_ERR(max_opp) && !IS_ERR(requested_opp))
|
||||
r = soc_test_validity(max_opp, requested_opp);
|
||||
rcu_read_unlock();
|
||||
/* do other things */
|
||||
}
|
||||
soc_test_validity(..)
|
||||
{
|
||||
if(opp_get_voltage(max_opp) < opp_get_voltage(requested_opp))
|
||||
return -EINVAL;
|
||||
if(opp_get_freq(max_opp) < opp_get_freq(requested_opp))
|
||||
return -EINVAL;
|
||||
/* do things.. */
|
||||
}
|
||||
|
||||
opp_get_opp_count - Retrieve the number of available opps for a device
|
||||
Example: Lets say a co-processor in the SoC needs to know the available
|
||||
frequencies in a table, the main processor can notify as following:
|
||||
soc_notify_coproc_available_frequencies()
|
||||
{
|
||||
/* Do things */
|
||||
rcu_read_lock();
|
||||
num_available = opp_get_opp_count(dev);
|
||||
speeds = kzalloc(sizeof(u32) * num_available, GFP_KERNEL);
|
||||
/* populate the table in increasing order */
|
||||
freq = 0;
|
||||
while (!IS_ERR(opp = opp_find_freq_ceil(dev, &freq))) {
|
||||
speeds[i] = freq;
|
||||
freq++;
|
||||
i++;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
soc_notify_coproc(AVAILABLE_FREQs, speeds, num_available);
|
||||
/* Do other things */
|
||||
}
|
||||
|
||||
6. Cpufreq Table Generation
|
||||
===========================
|
||||
opp_init_cpufreq_table - cpufreq framework typically is initialized with
|
||||
cpufreq_frequency_table_cpuinfo which is provided with the list of
|
||||
frequencies that are available for operation. This function provides
|
||||
a ready to use conversion routine to translate the OPP layer's internal
|
||||
information about the available frequencies into a format readily
|
||||
providable to cpufreq.
|
||||
|
||||
WARNING: Do not use this function in interrupt context.
|
||||
|
||||
Example:
|
||||
soc_pm_init()
|
||||
{
|
||||
/* Do things */
|
||||
r = opp_init_cpufreq_table(dev, &freq_table);
|
||||
if (!r)
|
||||
cpufreq_frequency_table_cpuinfo(policy, freq_table);
|
||||
/* Do other things */
|
||||
}
|
||||
|
||||
NOTE: This function is available only if CONFIG_CPU_FREQ is enabled in
|
||||
addition to CONFIG_PM as power management feature is required to
|
||||
dynamically scale voltage and frequency in a system.
|
||||
|
||||
7. Data Structures
|
||||
==================
|
||||
Typically an SoC contains multiple voltage domains which are variable. Each
|
||||
domain is represented by a device pointer. The relationship to OPP can be
|
||||
represented as follows:
|
||||
SoC
|
||||
|- device 1
|
||||
| |- opp 1 (availability, freq, voltage)
|
||||
| |- opp 2 ..
|
||||
... ...
|
||||
| `- opp n ..
|
||||
|- device 2
|
||||
...
|
||||
`- device m
|
||||
|
||||
OPP library maintains a internal list that the SoC framework populates and
|
||||
accessed by various functions as described above. However, the structures
|
||||
representing the actual OPPs and domains are internal to the OPP library itself
|
||||
to allow for suitable abstraction reusable across systems.
|
||||
|
||||
struct opp - The internal data structure of OPP library which is used to
|
||||
represent an OPP. In addition to the freq, voltage, availability
|
||||
information, it also contains internal book keeping information required
|
||||
for the OPP library to operate on. Pointer to this structure is
|
||||
provided back to the users such as SoC framework to be used as a
|
||||
identifier for OPP in the interactions with OPP layer.
|
||||
|
||||
WARNING: The struct opp pointer should not be parsed or modified by the
|
||||
users. The defaults of for an instance is populated by opp_add, but the
|
||||
availability of the OPP can be modified by opp_enable/disable functions.
|
||||
|
||||
struct device - This is used to identify a domain to the OPP layer. The
|
||||
nature of the device and it's implementation is left to the user of
|
||||
OPP library such as the SoC framework.
|
||||
|
||||
Overall, in a simplistic view, the data structure operations is represented as
|
||||
following:
|
||||
|
||||
Initialization / modification:
|
||||
+-----+ /- opp_enable
|
||||
opp_add --> | opp | <-------
|
||||
| +-----+ \- opp_disable
|
||||
\-------> domain_info(device)
|
||||
|
||||
Search functions:
|
||||
/-- opp_find_freq_ceil ---\ +-----+
|
||||
domain_info<---- opp_find_freq_exact -----> | opp |
|
||||
\-- opp_find_freq_floor ---/ +-----+
|
||||
|
||||
Retrieval functions:
|
||||
+-----+ /- opp_get_voltage
|
||||
| opp | <---
|
||||
+-----+ \- opp_get_freq
|
||||
|
||||
domain_info <- opp_get_opp_count
|
||||
@@ -1,6 +1,7 @@
|
||||
Run-time Power Management Framework for I/O Devices
|
||||
|
||||
(C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
|
||||
(C) 2010 Alan Stern <stern@rowland.harvard.edu>
|
||||
|
||||
1. Introduction
|
||||
|
||||
@@ -157,7 +158,8 @@ rules:
|
||||
to execute it, the other callbacks will not be executed for the same device.
|
||||
|
||||
* A request to execute ->runtime_resume() will cancel any pending or
|
||||
scheduled requests to execute the other callbacks for the same device.
|
||||
scheduled requests to execute the other callbacks for the same device,
|
||||
except for scheduled autosuspends.
|
||||
|
||||
3. Run-time PM Device Fields
|
||||
|
||||
@@ -165,7 +167,7 @@ The following device run-time PM fields are present in 'struct dev_pm_info', as
|
||||
defined in include/linux/pm.h:
|
||||
|
||||
struct timer_list suspend_timer;
|
||||
- timer used for scheduling (delayed) suspend request
|
||||
- timer used for scheduling (delayed) suspend and autosuspend requests
|
||||
|
||||
unsigned long timer_expires;
|
||||
- timer expiration time, in jiffies (if this is different from zero, the
|
||||
@@ -230,6 +232,28 @@ defined in include/linux/pm.h:
|
||||
interface; it may only be modified with the help of the pm_runtime_allow()
|
||||
and pm_runtime_forbid() helper functions
|
||||
|
||||
unsigned int no_callbacks;
|
||||
- indicates that the device does not use the run-time PM callbacks (see
|
||||
Section 8); it may be modified only by the pm_runtime_no_callbacks()
|
||||
helper function
|
||||
|
||||
unsigned int use_autosuspend;
|
||||
- indicates that the device's driver supports delayed autosuspend (see
|
||||
Section 9); it may be modified only by the
|
||||
pm_runtime{_dont}_use_autosuspend() helper functions
|
||||
|
||||
unsigned int timer_autosuspends;
|
||||
- indicates that the PM core should attempt to carry out an autosuspend
|
||||
when the timer expires rather than a normal suspend
|
||||
|
||||
int autosuspend_delay;
|
||||
- the delay time (in milliseconds) to be used for autosuspend
|
||||
|
||||
unsigned long last_busy;
|
||||
- the time (in jiffies) when the pm_runtime_mark_last_busy() helper
|
||||
function was last called for this device; used in calculating inactivity
|
||||
periods for autosuspend
|
||||
|
||||
All of the above fields are members of the 'power' member of 'struct device'.
|
||||
|
||||
4. Run-time PM Device Helper Functions
|
||||
@@ -255,6 +279,12 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h:
|
||||
error code on failure, where -EAGAIN or -EBUSY means it is safe to attempt
|
||||
to suspend the device again in future
|
||||
|
||||
int pm_runtime_autosuspend(struct device *dev);
|
||||
- same as pm_runtime_suspend() except that the autosuspend delay is taken
|
||||
into account; if pm_runtime_autosuspend_expiration() says the delay has
|
||||
not yet expired then an autosuspend is scheduled for the appropriate time
|
||||
and 0 is returned
|
||||
|
||||
int pm_runtime_resume(struct device *dev);
|
||||
- execute the subsystem-level resume callback for the device; returns 0 on
|
||||
success, 1 if the device's run-time PM status was already 'active' or
|
||||
@@ -267,6 +297,11 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h:
|
||||
device (the request is represented by a work item in pm_wq); returns 0 on
|
||||
success or error code if the request has not been queued up
|
||||
|
||||
int pm_request_autosuspend(struct device *dev);
|
||||
- schedule the execution of the subsystem-level suspend callback for the
|
||||
device when the autosuspend delay has expired; if the delay has already
|
||||
expired then the work item is queued up immediately
|
||||
|
||||
int pm_schedule_suspend(struct device *dev, unsigned int delay);
|
||||
- schedule the execution of the subsystem-level suspend callback for the
|
||||
device in future, where 'delay' is the time to wait before queuing up a
|
||||
@@ -298,12 +333,20 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h:
|
||||
- decrement the device's usage counter
|
||||
|
||||
int pm_runtime_put(struct device *dev);
|
||||
- decrement the device's usage counter, run pm_request_idle(dev) and return
|
||||
its result
|
||||
- decrement the device's usage counter; if the result is 0 then run
|
||||
pm_request_idle(dev) and return its result
|
||||
|
||||
int pm_runtime_put_autosuspend(struct device *dev);
|
||||
- decrement the device's usage counter; if the result is 0 then run
|
||||
pm_request_autosuspend(dev) and return its result
|
||||
|
||||
int pm_runtime_put_sync(struct device *dev);
|
||||
- decrement the device's usage counter, run pm_runtime_idle(dev) and return
|
||||
its result
|
||||
- decrement the device's usage counter; if the result is 0 then run
|
||||
pm_runtime_idle(dev) and return its result
|
||||
|
||||
int pm_runtime_put_sync_autosuspend(struct device *dev);
|
||||
- decrement the device's usage counter; if the result is 0 then run
|
||||
pm_runtime_autosuspend(dev) and return its result
|
||||
|
||||
void pm_runtime_enable(struct device *dev);
|
||||
- enable the run-time PM helper functions to run the device bus type's
|
||||
@@ -349,19 +392,51 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h:
|
||||
counter (used by the /sys/devices/.../power/control interface to
|
||||
effectively prevent the device from being power managed at run time)
|
||||
|
||||
void pm_runtime_no_callbacks(struct device *dev);
|
||||
- set the power.no_callbacks flag for the device and remove the run-time
|
||||
PM attributes from /sys/devices/.../power (or prevent them from being
|
||||
added when the device is registered)
|
||||
|
||||
void pm_runtime_mark_last_busy(struct device *dev);
|
||||
- set the power.last_busy field to the current time
|
||||
|
||||
void pm_runtime_use_autosuspend(struct device *dev);
|
||||
- set the power.use_autosuspend flag, enabling autosuspend delays
|
||||
|
||||
void pm_runtime_dont_use_autosuspend(struct device *dev);
|
||||
- clear the power.use_autosuspend flag, disabling autosuspend delays
|
||||
|
||||
void pm_runtime_set_autosuspend_delay(struct device *dev, int delay);
|
||||
- set the power.autosuspend_delay value to 'delay' (expressed in
|
||||
milliseconds); if 'delay' is negative then run-time suspends are
|
||||
prevented
|
||||
|
||||
unsigned long pm_runtime_autosuspend_expiration(struct device *dev);
|
||||
- calculate the time when the current autosuspend delay period will expire,
|
||||
based on power.last_busy and power.autosuspend_delay; if the delay time
|
||||
is 1000 ms or larger then the expiration time is rounded up to the
|
||||
nearest second; returns 0 if the delay period has already expired or
|
||||
power.use_autosuspend isn't set, otherwise returns the expiration time
|
||||
in jiffies
|
||||
|
||||
It is safe to execute the following helper functions from interrupt context:
|
||||
|
||||
pm_request_idle()
|
||||
pm_request_autosuspend()
|
||||
pm_schedule_suspend()
|
||||
pm_request_resume()
|
||||
pm_runtime_get_noresume()
|
||||
pm_runtime_get()
|
||||
pm_runtime_put_noidle()
|
||||
pm_runtime_put()
|
||||
pm_runtime_put_autosuspend()
|
||||
pm_runtime_enable()
|
||||
pm_suspend_ignore_children()
|
||||
pm_runtime_set_active()
|
||||
pm_runtime_set_suspended()
|
||||
pm_runtime_enable()
|
||||
pm_runtime_suspended()
|
||||
pm_runtime_mark_last_busy()
|
||||
pm_runtime_autosuspend_expiration()
|
||||
|
||||
5. Run-time PM Initialization, Device Probing and Removal
|
||||
|
||||
@@ -524,3 +599,141 @@ poweroff and run-time suspend callback, and similarly for system resume, thaw,
|
||||
restore, and run-time resume, can achieve this with the help of the
|
||||
UNIVERSAL_DEV_PM_OPS macro defined in include/linux/pm.h (possibly setting its
|
||||
last argument to NULL).
|
||||
|
||||
8. "No-Callback" Devices
|
||||
|
||||
Some "devices" are only logical sub-devices of their parent and cannot be
|
||||
power-managed on their own. (The prototype example is a USB interface. Entire
|
||||
USB devices can go into low-power mode or send wake-up requests, but neither is
|
||||
possible for individual interfaces.) The drivers for these devices have no
|
||||
need of run-time PM callbacks; if the callbacks did exist, ->runtime_suspend()
|
||||
and ->runtime_resume() would always return 0 without doing anything else and
|
||||
->runtime_idle() would always call pm_runtime_suspend().
|
||||
|
||||
Subsystems can tell the PM core about these devices by calling
|
||||
pm_runtime_no_callbacks(). This should be done after the device structure is
|
||||
initialized and before it is registered (although after device registration is
|
||||
also okay). The routine will set the device's power.no_callbacks flag and
|
||||
prevent the non-debugging run-time PM sysfs attributes from being created.
|
||||
|
||||
When power.no_callbacks is set, the PM core will not invoke the
|
||||
->runtime_idle(), ->runtime_suspend(), or ->runtime_resume() callbacks.
|
||||
Instead it will assume that suspends and resumes always succeed and that idle
|
||||
devices should be suspended.
|
||||
|
||||
As a consequence, the PM core will never directly inform the device's subsystem
|
||||
or driver about run-time power changes. Instead, the driver for the device's
|
||||
parent must take responsibility for telling the device's driver when the
|
||||
parent's power state changes.
|
||||
|
||||
9. Autosuspend, or automatically-delayed suspends
|
||||
|
||||
Changing a device's power state isn't free; it requires both time and energy.
|
||||
A device should be put in a low-power state only when there's some reason to
|
||||
think it will remain in that state for a substantial time. A common heuristic
|
||||
says that a device which hasn't been used for a while is liable to remain
|
||||
unused; following this advice, drivers should not allow devices to be suspended
|
||||
at run-time until they have been inactive for some minimum period. Even when
|
||||
the heuristic ends up being non-optimal, it will still prevent devices from
|
||||
"bouncing" too rapidly between low-power and full-power states.
|
||||
|
||||
The term "autosuspend" is an historical remnant. It doesn't mean that the
|
||||
device is automatically suspended (the subsystem or driver still has to call
|
||||
the appropriate PM routines); rather it means that run-time suspends will
|
||||
automatically be delayed until the desired period of inactivity has elapsed.
|
||||
|
||||
Inactivity is determined based on the power.last_busy field. Drivers should
|
||||
call pm_runtime_mark_last_busy() to update this field after carrying out I/O,
|
||||
typically just before calling pm_runtime_put_autosuspend(). The desired length
|
||||
of the inactivity period is a matter of policy. Subsystems can set this length
|
||||
initially by calling pm_runtime_set_autosuspend_delay(), but after device
|
||||
registration the length should be controlled by user space, using the
|
||||
/sys/devices/.../power/autosuspend_delay_ms attribute.
|
||||
|
||||
In order to use autosuspend, subsystems or drivers must call
|
||||
pm_runtime_use_autosuspend() (preferably before registering the device), and
|
||||
thereafter they should use the various *_autosuspend() helper functions instead
|
||||
of the non-autosuspend counterparts:
|
||||
|
||||
Instead of: pm_runtime_suspend use: pm_runtime_autosuspend;
|
||||
Instead of: pm_schedule_suspend use: pm_request_autosuspend;
|
||||
Instead of: pm_runtime_put use: pm_runtime_put_autosuspend;
|
||||
Instead of: pm_runtime_put_sync use: pm_runtime_put_sync_autosuspend.
|
||||
|
||||
Drivers may also continue to use the non-autosuspend helper functions; they
|
||||
will behave normally, not taking the autosuspend delay into account.
|
||||
Similarly, if the power.use_autosuspend field isn't set then the autosuspend
|
||||
helper functions will behave just like the non-autosuspend counterparts.
|
||||
|
||||
The implementation is well suited for asynchronous use in interrupt contexts.
|
||||
However such use inevitably involves races, because the PM core can't
|
||||
synchronize ->runtime_suspend() callbacks with the arrival of I/O requests.
|
||||
This synchronization must be handled by the driver, using its private lock.
|
||||
Here is a schematic pseudo-code example:
|
||||
|
||||
foo_read_or_write(struct foo_priv *foo, void *data)
|
||||
{
|
||||
lock(&foo->private_lock);
|
||||
add_request_to_io_queue(foo, data);
|
||||
if (foo->num_pending_requests++ == 0)
|
||||
pm_runtime_get(&foo->dev);
|
||||
if (!foo->is_suspended)
|
||||
foo_process_next_request(foo);
|
||||
unlock(&foo->private_lock);
|
||||
}
|
||||
|
||||
foo_io_completion(struct foo_priv *foo, void *req)
|
||||
{
|
||||
lock(&foo->private_lock);
|
||||
if (--foo->num_pending_requests == 0) {
|
||||
pm_runtime_mark_last_busy(&foo->dev);
|
||||
pm_runtime_put_autosuspend(&foo->dev);
|
||||
} else {
|
||||
foo_process_next_request(foo);
|
||||
}
|
||||
unlock(&foo->private_lock);
|
||||
/* Send req result back to the user ... */
|
||||
}
|
||||
|
||||
int foo_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct foo_priv foo = container_of(dev, ...);
|
||||
int ret = 0;
|
||||
|
||||
lock(&foo->private_lock);
|
||||
if (foo->num_pending_requests > 0) {
|
||||
ret = -EBUSY;
|
||||
} else {
|
||||
/* ... suspend the device ... */
|
||||
foo->is_suspended = 1;
|
||||
}
|
||||
unlock(&foo->private_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int foo_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct foo_priv foo = container_of(dev, ...);
|
||||
|
||||
lock(&foo->private_lock);
|
||||
/* ... resume the device ... */
|
||||
foo->is_suspended = 0;
|
||||
pm_runtime_mark_last_busy(&foo->dev);
|
||||
if (foo->num_pending_requests > 0)
|
||||
foo_process_requests(foo);
|
||||
unlock(&foo->private_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
The important point is that after foo_io_completion() asks for an autosuspend,
|
||||
the foo_runtime_suspend() callback may race with foo_read_or_write().
|
||||
Therefore foo_runtime_suspend() has to check whether there are any pending I/O
|
||||
requests (while holding the private lock) before allowing the suspend to
|
||||
proceed.
|
||||
|
||||
In addition, the power.autosuspend_delay field can be changed by user space at
|
||||
any time. If a driver cares about this, it can call
|
||||
pm_runtime_autosuspend_expiration() from within the ->runtime_suspend()
|
||||
callback while holding its private lock. If the function returns a nonzero
|
||||
value then the delay has not yet expired and the callback should return
|
||||
-EAGAIN.
|
||||
|
||||
@@ -49,6 +49,13 @@ machine that doesn't boot) is:
|
||||
device (lspci and /sys/devices/pci* is your friend), and see if you can
|
||||
fix it, disable it, or trace into its resume function.
|
||||
|
||||
If no device matches the hash (or any matches appear to be false positives),
|
||||
the culprit may be a device from a loadable kernel module that is not loaded
|
||||
until after the hash is checked. You can check the hash against the current
|
||||
devices again after more modules are loaded using sysfs:
|
||||
|
||||
cat /sys/power/pm_trace_dev_match
|
||||
|
||||
For example, the above happens to be the VGA device on my EVO, which I
|
||||
used to run with "radeonfb" (it's an ATI Radeon mobility). It turns out
|
||||
that "radeonfb" simply cannot resume that device - it tries to set the
|
||||
|
||||
@@ -66,7 +66,8 @@ swsusp saves the state of the machine into active swaps and then reboots or
|
||||
powerdowns. You must explicitly specify the swap partition to resume from with
|
||||
``resume='' kernel option. If signature is found it loads and restores saved
|
||||
state. If the option ``noresume'' is specified as a boot parameter, it skips
|
||||
the resuming.
|
||||
the resuming. If the option ``hibernate=nocompress'' is specified as a boot
|
||||
parameter, it saves hibernation image without compression.
|
||||
|
||||
In the meantime while the system is suspended you should not add/remove any
|
||||
of the hardware, write to the filesystems, etc.
|
||||
|
||||
@@ -3,6 +3,7 @@ obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o
|
||||
obj-$(CONFIG_PM_RUNTIME) += runtime.o
|
||||
obj-$(CONFIG_PM_OPS) += generic_ops.o
|
||||
obj-$(CONFIG_PM_TRACE_RTC) += trace.o
|
||||
obj-$(CONFIG_PM_OPP) += opp.o
|
||||
|
||||
ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
|
||||
ccflags-$(CONFIG_PM_VERBOSE) += -DDEBUG
|
||||
|
||||
@@ -46,7 +46,7 @@ int pm_generic_runtime_suspend(struct device *dev)
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
int ret;
|
||||
|
||||
ret = pm && pm->runtime_suspend ? pm->runtime_suspend(dev) : -EINVAL;
|
||||
ret = pm && pm->runtime_suspend ? pm->runtime_suspend(dev) : 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -65,7 +65,7 @@ int pm_generic_runtime_resume(struct device *dev)
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
int ret;
|
||||
|
||||
ret = pm && pm->runtime_resume ? pm->runtime_resume(dev) : -EINVAL;
|
||||
ret = pm && pm->runtime_resume ? pm->runtime_resume(dev) : 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@ static pm_message_t pm_transition;
|
||||
*/
|
||||
static bool transition_started;
|
||||
|
||||
static int async_error;
|
||||
|
||||
/**
|
||||
* device_pm_init - Initialize the PM-related part of a device object.
|
||||
* @dev: Device object being initialized.
|
||||
@@ -60,7 +62,8 @@ void device_pm_init(struct device *dev)
|
||||
dev->power.status = DPM_ON;
|
||||
init_completion(&dev->power.completion);
|
||||
complete_all(&dev->power.completion);
|
||||
dev->power.wakeup_count = 0;
|
||||
dev->power.wakeup = NULL;
|
||||
spin_lock_init(&dev->power.lock);
|
||||
pm_runtime_init(dev);
|
||||
}
|
||||
|
||||
@@ -120,6 +123,7 @@ void device_pm_remove(struct device *dev)
|
||||
mutex_lock(&dpm_list_mtx);
|
||||
list_del_init(&dev->power.entry);
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
device_wakeup_disable(dev);
|
||||
pm_runtime_remove(dev);
|
||||
}
|
||||
|
||||
@@ -407,7 +411,7 @@ static void pm_dev_err(struct device *dev, pm_message_t state, char *info,
|
||||
static void dpm_show_time(ktime_t starttime, pm_message_t state, char *info)
|
||||
{
|
||||
ktime_t calltime;
|
||||
s64 usecs64;
|
||||
u64 usecs64;
|
||||
int usecs;
|
||||
|
||||
calltime = ktime_get();
|
||||
@@ -600,6 +604,7 @@ static void dpm_resume(pm_message_t state)
|
||||
INIT_LIST_HEAD(&list);
|
||||
mutex_lock(&dpm_list_mtx);
|
||||
pm_transition = state;
|
||||
async_error = 0;
|
||||
|
||||
list_for_each_entry(dev, &dpm_list, power.entry) {
|
||||
if (dev->power.status < DPM_OFF)
|
||||
@@ -829,8 +834,6 @@ static int legacy_suspend(struct device *dev, pm_message_t state,
|
||||
return error;
|
||||
}
|
||||
|
||||
static int async_error;
|
||||
|
||||
/**
|
||||
* device_suspend - Execute "suspend" callbacks for given device.
|
||||
* @dev: Device to handle.
|
||||
@@ -885,6 +888,9 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
|
||||
device_unlock(dev);
|
||||
complete_all(&dev->power.completion);
|
||||
|
||||
if (error)
|
||||
async_error = error;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -894,10 +900,8 @@ static void async_suspend(void *data, async_cookie_t cookie)
|
||||
int error;
|
||||
|
||||
error = __device_suspend(dev, pm_transition, true);
|
||||
if (error) {
|
||||
if (error)
|
||||
pm_dev_err(dev, pm_transition, " async", error);
|
||||
async_error = error;
|
||||
}
|
||||
|
||||
put_device(dev);
|
||||
}
|
||||
@@ -1085,8 +1089,9 @@ EXPORT_SYMBOL_GPL(__suspend_report_result);
|
||||
* @dev: Device to wait for.
|
||||
* @subordinate: Device that needs to wait for @dev.
|
||||
*/
|
||||
void device_pm_wait_for_dev(struct device *subordinate, struct device *dev)
|
||||
int device_pm_wait_for_dev(struct device *subordinate, struct device *dev)
|
||||
{
|
||||
dpm_wait(dev, subordinate->power.async_suspend);
|
||||
return async_error;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_pm_wait_for_dev);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -34,6 +34,7 @@ extern void device_pm_move_last(struct device *);
|
||||
|
||||
static inline void device_pm_init(struct device *dev)
|
||||
{
|
||||
spin_lock_init(&dev->power.lock);
|
||||
pm_runtime_init(dev);
|
||||
}
|
||||
|
||||
@@ -59,6 +60,7 @@ static inline void device_pm_move_last(struct device *dev) {}
|
||||
|
||||
extern int dpm_sysfs_add(struct device *);
|
||||
extern void dpm_sysfs_remove(struct device *);
|
||||
extern void rpm_sysfs_remove(struct device *);
|
||||
|
||||
#else /* CONFIG_PM */
|
||||
|
||||
|
||||
+541
-479
File diff suppressed because it is too large
Load Diff
+208
-9
@@ -75,12 +75,27 @@
|
||||
* attribute is set to "enabled" by bus type code or device drivers and in
|
||||
* that cases it should be safe to leave the default value.
|
||||
*
|
||||
* autosuspend_delay_ms - Report/change a device's autosuspend_delay value
|
||||
*
|
||||
* Some drivers don't want to carry out a runtime suspend as soon as a
|
||||
* device becomes idle; they want it always to remain idle for some period
|
||||
* of time before suspending it. This period is the autosuspend_delay
|
||||
* value (expressed in milliseconds) and it can be controlled by the user.
|
||||
* If the value is negative then the device will never be runtime
|
||||
* suspended.
|
||||
*
|
||||
* NOTE: The autosuspend_delay_ms attribute and the autosuspend_delay
|
||||
* value are used only if the driver calls pm_runtime_use_autosuspend().
|
||||
*
|
||||
* wakeup_count - Report the number of wakeup events related to the device
|
||||
*/
|
||||
|
||||
static const char enabled[] = "enabled";
|
||||
static const char disabled[] = "disabled";
|
||||
|
||||
const char power_group_name[] = "power";
|
||||
EXPORT_SYMBOL_GPL(power_group_name);
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
static const char ctrl_auto[] = "auto";
|
||||
static const char ctrl_on[] = "on";
|
||||
@@ -170,6 +185,33 @@ static ssize_t rtpm_status_show(struct device *dev,
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(runtime_status, 0444, rtpm_status_show, NULL);
|
||||
|
||||
static ssize_t autosuspend_delay_ms_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
if (!dev->power.use_autosuspend)
|
||||
return -EIO;
|
||||
return sprintf(buf, "%d\n", dev->power.autosuspend_delay);
|
||||
}
|
||||
|
||||
static ssize_t autosuspend_delay_ms_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t n)
|
||||
{
|
||||
long delay;
|
||||
|
||||
if (!dev->power.use_autosuspend)
|
||||
return -EIO;
|
||||
|
||||
if (strict_strtol(buf, 10, &delay) != 0 || delay != (int) delay)
|
||||
return -EINVAL;
|
||||
|
||||
pm_runtime_set_autosuspend_delay(dev, delay);
|
||||
return n;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(autosuspend_delay_ms, 0644, autosuspend_delay_ms_show,
|
||||
autosuspend_delay_ms_store);
|
||||
|
||||
#endif
|
||||
|
||||
static ssize_t
|
||||
@@ -210,11 +252,122 @@ static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store);
|
||||
static ssize_t wakeup_count_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%lu\n", dev->power.wakeup_count);
|
||||
unsigned long count = 0;
|
||||
bool enabled = false;
|
||||
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
if (dev->power.wakeup) {
|
||||
count = dev->power.wakeup->event_count;
|
||||
enabled = true;
|
||||
}
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n");
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(wakeup_count, 0444, wakeup_count_show, NULL);
|
||||
#endif
|
||||
|
||||
static ssize_t wakeup_active_count_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
unsigned long count = 0;
|
||||
bool enabled = false;
|
||||
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
if (dev->power.wakeup) {
|
||||
count = dev->power.wakeup->active_count;
|
||||
enabled = true;
|
||||
}
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n");
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(wakeup_active_count, 0444, wakeup_active_count_show, NULL);
|
||||
|
||||
static ssize_t wakeup_hit_count_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
unsigned long count = 0;
|
||||
bool enabled = false;
|
||||
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
if (dev->power.wakeup) {
|
||||
count = dev->power.wakeup->hit_count;
|
||||
enabled = true;
|
||||
}
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n");
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(wakeup_hit_count, 0444, wakeup_hit_count_show, NULL);
|
||||
|
||||
static ssize_t wakeup_active_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
unsigned int active = 0;
|
||||
bool enabled = false;
|
||||
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
if (dev->power.wakeup) {
|
||||
active = dev->power.wakeup->active;
|
||||
enabled = true;
|
||||
}
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
return enabled ? sprintf(buf, "%u\n", active) : sprintf(buf, "\n");
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(wakeup_active, 0444, wakeup_active_show, NULL);
|
||||
|
||||
static ssize_t wakeup_total_time_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
s64 msec = 0;
|
||||
bool enabled = false;
|
||||
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
if (dev->power.wakeup) {
|
||||
msec = ktime_to_ms(dev->power.wakeup->total_time);
|
||||
enabled = true;
|
||||
}
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n");
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(wakeup_total_time_ms, 0444, wakeup_total_time_show, NULL);
|
||||
|
||||
static ssize_t wakeup_max_time_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
s64 msec = 0;
|
||||
bool enabled = false;
|
||||
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
if (dev->power.wakeup) {
|
||||
msec = ktime_to_ms(dev->power.wakeup->max_time);
|
||||
enabled = true;
|
||||
}
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n");
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(wakeup_max_time_ms, 0444, wakeup_max_time_show, NULL);
|
||||
|
||||
static ssize_t wakeup_last_time_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
s64 msec = 0;
|
||||
bool enabled = false;
|
||||
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
if (dev->power.wakeup) {
|
||||
msec = ktime_to_ms(dev->power.wakeup->last_time);
|
||||
enabled = true;
|
||||
}
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n");
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(wakeup_last_time_ms, 0444, wakeup_last_time_show, NULL);
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
#ifdef CONFIG_PM_ADVANCED_DEBUG
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
@@ -279,19 +432,20 @@ static DEVICE_ATTR(async, 0644, async_show, async_store);
|
||||
#endif /* CONFIG_PM_ADVANCED_DEBUG */
|
||||
|
||||
static struct attribute * power_attrs[] = {
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
&dev_attr_control.attr,
|
||||
&dev_attr_runtime_status.attr,
|
||||
&dev_attr_runtime_suspended_time.attr,
|
||||
&dev_attr_runtime_active_time.attr,
|
||||
#endif
|
||||
&dev_attr_wakeup.attr,
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
&dev_attr_wakeup_count.attr,
|
||||
&dev_attr_wakeup_active_count.attr,
|
||||
&dev_attr_wakeup_hit_count.attr,
|
||||
&dev_attr_wakeup_active.attr,
|
||||
&dev_attr_wakeup_total_time_ms.attr,
|
||||
&dev_attr_wakeup_max_time_ms.attr,
|
||||
&dev_attr_wakeup_last_time_ms.attr,
|
||||
#endif
|
||||
#ifdef CONFIG_PM_ADVANCED_DEBUG
|
||||
&dev_attr_async.attr,
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
&dev_attr_runtime_status.attr,
|
||||
&dev_attr_runtime_usage.attr,
|
||||
&dev_attr_runtime_active_kids.attr,
|
||||
&dev_attr_runtime_enabled.attr,
|
||||
@@ -300,10 +454,53 @@ static struct attribute * power_attrs[] = {
|
||||
NULL,
|
||||
};
|
||||
static struct attribute_group pm_attr_group = {
|
||||
.name = "power",
|
||||
.name = power_group_name,
|
||||
.attrs = power_attrs,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
||||
static struct attribute *runtime_attrs[] = {
|
||||
#ifndef CONFIG_PM_ADVANCED_DEBUG
|
||||
&dev_attr_runtime_status.attr,
|
||||
#endif
|
||||
&dev_attr_control.attr,
|
||||
&dev_attr_runtime_suspended_time.attr,
|
||||
&dev_attr_runtime_active_time.attr,
|
||||
&dev_attr_autosuspend_delay_ms.attr,
|
||||
NULL,
|
||||
};
|
||||
static struct attribute_group pm_runtime_attr_group = {
|
||||
.name = power_group_name,
|
||||
.attrs = runtime_attrs,
|
||||
};
|
||||
|
||||
int dpm_sysfs_add(struct device *dev)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = sysfs_create_group(&dev->kobj, &pm_attr_group);
|
||||
if (rc == 0 && !dev->power.no_callbacks) {
|
||||
rc = sysfs_merge_group(&dev->kobj, &pm_runtime_attr_group);
|
||||
if (rc)
|
||||
sysfs_remove_group(&dev->kobj, &pm_attr_group);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
void rpm_sysfs_remove(struct device *dev)
|
||||
{
|
||||
sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group);
|
||||
}
|
||||
|
||||
void dpm_sysfs_remove(struct device *dev)
|
||||
{
|
||||
rpm_sysfs_remove(dev);
|
||||
sysfs_remove_group(&dev->kobj, &pm_attr_group);
|
||||
}
|
||||
|
||||
#else /* CONFIG_PM_RUNTIME */
|
||||
|
||||
int dpm_sysfs_add(struct device * dev)
|
||||
{
|
||||
return sysfs_create_group(&dev->kobj, &pm_attr_group);
|
||||
@@ -313,3 +510,5 @@ void dpm_sysfs_remove(struct device * dev)
|
||||
{
|
||||
sysfs_remove_group(&dev->kobj, &pm_attr_group);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -188,8 +188,10 @@ static int show_file_hash(unsigned int value)
|
||||
static int show_dev_hash(unsigned int value)
|
||||
{
|
||||
int match = 0;
|
||||
struct list_head *entry = dpm_list.prev;
|
||||
struct list_head *entry;
|
||||
|
||||
device_pm_lock();
|
||||
entry = dpm_list.prev;
|
||||
while (entry != &dpm_list) {
|
||||
struct device * dev = to_device(entry);
|
||||
unsigned int hash = hash_string(DEVSEED, dev_name(dev), DEVHASH);
|
||||
@@ -199,11 +201,43 @@ static int show_dev_hash(unsigned int value)
|
||||
}
|
||||
entry = entry->prev;
|
||||
}
|
||||
device_pm_unlock();
|
||||
return match;
|
||||
}
|
||||
|
||||
static unsigned int hash_value_early_read;
|
||||
|
||||
int show_trace_dev_match(char *buf, size_t size)
|
||||
{
|
||||
unsigned int value = hash_value_early_read / (USERHASH * FILEHASH);
|
||||
int ret = 0;
|
||||
struct list_head *entry;
|
||||
|
||||
/*
|
||||
* It's possible that multiple devices will match the hash and we can't
|
||||
* tell which is the culprit, so it's best to output them all.
|
||||
*/
|
||||
device_pm_lock();
|
||||
entry = dpm_list.prev;
|
||||
while (size && entry != &dpm_list) {
|
||||
struct device *dev = to_device(entry);
|
||||
unsigned int hash = hash_string(DEVSEED, dev_name(dev),
|
||||
DEVHASH);
|
||||
if (hash == value) {
|
||||
int len = snprintf(buf, size, "%s\n",
|
||||
dev_driver_string(dev));
|
||||
if (len > size)
|
||||
len = size;
|
||||
buf += len;
|
||||
ret += len;
|
||||
size -= len;
|
||||
}
|
||||
entry = entry->prev;
|
||||
}
|
||||
device_pm_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int early_resume_init(void)
|
||||
{
|
||||
hash_value_early_read = read_magic_time();
|
||||
|
||||
+539
-82
File diff suppressed because it is too large
Load Diff
@@ -148,6 +148,65 @@ void sysfs_remove_group(struct kobject * kobj,
|
||||
sysfs_put(sd);
|
||||
}
|
||||
|
||||
/**
|
||||
* sysfs_merge_group - merge files into a pre-existing attribute group.
|
||||
* @kobj: The kobject containing the group.
|
||||
* @grp: The files to create and the attribute group they belong to.
|
||||
*
|
||||
* This function returns an error if the group doesn't exist or any of the
|
||||
* files already exist in that group, in which case none of the new files
|
||||
* are created.
|
||||
*/
|
||||
int sysfs_merge_group(struct kobject *kobj,
|
||||
const struct attribute_group *grp)
|
||||
{
|
||||
struct sysfs_dirent *dir_sd;
|
||||
int error = 0;
|
||||
struct attribute *const *attr;
|
||||
int i;
|
||||
|
||||
if (grp)
|
||||
dir_sd = sysfs_get_dirent(kobj->sd, NULL, grp->name);
|
||||
else
|
||||
dir_sd = sysfs_get(kobj->sd);
|
||||
if (!dir_sd)
|
||||
return -ENOENT;
|
||||
|
||||
for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr))
|
||||
error = sysfs_add_file(dir_sd, *attr, SYSFS_KOBJ_ATTR);
|
||||
if (error) {
|
||||
while (--i >= 0)
|
||||
sysfs_hash_and_remove(dir_sd, NULL, (*--attr)->name);
|
||||
}
|
||||
sysfs_put(dir_sd);
|
||||
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sysfs_merge_group);
|
||||
|
||||
/**
|
||||
* sysfs_unmerge_group - remove files from a pre-existing attribute group.
|
||||
* @kobj: The kobject containing the group.
|
||||
* @grp: The files to remove and the attribute group they belong to.
|
||||
*/
|
||||
void sysfs_unmerge_group(struct kobject *kobj,
|
||||
const struct attribute_group *grp)
|
||||
{
|
||||
struct sysfs_dirent *dir_sd;
|
||||
struct attribute *const *attr;
|
||||
|
||||
if (grp)
|
||||
dir_sd = sysfs_get_dirent(kobj->sd, NULL, grp->name);
|
||||
else
|
||||
dir_sd = sysfs_get(kobj->sd);
|
||||
if (dir_sd) {
|
||||
for (attr = grp->attrs; *attr; ++attr)
|
||||
sysfs_hash_and_remove(dir_sd, NULL, (*attr)->name);
|
||||
sysfs_put(dir_sd);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sysfs_unmerge_group);
|
||||
|
||||
|
||||
EXPORT_SYMBOL_GPL(sysfs_create_group);
|
||||
EXPORT_SYMBOL_GPL(sysfs_update_group);
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Generic OPP Interface
|
||||
*
|
||||
* Copyright (C) 2009-2010 Texas Instruments Incorporated.
|
||||
* Nishanth Menon
|
||||
* Romit Dasgupta
|
||||
* Kevin Hilman
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_OPP_H__
|
||||
#define __LINUX_OPP_H__
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/cpufreq.h>
|
||||
|
||||
struct opp;
|
||||
|
||||
#if defined(CONFIG_PM_OPP)
|
||||
|
||||
unsigned long opp_get_voltage(struct opp *opp);
|
||||
|
||||
unsigned long opp_get_freq(struct opp *opp);
|
||||
|
||||
int opp_get_opp_count(struct device *dev);
|
||||
|
||||
struct opp *opp_find_freq_exact(struct device *dev, unsigned long freq,
|
||||
bool available);
|
||||
|
||||
struct opp *opp_find_freq_floor(struct device *dev, unsigned long *freq);
|
||||
|
||||
struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq);
|
||||
|
||||
int opp_add(struct device *dev, unsigned long freq, unsigned long u_volt);
|
||||
|
||||
int opp_enable(struct device *dev, unsigned long freq);
|
||||
|
||||
int opp_disable(struct device *dev, unsigned long freq);
|
||||
|
||||
#else
|
||||
static inline unsigned long opp_get_voltage(struct opp *opp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline unsigned long opp_get_freq(struct opp *opp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int opp_get_opp_count(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct opp *opp_find_freq_exact(struct device *dev,
|
||||
unsigned long freq, bool available)
|
||||
{
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static inline struct opp *opp_find_freq_floor(struct device *dev,
|
||||
unsigned long *freq)
|
||||
{
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static inline struct opp *opp_find_freq_ceil(struct device *dev,
|
||||
unsigned long *freq)
|
||||
{
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static inline int opp_add(struct device *dev, unsigned long freq,
|
||||
unsigned long u_volt)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int opp_enable(struct device *dev, unsigned long freq)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int opp_disable(struct device *dev, unsigned long freq)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
#if defined(CONFIG_CPU_FREQ) && defined(CONFIG_PM_OPP)
|
||||
int opp_init_cpufreq_table(struct device *dev,
|
||||
struct cpufreq_frequency_table **table);
|
||||
#else
|
||||
static inline int opp_init_cpufreq_table(struct device *dev,
|
||||
struct cpufreq_frequency_table **table)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif /* CONFIG_CPU_FREQ */
|
||||
|
||||
#endif /* __LINUX_OPP_H__ */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user