pm: policy: new API to set maximum latency requirements

Add a new API that allows to configure maximum latency requirements.
When the policy manager computes the next state, it will check if the
state brings too much latency based on requirements. This can be useful,
for example, if a certain driver or the application want a system to
respond fast, since any low power state that brings too much latency
will not be used.

Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
This commit is contained in:
Gerard Marull-Paretas 2022-01-21 11:24:19 +01:00 committed by Carles Cufí
parent e6339eac1a
commit 35fd6eadbf
2 changed files with 121 additions and 0 deletions

View file

@ -11,6 +11,7 @@
#include <stdint.h>
#include <pm/state.h>
#include <sys/slist.h>
#ifdef __cplusplus
extern "C" {
@ -23,6 +24,13 @@ extern "C" {
* @{
*/
/** @brief Latency request. */
struct pm_policy_latency_request {
sys_snode_t node;
/** Request value. */
uint32_t value;
};
/** @cond INTERNAL_HIDDEN */
/**
@ -79,6 +87,34 @@ void pm_policy_state_lock_put(enum pm_state state);
* @retval false if power state lock is not active.
*/
bool pm_policy_state_lock_is_active(enum pm_state state);
/**
* @brief Add a new latency requirement.
*
* The system will not enter any power state that would make the system to
* exceed the given latency value.
*
* @param req Latency request.
* @param value Maximum allowed latency in microseconds.
*/
void pm_policy_latency_request_add(struct pm_policy_latency_request *req,
uint32_t value);
/**
* @brief Update a latency requirement.
*
* @param req Latency request.
* @param value New maximum allowed latency in microseconds.
*/
void pm_policy_latency_request_update(struct pm_policy_latency_request *req,
uint32_t value);
/**
* @brief Remove a latency requirement.
*
* @param req Latency request.
*/
void pm_policy_latency_request_remove(struct pm_policy_latency_request *req);
#else
static inline void pm_policy_state_lock_get(enum pm_state state)
{
@ -96,6 +132,26 @@ static inline bool pm_policy_state_lock_is_active(enum pm_state state)
return false;
}
static inline void pm_policy_latency_request_add(
struct pm_policy_latency_request *req, uint32_t value)
{
ARG_UNUSED(req);
ARG_UNUSED(value);
}
static inline void pm_policy_latency_request_update(
struct pm_policy_latency_request *req, uint32_t value)
{
ARG_UNUSED(req);
ARG_UNUSED(value);
}
static inline void pm_policy_latency_request_remove(
struct pm_policy_latency_request *req)
{
ARG_UNUSED(req);
}
#endif /* CONFIG_PM */
/**

View file

@ -5,8 +5,10 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <kernel.h>
#include <pm/pm.h>
#include <pm/policy.h>
#include <spinlock.h>
#include <sys_clock.h>
#include <sys/__assert.h>
#include <sys/time_units.h>
@ -16,6 +18,29 @@
/** State lock reference counting */
static atomic_t state_lock_cnt[PM_STATE_COUNT];
/** Lock to synchronize access to the latency request list. */
static struct k_spinlock latency_lock;
/** List of maximum latency requests. */
static sys_slist_t latency_reqs;
/** Maximum CPU latency in ticks */
static int32_t max_latency_ticks = K_TICKS_FOREVER;
/** @brief Update maximum allowed latency. */
static void update_max_latency(void)
{
int32_t new_max_latency_ticks = K_TICKS_FOREVER;
struct pm_policy_latency_request *req;
SYS_SLIST_FOR_EACH_CONTAINER(&latency_reqs, req, node) {
if ((new_max_latency_ticks == K_TICKS_FOREVER) ||
((int32_t)req->value < new_max_latency_ticks)) {
new_max_latency_ticks = (int32_t)req->value;
}
}
max_latency_ticks = new_max_latency_ticks;
}
#ifdef CONFIG_PM_POLICY_DEFAULT
const struct pm_state_info *pm_policy_next_state(uint8_t cpu, int32_t ticks)
{
@ -35,6 +60,12 @@ const struct pm_state_info *pm_policy_next_state(uint8_t cpu, int32_t ticks)
min_residency = k_us_to_ticks_ceil32(state->min_residency_us);
exit_latency = k_us_to_ticks_ceil32(state->exit_latency_us);
/* skip state if it brings too much latency */
if ((max_latency_ticks != K_TICKS_FOREVER) &&
(exit_latency >= max_latency_ticks)) {
continue;
}
if ((ticks == K_TICKS_FOREVER) ||
(ticks >= (min_residency + exit_latency))) {
return state;
@ -63,3 +94,37 @@ bool pm_policy_state_lock_is_active(enum pm_state state)
{
return (atomic_get(&state_lock_cnt[state]) != 0);
}
void pm_policy_latency_request_add(struct pm_policy_latency_request *req,
uint32_t value)
{
req->value = k_us_to_ticks_ceil32(value);
k_spinlock_key_t key = k_spin_lock(&latency_lock);
sys_slist_append(&latency_reqs, &req->node);
update_max_latency();
k_spin_unlock(&latency_lock, key);
}
void pm_policy_latency_request_update(struct pm_policy_latency_request *req,
uint32_t value)
{
k_spinlock_key_t key = k_spin_lock(&latency_lock);
req->value = k_us_to_ticks_ceil32(value);
update_max_latency();
k_spin_unlock(&latency_lock, key);
}
void pm_policy_latency_request_remove(struct pm_policy_latency_request *req)
{
k_spinlock_key_t key = k_spin_lock(&latency_lock);
(void)sys_slist_find_and_remove(&latency_reqs, &req->node);
update_max_latency();
k_spin_unlock(&latency_lock, key);
}