/* * Copyright (c) 2018 Intel Corporation. * Copyright (c) 2021 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include #include BUILD_ASSERT(DT_NODE_EXISTS(DT_PATH(cpus)), "cpus node not defined in Devicetree"); #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; } const char *pm_state_to_str(enum pm_state state) { switch (state) { case PM_STATE_ACTIVE: return "active"; case PM_STATE_RUNTIME_IDLE: return "runtime-idle"; case PM_STATE_SUSPEND_TO_IDLE: return "suspend-to-idle"; case PM_STATE_STANDBY: return "standby"; case PM_STATE_SUSPEND_TO_RAM: return "suspend-to-ram"; case PM_STATE_SUSPEND_TO_DISK: return "suspend-to-disk"; case PM_STATE_SOFT_OFF: return "soft-off"; default: return "UNKNOWN"; } } int pm_state_from_str(const char *name, enum pm_state *out) { if (strcmp(name, "active") == 0) { *out = PM_STATE_ACTIVE; } else if (strcmp(name, "runtime-idle") == 0) { *out = PM_STATE_RUNTIME_IDLE; } else if (strcmp(name, "suspend-to-idle") == 0) { *out = PM_STATE_SUSPEND_TO_IDLE; } else if (strcmp(name, "standby") == 0) { *out = PM_STATE_STANDBY; } else if (strcmp(name, "suspend-to-ram") == 0) { *out = PM_STATE_SUSPEND_TO_RAM; } else if (strcmp(name, "suspend-to-disk") == 0) { *out = PM_STATE_SUSPEND_TO_DISK; } else if (strcmp(name, "soft-off") == 0) { *out = PM_STATE_SOFT_OFF; } else { return -EINVAL; } return 0; } bool pm_state_in_constraints(const struct pm_state_constraints *constraints, const struct pm_state_constraint match) { struct pm_state_constraint *constraints_list = constraints->list; size_t num_constraints = constraints->count; bool match_found = false; for (int i = 0; i < num_constraints; i++) { enum pm_state state = constraints_list[i].state; uint8_t substate = constraints_list[i].substate_id; match_found |= ((state == match.state) && (substate == match.substate_id)); } return match_found; }