doc: guides: pm: device_runtime: new guide
Add a new and more extensive guide that describes the device runtime API. It contains a brief introduction, API design principles and goals, some example sequence diagrams and usage guidelines. While the API still needs important additions, such a guide will hopefully guide developers when trying to deal with device PM in Zephyr. Note: sequence diagrams have been created using PlantUML (2021 version). The rendered SVG diagrams embed the original code if they have to be edited and re-rendered. Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
This commit is contained in:
parent
40a94743a3
commit
a4d257f19b
3 changed files with 631 additions and 6 deletions
|
@ -1,10 +1,203 @@
|
|||
Device Runtime Power Management
|
||||
###############################
|
||||
|
||||
The Device Runtime Power Management framework is an Active Power
|
||||
Management mechanism which reduces the overall system Power consumtion
|
||||
by suspending the devices which are idle or not being used while the
|
||||
System is active or running.
|
||||
Introduction
|
||||
************
|
||||
|
||||
The interfaces and APIs provided by the Device Runtime PM are
|
||||
designed to be generic and architecture independent.
|
||||
The device runtime power management (PM) framework is an active power management
|
||||
mechanism which reduces the overall system power consumtion by suspending the
|
||||
devices which are idle or not used independently of the system state. It can be
|
||||
enabled by setting :kconfig:`CONFIG_PM_DEVICE_RUNTIME`. In this model the device
|
||||
driver is responsible to indicate when it needs the device and when it does not.
|
||||
This information is used to determine when to suspend or resume a device based
|
||||
on usage count.
|
||||
|
||||
When device runtime power management is enabled on a device, its state will be
|
||||
initially set to a :c:enumerator:`PM_DEVICE_STATE_SUSPENDED` indicating it is
|
||||
not used. On the first device request, it will be resumed and so put into the
|
||||
:c:enumerator:`PM_DEVICE_STATE_ACTIVE` state. The device will remain in this
|
||||
state until it is no longer used. At this point, the device will be suspended
|
||||
until the next device request. If the suspension is performed synchronously the
|
||||
device will be immediately put into the
|
||||
:c:enumerator:`PM_DEVICE_STATE_SUSPENDED` state, whereas if it is performed
|
||||
asynchonously, it will be put into the
|
||||
:c:enumerator:`PM_DEVICE_STATE_SUSPENDING` state first and then into the
|
||||
:c:enumerator:`PM_DEVICE_STATE_SUSPENDED` state when the action is run.
|
||||
|
||||
.. graphviz::
|
||||
:caption: Device states and transitions
|
||||
|
||||
digraph {
|
||||
node [shape=box];
|
||||
init [shape=point];
|
||||
|
||||
SUSPENDED [label=PM_DEVICE_STATE_SUSPENDED];
|
||||
ACTIVE [label=PM_DEVICE_STATE_ACTIVE];
|
||||
SUSPENDING [label=PM_DEVICE_STATE_SUSPENDING];
|
||||
|
||||
init -> SUSPENDED;
|
||||
SUSPENDED -> ACTIVE;
|
||||
ACTIVE -> SUSPENDED;
|
||||
ACTIVE -> SUSPENDING [constraint=false]
|
||||
SUSPENDING -> SUSPENDED [constraint=false];
|
||||
SUSPENDED -> SUSPENDING [style=invis];
|
||||
SUSPENDING -> ACTIVE [style=invis];
|
||||
}
|
||||
|
||||
The device runtime power management framework has been designed to minimize
|
||||
devices power consumption with minimal application work. Device drivers are
|
||||
responsible for indicating when they need the device to be operational and
|
||||
when they do not. Therefore, applications can not manually suspend or resume a
|
||||
device. An application can, however, decide when to disable or enable runtime
|
||||
power management for a device. This can be useful, for example, if an
|
||||
application wants a particular device to be always active.
|
||||
|
||||
Design principles
|
||||
*****************
|
||||
|
||||
When runtime PM is enabled on a device it will no longer be resumed or suspended
|
||||
during system power transitions. Instead, the device is fully responsible to
|
||||
indicate when it needs a device and when it does not. The device runtime PM API
|
||||
uses reference counting to keep track of device's usage. This allows the API to
|
||||
determine when a device needs to be resumed or suspended. The API uses the *get*
|
||||
and *put* terminology to indicate when a device is needed or not, respectively.
|
||||
This mechanism plays a key role when we account for device dependencies. For
|
||||
example, if a bus device is used by multiple sensors, we can keep the bus active
|
||||
until the last sensor has finished using it.
|
||||
|
||||
.. note::
|
||||
|
||||
As of today, the device runtime power management API does not manage device
|
||||
dependencies. This effectively means that, if a device depends on other
|
||||
devices to operate (e.g. a sensor may depend on a bus device), the bus will
|
||||
be resumed and suspended on every transaction. In general, it is more
|
||||
efficient to keep parent devices active when their children are used, since
|
||||
the children may perform multiple transactions in a short period of time.
|
||||
Until this feature is added, devices can manually *get* or *put* their
|
||||
dependencies.
|
||||
|
||||
The :c:func:`pm_device_runtime_get` function can be used by a device driver to
|
||||
indicate it *needs* the device to be active or operational. This function will
|
||||
increase device usage count and resume the device if necessary. Similarly, the
|
||||
:c:func:`pm_device_runtime_put` function can be used to indicate that the device
|
||||
is no longer needed. This function will decrease the device usage count and
|
||||
suspend the device if necessary. It is worth to note that in both cases, the
|
||||
operation is carried out synchronously. The sequence diagram shown in
|
||||
:numref:`pm_device_runtime_sync_ops` illustrates how a device can use this API
|
||||
and the expected sequence of events.
|
||||
|
||||
.. _pm_device_runtime_sync_ops:
|
||||
|
||||
.. figure:: images/devr-sync-ops.svg
|
||||
|
||||
Synchronous operation on a single device
|
||||
|
||||
The synchronous model is as simple as it gets. However, it may introduce
|
||||
unnecessary delays since the application will not get the operation result until
|
||||
the device is suspended (in case device is no longer used). It will likely not
|
||||
be a problem if the operation is fast, e.g. a register toggle. However, the
|
||||
situation will not be the same if suspension involves sending packets through a
|
||||
slow bus. For this reason the device drivers can also make use of the
|
||||
:c:func:`pm_device_runtime_put_async` function. This function will schedule
|
||||
the suspend operation, again, if device is no longer used. The suspension will
|
||||
then be carried out when the system work queue gets the chance to run. The
|
||||
sequence diagram in :numref:`pm_device_runtime_async_ops` illustrates this
|
||||
scenario.
|
||||
|
||||
.. _pm_device_runtime_async_ops:
|
||||
|
||||
.. figure:: images/devr-async-ops.svg
|
||||
|
||||
Asynchronous operation on a single device
|
||||
|
||||
Implementation guidelines
|
||||
*************************
|
||||
|
||||
In a first place, a device driver needs to implement the PM action callback used
|
||||
by the PM subsystem to suspend or resume devices.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
static int mydev_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;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
The PM action callback calls are serialized by the PM subsystem, therefore, no
|
||||
special synchronization is required.
|
||||
|
||||
To enable device runtime power management on a device, the driver needs to call
|
||||
:c:func:`pm_device_runtime_enable` at initialization time. Note that this
|
||||
function will put the device into the :c:enumerator:`PM_DEVICE_STATE_SUSPENDED`
|
||||
state. In case this does not reflect the actual device state, the init function
|
||||
must perform the necessary operations to suspend the device.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
/* device driver initialization function */
|
||||
static int mydev_init(const struct device *dev)
|
||||
{
|
||||
...
|
||||
/* make sure the device physically is suspended */
|
||||
/* enable device runtime power management */
|
||||
pm_device_runtime_enable(dev);
|
||||
...
|
||||
}
|
||||
|
||||
Assuming an example device driver that implements an ``operation`` API call, the
|
||||
*get* and *put* operations could be carried out as follows:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
static int mydev_operation(const struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* "get" device (increases usage count, resumes device if suspended) */
|
||||
ret = pm_device_runtime_get(dev);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* do something with the device */
|
||||
...
|
||||
|
||||
/* "put" device (decreases usage count, suspends device if no more users) */
|
||||
return pm_device_runtime_put(dev);
|
||||
}
|
||||
|
||||
In case the suspend operation is *slow*, the device driver can use the
|
||||
asynchronous API:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
static int mydev_operation(const struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* "get" device (increases usage count, resumes device if suspended) */
|
||||
ret = pm_device_runtime_get(dev);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* do something with the device */
|
||||
...
|
||||
|
||||
/* "put" device (decreases usage count, schedule suspend if no more users) */
|
||||
return pm_device_runtime_put_async(dev);
|
||||
}
|
||||
|
|
222
doc/guides/pm/images/devr-async-ops.svg
generated
Normal file
222
doc/guides/pm/images/devr-async-ops.svg
generated
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 16 KiB |
210
doc/guides/pm/images/devr-sync-ops.svg
generated
Normal file
210
doc/guides/pm/images/devr-sync-ops.svg
generated
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 13 KiB |
Loading…
Add table
Add a link
Reference in a new issue