Skip to content

Commit

Permalink
subsys: power: rework device runtime power management.
Browse files Browse the repository at this point in the history
In order to reduce the overall system power consumption, we should
suspend the devices which are idle or not being used while system is
active or running. Currently there is device idle power management
framework which intends to do that. But the implementation seems can
only do get/put one after one and can't handle the concurrency, for
example if multiple threads request for DEVICE_PM_ACTIVE_STATE
concurrently, there is possibility polling the signal without reset
and signal contention among multiple threads. And the disable function
doesn't consider the ongoing transition. Further it doesn't consider
the device dependency. So decide rework the implementation and rename
it device runtime power management following the definition in zephyrproject-rtos#24228.

The API rt_dpm_claim is trying to resume the device and protect any
hardware transfer after this call by increasing the usage count. When
there is concurrency with another claim or release, this API will pend
the current thread to the wait_q until previous transition finished.

The API rt_dpm_release is to release previous claim, forbid unexpected
release. And no hardware operation depends on release, so release has
asynchronous version. After the release, the parents of that device
will also be considered automatically.

And it can be decided by individual device to support device runtime
power management or not by the API rt_dpm_enable/rt_dpm_disable. And
also it's the device driver instead of this framework that decide how
to define device not in use, it means device driver decide where to
put rt_dpm_claim/rt_dpm_release, for example we can put them around
transfer function for i2c, but for net device, maybe we can only put
them around open/close or other similar place.

Signed-off-by: Wentong Wu <wentong.wu@intel.com>
  • Loading branch information
wentongwu committed Jul 20, 2020
1 parent ff50ca1 commit 7c7399f
Show file tree
Hide file tree
Showing 6 changed files with 526 additions and 230 deletions.
87 changes: 42 additions & 45 deletions doc/reference/power_management/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -399,90 +399,87 @@ Check Busy Status of All Devices API
Checks if any device is busy. The API returns 0 if no device in the system is busy.

Device Idle Power Management
Device Runtime Power Management
****************************


The Device Idle Power Management framework is a 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.
The Device Runtime Power Management framework is a kind of active power
management mechanism which reduces the overall system power consumtion
by suspending the devices which are not being used while the system is
active or running. And resume the devices when they are first needed.
And the terms suspend and resume used here is generic state transition
instead of going to specific device power state, because the framework
cannot make the exact decision based only on usage count, device should
adjust the going to state according to their characteristics.

The framework uses device_set_power_state() API set the
device power state accordingly based on the usage count.
The framework makes the state transition decision based on device usage
count, device current power state and the state transition of device's
parents when it receives claim/release request. The device dependencies
information is obtained from device tree. When concurrency requests
happenes, the ongoing state transition will wait until previous one
completes with the help of wait queue.

The interfaces and APIs provided by the Device Idle PM are
designed to be generic and architecture independent.
The APIs provided by the Device Runtime Power Management are designed
to be generic and architecture independent.

Device Idle Power Management API
Device Runtime Power Management API
================================

The Device Drivers use these APIs to perform device idle power management
operations on the devices.
The Device Drivers use these APIs to perform device runtime power
management operations on the devices.

Enable Device Idle Power Management of a Device API
Enable Device Runtime Power Management of a Device API
---------------------------------------------------

.. code-block:: c
void device_pm_enable(struct device *dev);
void rt_dpm_enable(struct device *dev);
Enbles Idle Power Management of the device.
Enables Runtime Power Management of the device.

Disable Device Idle Power Management of a Device API
Disable Device Runtime Power Management of a Device API
----------------------------------------------------

.. code-block:: c
void device_pm_disable(struct device *dev);
Disables Idle Power Management of the device.
void rt_dpm_disable(struct device *dev);
Resume Device asynchronously API
--------------------------------

.. code-block:: c
int device_pm_get(struct device *dev);
Marks the device as being used. This API will asynchronously
bring the device to resume state. The API returns 0 on success.
Disables Runtime Power Management of the device.

