pm: device_runtime: suspend device on enable

The pm_device_runtime_enable did not suspend devices, so it assumed that
the device was in a physically suspended state. This change makes sure
that device is left in a suspended state if the device is initially
active.

Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
This commit is contained in:
Gerard Marull-Paretas 2021-12-02 12:31:06 +01:00 committed by Carles Cufí
commit 91b8abfb4d
4 changed files with 34 additions and 7 deletions

View file

@ -142,23 +142,28 @@ special synchronization is required.
To enable device runtime power management on a device, the driver needs to call
:c:func:`pm_device_runtime_enable` at initialization time. Note that this
function will put the device into the :c:enumerator:`PM_DEVICE_STATE_SUSPENDED`
state. In case this does not reflect the actual device state, the init function
must perform the necessary operations to suspend the device.
function will suspend the device if its state is
:c:enumerator:`PM_DEVICE_STATE_ACTIVE`. In case the device is physically
suspended, the init function should call
:c:func:`pm_device_runtime_init_suspended` before calling
:c:func:`pm_device_runtime_enable`.
.. code-block:: c
/* device driver initialization function */
static int mydev_init(const struct device *dev)
{
int ret;
...
/* make sure the device physically is suspended */
/* OPTIONAL: mark device as suspended if it is physically suspended */
pm_device_runtime_init_suspended(dev);
/* enable device runtime power management */
ret = pm_device_runtime_enable(dev);
if (ret < 0) {
return ret;
if ((ret < 0) && (ret != -ENOSYS)) {
return ret;
}
...
}
Assuming an example device driver that implements an ``operation`` API call, the

View file

@ -25,6 +25,9 @@ extern "C" {
/**
* @brief Enable device runtime PM
*
* This function will enable runtime PM on the given device. If the device is
* in #PM_DEVICE_STATE_ACTIVE state, the device will be suspended.
*
* @funcprops \pre_kernel_ok
*
* @param dev Device instance.
@ -32,6 +35,9 @@ extern "C" {
* @retval 0 If the device runtime PM is enabled successfully.
* @retval -EPERM If device has power state locked.
* @retval -ENOSYS If the functionality is not available.
* @retval -errno Other negative errno, result of suspending the device.
*
* @see pm_device_runtime_init_suspended()
*/
int pm_device_runtime_enable(const struct device *dev);

View file

@ -188,6 +188,14 @@ int pm_device_runtime_enable(const struct device *dev)
pm->dev = dev;
k_work_init_delayable(&pm->work, runtime_suspend_work);
}
if (pm->state == PM_DEVICE_STATE_ACTIVE) {
ret = pm->action_cb(pm->dev, PM_DEVICE_ACTION_SUSPEND);
if (ret < 0) {
goto unlock;
}
}
pm->state = PM_DEVICE_STATE_SUSPENDED;
pm->usage = 0U;

View file

@ -35,6 +35,7 @@ static void get_runner(void *arg1, void *arg2, void *arg3)
static void test_api_setup(void)
{
int ret;
enum pm_device_state state;
/* check API always returns 0 when runtime PM is disabled */
ret = pm_device_runtime_get(dev);
@ -47,6 +48,13 @@ static void test_api_setup(void)
/* enable runtime PM */
ret = pm_device_runtime_enable(dev);
zassert_equal(ret, 0, NULL);
(void)pm_device_state_get(dev, &state);
zassert_equal(state, PM_DEVICE_STATE_SUSPENDED, NULL);
/* enabling again should succeed (no-op) */
ret = pm_device_runtime_enable(dev);
zassert_equal(ret, 0, NULL);
}
static void test_api_teardown(void)