Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 27 additions & 10 deletions include/zephyr/pm/pm.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,33 @@ extern "C" {
*/
struct pm_notifier {
sys_snode_t _node;
/**
* Application defined function for doing any target specific operations
* for power state entry.
*/
void (*state_entry)(enum pm_state state);
/**
* Application defined function for doing any target specific operations
* for power state exit.
*/
void (*state_exit)(enum pm_state state);
union {
struct {
/**
* Application defined function for doing any target specific operations
* for power state entry.
*/
void (*state_entry)(enum pm_state state);
/**
* Application defined function for doing any target specific operations
* for power state exit.
*/
void (*state_exit)(enum pm_state state);
};
struct {
/**
* Application defined function for doing any target specific operations
* for power state entry. Reports the substate id additionally.
*/
void (*substate_entry)(enum pm_state state, uint8_t substate_id);
/**
* Application defined function for doing any target specific operations
* for power state exit. Reports the substate id additionally.
*/
void (*substate_exit)(enum pm_state state, uint8_t substate_id);
};
};
bool report_substate; /* 0 is for backwards compatibility that didn't report substates */
};

#if defined(CONFIG_PM) || defined(__DOXYGEN__)
Expand Down
50 changes: 50 additions & 0 deletions include/zephyr/pm/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,48 @@ void pm_policy_state_lock_get(enum pm_state state, uint8_t substate_id);
*/
void pm_policy_state_lock_put(enum pm_state state, uint8_t substate_id);

/**
* @brief Request to lock all power states.
*
* Requests use a reference counter.
*/
void pm_policy_state_all_lock_get(void);

/**
* @brief Release locking of all power states.
*/
void pm_policy_state_all_lock_put(void);

/**
* @brief Apply power state constraints by locking the specified states.
*
* This function locks all power states specified in the union of all constraints
* in the provided constraint list. Each constraint in the set contributes to
* determining which power states should be locked (not allowed), by increasing
* a reference count just as if pm_policy_state_lock_get was called on each constraints'
* states individually.
*
* @param constraints Pointer to the power state constraints set to apply.
*
* @see pm_policy_state_constraints_put()
*/
void pm_policy_state_constraints_get(struct pm_state_constraints *constraints);

/**
* @brief Remove power state constraints by unlocking the specified states.
*
* This function unlocks all power states that were previously locked by a
* corresponding call to pm_policy_state_constraints_get() with the same
* constraint set. The function decreases the lock counter for each affected
* power state specified in the union of all constraints in the list, just as
* if pm_policy_state_put was called on all the constraints' states individually.
*
* @param constraints Pointer to the power state constraints set to remove.
*
* @see pm_policy_state_constraints_get()
*/
void pm_policy_state_constraints_put(struct pm_state_constraints *constraints);

/**
* @brief Check if a power state lock is active (not allowed).
*
Expand Down Expand Up @@ -244,6 +286,14 @@ static inline void pm_policy_state_lock_put(enum pm_state state, uint8_t substat
ARG_UNUSED(substate_id);
}

static inline void pm_policy_state_all_lock_get(void)
{
}

static inline void pm_policy_state_all_lock_put(void)
{
}

static inline bool pm_policy_state_lock_is_active(enum pm_state state, uint8_t substate_id)
{
ARG_UNUSED(state);
Expand Down
102 changes: 101 additions & 1 deletion include/zephyr/pm/state.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include <zephyr/sys/util.h>
#include <zephyr/devicetree.h>
#include <errno.h>

#ifdef __cplusplus
extern "C" {
Expand Down Expand Up @@ -167,7 +168,8 @@
};

/**
* Power state information needed to lock a power state.
* Information needed to be able to reference a power state as a constraint
* on some device or system functions.
*/
struct pm_state_constraint {
/**
Expand All @@ -184,6 +186,16 @@
uint8_t substate_id;
};

/**
* Collection of multiple power state constraints.
*/
struct pm_state_constraints {
/** Array of power state constraints */
struct pm_state_constraint *list;
/** Number of constraints in the list */
size_t count;
};

/** @cond INTERNAL_HIDDEN */

/**
Expand Down Expand Up @@ -358,6 +370,54 @@
Z_PM_STATE_FROM_DT_CPU, (), node_id) \
}

/**
* @brief initialize a device pm constraint with information from devicetree.
*
* @param node_id Node identifier.
*/
#define PM_STATE_CONSTRAINT_INIT(node_id) \
{ \
.state = PM_STATE_DT_INIT(node_id), \
.substate_id = DT_PROP_OR(node_id, substate_id, 0), \
}

#define Z_PM_STATE_CONSTRAINT_REF(node_id, phandle, idx) \
PM_STATE_CONSTRAINT_INIT(DT_PHANDLE_BY_IDX(node_id, phandle, idx))

#define Z_PM_STATE_CONSTRAINTS_LIST_NAME(node_id, phandles) \
_CONCAT_4(node_id, _, phandles, _constraints)

Check notice on line 388 in include/zephyr/pm/state.h

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

include/zephyr/pm/state.h:388 -#define PM_STATE_CONSTRAINT_INIT(node_id) \ - { \ - .state = PM_STATE_DT_INIT(node_id), \ - .substate_id = DT_PROP_OR(node_id, substate_id, 0), \ +#define PM_STATE_CONSTRAINT_INIT(node_id) \ + { \ + .state = PM_STATE_DT_INIT(node_id), \ + .substate_id = DT_PROP_OR(node_id, substate_id, 0), \ } -#define Z_PM_STATE_CONSTRAINT_REF(node_id, phandle, idx) \ +#define Z_PM_STATE_CONSTRAINT_REF(node_id, phandle, idx) \ PM_STATE_CONSTRAINT_INIT(DT_PHANDLE_BY_IDX(node_id, phandle, idx)) -#define Z_PM_STATE_CONSTRAINTS_LIST_NAME(node_id, phandles) \ +#define Z_PM_STATE_CONSTRAINTS_LIST_NAME(node_id, phandles) \

/**
* @brief Define a list of power state constraints from devicetree.
*
* This macro creates an array of pm_state_constraint structures initialized
* with power state information from the specified devicetree property.
*
* @param node_id Devicetree node identifier.
* @param prop Property name containing the list of power state phandles.
*/
#define PM_STATE_CONSTRAINTS_LIST_DEFINE(node_id, prop) \
struct pm_state_constraint Z_PM_STATE_CONSTRAINTS_LIST_NAME(node_id, prop)[] = \
{ \
DT_FOREACH_PROP_ELEM_SEP(node_id, prop, Z_PM_STATE_CONSTRAINT_REF, (,)) \
}

Check notice on line 404 in include/zephyr/pm/state.h

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

include/zephyr/pm/state.h:404 -#define PM_STATE_CONSTRAINTS_LIST_DEFINE(node_id, prop) \ - struct pm_state_constraint Z_PM_STATE_CONSTRAINTS_LIST_NAME(node_id, prop)[] = \ - { \ - DT_FOREACH_PROP_ELEM_SEP(node_id, prop, Z_PM_STATE_CONSTRAINT_REF, (,)) \ - } +#define PM_STATE_CONSTRAINTS_LIST_DEFINE(node_id, prop) \ + struct pm_state_constraint Z_PM_STATE_CONSTRAINTS_LIST_NAME(node_id, prop)[] = { \ + DT_FOREACH_PROP_ELEM_SEP(node_id, prop, Z_PM_STATE_CONSTRAINT_REF, (,)) }
/**
* @brief Get power state constraints structure from devicetree.
*
* This macro creates a structure containing a pointer to the constraints list
* and the count of constraints, suitable for initializing a pm_state_constraints
* structure. Must be used after the PM_STATE_CONSTRAINTS_LIST_DEFINE call for the same
* @param prop to refer to the array of constraints.
*
* @param node_id Devicetree node identifier.
* @param prop Property name containing the list of power state phandles.
*/
#define PM_STATE_CONSTRAINTS_GET(node_id, prop) \
{ \
.list = Z_PM_STATE_CONSTRAINTS_LIST_NAME(node_id, prop), \
.count = DT_PROP_LEN(node_id, prop), \
}

Check notice on line 420 in include/zephyr/pm/state.h

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

include/zephyr/pm/state.h:420 -#define PM_STATE_CONSTRAINTS_GET(node_id, prop) \ - { \ - .list = Z_PM_STATE_CONSTRAINTS_LIST_NAME(node_id, prop), \ - .count = DT_PROP_LEN(node_id, prop), \ +#define PM_STATE_CONSTRAINTS_GET(node_id, prop) \ + { \ + .list = Z_PM_STATE_CONSTRAINTS_LIST_NAME(node_id, prop), \ + .count = DT_PROP_LEN(node_id, prop), \

#if defined(CONFIG_PM) || defined(__DOXYGEN__)
/**
Expand All @@ -382,6 +442,38 @@
* @return Pointer to the power state structure or NULL if state is not found.
*/
const struct pm_state_info *pm_state_get(uint8_t cpu, enum pm_state state, uint8_t substate_id);

/**
* @brief Convert a pm_state enum value to its string representation.
*
* @param state Power state.
*
* @return A constant string representing the state.
*/
const char *pm_state_to_str(enum pm_state state);


Check notice on line 455 in include/zephyr/pm/state.h

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

include/zephyr/pm/state.h:455 -
/**
* @brief Parse a string and convert it to a pm_state enum value.
*
* @param name Input string (e.g., "suspend-to-ram").
* @param out Pointer to store the parsed pm_state value.
*
* @return 0 on success, -EINVAL if the string is invalid or NULL.
*/
int pm_state_from_str(const char *name, enum pm_state *out);

/**
* @brief Check if a power management constraint matches any in a set of constraints.
*
* @param constraints Pointer to the power state constraints structure.
* @param match The constraint to match against.
*
* @return true if the constraint matches, false otherwise.
*/
bool pm_state_in_constraints(const struct pm_state_constraints *constraints,
const struct pm_state_constraint match);

/**
* @}
*/
Expand All @@ -407,6 +499,14 @@
return NULL;
}

static inline bool pm_state_in_constraints(struct pm_state_constraints *constraints,
struct pm_state_constraint match)
{
ARG_UNUSED(constraints);
ARG_UNUSED(match);

return false;
}
#endif /* CONFIG_PM */

#ifdef __cplusplus
Expand Down
22 changes: 16 additions & 6 deletions subsys/pm/pm.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,29 @@ static inline void pm_state_notify(bool entering_state)
{
struct pm_notifier *notifier;
k_spinlock_key_t pm_notifier_key;
void (*callback)(enum pm_state state);
union {
void (*without_substate)(enum pm_state state);
void (*with_substate)(enum pm_state state, uint8_t substate_id);
} callback;

pm_notifier_key = k_spin_lock(&pm_notifier_lock);
SYS_SLIST_FOR_EACH_CONTAINER(&pm_notifiers, notifier, _node) {
if (entering_state) {
callback = notifier->state_entry;
/* should be equivalent to also setting the "with substate" */
callback.without_substate = notifier->state_entry;
} else {
callback = notifier->state_exit;
/* should be equivalent to also setting the "with substate" */
callback.without_substate = notifier->state_exit;
}

if (callback) {
callback(z_cpus_pm_state[CPU_ID]->state);
}
if (callback.with_substate && notifier->report_substate) {
callback.with_substate(z_cpus_pm_state[CPU_ID]->state,
z_cpus_pm_state[CPU_ID]->substate_id);
} else if (callback.without_substate) {
callback.without_substate(z_cpus_pm_state[CPU_ID]->state);
} else {
/* intentionally empty */
};
}
k_spin_unlock(&pm_notifier_lock, pm_notifier_key);
}
Expand Down
10 changes: 0 additions & 10 deletions subsys/pm/policy/policy_device_lock.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,6 @@ struct pm_state_device_constraint {
*/
#define PM_CONSTRAINTS_NAME(node_id) _CONCAT(__devicepmconstraints_, node_id)

/**
* @brief initialize a device pm constraint with information from devicetree.
*
* @param node_id Node identifier.
*/
#define PM_STATE_CONSTRAINT_INIT(node_id) \
{ \
.state = PM_STATE_DT_INIT(node_id), \
.substate_id = DT_PROP_OR(node_id, substate_id, 0), \
}

/**
* @brief Helper macro to define a device pm constraints.
Expand Down
43 changes: 41 additions & 2 deletions subsys/pm/policy/policy_state_lock.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,27 @@
static atomic_t lock_cnt[ARRAY_SIZE(substates)];
static atomic_t latency_mask = BIT_MASK(ARRAY_SIZE(substates));
static atomic_t unlock_mask = BIT_MASK(ARRAY_SIZE(substates));
static atomic_t global_lock_cnt;
static struct k_spinlock lock;

#endif

void pm_policy_state_all_lock_get(void)
{
#if DT_HAS_COMPAT_STATUS_OKAY(zephyr_power_state)
(void)atomic_inc(&global_lock_cnt);
#endif
}

void pm_policy_state_all_lock_put(void)
{
#if DT_HAS_COMPAT_STATUS_OKAY(zephyr_power_state)
__ASSERT(global_lock_cnt > 0, "Unbalanced state lock get/put");
(void)atomic_dec(&global_lock_cnt);
#endif
}


Check notice on line 66 in subsys/pm/policy/policy_state_lock.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

subsys/pm/policy/policy_state_lock.c:66 -
void pm_policy_state_lock_get(enum pm_state state, uint8_t substate_id)
{
#if DT_HAS_COMPAT_STATUS_OKAY(zephyr_power_state)
Expand All @@ -65,6 +82,14 @@
#endif
}

void pm_policy_state_constraints_get(struct pm_state_constraints *constraints)
{
for (int i = 0; i < constraints->count; i++) {
pm_policy_state_lock_get(constraints->list[i].state,
constraints->list[i].substate_id);
}
}

void pm_policy_state_lock_put(enum pm_state state, uint8_t substate_id)
{
#if DT_HAS_COMPAT_STATUS_OKAY(zephyr_power_state)
Expand All @@ -84,13 +109,22 @@
#endif
}

void pm_policy_state_constraints_put(struct pm_state_constraints *constraints)
{
for (int i = 0; i < constraints->count; i++) {
pm_policy_state_lock_put(constraints->list[i].state,
constraints->list[i].substate_id);
}
}

bool pm_policy_state_lock_is_active(enum pm_state state, uint8_t substate_id)
{
#if DT_HAS_COMPAT_STATUS_OKAY(zephyr_power_state)
for (size_t i = 0; i < ARRAY_SIZE(substates); i++) {
if (substates[i].state == state &&
(substates[i].substate_id == substate_id || substate_id == PM_ALL_SUBSTATES)) {
return atomic_get(&lock_cnt[i]) != 0;
return (atomic_get(&global_lock_cnt) != 0) ||
(atomic_get(&lock_cnt[i]) != 0);
}
}
#endif
Expand All @@ -101,6 +135,10 @@
bool pm_policy_state_is_available(enum pm_state state, uint8_t substate_id)
{
#if DT_HAS_COMPAT_STATUS_OKAY(zephyr_power_state)
if (atomic_get(&global_lock_cnt) != 0) {
return false;
}

for (size_t i = 0; i < ARRAY_SIZE(substates); i++) {
if (substates[i].state == state &&
(substates[i].substate_id == substate_id || substate_id == PM_ALL_SUBSTATES)) {
Expand All @@ -119,7 +157,8 @@
/* Check if there is any power state that is not locked and not disabled due
* to latency requirements.
*/
return atomic_get(&unlock_mask) & atomic_get(&latency_mask);
return (atomic_get(&global_lock_cnt) == 0) &&
(atomic_get(&unlock_mask) & atomic_get(&latency_mask));
#endif
return true;
}
Expand Down
Loading
Loading