Resume Device synchronously API
-------------------------------

.. code-block:: c
int device_pm_get_sync(struct device *dev);
int rt_dpm_claim(struct device *dev);
Marks the device as being used. It will bring up or resume
the device if it is in suspended state based on the device
usage count. This call is blocked until the device PM state
is changed to active. The API returns 0 on success.
Marks the device as being used and protects the hardware transfers after
this call from unexpected suspend by increasing the device usage count.
It will try to bring up or resume the device if it is in suspended state.
The API returns 0 on success.

Suspend Device asynchronously API
---------------------------------

.. code-block:: c
int device_pm_put(struct device *dev);
int rt_dpm_release_async(struct device *dev);
Marks the device as being released. This API asynchronously put
the device to suspend state if not already in suspend state.
The API returns 0 on success.
Release the given device asynchronously. This API decreases usage
count of the given device and submits suspend request to work queue
when usage count is crossing 1 - 0 boundary. The API returns 0 on success.

Suspend Device synchronously API
--------------------------------

.. code-block:: c
int device_pm_put_sync(struct device *dev);
int rt_dpm_release(struct device *dev);
Marks the device as being released. It will put the device to
suspended state if is is in active state based on the device
usage count. This call is blocked until the device PM state
is changed to resume. The API returns 0 on success. This
call is blocked until the device is suspended.
Release the given device synchronously. It will put the device to
suspended state if is is in active state and no one is using
the device which is indicated by the device usage count. The API
returns 0 on success.


Power Management Configuration Flags
Expand Down Expand Up @@ -512,9 +509,9 @@ the following configuration flags.
This flag is enabled if the SOC interface and the devices support device power
management.

:code:`CONFIG_DEVICE_IDLE_PM`
:code:`CONFIG_DEVICE_RUNTIME_PM`

This flag enables the Device Idle Power Management.
This flag enables the Device Runtime Power Management.

API Reference
*************
Expand Down
167 changes: 167 additions & 0 deletions include/power/rt_dpm.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/*
* Copyright (c) 2020 Intel corporation
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_INCLUDE_POWER_RT_DPM_H
#define ZEPHYR_INCLUDE_POWER_RT_DPM_H

#include <device.h>
#include <sys/atomic.h>
#include <kernel_structs.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief Callbacks defined for device runtime power management.
*
* The post_resume/pre_suspend callbacks are used to do stuff without
* irq locked (e.g. states sync between driver and firmware). Device
* driver should take care all the possible errors during this irq
* unlocked period and the result will be indicated by the return value.
*
* The resume/suspend callbacks will do the real power/clock involved
* stuff with irq locked. The specific state devices will go completely
* depends on every device, resume/suspend is generic definition here.
*
* @retval 0 if successfully excute post_resume/pre_suspend.
* @retval -EIO if error happens during irq unlocked stage.
*/
struct rt_dpm_ops {
void (*resume)(struct device *dev);
void (*suspend)(struct device *dev);
int (*post_resume)(struct device *dev);
int (*pre_suspend)(struct device *dev);
};

/**
* @brief Structure used to do device runtime power management.
*/
struct rt_dpm {
/* device runtime pm state */
int state;

/* wait queue on which threads are pended
* because of the possible concurrency.
*/
_wait_q_t wait_q;

/* work item used to do asynchronously state transition */
struct k_work work;

/* usage count of the device */
atomic_t usage_count;

struct k_spinlock lock;
struct rt_dpm_ops *ops;

/* protected by spinlock so don't use atomic */
unsigned int disable_count;
};

/**
* @brief Loop over parent for the given device.
*/
#define DEVICE_PARENT_FOREACH(dev, iterator) \
for (struct device *iterator = dev; iterator != NULL; )

/**
* @brief Initialize device runtime power management.
*
* Initialize device runtime power management for the given device.
*
* @param dev Pointer to the given device.
* @param is_suspend Initial state of given device.
*/
void rt_dpm_init(struct device *dev, bool is_suspend);

