diff --git a/include/zephyr/pm/policy.h b/include/zephyr/pm/policy.h index e1e910f3e0a538..9b71dd36784c65 100644 --- a/include/zephyr/pm/policy.h +++ b/include/zephyr/pm/policy.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #ifdef __cplusplus @@ -27,37 +28,29 @@ extern "C" { */ /** - * @brief Callback to notify when maximum latency changes. - * - * @param latency New maximum latency. Positive value represents latency in - * microseconds. SYS_FOREVER_US value lifts the latency constraint. Other values - * are forbidden. - */ -typedef void (*pm_policy_latency_changed_cb_t)(int32_t latency); - -/** - * @brief Latency change subscription. + * @brief Latency request. * * @note All fields in this structure are meant for private usage. */ -struct pm_policy_latency_subscription { +struct pm_policy_latency_request { /** @cond INTERNAL_HIDDEN */ sys_snode_t node; - pm_policy_latency_changed_cb_t cb; + uint32_t value_us; + struct onoff_client cli; + void *internal; + uint32_t flags; /** @endcond */ }; /** - * @brief Latency request. + * @brief Callback to notify when maximum latency changes. * - * @note All fields in this structure are meant for private usage. + * @param latency New maximum latency. Positive value represents latency in + * microseconds. SYS_FOREVER_US value lifts the latency constraint. Other values + * are forbidden. */ -struct pm_policy_latency_request { - /** @cond INTERNAL_HIDDEN */ - sys_snode_t node; - uint32_t value_us; - /** @endcond */ -}; +typedef void (*pm_policy_latency_changed_cb_t)(struct pm_policy_latency_request *req, + int32_t latency); /** * @brief Event. @@ -275,27 +268,96 @@ static inline int32_t pm_policy_next_event_ticks(void) * The system will not enter any power state that would make the system to * exceed the given latency value. * + * If immediate control manager is provided request may trigger an action asynchronous action + * to reconfigure the system runtime to fullfil requested latency. @p req contains an + * asynchronous notification field that must be initialize prior to the call to get the + * notification. + * * @param req Latency request. * @param value_us Maximum allowed latency in microseconds. + * + * @retval 0 if request is applied. + * @retval 1 if request required immediate action that is not completed. Configured completion + * notification will inform about completion. + * @retval -EIO immediate action returned error. + */ +int pm_policy_latency_request_add(struct pm_policy_latency_request *req, uint32_t value_us); + +/** + * @brief Synchronously add a new latency requirement. + * + * If immediate control manager is not used then it is an equivalent of + * @ref pm_policy_latency_request_add. If immediate control manager is used then context + * blocks until request is completed. It can only be called from the thread context. + * Asynchronous notification field in the @p req is configured and used internally. + * + * @retval 0 if request required immediate action that is not completed. Configured completion + * notification will inform about completion. + * @retval -EIO immediate action returned error. */ -void pm_policy_latency_request_add(struct pm_policy_latency_request *req, - uint32_t value_us); +int pm_policy_latency_request_add_sync(struct pm_policy_latency_request *req, uint32_t value_us); /** * @brief Update a latency requirement. * * @param req Latency request. * @param value_us New maximum allowed latency in microseconds. + * + * @retval 0 if request is applied. + * @retval 1 if request required immediate action that is not completed. Configured completion + * notification will inform about completion. + * @retval -EIO immediate action returned error. + */ +int pm_policy_latency_request_update(struct pm_policy_latency_request *req, uint32_t value_us); + +/** + * @brief Synchronously update a latency requirement. + * + * If immediate control manager is not used then it is an equivalent of + * @ref pm_policy_latency_request_update. If immediate control manager is used then context + * blocks until request is completed. It can only be called from the thread context. + * Asynchronous notification field in the @p req is configured and used internally. + * + * @retval 0 if request required immediate action that is not completed. Configured completion + * notification will inform about completion. + * @retval -EIO immediate action returned error. */ -void pm_policy_latency_request_update(struct pm_policy_latency_request *req, - uint32_t value_us); +int pm_policy_latency_request_update_sync(struct pm_policy_latency_request *req, uint32_t value_us); /** * @brief Remove a latency requirement. * * @param req Latency request. + * + * @retval 0 if request removed successfully. + * @retval -EALREADY if request is not active and cannot be removed. + * @retval -EIO immediate action returned error. + */ +int pm_policy_latency_request_remove(struct pm_policy_latency_request *req); + +/** @brief Immediate action manager for single threshold. + * + * If there is only a single threshold that triggers an action then @ref onoff_manager + * can handle that. Structure must be persistent. */ -void pm_policy_latency_request_remove(struct pm_policy_latency_request *req); +struct pm_policy_latency_immediate_binary { + /** Onoff manager. */ + struct onoff_manager *mgr; + /** Latency threshold. Value below threshold trigger 'on' action. */ + int32_t thr; +}; + +struct pm_policy_latency_immediate_ctrl { + /** Indication that onoff manager is used. In future gradual manager might + * also be supported. + */ + bool onoff; + /** Pointer to the manager which controls immediate action. */ + union { + struct pm_policy_latency_immediate_binary *bin_mgr; + void *mgr; + }; +}; /** * @brief Subscribe to maximum latency changes. @@ -303,35 +365,43 @@ void pm_policy_latency_request_remove(struct pm_policy_latency_request *req); * @param req Subscription request. * @param cb Callback function (NULL to disable). */ -void pm_policy_latency_changed_subscribe(struct pm_policy_latency_subscription *req, - pm_policy_latency_changed_cb_t cb); +int pm_policy_latency_immediate_ctrl_add(struct pm_policy_latency_immediate_ctrl *mgr); /** * @brief Unsubscribe to maximum latency changes. * * @param req Subscription request. */ -void pm_policy_latency_changed_unsubscribe(struct pm_policy_latency_subscription *req); #else -static inline void pm_policy_latency_request_add( +static inline int pm_policy_latency_request_add( struct pm_policy_latency_request *req, uint32_t value_us) { ARG_UNUSED(req); ARG_UNUSED(value_us); + return 0; } -static inline void pm_policy_latency_request_update( +static inline int pm_policy_latency_request_update( struct pm_policy_latency_request *req, uint32_t value_us) { ARG_UNUSED(req); ARG_UNUSED(value_us); + return 0; } -static inline void pm_policy_latency_request_remove( +static inline int pm_policy_latency_request_remove( struct pm_policy_latency_request *req) { ARG_UNUSED(req); + return 0; } + +static inline int pm_policy_latency_immediate_ctrl_add(struct pm_policy_latency_immediate_ctrl *mgr) +{ + ARG_UNUSED(mgr); + return 0; +} + #endif /* CONFIG_PM CONFIG_PM_POLICY_LATENCY_STANDALONE */ /** diff --git a/samples/subsys/pm/latency/sample.yaml b/samples/subsys/pm/latency/sample.yaml index 6cff279d1cc091..7dc5b4ff58eb29 100644 --- a/samples/subsys/pm/latency/sample.yaml +++ b/samples/subsys/pm/latency/sample.yaml @@ -25,8 +25,6 @@ tests: - " app: Sleeping for 1.3 seconds, we should enter SUSPEND_TO_IDLE" - " soc_pm: Entering SUSPEND_TO_IDLE{0}" - " app: Opening test device" - - " dev_test: Adding latency constraint: 20ms" - - " app: Latency constraint changed: 20ms" - " app: Sleeping for 1.1 seconds, we should enter RUNTIME_IDLE" - " soc_pm: Entering RUNTIME_IDLE{0}" - " app: Sleeping for 1.2 seconds, we should enter RUNTIME_IDLE" @@ -34,18 +32,12 @@ tests: - " app: Sleeping for 1.3 seconds, we should enter RUNTIME_IDLE" - " soc_pm: Entering RUNTIME_IDLE{0}" - " app: Updating latency constraint: 10ms" - - " app: Latency constraint changed: 10ms" - - " dev_test: Latency constraint changed: 10ms" - " app: Sleeping for 1.1 seconds, we should stay ACTIVE" - " app: Sleeping for 1.2 seconds, we should stay ACTIVE" - " app: Sleeping for 1.3 seconds, we should stay ACTIVE" - " app: Updating latency constraint: 30ms" - - " app: Latency constraint changed: 20ms" - - " dev_test: Latency constraint changed: 20ms" - " app: Closing test device" - " dev_test: Removing latency constraint" - - " app: Latency constraint changed: 30ms" - - " dev_test: Latency constraint changed: 30ms" - " app: Sleeping for 1.1 seconds, we should enter RUNTIME_IDLE" - " soc_pm: Entering RUNTIME_IDLE{0}" - " app: Sleeping for 1.2 seconds, we should enter SUSPEND_TO_IDLE" @@ -53,6 +45,5 @@ tests: - " app: Sleeping for 1.3 seconds, we should enter SUSPEND_TO_IDLE" - " soc_pm: Entering SUSPEND_TO_IDLE{0}" - " app: Removing latency constraint" - - " app: Latency constraint changed: none" - " app: Finished, we should now enter STANDBY" - " soc_pm: Entering STANDBY{0}" diff --git a/samples/subsys/pm/latency/src/main.c b/samples/subsys/pm/latency/src/main.c index e778432b68cb6e..2c278e7444afda 100644 --- a/samples/subsys/pm/latency/src/main.c +++ b/samples/subsys/pm/latency/src/main.c @@ -13,19 +13,8 @@ LOG_MODULE_REGISTER(app, CONFIG_APP_LOG_LEVEL); -static void on_latency_changed(int32_t latency) -{ - if (latency == SYS_FOREVER_US) { - LOG_INF("Latency constraint changed: none"); - } else { - LOG_INF("Latency constraint changed: %" PRId32 "ms", - latency / USEC_PER_MSEC); - } -} - int main(void) { - struct pm_policy_latency_subscription subs; struct pm_policy_latency_request req; const struct device *dev; @@ -35,8 +24,6 @@ int main(void) return 0; } - pm_policy_latency_changed_subscribe(&subs, on_latency_changed); - /* test without any latency constraint */ LOG_INF("Sleeping for 1.1 seconds, we should enter RUNTIME_IDLE"); k_msleep(1100); @@ -101,8 +88,6 @@ int main(void) LOG_INF("Removing latency constraints"); pm_policy_latency_request_remove(&req); - pm_policy_latency_changed_unsubscribe(&subs); - LOG_INF("Finished, we should now enter STANDBY"); return 0; } diff --git a/samples/subsys/pm/latency/src/test.c b/samples/subsys/pm/latency/src/test.c index bf94c9c1bf87e9..bda9802c32d5e7 100644 --- a/samples/subsys/pm/latency/src/test.c +++ b/samples/subsys/pm/latency/src/test.c @@ -13,28 +13,15 @@ LOG_MODULE_REGISTER(dev_test, CONFIG_APP_LOG_LEVEL); struct dev_test_data { - struct pm_policy_latency_subscription subs; struct pm_policy_latency_request req; }; -static void on_latency_changed(int32_t latency) -{ - if (latency == SYS_FOREVER_US) { - LOG_INF("Latency constraint changed: none"); - } else { - LOG_INF("Latency constraint changed: %" PRId32 "ms", - latency / USEC_PER_MSEC); - } -} - static void dev_test_open(const struct device *dev) { struct dev_test_data *data = dev->data; LOG_INF("Adding latency constraint: 20ms"); pm_policy_latency_request_add(&data->req, 20000); - - pm_policy_latency_changed_subscribe(&data->subs, on_latency_changed); } static void dev_test_close(const struct device *dev) @@ -43,8 +30,6 @@ static void dev_test_close(const struct device *dev) LOG_INF("Removing latency constraint"); pm_policy_latency_request_remove(&data->req); - - pm_policy_latency_changed_unsubscribe(&data->subs); } static const struct test_api dev_test_api = { diff --git a/subsys/pm/Kconfig b/subsys/pm/Kconfig index 8acee7c6848437..a18f1dccaa00ae 100644 --- a/subsys/pm/Kconfig +++ b/subsys/pm/Kconfig @@ -53,6 +53,11 @@ config PM_NEED_ALL_DEVICES_IDLE When this option is enabled, check that no devices are busy before entering into system low power mode. +config PM_POLICY_LATENCY_IMMEDIATE_BIN_ACTION + bool "Support latency change action" + select ONOFF + imply POLL + endif # PM config PM_DEVICE diff --git a/subsys/pm/policy/policy_latency.c b/subsys/pm/policy/policy_latency.c index 68e6ffcc5d5776..996c69ee3afadf 100644 --- a/subsys/pm/policy/policy_latency.c +++ b/subsys/pm/policy/policy_latency.c @@ -16,33 +16,40 @@ static struct k_spinlock latency_lock; /** List of maximum latency requests. */ static sys_slist_t latency_reqs; /** Maximum CPU latency in us */ -static int32_t max_latency_us = SYS_FOREVER_US; +static uint32_t max_latency_us = (uint32_t)SYS_FOREVER_US; /** Maximum CPU latency in cycles */ int32_t max_latency_cyc = -1; -/** List of latency change subscribers. */ -static sys_slist_t latency_subs; +/** Optional manager for applying immediate changes based on latency requests. */ +struct pm_policy_latency_immediate_ctrl immediate_mgr; /** @brief Update maximum allowed latency. */ -static void update_max_latency(void) +static void update_max_latency(uint32_t prev, uint32_t value) { - int32_t new_max_latency_us = SYS_FOREVER_US; + uint32_t new_max_latency_us = SYS_FOREVER_US; struct pm_policy_latency_request *req; + if (value < max_latency_us) { + /* If new value is smaller than previous max it becomes the new max latency. */ + max_latency_us = value; + max_latency_cyc = max_latency_us != SYS_FOREVER_US ? + (int32_t)k_us_to_cyc_ceil32(max_latency_us) : -1; + return; + } else if (prev > max_latency_us) { + /* If previous and new value is bigger than max there is no max latency change. */ + return; + } + + /* Need to iterate over all existing requests to get the new max latency. */ SYS_SLIST_FOR_EACH_CONTAINER(&latency_reqs, req, node) { if ((new_max_latency_us == SYS_FOREVER_US) || - ((int32_t)req->value_us < new_max_latency_us)) { - new_max_latency_us = (int32_t)req->value_us; + (req->value_us < new_max_latency_us)) { + new_max_latency_us = req->value_us; } } if (max_latency_us != new_max_latency_us) { - struct pm_policy_latency_subscription *sreq; int32_t new_max_latency_cyc = -1; - SYS_SLIST_FOR_EACH_CONTAINER(&latency_subs, sreq, node) { - sreq->cb(new_max_latency_us); - } - if (new_max_latency_us != SYS_FOREVER_US) { new_max_latency_cyc = (int32_t)k_us_to_cyc_ceil32(new_max_latency_us); } @@ -52,56 +59,164 @@ static void update_max_latency(void) } } -void pm_policy_latency_request_add(struct pm_policy_latency_request *req, - uint32_t value_us) +static void onoff_cb(struct onoff_manager *mgr, struct onoff_client *cli, uint32_t state, int res) { - req->value_us = value_us; + struct pm_policy_latency_request *req = + CONTAINER_OF(cli, struct pm_policy_latency_request, cli); + pm_policy_latency_changed_cb_t cb = req->internal; - k_spinlock_key_t key = k_spin_lock(&latency_lock); + cb(req, res); +} - sys_slist_append(&latency_reqs, &req->node); - update_max_latency(); +static int onoff_req(struct pm_policy_latency_request *req, uint32_t prev) +{ + uint32_t thr = immediate_mgr.bin_mgr->thr; + uint32_t notify_method = sys_notify_get_method(&req->cli.notify); + + if (req->value_us <= thr && prev > thr) { + /* Replace user callback with internal callback because onoff manager + * callback signature does not match latency changed callback signature. + */ + if (notify_method == SYS_NOTIFY_METHOD_CALLBACK) { + req->internal = req->cli.notify.method.callback; + req->cli.notify.method.callback = onoff_cb; + } - k_spin_unlock(&latency_lock, key); + return onoff_request(immediate_mgr.bin_mgr->mgr, &req->cli) == ONOFF_STATE_ON ? + 0 : 1; + } else if (req->value_us > thr && prev <= thr) { + return onoff_cancel_or_release(immediate_mgr.bin_mgr->mgr, &req->cli) == + ONOFF_STATE_ON ? 0 : 1; + } + + if (notify_method != SYS_NOTIFY_METHOD_COMPLETED) { + pm_policy_latency_changed_cb_t cb = sys_notify_finalize(&req->cli.notify, 0); + + if (cb) { + cb(req, 0); + } + } + + return 0; } -void pm_policy_latency_request_update(struct pm_policy_latency_request *req, - uint32_t value_us) +static int finalize(struct pm_policy_latency_request *req, uint32_t prev) { - k_spinlock_key_t key = k_spin_lock(&latency_lock); + if (IS_ENABLED(CONFIG_PM_POLICY_LATENCY_IMMEDIATE_BIN_ACTION) && immediate_mgr.mgr) { + if (immediate_mgr.onoff) { + return onoff_req(req, prev); + } else { + return -ENOTSUP; + } + } + + return 0; +} + +int pm_policy_latency_request_add(struct pm_policy_latency_request *req, uint32_t value_us) +{ + sys_snode_t *node; + k_spinlock_key_t key; + key = k_spin_lock(&latency_lock); + if (sys_slist_find(&latency_reqs, &req->node, &node)) { + k_spin_unlock(&latency_lock, key); + return -EALREADY; + } req->value_us = value_us; - update_max_latency(); + sys_slist_append(&latency_reqs, &req->node); + update_max_latency((uint32_t)SYS_FOREVER_US, value_us); k_spin_unlock(&latency_lock, key); + + return finalize(req, (uint32_t)SYS_FOREVER_US); } -void pm_policy_latency_request_remove(struct pm_policy_latency_request *req) +typedef int (*latency_func_t)(struct pm_policy_latency_request *req, uint32_t value_us); + +static int sync_req(latency_func_t func, struct pm_policy_latency_request *req, uint32_t value_us) { - k_spinlock_key_t key = k_spin_lock(&latency_lock); + if (!IS_ENABLED(CONFIG_PM_POLICY_LATENCY_IMMEDIATE_BIN_ACTION) || + immediate_mgr.mgr == NULL) { + return func(req, value_us); + } - (void)sys_slist_find_and_remove(&latency_reqs, &req->node); - update_max_latency(); + struct k_poll_signal sig; + struct k_poll_event evt = K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL, + K_POLL_MODE_NOTIFY_ONLY, + &sig); + int rv; - k_spin_unlock(&latency_lock, key); + k_poll_signal_init(&sig); + sys_notify_init_signal(&req->cli.notify, &sig); + + rv = func(req, value_us); + if (rv >= 0) { + rv = k_poll(&evt, 1, K_FOREVER); + } + + return rv; } -void pm_policy_latency_changed_subscribe(struct pm_policy_latency_subscription *req, - pm_policy_latency_changed_cb_t cb) +int pm_policy_latency_request_add_sync(struct pm_policy_latency_request *req, uint32_t value_us) { + return sync_req(pm_policy_latency_request_add, req, value_us); +} + +int pm_policy_latency_request_update(struct pm_policy_latency_request *req, uint32_t value_us) +{ + sys_snode_t *node; + uint32_t prev = req->value_us; + k_spinlock_key_t key = k_spin_lock(&latency_lock); - req->cb = cb; - sys_slist_append(&latency_subs, &req->node); + if (!sys_slist_find(&latency_reqs, &req->node, &node)) { + k_spin_unlock(&latency_lock, key); + return -EAGAIN; + } + + req->value_us = value_us; + update_max_latency(prev, value_us); k_spin_unlock(&latency_lock, key); + + return finalize(req, prev); +} + +int pm_policy_latency_request_update_sync(struct pm_policy_latency_request *req, uint32_t value_us) +{ + return sync_req(pm_policy_latency_request_update, req, value_us); } -void pm_policy_latency_changed_unsubscribe(struct pm_policy_latency_subscription *req) +int pm_policy_latency_request_remove(struct pm_policy_latency_request *req) { + sys_snode_t *node; + uint32_t prev = req->value_us; + k_spinlock_key_t key = k_spin_lock(&latency_lock); - (void)sys_slist_find_and_remove(&latency_subs, &req->node); + if (!sys_slist_find(&latency_reqs, &req->node, &node)) { + k_spin_unlock(&latency_lock, key); + return -EALREADY; + } + req->value_us = (uint32_t)SYS_FOREVER_US; + (void)sys_slist_find_and_remove(&latency_reqs, &req->node); + update_max_latency(prev, req->value_us); k_spin_unlock(&latency_lock, key); + + return finalize(req, prev); +} + +int pm_policy_latency_immediate_ctrl_add(struct pm_policy_latency_immediate_ctrl *mgr) +{ + if (mgr && !mgr->onoff) { + return -ENOTSUP; + } else if (mgr == NULL) { + immediate_mgr.mgr = NULL; + } else { + immediate_mgr = *mgr; + } + + return 0; } diff --git a/tests/subsys/pm/policy_api/src/main.c b/tests/subsys/pm/policy_api/src/main.c index bc3564bee8a288..45fb44c35e5ba2 100644 --- a/tests/subsys/pm/policy_api/src/main.c +++ b/tests/subsys/pm/policy_api/src/main.c @@ -143,23 +143,9 @@ ZTEST(policy_api, test_pm_policy_next_state_default_allowed) zassert_equal(next->state, PM_STATE_RUNTIME_IDLE); } -/** Flag to indicate number of times callback has been called */ -static uint8_t latency_cb_call_cnt; /** Flag to indicate expected latency */ static int32_t expected_latency; -/** - * Callback to notify when state allowed status changes. - */ -static void on_pm_policy_latency_changed(int32_t latency) -{ - TC_PRINT("Latency changed to %d\n", latency); - - zassert_equal(latency, expected_latency); - - latency_cb_call_cnt++; -} - /** * @brief Test the behavior of pm_policy_next_state() when * latency requirements are imposed and CONFIG_PM_POLICY_DEFAULT=y. @@ -167,13 +153,24 @@ static void on_pm_policy_latency_changed(int32_t latency) ZTEST(policy_api, test_pm_policy_next_state_default_latency) { struct pm_policy_latency_request req1, req2; - struct pm_policy_latency_subscription sreq1, sreq2; const struct pm_state_info *next; + int rv; + + rv = pm_policy_latency_immediate_ctrl_add(NULL); + zassert_equal(rv, 0); + + memset(&req1, 0, sizeof(req1)); + memset(&req2, 0, sizeof(req2)); /* add a latency requirement with a maximum value below the * latency given by any state, so we should stay active all the time */ - pm_policy_latency_request_add(&req1, 9000); + rv = pm_policy_latency_request_add(&req1, 9000); + zassert_equal(rv, 0); + + /* Cannot add already added request. */ + rv = pm_policy_latency_request_add(&req1, 1000); + zassert_equal(rv, -EALREADY); next = pm_policy_next_state(0U, k_us_to_ticks_floor32(110000)); zassert_is_null(next); @@ -185,7 +182,8 @@ ZTEST(policy_api, test_pm_policy_next_state_default_latency) * PM_STATE_RUNTIME_IDLE and PM_STATE_SUSPEND_TO_RAM, so we should * never enter PM_STATE_SUSPEND_TO_RAM. */ - pm_policy_latency_request_update(&req1, 50000); + rv = pm_policy_latency_request_update(&req1, 50000); + zassert_equal(rv, 0); next = pm_policy_next_state(0U, k_us_to_ticks_floor32(110000)); zassert_equal(next->state, PM_STATE_RUNTIME_IDLE); @@ -197,7 +195,8 @@ ZTEST(policy_api, test_pm_policy_next_state_default_latency) * latency given by any state, so we should stay active all the time * since it overrides the previous one. */ - pm_policy_latency_request_add(&req2, 8000); + rv = pm_policy_latency_request_add(&req2, 8000); + zassert_equal(rv, 0); next = pm_policy_next_state(0U, k_us_to_ticks_floor32(110000)); zassert_is_null(next); @@ -208,7 +207,8 @@ ZTEST(policy_api, test_pm_policy_next_state_default_latency) /* remove previous request, so we should recover behavior given by * first request. */ - pm_policy_latency_request_remove(&req2); + rv = pm_policy_latency_request_remove(&req2); + zassert_equal(rv, 0); next = pm_policy_next_state(0U, k_us_to_ticks_floor32(110000)); zassert_equal(next->state, PM_STATE_RUNTIME_IDLE); @@ -225,39 +225,175 @@ ZTEST(policy_api, test_pm_policy_next_state_default_latency) next = pm_policy_next_state(0U, k_us_to_ticks_floor32(1100000)); zassert_equal(next->state, PM_STATE_SUSPEND_TO_RAM); - /* get notified when latency requirement changes */ - pm_policy_latency_changed_subscribe(&sreq1, on_pm_policy_latency_changed); - pm_policy_latency_changed_subscribe(&sreq2, on_pm_policy_latency_changed); - /* add new request (expected notification) */ - latency_cb_call_cnt = 0; expected_latency = 10000; pm_policy_latency_request_add(&req1, 10000); - zassert_equal(latency_cb_call_cnt, 2); - /* update request (expected notification, but only sreq1) */ - pm_policy_latency_changed_unsubscribe(&sreq2); - latency_cb_call_cnt = 0; expected_latency = 50000; pm_policy_latency_request_update(&req1, 50000); - zassert_equal(latency_cb_call_cnt, 1); /* add a new request, with higher value (no notification, previous * prevails) */ - latency_cb_call_cnt = 0; pm_policy_latency_request_add(&req2, 60000); - zassert_equal(latency_cb_call_cnt, 0); pm_policy_latency_request_remove(&req2); - zassert_equal(latency_cb_call_cnt, 0); /* remove first request, we no longer have latency requirements */ expected_latency = SYS_FOREVER_US; pm_policy_latency_request_remove(&req1); - zassert_equal(latency_cb_call_cnt, 1); } + +struct test_data { + struct k_timer timer; + struct onoff_manager mgr; + struct onoff_monitor monitor; + int res; + onoff_notify_fn notify; + k_timeout_t timeout; + bool on; +}; + +struct test_req { + struct pm_policy_latency_request req; + volatile int result; +}; + +static void onoff_monitor_cb(struct onoff_manager *mgr, + struct onoff_monitor *mon, + uint32_t state, + int res) +{ + struct test_data *data = CONTAINER_OF(mgr, struct test_data, mgr); + + data->on = (state == ONOFF_STATE_ON); +} + +static void onoff_timeout(struct k_timer *timer) +{ + struct test_data *data = CONTAINER_OF(timer, struct test_data, timer); + + data->notify(&data->mgr, data->res); +} + +K_TIMER_DEFINE(onoff_timer, onoff_timeout, NULL); + +static void onoff_start(struct onoff_manager *mgr, onoff_notify_fn notify) +{ + struct test_data *data = CONTAINER_OF(mgr, struct test_data, mgr); + + data->notify = notify; + k_timer_start(&data->timer, data->timeout, K_NO_WAIT); +} + +static void onoff_stop(struct onoff_manager *mgr, onoff_notify_fn notify) +{ + struct test_data *data = CONTAINER_OF(mgr, struct test_data, mgr); + + data->notify = notify; + k_timer_start(&data->timer, data->timeout, K_NO_WAIT); +} + +static void immediate_ctrl_binary_init(struct test_data *data, uint32_t thr, uint32_t t_op) +{ + static const struct onoff_transitions transitions = { + .start = onoff_start, + .stop = onoff_stop + }; + static struct pm_policy_latency_immediate_binary bin_mgr; + struct pm_policy_latency_immediate_ctrl ctrl; + int rv; + + bin_mgr.mgr = &data->mgr; + bin_mgr.thr = thr; + ctrl.onoff = true; + ctrl.bin_mgr = &bin_mgr; + data->monitor.callback = onoff_monitor_cb; + + data->timeout = K_MSEC(t_op); + k_timer_init(&data->timer, onoff_timeout, NULL); + rv = onoff_manager_init(&data->mgr, &transitions); + zassert_equal(rv, 0); + + rv = onoff_monitor_register(&data->mgr, &data->monitor); + zassert_equal(rv, 0); + + rv = pm_policy_latency_immediate_ctrl_add(&ctrl); + zassert_equal(rv, 0); +} + +static void latency_changed(struct pm_policy_latency_request *req, int32_t result) +{ + struct test_req *test_req = CONTAINER_OF(req, struct test_req, req); + + test_req->result = result; +} + +ZTEST(policy_api, test_pm_policy_latency_immediate_action_bin) +{ + struct test_data data; + uint32_t thr = 1000; + uint32_t t_op = 1; + struct test_req req1, req2; + int rv; + int result = -EAGAIN; + + immediate_ctrl_binary_init(&data, thr, t_op); + + sys_notify_init_spinwait(&req1.req.cli.notify); + + /* Latency above threhold so requirement is already met. */ + rv = pm_policy_latency_request_add(&req1.req, thr + 1); + zassert_equal(rv, 0, "Unexpected rv: %d", rv); + zassert_equal(sys_notify_fetch_result(&req1.req.cli.notify, &result), 0); + zassert_equal(result, 0, "Unexpected result: %d", result); + + req2.result = -EIO; + sys_notify_init_callback(&req2.req.cli.notify, latency_changed); + rv = pm_policy_latency_request_add(&req2.req, thr + 2); + zassert_equal(rv, 0, "Unexpected rv: %d", rv); + zassert_equal(req2.result, 0, "Unexpected rv: %d", req2.result); + + req2.result = -EIO; + sys_notify_init_callback(&req2.req.cli.notify, latency_changed); + /* Below the threshold, it triggers asynchronous action. */ + rv = pm_policy_latency_request_update(&req2.req, thr - 1); + zassert_equal(rv, 1, "Unexpected rv: %d", rv); + /* Not finished yet. */ + zassert_equal(req2.result, -EIO, "Unexpected rv: %d", req2.result); + k_msleep(t_op); + zassert_equal(req2.result, 0, "Unexpected rv: %d", req2.result); + + rv = pm_policy_latency_request_remove(&req1.req); + zassert_equal(rv, 0, "Unexpected rv: %d", rv); + + rv = pm_policy_latency_request_remove(&req2.req); + zassert_equal(rv, 0, "Unexpected rv: %d", rv); + + zassert_false(data.on); + rv = pm_policy_latency_request_add_sync(&req1.req, thr - 1); + zassert_equal(rv, 0, "Unexpected rv: %d", rv); + /* Synchronous request blocks until immediate action is completed. */ + zassert_true(data.on); + + rv = pm_policy_latency_request_remove(&req1.req); + zassert_equal(rv, 0, "Unexpected rv: %d", rv); + + /* Add request below the threshold. */ + rv = pm_policy_latency_request_add(&req1.req, thr + 1); + zassert_equal(rv, 0, "Unexpected rv: %d", rv); + zassert_false(data.on); + + /* Synchronous request for update that trigger the immediate action. */ + rv = pm_policy_latency_request_update_sync(&req1.req, thr - 1); + zassert_equal(rv, 0, "Unexpected rv: %d", rv); + zassert_true(data.on); + + rv = pm_policy_latency_request_remove(&req1.req); + zassert_equal(rv, 0, "Unexpected rv: %d", rv); +} + #else ZTEST(policy_api, test_pm_policy_next_state_default) {