| .. _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:`CPU LPS (Low Power State)` |
| Refers to any one of the low power states supported by the CPU. The CPU is |
| usually powered on while the clocks are power gated. |
| |
| :dfn:`Active State` |
| The CPU and clocks are powered on. This is the normal operating state when |
| the system is running. |
| |
| :dfn:`Deep Sleep State` |
| The CPU is power gated and loses context. Most peripherals would also be |
| power gated. RAM is selectively retained. |
| |
| :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:`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 tasks. 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. This section provides a general overview of the hook |
| functions. Refer to :ref:`power_management_api` for the detailed description of |
| the APIs. |
| |
| Suspend Hook function |
| ===================== |
| |
| .. code-block:: c |
| |
| int _sys_soc_suspend(s32_t ticks); |
| |
| When the kernel is about to go idle, the power management subsystem calls the |
| :code:`_sys_soc_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 low power state. Before entering the low power state, |
| the SOC interface must setup a wake event. |
| |
| The power management subsystem expects the :code:`_sys_soc_suspend()` to |
| return one of the following values based on the power management operations |
| the SOC interface executed: |
| |
| :code:`SYS_PM_NOT_HANDLED` |
| |
| Indicates that no power management operations were performed. |
| |
| :code:`SYS_PM_LOW_POWER_STATE` |
| |
| Indicates that the CPU was put in a low power state. |
| |
| :code:`SYS_PM_DEEP_SLEEP` |
| |
| Indicates that the SOC was put in a deep sleep state. |
| |
| Resume Hook function |
| ==================== |
| |
| .. code-block:: c |
| |
| void _sys_soc_resume(void); |
| |
| The power management subsystem optionally calls this hook function when exiting |
| kernel idling if power management operations were performed in |
| :code:`_sys_soc_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_soc_pm_idle_exit_notification_disable()` from |
| :code:`_sys_soc_suspend()`. |
| |
| Resume From Deep Sleep Hook function |
| ==================================== |
| |
| .. code-block:: c |
| |
| void _sys_soc_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 low 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. |
| |
| * SYS_PM_LOW_POWER_STATE |
| * SYS_PM_DEEP_SLEEP |
| |
| SYS_PM_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. |
| |
| SYS_PM_DEEP_SLEEP |
| ================= |
| |
| 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 |
| `SYS_PM_LOW_POWER_STATE`_ 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. |
| Refer to :ref:`power_management_api` for detailed descriptions of the APIs. |
| |
| 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 low power states quickly when |
| :code:`_sys_soc_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 low power state by the application or component managing the |
| devices. |
| |
| Central method |
| ============== |
| |
| In this method device power management is mostly done inside |
| :code:`_sys_soc_suspend()` along with entering a CPU or SOC low 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); |
| |
| 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_soc_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_soc_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_soc_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_soc_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. |
| |
| :code:`CONFIG_SYS_POWER_MANAGEMENT` |
| |
| This flag enables the power management subsystem. |
| |
| :code:`CONFIG_TICKLESS_IDLE` |
| |
| This flag enables the tickless idle power saving feature. |
| |
| :code:`CONFIG_SYS_POWER_LOW_POWER_STATE` |
| |
| The SOC interface enables this flag to use the :code:`SYS_PM_LOW_POWER_STATE` policy. |
| |
| :code:`CONFIG_SYS_POWER_DEEP_SLEEP` |
| |
| This flag enables support for the :code:`SYS_PM_DEEP_SLEEP` policy. |
| |
| :code:`CONFIG_DEVICE_POWER_MANAGEMENT` |
| |
| This flag is enabled if the SOC interface and the devices support device power |
| management. |
| |