Function prototype in PM device implementation documentation had the incorrect prototype for device power management. Fix this to align with the correct prototype. Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
247 lines
9.2 KiB
ReStructuredText
247 lines
9.2 KiB
ReStructuredText
Device Power Management
|
|
#######################
|
|
|
|
Introduction
|
|
************
|
|
|
|
Device power management (PM) on Zephyr is a feature that enables devices to
|
|
save energy when they are not being used. This feature can be enabled by
|
|
setting :kconfig:option:`CONFIG_PM_DEVICE` to ``y``. When this option is
|
|
selected, device drivers implementing power management will be able to take
|
|
advantage of the device power management subsystem.
|
|
|
|
Zephyr supports two types of device power management:
|
|
|
|
- :ref:`Device Runtime Power Management <pm-device-runtime-pm>`
|
|
- :ref:`System Power Management <pm-device-system-pm>`
|
|
|
|
.. _pm-device-runtime-pm:
|
|
|
|
Device Runtime Power Management
|
|
*******************************
|
|
|
|
In this method, the application or any component that deals with devices directly
|
|
and has the best knowledge of their use, performs the device power management. This
|
|
saves power if some devices that are not in use can be turned off or put
|
|
in power saving mode. This method allows saving power even when the CPU is
|
|
active. The components that use the devices need to be power aware and should
|
|
be able to make decisions related to managing device power.
|
|
|
|
When using this type of device power management, the kernel can change CPU
|
|
power states quickly when :c:func:`pm_system_suspend()` gets called. This is
|
|
because it does not need to spend time doing device power management if the
|
|
devices are already put in the appropriate power state by the application or
|
|
component managing the devices.
|
|
|
|
For more information, see :ref:`pm-device-runtime`.
|
|
|
|
.. _pm-device-system-pm:
|
|
|
|
System Power Management
|
|
***********************
|
|
|
|
When using this type, device power management is mostly done inside
|
|
:c:func:`pm_system_suspend()` along with entering a CPU or SOC power state.
|
|
|
|
If a decision to enter a CPU lower power state is made, the power management
|
|
subsystem will suspend devices before changing state. The subsystem takes care
|
|
of suspending devices following their initialization order, ensuring that
|
|
possible dependencies between them are satisfied. As soon as the CPU wakes up
|
|
from a sleep state, devices are resumed in the opposite order that they were
|
|
suspended.
|
|
|
|
.. note::
|
|
|
|
When using :ref:`pm-system`, device transitions can be run from the idle thread.
|
|
As functions in this context cannot block, transitions that intend to use blocking
|
|
APIs **must** check whether they can do so with :c:func:`k_can_yield`.
|
|
|
|
This type of device power management can be useful when the application is not
|
|
power aware and does not implement runtime device power management. Though,
|
|
:ref:`Device Runtime Power Management <pm-device-runtime-pm>` is the **preferred**
|
|
option for device power management.
|
|
|
|
.. note::
|
|
|
|
When using this type of device power management, the CPU will only enter
|
|
a low power state only if no device is in the middle of a hardware
|
|
transaction that cannot be interrupted.
|
|
|
|
.. note::
|
|
|
|
This type of device power management is disabled when
|
|
:kconfig:option:`CONFIG_PM_DEVICE_RUNTIME_EXCLUSIVE` is set to ``y`` (that is
|
|
the default value when :kconfig:option:`CONFIG_PM_DEVICE_RUNTIME` is enabled)
|
|
|
|
.. note::
|
|
|
|
Devices are suspended only when the last active core is entering a low power
|
|
state and devices are resumed by the first core that becomes active.
|
|
|
|
Device Power Management States
|
|
******************************
|
|
|
|
The power management subsystem defines device states in
|
|
:c:enum:`pm_device_state`. This type is used to track power states of
|
|
a particular device. It is important to emphasize that, although the
|
|
state is tracked by the subsystem, it is the responsibility of each device driver
|
|
to handle device actions(:c:enum:`pm_device_action`) which change device state.
|
|
|
|
Each :c:enum:`pm_device_action` have a direct an unambiguous relationship with
|
|
a :c:enum:`pm_device_state`.
|
|
|
|
.. graphviz::
|
|
:caption: Device actions x states
|
|
|
|
digraph {
|
|
node [shape=circle];
|
|
rankdir=LR;
|
|
subgraph {
|
|
|
|
SUSPENDED [label=PM_DEVICE_STATE_SUSPENDED];
|
|
SUSPENDING [label=PM_DEVICE_STATE_SUSPENDING];
|
|
ACTIVE [label=PM_DEVICE_STATE_ACTIVE];
|
|
OFF [label=PM_DEVICE_STATE_OFF];
|
|
|
|
|
|
ACTIVE -> SUSPENDING -> SUSPENDED;
|
|
ACTIVE -> SUSPENDED ["label"="PM_DEVICE_ACTION_SUSPEND"];
|
|
SUSPENDED -> ACTIVE ["label"="PM_DEVICE_ACTION_RESUME"];
|
|
|
|
{rank = same; SUSPENDED; SUSPENDING;}
|
|
|
|
OFF -> SUSPENDED ["label"="PM_DEVICE_ACTION_TURN_ON"];
|
|
SUSPENDED -> OFF ["label"="PM_DEVICE_ACTION_TURN_OFF"];
|
|
ACTIVE -> OFF ["label"="PM_DEVICE_ACTION_TURN_OFF"];
|
|
}
|
|
}
|
|
|
|
As mentioned above, device drivers do not directly change between these states.
|
|
This is entirely done by the power management subsystem. Instead, drivers are
|
|
responsible for implementing any hardware-specific tasks needed to handle state
|
|
changes.
|
|
|
|
Device Model with Power Management Support
|
|
******************************************
|
|
|
|
Drivers initialize devices using macros. See :ref:`device_model_api` for
|
|
details on how these macros are used. A driver which implements device power
|
|
management support must provide these macros with arguments that describe its
|
|
power management implementation.
|
|
|
|
Use :c:macro:`PM_DEVICE_DEFINE` or :c:macro:`PM_DEVICE_DT_DEFINE` to define the
|
|
power management resources required by a driver. These macros allocate the
|
|
driver-specific state which is required by the power management subsystem.
|
|
|
|
Drivers can use :c:macro:`PM_DEVICE_GET` or
|
|
:c:macro:`PM_DEVICE_DT_GET` to get a pointer to this state. These
|
|
pointers should be passed to ``DEVICE_DEFINE`` or ``DEVICE_DT_DEFINE``
|
|
to initialize the power management field in each :c:struct:`device`.
|
|
|
|
Here is some example code showing how to implement device power management
|
|
support in a device driver.
|
|
|
|
.. code-block:: c
|
|
|
|
#define DT_DRV_COMPAT dummy_device
|
|
|
|
static int dummy_driver_pm_action(const struct device *dev,
|
|
enum pm_device_action action)
|
|
{
|
|
switch (action) {
|
|
case PM_DEVICE_ACTION_SUSPEND:
|
|
/* suspend the device */
|
|
...
|
|
break;
|
|
case PM_DEVICE_ACTION_RESUME:
|
|
/* resume the device */
|
|
...
|
|
break;
|
|
case PM_DEVICE_ACTION_TURN_ON:
|
|
/*
|
|
* powered on the device, used when the power
|
|
* domain this device belongs is resumed.
|
|
*/
|
|
...
|
|
break;
|
|
case PM_DEVICE_ACTION_TURN_OFF:
|
|
/*
|
|
* power off the device, used when the power
|
|
* domain this device belongs is suspended.
|
|
*/
|
|
...
|
|
break;
|
|
default:
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
PM_DEVICE_DT_INST_DEFINE(0, dummy_driver_pm_action);
|
|
|
|
DEVICE_DT_INST_DEFINE(0, &dummy_init,
|
|
PM_DEVICE_DT_INST_GET(0), NULL, NULL, POST_KERNEL,
|
|
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, NULL);
|
|
|
|
.. _pm-device-busy:
|
|
|
|
Busy Status Indication
|
|
**********************
|
|
|
|
When the system is idle and the SoC is going to sleep, the power management
|
|
subsystem can suspend devices, as described in :ref:`pm-device-system-pm`. This
|
|
can cause device hardware to lose some states. Suspending a device which is in
|
|
the middle of a hardware transaction, such as writing to a flash memory, may
|
|
lead to undefined behavior or inconsistent states. This API guards such
|
|
transactions by indicating to the kernel that the device is in the middle of an
|
|
operation and should not be suspended.
|
|
|
|
When :c:func:`pm_device_busy_set` is called, the device is marked as busy and
|
|
the system will not do power management on it. After the device is no
|
|
longer doing an operation and can be suspended, it should call
|
|
:c:func:`pm_device_busy_clear`.
|
|
|
|
Wakeup capability
|
|
*****************
|
|
|
|
Some devices are capable of waking the system up from a sleep state.
|
|
When a device has such capability, applications can enable or disable
|
|
this feature on a device dynamically using
|
|
:c:func:`pm_device_wakeup_enable`.
|
|
|
|
This property can be set on device declaring the property ``wakeup-source`` in
|
|
the device node in devicetree. For example, this devicetree fragment sets the
|
|
``gpio0`` device as a "wakeup" source:
|
|
|
|
.. code-block:: devicetree
|
|
|
|
gpio0: gpio@40022000 {
|
|
compatible = "ti,cc13xx-cc26xx-gpio";
|
|
reg = <0x40022000 0x400>;
|
|
interrupts = <0 0>;
|
|
status = "disabled";
|
|
label = "GPIO_0";
|
|
gpio-controller;
|
|
wakeup-source;
|
|
#gpio-cells = <2>;
|
|
};
|
|
|
|
By default, "wakeup" capable devices do not have this functionality enabled
|
|
during the device initialization. Applications can enable this functionality
|
|
later calling :c:func:`pm_device_wakeup_enable`.
|
|
|
|
.. note::
|
|
|
|
This property is **only** used by the system power management to identify
|
|
devices that should not be suspended.
|
|
It is responsibility of driver or the application to do any additional
|
|
configuration required by the device to support it.
|
|
|
|
Power Domain
|
|
************
|
|
|
|
Power domain on Zephyr is represented as a regular device. The power management
|
|
subsystem ensures that a domain is resumed before and suspended after devices
|
|
using it. For more details, see :ref:`pm-power-domain`.
|