pm_state_get() is used to get a PM state that can be forced. Disabled states (not included in automatic state selection) can also be forced thus they should be included in search inside pm_state_get(). Signed-off-by: Krzysztof Chruściński <krzysztof.chruscinski@nordicsemi.no>
109 lines
3.3 KiB
C
109 lines
3.3 KiB
C
/*
|
|
* Copyright (c) 2018 Intel Corporation.
|
|
* Copyright (c) 2021 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/pm/state.h>
|
|
#include <zephyr/toolchain.h>
|
|
|
|
BUILD_ASSERT(DT_NODE_EXISTS(DT_PATH(cpus)),
|
|
"cpus node not defined in Devicetree");
|
|
|
|
/**
|
|
* Check CPU power state consistency.
|
|
*
|
|
* @param i Power state index.
|
|
* @param node_id CPU node identifier.
|
|
*/
|
|
#define CHECK_POWER_STATE_CONSISTENCY(i, node_id) \
|
|
BUILD_ASSERT( \
|
|
DT_PROP_BY_PHANDLE_IDX_OR(node_id, cpu_power_states, i, \
|
|
min_residency_us, 0U) >= \
|
|
DT_PROP_BY_PHANDLE_IDX_OR(node_id, cpu_power_states, i, \
|
|
exit_latency_us, 0U), \
|
|
"Found CPU power state with min_residency < exit_latency")
|
|
|
|
/**
|
|
* @brief Check CPU power states consistency
|
|
*
|
|
* All states should have a minimum residency >= than the exit latency.
|
|
*
|
|
* @param node_id A CPU node identifier.
|
|
*/
|
|
#define CHECK_POWER_STATES_CONSISTENCY(node_id) \
|
|
LISTIFY(DT_PROP_LEN_OR(node_id, cpu_power_states, 0), \
|
|
CHECK_POWER_STATE_CONSISTENCY, (;), node_id); \
|
|
|
|
/* Check that all power states are consistent */
|
|
DT_FOREACH_CHILD(DT_PATH(cpus), CHECK_POWER_STATES_CONSISTENCY)
|
|
|
|
#define DEFINE_CPU_STATES(n) \
|
|
static const struct pm_state_info pmstates_##n[] \
|
|
= PM_STATE_INFO_LIST_FROM_DT_CPU(n);
|
|
#define CPU_STATE_REF(n) pmstates_##n
|
|
|
|
DT_FOREACH_CHILD(DT_PATH(cpus), DEFINE_CPU_STATES);
|
|
|
|
/** CPU power states information for each CPU */
|
|
static const struct pm_state_info *cpus_states[] = {
|
|
DT_FOREACH_CHILD_STATUS_OKAY_SEP(DT_PATH(cpus), CPU_STATE_REF, (,))
|
|
};
|
|
|
|
/** Number of states for each CPU */
|
|
static const uint8_t states_per_cpu[] = {
|
|
DT_FOREACH_CHILD_STATUS_OKAY_SEP(DT_PATH(cpus), DT_NUM_CPU_POWER_STATES, (,))
|
|
};
|
|
|
|
#define DEFINE_DISABLED_PM_STATE(node) \
|
|
IF_ENABLED(DT_NODE_HAS_STATUS(node, disabled), (PM_STATE_INFO_DT_INIT(node),))
|
|
|
|
/** Check if power states exists in the Devicetree. */
|
|
#define POWER_STATES_EXISTS() \
|
|
UTIL_OR(DT_NODE_EXISTS(DT_PATH(cpus, power_states)), \
|
|
DT_NODE_EXISTS(DT_PATH(power_states)))
|
|
|
|
/** Get node with power states. Macro assumes that power states exists. */
|
|
#define POWER_STATES_NODE() \
|
|
COND_CODE_1(DT_NODE_EXISTS(DT_PATH(cpus, power_states)), \
|
|
(DT_PATH(cpus, power_states)), (DT_PATH(power_states)))
|
|
|
|
/* Array with all states which are disabled but can be forced. */
|
|
static const struct pm_state_info disabled_states[] = {
|
|
IF_ENABLED(POWER_STATES_EXISTS(),
|
|
(DT_FOREACH_CHILD(POWER_STATES_NODE(), DEFINE_DISABLED_PM_STATE)))
|
|
};
|
|
|
|
uint8_t pm_state_cpu_get_all(uint8_t cpu, const struct pm_state_info **states)
|
|
{
|
|
if (cpu >= ARRAY_SIZE(cpus_states)) {
|
|
return 0;
|
|
}
|
|
|
|
*states = cpus_states[cpu];
|
|
|
|
return states_per_cpu[cpu];
|
|
}
|
|
|
|
const struct pm_state_info *pm_state_get(uint8_t cpu, enum pm_state state, uint8_t substate_id)
|
|
{
|
|
__ASSERT_NO_MSG(cpu < ARRAY_SIZE(cpus_states));
|
|
const struct pm_state_info *states = cpus_states[cpu];
|
|
uint8_t cnt = states_per_cpu[cpu];
|
|
|
|
for (uint8_t i = 0; i < cnt; i++) {
|
|
if ((states[i].state == state) && (states[i].substate_id == substate_id)) {
|
|
return &states[i];
|
|
}
|
|
}
|
|
|
|
for (uint8_t i = 0; i < ARRAY_SIZE(disabled_states); i++) {
|
|
if ((disabled_states[i].state == state) &&
|
|
(disabled_states[i].substate_id == substate_id)) {
|
|
return &disabled_states[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|