Skip to content

Commit

Permalink
power: Add device idle power management support
Browse files Browse the repository at this point in the history
Add framework for device Idle Power Management(IPM)
for suspending devices based on device idle. This will
help in saving power even while system(CPU) is active.

The framework uses device_set_power_state() API set the
device power state accordingly based on the usage count.

Signed-off-by: Ramakrishna Pallala <ramakrishna.pallala@intel.com>
  • Loading branch information
Ramakrishna Pallala committed Mar 13, 2019
1 parent c3b0da8 commit b4eb42f
Show file tree
Hide file tree
Showing 6 changed files with 423 additions and 4 deletions.
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@
/subsys/net/lib/mqtt/ @jukkar @tbursztyka
/subsys/net/lib/coap/ @rveerama1
/subsys/net/lib/sockets/ @jukkar @tbursztyka @pfalcon
/subsys/power/ @ramakrishnapallala @pizi-nordic
/subsys/settings/ @nvlsianpu
/subsys/shell/ @jarz-nordic @nordic-krch
/subsys/storage/ @nvlsianpu
Expand Down
90 changes: 90 additions & 0 deletions doc/reference/power_management/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,92 @@ 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
****************************


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 framework uses device_set_power_state() API set the
device power state accordingly based on the usage count.

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

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

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

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

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

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

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

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.

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

