The existing device_set_power_state() API works only in synchronous mode and this is not desirable for devices(ex: Gyro) which take longer time (few 100 mSec) to suspend/resume. To support async mode, a new callback argument is added to the API. The device drivers can asynchronously suspend/resume and call the callback function upon completion of the async request. This commit adds the missing callback parameter to all the drivers to make it compliant with the new API. Signed-off-by: Ramakrishna Pallala <ramakrishna.pallala@intel.com>
472 lines
17 KiB
ReStructuredText
472 lines
17 KiB
ReStructuredText
.. _power_management:
|
|
|
|
Power Management
|
|
################
|
|
|
|
Zephyr RTOS power management subsystem provides several means for a system
|
|
integrator to implement power management support that can take full
|
|
advantage of the power saving features of SOCs.
|
|
|
|
|
|
Terminology
|
|
***********
|
|
|
|
:dfn:`SOC interface`
|
|
This is a general term for the components that have knowledge of the
|
|
SOC and provide interfaces to the hardware features. It will abstract
|
|
the SOC specific implementations to the applications and the OS.
|
|
|
|
:dfn:`SOC Power State`
|
|
SOC Power State describes processor and device power states implemented at
|
|
the SOC level. Deep Sleep State is an example of SOC Power State.
|
|
|
|
:dfn:`Active State`
|
|
The CPU and clocks are powered on. This is the normal operating state when
|
|
the system is running.
|
|
|
|
:dfn:`Low Power State`
|
|
Refers to any one of the low power states supported by the SoC. The SoC is
|
|
usually powered on while the clocks are power gated.
|
|
|
|
:dfn:`Deep Sleep State`
|
|
The SoC is power gated and loses context. Most peripherals would also be
|
|
power gated. RAM may be selectively retained.
|
|
|
|
:dfn:`Idle Thread`
|
|
A system thread that runs when there are no other threads ready to run.
|
|
|
|
:dfn:`Power gating`
|
|
Power gating reduces power consumption by shutting off current to blocks of
|
|
the integrated circuit that are not in use.
|
|
|
|
Overview
|
|
********
|
|
|
|
The interfaces and APIs provided by the power management subsystem
|
|
are designed to be architecture and SOC independent. This enables power
|
|
management implementations to be easily adapted to different SOCs and
|
|
architectures. The kernel does not implement any power schemes of its own, giving
|
|
the system integrator the flexibility of implementing custom power schemes.
|
|
|
|
The architecture and SOC independence is achieved by separating the core
|
|
infrastructure and the SOC specific implementations. The SOC specific
|
|
implementations are abstracted to the application and the OS using hardware
|
|
abstraction layers.
|
|
|
|
The power management features are classified into the following categories.
|
|
|
|
* Tickless Idle
|
|
* System Power Management
|
|
* Device Power Management
|
|
|
|
Tickless Idle
|
|
*************
|
|
|
|
This is the name used to identify the event-based idling mechanism of the
|
|
Zephyr RTOS kernel scheduler. The kernel scheduler can run in two modes. During
|
|
normal operation, when at least one thread is active, it sets up the system
|
|
timer in periodic mode and runs in an interval-based scheduling mode. The
|
|
interval-based mode allows it to time slice between threads. Many times, the
|
|
threads would be waiting on semaphores, timeouts or for events. When there
|
|
are no threads running, it is inefficient for the kernel scheduler to run
|
|
in interval-based mode. This is because, in this mode the timer would trigger
|
|
an interrupt at fixed intervals causing the scheduler to be invoked at each
|
|
interval. The scheduler checks if any thread is ready to run. If no thread
|
|
is ready to run then it is a waste of power because of the unnecessary CPU
|
|
processing. This is avoided by the kernel switching to event-based idling
|
|
mode whenever there is no thread ready to run.
|
|
|
|
The kernel holds an ordered list of thread timeouts in the system. These are
|
|
the amount of time each thread has requested to wait. When the last active
|
|
thread goes to wait, the idle thread is scheduled. The idle thread programs
|
|
the timer to one-shot mode and programs the count to the earliest timeout
|
|
from the ordered thread timeout list. When the timer expires, a timer event
|
|
is generated. The ISR of this event will invoke the scheduler, which would
|
|
schedule the thread associated with the timeout. Before scheduling the
|
|
thread, the scheduler would switch the timer again to periodic mode. This
|
|
method saves power because the CPU is removed from the wait only when there
|
|
is a thread ready to run or if an external event occurred.
|
|
|
|
System Power Management
|
|
***********************
|
|
|
|
This consists of the hook functions that the power management subsystem calls
|
|
when the kernel enters and exits the idle state, in other words, when the kernel
|
|
has nothing to schedule. Enabling system power management compels Zephyr kernel
|
|
scheduler to work in tickless idle mode (see :option:`CONFIG_TICKLESS_IDLE`).
|
|
|
|
Suspend Hook function
|
|
=====================
|
|
|
|
.. code-block:: c
|
|
|
|
int sys_suspend(s32_t ticks);
|
|
|
|
When the kernel is about to go idle, the power management subsystem calls the
|
|
:code:`sys_suspend()` function, notifying the SOC interface that the kernel
|
|
is ready to enter the idle state.
|
|
|
|
At this point, the kernel has disabled interrupts and computed the maximum
|
|
time the system can remain idle. The function passes the time that
|
|
the system can remain idle. The SOC interface performs power operations that
|
|
can be done in the available time. The power management operation must halt
|
|
execution on a CPU or SOC power state. Before entering the power state,
|
|
the SOC interface must setup a wake event.
|
|
|
|
The power management subsystem expects the :code:`sys_suspend()` to return
|
|
the power state which was used or :code:`SYS_POWER_STATE_ACTIVE` if SoC was
|
|
kept in active state.
|
|
|
|
Resume Hook function
|
|
====================
|
|
|
|
.. code-block:: c
|
|
|
|
void sys_resume(void);
|
|
|
|
The power management subsystem optionally calls this hook function when exiting
|
|
kernel idling if power management operations were performed in
|
|
:code:`sys_suspend()`. Any necessary recovery operations can be performed
|
|
in this function before the kernel scheduler schedules another thread. Some
|
|
power states may not need this notification. It can be disabled by calling
|
|
:code:`sys_pm_idle_exit_notification_disable()` from
|
|
:code:`sys_suspend()`.
|
|
|
|
Resume From Deep Sleep Hook function
|
|
====================================
|
|
|
|
.. code-block:: c
|
|
|
|
void sys_resume_from_deep_sleep(void);
|
|
|
|
This function is optionally called when exiting from deep sleep if the SOC
|
|
interface does not have bootloader support to handle resume from deep sleep.
|
|
This function should restore context to the point where system entered
|
|
the deep sleep state.
|
|
|
|
.. note::
|
|
|
|
Since the hook functions are called with the interrupts disabled, the SOC
|
|
interface should ensure that its operations are completed quickly. Thus, the
|
|
SOC interface ensures that the kernel's scheduling performance is not
|
|
disrupted.
|
|
|
|
Power Schemes
|
|
*************
|
|
|
|
When the power management subsystem notifies the SOC interface that the kernel
|
|
is about to enter a system idle state, it specifies the period of time the
|
|
system intends to stay idle. The SOC interface can perform various power
|
|
management operations during this time. For example, put the processor or the
|
|
SOC in a power state, turn off some or all of the peripherals or power gate
|
|
device clocks.
|
|
|
|
Different levels of power savings and different wake latencies characterize
|
|
these power schemes. In general, operations that save more power have a
|
|
higher wake latency. When making decisions, the SOC interface chooses the
|
|
scheme that saves the most power. At the same time, the scheme's total
|
|
execution time must fit within the idle time allotted by the power management
|
|
subsystem.
|
|
|
|
The power management subsystem classifies power management schemes
|
|
into two categories based on whether the CPU loses execution context during the
|
|
power state transition.
|
|
|
|
* Low Power State
|
|
* Deep Sleep State
|
|
|
|
Low Power State
|
|
===============
|
|
|
|
CPU does not lose execution context. Devices also do not lose power while
|
|
entering power states in this category. The wake latencies of power states
|
|
in this category are relatively low.
|
|
|
|
Deep Sleep State
|
|
================
|
|
|
|
CPU is power gated and loses execution context. Execution will resume at
|
|
OS startup code or at a resume point determined by a bootloader that supports
|
|
deep sleep resume. Depending on the SOC's implementation of the power saving
|
|
feature, it may turn off power to most devices. RAM may be retained by some
|
|
implementations, while others may remove power from RAM saving considerable
|
|
power. Power states in this category save more power than Low Power states
|
|
and would have higher wake latencies.
|
|
|
|
Device Power Management Infrastructure
|
|
**************************************
|
|
|
|
The device power management infrastructure consists of interfaces to the
|
|
Zephyr RTOS device model. These APIs send control commands to the device driver
|
|
to update its power state or to get its current power state.
|
|
|
|
Zephyr RTOS supports two methods of doing device power management.
|
|
|
|
* Distributed method
|
|
* Central method
|
|
|
|
Distributed method
|
|
==================
|
|
|
|
In this method, the application or any component that deals with devices directly
|
|
and has the best knowledge of their use does 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. In this method, the
|
|
SOC interface can enter CPU or SOC power states quickly when
|
|
:code:`sys_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.
|
|
|
|
Central method
|
|
==============
|
|
|
|
In this method device power management is mostly done inside
|
|
:code:`sys_suspend()` along with entering a CPU or SOC power state.
|
|
|
|
If a decision to enter deep sleep is made, the implementation would enter it
|
|
only after checking if the devices are not in the middle of a hardware
|
|
transaction that cannot be interrupted. This method can be used in
|
|
implementations where the applications and components using devices are not
|
|
expected to be power aware and do not implement device power management.
|
|
|
|
This method can also be used to emulate a hardware feature supported by some
|
|
SOCs which cause automatic entry to deep sleep when all devices are idle.
|
|
Refer to `Busy Status Indication`_ to see how to indicate whether a device is busy
|
|
or idle.
|
|
|
|
Device Power Management States
|
|
==============================
|
|
The Zephyr RTOS power management subsystem defines four device states.
|
|
These states are classified based on the degree of device context that gets lost
|
|
in those states, kind of operations done to save power, and the impact on the
|
|
device behavior due to the state transition. Device context includes device
|
|
registers, clocks, memory etc.
|
|
|
|
The four device power states:
|
|
|
|
:code:`DEVICE_PM_ACTIVE_STATE`
|
|
|
|
Normal operation of the device. All device context is retained.
|
|
|
|
:code:`DEVICE_PM_LOW_POWER_STATE`
|
|
|
|
Device context is preserved by the HW and need not be restored by the driver.
|
|
|
|
:code:`DEVICE_PM_SUSPEND_STATE`
|
|
|
|
Most device context is lost by the hardware. Device drivers must save and
|
|
restore or reinitialize any context lost by the hardware.
|
|
|
|
:code:`DEVICE_PM_OFF_STATE`
|
|
|
|
Power has been fully removed from the device. The device context is lost
|
|
when this state is entered. Need to reinitialize the device when powering
|
|
it back on.
|
|
|
|
Device Power Management Operations
|
|
==================================
|
|
|
|
Zephyr RTOS power management subsystem provides a control function interface
|
|
to device drivers to indicate power management operations to perform.
|
|
The supported PM control commands are:
|
|
|
|
* DEVICE_PM_SET_POWER_STATE
|
|
* DEVICE_PM_GET_POWER_STATE
|
|
|
|
Each device driver defines:
|
|
|
|
* The device's supported power states.
|
|
* The device's supported transitions between power states.
|
|
* The device's necessary operations to handle the transition between power states.
|
|
|
|
The following are some examples of operations that the device driver may perform
|
|
in transition between power states:
|
|
|
|
* Save/Restore device states.
|
|
* Gate/Un-gate clocks.
|
|
* Gate/Un-gate power.
|
|
* Mask/Un-mask interrupts.
|
|
|
|
Device Model with Power Management Support
|
|
==========================================
|
|
|
|
Drivers initialize the devices using macros. See :ref:`device_drivers` for
|
|
details on how these macros are used. Use the DEVICE_DEFINE macro to initialize
|
|
drivers providing power management support via the PM control function.
|
|
One of the macro parameters is the pointer to the device_pm_control handler function.
|
|
|
|
Default Initializer Function
|
|
----------------------------
|
|
|
|
.. code-block:: c
|
|
|
|
int device_pm_control_nop(struct device *unused_device, u32_t unused_ctrl_command, void *unused_context);
|
|
|
|
|
|
If the driver doesn't implement any power control operations, the driver can
|
|
initialize the corresponding pointer with this default nop function. This
|
|
default nop function does nothing and should be used instead of
|
|
implementing a dummy function to avoid wasting code memory in the driver.
|
|
|
|
|
|
Device Power Management API
|
|
===========================
|
|
|
|
The SOC interface and application use these APIs to perform power management
|
|
operations on the devices.
|
|
|
|
Get Device List
|
|
---------------
|
|
|
|
.. code-block:: c
|
|
|
|
void device_list_get(struct device **device_list, int *device_count);
|
|
|
|
The Zephyr RTOS kernel internally maintains a list of all devices in the system.
|
|
The SOC interface uses this API to get the device list. The SOC interface can use the list to
|
|
identify the devices on which to execute power management operations.
|
|
|
|
.. note::
|
|
|
|
Ensure that the SOC interface does not alter the original list. Since the kernel
|
|
uses the original list, it must remain unchanged.
|
|
|
|
Device Set Power State
|
|
----------------------
|
|
|
|
.. code-block:: c
|
|
|
|
int device_set_power_state(struct device *device, u32_t device_power_state, device_pm_cb cb, void *arg);
|
|
|
|
Calls the :c:func:`device_pm_control()` handler function implemented by the
|
|
device driver with DEVICE_PM_SET_POWER_STATE command.
|
|
|
|
Device Get Power State
|
|
----------------------
|
|
|
|
.. code-block:: c
|
|
|
|
int device_get_power_state(struct device *device, u32_t * device_power_state);
|
|
|
|
Calls the :c:func:`device_pm_control()` handler function implemented by the
|
|
device driver with DEVICE_PM_GET_POWER_STATE command.
|
|
|
|
Busy Status Indication
|
|
======================
|
|
|
|
The SOC interface executes some power policies that can turn off power to devices,
|
|
causing them to lose their state. If the devices are in the middle of some
|
|
hardware transaction, like writing to flash memory when the power is turned
|
|
off, then such transactions would be left in an inconsistent state. This
|
|
infrastructure guards such transactions by indicating to the SOC interface that
|
|
the device is in the middle of a hardware transaction.
|
|
|
|
When the :code:`sys_suspend()` is called, the SOC interface checks if any device
|
|
is busy. The SOC interface can then decide to execute a power management scheme other than deep sleep or
|
|
to defer power management operations until the next call of
|
|
:code:`sys_suspend()`.
|
|
|
|
An alternative to using the busy status mechanism is to use the
|
|
`distributed method`_ of device power management. In such a method where the
|
|
device power management is handled in a distributed manner rather than centrally in
|
|
:code:`sys_suspend()`, the decision to enter deep sleep can be made based
|
|
on whether all devices are already turned off.
|
|
|
|
This feature can be also used to emulate a hardware feature found in some SOCs
|
|
that causes the system to automatically enter deep sleep when all devices are idle.
|
|
In such an usage, the busy status can be set by default and cleared as each
|
|
device becomes idle. When :code:`sys_suspend()` is called, deep sleep can
|
|
be entered if no device is found to be busy.
|
|
|
|
Here are the APIs used to set, clear, and check the busy status of devices.
|
|
|
|
Indicate Busy Status API
|
|
------------------------
|
|
|
|
.. code-block:: c
|
|
|
|
void device_busy_set(struct device *busy_dev);
|
|
|
|
Sets a bit corresponding to the device, in a data structure maintained by the
|
|
kernel, to indicate whether or not it is in the middle of a transaction.
|
|
|
|
Clear Busy Status API
|
|
---------------------
|
|
|
|
.. code-block:: c
|
|
|
|
void device_busy_clear(struct device *busy_dev);
|
|
|
|
Clears the bit corresponding to the device in a data structure
|
|
maintained by the kernel to indicate that the device is not in the middle of
|
|
a transaction.
|
|
|
|
Check Busy Status of Single Device API
|
|
--------------------------------------
|
|
|
|
.. code-block:: c
|
|
|
|
int device_busy_check(struct device *chk_dev);
|
|
|
|
Checks whether a device is busy. The API returns 0 if the device
|
|
is not busy.
|
|
|
|
Check Busy Status of All Devices API
|
|
------------------------------------
|
|
|
|
.. code-block:: c
|
|
|
|
int device_any_busy_check(void);
|
|
|
|
Checks if any device is busy. The API returns 0 if no device in the system is busy.
|
|
|
|
Power Management Configuration Flags
|
|
************************************
|
|
|
|
The Power Management features can be individually enabled and disabled using
|
|
the following configuration flags.
|
|
|
|
:option:`CONFIG_SYS_POWER_MANAGEMENT`
|
|
|
|
This flag enables the power management subsystem.
|
|
|
|
:option:`CONFIG_TICKLESS_IDLE`
|
|
|
|
This flag enables the tickless idle power saving feature.
|
|
|
|
:option:`CONFIG_SYS_POWER_LOW_POWER_STATES`
|
|
|
|
This flag enables support for the Low Power states.
|
|
|
|
:option:`CONFIG_SYS_POWER_DEEP_SLEEP_STATES`
|
|
|
|
This flag enables support for the Deep Sleep states.
|
|
|
|
:option:`CONFIG_DEVICE_POWER_MANAGEMENT`
|
|
|
|
This flag is enabled if the SOC interface and the devices support device power
|
|
management.
|
|
|
|
API Reference
|
|
*************
|
|
|
|
Power Management Hook Interface
|
|
===============================
|
|
|
|
.. doxygengroup:: power_management_hook_interface
|
|
:project: Zephyr
|
|
|
|
System Power Management APIs
|
|
============================
|
|
|
|
.. doxygengroup:: system_power_management_api
|
|
:project: Zephyr
|
|
|
|
Device Power Management APIs
|
|
============================
|
|
|
|
.. doxygengroup:: device_power_management_api
|
|
:project: Zephyr
|