From 378a19d2a83727b2a2afc1badf273728a817786f Mon Sep 17 00:00:00 2001 From: Flavio Ceolin Date: Mon, 17 May 2021 22:41:46 -0700 Subject: [PATCH] pm: device_runtime: Add helper to wait on async ops Add a function that properly uses a mutex to check a condition before wait on the conditional variable. Signed-off-by: Flavio Ceolin --- include/pm/device.h | 2 ++ include/pm/device_runtime.h | 19 +++++++++++++++++++ kernel/device.c | 1 + subsys/pm/device_runtime.c | 19 +++++++++++++++++++ 4 files changed, 41 insertions(+) diff --git a/include/pm/device.h b/include/pm/device.h index eae4703b30a..e23c0c9941e 100644 --- a/include/pm/device.h +++ b/include/pm/device.h @@ -119,6 +119,8 @@ struct pm_device { struct k_work_delayable work; /** Event conditional var to listen to the sync request events */ struct k_condvar condvar; + /** Condvar mutex */ + struct k_mutex condvar_lock; }; /** Bit position in device_pm::atomic_flags that records whether the diff --git a/include/pm/device_runtime.h b/include/pm/device_runtime.h index 6d281efeb81..31ec1ab6789 100644 --- a/include/pm/device_runtime.h +++ b/include/pm/device_runtime.h @@ -109,6 +109,23 @@ int pm_device_put(const struct device *dev); * @retval Errno Negative errno code if failure. */ int pm_device_put_sync(const struct device *dev); + +/** + * @brief Wait on a device to finish an operation. + * + * The calling thread blocks until the device finishes a @ref pm_device_put or + * @ref pm_device_get operation. If there is no operation in progress + * this function will return immediately. + * + * @param dev Pointer to device structure of the specific device driver + * the caller is interested in. + * @param timeout The timeout passed to k_condvar_wait. If a timeout happens + * this function will return immediately. + * @retval 0 If successful. + * @retval Errno Negative errno code if failure. + */ +int pm_device_wait(const struct device *dev, k_timeout_t timeout); + #else static inline void pm_device_enable(const struct device *dev) { } static inline void pm_device_disable(const struct device *dev) { } @@ -116,6 +133,8 @@ static inline int pm_device_get(const struct device *dev) { return -ENOSYS; } static inline int pm_device_get_sync(const struct device *dev) { return -ENOSYS; } static inline int pm_device_put(const struct device *dev) { return -ENOSYS; } static inline int pm_device_put_sync(const struct device *dev) { return -ENOSYS; } +static inline int pm_device_wait(const struct device *dev, + k_timeout_t timeout) { return -ENOSYS; } #endif /** @} */ diff --git a/kernel/device.c b/kernel/device.c index 5f3a962beb1..baa65cca02a 100644 --- a/kernel/device.c +++ b/kernel/device.c @@ -32,6 +32,7 @@ static inline void device_pm_state_init(const struct device *dev) .usage = ATOMIC_INIT(0), .lock = {}, .condvar = Z_CONDVAR_INITIALIZER(dev->pm->condvar), + .condvar_lock = Z_MUTEX_INITIALIZER(dev->pm->condvar_lock), }; #endif /* CONFIG_PM_DEVICE */ } diff --git a/subsys/pm/device_runtime.c b/subsys/pm/device_runtime.c index 4be4c9b9275..bf6ca036bed 100644 --- a/subsys/pm/device_runtime.c +++ b/subsys/pm/device_runtime.c @@ -224,3 +224,22 @@ void pm_device_disable(const struct device *dev) k_spin_unlock(&dev->pm->lock, key); SYS_PORT_TRACING_FUNC_EXIT(pm, device_disable, dev); } + +int pm_device_wait(const struct device *dev, k_timeout_t timeout) +{ + int ret = 0; + + k_mutex_lock(&dev->pm->condvar_lock, K_FOREVER); + while ((k_work_delayable_is_pending(&dev->pm->work)) || + (atomic_get(&dev->pm->state) == PM_DEVICE_STATE_SUSPENDING) || + (atomic_get(&dev->pm->state) == PM_DEVICE_STATE_RESUMING)) { + ret = k_condvar_wait(&dev->pm->condvar, &dev->pm->condvar_lock, + timeout); + if (ret != 0) { + break; + } + } + k_mutex_unlock(&dev->pm->condvar_lock); + + return ret; +}