/**
* @brief Enable device runtime power management.
*
* Enable device runtime power management for the given device.
*
* @param dev Pointer to the given device.
*/
void rt_dpm_enable(struct device *dev);

/**
* @brief Disable device runtime power management.
*
* Disable device runtime power management for the given device.
*
* @param dev Pointer to the given device.
*/
void rt_dpm_disable(struct device *dev);

/**
* @brief Claim a device to mark the device as being used.
*
* Claim the given device to make sure any device operation protected
* by the succeful claim is safe. Can't be used in ISRs.
*
* @param dev Pointer to the given device.
*
* @retval 0 if successfully claimed the given device.
* @retval -EIO if error happens during device post resume.
* @retval -EACCES if disabled device runtime power management.
*/
int rt_dpm_claim(struct device *dev);

/**
* @brief Release the given device.
*
* Synchronously decrease usage count of the given device and suspend
* that device if all the conditions satisfied, this function must be
* called after any claim happens, forbid asymmetric release. Can't be
* used in ISRs.
*
* @param dev Pointer to the given device.
*
* @retval 0 if successfully release the given device.
* @retval -EIO if error happens during device suspend prepare.
* @retval -EACCES if disabled device runtime power management.
*/
int rt_dpm_release(struct device *dev);

/**
* @brief Release the given device asynchronously.
*
* Decrease usage count of the given device and submit suspend request
* to work queue when usage count crossing 1 - 0 boundary. Can be used
* in ISRs.
*
* @param dev Pointer to the given device.
*
* @retval 0 if successfully release the given device.
* @retval 1 if successfully submit the release request.
*/
int rt_dpm_release_async(struct device *dev);

/**
* @brief Whether given device is in active state.
*
* @param dev Pointer to the given device.
*
* @retval True if given device is in active state.
* @retval False if given device isn't in active state.
*/
bool rt_dpm_is_active_state(struct device *dev);

/**
* @brief Whether given device is in suspended state.
*
* @param dev Pointer to the given device.
*
* @retval True if given device is in suspended state.
* @retval False if given device isn't in suspended state.
*/
bool rt_dpm_is_suspend_state(struct device *dev);

#ifdef __cplusplus
}
#endif

#endif
8 changes: 7 additions & 1 deletion subsys/power/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
zephyr_sources_ifdef(CONFIG_SYS_POWER_MANAGEMENT power.c)
zephyr_sources_ifdef(CONFIG_DEVICE_POWER_MANAGEMENT device.c)
zephyr_sources_ifdef(CONFIG_SYS_PM_STATE_LOCK pm_ctrl.c)
zephyr_sources_ifdef(CONFIG_DEVICE_IDLE_PM device_pm.c)
zephyr_sources_ifdef(CONFIG_DEVICE_RUNTIME_PM rt_dpm.c)
zephyr_sources_if_kconfig(reboot.c)
add_subdirectory(policy)

zephyr_include_directories_ifdef(
CONFIG_DEVICE_RUNTIME_PM
${ZEPHYR_BASE}/kernel/include
${ZEPHYR_BASE}/arch/${ARCH}/include
)
13 changes: 6 additions & 7 deletions subsys/power/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,14 @@ config SYS_PM_DEBUG
help
Enable System Power Management debugging hooks.

config DEVICE_IDLE_PM
bool "Enable device Idle Power Management"
config DEVICE_RUNTIME_PM
bool "Enable device runtime Power Management"
depends on DEVICE_POWER_MANAGEMENT
select POLL
help
Enable device Idle Power Management to save power.
With device Idle PM enabled, devices can be suspended or
resumed based on the device usage even while the CPU or
system is running.
Enable device runtime Power Management to save power.
With device runtime PM enabled, devices can be suspended
or resumed based on the device usage even while the CPU
or system is running.

source "subsys/power/policy/Kconfig"

Expand Down
Loading

0 comments on commit 7c7399f

Please sign in to comment.