Browse Source

pm: device_runtime: Make async optional

Async now uses its own work queue, which means it consumes more
resources. Since not all applications need the async API, we can make
it optional without any penalty for those applications.

Signed-off-by: Flavio Ceolin <flavio@hubblenetwork.com>
pull/85772/head
Flavio Ceolin 3 months ago committed by Benjamin Cabé
parent
commit
437b56d22e
  1. 2
      include/zephyr/pm/device.h
  2. 10
      subsys/pm/Kconfig
  3. 22
      subsys/pm/device_runtime.c
  4. 16
      tests/subsys/pm/device_runtime_api/src/main.c
  5. 5
      tests/subsys/pm/device_runtime_api/testcase.yaml

2
include/zephyr/pm/device.h

@ -170,8 +170,10 @@ struct pm_device { @@ -170,8 +170,10 @@ struct pm_device {
struct k_sem lock;
/** Event var to listen to the sync request events */
struct k_event event;
#if defined(CONFIG_PM_DEVICE_RUNTIME_ASYNC) || defined(__DOXYGEN__)
/** Work object for asynchronous calls */
struct k_work_delayable work;
#endif /* CONFIG_PM_DEVICE_RUNTIME_ASYNC */
#endif /* CONFIG_PM_DEVICE_RUNTIME */
};

10
subsys/pm/Kconfig

@ -126,6 +126,15 @@ if PM_DEVICE_RUNTIME @@ -126,6 +126,15 @@ if PM_DEVICE_RUNTIME
config PM_DEVICE_DRIVER_NEEDS_DEDICATED_WQ
bool
config PM_DEVICE_RUNTIME_ASYNC
bool "Asynchronous device runtime power management"
default y
help
Use this option to enable support for asynchronous operation
in the power management device runtime.
if PM_DEVICE_RUNTIME_ASYNC
choice PM_DEVICE_RUNTIME_WQ
prompt "Work queue to be used by pm device runtime async"
default PM_DEVICE_RUNTIME_USE_DEDICATED_WQ if PM_DEVICE_DRIVER_NEEDS_DEDICATED_WQ
@ -164,6 +173,7 @@ config PM_DEVICE_RUNTIME_DEDICATED_WQ_INIT_PRIO @@ -164,6 +173,7 @@ config PM_DEVICE_RUNTIME_DEDICATED_WQ_INIT_PRIO
endif #PM_DEVICE_RUNTIME_USE_DEDICATED_WQ
endchoice
endif # PM_DEVICE_RUNTIME_ASYNC
endif # PM_DEVICE_RUNTIME
config PM_DEVICE_SHELL

22
subsys/pm/device_runtime.c

@ -20,10 +20,12 @@ LOG_MODULE_DECLARE(pm_device, CONFIG_PM_DEVICE_LOG_LEVEL); @@ -20,10 +20,12 @@ LOG_MODULE_DECLARE(pm_device, CONFIG_PM_DEVICE_LOG_LEVEL);
#define PM_DOMAIN(_pm) NULL
#endif
#ifdef CONFIG_PM_DEVICE_RUNTIME_ASYNC
#ifdef CONFIG_PM_DEVICE_RUNTIME_USE_DEDICATED_WQ
K_THREAD_STACK_DEFINE(pm_device_runtime_stack, CONFIG_PM_DEVICE_RUNTIME_DEDICATED_WQ_STACK_SIZE);
static struct k_work_q pm_device_runtime_wq;
#endif /* CONFIG_PM_DEVICE_RUNTIME_USE_DEDICATED_WQ */
#endif /* CONFIG_PM_DEVICE_RUNTIME_ASYNC */
#define EVENT_STATE_ACTIVE BIT(PM_DEVICE_STATE_ACTIVE)
#define EVENT_STATE_SUSPENDED BIT(PM_DEVICE_STATE_SUSPENDED)
@ -84,12 +86,14 @@ static int runtime_suspend(const struct device *dev, bool async, @@ -84,12 +86,14 @@ static int runtime_suspend(const struct device *dev, bool async,
if (async) {
/* queue suspend */
#ifdef CONFIG_PM_DEVICE_RUNTIME_ASYNC
pm->base.state = PM_DEVICE_STATE_SUSPENDING;
#ifdef CONFIG_PM_DEVICE_RUNTIME_USE_SYSTEM_WQ
(void)k_work_schedule(&pm->work, delay);
#else
(void)k_work_schedule_for_queue(&pm_device_runtime_wq, &pm->work, delay);
#endif /* CONFIG_PM_DEVICE_RUNTIME_USE_SYSTEM_WQ */
#endif /* CONFIG_PM_DEVICE_RUNTIME_ASYNC */
} else {
/* suspend now */
ret = pm->base.action_cb(pm->dev, PM_DEVICE_ACTION_SUSPEND);
@ -109,6 +113,7 @@ unlock: @@ -109,6 +113,7 @@ unlock:
return ret;
}
#ifdef CONFIG_PM_DEVICE_RUNTIME_ASYNC
static void runtime_suspend_work(struct k_work *work)
{
int ret;
@ -138,6 +143,7 @@ static void runtime_suspend_work(struct k_work *work) @@ -138,6 +143,7 @@ static void runtime_suspend_work(struct k_work *work)
__ASSERT(ret == 0, "Could not suspend device (%d)", ret);
}
#endif /* CONFIG_PM_DEVICE_RUNTIME_ASYNC */
static int get_sync_locked(const struct device *dev)
{
@ -235,6 +241,7 @@ int pm_device_runtime_get(const struct device *dev) @@ -235,6 +241,7 @@ int pm_device_runtime_get(const struct device *dev)
pm->base.usage++;
#ifdef CONFIG_PM_DEVICE_RUNTIME_ASYNC
/*
* Check if the device has a pending suspend operation (not started
* yet) and cancel it. This way we avoid unnecessary operations because
@ -260,6 +267,7 @@ int pm_device_runtime_get(const struct device *dev) @@ -260,6 +267,7 @@ int pm_device_runtime_get(const struct device *dev)
(void)k_sem_take(&pm->lock, K_FOREVER);
}
}
#endif /* CONFIG_PM_DEVICE_RUNTIME_ASYNC */
if (pm->base.usage > 1U) {
goto unlock;
@ -358,6 +366,7 @@ int pm_device_runtime_put(const struct device *dev) @@ -358,6 +366,7 @@ int pm_device_runtime_put(const struct device *dev)
int pm_device_runtime_put_async(const struct device *dev, k_timeout_t delay)
{
#ifdef CONFIG_PM_DEVICE_RUNTIME_ASYNC
int ret;
if (dev->pm_base == NULL) {
@ -378,6 +387,10 @@ int pm_device_runtime_put_async(const struct device *dev, k_timeout_t delay) @@ -378,6 +387,10 @@ int pm_device_runtime_put_async(const struct device *dev, k_timeout_t delay)
SYS_PORT_TRACING_FUNC_EXIT(pm, device_runtime_put_async, dev, delay, ret);
return ret;
#else
LOG_WRN("Function not available");
return -ENOSYS;
#endif /* CONFIG_PM_DEVICE_RUNTIME_ASYNC */
}
__boot_func
@ -449,7 +462,9 @@ int pm_device_runtime_enable(const struct device *dev) @@ -449,7 +462,9 @@ int pm_device_runtime_enable(const struct device *dev)
/* lazy init of PM fields */
if (pm->dev == NULL) {
pm->dev = dev;
#ifdef CONFIG_PM_DEVICE_RUNTIME_ASYNC
k_work_init_delayable(&pm->work, runtime_suspend_work);
#endif /* CONFIG_PM_DEVICE_RUNTIME_ASYNC */
}
if (pm->base.state == PM_DEVICE_STATE_ACTIVE) {
@ -522,6 +537,7 @@ int pm_device_runtime_disable(const struct device *dev) @@ -522,6 +537,7 @@ int pm_device_runtime_disable(const struct device *dev)
(void)k_sem_take(&pm->lock, K_FOREVER);
}
#ifdef CONFIG_PM_DEVICE_RUNTIME_ASYNC
if (!k_is_pre_kernel()) {
if ((pm->base.state == PM_DEVICE_STATE_SUSPENDING) &&
((k_work_cancel_delayable(&pm->work) & K_WORK_RUNNING) == 0)) {
@ -539,6 +555,7 @@ int pm_device_runtime_disable(const struct device *dev) @@ -539,6 +555,7 @@ int pm_device_runtime_disable(const struct device *dev)
(void)k_sem_take(&pm->lock, K_FOREVER);
}
}
#endif /* CONFIG_PM_DEVICE_RUNTIME_ASYNC */
/* wake up the device if suspended */
if (pm->base.state == PM_DEVICE_STATE_SUSPENDED) {
@ -549,8 +566,9 @@ int pm_device_runtime_disable(const struct device *dev) @@ -549,8 +566,9 @@ int pm_device_runtime_disable(const struct device *dev)
pm->base.state = PM_DEVICE_STATE_ACTIVE;
}
#ifdef CONFIG_PM_DEVICE_RUNTIME_ASYNC
clear_bit:
#endif
atomic_clear_bit(&pm->base.flags, PM_DEVICE_FLAG_RUNTIME_ENABLED);
unlock:
@ -580,6 +598,7 @@ int pm_device_runtime_usage(const struct device *dev) @@ -580,6 +598,7 @@ int pm_device_runtime_usage(const struct device *dev)
return dev->pm_base->usage;
}
#ifdef CONFIG_PM_DEVICE_RUNTIME_ASYNC
#ifdef CONFIG_PM_DEVICE_RUNTIME_USE_DEDICATED_WQ
static int pm_device_runtime_wq_init(void)
@ -599,3 +618,4 @@ SYS_INIT(pm_device_runtime_wq_init, POST_KERNEL, @@ -599,3 +618,4 @@ SYS_INIT(pm_device_runtime_wq_init, POST_KERNEL,
CONFIG_PM_DEVICE_RUNTIME_DEDICATED_WQ_INIT_PRIO);
#endif /* CONFIG_PM_DEVICE_RUNTIME_USE_DEDICATED_WQ */
#endif /* CONFIG_PM_DEVICE_RUNTIME_ASYNC */

16
tests/subsys/pm/device_runtime_api/src/main.c

@ -11,7 +11,10 @@ @@ -11,7 +11,10 @@
#include "test_driver.h"
#include "zephyr/sys/util_macro.h"
static const struct device *test_dev;
#ifdef CONFIG_PM_DEVICE_RUNTIME_ASYNC
static struct k_thread get_runner_td;
K_THREAD_STACK_DEFINE(get_runner_stack, 1024);
@ -32,6 +35,7 @@ static void get_runner(void *arg1, void *arg2, void *arg3) @@ -32,6 +35,7 @@ static void get_runner(void *arg1, void *arg2, void *arg3)
ret = pm_device_runtime_get(test_dev);
zassert_equal(ret, 0);
}
#endif /* CONFIG_PM_DEVICE_RUNTIME_ASYNC */
void test_api_setup(void *data)
{
@ -44,7 +48,11 @@ void test_api_setup(void *data) @@ -44,7 +48,11 @@ void test_api_setup(void *data)
ret = pm_device_runtime_put(test_dev);
zassert_equal(ret, 0);
ret = pm_device_runtime_put_async(test_dev, K_NO_WAIT);
#ifdef CONFIG_PM_DEVICE_RUNTIME_ASYNC
zassert_equal(ret, 0);
#else
zassert_equal(ret, -ENOSYS);
#endif
/* enable runtime PM */
ret = pm_device_runtime_enable(test_dev);
@ -131,6 +139,7 @@ ZTEST(device_runtime_api, test_api) @@ -131,6 +139,7 @@ ZTEST(device_runtime_api, test_api)
zassert_equal(ret, -EALREADY);
zassert_equal(pm_device_runtime_usage(test_dev), 0);
#ifdef CONFIG_PM_DEVICE_RUNTIME_ASYNC
/*** get + asynchronous put until suspended ***/
/* usage: 0, +1, resume: yes */
@ -262,6 +271,7 @@ ZTEST(device_runtime_api, test_api) @@ -262,6 +271,7 @@ ZTEST(device_runtime_api, test_api)
ret = pm_device_runtime_disable(test_dev);
zassert_equal(ret, 0);
zassert_equal(pm_device_runtime_usage(test_dev), -ENOTSUP);
#endif /* CONFIG_PM_DEVICE_RUNTIME_ASYNC */
}
DEVICE_DEFINE(pm_unsupported_device, "PM Unsupported", NULL, NULL, NULL, NULL,
@ -276,7 +286,11 @@ ZTEST(device_runtime_api, test_unsupported) @@ -276,7 +286,11 @@ ZTEST(device_runtime_api, test_unsupported)
zassert_equal(pm_device_runtime_disable(dev), -ENOTSUP, "");
zassert_equal(pm_device_runtime_get(dev), 0, "");
zassert_equal(pm_device_runtime_put(dev), 0, "");
zassert_false(pm_device_runtime_put_async(dev, K_NO_WAIT), "");
#ifdef CONFIG_PM_DEVICE_RUNTIME_ASYNC
zassert_equal(pm_device_runtime_put_async(dev, K_NO_WAIT), 0, "");
#else
zassert_equal(pm_device_runtime_put_async(dev, K_NO_WAIT), -ENOSYS, "");
#endif
}
int dev_pm_control(const struct device *dev, enum pm_device_action action)

5
tests/subsys/pm/device_runtime_api/testcase.yaml

@ -15,3 +15,8 @@ tests: @@ -15,3 +15,8 @@ tests:
- native_sim
extra_configs:
- CONFIG_PM_DEVICE_RUNTIME_USE_DEDICATED_WQ=y
pm.device_runtime.async_disabled.api:
platform_allow:
- native_sim
extra_configs:
- CONFIG_PM_DEVICE_RUNTIME_ASYNC=n

Loading…
Cancel
Save