power: Add device idle power management support
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>
This commit is contained in:
parent
e1639b5345
commit
6b21e1b7a7
6 changed files with 423 additions and 4 deletions
|
@ -261,6 +261,7 @@
|
||||||
/subsys/net/lib/mqtt/ @jukkar @tbursztyka
|
/subsys/net/lib/mqtt/ @jukkar @tbursztyka
|
||||||
/subsys/net/lib/coap/ @rveerama1
|
/subsys/net/lib/coap/ @rveerama1
|
||||||
/subsys/net/lib/sockets/ @jukkar @tbursztyka @pfalcon
|
/subsys/net/lib/sockets/ @jukkar @tbursztyka @pfalcon
|
||||||
|
/subsys/power/ @ramakrishnapallala @pizi-nordic
|
||||||
/subsys/settings/ @nvlsianpu
|
/subsys/settings/ @nvlsianpu
|
||||||
/subsys/shell/ @jarz-nordic @nordic-krch
|
/subsys/shell/ @jarz-nordic @nordic-krch
|
||||||
/subsys/storage/ @nvlsianpu
|
/subsys/storage/ @nvlsianpu
|
||||||
|
|
|
@ -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.
|
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
|
Power Management Configuration Flags
|
||||||
************************************
|
************************************
|
||||||
|
|
||||||
|
@ -450,6 +536,10 @@ the following configuration flags.
|
||||||
This flag is enabled if the SOC interface and the devices support device power
|
This flag is enabled if the SOC interface and the devices support device power
|
||||||
management.
|
management.
|
||||||
|
|
||||||
|
:code:`CONFIG_DEVICE_IDLE_PM`
|
||||||
|
|
||||||
|
This flag enables the Device Idle Power Management.
|
||||||
|
|
||||||
API Reference
|
API Reference
|
||||||
*************
|
*************
|
||||||
|
|
||||||
|
|
148
include/device.h
148
include/device.h
|
@ -130,7 +130,9 @@ extern "C" {
|
||||||
* @def DEVICE_DEFINE
|
* @def DEVICE_DEFINE
|
||||||
*
|
*
|
||||||
* @brief Create device object and set it up for boot time initialization,
|
* @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
|
* @copydetails DEVICE_AND_API_INIT
|
||||||
* @param pm_control_fn Pointer to device_pm_control function.
|
* @param pm_control_fn Pointer to device_pm_control function.
|
||||||
|
@ -144,17 +146,29 @@ extern "C" {
|
||||||
#else
|
#else
|
||||||
#define DEVICE_DEFINE(dev_name, drv_name, init_fn, pm_control_fn, \
|
#define DEVICE_DEFINE(dev_name, drv_name, init_fn, pm_control_fn, \
|
||||||
data, cfg_info, level, prio, api) \
|
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 \
|
static struct device_config _CONCAT(__config_, dev_name) __used \
|
||||||
__attribute__((__section__(".devconfig.init"))) = { \
|
__attribute__((__section__(".devconfig.init"))) = { \
|
||||||
.name = drv_name, .init = (init_fn), \
|
.name = drv_name, .init = (init_fn), \
|
||||||
.device_pm_control = (pm_control_fn), \
|
.device_pm_control = (pm_control_fn), \
|
||||||
|
.pm = &_CONCAT(__pm_, dev_name), \
|
||||||
.config_info = (cfg_info) \
|
.config_info = (cfg_info) \
|
||||||
}; \
|
}; \
|
||||||
static struct device _CONCAT(__device_, dev_name) __used \
|
static struct device _CONCAT(__device_, dev_name) __used \
|
||||||
__attribute__((__section__(".init_" #level STRINGIFY(prio)))) = { \
|
__attribute__((__section__(".init_" #level STRINGIFY(prio)))) = { \
|
||||||
.config = &_CONCAT(__config_, dev_name), \
|
.config = &_CONCAT(__config_, dev_name), \
|
||||||
.driver_api = api, \
|
.driver_api = api, \
|
||||||
.driver_data = data \
|
.driver_data = data, \
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -211,6 +225,28 @@ struct device;
|
||||||
typedef void (*device_pm_cb)(struct device *dev,
|
typedef void (*device_pm_cb)(struct device *dev,
|
||||||
int status, void *context, void *arg);
|
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
|
* @brief Static device information (In ROM) Per driver instance
|
||||||
*
|
*
|
||||||
|
@ -224,6 +260,7 @@ struct device_config {
|
||||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||||
int (*device_pm_control)(struct device *device, u32_t command,
|
int (*device_pm_control)(struct device *device, u32_t command,
|
||||||
void *context, device_pm_cb cb, void *arg);
|
void *context, device_pm_cb cb, void *arg);
|
||||||
|
struct device_pm *pm;
|
||||||
#endif
|
#endif
|
||||||
const void *config_info;
|
const void *config_info;
|
||||||
};
|
};
|
||||||
|
@ -387,8 +424,8 @@ int device_pm_control_nop(struct device *unused_device,
|
||||||
* @param cb Callback function to notify device power status
|
* @param cb Callback function to notify device power status
|
||||||
* @param arg Caller passed argument to callback function
|
* @param arg Caller passed argument to callback function
|
||||||
*
|
*
|
||||||
* @retval 0 If successful.
|
* @retval 0 If successful in queuing the request or changing the state.
|
||||||
* @retval Errno Negative errno code if failure.
|
* @retval Errno Negative errno code if failure. Callback will not be called.
|
||||||
*/
|
*/
|
||||||
static inline int device_set_power_state(struct device *device,
|
static inline int device_set_power_state(struct device *device,
|
||||||
u32_t device_power_state,
|
u32_t device_power_state,
|
||||||
|
@ -458,6 +495,109 @@ int device_any_busy_check(void);
|
||||||
*/
|
*/
|
||||||
int device_busy_check(struct device *chk_dev);
|
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
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
zephyr_sources_ifdef(CONFIG_SYS_POWER_MANAGEMENT power.c)
|
zephyr_sources_ifdef(CONFIG_SYS_POWER_MANAGEMENT power.c)
|
||||||
zephyr_sources_ifdef(CONFIG_DEVICE_POWER_MANAGEMENT device.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_SYS_PM_STATE_LOCK pm_ctrl.c)
|
||||||
|
zephyr_sources_ifdef(CONFIG_DEVICE_IDLE_PM device_pm.c)
|
||||||
zephyr_sources_if_kconfig(reboot.c)
|
zephyr_sources_if_kconfig(reboot.c)
|
||||||
add_subdirectory(policy)
|
add_subdirectory(policy)
|
||||||
|
|
|
@ -11,6 +11,16 @@ config SYS_PM_DEBUG
|
||||||
help
|
help
|
||||||
Enable System Power Management debugging hooks.
|
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"
|
source "subsys/power/policy/Kconfig"
|
||||||
|
|
||||||
module = SYS_PM
|
module = SYS_PM
|
||||||
|
|
177
subsys/power/device_pm.c
Normal file
177
subsys/power/device_pm.c
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Intel Corporation.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
#include <device.h>
|
||||||
|
#include <misc/__assert.h>
|
||||||
|
|
||||||
|
#define LOG_LEVEL CONFIG_PM_LOG_LEVEL /* From power module Kconfig */
|
||||||
|
#include <logging/log.h>
|
||||||
|
LOG_MODULE_DECLARE(power);
|
||||||
|
|
||||||
|
/* Device PM request type */
|
||||||
|
#define DEVICE_PM_SYNC (0 << 0)
|
||||||
|
#define DEVICE_PM_ASYNC (1 << 0)
|
||||||
|
|
||||||
|
static void device_pm_callback(struct device *dev,
|
||||||
|
int retval, void *context, void *arg)
|
||||||
|
{
|
||||||
|
__ASSERT(retval == 0, "Device set power state failed");
|
||||||
|
|
||||||
|
/* Set the fsm_state */
|
||||||
|
if (*((u32_t *)context) == DEVICE_PM_ACTIVE_STATE) {
|
||||||
|
atomic_set(&dev->config->pm->fsm_state,
|
||||||
|
DEVICE_PM_FSM_STATE_ACTIVE);
|
||||||
|
} else {
|
||||||
|
atomic_set(&dev->config->pm->fsm_state,
|
||||||
|
DEVICE_PM_FSM_STATE_SUSPENDED);
|
||||||
|
}
|
||||||
|
|
||||||
|
k_work_submit(&dev->config->pm->work);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pm_work_handler(struct k_work *work)
|
||||||
|
{
|
||||||
|
struct device_pm *pm = CONTAINER_OF(work,
|
||||||
|
struct device_pm, work);
|
||||||
|
struct device *dev = pm->dev;
|
||||||
|
int ret = 0;
|
||||||
|
u8_t pm_state;
|
||||||
|
|
||||||
|
switch (atomic_get(&dev->config->pm->fsm_state)) {
|
||||||
|
case DEVICE_PM_FSM_STATE_ACTIVE:
|
||||||
|
if ((atomic_get(&dev->config->pm->usage) == 0) &&
|
||||||
|
dev->config->pm->enable) {
|
||||||
|
atomic_set(&dev->config->pm->fsm_state,
|
||||||
|
DEVICE_PM_FSM_STATE_SUSPENDING);
|
||||||
|
ret = device_set_power_state(dev,
|
||||||
|
DEVICE_PM_SUSPEND_STATE,
|
||||||
|
device_pm_callback, NULL);
|
||||||
|
} else {
|
||||||
|
pm_state = DEVICE_PM_ACTIVE_STATE;
|
||||||
|
goto fsm_out;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DEVICE_PM_FSM_STATE_SUSPENDED:
|
||||||
|
if ((atomic_get(&dev->config->pm->usage) > 0) ||
|
||||||
|
!dev->config->pm->enable) {
|
||||||
|
atomic_set(&dev->config->pm->fsm_state,
|
||||||
|
DEVICE_PM_FSM_STATE_RESUMING);
|
||||||
|
ret = device_set_power_state(dev,
|
||||||
|
DEVICE_PM_ACTIVE_STATE,
|
||||||
|
device_pm_callback, NULL);
|
||||||
|
} else {
|
||||||
|
pm_state = DEVICE_PM_SUSPEND_STATE;
|
||||||
|
goto fsm_out;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DEVICE_PM_FSM_STATE_SUSPENDING:
|
||||||
|
case DEVICE_PM_FSM_STATE_RESUMING:
|
||||||
|
/* Do nothing: We are waiting for device_pm_callback() */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_ERR("Invalid FSM state!!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
__ASSERT(ret == 0, "Set Power state error");
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
fsm_out:
|
||||||
|
k_poll_signal_raise(&dev->config->pm->signal, pm_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int device_pm_request(struct device *dev,
|
||||||
|
u32_t target_state, u32_t pm_flags)
|
||||||
|
{
|
||||||
|
int result, signaled = 0;
|
||||||
|
|
||||||
|
__ASSERT((target_state == DEVICE_PM_ACTIVE_STATE) ||
|
||||||
|
(target_state == DEVICE_PM_SUSPEND_STATE),
|
||||||
|
"Invalid device PM state requested");
|
||||||
|
|
||||||
|
if (target_state == DEVICE_PM_ACTIVE_STATE) {
|
||||||
|
if (atomic_inc(&dev->config->pm->usage) < 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (atomic_dec(&dev->config->pm->usage) > 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
k_work_submit(&dev->config->pm->work);
|
||||||
|
|
||||||
|
/* Return in case of Async request */
|
||||||
|
if (pm_flags & DEVICE_PM_ASYNC) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Incase of Sync request wait for completion event */
|
||||||
|
do {
|
||||||
|
(void)k_poll(&dev->config->pm->event, 1, K_FOREVER);
|
||||||
|
k_poll_signal_check(&dev->config->pm->signal,
|
||||||
|
&signaled, &result);
|
||||||
|
} while (!signaled);
|
||||||
|
|
||||||
|
dev->config->pm->event.state = K_POLL_STATE_NOT_READY;
|
||||||
|
k_poll_signal_reset(&dev->config->pm->signal);
|
||||||
|
|
||||||
|
|
||||||
|
return result == target_state ? 0 : -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
int device_pm_get(struct device *dev)
|
||||||
|
{
|
||||||
|
return device_pm_request(dev,
|
||||||
|
DEVICE_PM_ACTIVE_STATE, DEVICE_PM_ASYNC);
|
||||||
|
}
|
||||||
|
|
||||||
|
int device_pm_get_sync(struct device *dev)
|
||||||
|
{
|
||||||
|
return device_pm_request(dev, DEVICE_PM_ACTIVE_STATE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int device_pm_put(struct device *dev)
|
||||||
|
{
|
||||||
|
return device_pm_request(dev,
|
||||||
|
DEVICE_PM_SUSPEND_STATE, DEVICE_PM_ASYNC);
|
||||||
|
}
|
||||||
|
|
||||||
|
int device_pm_put_sync(struct device *dev)
|
||||||
|
{
|
||||||
|
return device_pm_request(dev, DEVICE_PM_SUSPEND_STATE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void device_pm_enable(struct device *dev)
|
||||||
|
{
|
||||||
|
k_sem_take(&dev->config->pm->lock, K_FOREVER);
|
||||||
|
dev->config->pm->enable = true;
|
||||||
|
|
||||||
|
/* During the driver init, device can set the
|
||||||
|
* PM state accordingly. For later cases we need
|
||||||
|
* to check the usage and set the device PM state.
|
||||||
|
*/
|
||||||
|
if (!dev->config->pm->dev) {
|
||||||
|
dev->config->pm->dev = dev;
|
||||||
|
atomic_set(&dev->config->pm->fsm_state,
|
||||||
|
DEVICE_PM_FSM_STATE_SUSPENDED);
|
||||||
|
k_work_init(&dev->config->pm->work, pm_work_handler);
|
||||||
|
} else {
|
||||||
|
k_work_submit(&dev->config->pm->work);
|
||||||
|
}
|
||||||
|
k_sem_give(&dev->config->pm->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void device_pm_disable(struct device *dev)
|
||||||
|
{
|
||||||
|
k_sem_take(&dev->config->pm->lock, K_FOREVER);
|
||||||
|
dev->config->pm->enable = false;
|
||||||
|
/* Bring up the device before disabling the Idle PM */
|
||||||
|
k_work_submit(&dev->config->pm->work);
|
||||||
|
k_sem_give(&dev->config->pm->lock);
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue