/* * Copyright (c) 2019 Linaro Limited. * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include "pm_policy.h" #include #include #include #include #include #include #define LOG_LEVEL CONFIG_SYS_PM_LOG_LEVEL #include LOG_MODULE_DECLARE(power); #define SECS_TO_TICKS CONFIG_SYS_CLOCK_TICKS_PER_SEC /* Wakeup delay from standby in microseconds */ #define WAKEDELAYSTANDBY 240 extern PowerCC26X2_ModuleState PowerCC26X2_module; /* PM Policy based on SoC/Platform residency requirements */ static const unsigned int pm_min_residency[] = { #ifdef CONFIG_SYS_POWER_SLEEP_STATES CONFIG_SYS_PM_MIN_RESIDENCY_SLEEP_1 * SECS_TO_TICKS / MSEC_PER_SEC, CONFIG_SYS_PM_MIN_RESIDENCY_SLEEP_2 * SECS_TO_TICKS / MSEC_PER_SEC, #endif /* CONFIG_SYS_POWER_SLEEP_STATES */ }; enum power_states sys_pm_policy_next_state(int32_t ticks) { uint32_t constraints; bool disallowed = false; int i; /* check operating conditions, optimally choose DCDC versus GLDO */ SysCtrl_DCDC_VoltageConditionalControl(); /* query the declared constraints */ constraints = Power_getConstraintMask(); if ((ticks != K_TICKS_FOREVER) && (ticks < pm_min_residency[0])) { LOG_DBG("Not enough time for PM operations: %d", ticks); return SYS_POWER_STATE_ACTIVE; } for (i = ARRAY_SIZE(pm_min_residency) - 1; i >= 0; i--) { #ifdef CONFIG_SYS_PM_STATE_LOCK if (!sys_pm_ctrl_is_state_enabled((enum power_states)(i))) { continue; } #endif if ((ticks == K_TICKS_FOREVER) || (ticks >= pm_min_residency[i])) { /* Verify if Power module has constraints set to * disallow a state */ switch (i) { case 0: /* Idle mode */ if ((constraints & (1 << PowerCC26XX_DISALLOW_IDLE)) != 0) { disallowed = true; } break; case 1: /* Standby mode */ if ((constraints & (1 << PowerCC26XX_DISALLOW_STANDBY)) != 0) { disallowed = true; } /* Set timeout for wakeup event */ __ASSERT( CONFIG_SYS_PM_MIN_RESIDENCY_SLEEP_2 > 1, "CONFIG_SYS_PM_MIN_RESIDENCY_SLEEP_2 must " "be greater than 1."); if (ticks != K_TICKS_FOREVER) { /* NOTE: Ideally we'd like to set a * timer to wake up just a little * earlier to take care of the wakeup * sequence, ie. by WAKEDELAYSTANDBY * microsecs. However, given * k_timer_start (called later by * ClockP_start) does not currently * have sub-millisecond accuracy, wakeup * would be at up to (WAKEDELAYSTANDBY * + 1 ms) ahead of the next timeout. * This also has the implication that * SYS_PM_MIN_RESIDENCY_SLEEP_2 * must be greater than 1. */ ticks -= (WAKEDELAYSTANDBY * CONFIG_SYS_CLOCK_TICKS_PER_SEC + 1000000) / 1000000; #if (CONFIG_SYS_CLOCK_TICKS_PER_SEC <= 1000) /* * ClockP_setTimeout cannot handle any * more ticks */ ticks = MIN(ticks, UINT32_MAX / 1000 * CONFIG_SYS_CLOCK_TICKS_PER_SEC); #endif ClockP_setTimeout(ClockP_handle( (ClockP_Struct *) &PowerCC26X2_module.clockObj), ticks); } break; default: /* This should never be reached */ LOG_ERR("Invalid sleep state detected\n"); } if (disallowed) { disallowed = false; continue; } LOG_DBG("Selected power state %d " "(ticks: %d, min_residency: %u)", i, ticks, pm_min_residency[i]); return (enum power_states)(i); } } LOG_DBG("No suitable power state found!"); return SYS_POWER_STATE_ACTIVE; } __weak bool sys_pm_policy_low_power_devices(enum power_states pm_state) { #ifdef CONFIG_SYS_POWER_SLEEP_STATES return (pm_state == SYS_POWER_STATE_SLEEP_2); #else return false; #endif }