.. code-block:: c
int device_pm_get_sync(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.

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

.. code-block:: c
int device_pm_put(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.

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

.. code-block:: c
int device_pm_put_sync(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.


Power Management Configuration Flags
************************************

Expand Down Expand Up @@ -450,6 +536,10 @@ the following configuration flags.
This flag is enabled if the SOC interface and the devices support device power
management.

:code:`CONFIG_DEVICE_IDLE_PM`

This flag enables the Device Idle Power Management.

API Reference
*************

Expand Down
148 changes: 144 additions & 4 deletions include/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,9 @@ extern "C" {
* @def DEVICE_DEFINE
*
* @brief Create device object and set it up for boot time initialization,
* with the option to device_pm_control.
* with the option to device_pm_control. In case of Device Idle Power
* Management is enabled, make sure the device is in suspended state after
* initialization.
*
* @copydetails DEVICE_AND_API_INIT
* @param pm_control_fn Pointer to device_pm_control function.
Expand All @@ -144,17 +146,29 @@ extern "C" {
#else
#define DEVICE_DEFINE(dev_name, drv_name, init_fn, pm_control_fn, \
data, cfg_info, level, prio, api) \
static struct device_pm _CONCAT(__pm_, dev_name) __used \
= { \
.usage = ATOMIC_INIT(0), \
.lock = _K_SEM_INITIALIZER( \
_CONCAT(__pm_, dev_name).lock, 1, 1), \
.signal = K_POLL_SIGNAL_INITIALIZER( \
_CONCAT(__pm_, dev_name).signal), \
.event = K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL, \
K_POLL_MODE_NOTIFY_ONLY, \
&_CONCAT(__pm_, dev_name).signal), \
}; \
static struct device_config _CONCAT(__config_, dev_name) __used \
__attribute__((__section__(".devconfig.init"))) = { \
.name = drv_name, .init = (init_fn), \
.device_pm_control = (pm_control_fn), \
.pm = &_CONCAT(__pm_, dev_name), \
.config_info = (cfg_info) \
}; \
static struct device _CONCAT(__device_, dev_name) __used \
__attribute__((__section__(".init_" #level STRINGIFY(prio)))) = { \
.config = &_CONCAT(__config_, dev_name), \
.driver_api = api, \
.driver_data = data \
.driver_data = data, \
}
#endif

Expand Down Expand Up @@ -211,6 +225,28 @@ struct device;
typedef void (*device_pm_cb)(struct device *dev,
int status, void *context, void *arg);

/**
* @brief Device PM info
*
* @param dev pointer to device structure
* @param lock lock to synchronize the get/put operations
* @param enable device pm enable flag
* @param usage device usage count
* @param fsm_state device idle internal power state
* @param event event object to listen to the sync request events
* @param signal signal to notify the Async API callers
*/
struct device_pm {
struct device *dev;
struct k_sem lock;
bool enable;
atomic_t usage;
atomic_t fsm_state;
struct k_work work;
struct k_poll_event event;
struct k_poll_signal signal;
};

/**
* @brief Static device information (In ROM) Per driver instance
*
Expand All @@ -224,6 +260,7 @@ struct device_config {
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
int (*device_pm_control)(struct device *device, u32_t command,
void *context, device_pm_cb cb, void *arg);
struct device_pm *pm;
#endif
const void *config_info;
};
Expand Down Expand Up @@ -387,8 +424,8 @@ int device_pm_control_nop(struct device *unused_device,
* @param cb Callback function to notify device power status
* @param arg Caller passed argument to callback function
*
* @retval 0 If successful.
* @retval Errno Negative errno code if failure.
* @retval 0 If successful in queuing the request or changing the state.
* @retval Errno Negative errno code if failure. Callback will not be called.
*/
static inline int device_set_power_state(struct device *device,
u32_t device_power_state,
Expand Down Expand Up @@ -458,6 +495,109 @@ int device_any_busy_check(void);
*/
int device_busy_check(struct device *chk_dev);

#ifdef CONFIG_DEVICE_IDLE_PM

/* Device PM FSM states */
enum device_pm_fsm_state {
DEVICE_PM_FSM_STATE_ACTIVE = 1,
DEVICE_PM_FSM_STATE_SUSPENDED,
DEVICE_PM_FSM_STATE_SUSPENDING,
DEVICE_PM_FSM_STATE_RESUMING,
};

/**
* @brief Enable device idle PM
*
* Called by a device driver to enable device idle power management.
* The device might be asynchronously suspended if Idle PM is enabled
* when the device is not use.
*
* @param dev Pointer to device structure of the specific device driver
* the caller is interested in.
*/
void device_pm_enable(struct device *dev);

/**
* @brief Disable device idle PM
*
* Called by a device driver to disable device idle power management.
* The device might be asynchronously resumed if Idle PM is disabled
*
* @param dev Pointer to device structure of the specific device driver
* the caller is interested in.
*/
void device_pm_disable(struct device *dev);

/**
* @brief Call device resume asynchronously based on usage count
*
* Called by a device driver to mark the device as being used.
* This API will asynchronously bring the device to resume state
* if it not already in active state.
*
* @param dev Pointer to device structure of the specific device driver
* the caller is interested in.
* @retval 0 If successfully queued the Async request. If queued,
* the caller need to wait on the poll event linked to device
* pm signal mechanism to know the comepletion of resume operation.
* @retval Errno Negative errno code if failure.
*/
int device_pm_get(struct device *dev);

/**
* @brief Call device resume synchronously based on usage count
*
* Called by a device driver to mark 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 resume.
*
* @param dev Pointer to device structure of the specific device driver
* the caller is interested in.
* @retval 0 If successful.
* @retval Errno Negative errno code if failure.
*/
int device_pm_get_sync(struct device *dev);

/**
* @brief Call device suspend asynchronously based on usage count
*
* Called by a device driver to mark the device as being released.
* This API asynchronously put the device to suspend state if
* it not already in suspended state.
*
* @param dev Pointer to device structure of the specific device driver
* the caller is interested in.
* @retval 0 If successfully queued the Async request. If queued,
* the caller need to wait on the poll event linked to device pm
* signal mechanism to know the comepletion of suspend operation.
* @retval Errno Negative errno code if failure.
*/
int device_pm_put(struct device *dev);

/**
* @brief Call device suspend synchronously based on usage count
*
* Called by a device driver to mark 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.
*
* @param dev Pointer to device structure of the specific device driver
* the caller is interested in.
* @retval 0 If successful.
* @retval Errno Negative errno code if failure.
*/
int device_pm_put_sync(struct device *dev);
#else
static inline void device_pm_enable(struct device *dev) { }
static inline void device_pm_disable(struct device *dev) { }
static inline int device_pm_get(struct device *dev) { return 0; }
static inline int device_pm_get_sync(struct device *dev) { return 0; }
static inline int device_pm_put(struct device *dev) { return 0; }
static inline int device_pm_put_sync(struct device *dev) { return 0; }
#endif

#endif

/**
Expand Down
1 change: 1 addition & 0 deletions subsys/power/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
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_if_kconfig(reboot.c)
add_subdirectory(policy)
10 changes: 10 additions & 0 deletions subsys/power/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ config SYS_PM_DEBUG
help
Enable System Power Management debugging hooks.

config DEVICE_IDLE_PM
bool "Enable device Idle 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.

source "subsys/power/policy/Kconfig"

module = SYS_PM
Expand Down
Loading

0 comments on commit b4eb42f

Please sign in to comment.